From 68fcb172dc4e019b71d2c521688ab9477c4a722e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 21:01:54 +0000 Subject: [PATCH 01/12] Bump actions/download-artifact from 5 to 6 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9cabc8b6..49eace5f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -122,7 +122,7 @@ jobs: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v6 with: merge-multiple: true path: dist @@ -139,7 +139,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v6 with: merge-multiple: true path: dist From 4ddbff77ea685e4acbc7e7460032d9542c8ca3d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 21:01:57 +0000 Subject: [PATCH 02/12] Bump actions/upload-artifact from 4 to 5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9cabc8b6..42dc669a0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,7 +98,7 @@ jobs: mv ./wheelhouse/.ccache $HOST_CCACHE_DIR ls -la $HOST_CCACHE_DIR ccache -s - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl @@ -113,7 +113,7 @@ jobs: - name: Build SDist run: pipx run build --sdist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: name: artifact-sdist path: dist/*.tar.gz From dd8cc8973f3561ab73a66389068082077f37a73e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Nov 2025 20:35:33 +0100 Subject: [PATCH 03/12] Implemented a solution for issue #2214 - The mouse release event's button mask reflects the keyboard modifiers at the time the button was released - Same for click event - Selection tool and partial selection uses the buttons from the release event - New bitmap mask constants are available to extract the keyboard modifiers and mouse buttons from the button mask. - Documentation was enhanced --- src/edt/edt/edtPartialService.cc | 4 +- src/laybasic/laybasic/gsiDeclLayAdded.cc | 4 ++ src/laybasic/laybasic/gsiDeclLayPlugin.cc | 76 +++++++++++++++-------- src/laybasic/laybasic/laySelector.cc | 8 +-- src/laybasic/laybasic/laySelector.h | 1 - src/laybasic/laybasic/layViewObject.cc | 22 ++++--- src/laybasic/laybasic/layViewObject.h | 14 +++-- 7 files changed, 81 insertions(+), 48 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index c1d80a2bb..b056b5692 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2315,8 +2315,8 @@ PartialService::mouse_release_event (const db::DPoint &p, unsigned int buttons, if (ui ()->mouse_event_viewport ().contains (p)) { lay::Editable::SelectionMode mode = lay::Editable::Replace; - bool shift = ((m_buttons & lay::ShiftButton) != 0); - bool ctrl = ((m_buttons & lay::ControlButton) != 0); + bool shift = ((buttons & lay::ShiftButton) != 0); + bool ctrl = ((buttons & lay::ControlButton) != 0); if (shift && ctrl) { mode = lay::Editable::Invert; } else if (shift) { diff --git a/src/laybasic/laybasic/gsiDeclLayAdded.cc b/src/laybasic/laybasic/gsiDeclLayAdded.cc index 32cb1c54d..13f0b4f88 100644 --- a/src/laybasic/laybasic/gsiDeclLayAdded.cc +++ b/src/laybasic/laybasic/gsiDeclLayAdded.cc @@ -84,11 +84,15 @@ class ButtonStateNamespace { }; static int const_ShiftButton() { return (int) lay::ShiftButton; } static int const_ControlButton() { return (int) lay::ControlButton; } static int const_AltButton() { return (int) lay::AltButton; } +static int const_ModifierMask() { return (int) lay::ModifierMask; } static int const_LeftButton() { return (int) lay::LeftButton; } static int const_MidButton() { return (int) lay::MidButton; } static int const_RightButton() { return (int) lay::RightButton; } +static int const_MouseButtonMask() { return (int) lay::MouseButtonMask; } Class decl_ButtonState ("lay", "ButtonState", + method ("ModifierMask", &const_ModifierMask, "@brief A bit mask that selects all keyboard modifiers in the button state mask\nThis constant has been introduced in version 0.30.6.") + + method ("MouseButtonMask", &const_MouseButtonMask, "@brief A bit mask that selects all mouse buttons in the button state mask\nThis constant has been introduced in version 0.30.6.") + method ("ShiftKey", &const_ShiftButton, "@brief Indicates that the Shift key is pressed\nThis constant is combined with other constants within \\ButtonState") + method ("ControlKey", &const_ControlButton, "@brief Indicates that the Control key is pressed\nThis constant is combined with other constants within \\ButtonState") + method ("AltKey", &const_AltButton, "@brief Indicates that the Alt key is pressed\nThis constant is combined with other constants within \\ButtonState") + diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.cc b/src/laybasic/laybasic/gsiDeclLayPlugin.cc index 28d5245b3..ef3d696dc 100644 --- a/src/laybasic/laybasic/gsiDeclLayPlugin.cc +++ b/src/laybasic/laybasic/gsiDeclLayPlugin.cc @@ -125,58 +125,76 @@ static void drag_cancel_impl (lay::EditorServiceBase *p) Class decl_PluginBase ("lay", "PluginBase", gsi::method_ext ("tracking_position", &tracking_position_impl, - "@brief Gets the tracking position (base class implementation)" + "@brief Gets the tracking position (base class implementation)\n" + "See \\Plugin#tracking_position for details." ) + gsi::method_ext ("has_tracking_position", &has_tracking_position_impl, - "@brief Gets a value indicating whether the plugin provides a tracking position (base class implementation)" + "@brief Gets a value indicating whether the plugin provides a tracking position (base class implementation)\n" + "See \\Plugin#has_tracking_position for details." ) + gsi::method_ext ("menu_activated", &menu_activated_impl, gsi::arg ("symbol"), - "@brief Gets called when a custom menu item is selected (base class implementation)" + "@brief Gets called when a custom menu item is selected (base class implementation)\n" + "See \\Plugin#menu_activated for details." ) + gsi::method_ext ("configure", &configure_impl, gsi::arg ("name"), gsi::arg ("value"), - "@brief Sends configuration requests to the plugin (base class implementation)" + "@brief Sends configuration requests to the plugin (base class implementation)\n" + "See \\Plugin#configure for details." ) + gsi::method_ext ("config_finalize", &config_finalize_impl, - "@brief Sends the post-configuration request to the plugin (base class implementation)" + "@brief Sends the post-configuration request to the plugin (base class implementation)\n" + "See \\Plugin#config_finalize for details." ) + gsi::method_ext ("key_event", &key_event_impl, gsi::arg ("key"), gsi::arg ("buttons"), - "@brief Handles the key pressed event (base class implementation)" + "@brief Handles the key pressed event (base class implementation)\n" + "See \\Plugin#key_event for details." ) + gsi::method_ext ("mouse_button_pressed_event", &mouse_press_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), - "@brief Handles the mouse button pressed event (base class implementation)" + "@brief Handles the mouse button pressed event (base class implementation)\n" + "See \\Plugin#mouse_button_pressed_event for details." ) + gsi::method_ext ("mouse_click_event", &mouse_click_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), - "@brief Handles the mouse button click event after the button has been released (base class implementation)" + "@brief Handles the mouse button click event after the button has been released (base class implementation)\n" + "See \\Plugin#mouse_click_event for details." ) + gsi::method_ext ("mouse_double_click_event", &mouse_double_click_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), - "@brief Handles the mouse button double-click event (base class implementation)" + "@brief Handles the mouse button double-click event (base class implementation)\n" + "See \\Plugin#mouse_double_click_event for details." ) + gsi::method_ext ("leave_event", &leave_event_impl, gsi::arg ("prio"), - "@brief Handles the leave event (base class implementation)" + "@brief Handles the leave event (base class implementation)\n" + "See \\Plugin#leave_event for details." ) + gsi::method_ext ("enter_event", &enter_event_impl, gsi::arg ("prio"), - "@brief Handles the enter event (base class implementation)" + "@brief Handles the enter event (base class implementation)\n" + "See \\Plugin#enter_event for details." ) + gsi::method_ext ("mouse_moved_event", &mouse_move_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), - "@brief Handles the mouse move event (base class implementation)" + "@brief Handles the mouse move event (base class implementation)\n" + "See \\Plugin#mouse_moved_event for details." ) + gsi::method_ext ("mouse_button_released_event", &mouse_release_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), - "@brief Handles the mouse button release event (base class implementation)" + "@brief Handles the mouse button release event (base class implementation)\n" + "See \\Plugin#mouse_button_released_event for details." ) + gsi::method_ext ("wheel_event", &wheel_event_impl, gsi::arg ("delta"), gsi::arg ("horizontal"), gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), - "@brief Handles the mouse wheel event (base class implementation)" + "@brief Handles the mouse wheel event (base class implementation)\n" + "See \\Plugin#wheel_event for details." ) + gsi::method_ext ("activated", &activated_impl, - "@brief Gets called when the plugin is activated (base class implementation)" + "@brief Gets called when the plugin is activated (base class implementation)\n" + "See \\Plugin#activated for details." ) + gsi::method_ext ("deactivated", &deactivated_impl, - "@brief Gets called when the plugin is deactivated and another plugin is activated (base class implementation)" + "@brief Gets called when the plugin is deactivated and another plugin is activated (base class implementation)\n" + "See \\Plugin#deactivated for details." ) + gsi::method_ext ("drag_cancel", &drag_cancel_impl, - "@brief This method is called when some mouse dragging operation should be cancelled (base class implementation)" + "@brief This method is called when some mouse dragging operation should be cancelled (base class implementation)\n" + "See \\Plugin#drag_cancel for details." ) + gsi::method_ext ("update", &update_impl, - "@brief Gets called when the view has changed (base class implementation)" + "@brief Gets called when the view has changed (base class implementation)\n" + "See \\Plugin#update for details." ), "@brief The plugin base class\n" "\n" @@ -735,36 +753,44 @@ Class decl_Plugin (decl_PluginBase, "lay", "Plugin", ) + callback ("mouse_click_event", &gsi::PluginImpl::mouse_click_event_noref, &gsi::PluginImpl::f_mouse_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button click event (after the button has been released)\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button has been released without moving it.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse button has been released without moving it.\n" + "A mouse click is not defined by duration, but by releasing a button without moving the mouse after the button was pressed. " + "As a consequence, a \\mouse_button_pressed_event is always issued at the beginning, but it is not followed by a \\mouse_button_released_event.\n" + "Instead, the 'mouse_click_event' is issued.\n" + "\n" + "Starting with version 0.30.6, the button mask reflects the keyboard modifiers at the moment the mouse was released. Before, the keyboard modifiers were " + "captured at the moment when the mouse was pressed." ) + callback ("mouse_double_click_event", &gsi::PluginImpl::mouse_double_click_event_noref, &gsi::PluginImpl::f_mouse_double_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button double-click event\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button has been double-clicked.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse button has been double-clicked.\n" ) + callback ("leave_event", &gsi::PluginImpl::leave_event, &gsi::PluginImpl::f_leave_event, gsi::arg ("prio"), "@brief Handles the leave event (mouse leaves canvas area of view)\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse leaves the canvas area.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse leaves the canvas area.\n" "This method does not have a position nor button flags.\n" ) + callback ("enter_event", &gsi::PluginImpl::enter_event, &gsi::PluginImpl::f_enter_event, gsi::arg ("prio"), "@brief Handles the enter event (mouse enters canvas area of view)\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse enters the canvas area.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse enters the canvas area.\n" "This method does not have a position nor button flags.\n" ) + callback ("mouse_moved_event", &gsi::PluginImpl::mouse_move_event_noref, &gsi::PluginImpl::f_mouse_move_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse move event\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse is moved in the canvas area.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse is moved in the canvas area.\n" "\n" "The mouse move event is important for a number of background jobs, such as coordinate display in the status bar.\n" "Hence, you should not consume the event - i.e. you should return 'false' from this method.\n" ) + callback ("mouse_button_released_event", &gsi::PluginImpl::mouse_release_event_noref, &gsi::PluginImpl::f_mouse_release_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button release event\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button is released.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse button is released.\n" + "Starting with version 0.30.6, the button mask reflects the keyboard modifiers at the moment the mouse was released. Before, the keyboard modifiers were " + "captured at the moment when the mouse was pressed." ) + callback ("wheel_event", &gsi::PluginImpl::wheel_event_noref, &gsi::PluginImpl::f_wheel_event, gsi::arg ("delta"), gsi::arg ("horizontal"), gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse wheel event\n" - "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse wheel is rotated.\n" + "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse wheel is rotated.\n" "Additional parameters for this event are 'delta' (the rotation angle in units of 1/8th degree) and 'horizontal' which is true when the horizontal wheel was rotated and " "false if the vertical wheel was rotated.\n" ) + diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc index 8a86a6048..b5e7cf4cb 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -47,7 +47,6 @@ SelectionService::SelectionService (lay::LayoutViewBase *view) : mp_view (view), mp_box (0), m_color (0), - m_buttons (0), m_hover (false), m_hover_wait (false), m_mouse_in_window (false) @@ -204,7 +203,6 @@ SelectionService::mouse_press_event (const db::DPoint &p, unsigned int buttons, if ((buttons & lay::LeftButton) != 0) { mp_view->stop_redraw (); // TODO: how to restart if selection is aborted? - m_buttons = buttons; begin (p); return true; } @@ -263,7 +261,7 @@ SelectionService::mouse_click_event (const db::DPoint &p, unsigned int buttons, } bool -SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio) +SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio) { hover_reset (); @@ -274,8 +272,8 @@ SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int /* if (mp_view) { lay::Editable::SelectionMode mode = lay::Editable::Replace; - bool shift = ((m_buttons & lay::ShiftButton) != 0); - bool ctrl = ((m_buttons & lay::ControlButton) != 0); + bool shift = ((buttons & lay::ShiftButton) != 0); + bool ctrl = ((buttons & lay::ControlButton) != 0); if (shift && ctrl) { mode = lay::Editable::Invert; } else if (shift) { diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h index d4f29dbd3..0d9eee884 100644 --- a/src/laybasic/laybasic/laySelector.h +++ b/src/laybasic/laybasic/laySelector.h @@ -91,7 +91,6 @@ private: lay::LayoutViewBase *mp_view; lay::RubberBox *mp_box; unsigned int m_color; - unsigned int m_buttons; bool m_hover; bool m_hover_wait; db::DPoint m_hover_point; diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index 6efa9f881..488ff5fbb 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -907,7 +907,7 @@ ViewObjectUI::send_mouse_double_clicked_event (const db::DPoint &pt, unsigned in } void -ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*buttons*/) +ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int buttons) { try { @@ -916,23 +916,27 @@ ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*but bool done = false; + // Qt does not include the released button in the mask, so we take the mouse buttons that we stored + // on "press", but use the current modifiers (issue #2214) + unsigned int effective_buttons = (m_mouse_buttons & lay::MouseButtonMask) | (buttons & lay::ModifierMask); + m_mouse_pos = pt; db::DPoint p = pixel_to_um (m_mouse_pos); auto grabbed = m_grabbed; for (auto g = grabbed.begin (); !done && g != grabbed.end (); ++g) { if (m_mouse_pressed_state) { - done = (*g)->enabled () && (*g)->mouse_click_event (p, m_mouse_buttons, true); + done = (*g)->enabled () && (*g)->mouse_click_event (p, effective_buttons, true); } else { - done = (*g)->enabled () && (*g)->mouse_release_event (p, m_mouse_buttons, true); + done = (*g)->enabled () && (*g)->mouse_release_event (p, effective_buttons, true); } } if (! done && mp_active_service && mp_active_service->enabled ()) { if (m_mouse_pressed_state) { - done = mp_active_service->mouse_click_event (p, m_mouse_buttons, true); + done = mp_active_service->mouse_click_event (p, effective_buttons, true); } else { - done = mp_active_service->mouse_release_event (p, m_mouse_buttons, true); + done = mp_active_service->mouse_release_event (p, effective_buttons, true); } } @@ -942,9 +946,9 @@ ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*but ++next; if ((*svc)->enabled ()) { if (m_mouse_pressed_state) { - done = (*svc)->mouse_click_event (p, m_mouse_buttons, false); + done = (*svc)->mouse_click_event (p, effective_buttons, false); } else { - done = (*svc)->mouse_release_event (p, m_mouse_buttons, false); + done = (*svc)->mouse_release_event (p, effective_buttons, false); } } svc = next; @@ -952,9 +956,9 @@ ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*but if (! done) { if (m_mouse_pressed_state) { - mouse_click_event (p, m_mouse_buttons); + mouse_click_event (p, effective_buttons); } else { - mouse_release_event (p, m_mouse_buttons); + mouse_release_event (p, effective_buttons); } } diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index a175e5c5d..6ef9b691e 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -548,12 +548,14 @@ private: * @brief Describes the button state (supposed to be ored) */ enum ButtonState { - ShiftButton = 1, - ControlButton = 2, - AltButton = 4, - LeftButton = 8, - MidButton = 16, - RightButton = 32 + ShiftButton = 0x01, + ControlButton = 0x02, + AltButton = 0x04, + ModifierMask = 0x07, // all keyboard modifiers + LeftButton = 0x08, + MidButton = 0x10, + RightButton = 0x20, + MouseButtonMask = 0x38 // all mouse buttons }; /** From 12b74517a66aad74e834cc840aab47e72e8c3ee0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Nov 2025 22:55:40 +0100 Subject: [PATCH 04/12] Issue #2219 (drawing of empty cells) 1. Optimiziation - not more than one empty cell per pixel This is not very efficient on the available test cases. 2. Introducing a virtual dimension for the empty cells for the purpose of label scaling. With this, the labels are scaled (if a scalable font is selected) and not drawn when becoming very small. The parameter is found in the setup pages under Display/Cells. --- src/laybasic/laybasic/layLayoutViewBase.cc | 19 +- src/laybasic/laybasic/layLayoutViewBase.h | 16 +- src/laybasic/laybasic/layLayoutViewConfig.cc | 1 + .../laybasic/layRedrawThreadWorker.cc | 246 +++++++++++------- src/laybasic/laybasic/layRedrawThreadWorker.h | 5 +- src/laybasic/laybasic/laybasicConfig.h | 1 + src/layui/layui/LayoutViewConfigPage2a.ui | 246 +++++++++++------- src/layui/layui/layLayoutViewConfigPages.cc | 10 + 8 files changed, 348 insertions(+), 196 deletions(-) diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c0a5b7879..8be8e2767 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -342,6 +342,7 @@ LayoutViewBase::init (db::Manager *mgr) m_box_text_transform = true; m_box_font = 0; m_min_size_for_label = 16; + m_empty_cell_dimension = 4.0; m_cell_box_visible = true; m_ghost_cells_visible = true; m_text_visible = true; @@ -944,6 +945,13 @@ LayoutViewBase::configure (const std::string &name, const std::string &value) min_inst_label_size (n); return true; + } else if (name == cfg_empty_cell_dimension) { + + double n; + tl::from_string (value, n); + empty_cell_dimension (n); + return true; + } else if (name == cfg_cell_box_text_font) { int n; @@ -5403,7 +5411,16 @@ LayoutViewBase::min_inst_label_size (int px) } } -void +void +LayoutViewBase::empty_cell_dimension (double um) +{ + if (m_empty_cell_dimension != um) { + m_empty_cell_dimension = um; + redraw (); + } +} + +void LayoutViewBase::text_visible (bool vis) { if (m_text_visible != vis) { diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index b0ad6d28e..87e7544ef 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -1149,7 +1149,20 @@ public: return m_min_size_for_label; } - /** + /** + * @brief Empty cell dimension for the purpose of label generation setter + */ + void empty_cell_dimension (double um); + + /** + * @brief Empty cell dimension for the purpose of label generation getter + */ + int empty_cell_dimension () const + { + return m_empty_cell_dimension; + } + + /** * @brief Visibility of text objects */ void text_visible (bool vis); @@ -2941,6 +2954,7 @@ private: bool m_box_text_transform; unsigned int m_box_font; int m_min_size_for_label; + double m_empty_cell_dimension; bool m_cell_box_visible; bool m_ghost_cells_visible; diff --git a/src/laybasic/laybasic/layLayoutViewConfig.cc b/src/laybasic/laybasic/layLayoutViewConfig.cc index 7f7908bd8..ebf2b899e 100644 --- a/src/laybasic/laybasic/layLayoutViewConfig.cc +++ b/src/laybasic/laybasic/layLayoutViewConfig.cc @@ -55,6 +55,7 @@ public: options.push_back (std::pair (cfg_current_lib_view, "")); options.push_back (std::pair (cfg_hide_empty_layers, "false")); options.push_back (std::pair (cfg_min_inst_label_size, "16")); + options.push_back (std::pair (cfg_empty_cell_dimension, "4")); options.push_back (std::pair (cfg_cell_box_text_font, "0")); options.push_back (std::pair (cfg_cell_box_text_transform, "true")); options.push_back (std::pair (cfg_cell_box_color, "auto")); diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index d8a332b50..0be5fc16b 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -40,6 +40,90 @@ static inline db::Box safe_transformed_box (const db::Box &box, const db::ICplxT return db::Box (db); } +static bool +has_zero_bit (const lay::Bitmap *bitmap, unsigned int ixmin, unsigned int iymin, unsigned int ixmax, unsigned int iymax) +{ + uint32_t imin = ixmin / 32; + uint32_t imax = ixmax / 32; + + if (imin == imax) { + + uint32_t m = ((unsigned int) 0xffffffff << (ixmin % 32)) & ((unsigned int) 0xffffffff >> (31 - (ixmax % 32))); + + for (unsigned int y = iymin; y <= iymax; ++y) { + + if (bitmap->is_scanline_empty (y)) { + return true; + } + + if ((bitmap->scanline (y) [imin] & m) != m) { + return true; + } + + } + + } else { + + uint32_t m1 = ((unsigned int) 0xffffffff << (ixmin % 32)); + uint32_t m2 = ((unsigned int) 0xffffffff >> (31 - (ixmax % 32))); + + for (unsigned int y = iymin; y <= iymax; ++y) { + + if (bitmap->is_scanline_empty (y)) { + return true; + } + + if ((bitmap->scanline (y) [imin] & m1) != m1) { + return true; + } + for (unsigned int i = imin + 1; i < imax; ++i) { + if (bitmap->scanline (y) [i] != 0xffffffff) { + return true; + } + } + if ((bitmap->scanline (y) [imax] & m2) != m2) { + return true; + } + + } + + } + + return false; +} + +static bool +skip_quad (const db::Box &qb, const lay::Bitmap *vertex_bitmap, const db::CplxTrans &trans) +{ + double threshold = 32 / trans.mag (); // only check cells below 32x32 pixels + if (qb.empty () || qb.width () > threshold || qb.height () > threshold || !vertex_bitmap) { + return false; + } + + db::DBox qb_trans = (trans * qb) & db::DBox (0, 0, vertex_bitmap->width () - 1.0 - 1e-6, vertex_bitmap->height () - 1.0 - 1e-6); + if (qb_trans.empty ()) { + return true; + } + + int ixmin = (unsigned int)(qb_trans.left () + 0.5); + int ixmax = (unsigned int)(qb_trans.right () + 0.5); + int iymin = (unsigned int)(qb_trans.bottom () + 0.5); + int iymax = (unsigned int)(qb_trans.top () + 0.5); + if (! has_zero_bit (vertex_bitmap, ixmin, iymin, ixmax, iymax)) { + return true; // skip + } else { + return false; + } +} + +inline void +copy_bitmap (const lay::Bitmap *from, lay::Bitmap *to, int dx, int dy) +{ + if (to) { + to->merge (from, dx, dy); + } +} + // ------------------------------------------------------------- // RedrawThreadWorker implementation @@ -61,6 +145,7 @@ RedrawThreadWorker::RedrawThreadWorker (RedrawThread *redraw_thread) m_box_text_transform = false; m_box_font = 0; m_min_size_for_label = 1; + m_empty_cell_dimension = 1.0; m_text_font = 0; m_text_visible = false; m_text_lazy_rendering = false; @@ -600,6 +685,7 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con m_from_level_default = view->get_hier_levels ().first; m_to_level_default = view->get_hier_levels ().second; m_min_size_for_label = view->min_inst_label_size (); + m_empty_cell_dimension = view->empty_cell_dimension (); m_box_text_transform = view->cell_box_text_transform (); m_box_font = view->cell_box_text_font (); m_text_font = view->text_font (); @@ -680,7 +766,7 @@ RedrawThreadWorker::test_snapshot (const UpdateSnapshotCallback *update_snapshot } void -RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt) +RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const db::Box &box_for_label, bool empty_cell, const std::string &txt, lay::Bitmap *opt_bitmap) { lay::Renderer &r = *mp_renderer; @@ -699,13 +785,17 @@ RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTr if (empty_cell) { r.draw (dbox, 0, contour, vertices, 0); + if (opt_bitmap) { + r.draw (dbox, 0, 0, opt_bitmap, 0); + } } else { r.draw (dbox, fill, contour, 0, 0); } - if (! txt.empty () && (empty_cell || (dbox.width () > m_min_size_for_label && dbox.height () > m_min_size_for_label))) { + db::DBox dbox_for_label = trans * box_for_label; + if (! txt.empty () && (empty_cell || (dbox_for_label.width () > m_min_size_for_label && dbox_for_label.height () > m_min_size_for_label))) { // Hint: we render to contour because the texts plane is reserved for properties - r.draw (dbox, txt, + r.draw (dbox_for_label, txt, db::Font (m_box_font), db::HAlignCenter, db::VAlignCenter, @@ -817,34 +907,58 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c return; } + std::unique_ptr opt_bitmap; + + unsigned int plane_group = 2; + if (drawing_context) { + plane_group = 0; + } else if (m_child_context_enabled && level + 1 > 0) { + plane_group = 1; + } + + lay::CanvasPlane *vertex = m_planes[3 + plane_group * (planes_per_layer / 3)]; + lay::Bitmap *vertex_bitmap = dynamic_cast (vertex); + if (vertex_bitmap) { + opt_bitmap.reset (new lay::Bitmap (vertex_bitmap->width (), vertex_bitmap->height (), vertex_bitmap->resolution (), vertex_bitmap->font_resolution ())); + } + for (std::vector::const_iterator b = redraw_regions.begin (); b != redraw_regions.end (); ++b) { - draw_boxes_impl (drawing_context, ci, trans, *b, level, for_ghosts); + draw_boxes_impl (drawing_context, ci, trans, *b, level, for_ghosts, opt_bitmap.get ()); } } void -RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, bool for_ghosts) +RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, bool for_ghosts, lay::Bitmap *opt_bitmap) { lay::Renderer &r = *mp_renderer; const db::Cell &cell = mp_layout->cell (ci); + // small cells are dropped + if (m_drop_small_cells && drop_cell (cell, trans)) { + return; + } + // For small bboxes, the cell outline can be reduced .. db::Box bbox = cell.bbox_with_empty (); bool empty_cell = cell.bbox ().empty (); - if (m_drop_small_cells && drop_cell (cell, trans)) { + db::Box bbox_for_label; + if (empty_cell) { + db::Coord d = db::coord_traits::rounded (0.5 * m_empty_cell_dimension / mp_layout->dbu ()); + bbox_for_label = bbox.enlarged (db::Vector (d, d)); + } else { + bbox_for_label = bbox; + } - // small cell dropped - - } else if (for_ghosts && cell.is_ghost_cell ()) { + if (for_ghosts && cell.is_ghost_cell ()) { // paint the box on this level - draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci)); + draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap); } else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) { // paint the box on this level - draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci)); + draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap); } else if (level < m_to_level) { @@ -855,7 +969,7 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c // the cell is a very small box and we know there must be // some level at which to draw the boundary: just draw it // here and stop looking further down .. - draw_cell (drawing_context, level, trans, bbox, empty_cell, std::string ()); + draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, std::string (), opt_bitmap); } } else { @@ -884,6 +998,8 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c bool anything = false; db::cell_index_type last_ci = std::numeric_limits::max (); + size_t current_quad_id = 0; + db::Cell::touching_iterator inst = cell.begin_touching (*v); while (! inst.at_end ()) { @@ -893,6 +1009,20 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c db::Box new_cell_box = cell_inst.bbox (bc); bool empty_inst_cell = mp_layout->cell (new_ci).bbox ().empty (); + // skip this quad if we have drawn something here already + size_t qid = inst.quad_id (); + bool skip = false; + if (empty_inst_cell && qid != current_quad_id) { + current_quad_id = qid; + skip = opt_bitmap && skip_quad (inst.quad_box () & bbox, opt_bitmap, trans); + } + + if (skip) { + // move on to the next quad + inst.skip_quad (); + continue; + } + if (last_ci != new_ci) { // Hint: don't use any_cell_box on partially visible cells because that will degrade performance if (new_cell_box.inside (*v)) { @@ -940,6 +1070,9 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c if (empty_inst_cell) { lay::CanvasPlane *vertices = m_planes[3 + plane_group * (planes_per_layer / 3)]; r.draw (cell_inst.bbox (bc), trans, 0, 0, vertices, 0); + if (opt_bitmap) { + r.draw (cell_inst.bbox (bc), trans, opt_bitmap, 0, 0, 0); + } } lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)]; @@ -955,9 +1088,10 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) { test_snapshot (0); + db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = safe_transformed_box (*v, t.inverted ()); - draw_boxes_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, for_ghosts); + draw_boxes_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, for_ghosts, opt_bitmap); if (p.quad_id () > 0 && p.quad_id () != qid) { @@ -1292,91 +1426,7 @@ RedrawThreadWorker::any_text_shapes (db::cell_index_type cell_index, unsigned in return c->second; } -static bool -has_zero_bit (const lay::Bitmap *bitmap, unsigned int ixmin, unsigned int iymin, unsigned int ixmax, unsigned int iymax) -{ - uint32_t imin = ixmin / 32; - uint32_t imax = ixmax / 32; - - if (imin == imax) { - - uint32_t m = ((unsigned int) 0xffffffff << (ixmin % 32)) & ((unsigned int) 0xffffffff >> (31 - (ixmax % 32))); - - for (unsigned int y = iymin; y <= iymax; ++y) { - - if (bitmap->is_scanline_empty (y)) { - return true; - } - - if ((bitmap->scanline (y) [imin] & m) != m) { - return true; - } - - } - - } else { - - uint32_t m1 = ((unsigned int) 0xffffffff << (ixmin % 32)); - uint32_t m2 = ((unsigned int) 0xffffffff >> (31 - (ixmax % 32))); - - for (unsigned int y = iymin; y <= iymax; ++y) { - - if (bitmap->is_scanline_empty (y)) { - return true; - } - - if ((bitmap->scanline (y) [imin] & m1) != m1) { - return true; - } - for (unsigned int i = imin + 1; i < imax; ++i) { - if (bitmap->scanline (y) [i] != 0xffffffff) { - return true; - } - } - if ((bitmap->scanline (y) [imax] & m2) != m2) { - return true; - } - - } - - } - - return false; -} - -static bool -skip_quad (const db::Box &qb, const lay::Bitmap *vertex_bitmap, const db::CplxTrans &trans) -{ - double threshold = 32 / trans.mag (); // don't check cells below 32x32 pixels - if (qb.empty () || qb.width () > threshold || qb.height () > threshold || !vertex_bitmap) { - return false; - } - - db::DBox qb_trans = (trans * qb) & db::DBox (0, 0, vertex_bitmap->width () - 1.0 - 1e-6, vertex_bitmap->height () - 1.0 - 1e-6); - if (qb_trans.empty ()) { - return true; - } - - int ixmin = (unsigned int)(qb_trans.left () + 0.5); - int ixmax = (unsigned int)(qb_trans.right () + 0.5); - int iymin = (unsigned int)(qb_trans.bottom () + 0.5); - int iymax = (unsigned int)(qb_trans.top () + 0.5); - if (! has_zero_bit (vertex_bitmap, ixmin, iymin, ixmax, iymax)) { - return true; // skip - } else { - return false; - } -} - -inline void -copy_bitmap (const lay::Bitmap *from, lay::Bitmap *to, int dx, int dy) -{ - if (to) { - to->merge (from, dx, dy); - } -} - -std::vector +std::vector RedrawThreadWorker::search_regions (const db::Box &cell_bbox, const db::Box &vp, int level) { std::vector vv; diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index 4f108c587..7b97b7eae 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -185,13 +185,13 @@ private: void draw_boxes_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts); - void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, bool for_ghosts); + void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, bool for_ghosts, Bitmap *opt_bitmap); void draw_box_properties_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts); void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, db::properties_id_type prop_id, bool for_ghosts); void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, db::properties_id_type prop_id, bool for_ghosts); - void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt); + void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const db::Box &box_for_label, bool empty_cell, const std::string &txt, Bitmap *opt_bitmap); void draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id); void draw_cell_shapes (const db::CplxTrans &trans, const db::Cell &cell, const db::Box &vp, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text); void test_snapshot (const UpdateSnapshotCallback *update_snapshot); @@ -216,6 +216,7 @@ private: int m_from_level, m_to_level; int m_from_level_default, m_to_level_default; int m_min_size_for_label; + double m_empty_cell_dimension; bool m_box_text_transform; unsigned int m_box_font; unsigned int m_text_font; diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 681c3c8ac..e5f3a3777 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -91,6 +91,7 @@ static const std::string cfg_crosshair_cursor_enabled ("crosshair-cursor-enabled static const std::string cfg_markers_visible ("markers-visible"); static const std::string cfg_min_inst_label_size ("min-inst-label-size"); +static const std::string cfg_empty_cell_dimension ("empty-cell-dimension"); static const std::string cfg_cell_box_text_font ("inst-label-font"); static const std::string cfg_cell_box_text_transform ("inst-label-transform"); static const std::string cfg_cell_box_color ("inst-color"); diff --git a/src/layui/layui/LayoutViewConfigPage2a.ui b/src/layui/layui/LayoutViewConfigPage2a.ui index 23bc34c44..0dbfdb25f 100644 --- a/src/layui/layui/LayoutViewConfigPage2a.ui +++ b/src/layui/layui/LayoutViewConfigPage2a.ui @@ -7,7 +7,7 @@ 0 0 656 - 397 + 451 @@ -49,85 +49,14 @@ false - - 9 - 6 - - - - QFrame::NoFrame + + + + Label font - - QFrame::Raised - - - - 6 - - - 0 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 141 - 21 - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 6 - - - 0 - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - @@ -190,6 +119,20 @@ + + + + Cell box color + + + + + + + Transform text with cell instance (not available for "Default" font) + + + @@ -197,21 +140,7 @@ - - - - Label font - - - - - - - Cell box color - - - - + Qt::Vertical @@ -227,10 +156,139 @@ - - + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 141 + 21 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + µm (for label scaling) + + + + + + + Qt::Horizontal + + + + 159 + 20 + + + + + + + + + - Transform text with cell instance (not available for "Default" font) + Empty cell virtual size diff --git a/src/layui/layui/layLayoutViewConfigPages.cc b/src/layui/layui/layLayoutViewConfigPages.cc index 55a766913..145d2476f 100644 --- a/src/layui/layui/layLayoutViewConfigPages.cc +++ b/src/layui/layui/layLayoutViewConfigPages.cc @@ -227,6 +227,10 @@ LayoutViewConfigPage2a::setup (lay::Dispatcher *root) root->config_get (cfg_min_inst_label_size, n); mp_ui->cell_min_size_for_label_edit->setText (tl::to_qstring (tl::to_string (n))); + double ecd = 0; + root->config_get (cfg_empty_cell_dimension, ecd); + mp_ui->empty_cell_dimension->setText (tl::to_qstring (tl::to_string (ecd))); + bool gs_visible = true; root->config_get (cfg_guiding_shape_visible, gs_visible); mp_ui->pcell_gs_group->setChecked (gs_visible); @@ -263,6 +267,12 @@ LayoutViewConfigPage2a::commit (lay::Dispatcher *root) tl::from_string_ext (tl::to_string (mp_ui->cell_min_size_for_label_edit->text ()), n); root->config_set (cfg_min_inst_label_size, n); } catch (...) { } + + try { + double ecd; + tl::from_string_ext (tl::to_string (mp_ui->empty_cell_dimension->text ()), ecd); + root->config_set (cfg_empty_cell_dimension, ecd); + } catch (...) { } } // ------------------------------------------------------------ From 7026cad2c13e17aed80836285c9ec3a2ae4f62b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Nov 2025 22:35:16 +0100 Subject: [PATCH 05/12] Fixing issue #2220 (select fails to select from 2nd layout when two are loaded in same panel) --- src/laybasic/laybasic/layFinder.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 4056d8050..700253032 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -805,8 +805,12 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db void InstFinder::checkpoint () { - if (--m_tries < 0) { - throw StopException (); + if (! point_mode ()) { + ++*mp_progress; + } else { + if (--m_tries < 0) { + throw StopException (); + } } } @@ -829,15 +833,13 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d if (! point_mode ()) { - ++*mp_progress; - // look for instances to check here .. for (db::Cell::touching_iterator inst = cell.begin_touching (search_box); ! inst.at_end (); ++inst) { const db::CellInstArray &cell_inst = inst->cell_inst (); const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ()); - ++*mp_progress; + checkpoint (); if (! consider_cell (inst_cell)) { continue; @@ -851,7 +853,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d db::box_convert bc (layout ()); for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) { - ++*mp_progress; + checkpoint (); db::Box ibox; if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { From bbaaa14b2c8ef686b454823a5286ff0b77c300ad Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Nov 2025 23:57:40 +0100 Subject: [PATCH 06/12] Bringing 'save all' on the same level than 'same' - Taking the technology's settings for save options - Deploying "libname", e.g. for GDS2 --- src/lay/lay/layMainWindow.cc | 43 ++++++++++++++++------------ src/laybasic/laybasic/layCellView.cc | 26 ----------------- src/laybasic/laybasic/layCellView.h | 9 ------ 3 files changed, 25 insertions(+), 53 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index b54499056..b1fa11141 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2277,6 +2277,29 @@ MainWindow::cm_save_as () do_save (true); } +static db::SaveLayoutOptions +get_save_options_from_cv (const lay::CellView &cv) +{ + db::SaveLayoutOptions options = cv->save_options (); + if (!cv->save_options_valid () && cv->technology ()) { + options = cv->technology ()->save_layout_options (); + options.set_format (cv->save_options ().format ()); + } + + // preconfigure options with current values + + options.set_dbu (cv->layout ().dbu ()); + + if (cv->layout ().has_meta_info ("libname")) { + tl::Variant libname = cv->layout ().meta_info ("libname").value; + if (libname.is_a_string ()) { + options.set_libname (libname.to_stdstring ()); + } + } + + return options; +} + void MainWindow::do_save (bool as) { @@ -2309,22 +2332,7 @@ MainWindow::do_save (bool as) // - if the layout's save options are valid we take the options from there, otherwise we take the options from the technology // - on "save as" we let the user edit the options - db::SaveLayoutOptions options = cv->save_options (); - if (!cv->save_options_valid () && cv->technology ()) { - options = cv->technology ()->save_layout_options (); - options.set_format (cv->save_options ().format ()); - } - - // preconfigure options with current values - - options.set_dbu (cv->layout ().dbu ()); - - if (cv->layout ().has_meta_info ("libname")) { - tl::Variant libname = cv->layout ().meta_info ("libname").value; - if (libname.is_a_string ()) { - options.set_libname (libname.to_stdstring ()); - } - } + db::SaveLayoutOptions options = get_save_options_from_cv (cv); if (as || options.format ().empty ()) { options.set_format_from_filename (fn); @@ -2362,8 +2370,7 @@ MainWindow::cm_save_all () if (! fn.empty () || mp_layout_fdia->get_save (fn, tl::to_string (tr ("Save Layout '%1'").arg (tl::to_qstring (cv->name ()))))) { - db::SaveLayoutOptions options (cv->save_options ()); - options.set_dbu (cv->layout ().dbu ()); + db::SaveLayoutOptions options = get_save_options_from_cv (cv); if (options.format ().empty ()) { options.set_format_from_filename (fn); diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc index 84cbde12c..4249f91db 100644 --- a/src/laybasic/laybasic/layCellView.cc +++ b/src/laybasic/laybasic/layCellView.cc @@ -300,32 +300,6 @@ LayoutHandle::set_save_options (const db::SaveLayoutOptions &options, bool valid m_save_options_valid = valid; } -void -LayoutHandle::update_save_options (db::SaveLayoutOptions &options) -{ -#if defined(HAVE_QT) - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - - const lay::StreamWriterPluginDeclaration *decl = dynamic_cast (&*cls); - if (! decl || decl->options_alias ()) { - continue; - } - - std::unique_ptr specific_options; - if (options.get_options (decl->format_name ())) { - specific_options.reset (options.get_options (decl->format_name ())->clone ()); - } else { - specific_options.reset (decl->create_specific_options ()); - } - - if (specific_options.get ()) { - options.set_options (specific_options.release ()); - } - - } -#endif -} - void LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update, int keep_backups) { diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h index 661d6c8c1..0b5adadd1 100644 --- a/src/laybasic/laybasic/layCellView.h +++ b/src/laybasic/laybasic/layCellView.h @@ -242,15 +242,6 @@ public: return m_save_options; } - /** - * @brief Updates the given save options with attributes from this cell view - * - * Some formats will initialize attributes from the cell view and the layout's - * metadata (example: libname of GDS2). This method will update the options - * if the layout provides attributes for initializing the latter. - */ - void update_save_options (db::SaveLayoutOptions &options); - /** * @brief Gets a flag indicating whether the save options are valid * From bc835adf648fe86ef159f16ce9ad2b61bfbc4082 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Nov 2025 21:57:13 +0100 Subject: [PATCH 07/12] Fixed issue #2221 (restoring 'save' behavior on 'save all') 'save all' now behaves like 'save' on all layouts by respecting the application-wide writer options. In addition, a weird feature was fixed: when using 'save' for the first time, old versions were freezing the writer options and changing the application-wide options did not have an effect on views after they were saved the first time. Now, this behavior only applies to "save as" and a popup is shown in the top of the layout view indicating that from now on the special options apply. This way, special options can be picked in "save as" and they will remain valid during the next "save". --- src/lay/lay/layMainWindow.cc | 21 ++++--- src/laybasic/laybasic/layCellView.cc | 2 - src/laybasic/laybasic/layLayoutViewBase.h | 72 ++++++++++++++++++++++ src/layview/layview/layLayoutView_qt.cc | 8 +++ src/layview/layview/layLayoutView_qt.h | 75 +++-------------------- 5 files changed, 100 insertions(+), 78 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index b1fa11141..9b1b12c60 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2345,7 +2345,18 @@ MainWindow::do_save (bool as) } current_view ()->save_as ((unsigned int) cv_index, fn, om, options, true, m_keep_backups); - add_mru (fn, current_view ()->cellview (cv_index)->tech_name ()); + add_mru (fn, cv->tech_name ()); + + if (as) { + + lay::LayoutViewNotification n ("reload", tl::to_string (tr ("The next 'save' operations will use the writer options you have picked, instead of the application-wide ones."))); + current_view ()->add_notification (n); + + // freeze writer options in the 'save_as' case, so we can do another "save" and get the + // selected options again + cv->set_save_options (options, true); + + } } @@ -2378,14 +2389,6 @@ MainWindow::cm_save_all () tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; - // initialize the specific options from the configuration if required - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const StreamWriterPluginDeclaration *decl = dynamic_cast (&*cls); - if (decl) { - options.set_options (decl->create_specific_options ()); - } - } - view (view_index)->save_as (cv_index, fn, om, options, true, m_keep_backups); add_mru (fn, cv->tech_name ()); diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc index 4249f91db..df8fb73a3 100644 --- a/src/laybasic/laybasic/layCellView.cc +++ b/src/laybasic/laybasic/layCellView.cc @@ -305,8 +305,6 @@ LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode { if (update) { - m_save_options = options; - m_save_options_valid = true; // We must not load with the original options after we have saved the file - hence we reset the // reader options. m_load_options = db::LoadLayoutOptions (); diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index b0ad6d28e..db17f7148 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -149,6 +149,70 @@ struct LAYBASIC_PUBLIC LayerDisplayProperties std::string name; }; +/** + * @brief Descriptor for a notification inside the layout view + * + * Notifications are popups added at the top of the view to indicate need for reloading for example. + * Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload). + * Actions are mapped to QPushButtons. + */ +class LAYBASIC_PUBLIC LayoutViewNotification +{ +public: + LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ()) + : m_name (name), m_title (title), m_parameter (parameter) + { + // .. nothing yet .. + } + + void add_action (const std::string &name, const std::string &title) + { + m_actions.push_back (std::make_pair (name, title)); + } + + const std::vector > &actions () const + { + return m_actions; + } + + const std::string &name () const + { + return m_name; + } + + const std::string &title () const + { + return m_title; + } + + const tl::Variant ¶meter () const + { + return m_parameter; + } + + bool operator<(const LayoutViewNotification &other) const + { + if (m_name != other.name ()) { + return m_name < other.name (); + } + return m_parameter < other.parameter (); + } + + bool operator==(const LayoutViewNotification &other) const + { + if (m_name != other.name ()) { + return false; + } + return m_parameter == other.parameter (); + } + +private: + std::string m_name; + std::string m_title; + tl::Variant m_parameter; + std::vector > m_actions; +}; + /** * @brief The layout view object * @@ -279,6 +343,14 @@ public: */ virtual void cut (); + /** + * @brief Adds a notification + */ + virtual void add_notification (const LayoutViewNotification & /*notification*/) + { + // the base implementation does nothing + } + /** * @brief Gets the explicit title string of the view * diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index 55cad6857..b58214835 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -453,6 +453,14 @@ LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool edit LayoutView::set_active_cellview_index (source->active_cellview_index ()); } +void +LayoutView::add_notification (const LayoutViewNotification ¬ification) +{ + if (mp_widget) { + mp_widget->add_notification (notification); + } +} + bool LayoutView::event_filter (QObject *obj, QEvent *event, bool &taken) { diff --git a/src/layview/layview/layLayoutView_qt.h b/src/layview/layview/layLayoutView_qt.h index 837146a9a..6f2b3f0f8 100644 --- a/src/layview/layview/layLayoutView_qt.h +++ b/src/layview/layview/layLayoutView_qt.h @@ -1,4 +1,4 @@ - +# /* KLayout Layout Viewer @@ -149,6 +149,11 @@ public: */ ~LayoutView (); + /** + * @brief Adds a notification + */ + virtual void add_notification (const LayoutViewNotification ¬ification); + /** * @brief Gets the widget object that view is embedded in */ @@ -695,70 +700,6 @@ private: void activate_editor_option_pages (); }; -/** - * @brief Descriptor for a notification inside the layout view - * - * Notifications are popups added at the top of the view to indicate need for reloading for example. - * Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload). - * Actions are mapped to QPushButtons. - */ -class LAYVIEW_PUBLIC LayoutViewNotification -{ -public: - LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ()) - : m_name (name), m_title (title), m_parameter (parameter) - { - // .. nothing yet .. - } - - void add_action (const std::string &name, const std::string &title) - { - m_actions.push_back (std::make_pair (name, title)); - } - - const std::vector > &actions () const - { - return m_actions; - } - - const std::string &name () const - { - return m_name; - } - - const std::string &title () const - { - return m_title; - } - - const tl::Variant ¶meter () const - { - return m_parameter; - } - - bool operator<(const LayoutViewNotification &other) const - { - if (m_name != other.name ()) { - return m_name < other.name (); - } - return m_parameter < other.parameter (); - } - - bool operator==(const LayoutViewNotification &other) const - { - if (m_name != other.name ()) { - return false; - } - return m_parameter == other.parameter (); - } - -private: - std::string m_name; - std::string m_title; - tl::Variant m_parameter; - std::vector > m_actions; -}; - /** * @brief A widget representing a notification */ @@ -810,12 +751,12 @@ public: /** * @brief Adds a notification */ - void add_notification (const LayoutViewNotification ¬ificaton); + void add_notification (const LayoutViewNotification ¬ification); /** * @brief Removes a notification */ - void remove_notification (const LayoutViewNotification ¬ificaton); + void remove_notification (const LayoutViewNotification ¬ification); /** * @brief Gets the LayoutView embedded into this widget From 6c4ac26b26f7c86f649c9f7e5fa11755ae0682c1 Mon Sep 17 00:00:00 2001 From: Maxim Cournoyer Date: Mon, 1 Dec 2025 09:20:18 +0900 Subject: [PATCH 08/12] COPYRIGHT: Update license notice to that of GPL-3-or-later. Fixes: #2233 --- COPYRIGHT | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index d01b4e3df..4516b586a 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -7,18 +7,19 @@ Authors: Copyright: Copyright (C) 2006-2025 by Matthias Köfferlein. - 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 or 3 of the License. + 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 3 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. + 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 package; if not, write to the Free Software Foundation, - Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + along with this program. If not, see + . On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. From 0be510f3251c4b888457412701de4d8ebdb49e14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 23:49:14 +0000 Subject: [PATCH 09/12] Bump pypa/cibuildwheel from 3.2.1 to 3.3.0 Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.2.1 to 3.3.0. - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/v3.2.1...v3.3.0) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-version: 3.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbe9bf596..a6f7bfb3f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,7 +66,7 @@ jobs: mkdir -p $HOST_CCACHE_DIR - name: Build wheels (ARM) if: matrix.os == 'ubuntu-24.04-arm' - uses: pypa/cibuildwheel@v3.2.1 + uses: pypa/cibuildwheel@v3.3.0 env: # override the default CentOS “yum install … ccache” and drop ccache CIBW_BEFORE_ALL_LINUX: | @@ -81,7 +81,7 @@ jobs: - name: Build wheels (all other platforms) if: matrix.os != 'ubuntu-24.04-arm' - uses: pypa/cibuildwheel@v3.2.1 + uses: pypa/cibuildwheel@v3.3.0 env: CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} From 148a93286c5431f6c2a40a882af59bd8010be11c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 23:49:18 +0000 Subject: [PATCH 10/12] Bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- .github/workflows/cache-maintenance.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbe9bf596..5a8124459 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: - uses: hmarr/debug-action@v3 - name: Cancel Workflow Action uses: styfle/cancel-workflow-action@0.12.1 - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: ccache if: matrix.os != 'ubuntu-24.04-arm' uses: hendrikmuhs/ccache-action@v1.2 @@ -108,7 +108,7 @@ jobs: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build SDist run: pipx run build --sdist diff --git a/.github/workflows/cache-maintenance.yml b/.github/workflows/cache-maintenance.yml index 33d9f5d62..f57190c28 100644 --- a/.github/workflows/cache-maintenance.yml +++ b/.github/workflows/cache-maintenance.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Cleanup run: | From fc4a8b92b0f69a142d6b2126928cf7d48ddc5496 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 Dec 2025 23:00:07 +0100 Subject: [PATCH 11/12] First bug fixes * Only check for layers in the selected stack - this avoids problems with "masking" pin shapes for example * Use shape transformation for shapes inside hierarchy --- src/edt/edt/edtPartialService.cc | 8 ++-- src/laybasic/laybasic/layFinder.cc | 38 +++++++++++-------- src/laybasic/laybasic/layFinder.h | 12 +++--- .../tools/net_tracer/db_plugin/dbNetTracer.cc | 10 +++++ .../tools/net_tracer/db_plugin/dbNetTracer.h | 7 ++++ .../lay_plugin/layNetTracerDialog.cc | 38 +++++++++++-------- 6 files changed, 71 insertions(+), 42 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index c1d80a2bb..b4d30c4a8 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -762,9 +762,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co { if (! point_mode ()) { - for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { + for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (scan_box))) { checkpoint (); @@ -897,9 +897,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co } else { - for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { + for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (hit_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (hit_box))) { checkpoint (); diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 4056d8050..cd0ae6cc7 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -87,7 +87,7 @@ Finder::closer (double d) } void -Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers) +Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers) { const lay::CellView &cv = view->cellview (cv_index); @@ -100,8 +100,8 @@ Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vect if (layers.size () == 1) { - m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]); - m_cell_box_convert = db::box_convert ((unsigned int) layers [0]); + m_box_convert = db::box_convert (*mp_layout, layers [0]); + m_cell_box_convert = db::box_convert (layers [0]); } else { @@ -202,7 +202,7 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons if (level <= m_max_level /*take level of cell itself*/ && cell.is_proxy () && m_layers.size () == 1 - && (unsigned int) m_layers [0] == mp_layout->guiding_shape_layer ()) { + && m_layers [0] == mp_layout->guiding_shape_layer ()) { // when looking at the guiding shape layer, we can visit this cell as well allowing to find the guiding shapes @@ -339,7 +339,7 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu) std::sort (lprops.begin (), lprops.end (), LPContextCompareOp ()); - std::vector layers; + std::vector layers; for (std::vector::const_iterator llp = lprops.begin (); llp != lprops.end (); ) { layers.clear (); @@ -347,7 +347,10 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu) lay::LayerPropertiesConstIterator lp0 = *llp; LPContextEqualOp eq; do { - layers.push_back ((*llp)->layer_index ()); + int li = (*llp)->layer_index (); + if (li >= 0) { + layers.push_back ((unsigned int) li); + } ++llp; } while (llp != lprops.end () && eq(lp0, *llp)); @@ -398,16 +401,19 @@ ShapeFinder::find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops lay::TextInfo text_info (view); mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0; - std::vector layers; - layers.push_back (lprops.layer_index ()); + std::vector layers; + int li = lprops.layer_index (); + if (li >= 0) { + layers.push_back ((unsigned int) li); + } bool result = find_internal (view, lprops.cellview_index (), &lprops.prop_sel (), lprops.inverse_prop_sel (), lprops.hier_levels (), lprops.trans (), layers, region_mu); mp_progress = 0; return result; } -bool -ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, const std::set *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector &trans_mu, const std::vector &layers, const db::DBox ®ion_mu) +bool +ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, const std::set *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector &trans_mu, const std::vector &layers, const db::DBox ®ion_mu) { m_cv_index = cv_index; @@ -511,13 +517,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: if (! point_mode ()) { - for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { + for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (scan_box))) { checkpoint (); - const db::Shapes &shapes = cell.shapes ((unsigned int) *l); + const db::Shapes &shapes = cell.shapes (*l); db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel); while (! shape.at_end ()) { @@ -563,9 +569,9 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: } else { - for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { + for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) { - if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) { + if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (scan_box))) { checkpoint (); @@ -793,7 +799,7 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db try { std::vector tv; tv.push_back (trans_mu); - start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ()); + start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ()); } catch (StopException) { // .. } diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index eca725dc7..5ebe9152d 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -132,7 +132,7 @@ public: } protected: - const std::vector &layers () const + const std::vector &layers () const { return m_layers; } @@ -183,7 +183,7 @@ protected: * @param max_level The maximum hierarchy level to check * @param layers A set of layers to check */ - void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ()); + void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ()); /** * @brief Provide a basic edge test facility @@ -232,7 +232,7 @@ private: unsigned int m_cv_index; db::Box m_region; db::Box m_scan_region; - std::vector m_layers; + std::vector m_layers; double m_distance; bool m_point_mode; bool m_catch_all; @@ -267,8 +267,8 @@ public: */ ShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator::flags_type flags, const std::set *excludes = 0, bool capture_all_shapes = false); - bool find (LayoutViewBase *view, const lay::LayerProperties &lprops, const db::DBox ®ion_mu); - bool find (LayoutViewBase *view, const db::DBox ®ion_mu); + bool find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops, const db::DBox ®ion_mu); + bool find (lay::LayoutViewBase *view, const db::DBox ®ion_mu); iterator begin () const { @@ -327,7 +327,7 @@ private: bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector &trans_mu, - const std::vector &layers, + const std::vector &layers, const db::DBox ®ion_mu); const std::set *mp_excludes; diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc index 45cc16007..b35abccac 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc @@ -337,6 +337,16 @@ NetTracerData::requires_booleans (unsigned int from_layer) const return r->second; } +std::set +NetTracerData::original_layers () const +{ + std::set ol; + for (std::map >::const_iterator g = m_original_layers.begin (); g != m_original_layers.end (); ++g) { + ol.insert (g->second.begin (), g->second.end ()); + } + return ol; +} + void NetTracerData::clean_l2n_regions () { diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h index ea6412243..1d926ab5a 100644 --- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h +++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h @@ -626,6 +626,13 @@ public: */ void configure_l2n (db::LayoutToNetlist &l2n); + /** + * @brief Gets the original layers + * + * These are the layer indexes used for computing the net tracer functions + */ + std::set original_layers () const; + private: unsigned int m_next_log_layer; std::vector m_connections; diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index 4106392ce..f7b0011bf 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -414,9 +414,24 @@ NetTracerDialog::get_net_tracer_setup (const lay::CellView &cv, db::NetTracerDat db::NetTracerNet * NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path) { + // determine the cellview + lay::CellView cv = view ()->cellview (m_cv_index); + if (! cv.is_valid ()) { + return 0; + } + + // Set up the net tracer environment + db::NetTracerData tracer_data; + if (! get_net_tracer_setup (cv, tracer_data)) { + return 0; + } + + std::set original_layers = tracer_data.original_layers (); + unsigned int start_layer = 0; db::Point start_point; db::Shape start_shape; + db::ICplxTrans start_trans; // locate the seed { @@ -426,7 +441,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto // go through all visible layers of all cellviews and find a seed shape for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) { - if (lprop->is_visual ()) { + if (lprop->is_visual () && lprop->cellview_index () == m_cv_index && original_layers.find ((unsigned int) lprop->layer_index ()) != original_layers.end ()) { finder.find (view (), *lprop, start_search_box); } } @@ -440,15 +455,10 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto m_cv_index = r->cv_index (); start_shape = r->shape (); start_layer = r->layer (); + start_trans = r->trans (); } - // determine the cellview - lay::CellView cv = view ()->cellview (m_cv_index); - if (! cv.is_valid ()) { - return 0; - } - // determine the start point { @@ -463,20 +473,15 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto // stop if the center start point is not inside the start polygon db::Polygon poly; - if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_point) < 0) { + if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_trans.inverted () * start_point) < 0) { return 0; } } - // Set up the net tracer environment - db::NetTracerData tracer_data; - if (! get_net_tracer_setup (cv, tracer_data)) { - return 0; - } - unsigned int stop_layer = 0; db::Point stop_point; + db::ICplxTrans stop_trans; // locate the stop shape (the second mouse click) if (trace_path) { @@ -486,7 +491,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto // go through all visible layers of all cellviews and find a seed shape for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) { - if (lprop->is_visual ()) { + if (lprop->is_visual () && lprop->cellview_index () == m_cv_index && original_layers.find ((unsigned int) lprop->layer_index ()) != original_layers.end ()) { finder.find (view (), *lprop, stop_search_box); } } @@ -510,10 +515,11 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto stop_point = tt.inverted ().trans (stop_search_box.center ()); stop_layer = r->layer (); + stop_trans = r->trans (); // stop if the center stop point is not inside the stop polygon db::Polygon poly; - if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_point) < 0) { + if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_trans.inverted () * stop_point) < 0) { return 0; } From 389a0996fdb40ddeb8d97bcf14382933f009cfa7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 3 Dec 2025 22:11:54 +0100 Subject: [PATCH 12/12] Messages can have priorities now. The net tracer uses higher-priority messages to make the 'click on second/first point' message more persistent. The have infinite lifetime now. --- src/lay/lay/gsiDeclLayMainWindow.cc | 31 +++++++++- src/lay/lay/layMainWindow.cc | 59 +++++++++++++++---- src/lay/lay/layMainWindow.h | 7 ++- src/laybasic/laybasic/layLayoutViewBase.cc | 4 +- src/laybasic/laybasic/layLayoutViewBase.h | 2 +- src/layview/layview/layLayoutView_qt.cc | 4 +- src/layview/layview/layLayoutView_qt.h | 6 +- .../lay_plugin/layNetTracerDialog.cc | 10 ++-- 8 files changed, 93 insertions(+), 30 deletions(-) diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index 9bb58226d..4339aa8bf 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -328,6 +328,12 @@ set_menu_items_hidden (lay::MainWindow *mw, const std::map &h mw->dispatcher ()->config_set (lay::cfg_menu_items_hidden, lay::pack_menu_items_hidden (hv)); } +static void +clear_message (lay::MainWindow *mw, int priority) +{ + mw->message (std::string (), 0, priority); +} + Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "MainWindow", // Dispatcher interface and convenience functions @@ -475,15 +481,36 @@ Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M "\n" "This method has been added in version 0.24." ) + - gsi::method ("message", &lay::MainWindow::message, gsi::arg ("message"), gsi::arg ("time", -1, "infinite"), + gsi::method_ext ("clear_message", &clear_message, gsi::arg ("priority", -1, "all priorities"), + "@brief Clears the message\n" + "When calling this method with a priority, it will clear the message in the given priority slot, thus " + "unhiding the messages with lower priority. This is equivalent to calling \\message with an empty string.\n" + "\n" + "When calling without a priority, all messages in all priority slots will be cleared.\n" + "\n" + "This method has been added in version 0.30.6." + ) + + gsi::method ("message", &lay::MainWindow::message, gsi::arg ("message"), gsi::arg ("time", -1, "infinite"), gsi::arg ("priority", 0), "@brief Displays a message in the status bar\n" "\n" "@param message The message to display\n" "@param time The time how long to display the message in ms. A negative value means 'infinitely'.\n" + "@param priority The priority of the message. Higher-priority messages have precendence over lower-priority ones.\n" "\n" "This given message is shown in the status bar for the given time.\n" + "If a priority is given, higher-priority messages have precedence over lower-priority ones.\n" + "Placing an empty message clears a message with a given priority and unhides messages with lower " + "priority. Standard messages like selection descriptions have priority 0, which is the lowest priority.\n" "\n" - "This method has been added in version 0.18. The 'time' parameter was made optional in version 0.28.10." + "Messages generated during modal actions (e.g. 'Click on first point') by convention should have priority 10 " + "and should be tied to the active state of a plugin. This ensures there is only one such message.\n" + "Higher-priority messages must be cleared (set to empty string) explicitly to unhide lower-priority messages " + "when the indicated action is finished.\n" + "\n" + "\\clear_message is a convenience method that will clear messages.\n" + "\n" + "This method has been added in version 0.18. The 'time' parameter was made optional in version 0.28.10.\n" + "The 'priority' argument has been added in version 0.30.6." ) + gsi::method ("resize", (void (lay::MainWindow::*)(int, int)) &lay::MainWindow::resize, gsi::arg ("width"), gsi::arg ("height"), "@brief Resizes the window\n" diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index b54499056..e787d0dcd 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -186,6 +186,7 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) m_synchronous (false), m_busy (false), mp_app (app), + m_message_timer_priority (-1), m_manager (undo_enabled) { setAnimated (false); @@ -823,10 +824,15 @@ static int fm_width (const QFontMetrics &fm, const QString &s) void MainWindow::format_message () { + std::string msg; + if (! m_messages.empty ()) { + msg = (--m_messages.end ())->second; + } + QFontMetrics fm (mp_msg_label->font ()); std::string full_message; - for (const char *c = m_message.c_str (); *c; ++c) { + for (const char *c = msg.c_str (); *c; ++c) { if (*c == '\\' && (c[1] == '(' || c[1] == ')')) { ++c; } else { @@ -847,7 +853,7 @@ MainWindow::format_message () short_message.clear (); - for (const char *c = m_message.c_str (); *c; ++c) { + for (const char *c = msg.c_str (); *c; ++c) { if (*c == '\\' && c[1] == '(') { if (nsection++ < ndrop) { in_drop = true; @@ -875,24 +881,53 @@ MainWindow::format_message () } void -MainWindow::message (const std::string &s, int ms) +MainWindow::message (const std::string &s, int ms, int priority) { - m_message = s; + if (s.empty () && priority < 0) { + + m_messages.clear (); + + } else if (s.empty ()) { + + m_messages.erase (priority); + + } else { + + // simulate timeout of previous message timer + if (m_message_timer_priority >= 0) { + m_messages.erase (m_message_timer_priority); + } + + m_messages[priority] = s; + + if (ms >= 0) { + m_message_timer_priority = priority; + m_message_timer.start (ms); + } else { + m_message_timer_priority = -1; + } + + } + format_message (); - m_message_timer.start (ms); } void -MainWindow::clear_message () +MainWindow::clear_messages () { - m_message.clear (); - m_message_timer.start (0); + m_message_timer.stop (); + m_message_timer_priority = -1; + m_messages.clear (); + format_message (); } void MainWindow::message_timer () { - m_message.clear (); + if (m_message_timer_priority >= 0) { + m_messages.erase (m_message_timer_priority); + } + m_message_timer_priority = -1; format_message (); } @@ -2457,7 +2492,7 @@ MainWindow::select_view (int index) update_editor_options_dock (); clear_current_pos (); edits_enabled_changed (); - clear_message (); + clear_messages (); menu_needs_update (); m_disable_tab_selected = dis; @@ -2984,7 +3019,7 @@ MainWindow::close_view (int index) clear_current_pos (); edits_enabled_changed (); menu_needs_update (); - clear_message (); + clear_messages (); update_dock_widget_state (); @@ -3469,7 +3504,7 @@ MainWindow::add_view (lay::LayoutViewWidget *view) connect (view, SIGNAL (dirty_changed (lay::LayoutView *)), this, SLOT (view_title_changed (lay::LayoutView *))); connect (view, SIGNAL (edits_enabled_changed ()), this, SLOT (edits_enabled_changed ())); connect (view, SIGNAL (menu_needs_update ()), this, SLOT (menu_needs_update ())); - connect (view, SIGNAL (show_message (const std::string &, int)), this, SLOT (message (const std::string &, int))); + connect (view, SIGNAL (show_message (const std::string &, int, int)), this, SLOT (message (const std::string &, int, int))); connect (view, SIGNAL (current_pos_changed (double, double, bool)), this, SLOT (current_pos (double, double, bool))); connect (view, SIGNAL (clear_current_pos ()), this, SLOT (clear_current_pos ())); connect (view, SIGNAL (mode_change (int)), this, SLOT (select_mode (int))); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 737e24efc..0aab28d1a 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -649,12 +649,12 @@ public slots: /** * @brief Displays a status message next to the coordinates */ - void message (const std::string &s, int ms); + void message (const std::string &s, int ms, int); /** * @brief Clears the current message */ - void clear_message (); + void clear_messages (); /** * @brief Selects the given mode @@ -789,7 +789,8 @@ private: QApplication *mp_app; lay::HelpDialog *mp_assistant; std::string m_current_session; - std::string m_message; + int m_message_timer_priority; + std::map m_messages; std::unique_ptr mp_printer; std::vector m_changed_files; std::string m_title; diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c0a5b7879..b357fb073 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -693,7 +693,7 @@ LayoutViewBase::set_synchronous (bool s) } void -LayoutViewBase::message (const std::string & /*s*/, int /*timeout*/) +LayoutViewBase::message (const std::string & /*s*/, int /*timeout*/, int /*priority*/) { // .. nothing yet .. } @@ -4156,7 +4156,7 @@ void LayoutViewBase::cancel_edits () { // clear any messages - message (); + message (std::string (), 0, -1); // the move service takes a special role here as it manages the // transaction for the collective move operation. diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index b0ad6d28e..4d64b410f 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -293,7 +293,7 @@ public: /** * @brief Display a status message */ - virtual void message (const std::string &s = "", int timeout = 10); + virtual void message (const std::string &s = "", int timeout = 10, int priority = 0); /** * @brief Sets the keyboard focus to the view diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index 55cad6857..4fcc1b810 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -1577,10 +1577,10 @@ LayoutView::signal_selection_changed () } void -LayoutView::message (const std::string &s, int timeout) +LayoutView::message (const std::string &s, int timeout, int priority) { if (mp_widget) { - mp_widget->emit_show_message (s, timeout * 1000); + mp_widget->emit_show_message (s, timeout * 1000, priority); } } diff --git a/src/layview/layview/layLayoutView_qt.h b/src/layview/layview/layLayoutView_qt.h index 837146a9a..26b18593c 100644 --- a/src/layview/layview/layLayoutView_qt.h +++ b/src/layview/layview/layLayoutView_qt.h @@ -182,7 +182,7 @@ public: /** * @brief Displays a status message */ - void message (const std::string &s = "", int timeout = 10); + virtual void message (const std::string &s = "", int timeout = 10, int priority = 0); /** * @brief Sets the keyboard focus to the view @@ -832,7 +832,7 @@ public: void emit_title_changed (lay::LayoutView *view) { emit title_changed (view); } void emit_dirty_changed (lay::LayoutView *view) { emit dirty_changed (view); } - void emit_show_message (const std::string &s, int ms) { emit show_message (s, ms); } + void emit_show_message (const std::string &s, int ms, int priority) { emit show_message (s, ms, priority); } void emit_current_pos_changed (double x, double y, bool dbu_units) { emit current_pos_changed (x, y, dbu_units); } void emit_clear_current_pos () { emit clear_current_pos (); } void emit_edits_enabled_changed () { emit edits_enabled_changed (); } @@ -885,7 +885,7 @@ signals: /** * @brief This signal is emitted when the view wants to show a message for the given time (of infinitely for ms == 0) */ - void show_message (const std::string &s, int ms); + void show_message (const std::string &s, int ms, int priority); /** * @brief This signal is emitted when the view wants to indicate a mouse position change diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index f7b0011bf..68b49a361 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -207,7 +207,7 @@ NetTracerDialog::drag_cancel () { if (m_mouse_state > 0) { - view ()->message (); + view ()->message (std::string (), 0, 10); ui ()->ungrab_mouse (this); set_cursor (lay::Cursor::none); @@ -241,7 +241,7 @@ NetTracerDialog::mouse_click_event (const db::DPoint &p, unsigned int buttons, b m_mouse_first_point = p; m_mouse_state = 3; - view ()->message (tl::to_string (QObject::tr ("Click on the second point in the net"))); + view ()->message (tl::to_string (QObject::tr ("Click on the second point in the net")), -1 /*infinitely*/, 10); } else { @@ -1264,7 +1264,7 @@ BEGIN_PROTECTED commit (); net_list->setCurrentItem (0); m_mouse_state = 2; - view ()->message (tl::to_string (QObject::tr ("Click on the first point in the net"))); + view ()->message (tl::to_string (QObject::tr ("Click on the first point in the net")), -1 /*infinitely*/, 10); ui ()->grab_mouse (this, false); END_PROTECTED } @@ -1276,7 +1276,7 @@ BEGIN_PROTECTED commit (); net_list->setCurrentItem (0); m_mouse_state = 1; - view ()->message (tl::to_string (QObject::tr ("Click on a point in the net"))); + view ()->message (tl::to_string (QObject::tr ("Click on a point in the net")), -1 /*infinitely*/, 10); ui ()->grab_mouse (this, false); END_PROTECTED } @@ -1299,7 +1299,7 @@ NetTracerDialog::release_mouse () add_pb->setChecked (false); add2_pb->setChecked (false); m_mouse_state = 0; - view ()->message (); + view ()->message (std::string (), 0, 10); ui ()->ungrab_mouse (this); set_cursor (lay::Cursor::none); }