WIP: undo, backspace implementation, bug fixes

This commit is contained in:
Matthias Koefferlein 2025-08-19 23:12:43 +02:00
parent 57cd512bf9
commit d3d2eda54a
5 changed files with 137 additions and 65 deletions

View File

@ -122,7 +122,9 @@ Manager::transaction (const std::string &description, transaction_id_t join_with
tl_assert (! m_replay);
if (! m_transactions.empty () && reinterpret_cast<transaction_id_t> (& m_transactions.back ()) == join_with) {
m_transactions.back ().second = description;
if (! description.empty ()) {
m_transactions.back ().second = description;
}
} else {
// delete all following transactions and add a new one
erase_transactions (m_current, m_transactions.end ());

View File

@ -57,7 +57,7 @@ namespace edt
ShapeEditService::ShapeEditService (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIterator::flags_type shape_types)
: edt::Service (manager, view, shape_types),
m_layer (0), m_cv_index (0), mp_cell (0), mp_layout (0), m_combine_mode (CM_Add)
m_layer (0), m_cv_index (0), mp_cell (0), mp_layout (0), m_combine_mode (CM_Add), m_update_edit_layer_enabled (true)
{
view->current_layer_changed_event.add (this, &ShapeEditService::update_edit_layer);
}
@ -151,7 +151,7 @@ ShapeEditService::get_edit_layer ()
// fetches the last configuration for the given layer
view ()->set_active_cellview_index (cv_index);
edt::config_recent_for_layer (view (), cv->layout ().get_properties ((unsigned int) layer), cv_index);
edt::config_recent_for_layer (view (), dispatcher (), cv->layout ().get_properties ((unsigned int) layer), cv_index);
}
void
@ -171,12 +171,41 @@ ShapeEditService::change_edit_layer (const db::LayerProperties &lp)
// fetches the last configuration for the given layer
view ()->set_active_cellview_index (m_cv_index);
edt::config_recent_for_layer (view (), lp, m_cv_index);
edt::config_recent_for_layer (view (), dispatcher (), lp, m_cv_index);
}
void
ShapeEditService::set_layer (const db::LayerProperties &lp, unsigned int cv_index)
{
if (! mp_layout) {
return;
}
int layer = mp_layout->get_layer_maybe (lp);
if (layer < 0) {
layer = mp_layout->insert_layer (lp);
}
m_layer = (unsigned int) layer;
m_cv_index = cv_index;
m_update_edit_layer_enabled = false;
try {
view ()->set_current_layer (cv_index, lp);
m_update_edit_layer_enabled = true;
} catch (...) {
m_update_edit_layer_enabled = true;
throw;
}
}
void
ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl)
{
if (! m_update_edit_layer_enabled) {
return;
}
if (! editing ()) {
return;
}
@ -238,7 +267,7 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl
// fetches the last configuration for the given layer
view ()->set_active_cellview_index (cv_index);
edt::config_recent_for_layer (view (), cv->layout ().get_properties ((unsigned int) layer), cv_index);
edt::config_recent_for_layer (view (), dispatcher (), cv->layout ().get_properties ((unsigned int) layer), cv_index);
current_layer_changed ();
}
@ -1360,7 +1389,12 @@ PathService::do_delete ()
update_marker ();
update_via ();
} else if (! m_previous_segments.empty ()) {
pop_segment ();
}
}
void
@ -1459,6 +1493,10 @@ PathService::via (int dir)
tl_assert (false); // see TODO
#endif
if (! editing ()) {
return;
}
// not enough points to form a path
if (m_points.size () < 2) {
return;
@ -1512,15 +1550,11 @@ PathService::via (int dir)
}
commit_recent (view ());
// produce the path up to the current point
db::DPoint via_pos = m_points.back ();
db::Shape path_shape;
{
db::Transaction transaction (manager (), tl::to_string (tr ("Create path")));
path_shape = cell ().shapes (layer ()).insert (get_path ());
}
bool is_bottom = via_def.via_type.bottom.log_equal (lp);
db::LayerProperties lp_new = is_bottom ? via_def.via_type.top : via_def.via_type.bottom;
@ -1545,7 +1579,9 @@ PathService::via (int dir)
params.insert (std::make_pair ("h_top", tl::Variant (h_top)));
{
db::Transaction transaction (manager (), tl::to_string (tr ("Create via")));
db::Transaction transaction (manager (), tl::to_string (tr ("Create path segment")));
db::Shape path_shape = cell ().shapes (layer ()).insert (get_path ());
auto via_lib_cell = via_def.lib->layout ().get_pcell_variant_dict (via_def.pcell, params);
auto via_cell = layout ().get_lib_proxy (via_def.lib, via_lib_cell);
@ -1558,15 +1594,15 @@ PathService::via (int dir)
}
change_edit_layer (lp_new);
m_points.clear ();
m_points.push_back (via_pos);
m_points.push_back (via_pos);
m_last = m_points.back ();
update_marker ();
update_via ();
}
m_points.clear ();
m_points.push_back (via_pos);
m_points.push_back (via_pos);
m_last = m_points.back ();
update_marker ();
update_via ();
}
void
@ -1615,35 +1651,37 @@ PathService::update_via ()
// change the via PCell
{
db::Transaction transaction (manager (), tl::to_string (tr ("Create via")), ps.via_transaction_id);
db::Transaction transaction (manager () && ! manager ()->transacting () ? manager () : 0, std::string (), ps.transaction_id);
ps.via_instance = via_parent_cell->change_pcell_parameters (ps.via_instance, params);
layout ().cleanup ();
}
}
static std::string path_config_keys [] = {
cfg_edit_path_width,
cfg_edit_path_ext_var_begin,
cfg_edit_path_ext_var_end,
cfg_edit_path_ext_type
};
void
PathService::push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t via_transaction_id)
PathService::push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t transaction_id)
{
m_previous_segments.push_back (PathSegment ());
PathSegment &ps = m_previous_segments.back ();
ps.points = m_points;
ps.last_point = m_last;
ps.path_shape = shape;
ps.via_instance = instance;
ps.via_transaction_id = via_transaction_id;
ps.via_type = via_type;
ps.layer = layer ();
ps.layer = layout ().get_properties (layer ());
ps.cv_index = cv_index ();
ps.transaction_id = transaction_id;
std::string cfg [] = {
cfg_edit_path_width,
cfg_edit_path_ext_var_begin,
cfg_edit_path_ext_var_end,
cfg_edit_path_ext_type
};
for (unsigned int i = 0; i < sizeof (cfg) / sizeof (cfg[0]); ++i) {
ps.config.push_back (std::make_pair (cfg [i], std::string ()));
for (unsigned int i = 0; i < sizeof (path_config_keys) / sizeof (path_config_keys[0]); ++i) {
ps.config.push_back (std::make_pair (path_config_keys [i], std::string ()));
view ()->config_get (ps.config.back ().first, ps.config.back ().second);
}
}
@ -1651,7 +1689,48 @@ PathService::push_segment (const db::Shape &shape, const db::Instance &instance,
void
PathService::pop_segment ()
{
// @@@
PathSegment ps = m_previous_segments.back ();
m_previous_segments.pop_back ();
if (manager () && manager ()->transaction_id_for_undo () == ps.transaction_id) {
// should remove shape and via instance
manager ()->undo ();
// empties the undo queue, so we don't keep objects there and spoil subsequent "update_via" actions
// TODO: is there a better way to do this?
manager ()->transaction (std::string ());
manager ()->cancel ();
} else {
// fallback without using undo
db::Transaction transaction (manager (), tl::to_string (tr ("Undo path segment")));
if (! ps.path_shape.is_null () && ps.path_shape.shapes ()) {
ps.path_shape.shapes ()->erase_shape (ps.path_shape);
}
if (! ps.via_instance.is_null () && ps.via_instance.instances ()) {
ps.via_instance.instances ()->erase (ps.via_instance);
}
}
set_layer (ps.layer, ps.cv_index);
m_points = ps.points;
m_last = ps.last_point;
for (auto i = ps.config.begin (); i != ps.config.end (); ++i) {
dispatcher ()->config_set (i->first, i->second);
}
// avoids update_via() which might spoil the via we just recovered
m_needs_update = false;
dispatcher ()->config_end ();
update_marker ();
}
bool

View File

@ -71,6 +71,7 @@ protected:
void deliver_shape (const db::Path &path);
void deliver_shape (const db::Box &box);
void deliver_shape (const db::Point &point);
void set_layer (const db::LayerProperties &lp, unsigned int cv_index);
void open_editor_hooks ();
template <class Shape>
void deliver_shape_to_hooks (const Shape &shape);
@ -92,6 +93,7 @@ private:
db::Layout *mp_layout;
combine_mode_type m_combine_mode;
tl::weak_collection<edt::EditorHooks> m_editor_hooks;
bool m_update_edit_layer_enabled;
void update_edit_layer (const lay::LayerPropertiesConstIterator &iter);
};
@ -248,18 +250,20 @@ protected:
private:
struct PathSegment
{
PathSegment () : layer (0), cv_index (0), via_transaction_id (0) { }
PathSegment () : cv_index (0), transaction_id (0) { }
unsigned int layer;
db::LayerProperties layer;
int cv_index;
std::list<std::pair<std::string, std::string> > config;
std::vector<db::DPoint> points;
db::DPoint last_point;
db::Shape path_shape;
db::Instance via_instance;
db::Manager::transaction_id_t via_transaction_id;
db::ViaType via_type;
db::Manager::transaction_id_t transaction_id;
};
std::vector <db::DPoint> m_points;
std::vector<db::DPoint> m_points;
double m_width, m_bgnext, m_endext;
enum { Flush = 0, Square, Variable, Round } m_type;
bool m_needs_update;
@ -270,7 +274,7 @@ private:
db::Path get_path () const;
void set_last_point (const db::DPoint &p);
void update_via ();
void push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t via_transaction_id);
void push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t transaction_id);
void pop_segment ();
};

