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); tl_assert (! m_replay);
if (! m_transactions.empty () && reinterpret_cast<transaction_id_t> (& m_transactions.back ()) == join_with) { 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 { } else {
// delete all following transactions and add a new one // delete all following transactions and add a new one
erase_transactions (m_current, m_transactions.end ()); 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) ShapeEditService::ShapeEditService (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIterator::flags_type shape_types)
: edt::Service (manager, view, 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); 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 // fetches the last configuration for the given layer
view ()->set_active_cellview_index (cv_index); 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 void
@ -171,12 +171,41 @@ ShapeEditService::change_edit_layer (const db::LayerProperties &lp)
// fetches the last configuration for the given layer // fetches the last configuration for the given layer
view ()->set_active_cellview_index (m_cv_index); 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 void
ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl) ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl)
{ {
if (! m_update_edit_layer_enabled) {
return;
}
if (! editing ()) { if (! editing ()) {
return; return;
} }
@ -238,7 +267,7 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl
// fetches the last configuration for the given layer // fetches the last configuration for the given layer
view ()->set_active_cellview_index (cv_index); 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 (); current_layer_changed ();
} }
@ -1360,7 +1389,12 @@ PathService::do_delete ()
update_marker (); update_marker ();
update_via (); update_via ();
} else if (! m_previous_segments.empty ()) {
pop_segment ();
} }
} }
void void
@ -1459,6 +1493,10 @@ PathService::via (int dir)
tl_assert (false); // see TODO tl_assert (false); // see TODO
#endif #endif
if (! editing ()) {
return;
}
// not enough points to form a path // not enough points to form a path
if (m_points.size () < 2) { if (m_points.size () < 2) {
return; return;
@ -1512,15 +1550,11 @@ PathService::via (int dir)
} }
commit_recent (view ());
// produce the path up to the current point // produce the path up to the current point
db::DPoint via_pos = m_points.back (); 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); 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; 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))); 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_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); 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); 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 void
@ -1615,35 +1651,37 @@ PathService::update_via ()
// change the via PCell // 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); ps.via_instance = via_parent_cell->change_pcell_parameters (ps.via_instance, params);
layout ().cleanup (); 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 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 ()); m_previous_segments.push_back (PathSegment ());
PathSegment &ps = m_previous_segments.back (); PathSegment &ps = m_previous_segments.back ();
ps.points = m_points;
ps.last_point = m_last;
ps.path_shape = shape; ps.path_shape = shape;
ps.via_instance = instance; ps.via_instance = instance;
ps.via_transaction_id = via_transaction_id;
ps.via_type = via_type; ps.via_type = via_type;
ps.layer = layer (); ps.layer = layout ().get_properties (layer ());
ps.cv_index = cv_index (); ps.cv_index = cv_index ();
ps.transaction_id = transaction_id;
std::string cfg [] = { for (unsigned int i = 0; i < sizeof (path_config_keys) / sizeof (path_config_keys[0]); ++i) {
cfg_edit_path_width, ps.config.push_back (std::make_pair (path_config_keys [i], std::string ()));
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 ()));
view ()->config_get (ps.config.back ().first, ps.config.back ().second); 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 void
PathService::pop_segment () 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 bool

View File

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

View File

@ -97,7 +97,7 @@ commit_recent (lay::LayoutViewBase *view)
} }
void 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) #if defined(HAVE_QT)
lay::EditorOptionsPages *eo_pages = view->editor_options_pages (); 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) { for (std::vector<lay::EditorOptionsPage *>::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) {
if ((*op)->active ()) { if ((*op)->active ()) {
(*op)->config_recent_for_layer (view, lp, cv_index); (*op)->config_recent_for_layer (dispatcher, lp, cv_index);
} }
} }
#endif #endif
@ -116,7 +116,6 @@ config_recent_for_layer (lay::LayoutViewBase *view, const db::LayerProperties &l
bool bool
set_or_request_current_layer (lay::LayoutViewBase *view, const db::LayerProperties &lp, unsigned int cv_index, bool make_current) 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 // try to find an existing layer
if (make_current) { if (make_current) {
if (view->set_current_layer (cv_index, lp)) { 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); const lay::CellView &cv = view->cellview (cv_index);
if (! cv.is_valid ()) { if (! cv.is_valid ()) {
return false; 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)); db::Transaction transaction (! view->manager ()->transacting () ? view->manager () : 0, tl::to_string (QObject::tr ("Create new layer")));
view->init_layer_properties (lpn);
{ lay::LayerPropertiesConstIterator lpi = lay::LayerPropertiesConstIterator (& view->insert_layer (view->end_layers (), lpn));
db::Transaction transaction (! view->manager ()->transacting () ? view->manager () : 0, tl::to_string (QObject::tr ("Create new layer"))); if (make_current) {
view->set_current_layer (lpi);
lay::LayerPropertiesConstIterator lpi = lay::LayerPropertiesConstIterator (& view->insert_layer (view->end_layers (), lpn));
if (make_current) {
view->set_current_layer (lpi);
}
lpi->realize_source ();
} }
lpi->realize_source ();
return true;
} }
return false;
#else
return true; return true;
#endif
} }
// ------------------------------------------------------------- // -------------------------------------------------------------

View File

@ -39,6 +39,7 @@
namespace lay namespace lay
{ {
class LayoutViewBase; class LayoutViewBase;
class Dispatcher;
} }
namespace edt { namespace edt {
@ -77,7 +78,7 @@ commit_recent (lay::LayoutViewBase *view);
* @brief Configure attributes for the most-recent entry with the given layer * @brief Configure attributes for the most-recent entry with the given layer
*/ */
void 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) * @brief Request to make the given layer the current one (asks whether to create the layer if needed)