From 6b5dbb144205aa11081395584bb607af39139333 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 16 Jan 2026 01:03:36 +0100 Subject: [PATCH] Enabling toolbox widgets for box service. Plus add-on: pressing Shift while dragging a box makes it centered. --- src/edt/edt/edtBoxService.cc | 62 ++++++++++++++++++--- src/edt/edt/edtBoxService.h | 5 ++ src/edt/edt/edtEditorOptionsPages.cc | 80 ++++++++++++++++++++++++++++ src/edt/edt/edtEditorOptionsPages.h | 25 +++++++++ src/edt/edt/edtService.cc | 64 ++++++++++++++++++---- src/edt/edt/edtService.h | 27 +++++++++- 6 files changed, 246 insertions(+), 17 deletions(-) diff --git a/src/edt/edt/edtBoxService.cc b/src/edt/edt/edtBoxService.cc index a8554c43b..858c6473b 100644 --- a/src/edt/edt/edtBoxService.cc +++ b/src/edt/edt/edtBoxService.cc @@ -24,6 +24,7 @@ #include "edtBoxService.h" #include "layLayoutViewBase.h" +#include "layEditorOptionsPage.h" #if defined(HAVE_QT) # include "edtPropertiesPages.h" @@ -35,8 +36,11 @@ namespace edt // ----------------------------------------------------------------------------- // BoxService implementation +const char *BoxService::configure_name () { return "box-toolkit-widget-value"; } +const char *BoxService::function_name () { return "box-toolkit-widget-commit"; } + BoxService::BoxService (db::Manager *manager, lay::LayoutViewBase *view) - : ShapeEditService (manager, view, db::ShapeIterator::Boxes) + : ShapeEditService (manager, view, db::ShapeIterator::Boxes), m_centered (false) { // .. nothing yet .. } @@ -65,10 +69,46 @@ BoxService::do_begin_edit (const db::DPoint &p) update_marker (); } +void +BoxService::function (const std::string &name, const std::string &value) +{ + if (name == function_name ()) { + + try { + + db::DVector dim; + tl::from_string (value, dim); + + if (! m_centered) { + // Adjust the direction so it reflects the current + if (m_p2.x () < m_p1.x ()) { + dim.set_x (-fabs (dim.x ())); + } + if (m_p2.y () < m_p1.y ()) { + dim.set_y (-fabs (dim.y ())); + } + } else { + dim = db::DVector (fabs (dim.x ()) * 0.5, fabs (dim.y ()) * 0.5); + } + m_p2 = m_p1 + dim; + + finish_editing (); + + } catch (...) { + } + + } +} + db::Box BoxService::get_box () const { - return db::Box (trans () * m_p1, trans () * m_p2); + if (m_centered) { + db::DVector d = m_p2 - m_p1; + return db::Box (trans () * (m_p1 - d), trans () * (m_p1 + d)); + } else { + return db::Box (trans () * m_p1, trans () * m_p2); + } } void @@ -79,10 +119,18 @@ BoxService::update_marker () marker->set (get_box (), db::VCplxTrans (1.0 / layout ().dbu ()) * trans ().inverted ()); + db::DVector d = m_p2 - m_p1; + db::DVector dim = db::DVector (fabs (d.x ()), fabs (d.y ())) * (m_centered ? 2.0 : 1.0); + view ()->message (std::string ("lx: ") + - tl::micron_to_string (m_p2.x () - m_p1.x ()) + + tl::micron_to_string (dim.x ()) + std::string (" ly: ") + - tl::micron_to_string (m_p2.y () - m_p1.y ())); + tl::micron_to_string (dim.y ())); + + auto p = toolbox_widget (); + if (p) { + p->configure (configure_name (), dim.to_string ()); + } } @@ -109,12 +157,13 @@ void BoxService::do_mouse_move (const db::DPoint &p) { // snap to square if Ctrl button is pressed - bool snap_square = alt_ac () == lay::AC_Diagonal; + bool snap_square = (mouse_buttons () & lay::ControlButton) != 0; + bool centered = (mouse_buttons () & lay::ShiftButton) != 0; lay::PointSnapToObjectResult snap_details = snap2_details (p, m_p1, snap_square ? lay::AC_DiagonalOnly : lay::AC_Any); db::DPoint ps = snap_details.snapped_point; - if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) { + if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject && ! m_centered) { clear_mouse_cursors (); @@ -152,6 +201,7 @@ BoxService::do_mouse_move (const db::DPoint &p) set_cursor (lay::Cursor::cross); m_p2 = ps; + m_centered = centered; update_marker (); } diff --git a/src/edt/edt/edtBoxService.h b/src/edt/edt/edtBoxService.h index c1455eaf9..c14b557f8 100644 --- a/src/edt/edt/edtBoxService.h +++ b/src/edt/edt/edtBoxService.h @@ -36,6 +36,9 @@ class BoxService : public ShapeEditService { public: + static const char *configure_name (); + static const char *function_name (); + BoxService (db::Manager *manager, lay::LayoutViewBase *view); #if defined(HAVE_QT) @@ -48,9 +51,11 @@ public: virtual void do_finish_edit (); virtual void do_cancel_edit (); virtual bool selection_applies (const lay::ObjectInstPath &sel) const; + virtual void function (const std::string &name, const std::string &value); private: db::DPoint m_p1, m_p2; + bool m_centered; void update_marker (); db::Box get_box () const; diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index c7cb4b6ad..38298a260 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -29,6 +29,7 @@ #include "edtPCellParametersPage.h" #include "edtConfig.h" #include "edtService.h" +#include "edtBoxService.h" #include "edtEditorOptionsPages.h" #include "edtPropertiesPageUtils.h" #include "tlExceptions.h" @@ -898,6 +899,82 @@ EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector set_label ("w:"); + mp_layout->addWidget (mp_x_le); + + mp_y_le = new lay::DecoratedLineEdit (this); + mp_y_le->set_label ("h:"); + mp_layout->addWidget (mp_y_le); + + mp_layout->addStretch (1); + + hide (); + + set_toolbox_widget (true); + set_transparent (true); +} + +BoxToolkitWidget::~BoxToolkitWidget () +{ + // .. nothing yet .. +} + +std::string +BoxToolkitWidget::title () const +{ + return "Box Options"; +} + +void +BoxToolkitWidget::deactivated () +{ + hide (); +} + +void +BoxToolkitWidget::commit (lay::Dispatcher *dispatcher) +{ + try { + + double dx = 0.0, dy = 0.0; + + tl::from_string (tl::to_string (mp_x_le->text ()), dx); + tl::from_string (tl::to_string (mp_y_le->text ()), dy); + + dispatcher->call_function (BoxService::function_name (), db::DVector (dx, dy).to_string ()); + + } catch (...) { + } +} + +void +BoxToolkitWidget::configure (const std::string &name, const std::string &value) +{ + if (name == BoxService::configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) { + + try { + + db::DVector mv; + tl::from_string (value, mv); + + mp_x_le->setText (tl::to_qstring (tl::micron_to_string (mv.x ()))); + mp_y_le->setText (tl::to_qstring (tl::micron_to_string (mv.y ()))); + + } catch (...) { + } + + } +} + // ------------------------------------------------------------------ // Registrations @@ -909,6 +986,9 @@ static tl::RegisteredClass s_factory_paths (n static tl::RegisteredClass s_factory_insts (new lay::EditorOptionsPageFactory ("edt::Service(CellInstances)"), 0); static tl::RegisteredClass s_factory_insts_pcell (new lay::EditorOptionsPageFactory ("edt::Service(CellInstances)"), 0); +// toolkit widgets +static tl::RegisteredClass s_box_tookit_widget_factory (new lay::EditorOptionsPageFactory (), 0); + } #endif diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index c2056e2c9..d04b43978 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -36,6 +36,7 @@ class QTabWidget; class QLabel; +class QHBoxLayout; namespace Ui { @@ -55,6 +56,7 @@ namespace lay class Dispatcher; class LayoutViewBase; class Plugin; + class DecoratedLineEdit; } namespace edt @@ -193,6 +195,29 @@ private: virtual void technology_changed (const std::string &); }; +/** + * @brief The toolbox widget for boxes + */ +class BoxToolkitWidget + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + BoxToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~BoxToolkitWidget (); + + virtual std::string title () const; + virtual int order () const { return 0; } + virtual void configure (const std::string &name, const std::string &value); + virtual void commit (lay::Dispatcher *root); + virtual void deactivated (); + +private: + QHBoxLayout *mp_layout; + lay::DecoratedLineEdit *mp_x_le, *mp_y_le; +}; + } #endif diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index d44cd83ec..137040363 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -46,7 +46,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter db::Object (manager), mp_view (view), mp_transient_marker (0), - m_mouse_in_view (false), m_editing (false), m_immediate (false), + m_mouse_buttons (0), m_mouse_in_view (false), m_editing (false), m_immediate (false), m_selection_maybe_invalid (false), m_cell_inst_service (false), m_flags (flags), @@ -70,7 +70,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) db::Object (manager), mp_view (view), mp_transient_marker (0), - m_mouse_in_view (false), m_editing (false), m_immediate (false), + m_mouse_buttons (0), m_mouse_in_view (false), m_editing (false), m_immediate (false), m_selection_maybe_invalid (false), m_cell_inst_service (true), m_flags (db::ShapeIterator::Nothing), @@ -902,6 +902,7 @@ bool Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { m_mouse_pos = p; + m_mouse_buttons = buttons; if (view ()->is_editable () && prio) { @@ -938,6 +939,9 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) bool Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) { + m_mouse_pos = p; + m_mouse_buttons = buttons; + if (view ()->is_editable () && prio) { if ((buttons & lay::LeftButton) != 0) { @@ -952,9 +956,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio } else { if (do_mouse_click (p)) { - m_editing = false; - set_edit_marker (0); - do_finish_edit (); + finish_editing (); } } @@ -984,14 +986,14 @@ Service::enter_event (bool /*prio*/) } bool -Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio) +Service::mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { + m_mouse_pos = p; + m_mouse_buttons = buttons; + if (m_editing && prio && (buttons & lay::LeftButton) != 0) { m_alt_ac = lay::ac_from_buttons (buttons); - do_finish_edit (); - m_editing = false; - set_edit_marker (0); - m_alt_ac = lay::AC_Global; + finish_editing (); return true; } else { return false; @@ -1001,6 +1003,9 @@ Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int button bool Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { + m_mouse_pos = p; + m_mouse_buttons = buttons; + if (view ()->is_editable () && prio && (buttons & lay::RightButton) != 0 && m_editing) { m_alt_ac = lay::ac_from_buttons (buttons); do_mouse_transform (p, db::DFTrans (db::DFTrans::r90)); @@ -1022,6 +1027,17 @@ Service::key_event (unsigned int key, unsigned int buttons) } } +void +Service::finish_editing () +{ + do_finish_edit (); + + m_editing = false; + show_toolbox (false); + set_edit_marker (0); + m_alt_ac = lay::AC_Global; +} + void Service::activated () { @@ -1749,6 +1765,7 @@ void Service::begin_edit (const db::DPoint &p) { do_begin_edit (p); + show_toolbox (true); m_editing = true; } @@ -2055,6 +2072,33 @@ Service::commit_recent () } } +void +Service::show_toolbox (bool visible) +{ + auto p = toolbox_widget (); + if (p) { + p->set_visible (visible); + } +} + +lay::EditorOptionsPage * +Service::toolbox_widget () +{ + lay::EditorOptionsPageCollection *eo_pages = view ()->editor_options_pages (); + if (! eo_pages) { + return 0; + } + + auto pages = eo_pages->editor_options_pages (plugin_declaration ()); + for (auto op = pages.begin (); op != pages.end (); ++op) { + if ((*op)->is_toolbox_widget ()) { + return *op; + } + } + + return 0; +} + // ------------------------------------------------------------- // Implementation of EditableSelectionIterator diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 814e4db93..be535ac4c 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -650,11 +650,33 @@ protected: return m_mouse_pos; } + int mouse_buttons () const + { + return m_mouse_buttons; + } + /** * @brief Commits the current configuration to the recent attributes list */ void commit_recent (); + /** + * @brief Shows the toolbox widget in case one is registered for this plugin + */ + void show_toolbox (bool visible); + + /** + * @brief Finishes the edit operation + * + * Calls do_finish_edit() and terminates the editing operation. + */ + void finish_editing (); + + /** + * @brief Gets the toolbox widget or 0 if none is registered + */ + lay::EditorOptionsPage *toolbox_widget (); + /** * @brief Point snapping with detailed return value */ @@ -685,9 +707,12 @@ private: // The marker representing the object to be edited std::vector m_edit_markers; - // The last mouse position + // The last mouse position of the current mouse move/press/click event db::DPoint m_mouse_pos; + // The buttons flag of the current mouse move/press/click event + int m_mouse_buttons; + // A flag indicating whether the mouse is inside the view bool m_mouse_in_view;