View File

@ -97,7 +97,7 @@ commit_recent (lay::LayoutViewBase *view)
}
void
config_recent_for_layer (lay::LayoutViewBase *view, const db::LayerProperties &lp, int cv_index)
config_recent_for_layer (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher, const db::LayerProperties &lp, int cv_index)
{
#if defined(HAVE_QT)
lay::EditorOptionsPages *eo_pages = view->editor_options_pages ();
@ -107,7 +107,7 @@ config_recent_for_layer (lay::LayoutViewBase *view, const db::LayerProperties &l
for (std::vector<lay::EditorOptionsPage *>::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) {
if ((*op)->active ()) {
(*op)->config_recent_for_layer (view, lp, cv_index);
(*op)->config_recent_for_layer (dispatcher, lp, cv_index);
}
}
#endif
@ -116,7 +116,6 @@ config_recent_for_layer (lay::LayoutViewBase *view, const db::LayerProperties &l
bool
set_or_request_current_layer (lay::LayoutViewBase *view, const db::LayerProperties &lp, unsigned int cv_index, bool make_current)
{
#if defined(HAVE_QT)
// try to find an existing layer
if (make_current) {
if (view->set_current_layer (cv_index, lp)) {
@ -128,39 +127,26 @@ set_or_request_current_layer (lay::LayoutViewBase *view, const db::LayerProperti
}
}
if (! view->control_panel ()) {
return false;
}
const lay::CellView &cv = view->cellview (cv_index);
if (! cv.is_valid ()) {
return false;
}
if (QMessageBox::question (view->widget (), tr ("Create Layer"), tr ("Layer %1 does not exist yet. Create it now?").arg (tl::to_qstring (lp.to_string ()))) == QMessageBox::Yes) {
lay::LayerPropertiesNode lpn;
lpn.set_source (lay::ParsedLayerSource (lp, cv_index));
view->init_layer_properties (lpn);
lay::LayerPropertiesNode lpn;
lpn.set_source (lay::ParsedLayerSource (lp, cv_index));
view->init_layer_properties (lpn);
{
db::Transaction transaction (! view->manager ()->transacting () ? view->manager () : 0, tl::to_string (QObject::tr ("Create new layer")));
{
db::Transaction transaction (! view->manager ()->transacting () ? view->manager () : 0, tl::to_string (QObject::tr ("Create new layer")));
lay::LayerPropertiesConstIterator lpi = lay::LayerPropertiesConstIterator (& view->insert_layer (view->end_layers (), lpn));
if (make_current) {
view->set_current_layer (lpi);
}
lpi->realize_source ();
lay::LayerPropertiesConstIterator lpi = lay::LayerPropertiesConstIterator (& view->insert_layer (view->end_layers (), lpn));
if (make_current) {
view->set_current_layer (lpi);
}
return true;
lpi->realize_source ();
}
return false;
#else
return true;
#endif
}
// -------------------------------------------------------------

View File

@ -39,6 +39,7 @@
namespace lay
{
class LayoutViewBase;
class Dispatcher;
}
namespace edt {
@ -77,7 +78,7 @@ commit_recent (lay::LayoutViewBase *view);
* @brief Configure attributes for the most-recent entry with the given layer
*/
void
config_recent_for_layer (lay::LayoutViewBase *view, const db::LayerProperties &lp, int cv_index);
config_recent_for_layer (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher, const db::LayerProperties &lp, int cv_index);
/**
* @brief Request to make the given layer the current one (asks whether to create the layer if needed)