From b454d2ae42acc924ba1512f5cec124363f2d33ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Oct 2025 23:12:22 +0200 Subject: [PATCH] Fixed issue #2201 (trace path) * you can zoom in now to select the end point. Problem was actually that zooming in was a problem when the start point went out of the viewport In addition: * Messages are sticky now ("Click on second point") * "Esc" will cancel path trace mode * The cursor switches back to normal after tracing --- src/edt/edt/edtService.cc | 7 ++++ src/laybasic/laybasic/layFinder.cc | 22 +++++++---- src/laybasic/laybasic/layFinder.h | 20 ++++++++++ src/laybasic/laybasic/layViewObject.cc | 23 +++++------ src/laybasic/laybasic/layViewObject.h | 9 +++++ .../lay_plugin/layNetTracerDialog.cc | 39 ++++++++++++++++++- .../lay_plugin/layNetTracerDialog.h | 2 + 7 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index d0c5e4933..83c164472 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -1327,6 +1327,13 @@ static std::string path_to_string (const db::Layout &layout, const lay::ObjectIn void Service::display_status (bool transient) { + if (transient && view ()->canvas ()->begin_mouse_receivers () != view ()->canvas ()->end_mouse_receivers () + && (*view ()->canvas ()->begin_mouse_receivers ())->claims_message_bar ()) { + // do not display transient if there is a plugin active that has captured the mouse and claims the message bar - + // this way we can use messages in active plugins and still get visual feedback by the transient selection. + return; + } + EditableSelectionIterator r = transient ? begin_transient_selection () : begin_selection (); EditableSelectionIterator rr = r; diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index f3083e2d8..4056d8050 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -63,7 +63,7 @@ static int inst_point_sel_tests = 10000; Finder::Finder (bool point_mode, bool top_level_sel) : m_min_level (0), m_max_level (0), - mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_top_level_sel (top_level_sel) + mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_consider_viewport (true), m_top_level_sel (top_level_sel) { m_distance = std::numeric_limits::max (); } @@ -482,7 +482,10 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: checkpoint (); // Viewport in current cell coordinate space (DBU) - db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + db::Box viewport_box; + if (consider_viewport ()) { + viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + } if (! m_context_layers.empty ()) { @@ -583,7 +586,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: bool any_valid_edge = m_capture_all_shapes; for (db::Shape::polygon_edge_iterator e = shape->begin_edge (); ! e.at_end (); ++e) { - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } @@ -606,7 +609,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: ++pt; for (; pt != shape->end_point (); ++pt) { db::Edge e (p, *pt); - if (e.clipped (viewport_box).first) { + if (viewport_box.empty () || e.clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, e, d, match); } @@ -618,7 +621,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: db::Polygon poly; shape->polygon (poly); for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } @@ -651,7 +654,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: // convert to polygon and test those edges db::Polygon poly (box); for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } @@ -819,7 +822,10 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d checkpoint (); // Viewport in current cell coordinate space (DBU) - db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + db::Box viewport_box; + if (consider_viewport ()) { + viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + } if (! point_mode ()) { @@ -952,7 +958,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d bool any_valid_edge = false; for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { // only consider edges that cut through the viewport - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index f0ecdcc46..eca725dc7 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -96,6 +96,25 @@ public: m_catch_all = f; } + /** + * @brief Gets a flag indicating that the viewport will be considered + */ + bool consider_viewport () const + { + return m_consider_viewport; + } + + /** + * @brief Sets a flag indicating that the viewport will be considered + * If this flag is true (the default), only shapes and instances will be considered + * if edges (or polygons) or boundary edges (for instances) are visible in the + * viewport. If this flag is false, shapes or instances are considered always. + */ + void set_consider_viewport (bool f) + { + m_consider_viewport = f; + } + /** * @brief Destructor (just provided to please the compiler) */ @@ -217,6 +236,7 @@ private: double m_distance; bool m_point_mode; bool m_catch_all; + bool m_consider_viewport; bool m_top_level_sel; db::box_convert m_box_convert; db::box_convert m_cell_box_convert; diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index 3cfa61c04..6efa9f881 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -621,7 +621,10 @@ END_PROTECTED void ViewObjectUI::set_cursor (lay::Cursor::cursor_shape cursor) { - m_cursor = cursor; + if (m_cursor != cursor) { + m_cursor = cursor; + realize_cursor (); + } } void @@ -629,15 +632,7 @@ ViewObjectUI::set_default_cursor (lay::Cursor::cursor_shape cursor) { if (cursor != m_default_cursor) { m_default_cursor = cursor; -#if defined(HAVE_QT) - if (m_cursor == lay::Cursor::none && mp_widget) { - if (m_default_cursor == lay::Cursor::none) { - mp_widget->unsetCursor (); - } else { - mp_widget->setCursor (lay::Cursor::qcursor (m_default_cursor)); - } - } -#endif + realize_cursor (); } } @@ -652,11 +647,17 @@ ViewObjectUI::ensure_entered () void ViewObjectUI::begin_mouse_event (lay::Cursor::cursor_shape cursor) { - m_cursor = cursor; + set_cursor (cursor); } void ViewObjectUI::end_mouse_event () +{ + realize_cursor (); +} + +void +ViewObjectUI::realize_cursor () { #if defined(HAVE_QT) if (mp_widget) { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 7664b8f78..a175e5c5d 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -285,6 +285,14 @@ public: */ virtual void drag_cancel () { } + /** + * @brief Gets a value indicating whether the mouse receiver claims the view message bar + * + * If this method returns true, other services are not supposed to emit transient + * messages. + */ + virtual bool claims_message_bar () const { return false; } + /** * @brief Gets a value indicating whether a cursor position it set */ @@ -1121,6 +1129,7 @@ private: void objects_changed (); int widget_height () const; int widget_width () const; + void realize_cursor (); /** * @brief Register a service diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index 598b1f475..4106392ce 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -202,13 +202,32 @@ NetTracerDialog::item_double_clicked (QListWidgetItem *item) } } +void +NetTracerDialog::drag_cancel () +{ + if (m_mouse_state > 0) { + + view ()->message (); + ui ()->ungrab_mouse (this); + set_cursor (lay::Cursor::none); + + m_mouse_state = 0; + + } +} + +bool +NetTracerDialog::claims_message_bar () const +{ + return true; +} + bool NetTracerDialog::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio) { if (prio && m_mouse_state != 0) { set_cursor (lay::Cursor::cross); } - return false; } @@ -397,11 +416,13 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto { unsigned int start_layer = 0; db::Point start_point; + db::Shape start_shape; // locate the seed { lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All); + finder.set_consider_viewport (false); // go through all visible layers of all cellviews and find a seed shape for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) { @@ -417,7 +438,7 @@ 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 (); } @@ -440,6 +461,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto start_point = tt.inverted ().trans (start_search_box.center ()); + // 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) { + return 0; + } + } // Set up the net tracer environment @@ -455,6 +482,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto if (trace_path) { lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All); + finder.set_consider_viewport (false); // go through all visible layers of all cellviews and find a seed shape for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) { @@ -483,6 +511,12 @@ 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 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) { + return 0; + } + } db::NetTracer net_tracer; @@ -1261,6 +1295,7 @@ NetTracerDialog::release_mouse () m_mouse_state = 0; view ()->message (); ui ()->ungrab_mouse (this); + set_cursor (lay::Cursor::none); } void diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h index 36f2f6cc6..4fe41b85b 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h @@ -60,6 +60,8 @@ public: NetTracerDialog (lay::Dispatcher *root, lay::LayoutViewBase *view); virtual ~NetTracerDialog (); + virtual void drag_cancel (); + virtual bool claims_message_bar () const; virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual void menu_activated (const std::string &symbol);