From 91ab72e8e5d8a55b273da59a2ef4b21ed6fe313d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 2 Feb 2026 22:05:45 +0100 Subject: [PATCH] Toolkit widget solution enhanced - Bring text and path options to front when changing mode - Path width and text string can be configure in toolkit widget - Enter key finishes move and partial move --- src/edt/edt/edtEditorOptionsPages.cc | 173 ++++++++++++++++++- src/edt/edt/edtEditorOptionsPages.h | 64 ++++++- src/edt/edt/edtPartialService.cc | 11 ++ src/edt/edt/edtPartialService.h | 5 + src/edt/edt/edtPathService.cc | 5 + src/edt/edt/edtTextService.cc | 6 + src/laybasic/laybasic/layEditorOptionsPage.h | 168 +++++++++++++++++- src/laybasic/laybasic/layMove.cc | 13 ++ src/laybasic/laybasic/layMove.h | 1 + src/layview/layview/layEditorOptionsPages.cc | 18 +- 10 files changed, 447 insertions(+), 17 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 47988b0fe..a32d84b09 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -976,7 +976,7 @@ BoxToolboxWidget::configure (const std::string &name, const std::string &value) } // ------------------------------------------------------------------ -// Connections toolbox widget (for paths and polygons) +// Connections toolbox widget (for polygons) ConnectionToolboxWidget::ConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) : lay::EditorOptionsPageWidget (view, dispatcher), m_in_commit (false) @@ -1056,6 +1056,174 @@ ConnectionToolboxWidget::configure (const std::string &name, const std::string & } } +// ------------------------------------------------------------------ +// Connections toolbox widget (for paths) + +PathConnectionToolboxWidget::PathConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher), m_in_commit (false) +{ + mp_layout = new QHBoxLayout (this); + + mp_x_le = new lay::DecoratedLineEdit (this); + mp_x_le->set_label ("dx:"); + mp_layout->addWidget (mp_x_le); + + mp_y_le = new lay::DecoratedLineEdit (this); + mp_y_le->set_label ("dy:"); + mp_layout->addWidget (mp_y_le); + + mp_width = new lay::DecoratedLineEdit (this); + mp_width->set_label ("w:"); + mp_layout->addWidget (mp_width); + + mp_layout->addStretch (1); + + hide (); + + set_toolbox_widget (true); + set_transparent (true); +} + +PathConnectionToolboxWidget::~PathConnectionToolboxWidget () +{ + // .. nothing yet .. +} + +std::string +PathConnectionToolboxWidget::title () const +{ + return "Path Connection Options"; +} + +void +PathConnectionToolboxWidget::deactivated () +{ + hide (); +} + +void +PathConnectionToolboxWidget::commit (lay::Dispatcher *dispatcher) +{ + m_in_commit = true; + + try { + + if (mp_x_le->hasFocus () || mp_y_le->hasFocus ()) { + + 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 (ShapeEditService::connection_function_name (), db::DVector (dx, dy).to_string ()); + + } else if (mp_width->hasFocus ()) { + + double w = 0.0; + tl::from_string (tl::to_string (mp_width->text ()), w); + + // NOTE: going the way through "configure" makes the width part of the recent path configuration + dispatcher->config_set (cfg_edit_path_width, tl::to_string (w)); + + } + + } catch (...) { + } + + m_in_commit = false; +} + +void +PathConnectionToolboxWidget::configure (const std::string &name, const std::string &value) +{ + if (name == ShapeEditService::connection_configure_name () && + ((! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) || m_in_commit)) { + + 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 (...) { + } + + } else if (name == cfg_edit_path_width && + (! mp_width->hasFocus () || m_in_commit)) { + + try { + + double w = 0.0; + tl::from_string (value, w); + + mp_width->setText (tl::to_qstring (tl::micron_to_string (w))); + + } catch (...) { + } + + } +} + +// ------------------------------------------------------------------ +// Connections toolbox widget (for texts) + +TextToolboxWidget::TextToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher), m_in_commit (false) +{ + mp_layout = new QHBoxLayout (this); + + mp_text = new lay::DecoratedLineEdit (this); + mp_text->set_label ("l:"); + mp_layout->addWidget (mp_text); + + mp_layout->addStretch (1); + + hide (); + + set_toolbox_widget (true); + set_transparent (true); +} + +TextToolboxWidget::~TextToolboxWidget () +{ + // .. nothing yet .. +} + +std::string +TextToolboxWidget::title () const +{ + return "Text Options"; +} + +void +TextToolboxWidget::deactivated () +{ + hide (); +} + +void +TextToolboxWidget::commit (lay::Dispatcher *dispatcher) +{ + m_in_commit = true; + + try { + dispatcher->config_set (cfg_edit_text_string, tl::to_string (mp_text->text ())); + } catch (...) { + } + + m_in_commit = false; +} + +void +TextToolboxWidget::configure (const std::string &name, const std::string &value) +{ + if (name == cfg_edit_text_string && (! mp_text->hasFocus () || m_in_commit)) { + mp_text->setText (tl::to_qstring (value)); + } +} + // ------------------------------------------------------------------ // Registrations @@ -1069,8 +1237,9 @@ static tl::RegisteredClass s_factory_insts_pc // toolkit widgets static tl::RegisteredClass s_box_tookit_widget_factory (new lay::EditorOptionsPageFactory ("edt::Service(Boxes)"), 0); -static tl::RegisteredClass s_connection_tookit_widget_factory_paths (new lay::EditorOptionsPageFactory ("edt::Service(Paths)"), 0); +static tl::RegisteredClass s_path_connection_tookit_widget_factory (new lay::EditorOptionsPageFactory ("edt::Service(Paths)"), 0); static tl::RegisteredClass s_connection_tookit_widget_factory_polygons (new lay::EditorOptionsPageFactory ("edt::Service(Polygons)"), 0); +static tl::RegisteredClass s_text_tookit_widget_factory (new lay::EditorOptionsPageFactory ("edt::Service(Texts)"), 0); } diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 23ae0e59c..6f2f7bfac 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -76,7 +76,7 @@ public: ~EditorOptionsGeneric (); virtual std::string title () const; - virtual int order () const { return 0; } + virtual int order () const { return -10; } void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); @@ -99,7 +99,7 @@ public: ~EditorOptionsText (); virtual std::string title () const; - virtual int order () const { return 10; } + virtual int order () const { return 0; } void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); @@ -120,7 +120,7 @@ public: ~EditorOptionsPath (); virtual std::string title () const; - virtual int order () const { return 30; } + virtual int order () const { return 0; } void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); @@ -144,7 +144,7 @@ public: ~EditorOptionsInst (); virtual std::string title () const; - virtual int order () const { return 20; } + virtual int order () const { return 0; } void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); @@ -176,7 +176,7 @@ public: ~EditorOptionsInstPCellParam (); virtual std::string title () const; - virtual int order () const { return 21; } + virtual int order () const { return 1; } void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); @@ -207,7 +207,7 @@ public: ~BoxToolboxWidget (); virtual std::string title () const; - virtual int order () const { return 0; } + virtual int order () const { return 200; } virtual void configure (const std::string &name, const std::string &value); virtual void commit (lay::Dispatcher *root); virtual void deactivated (); @@ -218,7 +218,7 @@ private: }; /** - * @brief The toolbox widget for connections (path, polygon edges) + * @brief The toolbox widget for connections (polygon edges) */ class ConnectionToolboxWidget : public lay::EditorOptionsPageWidget @@ -230,7 +230,7 @@ public: ~ConnectionToolboxWidget (); virtual std::string title () const; - virtual int order () const { return 0; } + virtual int order () const { return 200; } virtual void configure (const std::string &name, const std::string &value); virtual void commit (lay::Dispatcher *root); virtual void deactivated (); @@ -241,6 +241,54 @@ private: bool m_in_commit; }; +/** + * @brief The toolbox widget for connections (path segments) + */ +class PathConnectionToolboxWidget + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + PathConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~PathConnectionToolboxWidget (); + + virtual std::string title () const; + virtual int order () const { return 200; } + 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, *mp_width; + bool m_in_commit; +}; + +/** + * @brief The toolbox widget for texts + */ +class TextToolboxWidget + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + TextToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~TextToolboxWidget (); + + virtual std::string title () const; + virtual int order () const { return 200; } + 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_text; + bool m_in_commit; +}; + } #endif diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index b5b8ca700..716452db7 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2283,6 +2283,17 @@ PartialService::mouse_release_event (const db::DPoint &p, unsigned int buttons, return false; } +bool +PartialService::key_event (unsigned int key, unsigned int buttons) +{ + if (m_moving && buttons == 0 && (key == lay::KeyEnter || key == lay::KeyReturn)) { + mp_view->move_service ()->end_move (); + return true; + } else { + return false; + } +} + bool PartialService::begin_move (MoveMode mode, const db::DPoint &p, lay::angle_constraint_type ac) { diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 7d3c55d49..d49361264 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -234,6 +234,11 @@ public: */ virtual bool mouse_release_event (const db::DPoint &p, unsigned int buttons, bool prio); + /** + * @brief Key handling event + */ + virtual bool key_event (unsigned int key, unsigned int buttons); + /** * @brief Transforms the selection * diff --git a/src/edt/edt/edtPathService.cc b/src/edt/edt/edtPathService.cc index 9ecc36992..81ba86886 100644 --- a/src/edt/edt/edtPathService.cc +++ b/src/edt/edt/edtPathService.cc @@ -729,6 +729,11 @@ PathService::pop_segment () bool PathService::configure (const std::string &name, const std::string &value) { + auto tb = toolbox_widget (); + if (tb) { + tb->configure (name, value); + } + if (name == cfg_edit_path_width) { tl::from_string (value, m_width); m_needs_update = true; diff --git a/src/edt/edt/edtTextService.cc b/src/edt/edt/edtTextService.cc index 9734a7af8..b95919369 100644 --- a/src/edt/edt/edtTextService.cc +++ b/src/edt/edt/edtTextService.cc @@ -25,6 +25,7 @@ #include "layLayoutViewBase.h" #include "layConverters.h" +#include "layEditorOptionsPage.h" #if defined(HAVE_QT) # include "edtPropertiesPages.h" @@ -199,6 +200,11 @@ TextService::selection_applies (const lay::ObjectInstPath &sel) const bool TextService::configure (const std::string &name, const std::string &value) { + auto tb = toolbox_widget (); + if (tb) { + tb->configure (name, value); + } + if (name == cfg_edit_text_size) { double size (0); tl::from_string (value, size); diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 239224f62..af88422b2 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -67,43 +67,180 @@ public: }; /** - * @brief The base class for a object properties page + * @brief The base class for a editor options page + * + * The object properties page is shown in the editor options panel + * for the active plugin. + * + * Pages can be toolbox widgets, i.e. they are shown in the drawing area + * at the top of the canvas, instead of being shown in the editor options + * panel. */ class LAYBASIC_PUBLIC EditorOptionsPage : public tl::Object { public: + /** + * @brief Constructor + */ EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + + /** + * @brief Default constructor + */ EditorOptionsPage (); + + /** + * @brief Destructor + */ virtual ~EditorOptionsPage (); + /** + * @brief The title of the page + * This title is used for the tab title the page appears + */ virtual std::string title () const = 0; + + /** + * @brief The order in which the pages appear + * This index specifies the position of the page. The page with the + * lower index appears left. + * The page with order 0 is the default page, picked when the plugin + * becomes active. + */ virtual int order () const = 0; + + /** + * @brief The page name + * Giving a page name allows looking up a page by name. + * The page name is optional and if not specified, a zero pointer + * should be returned. + */ virtual const char *name () const { return 0; } + + /** + * @brief A callback to apply all values + * The page is expected to issue "config_set" calls to the dispatcher + * to deliver the settings. + * This callback is not used for toolbox widgets. + */ virtual void apply (lay::Dispatcher * /*root*/) { } - virtual void cancel () { } - virtual void commit (lay::Dispatcher * /*root*/) { } + + /** + * @brief A callback to setup the page + * This callback is expected to set up the page values from + * the configuration, stored inside the dispatcher. + * This callback is not used for toolbox widgets. + */ virtual void setup (lay::Dispatcher * /*root*/) { } + + /** + * @brief A callback to cancel the page edits + * This callback is used for toolbox widgets if the user presses + * "Escape". + */ + virtual void cancel () { } + + /** + * @brief A callback to commit the values + * This callback is used for toolbox widgets if the user presses + * "Enter". It can either commit values to the dispatcher + * through "config_set", or perform other functions. + */ + virtual void commit (lay::Dispatcher * /*root*/) { } + + /** + * @brief Configure the page + * This interface can be used by plugin implementation to transfer + * data from the plugin to a toolbox widget. This method is not + * used by the system directly. + */ virtual void configure (const std::string & /*name*/, const std::string & /*value*/) { } + + /** + * @brief Called by the system to commit the current settings into some "recently used" list + */ virtual void commit_recent (lay::Dispatcher * /*root*/) { } + + /** + * @brief Called by the system to restore recent settings for a given layer + */ virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { } + + /** + * @brief Sets the focus to the page + * This function is called by the system to establish the focus on this page. + */ virtual void set_focus () { } + + /** + * @brief Returns the widget for the page + * The page itself is not a Qt object. To fetch the corresponding widget, use this method. + */ virtual EditorOptionsPageWidget *widget () { return 0; } + /** + * @brief Gets a value indicating whether the page is visible + */ virtual bool is_visible () const { return false; } + + /** + * @brief Changes the visibility of the page + */ virtual void set_visible (bool /*visible*/) { } + /** + * @brief Returns a flag indicating whether the page is a focus page + * Focus pages are pages that are activated when the user presses the Tab + * key in the canvas. Toolbox widgets receive the focus and modal pages + * are shown modally. + */ bool is_focus_page () const { return m_focus_page; } + + /** + * @brief Sets a flag indicating whether the page is a focus page + */ void set_focus_page (bool f) { m_focus_page = f; } + /** + * @brief Returns a flag indicating whether the page is a modal page + * Modal pages are shown in a modal dialog when they receive the focus. + * Otherwise they remain invisible. + */ bool is_modal_page () const { return m_modal_page; } + + /** + * @brief Sets a flag indicating whether the page is a modal page + */ void set_modal_page (bool f) { m_modal_page = f; } + /** + * @brief Returns a flag indicating whether the page is a toolbox widget + */ bool is_toolbox_widget () const { return m_toolbox_widget; } + + /** + * @brief Sets a flag indicating whether the page is a toolbox widget + */ void set_toolbox_widget (bool f) { m_toolbox_widget = f; } + /** + * @brief Gets a value indicating whether the page is active + * A page is active when the corresponding plugin is active + */ bool active () const { return m_active; } + + /** + * @brief Activates a page + * This function is called when the system activates a page because the + * corresponding plugin was activate. + */ void activate (bool active); + + /** + * @brief Sets the owner of the page + * This function is used by the system and must not be used otherwise. + */ void set_owner (EditorOptionsPageCollection *owner); /** @@ -112,35 +249,56 @@ public: */ int show (); + /** + * @brief Gets a value indicating whether the page is for that specific plugin (given by a declaration object) + */ bool for_plugin_declaration (const lay::PluginDeclaration *pd) { return m_plugin_declarations.find (pd) != m_plugin_declarations.end (); } + /** + * @brief Sets the plugin the page is associated with + * This function is used by the system and must not be used otherwise. + */ void set_plugin_declaration (const lay::PluginDeclaration *pd) { m_plugin_declarations.clear (); m_plugin_declarations.insert (pd); } + /** + * @brief Sets the plugins the page is associated with + * This function is used by the system and must not be used otherwise. + */ void set_plugin_declarations (const std::vector &pd) { m_plugin_declarations.clear (); m_plugin_declarations.insert (pd.begin (), pd.end ()); } - void init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); - + /** + * @brief Gets the dispatcher the page is connected to + */ lay::Dispatcher *dispatcher () const { return mp_dispatcher; } + /** + * @brief Gets the view the page is connected to + */ lay::LayoutViewBase *view () const { return mp_view; } + /** + * @brief Initializes the page + * This function is used by the system and must not be used otherwise. + */ + void init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + protected: virtual void active_cellview_changed () { } virtual void technology_changed (const std::string & /*tech*/) { } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 19d222f8e..c946b0950 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -117,6 +117,11 @@ MoveService::key_event (unsigned int key, unsigned int buttons) return true; } + if (buttons == 0 && (key == lay::KeyEnter || key == lay::KeyReturn)) { + end_move (); + return true; + } + double dx = 0.0, dy = 0.0; if (int (key) == lay::KeyDown) { dy = -1.0; @@ -369,6 +374,14 @@ MoveService::start_move (db::Transaction *transaction, bool transient_selection) return handle_click (pstart, 0, drag_transient, trans_holder.release ()); } +void +MoveService::end_move () +{ + if (m_dragging) { + handle_click (m_mouse_pos, 0, false, 0); + } +} + bool MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction) { diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index ee4869bcf..64b3aadca 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -45,6 +45,7 @@ public: ~MoveService (); bool start_move (db::Transaction *transaction = 0, bool transient_selection = false); + void end_move (); bool configure (const std::string &name, const std::string &value); void function (const std::string &name, const std::string &value); diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index d1fd9ecee..9461b8488 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -162,14 +162,28 @@ EditorOptionsPages::activate (const lay::Plugin *plugin) { m_update_enabled = false; + lay::EditorOptionsPage *page = 0; + for (auto op = m_pages.begin (); op != m_pages.end (); ++op) { + BEGIN_PROTECTED - op->activate (plugin && op->for_plugin_declaration (plugin->plugin_declaration ())); + + bool is_active = plugin && op->for_plugin_declaration (plugin->plugin_declaration ()); + + // The zero order page is picked as the initial one + if (is_active && ! op->active () && op->order () == 0 && page == 0) { + page = op.operator-> (); + } + + op->activate (is_active); + END_PROTECTED + } m_update_enabled = true; - update (0); + + update (page); } void