From c2c941078df1c24e0862969d487d0d9d83093f48 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Jan 2026 01:05:56 +0100 Subject: [PATCH] Enabling toolkit widgets for rulers --- src/ant/ant/ant.pro | 2 + src/ant/ant/antEditorOptionsPages.cc | 127 ++++++++++++++++++ src/ant/ant/antEditorOptionsPages.h | 68 ++++++++++ src/ant/ant/antService.cc | 90 +++++++++++++ src/ant/ant/antService.h | 15 ++- src/edt/edt/edtBoxService.cc | 10 +- src/edt/edt/edtEditorOptionsPages.cc | 2 +- src/laybasic/laybasic/layMove.cc | 21 +-- src/laybasic/laybasic/layMove.h | 1 + src/layview/layview/layEditorOptionsPages.cc | 22 ++- .../layview/layMoveEditorOptionsPage.cc | 2 +- 11 files changed, 337 insertions(+), 23 deletions(-) create mode 100644 src/ant/ant/antEditorOptionsPages.cc create mode 100644 src/ant/ant/antEditorOptionsPages.h diff --git a/src/ant/ant/ant.pro b/src/ant/ant/ant.pro index c6052dab5..1d1286c73 100644 --- a/src/ant/ant/ant.pro +++ b/src/ant/ant/ant.pro @@ -31,6 +31,7 @@ SOURCES = \ HEADERS += \ antConfig.h \ + antEditorOptionsPages.h \ antObject.h \ antPlugin.h \ antService.h \ @@ -40,6 +41,7 @@ HEADERS += \ SOURCES += \ antConfig.cc \ + antEditorOptionsPages.cc \ antObject.cc \ antPlugin.cc \ antService.cc \ diff --git a/src/ant/ant/antEditorOptionsPages.cc b/src/ant/ant/antEditorOptionsPages.cc new file mode 100644 index 000000000..572b020f2 --- /dev/null +++ b/src/ant/ant/antEditorOptionsPages.cc @@ -0,0 +1,127 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#if defined(HAVE_QT) + +#include "antService.h" +#include "antEditorOptionsPages.h" + +#include "layWidgets.h" +#include "layDispatcher.h" +#include "tlInternational.h" + +#include + +namespace ant +{ + +// ------------------------------------------------------------------ +// Annotations Toolbox widget + +ToolkitWidget::ToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) +{ + 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_layout->addStretch (1); + + hide (); + + set_toolbox_widget (true); + set_transparent (true); +} + +ToolkitWidget::~ToolkitWidget () +{ + // .. nothing yet .. +} + +std::string +ToolkitWidget::title () const +{ + return "Box Options"; +} + +const char * +ToolkitWidget::name () const +{ + return ant::Service::editor_options_name (); +} + +void +ToolkitWidget::deactivated () +{ + hide (); +} + +void +ToolkitWidget::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 (ant::Service::function_name (), db::DVector (dx, dy).to_string ()); + + } catch (...) { + } +} + +void +ToolkitWidget::configure (const std::string &name, const std::string &value) +{ + if (name == ant::Service::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 + +// toolkit widgets +static tl::RegisteredClass s_tookit_widget_factory (new lay::EditorOptionsPageFactory ("ant::Plugin"), 0); + +} + +#endif diff --git a/src/ant/ant/antEditorOptionsPages.h b/src/ant/ant/antEditorOptionsPages.h new file mode 100644 index 000000000..1a28ebf81 --- /dev/null +++ b/src/ant/ant/antEditorOptionsPages.h @@ -0,0 +1,68 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#if defined(HAVE_QT) + +#ifndef HDR_antEditorOptionsPages +#define HDR_antEditorOptionsPages + +#include "layEditorOptionsPageWidget.h" + +class QHBoxLayout; + +namespace lay +{ + class DecoratedLineEdit; +} + +namespace ant +{ + +/** + * @brief The toolbox widget for annotations + */ +class ToolkitWidget + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + ToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~ToolkitWidget (); + + virtual std::string title () const; + virtual const char *name () 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 + +#endif diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 188b092f0..cdfa45719 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -33,6 +33,7 @@ #include "layConverters.h" #include "layLayoutCanvas.h" #include "layFixedFont.h" +#include "layEditorOptionsPage.h" #if defined(HAVE_QT) # include "layProperties.h" #endif @@ -1041,6 +1042,10 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) // ------------------------------------------------------------- // ant::Service implementation +const char *Service::editor_options_name () { return "ant-toolkit-widget-name"; } +const char *Service::configure_name () { return "ant-toolkit-widget-value"; } +const char *Service::function_name () { return "ant-toolkit-widget-commit"; } + Service::Service (db::Manager *manager, lay::LayoutViewBase *view) : lay::EditorServiceBase (view), lay::Drawing (1/*number of planes*/, view->drawings ()), @@ -1169,6 +1174,21 @@ Service::config_finalize () { } +void +Service::show_toolbox (bool visible) +{ + lay::EditorOptionsPage *tb = toolbox_widget (); + if (tb) { + tb->set_visible (visible); + } +} + +lay::EditorOptionsPage * +Service::toolbox_widget () +{ + return mp_view->editor_options_pages () ? mp_view->editor_options_pages ()->page_with_name (editor_options_name ()) : 0; +} + void Service::annotations_changed () { @@ -1244,6 +1264,7 @@ void Service::drag_cancel () { if (m_drawing) { + show_toolbox (false); ui ()->ungrab_mouse (this); m_drawing = false; } @@ -1981,6 +2002,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio mp_active_ruler->thaw (); m_drawing = true; + show_toolbox (true); ui ()->grab_mouse (this, false); } @@ -2033,6 +2055,63 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type } } +void +Service::function (const std::string &name, const std::string &value) +{ + if (name == function_name ()) { + + try { + + db::DVector s; + tl::from_string (value, s); + + if (m_drawing) { + + ant::Object::point_list pts = m_current.points (); + if (pts.size () >= 2) { + + db::DVector d = pts.back () - pts [pts.size () - 2]; + + // Adjust the direction so positive coordinates are in the current drag direction + s = db::DVector (s.x () * (d.x () < 0 ? -1.0 : 1.0), s.y () * (d.y () < 0 ? -1.0 : 1.0)); + + pts.back () = pts [pts.size () - 2] + s; + m_current.set_points_exact (pts); + + } + + const ant::Template &tpl = current_template (); + + if (tpl.mode () == ant::Template::RulerMultiSegment || tpl.mode () == ant::Template::RulerThreeClicks) { + + if (tpl.mode () == ant::Template::RulerThreeClicks && pts.size () == 3) { + + finish_drawing (); + + } else { + + // add a new point + m_p1 = pts.back (); + + pts.push_back (m_p1); + m_current.set_points_exact (pts); + + } + + } else { + + finish_drawing (); + + } + + } + + } catch (...) { + } + + } +} + bool Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { @@ -2074,6 +2153,17 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) } m_current.set_points_exact (pts); + db::DVector delta; + if (pts.size () >= 2) { + delta = pts.back () - pts[pts.size () - 2]; + delta = db::DVector (fabs (delta.x ()), fabs (delta.y ())); + } + + lay::EditorOptionsPage *tb = toolbox_widget (); + if (tb) { + tb->configure (configure_name (), delta.to_string ()); + } + mp_active_ruler->redraw (); show_message (); diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index f270ff914..d8d97b6f8 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -197,6 +197,11 @@ Q_OBJECT public: typedef lay::AnnotationShapes::iterator obj_iterator; + // for communicating with the toolbox widget + static const char *editor_options_name (); + static const char *configure_name (); + static const char *function_name (); + /** * The current move mode: * MoveNone - not moving @@ -503,10 +508,15 @@ public: } /** - * @brief Implement the menu response function + * @brief Implements the menu response function */ void menu_activated (const std::string &symbol); + /** + * @brief Implements the toolbox widget response function + */ + void function (const std::string &name, const std::string &value); + /** * @brief Return the annotation iterator that delivers the annotations (and only these) */ @@ -611,6 +621,9 @@ private: const ant::Template ¤t_template () const; + void show_toolbox (bool visible); + lay::EditorOptionsPage *toolbox_widget (); + void show_message (); /** diff --git a/src/edt/edt/edtBoxService.cc b/src/edt/edt/edtBoxService.cc index 858c6473b..514cc543e 100644 --- a/src/edt/edt/edtBoxService.cc +++ b/src/edt/edt/edtBoxService.cc @@ -80,13 +80,9 @@ BoxService::function (const std::string &name, const std::string &value) 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 ())); - } + // Adjust the direction so positive coordinates are in the current drag direction + db::DVector d = m_p2 - m_p1; + dim = db::DVector (dim.x () * (d.x () < 0 ? -1.0 : 1.0), dim.y () * (d.y () < 0 ? -1.0 : 1.0)); } else { dim = db::DVector (fabs (dim.x ()) * 0.5, fabs (dim.y ()) * 0.5); } diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 38298a260..f17b0b9db 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -987,7 +987,7 @@ static tl::RegisteredClass s_factory_insts (n 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); +static tl::RegisteredClass s_box_tookit_widget_factory (new lay::EditorOptionsPageFactory ("edt::Service(Boxes)"), 0); } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index de39bc3b0..cca542403 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -176,12 +176,18 @@ MoveService::shortcut_override_event (unsigned int key, unsigned int buttons) void MoveService::show_toolbox (bool visible) { - lay::EditorOptionsPage *op = mp_view->editor_options_pages () ? mp_view->editor_options_pages ()->page_with_name (move_editor_options_name) : 0; - if (op) { - op->set_visible (visible); + lay::EditorOptionsPage *tb = toolbox_widget (); + if (tb) { + tb->set_visible (visible); } } +lay::EditorOptionsPage * +MoveService::toolbox_widget () +{ + return mp_view->editor_options_pages () ? mp_view->editor_options_pages ()->page_with_name (move_editor_options_name) : 0; +} + bool MoveService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { @@ -201,12 +207,9 @@ MoveService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool p } mp_view->message (pos); - lay::EditorOptionsPage *toolbox_widget = 0; - if (mp_view->editor_options_pages ()) { - toolbox_widget = mp_view->editor_options_pages ()->page_with_name (move_editor_options_name); - } - if (toolbox_widget) { - toolbox_widget->configure (move_distance_setter_name, pmv.second.disp ().to_string ()); + lay::EditorOptionsPage *tb = toolbox_widget (); + if (tb) { + tb->configure (move_distance_setter_name, pmv.second.disp ().to_string ()); } } diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 233647ae8..ee4869bcf 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -63,6 +63,7 @@ private: virtual void drag_cancel (); virtual void deactivated (); void show_toolbox (bool visible); + lay::EditorOptionsPage *toolbox_widget (); bool handle_click (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction); diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index 904ac825e..a3093e4a0 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -254,10 +254,22 @@ EditorOptionsPages::update (lay::EditorOptionsPage *page) } for (auto p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { - if ((*p)->active ()) { - if ((*p)->is_toolbox_widget ()) { - mp_view->add_toolbox_widget (*p); - } else if (! (*p)->is_modal_page ()) { + + if ((*p)->is_toolbox_widget ()) { + + // NOTE: toolbox widgets are always created, but hidden if the + // page is not active. However, even inactive pages can become + // visible, if needed. The "move" plugin does that if used from + // externally. + if (! (*p)->active ()) { + (*p)->set_visible (false); + } + + mp_view->add_toolbox_widget (*p); + + } else if ((*p)->active ()) { + + if (! (*p)->is_modal_page ()) { if ((*p) == page) { index = mp_pages->count (); } @@ -268,9 +280,11 @@ EditorOptionsPages::update (lay::EditorOptionsPage *page) } mp_modal_pages->add_page (*p); } + } else { (*p)->setParent (0); } + } if (index < 0) { diff --git a/src/layview/layview/layMoveEditorOptionsPage.cc b/src/layview/layview/layMoveEditorOptionsPage.cc index 6bb86c81f..6bec05971 100644 --- a/src/layview/layview/layMoveEditorOptionsPage.cc +++ b/src/layview/layview/layMoveEditorOptionsPage.cc @@ -111,6 +111,6 @@ MoveEditorOptionsPage::configure (const std::string &name, const std::string &va } // registers the factory for the move editor options page -static tl::RegisteredClass s_factory (new lay::EditorOptionsPageFactory (), 0); +static tl::RegisteredClass s_factory (new lay::EditorOptionsPageFactory ("laybasic::MoveServicePlugin"), 0); }