From 989f80e1a657175fce37c04612661224576b4c5c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 22 Dec 2025 21:02:22 +0100 Subject: [PATCH 01/36] Using floats for Polygon::smooth distance checks, so that precise measurements are taken --- src/db/db/dbPolygonTools.cc | 4 +- src/db/unit_tests/dbPolygonToolsTests.cc | 59 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index f709df749..c45cdc503 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -814,7 +814,7 @@ smooth_contour (db::Polygon::polygon_contour_iterator from, db::Polygon::polygon if (keep_hv && (p1.x () == p0.x () || p1.y () == p0.y () || p2.x () == p1.x () || p2.y () == p1.y ())) { // keep points which participate in either a vertical or horizontal edge - } else if (db::Coord (p1.distance(p0)) <= d && db::sprod_sign (p2 - p1, p0 - pm1) > 0 && std::abs (db::vprod (p2 - p1, p0 - pm1)) < 0.8 * p2.distance (p1) * p0.distance (pm1)) { + } else if (p1.double_distance (p0) <= d * (1.0 + db::epsilon) && db::sprod_sign (p2 - p1, p0 - pm1) > 0 && std::abs (db::vprod (p2 - p1, p0 - pm1)) < 0.8 * p2.distance (p1) * p0.distance (pm1)) { // jog configurations with small edges are candidates can_drop = true; } else if (db::vprod_sign (p2 - p1, p1 - p0) < 0) { @@ -826,7 +826,7 @@ smooth_contour (db::Polygon::polygon_contour_iterator from, db::Polygon::polygon } for (size_t j = pi0; can_drop; ) { - if (std::abs (db::Edge (p0, p2).distance (org_points [j])) > d) { + if (std::abs (db::DEdge (db::DPoint (p0), db::DPoint (p2)).distance (db::DPoint (org_points [j]))) > d * (1.0 + db::epsilon)) { can_drop = false; } if (j == pi2) { diff --git a/src/db/unit_tests/dbPolygonToolsTests.cc b/src/db/unit_tests/dbPolygonToolsTests.cc index dab78ba79..5b672f6d1 100644 --- a/src/db/unit_tests/dbPolygonToolsTests.cc +++ b/src/db/unit_tests/dbPolygonToolsTests.cc @@ -1366,6 +1366,65 @@ TEST(106) EXPECT_EQ (smooth (p, 100, true).to_string (), "(0,0;0,73235;1200,90468;2300,114468;2800,138468;2800,154468;2000,186468;700,210468;0,219701;0,272971;126450,272971;126450,0)"); } +// smoothing, small units +TEST(107) +{ + db::Point pattern [] = { + db::Point (1, 1), + db::Point (1, 2), + db::Point (2, 2), + db::Point (2, 4), + db::Point (3, 4), + db::Point (3, 5), + db::Point (4, 5), + db::Point (4, 7), + db::Point (5, 7), + db::Point (5, 8), + db::Point (6, 8), + db::Point (6, 9), + db::Point (7, 9), + db::Point (7, 16), + db::Point (8, 16), + db::Point (8, 17), + db::Point (9, 17), + db::Point (9, 18), + db::Point (10, 18), + db::Point (10, 19), + db::Point (12, 19), + db::Point (12, 20), + db::Point (16, 20), + db::Point (16, 21), + db::Point (17, 21), + db::Point (17, 22), + db::Point (18, 22), + db::Point (18, 23), + db::Point (24, 23), + db::Point (24, 15), + db::Point (23, 15), + db::Point (23, 14), + db::Point (22, 14), + db::Point (22, 12), + db::Point (21, 12), + db::Point (21, 10), + db::Point (20, 10), + db::Point (20, 8), + db::Point (19, 8), + db::Point (19, 6), + db::Point (18, 6), + db::Point (18, 4), + db::Point (17, 4), + db::Point (17, 3), + db::Point (16, 3), + db::Point (16, 1) + }; + + db::Polygon p; + p.assign_hull (&pattern[0], &pattern[0] + sizeof (pattern) / sizeof (pattern[0])); + + EXPECT_EQ (smooth (p, 0, false).to_string (), "(1,1;1,2;2,2;2,4;3,4;3,5;4,5;4,7;5,7;5,8;6,8;6,9;7,9;7,16;8,16;8,17;9,17;9,18;10,18;10,19;12,19;12,20;16,20;16,21;17,21;17,22;18,22;18,23;24,23;24,15;23,15;23,14;22,14;22,12;21,12;21,10;20,10;20,8;19,8;19,6;18,6;18,4;17,4;17,3;16,3;16,1)"); + EXPECT_EQ (smooth (p, 1, false).to_string (), "(1,1;2,4;4,5;4,7;7,9;7,16;10,18;18,22;24,23;24,15;22,14;18,4;17,4;16,1)"); +} + // rounding TEST(200) { From c0059959b8d91096da3fe40cf2dbc7f81cdbca40 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 22 Dec 2025 23:09:38 +0100 Subject: [PATCH 02/36] Two small improvements 1. point-like selections use pan-to-selection instead of zoom-to-selection 2. Drawing texts on hidden layers: one warning less about drawing on a hidden layer --- src/edt/edt/edtTextService.cc | 11 +++-------- src/laybasic/laybasic/layLayoutViewBase.cc | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/edt/edt/edtTextService.cc b/src/edt/edt/edtTextService.cc index fe3470a51..c016b7536 100644 --- a/src/edt/edt/edtTextService.cc +++ b/src/edt/edt/edtTextService.cc @@ -157,14 +157,9 @@ TextService::get_text () const void TextService::do_finish_edit () { - get_edit_layer (); - - if (manager ()) { - manager ()->transaction (tl::to_string (tr ("Create text"))); - } - cell ().shapes (layer ()).insert (get_text ()); - if (manager ()) { - manager ()->commit (); + { + db::Transaction transaction (manager (), tl::to_string (tr ("Create text"))); + cell ().shapes (layer ()).insert (get_text ()); } commit_recent (); diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index 67c07e246..35c60e745 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -5608,7 +5608,7 @@ LayoutViewBase::paste () db::DBox sel_bbox = selection_bbox (); if (! sel_bbox.empty ()) { - if (m_paste_display_mode == 1) { + if (m_paste_display_mode == 1 || (m_paste_display_mode == 2 && sel_bbox.is_point ())) { // just make selection visible, i.e. shift window somewhat pan_center (sel_bbox.center ()); } else if (m_paste_display_mode == 2) { From c3b6476176d14b85bffb1098f2bcce97cc43e233 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 11 Jan 2026 00:32:03 +0100 Subject: [PATCH 03/36] [consider merging] Bugfix: DRC collect_xyz is available now also for edge pair layers --- src/drc/drc/built-in-macros/_drc_layer.rb | 2 +- testdata/drc/drcSimpleTests_2.drc | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 44466da10..7bb79f054 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1714,7 +1714,7 @@ def #{f}(&block) dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu) @engine.run_timed("\\"#{f}\\" in: " + @engine.src_line, self.data) do - self.data.send(new_data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| + self.data.send(self.data.is_a?(RBA::EdgePairs) ? :each : :each_merged) do |object| insert_object_into(new_data, block.call(object.transformed(t)), dbu_trans) end new_data diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc index 8973ec5b1..e32bffc1e 100644 --- a/testdata/drc/drcSimpleTests_2.drc +++ b/testdata/drc/drcSimpleTests_2.drc @@ -105,8 +105,10 @@ a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.enlarged(0.1, 0.1) }.o a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(100, 100) }.output(1123, 0) # edge pair collect -a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0) -a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0) +a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1130, 0) +a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1131, 0) +a1.width(1.5).collect_to_edges { |p| p.transformed(RBA::VCplxTrans::new(1000.0)).first }.output(1132, 0) +a1.width(1.5).collect_to_region { |p| p.polygon(0.0).transformed(RBA::VCplxTrans::new(1000.0)) }.output(1133, 0) expect_count(a1.edges, 9, 9, "a1.edges") expect_count(a1.width(1.5), 5, 5, "a1.width(1.5)") From b409ed8b449dfd30886ad05020149751ae44afcd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 11 Jan 2026 01:30:25 +0100 Subject: [PATCH 04/36] Include hidden classes in documentation to avoid confusion. --- src/gsi/gsi/gsiClassBase.h | 18 +++++- src/lay/lay/layGSIHelpProvider.cc | 99 +++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/src/gsi/gsi/gsiClassBase.h b/src/gsi/gsi/gsiClassBase.h index 6915c00de..8d0c03d5f 100644 --- a/src/gsi/gsi/gsiClassBase.h +++ b/src/gsi/gsi/gsiClassBase.h @@ -203,13 +203,29 @@ class GSI_PUBLIC ClassBase } /** - * @brief Iterates all subclasses (end) + * @brief Iterates all child classes (end) */ tl::weak_collection::const_iterator end_child_classes () const { return m_child_classes.end (); } + /** + * @brief Iterates all subclasses (begin) + */ + tl::weak_collection::const_iterator begin_subclasses () const + { + return m_subclasses.begin (); + } + + /** + * @brief Iterates all subclasses (end) + */ + tl::weak_collection::const_iterator end_subclasses () const + { + return m_subclasses.end (); + } + /** * @brief Iterates all classes present (begin) */ diff --git a/src/lay/lay/layGSIHelpProvider.cc b/src/lay/lay/layGSIHelpProvider.cc index 43cea0feb..6ece8b5da 100644 --- a/src/lay/lay/layGSIHelpProvider.cc +++ b/src/lay/lay/layGSIHelpProvider.cc @@ -993,7 +993,11 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const << "" << std::endl << std::endl; - os << "" << tl::to_string (QObject::tr ("API reference - Class")) << " " << escape_xml (cls) << "" << std::endl; + os << "" << tl::to_string (QObject::tr ("API reference - Class")) << " " << escape_xml (cls); + if (class_doc.hidden) { + os << " " << tl::to_string (QObject::tr ("[internal]")); + } + os << "" << std::endl; os << "module ()) << "\"/>" << std::endl; os << "" << std::endl; @@ -1006,13 +1010,15 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "

" << tl::to_string (QObject::tr ("Description")) << ": " << escape_xml (class_doc.brief_doc) << "

" << std::endl; + const gsi::ClassBase *act_cls_obj = real_class (cls_obj); + std::vector classes; - classes.push_back (real_class (cls_obj)); + classes.push_back (act_cls_obj); - const gsi::ClassBase *base = real_class (cls_obj)->base (); + const gsi::ClassBase *base = act_cls_obj->base (); if (base) { - const gsi::ClassBase *last_cls = real_class (cls_obj); + const gsi::ClassBase *last_cls = act_cls_obj; bool all_collected = false; os << "

" << tl::to_string (QObject::tr ("Class hierarchy")) << ": " << make_qualified_name (cls_obj); @@ -1032,6 +1038,7 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const all_collected = true; } else if (! all_collected) { // class needs to be mixed into the parent + os << " » name ())) << "\">" << escape_xml (base->name ()) << " " << tl::to_string (QObject::tr ("[internal]")) << ""; classes.push_back (base); } @@ -1051,34 +1058,90 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "

" << std::endl; } + // Produce child classes + bool any = false; for (std::vector::const_iterator c = classes.begin (); c != classes.end (); ++c) { + for (tl::weak_collection::const_iterator cc = (*c)->begin_child_classes (); cc != (*c)->end_child_classes (); ++cc) { DocumentationParser &cdoc = cls_documentation (cc.operator-> ()); - if (! cdoc.hidden || ! cdoc.alias.empty ()) { - if (any) { - os << ", "; - } else { - os << "

" << tl::to_string (QObject::tr ("Sub-classes")) << ": "; - any = true; - } - - os << " ()))) << "\">" << escape_xml (cc->name ()) << ""; + if (any) { + os << ", "; + } else { + os << "

" << tl::to_string (QObject::tr ("Child classes")) << ": "; + any = true; + } + os << " ()))) + << "\">" + << escape_xml (cc->name ()); + if (cdoc.hidden && cdoc.alias.empty ()) { + os << " " << tl::to_string (QObject::tr ("[internal]")); } + os << ""; + + } + + } + + if (any) { + os << "

" << std::endl; + } + // Produce subclasses (parent classes) + + any = false; + + for (tl::weak_collection::const_iterator cc = act_cls_obj->begin_subclasses (); cc != act_cls_obj->end_subclasses (); ++cc) { + + DocumentationParser &cdoc = cls_documentation (cc.operator-> ()); + + if (any) { + os << ", "; + } else { + os << "

" << tl::to_string (QObject::tr ("Subclasses")) << ": "; + any = true; + } + + os << " ()))) + << "\">" + << escape_xml (cc->name ()); + if (cdoc.hidden && cdoc.alias.empty ()) { + os << " " << tl::to_string (QObject::tr ("[internal]")); } + os << ""; + } if (any) { os << "

" << std::endl; } + // Inserts an index + os << "" << std::endl; + // Produce class doc body + + if (class_doc.hidden && class_doc.alias.empty ()) { + os << "

" + << tl::to_string (QObject::tr ("Note")) + << ": " + << tl::to_string (QObject::tr ( + "This class is an internal class provided for technical reasons - i.e. " + "as a placeholder class for argument binding or as an abstract interface. " + "You should not instantiate objects of this class directly. " + "Instead, use the subclasses listed above. " + "Also see there for more documentation and actual incarnations of this class." + )) + << "

" << std::endl; + } + os << replace_references (class_doc.doc_html (), cls_obj) << std::endl; // collect the methods of the class and their hidden base classes @@ -1116,6 +1179,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "
" << std::endl; return os.str (); } + + // Produce methods brief descriptions int n = 0; int row = 0; @@ -1190,6 +1255,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "" << std::endl; } + // Produce static methods brief descriptions + any = false; n = 0; @@ -1230,6 +1297,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "" << std::endl; } + // Produce protected methods brief descriptions + any = false; n = 0; @@ -1272,6 +1341,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "" << std::endl; } + // Produce deprecated methods brief descriptions + any = false; n = 0; @@ -1323,6 +1394,8 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const os << "" << std::endl; } + // Produce method details + n = 0; os << "

" << tl::to_string (QObject::tr ("Detailed description")) << "

" << std::endl; From c6faa3e6282b2ca0057c8a24da42243bd12fcab3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 11 Jan 2026 22:34:52 +0100 Subject: [PATCH 05/36] 'extent_refs' DRC function: enabling for edge pairs and edges, clarification of documentation --- src/db/db/gsiDeclDbEdgePairs.cc | 76 +++++++++++++++++++++- src/db/db/gsiDeclDbEdges.cc | 72 ++++++++++++++++++++ src/doc/doc/about/drc_ref.xml | 2 +- src/doc/doc/about/drc_ref_drc.xml | 2 +- src/doc/doc/about/drc_ref_global.xml | 2 +- src/doc/doc/about/drc_ref_layer.xml | 19 +++--- src/doc/doc/about/drc_ref_netter.xml | 2 +- src/doc/doc/about/drc_ref_source.xml | 2 +- src/doc/doc/about/lvs_ref.xml | 2 +- src/doc/doc/about/lvs_ref_global.xml | 2 +- src/doc/doc/about/lvs_ref_netter.xml | 2 +- src/drc/drc/built-in-macros/_drc_layer.rb | 23 ++++--- testdata/drc/drcSimpleTests_2.drc | 6 ++ testdata/drc/drcSimpleTests_au2.gds | Bin 15470 -> 16532 bytes 14 files changed, 183 insertions(+), 29 deletions(-) diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 3c83fe30c..9d45b739a 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -31,6 +31,7 @@ #include "dbEdgesUtils.h" #include "dbEdgePairFilters.h" #include "dbPropertiesFilter.h" +#include "dbRegionProcessors.h" #include "gsiDeclDbContainerHelpers.h" #include "gsiDeclDbMeasureHelpers.h" @@ -531,6 +532,71 @@ static db::Region extents0 (const db::EdgePairs *r) return extents2 (r, 0, 0); } +namespace { + +// a combined processor that implements db::RelativeExtents on the edge bounding boxes + +class DB_PUBLIC EdgePairsRelativeExtents + : virtual public db::EdgePairToPolygonProcessorBase, + virtual public db::RelativeExtents +{ +public: + EdgePairsRelativeExtents (double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) + : db::RelativeExtents (fx1, fy1, fx2, fy2, dx, dy) + { + // .. nothing yet .. + } + + // not needed, but mutes + void process (const db::PolygonWithProperties &poly, std::vector &result) const + { + db::RelativeExtents::process (poly, result); + } + + void process (const db::EdgePairWithProperties &ep, std::vector &result) const + { + db::RelativeExtents::process (db::Polygon (ep.bbox ()), result); + } +}; + +class DB_PUBLIC EdgePairsRelativeExtentsAsEdges + : virtual public db::EdgePairToEdgeProcessorBase, + virtual public db::RelativeExtentsAsEdges +{ +public: + EdgePairsRelativeExtentsAsEdges (double fx1, double fy1, double fx2, double fy2) + : db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2) + { + // .. nothing yet .. + } + + void process (const db::PolygonWithProperties &poly, std::vector &result) const + { + db::RelativeExtentsAsEdges::process (poly, result); + } + + void process (const db::EdgePairWithProperties &ep, std::vector &result) const + { + db::RelativeExtentsAsEdges::process (db::Polygon (ep.bbox ()), result); + } +}; + +} + +static db::Region extent_refs (const db::EdgePairs *r, double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) +{ + db::Region result; + r->processed (result, EdgePairsRelativeExtents (fx1, fy1, fx2, fy2, dx, dy)); + return result; +} + +static db::Edges extent_refs_edges (const db::EdgePairs *r, double fx1, double fy1, double fx2, double fy2) +{ + db::Edges result; + r->processed (result, EdgePairsRelativeExtentsAsEdges (fx1, fy1, fx2, fy2)); + return result; +} + static db::Edges edges (const db::EdgePairs *ep) { db::Edges e; @@ -1247,7 +1313,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + - method_ext ("filter", &filter, gsi::arg ("filter"), + method_ext ("extent_refs", &extent_refs, + "@hide\n" + "This method is provided for DRC implementation.\n" + ) + + method_ext ("extent_refs_edges", &extent_refs_edges, + "@hide\n" + "This method is provided for DRC implementation.\n" + ) + + method_ext ("filter", &filter, gsi::arg ("filter"), "@brief Applies a generic filter in place (replacing the edge pairs from the EdgePair collection)\n" "See \\EdgePairFilter for a description of this feature.\n" "\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 109101272..a600c1732 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -32,6 +32,7 @@ #include "dbOriginalLayerRegion.h" #include "dbLayoutUtils.h" #include "dbPropertiesFilter.h" +#include "dbRegionProcessors.h" #include "gsiDeclDbContainerHelpers.h" #include "gsiDeclDbMeasureHelpers.h" @@ -941,6 +942,69 @@ static std::vector split_interacting_with_region (const db::Edges *r, return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } +namespace { + +// a combined processor that implements db::RelativeExtents on the edge bounding boxes + +class DB_PUBLIC EdgesRelativeExtents + : virtual public db::EdgeToPolygonProcessorBase, + virtual public db::RelativeExtents +{ +public: + EdgesRelativeExtents (double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) + : db::RelativeExtents (fx1, fy1, fx2, fy2, dx, dy) + { + // .. nothing yet .. + } + + // not needed, but mutes + void process (const db::PolygonWithProperties &poly, std::vector &result) const + { + db::RelativeExtents::process (poly, result); + } + + void process (const db::EdgeWithProperties &edge, std::vector &result) const + { + db::RelativeExtents::process (db::Polygon (edge.bbox ()), result); + } +}; + +class DB_PUBLIC EdgesRelativeExtentsAsEdges + : virtual public db::EdgeProcessorBase, + virtual public db::RelativeExtentsAsEdges +{ +public: + EdgesRelativeExtentsAsEdges (double fx1, double fy1, double fx2, double fy2) + : db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2) + { + // .. nothing yet .. + } + + void process (const db::PolygonWithProperties &poly, std::vector &result) const + { + db::RelativeExtentsAsEdges::process (poly, result); + } + + void process (const db::EdgeWithProperties &edge, std::vector &result) const + { + db::RelativeExtentsAsEdges::process (db::Polygon (edge.bbox ()), result); + } +}; + +} + +static db::Region extent_refs (const db::Edges *r, double fx1, double fy1, double fx2, double fy2, db::Coord dx, db::Coord dy) +{ + db::Region result; + r->processed (result, EdgesRelativeExtents (fx1, fy1, fx2, fy2, dx, dy)); + return result; +} + +static db::Edges extent_refs_edges (const db::Edges *r, double fx1, double fy1, double fx2, double fy2) +{ + return r->processed (EdgesRelativeExtentsAsEdges (fx1, fy1, fx2, fy2)); +} + static tl::Variant nth (const db::Edges *edges, size_t n) { const db::Edge *e = edges->nth (n); @@ -2372,6 +2436,14 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + + method_ext ("extent_refs", &extent_refs, + "@hide\n" + "This method is provided for DRC implementation.\n" + ) + + method_ext ("extent_refs_edges", &extent_refs_edges, + "@hide\n" + "This method is provided for DRC implementation.\n" + ) + method_ext ("extended_in", &extended_in, gsi::arg ("e"), "@brief Returns a region with shapes representing the edges with the given width\n" "@param e The extension width\n" diff --git a/src/doc/doc/about/drc_ref.xml b/src/doc/doc/about/drc_ref.xml index 203941a1d..3974cf751 100644 --- a/src/doc/doc/about/drc_ref.xml +++ b/src/doc/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index a4ad2a239..78e0e1744 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 735374f88..da588bf12 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index df0a8c7cc..0e485e947 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -1,7 +1,7 @@ - + @@ -1182,20 +1182,19 @@ The formal specifiers for lines are:
  • :right or :r : the right line
  • -Dots are represented by small (2x2 DBU) boxes or point-like -edges with edge output. Lines are represented by narrow or -flat (2 DBU) boxes or edges for edge output. Edges will follow -the orientation convention for the corresponding edges - i.e. -"inside" of the bounding box is on the right side of the edge. -

    The following additional option controls the output format:

      -
    • as_boxes : with this option, small boxes will be produced as markers
    • -
    • as_dots or as_edges : with this option, point-like edges will be produced for dots -and edges will be produced for line-like selections
    • +
    • as_boxes : with this option, boxes (rectangular polygons) will be produced on output
    • +
    • as_dots or as_edges : with this option, edges will be produced on output

    +Dots on are represented by small (2x2 DBU) boxes or point-like +edges with edge output. Lines are represented by narrow or +flat (2 DBU) boxes or edges for edge output. Edges will follow +the orientation convention for the corresponding edges - i.e. +"inside" of the bounding box is on the right side of the edge. +

    The following table shows a few applications:

    diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index 009b97ce4..0157af89f 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml index a746917e3..a3fea2b89 100644 --- a/src/doc/doc/about/drc_ref_source.xml +++ b/src/doc/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml index 58168f012..842f21820 100644 --- a/src/doc/doc/about/lvs_ref.xml +++ b/src/doc/doc/about/lvs_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index 494ef2f6e..d1121526c 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index 2e1dbd89e..be3e6e538 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 7bb79f054..eeb76e244 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1463,20 +1463,19 @@ def corners(*args) # @li @b :right @/b or @b :r @/b: the right line @/li # @/ul # - # Dots are represented by small (2x2 DBU) boxes or point-like - # edges with edge output. Lines are represented by narrow or - # flat (2 DBU) boxes or edges for edge output. Edges will follow - # the orientation convention for the corresponding edges - i.e. - # "inside" of the bounding box is on the right side of the edge. - # # The following additional option controls the output format: # # @ul - # @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li - # @li @b as_dots @/b or @b as_edges @/b: with this option, point-like edges will be produced for dots - # and edges will be produced for line-like selections @/li + # @li @b as_boxes @/b: with this option, boxes (rectangular polygons) will be produced on output @/li + # @li @b as_dots @/b or @b as_edges @/b: with this option, edges will be produced on output @/li # @/ul # + # Dots on are represented by small (2x2 DBU) boxes or point-like + # edges with edge output. Lines are represented by narrow or + # flat (2 DBU) boxes or edges for edge output. Edges will follow + # the orientation convention for the corresponding edges - i.e. + # "inside" of the bounding box is on the right side of the edge. + # # The following table shows a few applications: # # @table @@ -1511,7 +1510,7 @@ def #{f}(*args) @engine._context("#{f}") do - requires_region + requires_edges_edge_pairs_or_region f = [] as_edges = false @@ -6105,6 +6104,10 @@ def requires_edges_or_region(name = nil) self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise(name ? "#{name} requires an edge or polygon layer" : "Requires an edge or polygon layer") end + def requires_edges_edge_pairs_or_region(name = nil) + self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::EdgePairs) || raise(name ? "#{name} requires an edge, edge pair or polygon layer" : "Requires an edge, edge pair or polygon layer") + end + def requires_edges_texts_or_region(name = nil) self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise(name ? "#{name} requires an edge, text or polygon layer" : "Requires an edge, text or polygon layer") end diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc index e32bffc1e..d77afb552 100644 --- a/testdata/drc/drcSimpleTests_2.drc +++ b/testdata/drc/drcSimpleTests_2.drc @@ -64,6 +64,12 @@ a1.extent_refs(:lc).sized(0.05).output(1051, 1) a1.extent_refs(:right_center).sized(0.05).output(1052, 0) a1.extent_refs(:rc).sized(0.05).output(1052, 1) a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0) +a1.extent_refs(0.5, 0.5, 0.25, 0.75, as_edges).output(1054, 0) +a1.extent_refs(0.5, 0.5, 0.25, 0.75, as_boxes).output(1055, 0) +a1.extents.width(0.8.um).extent_refs(0.5, 0.5, 0.25, 0.75, as_edges).output(1056, 0) +a1.extents.width(0.8.um).extent_refs(0.5, 0.5, 0.25, 0.75, as_boxes).output(1057, 0) +a1.extents.width(0.8.um).first_edges.extent_refs(:center, as_edges).output(1058, 0) +a1.extents.width(0.8.um).first_edges.extent_refs(:center, as_boxes).output(1059, 0) a1.corners.sized(0.05).output(1060, 0) a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0) diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds index 6996b4a94cd000b4abb3436256b75b93984cdd57..dc6143932356f985339b2cee7e3f98d2253ff3b4 100644 GIT binary patch delta 608 zcmaD?F{P0)ih+%Ri7A3XhLMT=6$2v!H-i|1JcBYan}LIg&BxP;fkA|s)y}cg-22$_ z50)-F^cT#I3Sp38#i?&&)ITW>P6h@xUM3c~$qo7vatsU%DgXa}-t_XjxxFp_fvJHP_u5ES#38QA!kfKJpAU Date: Sun, 11 Jan 2026 23:46:11 +0100 Subject: [PATCH 06/36] Pressing Ctrl key while drawing a box forces it into a square --- src/edt/edt/edtBoxService.cc | 19 ++++++++++++++++--- src/edt/edt/edtService.cc | 27 +++++++++++++++++++++++---- src/edt/edt/edtService.h | 11 +++++++++++ src/laybasic/laybasic/laySnap.cc | 12 +++++++++--- src/laybasic/laybasic/laySnap.h | 2 +- 5 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/edt/edt/edtBoxService.cc b/src/edt/edt/edtBoxService.cc index 8b1cdd173..a8554c43b 100644 --- a/src/edt/edt/edtBoxService.cc +++ b/src/edt/edt/edtBoxService.cc @@ -108,7 +108,10 @@ BoxService::do_mouse_move_inactive (const db::DPoint &p) void BoxService::do_mouse_move (const db::DPoint &p) { - lay::PointSnapToObjectResult snap_details = snap2_details (p); + // snap to square if Ctrl button is pressed + bool snap_square = alt_ac () == lay::AC_Diagonal; + + lay::PointSnapToObjectResult snap_details = snap2_details (p, m_p1, snap_square ? lay::AC_DiagonalOnly : lay::AC_Any); db::DPoint ps = snap_details.snapped_point; if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) { @@ -122,12 +125,22 @@ BoxService::do_mouse_move (const db::DPoint &p) lay::PointSnapToObjectResult snap_details_y = snap2_details (py); if (snap_details_x.object_snap != lay::PointSnapToObjectResult::NoObject) { - ps = db::DPoint (snap_details_x.snapped_point.x (), ps.y ()); + if (snap_square) { + double dx = fabs (snap_details_x.snapped_point.x () - m_p1.x ()); + ps = db::DPoint (snap_details_x.snapped_point.x (), m_p1.y () + (ps.y () < m_p1.y () ? -dx : dx)); + } else { + ps = db::DPoint (snap_details_x.snapped_point.x (), ps.y ()); + } mouse_cursor_from_snap_details (snap_details_x, true /*add*/); } if (snap_details_y.object_snap != lay::PointSnapToObjectResult::NoObject) { - ps = db::DPoint (ps.x (), snap_details_y.snapped_point.y ()); + if (snap_square) { + double dy = fabs (snap_details_y.snapped_point.x () - m_p1.y ()); + ps = db::DPoint (m_p1.x () + (ps.x () < m_p1.x () ? -dy : dy), snap_details_y.snapped_point.y ()); + } else { + ps = db::DPoint (ps.x (), snap_details_y.snapped_point.y ()); + } mouse_cursor_from_snap_details (snap_details_y, true /*add*/); } diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 83c164472..ff098ac94 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -103,14 +103,21 @@ Service::~Service () clear_transient_selection (); } -lay::angle_constraint_type +lay::angle_constraint_type +Service::alt_ac () const +{ + // fetch m_alt_ac (which is set from mouse buttons) + return m_alt_ac; +} + +lay::angle_constraint_type Service::connect_ac () const { // m_alt_ac (which is set from mouse buttons) can override the specified connect angle constraint return m_alt_ac != lay::AC_Global ? m_alt_ac : m_connect_ac; } -lay::angle_constraint_type +lay::angle_constraint_type Service::move_ac () const { // m_alt_ac (which is set from mouse buttons) can override the specified move angle constraint @@ -290,11 +297,23 @@ Service::snap2 (const db::DPoint &p) const return snap2_details (p).snapped_point; } +lay::PointSnapToObjectResult +Service::snap2_details (const db::DPoint &p, const db::DPoint &plast, lay::angle_constraint_type ac) const +{ + double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (lay::snap_range_pixels ()); + return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, ac, snap_range); +} + +lay::PointSnapToObjectResult +Service::snap2_details (const db::DPoint &p, const db::DPoint &plast, bool connect) const +{ + return snap2_details (p, plast, connect ? connect_ac () : move_ac ()); +} + db::DPoint Service::snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect) const { - double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (lay::snap_range_pixels ()); - return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).snapped_point; + return snap2_details (p, plast, connect ? connect_ac () : move_ac ()).snapped_point; } void diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index a791187e5..09bb56e96 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -611,6 +611,7 @@ class EDT_PUBLIC Service db::DPoint snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect = true) const; protected: + lay::angle_constraint_type alt_ac () const; lay::angle_constraint_type connect_ac () const; lay::angle_constraint_type move_ac () const; @@ -654,6 +655,16 @@ class EDT_PUBLIC Service */ lay::PointSnapToObjectResult snap2_details (const db::DPoint &p) const; + /** + * @brief Point snapping with detailed return value + */ + lay::PointSnapToObjectResult snap2_details (const db::DPoint &p, const db::DPoint &plast, bool connect) const; + + /** + * @brief Point snapping with detailed return value and specific angle constraint + */ + lay::PointSnapToObjectResult snap2_details (const db::DPoint &p, const db::DPoint &plast, lay::angle_constraint_type ac) const; + private: friend class EditableSelectionIterator; diff --git a/src/laybasic/laybasic/laySnap.cc b/src/laybasic/laybasic/laySnap.cc index 8578e84dd..c7f02b351 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -188,9 +188,11 @@ snap_angle (const db::DVector &in, lay::angle_constraint_type ac, db::DVector *s std::vector ref_dir; if (ac != lay::AC_Any) { ref_dir.reserve (4); - ref_dir.push_back (db::DVector (1.0, 0)); - ref_dir.push_back (db::DVector (0, 1.0)); - if (ac == lay::AC_Diagonal) { + if (ac != lay::AC_DiagonalOnly) { + ref_dir.push_back (db::DVector (1.0, 0)); + ref_dir.push_back (db::DVector (0, 1.0)); + } + if (ac == lay::AC_Diagonal || ac == lay::AC_DiagonalOnly) { ref_dir.push_back (db::DVector (-1.0, 1.0)); ref_dir.push_back (db::DVector (1.0, 1.0)); } @@ -963,6 +965,10 @@ make_cutlines (lay::angle_constraint_type snap_mode, const db::DPoint &p1, std:: cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, 0.0))); cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, 1.0))); cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, -1.0))); + } else if (snap_mode == lay::AC_DiagonalOnly) { + cutlines.reserve (2); + cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, 1.0))); + cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, -1.0))); } } diff --git a/src/laybasic/laybasic/laySnap.h b/src/laybasic/laybasic/laySnap.h index 51f209633..cfa63ae9b 100644 --- a/src/laybasic/laybasic/laySnap.h +++ b/src/laybasic/laybasic/laySnap.h @@ -53,7 +53,7 @@ namespace lay * Vertical: vertical only * Global: use global setting (templates and ruler specific setting only) */ - enum angle_constraint_type { AC_Any = 0, AC_Diagonal, AC_Ortho, AC_Horizontal, AC_Vertical, AC_Global, AC_NumModes }; + enum angle_constraint_type { AC_Any = 0, AC_Diagonal, AC_DiagonalOnly, AC_Ortho, AC_Horizontal, AC_Vertical, AC_Global, AC_NumModes }; /** * @brief snap a coordinate value to a unit grid From c738cf72552737dac7d4a86caa22a5cba3f26203 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 12 Jan 2026 15:45:17 +0100 Subject: [PATCH 07/36] WIP --- src/edt/edt/edtEditorOptionsPages.cc | 10 +- src/edt/edt/edtEditorOptionsPages.h | 10 +- src/edt/edt/edtPlugin.cc | 17 +-- src/edt/edt/edtRecentConfigurationPage.h | 6 +- src/edt/edt/edtService.cc | 9 +- src/edt/edt/edtShapeService.cc | 18 +-- src/lay/lay/layMainWindow.cc | 2 +- src/laybasic/laybasic/gsiDeclLayPlugin.cc | 1 - .../laybasic/gsiDeclLayPluginFactory.cc | 2 - src/laybasic/laybasic/layEditorOptionsPage.cc | 136 ++++++++++-------- src/laybasic/laybasic/layEditorOptionsPage.h | 77 +++++++--- src/laybasic/laybasic/layEditorServiceBase.cc | 35 +---- src/laybasic/laybasic/layEditorServiceBase.h | 2 - src/laybasic/laybasic/layLayoutViewBase.h | 34 ++++- src/laybasic/laybasic/layMove.cc | 55 +++++++ src/laybasic/laybasic/laybasic.pro | 2 - src/layui/layui/layui.pro | 2 - .../layview}/layEditorOptionsFrame.cc | 5 +- .../layview}/layEditorOptionsFrame.h | 8 +- .../layview}/layEditorOptionsPages.cc | 106 +++++++++----- .../layview}/layEditorOptionsPages.h | 44 +++--- src/layview/layview/layLayoutView_qt.cc | 34 ++++- src/layview/layview/layLayoutView_qt.h | 27 +++- src/layview/layview/layview.pro | 4 + 24 files changed, 405 insertions(+), 241 deletions(-) rename src/{layui/layui => layview/layview}/layEditorOptionsFrame.cc (95%) rename src/{layui/layui => layview/layview}/layEditorOptionsFrame.h (91%) rename src/{laybasic/laybasic => layview/layview}/layEditorOptionsPages.cc (78%) rename src/{laybasic/laybasic => layview/layview}/layEditorOptionsPages.h (66%) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 416e08238..a51a7c45e 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -72,7 +72,7 @@ static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le // EditorOptionsGeneric implementation EditorOptionsGeneric::EditorOptionsGeneric (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : EditorOptionsPage (view, dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) { mp_ui = new Ui::EditorOptionsGeneric (); mp_ui->setupUi (this); @@ -215,7 +215,7 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) // EditorOptionsText implementation EditorOptionsText::EditorOptionsText (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (view, dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); @@ -293,7 +293,7 @@ EditorOptionsText::setup (lay::Dispatcher *root) // EditorOptionsPath implementation EditorOptionsPath::EditorOptionsPath (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (view, dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) { mp_ui = new Ui::EditorOptionsPath (); mp_ui->setupUi (this); @@ -394,7 +394,7 @@ EditorOptionsPath::setup (lay::Dispatcher *root) // EditorOptionsInst implementation EditorOptionsInst::EditorOptionsInst (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (view, dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); @@ -687,7 +687,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) // EditorOptionsInstPCellParam implementation EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (view, dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) + : lay::EditorOptionsPageWidget (view, dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) { mp_ui = new Ui::EditorOptionsInstPCellParam (); mp_ui->setupUi (this); diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index acf94b4f1..5080c2dfe 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -65,7 +65,7 @@ class PCellParametersPage; * @brief The generic properties page */ class EditorOptionsGeneric - : public lay::EditorOptionsPage + : public lay::EditorOptionsPageWidget { Q_OBJECT @@ -90,7 +90,7 @@ public slots: * @brief The text properties page */ class EditorOptionsText - : public lay::EditorOptionsPage + : public lay::EditorOptionsPageWidget { public: EditorOptionsText (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); @@ -109,7 +109,7 @@ class EditorOptionsText * @brief The path properties page */ class EditorOptionsPath - : public lay::EditorOptionsPage + : public lay::EditorOptionsPageWidget { Q_OBJECT @@ -133,7 +133,7 @@ public slots: * @brief The instance properties page */ class EditorOptionsInst - : public lay::EditorOptionsPage + : public lay::EditorOptionsPageWidget { Q_OBJECT @@ -165,7 +165,7 @@ private slots: * @brief The instance properties page (PCell parameters) */ class EditorOptionsInstPCellParam - : public lay::EditorOptionsPage + : public lay::EditorOptionsPageWidget { Q_OBJECT diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 33fbb341f..1062bc71a 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -21,13 +21,10 @@ */ -#if defined(HAVE_QT) -# include "layTipDialog.h" -# include "layEditorOptionsPages.h" -#endif - #include "layDispatcher.h" #include "layLayoutViewBase.h" +#include "layEditorOptionsPage.h" +#include "layTipDialog.h" #include "edtPlugin.h" #include "edtConfig.h" #include "edtService.h" @@ -40,10 +37,8 @@ #include "edtMainService.h" #include "edtPartialService.h" #include "edtMoveTrackerService.h" -#if defined(HAVE_QT) -# include "edtEditorOptionsPages.h" -# include "edtRecentConfigurationPage.h" -#endif +#include "edtEditorOptionsPages.h" +#include "edtRecentConfigurationPage.h" #if defined(HAVE_QT) # include @@ -65,7 +60,7 @@ edt::RecentConfigurationPage::ConfigurationDescriptor shape_cfg_descriptors[] = static void get_shape_editor_options_pages (std::vector &ret, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) { - ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-shape-param", + ret.push_back (new edt::RecentConfigurationPage (view, dispatcher, "edit-recent-shape-param", &shape_cfg_descriptors[0], &shape_cfg_descriptors[sizeof (shape_cfg_descriptors) / sizeof (shape_cfg_descriptors[0])])); } #else @@ -94,7 +89,7 @@ void get_text_editor_options_pages (std::vector &ret, edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_valign, tl::to_string (tr ("Vert. align")), edt::RecentConfigurationPage::Text) }; - ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-text-param", + ret.push_back (new edt::RecentConfigurationPage (view, dispatcher, "edit-recent-text-param", &text_cfg_descriptors[0], &text_cfg_descriptors[sizeof (text_cfg_descriptors) / sizeof (text_cfg_descriptors[0])])); ret.push_back (new edt::EditorOptionsText (view, dispatcher)); } diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index bf0777ba5..dd6ff0e96 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -48,13 +48,11 @@ namespace edt class PCellParametersPage; -class EditorOptionsPages; - /** * @brief The base class for a object properties page */ class EDT_PUBLIC RecentConfigurationPage - : public lay::EditorOptionsPage + : public lay::EditorOptionsPageWidget { Q_OBJECT @@ -86,7 +84,7 @@ Q_OBJECT template RecentConfigurationPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg) - : EditorOptionsPage (view, dispatcher), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg), dm_update_list (this, &RecentConfigurationPage::update_list) + : EditorOptionsPageWidget (view, dispatcher), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg), dm_update_list (this, &RecentConfigurationPage::update_list) { init (); } diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index ff098ac94..ededabe19 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -2024,18 +2024,17 @@ Service::handle_guiding_shape_changes (bool commit) void Service::commit_recent () { -#if defined(HAVE_QT) - lay::EditorOptionsPages *eo_pages = view ()->editor_options_pages (); - if (!eo_pages) { + lay::EditorOptionsPageCollection *eo_pages = view ()->editor_options_pages (); + if (! eo_pages) { return; } - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + auto pages = eo_pages->editor_options_pages (); + for (auto op = pages.begin (); op != pages.end (); ++op) { if ((*op)->plugin_declaration () == plugin_declaration ()) { (*op)->commit_recent (view ()); } } -#endif } // ------------------------------------------------------------- diff --git a/src/edt/edt/edtShapeService.cc b/src/edt/edt/edtShapeService.cc index 4c16060cf..4b95ee300 100644 --- a/src/edt/edt/edtShapeService.cc +++ b/src/edt/edt/edtShapeService.cc @@ -25,15 +25,11 @@ #include "edtPathService.h" #include "edtPropertiesPages.h" #include "layLayoutView.h" +#include "layEditorOptionsPage.h" +#include "layTipDialog.h" #include "dbEdgeProcessor.h" #include "dbPolygonTools.h" -#if defined(HAVE_QT) -# include "layTipDialog.h" -#endif - -#include "layEditorOptionsPages.h" - namespace edt { @@ -76,21 +72,19 @@ ShapeEditService::config_recent_for_layer (const db::LayerProperties &lp, int cv return; } -#if defined(HAVE_QT) - lay::EditorOptionsPages *eo_pages = view ()->editor_options_pages (); - if (!eo_pages) { + lay::EditorOptionsPageCollection *eo_pages = view ()->editor_options_pages (); + if (! eo_pages) { return; } - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + auto pages = eo_pages->editor_options_pages (); + for (auto op = pages.begin (); op != pages.end (); ++op) { if ((*op)->plugin_declaration () == plugin_declaration ()) { (*op)->config_recent_for_layer (dispatcher (), lp, cv_index); } } -#endif } - void ShapeEditService::get_edit_layer () { diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 75a73dd16..5fa135a1c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -1722,7 +1722,7 @@ MainWindow::update_editor_options_dock () eo_visible = pd_sel->editable_enabled (); } if (current_view () && eo_visible) { - lay::EditorOptionsPages *eo_pages = current_view ()->editor_options_pages (); + lay::EditorOptionsPageCollection *eo_pages = current_view ()->editor_options_pages (); if (! eo_pages || ! eo_pages->has_content ()) { eo_visible = false; } diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.cc b/src/laybasic/laybasic/gsiDeclLayPlugin.cc index ef3d696dc..ae83521cb 100644 --- a/src/laybasic/laybasic/gsiDeclLayPlugin.cc +++ b/src/laybasic/laybasic/gsiDeclLayPlugin.cc @@ -25,7 +25,6 @@ #include "gsiDecl.h" #include "gsiDeclBasic.h" #include "gsiEnums.h" -#include "layEditorOptionsPages.h" #include "layCursor.h" #include "layEditorUtils.h" #include "layConverters.h" diff --git a/src/laybasic/laybasic/gsiDeclLayPluginFactory.cc b/src/laybasic/laybasic/gsiDeclLayPluginFactory.cc index 85f0541bf..38cb0a17d 100644 --- a/src/laybasic/laybasic/gsiDeclLayPluginFactory.cc +++ b/src/laybasic/laybasic/gsiDeclLayPluginFactory.cc @@ -28,8 +28,6 @@ #include "gsiDeclLayConfigPage.h" #include "gsiDeclLayPlugin.h" -#include "layEditorOptionsPages.h" - namespace gsi { diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index 0efde1109..e489bc1d8 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -20,11 +20,8 @@ */ -#if defined(HAVE_QT) - #include "tlInternational.h" #include "layEditorOptionsPage.h" -#include "layEditorOptionsPages.h" #include "layLayoutViewBase.h" #include "tlExceptions.h" @@ -38,13 +35,13 @@ namespace lay // EditorOptionsPage implementation EditorOptionsPage::EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), mp_plugin_declaration (0), mp_dispatcher (dispatcher), mp_view (view) + : mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), m_toolbox_widget (false), mp_plugin_declaration (0), mp_dispatcher (0), mp_view (0) { - attach_events (); + init (view, dispatcher); } EditorOptionsPage::EditorOptionsPage () - : QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), mp_plugin_declaration (0), mp_dispatcher (0), mp_view (0) + : mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), m_toolbox_widget (false), mp_plugin_declaration (0), mp_dispatcher (0), mp_view (0) { // .. nothing yet .. } @@ -62,57 +59,6 @@ EditorOptionsPage::init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) attach_events (); } -void -EditorOptionsPage::edited () -{ - apply (dispatcher ()); -} - -static bool is_parent_widget (QWidget *w, QWidget *parent) -{ - while (w && w != parent) { - w = dynamic_cast (w->parent ()); - } - return w == parent; -} - -bool -EditorOptionsPage::focusNextPrevChild (bool next) -{ - bool res = QWidget::focusNextPrevChild (next); - - // Stop making the focus leave the page - this way we can jump back to the - // view on "enter" - if (res && ! is_modal_page () && ! is_parent_widget (QApplication::focusWidget (), this) && focusWidget ()) { - focusWidget ()->setFocus (); - } - - return res; -} - -void -EditorOptionsPage::keyPressEvent (QKeyEvent *event) -{ -BEGIN_PROTECTED - if (! is_modal_page () && event->modifiers () == Qt::NoModifier && event->key () == Qt::Key_Return) { - // The Return key on a non-modal page commits the values and gives back the focus - // to the view - apply (dispatcher ()); - view ()->set_focus (); - event->accept (); - } else { - QWidget::keyPressEvent (event); - } -END_PROTECTED -} - -void -EditorOptionsPage::set_focus () -{ - setFocus (Qt::TabFocusReason); - QWidget::focusNextPrevChild (true); -} - int EditorOptionsPage::show () { @@ -153,7 +99,7 @@ EditorOptionsPage::on_technology_changed () } void -EditorOptionsPage::set_owner (EditorOptionsPages *owner) +EditorOptionsPage::set_owner (EditorOptionsPageCollection *owner) { if (mp_owner) { mp_owner->unregister_page (this); @@ -172,6 +118,80 @@ EditorOptionsPage::activate (bool active) } } +#if defined(HAVE_QT) + +// ------------------------------------------------------------------ +// EditorOptionsPage implementation + +EditorOptionsPageWidget::EditorOptionsPageWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : QWidget (0), EditorOptionsPage (view, dispatcher) +{ + init (view, dispatcher); +} + +EditorOptionsPageWidget::EditorOptionsPageWidget () + : QWidget (0), EditorOptionsPage () +{ + // .. nothing yet .. +} + +EditorOptionsPageWidget::~EditorOptionsPageWidget () +{ + set_owner (0); +} + +void +EditorOptionsPageWidget::edited () +{ + apply (dispatcher ()); +} + +static bool is_parent_widget (QWidget *w, QWidget *parent) +{ + while (w && w != parent) { + w = dynamic_cast (w->parent ()); + } + return w == parent; +} + +bool +EditorOptionsPageWidget::focusNextPrevChild (bool next) +{ + bool res = QWidget::focusNextPrevChild (next); + + // Stop making the focus leave the page - this way we can jump back to the + // view on "enter" + if (res && ! is_modal_page () && ! is_parent_widget (QApplication::focusWidget (), this) && focusWidget ()) { + focusWidget ()->setFocus (); + } + + return res; +} + +void +EditorOptionsPageWidget::keyPressEvent (QKeyEvent *event) +{ +BEGIN_PROTECTED + if (! is_modal_page () && event->modifiers () == Qt::NoModifier && event->key () == Qt::Key_Return) { + // The Return key on a non-modal page commits the values and gives back the focus + // to the view + apply (dispatcher ()); + view ()->set_focus (); + event->accept (); + } else { + QWidget::keyPressEvent (event); + } +END_PROTECTED +} + +void +EditorOptionsPageWidget::set_focus () +{ + setFocus (Qt::TabFocusReason); + QWidget::focusNextPrevChild (true); } #endif + +} + diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 33b9fc1d3..33a553034 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -20,8 +20,6 @@ */ -#if defined(HAVE_QT) - #ifndef HDR_layEditorOptionsPage #define HDR_layEditorOptionsPage @@ -29,7 +27,9 @@ #include "tlObject.h" -#include +#if defined(HAVE_QT) +# include +#endif namespace db { @@ -44,16 +44,34 @@ class Dispatcher; class LayoutViewBase; class Plugin; class CellView; -class EditorOptionsPages; +class EditorOptionsPage; +class EditorOptionsPageWidget; + +/** + * @brief An interface managing a collection of EditorOptionPage objects + */ +class LAYBASIC_PUBLIC EditorOptionsPageCollection +{ +public: + virtual ~EditorOptionsPageCollection () { } + + virtual void unregister_page (EditorOptionsPage *page) = 0; + virtual bool has_content () const = 0; + virtual bool has_modal_content () const = 0; + virtual void make_page_current (EditorOptionsPage *page) = 0; + virtual void activate_page (EditorOptionsPage *page) = 0; + virtual void activate (const lay::Plugin *plugin) = 0; + virtual bool exec_modal (EditorOptionsPage *page) = 0; + virtual std::vector editor_options_pages (const lay::PluginDeclaration *plugin) = 0; + virtual std::vector editor_options_pages () = 0; +}; /** * @brief The base class for a object properties page */ class LAYBASIC_PUBLIC EditorOptionsPage - : public QWidget, public tl::Object + : public tl::Object { -Q_OBJECT - public: EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); EditorOptionsPage (); @@ -65,17 +83,21 @@ Q_OBJECT virtual void setup (lay::Dispatcher * /*root*/) { } virtual void commit_recent (lay::Dispatcher * /*root*/) { } virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { } + virtual void set_focus () { } + virtual EditorOptionsPageWidget *widget () { return 0; } bool is_focus_page () const { return m_focus_page; } void set_focus_page (bool f) { m_focus_page = f; } - void set_focus (); bool is_modal_page () const { return m_modal_page; } void set_modal_page (bool f) { m_modal_page = f; } + bool is_toolbox_widget () const { return m_toolbox_widget; } + void set_toolbox_widget (bool f) { m_toolbox_widget = f; } + bool active () const { return m_active; } void activate (bool active); - void set_owner (EditorOptionsPages *owner); + void set_owner (EditorOptionsPageCollection *owner); /** * @brief Shows the editor page @@ -98,21 +120,15 @@ Q_OBJECT return mp_view; } -protected slots: - void edited (); - protected: virtual void active_cellview_changed () { } virtual void technology_changed (const std::string & /*tech*/) { } - virtual bool focusNextPrevChild (bool next); - virtual void keyPressEvent (QKeyEvent *event); - private: - EditorOptionsPages *mp_owner; + EditorOptionsPageCollection *mp_owner; bool m_active; bool m_focus_page; - bool m_modal_page; + bool m_modal_page, m_toolbox_widget; const lay::PluginDeclaration *mp_plugin_declaration; lay::Dispatcher *mp_dispatcher; lay::LayoutViewBase *mp_view; @@ -122,8 +138,33 @@ protected slots: void attach_events (); }; +#if defined(HAVE_QT) +/** + * @brief The base class for a object properties page + */ +class LAYBASIC_PUBLIC EditorOptionsPageWidget + : public QWidget, public EditorOptionsPage +{ +Q_OBJECT + +public: + EditorOptionsPageWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + EditorOptionsPageWidget (); + virtual ~EditorOptionsPageWidget (); + + virtual void set_focus (); + virtual EditorOptionsPageWidget *widget () { return this; } + +protected slots: + void edited (); + +protected: + virtual bool focusNextPrevChild (bool next); + virtual void keyPressEvent (QKeyEvent *event); +}; +#endif // defined(HAVE_QT) + } #endif -#endif // defined(HAVE_QT) diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index 9be73bf37..a3ae88baf 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -22,7 +22,6 @@ #include "layEditorServiceBase.h" #include "layEditorOptionsPage.h" -#include "layEditorOptionsPages.h" #include "layViewport.h" #include "layLayoutViewBase.h" #include "laybasicConfig.h" @@ -345,22 +344,14 @@ EditorServiceBase::activated () m_active = true; } -#if defined(HAVE_QT) - std::vector EditorServiceBase::editor_options_pages () { - lay::EditorOptionsPages *eo_pages = mp_view->editor_options_pages (); + lay::EditorOptionsPageCollection *eo_pages = mp_view->editor_options_pages (); if (!eo_pages) { return std::vector (); } else { - std::vector pages; - for (auto p = eo_pages->pages ().begin (); p != eo_pages->pages ().end (); ++p) { - if ((*p)->plugin_declaration () == plugin_declaration ()) { - pages.push_back (*p); - } - } - return pages; + return eo_pages->editor_options_pages (plugin_declaration ()); } } @@ -402,26 +393,4 @@ EditorServiceBase::show_error (tl::Exception &ex) QMessageBox::critical (ui ()->widget (), tr ("Error"), tl::to_qstring (ex.msg ())); } -#else - -bool -EditorServiceBase::key_event (unsigned int key, unsigned int buttons) -{ - return false; -} - -void -EditorServiceBase::show_error (tl::Exception &ex) -{ - tl::error << ex.msg (); -} - -int -EditorServiceBase::focus_page_open () -{ - return 0; -} - -#endif - } diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h index d23b37755..641aac92f 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.h +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -272,7 +272,6 @@ class LAYBASIC_PUBLIC EditorServiceBase */ virtual int focus_page_open (); -#if defined(HAVE_QT) /** * @brief Gets the editor options pages associated with this plugin */ @@ -282,7 +281,6 @@ class LAYBASIC_PUBLIC EditorServiceBase * @brief Gets the focus page or 0 if there is none */ lay::EditorOptionsPage *focus_page (); -#endif private: // The marker representing the mouse cursor diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index bc40922b1..1423ec49b 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -77,11 +77,12 @@ class MouseTracker; class ZoomService; class SelectionService; class MoveService; +class EditorOptionsPage; +class EditorOptionsPageCollection; #if defined(HAVE_QT) class LayerControlPanel; class HierarchyControlPanel; -class EditorOptionsPages; #endif /** @@ -351,6 +352,33 @@ class LAYBASIC_PUBLIC LayoutViewBase : // the base implementation does nothing } + /** + * @brief Removes a notification + */ + virtual void remove_notification (const LayoutViewNotification & /*notification*/) + { + // the base implementation does nothing + } + + /** + * @brief Shows or hides a toolbox widget with the given name + * + * Initially toolbox widgets are invisible. They are made visible + * by using this method. + */ + virtual void show_toolbox_widget (const std::string & /*name*/, bool /*visible*/) + { + // the base implementation does nothing + } + + /** + * @brief Adds an editor options page as a toolbox widget + */ + virtual void add_toolbox_widget (lay::EditorOptionsPage * /*toolbox_widget*/) + { + // the base implementation does nothing + } + /** * @brief Gets the explicit title string of the view * @@ -1899,15 +1927,15 @@ class LAYBASIC_PUBLIC LayoutViewBase : { return 0; } +#endif /** * @brief Gets the editor options page */ - virtual lay::EditorOptionsPages *editor_options_pages () + virtual lay::EditorOptionsPageCollection *editor_options_pages () { return 0; } -#endif /** * @brief Get the current viewport diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index a2a6ba96c..671b7cc69 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -27,6 +27,13 @@ #include "laySelector.h" #include "laybasicConfig.h" +#if defined(HAVE_QT) +# include "layEditorOptionsPage.h" +# include +# include +# include +#endif + namespace lay { @@ -369,6 +376,47 @@ MoveService::finish () // ---------------------------------------------------------------------------- +#if defined(HAVE_QT) +namespace { + +class MoveToolboxPage + : public lay::EditorOptionsPageWidget +{ +public: + MoveToolboxPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) + { + mp_layout = new QHBoxLayout (this); + + mp_x_le = new QLineEdit (this); + mp_layout->addWidget (mp_x_le); + mp_y_le = new QLineEdit (this); + mp_layout->addWidget (mp_y_le); + mp_layout->addStretch (1); + + // @@@ + + set_toolbox_widget (true); + } + + virtual std::string title () const + { + return "Move Options"; + } + + virtual int order () const + { + return 0; + } + +private: + QHBoxLayout *mp_layout; + QLineEdit *mp_x_le, *mp_y_le; +}; + +} +#endif + class MoveServiceDeclaration : public lay::PluginDeclaration { @@ -383,6 +431,13 @@ class MoveServiceDeclaration { return new MoveService (view); } + +#if defined(HAVE_QT) + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) const + { + pages.push_back (new MoveToolboxPage (view, dispatcher)); + } +#endif }; static tl::RegisteredClass move_service_decl (new MoveServiceDeclaration (), -970, "laybasic::MoveServicePlugin"); diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 5ba3943e7..85c921ff0 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -42,7 +42,6 @@ SOURCES += \ gsiDeclLayPluginFactory.cc \ layAbstractMenu.cc \ layEditorOptionsPage.cc \ - layEditorOptionsPages.cc \ layEditorUtils.cc \ layLayoutViewConfig.cc \ layMargin.cc \ @@ -100,7 +99,6 @@ HEADERS += \ gsiDeclLayEditorOptionsPage.h \ gsiDeclLayPlugin.h \ layEditorOptionsPage.h \ - layEditorOptionsPages.h \ layEditorUtils.h \ layMargin.h \ laybasicConfig.h \ diff --git a/src/layui/layui/layui.pro b/src/layui/layui/layui.pro index cf30329d0..2fb51d0d9 100644 --- a/src/layui/layui/layui.pro +++ b/src/layui/layui/layui.pro @@ -103,7 +103,6 @@ SOURCES = \ layEditLineStylesForm.cc \ layEditStippleWidget.cc \ layEditStipplesForm.cc \ - layEditorOptionsFrame.cc \ layFileDialog.cc \ layGenericSyntaxHighlighter.cc \ layHierarchyControlPanel.cc \ @@ -161,7 +160,6 @@ HEADERS = \ layEditLineStylesForm.h \ layEditStippleWidget.h \ layEditStipplesForm.h \ - layEditorOptionsFrame.h \ layFileDialog.h \ layGenericSyntaxHighlighter.h \ layHierarchyControlPanel.h \ diff --git a/src/layui/layui/layEditorOptionsFrame.cc b/src/layview/layview/layEditorOptionsFrame.cc similarity index 95% rename from src/layui/layui/layEditorOptionsFrame.cc rename to src/layview/layview/layEditorOptionsFrame.cc index 2967605a7..135d34bcb 100644 --- a/src/layui/layui/layEditorOptionsFrame.cc +++ b/src/layview/layview/layEditorOptionsFrame.cc @@ -20,8 +20,6 @@ */ -#if defined(HAVE_QT) - #include "layEditorOptionsFrame.h" #include "layEditorOptionsPage.h" #include "layEditorOptionsPages.h" @@ -64,11 +62,10 @@ EditorOptionsFrame::populate (LayoutViewBase *view) delete mp_pages; } - mp_pages = new lay::EditorOptionsPages (this, prop_dialog_pages, view); + mp_pages = new lay::EditorOptionsPages (this, view, prop_dialog_pages); layout ()->addWidget (mp_pages); setFocusProxy (mp_pages); } } -#endif diff --git a/src/layui/layui/layEditorOptionsFrame.h b/src/layview/layview/layEditorOptionsFrame.h similarity index 91% rename from src/layui/layui/layEditorOptionsFrame.h rename to src/layview/layview/layEditorOptionsFrame.h index b33989423..1ea107ccb 100644 --- a/src/layui/layui/layEditorOptionsFrame.h +++ b/src/layview/layview/layEditorOptionsFrame.h @@ -20,12 +20,10 @@ */ -#if defined(HAVE_QT) - #ifndef HDR_layEditorOptionsFrame #define HDR_layEditorOptionsFrame -#include "layuiCommon.h" +#include "layviewCommon.h" #include namespace lay @@ -34,7 +32,7 @@ namespace lay class EditorOptionsPages; class LayoutViewBase; -class LAYUI_PUBLIC EditorOptionsFrame +class LAYVIEW_PUBLIC EditorOptionsFrame : public QFrame { public: @@ -55,5 +53,3 @@ class LAYUI_PUBLIC EditorOptionsFrame } #endif - -#endif // defined(HAVE_QT) diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc similarity index 78% rename from src/laybasic/laybasic/layEditorOptionsPages.cc rename to src/layview/layview/layEditorOptionsPages.cc index 994369917..976872f9f 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -20,8 +20,6 @@ */ -#if defined(HAVE_QT) - #include "tlInternational.h" #include "layEditorOptionsPages.h" #include "tlExceptions.h" @@ -51,8 +49,8 @@ struct EOPCompareOp } }; -EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *dispatcher) - : QFrame (parent), mp_dispatcher (dispatcher) +EditorOptionsPages::EditorOptionsPages (QWidget *parent, lay::LayoutViewBase *view, const std::vector &pages) + : QFrame (parent), mp_view (view) { mp_modal_pages = new EditorOptionsModalPages (this); @@ -63,9 +61,14 @@ EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vectorsetSizePolicy (QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored)); ly1->addWidget (mp_pages); - m_pages = pages; - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - (*p)->set_owner (this); + for (auto p = pages.begin (); p != pages.end (); ++p) { + m_pages.push_back (*p); + } + + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (! p->is_toolbox_widget ()) { + p->set_owner (this); + } } update (0); @@ -91,11 +94,39 @@ EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) } } +const tl::weak_collection & +EditorOptionsPages::pages () const +{ + return m_pages; +} + +std::vector +EditorOptionsPages::editor_options_pages (const lay::PluginDeclaration *plugin_declaration) +{ + std::vector pages; + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->plugin_declaration () == plugin_declaration) { + pages.push_back (const_cast (p.operator-> ())); + } + } + return pages; +} + +std::vector +EditorOptionsPages::editor_options_pages () +{ + std::vector pages; + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + pages.push_back (const_cast (p.operator-> ())); + } + return pages; +} + bool EditorOptionsPages::has_content () const { - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active () && ! (*p)->is_modal_page ()) { + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->active () && ! p->is_modal_page () && ! p->is_toolbox_widget ()) { return true; } } @@ -105,8 +136,8 @@ EditorOptionsPages::has_content () const bool EditorOptionsPages::has_modal_content () const { - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active () && (*p)->is_modal_page ()) { + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->active () && p->is_modal_page () && ! p->is_toolbox_widget ()) { return true; } } @@ -122,7 +153,7 @@ EditorOptionsPages::exec_modal (EditorOptionsPage *page) // found the page - make it current and show the dialog mp_modal_pages->set_current_index (i); - page->setup (mp_dispatcher); + page->setup (mp_view); page->set_focus (); return mp_modal_pages->exec () != 0; @@ -138,25 +169,19 @@ EditorOptionsPages::activate (const lay::Plugin *plugin) { for (auto op = m_pages.begin (); op != m_pages.end (); ++op) { bool is_active = false; - if ((*op)->plugin_declaration () == 0) { + if (op->plugin_declaration () == 0) { is_active = (plugin && plugin->plugin_declaration ()->enable_catchall_editor_options_pages ()); - } else if (plugin && plugin->plugin_declaration () == (*op)->plugin_declaration ()) { + } else if (plugin && plugin->plugin_declaration () == op->plugin_declaration ()) { is_active = true; } - (*op)->activate (is_active); + op->activate (is_active); } } void EditorOptionsPages::unregister_page (lay::EditorOptionsPage *page) { - std::vector pages; - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if (*p != page) { - pages.push_back (*p); - } - } - m_pages = pages; + m_pages.erase (page); update (0); } @@ -164,9 +189,9 @@ void EditorOptionsPages::make_page_current (lay::EditorOptionsPage *page) { for (int i = 0; i < mp_pages->count (); ++i) { - if (mp_pages->widget (i) == page) { + if (mp_pages->widget (i) == page->widget ()) { mp_pages->setCurrentIndex (i); - page->setup (mp_dispatcher); + page->setup (mp_view); page->set_focus (); break; } @@ -178,7 +203,7 @@ EditorOptionsPages::activate_page (lay::EditorOptionsPage *page) { try { if (page->active ()) { - page->setup (mp_dispatcher); + page->setup (mp_view); } } catch (...) { // catch any errors related to configuration file errors etc. @@ -190,7 +215,12 @@ EditorOptionsPages::activate_page (lay::EditorOptionsPage *page) void EditorOptionsPages::update (lay::EditorOptionsPage *page) { - std::vector sorted_pages = m_pages; + std::vector sorted_pages; + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->widget ()) { + sorted_pages.push_back (p->widget ()); + } + } std::sort (sorted_pages.begin (), sorted_pages.end (), EOPCompareOp ()); if (! page && m_pages.size () > 0) { @@ -208,9 +238,11 @@ EditorOptionsPages::update (lay::EditorOptionsPage *page) int index = -1; int modal_index = -1; - for (std::vector ::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { + for (auto p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { if ((*p)->active ()) { - if (! (*p)->is_modal_page ()) { + if ((*p)->is_toolbox_widget ()) { + mp_view->add_toolbox_widget (*p); + } else if (! (*p)->is_modal_page ()) { if ((*p) == page) { index = mp_pages->count (); } @@ -250,9 +282,9 @@ EditorOptionsPages::setup () { BEGIN_PROTECTED - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - (*p)->setup (mp_dispatcher); + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->active ()) { + p->setup (mp_view); } } @@ -267,10 +299,10 @@ END_PROTECTED_W (this) void EditorOptionsPages::do_apply (bool modal) { - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active () && modal == (*p)->is_modal_page ()) { + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->active () && modal == p->is_modal_page ()) { // NOTE: we apply to the root dispatcher, so other dispatchers (views) get informed too. - (*p)->apply (mp_dispatcher->dispatcher ()); + p->apply (mp_view->dispatcher ()); } } } @@ -349,7 +381,7 @@ EditorOptionsModalPages::set_current_index (int index) } void -EditorOptionsModalPages::add_page (EditorOptionsPage *page) +EditorOptionsModalPages::add_page (EditorOptionsPageWidget *page) { if (! mp_single_page) { if (mp_pages->count () == 0) { @@ -388,7 +420,7 @@ EditorOptionsModalPages::remove_page (int index) mp_pages->removeTab (index); if (mp_pages->count () == 1) { mp_pages->hide (); - mp_single_page = dynamic_cast (mp_pages->widget (0)); + mp_single_page = dynamic_cast (mp_pages->widget (0)); mp_pages->removeTab (0); mp_single_page->setParent (mp_single_page_frame); mp_single_page_frame->layout ()->addWidget (mp_single_page); @@ -445,5 +477,3 @@ END_PROTECTED } } - -#endif diff --git a/src/laybasic/laybasic/layEditorOptionsPages.h b/src/layview/layview/layEditorOptionsPages.h similarity index 66% rename from src/laybasic/laybasic/layEditorOptionsPages.h rename to src/layview/layview/layEditorOptionsPages.h index 7ce48559c..4eac5cb7b 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.h +++ b/src/layview/layview/layEditorOptionsPages.h @@ -20,14 +20,14 @@ */ -#if defined(HAVE_QT) - #ifndef HDR_layEditorOptionsPages #define HDR_layEditorOptionsPages -#include "laybasicCommon.h" +#include "layviewCommon.h" #include "layEditorOptionsPage.h" +#include "tlObjectCollection.h" + #include #include @@ -50,29 +50,27 @@ class EditorOptionsModalPages; /** * @brief The object properties tab widget */ -class LAYBASIC_PUBLIC EditorOptionsPages - : public QFrame +class LAYVIEW_PUBLIC EditorOptionsPages + : public QFrame, public lay::EditorOptionsPageCollection { Q_OBJECT public: - EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root); + EditorOptionsPages (QWidget *parent, lay::LayoutViewBase *view, const std::vector &pages); ~EditorOptionsPages (); - void unregister_page (lay::EditorOptionsPage *page); - void activate_page (lay::EditorOptionsPage *page); - void activate (const lay::Plugin *plugin); - void focusInEvent (QFocusEvent *event); - void make_page_current (lay::EditorOptionsPage *page); - bool exec_modal (lay::EditorOptionsPage *page); + virtual void unregister_page (lay::EditorOptionsPage *page); + virtual bool has_content () const; + virtual bool has_modal_content () const; + virtual void activate_page (lay::EditorOptionsPage *page); + virtual void make_page_current (lay::EditorOptionsPage *page); + virtual bool exec_modal (lay::EditorOptionsPage *page); + virtual std::vector editor_options_pages (const lay::PluginDeclaration *plugin_declaration); + virtual std::vector editor_options_pages (); + virtual void activate (const lay::Plugin *plugin); - const std::vector &pages () const - { - return m_pages; - } + const tl::weak_collection &pages () const; - bool has_content () const; - bool has_modal_content () const; void do_apply (bool modal); public slots: @@ -80,12 +78,13 @@ public slots: void setup (); private: - std::vector m_pages; - lay::Dispatcher *mp_dispatcher; + tl::weak_collection m_pages; + lay::LayoutViewBase *mp_view; QTabWidget *mp_pages; EditorOptionsModalPages *mp_modal_pages; void update (lay::EditorOptionsPage *page); + void focusInEvent (QFocusEvent *event); }; /** @@ -103,7 +102,7 @@ Q_OBJECT int count (); int current_index (); void set_current_index (int index); - void add_page (EditorOptionsPage *page); + void add_page (EditorOptionsPageWidget *page); void remove_page (int index); EditorOptionsPage *widget (int index); @@ -116,7 +115,7 @@ private slots: EditorOptionsPages *mp_parent; QTabWidget *mp_pages; QFrame *mp_single_page_frame; - EditorOptionsPage *mp_single_page; + EditorOptionsPageWidget *mp_single_page; QDialogButtonBox *mp_button_box; void update_title (); @@ -126,4 +125,3 @@ private slots: #endif -#endif // defined(HAVE_QT) diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index 2b74c67c8..d1f43c998 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -190,6 +190,13 @@ LayoutViewWidget::~LayoutViewWidget () delete view; } +void +LayoutViewWidget::add_toolbox_widget (lay::EditorOptionsPageWidget *toolbox_widget) +{ + toolbox_widget->setParent (this); + mp_layout->insertWidget (0, toolbox_widget); +} + void LayoutViewWidget::add_notification (const LayoutViewNotification ¬ificaton) { @@ -461,6 +468,22 @@ LayoutView::add_notification (const LayoutViewNotification ¬ification) } } +void +LayoutView::remove_notification (const LayoutViewNotification ¬ification) +{ + if (mp_widget) { + mp_widget->remove_notification (notification); + } +} + +void +LayoutView::add_toolbox_widget (lay::EditorOptionsPage *toolbox_widget) +{ + if (mp_widget && toolbox_widget->widget ()) { + mp_widget->add_toolbox_widget (toolbox_widget->widget ()); + } +} + bool LayoutView::event_filter (QObject *obj, QEvent *event, bool &taken) { @@ -655,7 +678,7 @@ QWidget *LayoutView::widget () return mp_widget; } -void LayoutView::close() +void LayoutView::close () { close_event (); close_event.clear (); @@ -758,7 +781,7 @@ LayoutView::do_change_active_cellview () dm_setup_editor_option_pages (); } -lay::EditorOptionsPages *LayoutView::editor_options_pages () +lay::EditorOptionsPageCollection *LayoutView::editor_options_pages () { if (! mp_editor_options_frame) { return 0; @@ -770,9 +793,10 @@ lay::EditorOptionsPages *LayoutView::editor_options_pages () void LayoutView::do_setup_editor_options_pages () { // initialize the editor option pages - lay::EditorOptionsPages *eo_pages = editor_options_pages (); + lay::EditorOptionsPageCollection *eo_pages = editor_options_pages (); if (eo_pages) { - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + auto pages = eo_pages->editor_options_pages (); + for (auto op = pages.begin (); op != pages.end (); ++op) { (*op)->setup (this); } } @@ -1613,7 +1637,7 @@ LayoutView::mode (int m) void LayoutView::activate_editor_option_pages () { - lay::EditorOptionsPages *eo_pages = editor_options_pages (); + lay::EditorOptionsPageCollection *eo_pages = editor_options_pages (); if (eo_pages) { eo_pages->activate (active_plugin ()); } diff --git a/src/layview/layview/layLayoutView_qt.h b/src/layview/layview/layLayoutView_qt.h index 548e13012..7cb72ac60 100644 --- a/src/layview/layview/layLayoutView_qt.h +++ b/src/layview/layview/layLayoutView_qt.h @@ -89,6 +89,7 @@ class Browser; class ColorButton; class ConfigureAction; class EditorOptionsPages; +class EditorOptionsPageWidget; class PropertiesDialog; /** @@ -154,6 +155,19 @@ class LAYVIEW_PUBLIC LayoutView */ virtual void add_notification (const LayoutViewNotification ¬ification); + /** + * @brief Removes a notification + */ + virtual void remove_notification (const LayoutViewNotification ¬ification); + + /** + * @brief Adds a toolbox widget + * + * This will take ownership over the EditorOptionsPage object until + * it is re-parented. + */ + virtual void add_toolbox_widget (EditorOptionsPage *toolbox_widget); + /** * @brief Gets the widget object that view is embedded in */ @@ -376,7 +390,7 @@ class LAYVIEW_PUBLIC LayoutView /** * @brief Gets the editor options pages */ - virtual lay::EditorOptionsPages *editor_options_pages (); + virtual lay::EditorOptionsPageCollection *editor_options_pages (); /** * @brief Gets the layer control panel @@ -750,6 +764,8 @@ Q_OBJECT /** * @brief Adds a notification + * + * Notifications are banners that pop up at the top of the view canvas. */ void add_notification (const LayoutViewNotification ¬ification); @@ -758,6 +774,15 @@ Q_OBJECT */ void remove_notification (const LayoutViewNotification ¬ification); + /** + * @brief Adds a tool box widget + * + * Toolbox widgets are EditorOptionsPage widgets that are placed + * at the top of the view canvas instead of being put into + * the editor options panel. + */ + void add_toolbox_widget (lay::EditorOptionsPageWidget *toolbox_widget); + /** * @brief Gets the LayoutView embedded into this widget */ diff --git a/src/layview/layview/layview.pro b/src/layview/layview/layview.pro index 4fd39c63e..3d5ab457a 100644 --- a/src/layview/layview/layview.pro +++ b/src/layview/layview/layview.pro @@ -10,11 +10,15 @@ RESOURCES = \ SOURCES = \ layGridNet.cc \ + layEditorOptionsFrame.cc \ + layEditorOptionsPages.cc \ layviewForceLink.cc \ gsiDeclLayAdditional.cc \ HEADERS = \ layGridNet.h \ + layEditorOptionsFrame.h \ + layEditorOptionsPages.h \ layLayoutView.h \ layviewForceLink.h \ From 0b9a0c3af1933d2faa3b2d204c60fb6eba8fccfe Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 12 Jan 2026 17:05:38 +0100 Subject: [PATCH 08/36] WIP, e.g. avoiding sticky selection when aborting 'move' by chosing a different mode --- src/laybasic/laybasic/layEditorOptionsPage.cc | 19 ++++++++++ src/laybasic/laybasic/layEditorOptionsPage.h | 7 ++++ src/laybasic/laybasic/layLayoutViewBase.h | 11 ------ src/laybasic/laybasic/layMove.cc | 36 +++++++++++++++++-- src/laybasic/laybasic/layMove.h | 1 + src/layview/layview/layEditorOptionsFrame.cc | 8 ++--- src/layview/layview/layEditorOptionsPages.cc | 17 +++++---- src/layview/layview/layEditorOptionsPages.h | 3 +- 8 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index e489bc1d8..cee43ea82 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -31,6 +31,14 @@ namespace lay { +// ------------------------------------------------------------------ +// EditorOptionsPageCollection implementation + +EditorOptionsPageCollection::EditorOptionsPageCollection () +{ + // .. nothing yet .. +} + // ------------------------------------------------------------------ // EditorOptionsPage implementation @@ -115,6 +123,11 @@ EditorOptionsPage::activate (bool active) if (mp_owner) { mp_owner->activate_page (this); } + if (m_active) { + activated (); + } else { + deactivated (); + } } } @@ -191,6 +204,12 @@ EditorOptionsPageWidget::set_focus () QWidget::focusNextPrevChild (true); } +void +EditorOptionsPageWidget::set_visible (bool visible) +{ + setVisible (visible); +} + #endif } diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 33a553034..b4a541e40 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -53,6 +53,7 @@ class EditorOptionsPageWidget; class LAYBASIC_PUBLIC EditorOptionsPageCollection { public: + EditorOptionsPageCollection (); virtual ~EditorOptionsPageCollection () { } virtual void unregister_page (EditorOptionsPage *page) = 0; @@ -64,6 +65,7 @@ class LAYBASIC_PUBLIC EditorOptionsPageCollection virtual bool exec_modal (EditorOptionsPage *page) = 0; virtual std::vector editor_options_pages (const lay::PluginDeclaration *plugin) = 0; virtual std::vector editor_options_pages () = 0; + virtual lay::EditorOptionsPage *page_with_name (const std::string &name) = 0; }; /** @@ -79,11 +81,13 @@ class LAYBASIC_PUBLIC EditorOptionsPage virtual std::string title () const = 0; virtual int order () const = 0; + virtual const char *name () const { return 0; } virtual void apply (lay::Dispatcher * /*root*/) { } virtual void setup (lay::Dispatcher * /*root*/) { } virtual void commit_recent (lay::Dispatcher * /*root*/) { } virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { } virtual void set_focus () { } + virtual void set_visible (bool /*visible*/) { } virtual EditorOptionsPageWidget *widget () { return 0; } bool is_focus_page () const { return m_focus_page; } @@ -123,6 +127,8 @@ class LAYBASIC_PUBLIC EditorOptionsPage protected: virtual void active_cellview_changed () { } virtual void technology_changed (const std::string & /*tech*/) { } + virtual void activated () { } + virtual void deactivated () { } private: EditorOptionsPageCollection *mp_owner; @@ -153,6 +159,7 @@ Q_OBJECT virtual ~EditorOptionsPageWidget (); virtual void set_focus (); + virtual void set_visible (bool visible); virtual EditorOptionsPageWidget *widget () { return this; } protected slots: diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 1423ec49b..63c2c3fe4 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -360,17 +360,6 @@ class LAYBASIC_PUBLIC LayoutViewBase : // the base implementation does nothing } - /** - * @brief Shows or hides a toolbox widget with the given name - * - * Initially toolbox widgets are invisible. They are made visible - * by using this method. - */ - virtual void show_toolbox_widget (const std::string & /*name*/, bool /*visible*/) - { - // the base implementation does nothing - } - /** * @brief Adds an editor options page as a toolbox widget */ diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 671b7cc69..e3866e04d 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -37,6 +37,8 @@ namespace lay { +const char *move_editor_options_name = "move-editor-options"; + // ------------------------------------------------------------- // MoveService implementation @@ -62,7 +64,11 @@ MoveService::deactivated () EditorServiceBase::deactivated (); m_shift = db::DPoint (); mp_editables->clear_transient_selection (); - drag_cancel (); + + if (m_dragging) { + // we don't just call drag_cancel() - this way avoids pending selections with the wrong coordinates + mp_view->edit_cancel (); + } } bool @@ -127,6 +133,15 @@ MoveService::key_event (unsigned int key, unsigned int buttons) } } +void +MoveService::show_toolbox (bool visible) +{ + lay::EditorOptionsPage *op = mp_view->editor_options_pages () ? mp_view->editor_options_pages ()->page_with_name (move_editor_options_name) : 0; + if (op) { + op->set_visible (visible); + } +} + int MoveService::focus_page_open () { @@ -320,6 +335,8 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_ m_dragging = true; m_dragging_transient = drag_transient; + + show_toolbox (true); ui ()->grab_mouse (this, false); m_shift = db::DPoint (); @@ -332,7 +349,9 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_ m_dragging = false; + show_toolbox (false); ui ()->ungrab_mouse (this); + mp_editables->end_move (p, ac_from_buttons (buttons), mp_transaction.release ()); if (m_dragging_transient) { @@ -350,6 +369,7 @@ MoveService::drag_cancel () { m_shift = db::DPoint (); if (m_dragging) { + show_toolbox (false); ui ()->ungrab_mouse (this); m_dragging = false; } @@ -394,8 +414,7 @@ class MoveToolboxPage mp_layout->addWidget (mp_y_le); mp_layout->addStretch (1); - // @@@ - + hide (); set_toolbox_widget (true); } @@ -404,11 +423,21 @@ class MoveToolboxPage return "Move Options"; } + virtual const char *name () const + { + return move_editor_options_name; + } + virtual int order () const { return 0; } + virtual void deactivated () + { + hide (); + } + private: QHBoxLayout *mp_layout; QLineEdit *mp_x_le, *mp_y_le; @@ -436,6 +465,7 @@ class MoveServiceDeclaration virtual void get_editor_options_pages (std::vector &pages, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) const { pages.push_back (new MoveToolboxPage (view, dispatcher)); + pages.back ()->set_plugin_declaration (this); } #endif }; diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index a8cd8cdb9..9fdfaf357 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -57,6 +57,7 @@ class LAYBASIC_PUBLIC MoveService : virtual void drag_cancel (); virtual void deactivated (); int focus_page_open (); + void show_toolbox (bool visible); bool handle_click (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction); diff --git a/src/layview/layview/layEditorOptionsFrame.cc b/src/layview/layview/layEditorOptionsFrame.cc index 135d34bcb..bd71de541 100644 --- a/src/layview/layview/layEditorOptionsFrame.cc +++ b/src/layview/layview/layEditorOptionsFrame.cc @@ -49,12 +49,12 @@ EditorOptionsFrame::~EditorOptionsFrame () void EditorOptionsFrame::populate (LayoutViewBase *view) { - std::vector prop_dialog_pages; + std::vector editor_options_pages; for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - cls->get_editor_options_pages (prop_dialog_pages, view, view->dispatcher ()); + cls->get_editor_options_pages (editor_options_pages, view, view->dispatcher ()); } - for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { + for (std::vector::const_iterator op = editor_options_pages.begin (); op != editor_options_pages.end (); ++op) { (*op)->activate (false); } @@ -62,7 +62,7 @@ EditorOptionsFrame::populate (LayoutViewBase *view) delete mp_pages; } - mp_pages = new lay::EditorOptionsPages (this, view, prop_dialog_pages); + mp_pages = new lay::EditorOptionsPages (this, view, editor_options_pages); layout ()->addWidget (mp_pages); setFocusProxy (mp_pages); } diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index 976872f9f..ebcca5323 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -94,12 +94,6 @@ EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) } } -const tl::weak_collection & -EditorOptionsPages::pages () const -{ - return m_pages; -} - std::vector EditorOptionsPages::editor_options_pages (const lay::PluginDeclaration *plugin_declaration) { @@ -185,6 +179,17 @@ EditorOptionsPages::unregister_page (lay::EditorOptionsPage *page) update (0); } +lay::EditorOptionsPage * +EditorOptionsPages::page_with_name (const std::string &name) +{ + for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { + if (p->name () && name == p->name ()) { + return p.operator-> (); + } + } + return 0; +} + void EditorOptionsPages::make_page_current (lay::EditorOptionsPage *page) { diff --git a/src/layview/layview/layEditorOptionsPages.h b/src/layview/layview/layEditorOptionsPages.h index 4eac5cb7b..2e0bc100f 100644 --- a/src/layview/layview/layEditorOptionsPages.h +++ b/src/layview/layview/layEditorOptionsPages.h @@ -68,8 +68,7 @@ Q_OBJECT virtual std::vector editor_options_pages (const lay::PluginDeclaration *plugin_declaration); virtual std::vector editor_options_pages (); virtual void activate (const lay::Plugin *plugin); - - const tl::weak_collection &pages () const; + virtual lay::EditorOptionsPage *page_with_name (const std::string &name); void do_apply (bool modal); From 67790c0ce71e5bfb89f23e82755113bd521fb8fe Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 12 Jan 2026 19:32:29 +0100 Subject: [PATCH 09/36] WIP --- src/laybasic/laybasic/gsiDeclLayPlugin.cc | 21 ++++++ src/laybasic/laybasic/gsiDeclLayPlugin.h | 2 + src/laybasic/laybasic/layEditorOptionsPage.cc | 11 ++- src/laybasic/laybasic/layEditorServiceBase.cc | 6 ++ src/laybasic/laybasic/layEditorServiceBase.h | 5 ++ src/laybasic/laybasic/layMove.cc | 42 +++++++++--- src/laybasic/laybasic/layMove.h | 1 - src/laybasic/laybasic/layViewObject.cc | 68 +++++++++++++------ src/laybasic/laybasic/layViewObject.h | 21 +++++- 9 files changed, 143 insertions(+), 34 deletions(-) diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.cc b/src/laybasic/laybasic/gsiDeclLayPlugin.cc index ae83521cb..d3e1d6f5a 100644 --- a/src/laybasic/laybasic/gsiDeclLayPlugin.cc +++ b/src/laybasic/laybasic/gsiDeclLayPlugin.cc @@ -415,6 +415,16 @@ PluginImpl::key_event (unsigned int key, unsigned int buttons) } } +bool +PluginImpl::shortcut_override_event (unsigned int key, unsigned int buttons) +{ + if (f_shortcut_override_event.can_issue ()) { + return f_shortcut_override_event.issue (&lay::ViewService::shortcut_override_event, key, buttons); + } else { + return lay::EditorServiceBase::shortcut_override_event (key, buttons); + } +} + bool PluginImpl::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) { @@ -728,6 +738,17 @@ Class decl_Plugin (decl_PluginBase, "lay", "Plugin", "@param buttons A combination of the constants in the \\ButtonState class which codes both the mouse buttons and the key modifiers (.e. ShiftButton etc).\n" "@return True to terminate dispatcher\n" ) + + callback ("shortcut_override_event", &gsi::PluginImpl::shortcut_override_event, &gsi::PluginImpl::f_shortcut_override_event, gsi::arg ("key"), gsi::arg ("buttons"), + "@brief Allows overriding keyboard shortcuts for this plugin\n" + "If the implementation returns true, the given key is not handled by the shortcut system, but rather\n" + "passed to 'key_event' the usual way.\n" + "\n" + "@param key The Qt key code of the key that was pressed\n" + "@param buttons A combination of the constants in the \\ButtonState class which codes both the mouse buttons and the key modifiers (.e. ShiftButton etc).\n" + "@return True to request 'key_event' handling\n" + "\n" + "This method has been introduced in version 0.30.5." + ) + callback ("mouse_button_pressed_event", &gsi::PluginImpl::mouse_press_event_noref, &gsi::PluginImpl::f_mouse_press_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button pressed event\n" "This method will called by the view when a button is pressed on the mouse.\n" diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.h b/src/laybasic/laybasic/gsiDeclLayPlugin.h index c1a442633..8bc0e3eb9 100644 --- a/src/laybasic/laybasic/gsiDeclLayPlugin.h +++ b/src/laybasic/laybasic/gsiDeclLayPlugin.h @@ -62,6 +62,7 @@ class PluginImpl virtual void config_finalize_impl (); virtual void config_finalize (); virtual bool key_event (unsigned int key, unsigned int buttons); + virtual bool shortcut_override_event (unsigned int key, unsigned int buttons); virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) ; bool mouse_press_event_noref (db::DPoint p, unsigned int buttons, bool prio); virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio); @@ -116,6 +117,7 @@ class PluginImpl gsi::Callback f_configure; gsi::Callback f_config_finalize; gsi::Callback f_key_event; + gsi::Callback f_shortcut_override_event; gsi::Callback f_mouse_press_event; gsi::Callback f_mouse_click_event; gsi::Callback f_mouse_double_click_event; diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index cee43ea82..291d815db 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -70,7 +70,9 @@ EditorOptionsPage::init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) int EditorOptionsPage::show () { - if (mp_owner && m_active) { + if (! m_active) { + return -1; + } else if (mp_owner) { if (! is_modal_page ()) { mp_owner->make_page_current (this); return -1; @@ -78,6 +80,7 @@ EditorOptionsPage::show () return mp_owner->exec_modal (this) ? 1 : 0; } } else { + set_focus (); return -1; } } @@ -200,8 +203,10 @@ END_PROTECTED void EditorOptionsPageWidget::set_focus () { - setFocus (Qt::TabFocusReason); - QWidget::focusNextPrevChild (true); + if (isVisible ()) { + setFocus (Qt::TabFocusReason); + QWidget::focusNextPrevChild (true); + } } void diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index a3ae88baf..0783817ba 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -379,6 +379,12 @@ EditorServiceBase::key_event (unsigned int key, unsigned int buttons) } } +bool +EditorServiceBase::shortcut_override_event (unsigned int key, unsigned int buttons) +{ + return is_active () && key == Qt::Key_Tab && buttons == 0 && focus_page (); +} + int EditorServiceBase::focus_page_open () { diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h index 641aac92f..86441aadc 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.h +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -185,6 +185,11 @@ class LAYBASIC_PUBLIC EditorServiceBase */ virtual bool key_event (unsigned int /*key*/, unsigned int /*buttons*/); + /** + * @brief Shortcut override event handler + */ + virtual bool shortcut_override_event (unsigned int /*key*/, unsigned int /*buttons*/); + /** * @brief Mouse press event handler */ diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index e3866e04d..0664a4bc9 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -30,6 +30,7 @@ #if defined(HAVE_QT) # include "layEditorOptionsPage.h" # include +# include // @@@ # include # include #endif @@ -142,16 +143,6 @@ MoveService::show_toolbox (bool visible) } } -int -MoveService::focus_page_open () -{ - // This method is called on "Tab" by "key_event". "fp" is null as we don't have a focus page registered. - if (is_active () && dispatcher ()) { - dispatcher ()->menu_activated ("cm_sel_move"); - } - return 0; -} - bool MoveService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { @@ -415,6 +406,8 @@ class MoveToolboxPage mp_layout->addStretch (1); hide (); + + set_focus_page (true); set_toolbox_widget (true); } @@ -438,6 +431,35 @@ class MoveToolboxPage hide (); } + virtual void keyPressEvent (QKeyEvent *event) + { + if (event->key () == Qt::Key_Escape) { + tl::info << "@@@ Escape!"; + view ()->set_focus (); + } else if (event->key () == Qt::Key_Enter || event->key () == Qt::Key_Return) { + tl::info << "@@@ Accept!"; + view ()->set_focus (); + } + QWidget::keyPressEvent (event); + } + + virtual bool event (QEvent *event) + { + if (event->type () == QEvent::ShortcutOverride) { + QKeyEvent *ke = dynamic_cast (event); + if (ke->key () == Qt::Key_Escape || + ke->key () == Qt::Key_Tab || + ke->key () == Qt::Key_Enter || + ke->key () == Qt::Key_Return || + ke->key () == Qt::Key_Backtab) { + // accept the shortcut override event for some keys, so we can handle + // it in keyPressEvent + ke->accept (); + } + } + return QWidget::event (event); + } + private: QHBoxLayout *mp_layout; QLineEdit *mp_x_le, *mp_y_le; diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 9fdfaf357..635c25f31 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -56,7 +56,6 @@ class LAYBASIC_PUBLIC MoveService : virtual bool key_event (unsigned int key, unsigned int buttons); virtual void drag_cancel (); virtual void deactivated (); - int focus_page_open (); void show_toolbox (bool visible); bool handle_click (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction); diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index 488ff5fbb..44094faf6 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -257,6 +257,37 @@ class ViewObjectQWidget : public QWidget END_PROTECTED } + bool event (QEvent *e) + { + if (e->type () == QEvent::MaxUser) { + + // GTF probe event + // record the contents (the screenshot) as ASCII text + mp_view->gtf_probe (); + + e->accept (); + return true; + + } else if (e->type () == QEvent::ShortcutOverride) { + + BEGIN_PROTECTED + QKeyEvent *ke = dynamic_cast (e); + if (ke) { + unsigned int buttons = qt_to_buttons (Qt::MouseButtons (), ke->modifiers ()); + if (mp_view->send_shortcut_override_event ((unsigned int) ke->key (), buttons)) { + e->accept (); + return true; + } + } + END_PROTECTED + + return false; + + } else { + return QWidget::event (e); + } + } + DragDropDataBase *get_drag_drop_data (const QMimeData *data) { if (! data || ! data->hasFormat (QString::fromUtf8 (drag_drop_mime_type ()))) { @@ -481,24 +512,6 @@ class ViewObjectQWidget : public QWidget END_PROTECTED } -#if defined(HAVE_QT) - bool event (QEvent *e) - { - if (e->type () == QEvent::MaxUser) { - - // GTF probe event - // record the contents (the screenshot) as ASCII text - mp_view->gtf_probe (); - - e->accept (); - return true; - - } else { - return QWidget::event (e); - } - } -#endif - private: ViewObjectUI *mp_view; }; @@ -674,7 +687,7 @@ ViewObjectUI::realize_cursor () #endif } -void +bool ViewObjectUI::send_key_press_event (unsigned int key, unsigned int buttons) { bool done = false; @@ -685,6 +698,23 @@ ViewObjectUI::send_key_press_event (unsigned int key, unsigned int buttons) if (! done) { key_event (key, buttons); } + + return done; +} + +bool +ViewObjectUI::send_shortcut_override_event(unsigned int key, unsigned int buttons) +{ + bool done = false; + if (mp_active_service) { + done = (mp_active_service->enabled () && mp_active_service->shortcut_override_event (key, buttons)); + } + + if (! done) { + key_event (key, buttons); + } + + return done; } void diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 6ef9b691e..2c680e268 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -125,6 +125,17 @@ class LAYBASIC_PUBLIC ViewService */ virtual bool key_event (unsigned int /*key*/, unsigned int /*buttons*/) { return false; } + /** + * @brief Handler for the shortcut override event + * + * This method will be called by the ViewObjectWidget object to + * ask if a plugin wants to consume a key event. + * + * If the implementation returns true, the key is passed through "key_event". Otherwise + * it is handled by the shortcut system. + */ + virtual bool shortcut_override_event (unsigned int /*key*/, unsigned int /*buttons*/) { return false; } + #if defined(HAVE_QT) /** * @brief The drag enter event @@ -1023,7 +1034,15 @@ class LAYBASIC_PUBLIC ViewObjectUI : /** * @brief External entry point for key press event generation */ - void send_key_press_event (unsigned int key, unsigned int buttons); + bool send_key_press_event(unsigned int key, unsigned int buttons); + + /** + * @brief External entry point for ShortcutOverride event handling + * + * Editables may return true to indicate that they want to consume the given key + * sequence through "key_pressed" instead of being handled by Qt's shortcut system. + */ + bool send_shortcut_override_event(unsigned int key, unsigned int buttons); /** * @brief External entry point for mouse move event generation From 8add404adcef1aa2c9744edec5c3738d4586621f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 12 Jan 2026 21:00:24 +0100 Subject: [PATCH 10/36] WIP --- .../laybasic/gsiDeclLayEditorOptionsPage.cc | 31 +++++++++++++++ .../laybasic/gsiDeclLayEditorOptionsPage.h | 3 ++ src/laybasic/laybasic/layEditorOptionsPage.cc | 39 +++++++++++++++++-- src/laybasic/laybasic/layEditorOptionsPage.h | 7 +++- src/laybasic/laybasic/layEditorServiceBase.cc | 12 ++++-- src/laybasic/laybasic/layMove.cc | 27 ++----------- 6 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc index 7a63b0df5..1d2d335fd 100644 --- a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc +++ b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc @@ -73,6 +73,10 @@ Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWid ) + method ("setup", &lay::EditorOptionsPage::setup, gsi::arg ("dispatcher"), "@brief Transfers data from the configuration to the page\n" + ) + + method ("cancel", &lay::EditorOptionsPage::cancel, + "@brief Gets called when the Escape key is pressed on a non-modal page\n" + "This method has been introduced in version 0.30.6." ), "@brief The plugin framework's editor options page base class\n" "\n" @@ -111,6 +115,11 @@ static void setup_fb (EditorOptionsPageImpl *ep, lay::Dispatcher *root) ep->lay::EditorOptionsPage::setup (root); } +static void cancel_fb (EditorOptionsPageImpl *ep) +{ + ep->lay::EditorOptionsPage::cancel (); +} + void EditorOptionsPageImpl::apply_impl (lay::Dispatcher *root) { @@ -143,6 +152,22 @@ EditorOptionsPageImpl::setup (lay::Dispatcher *root) } } +void +EditorOptionsPageImpl::cancel_impl () +{ + lay::EditorOptionsPage::cancel (); +} + +void +EditorOptionsPageImpl::cancel () +{ + if (f_cancel.can_issue ()) { + f_cancel.issue (&EditorOptionsPageImpl::cancel_impl); + } else { + EditorOptionsPageImpl::cancel_impl (); + } +} + EditorOptionsPageImpl *new_editor_options_page (const std::string &title, int index) { return new EditorOptionsPageImpl (title, index); @@ -173,6 +198,12 @@ Class decl_EditorOptionsPage (decl_EditorOptionsPageBase, "In this method, you should transfer all configuration data to the widgets.\n" "Use \\Dispatcher#get_config on the dispatcher object ('dispatcher' argument) to get a configuration parameter " "and set the editing widget's state accordingly.\n" + ) + + // prevents infinite recursion + method_ext ("cancel", &cancel_fb, "@hide") + + callback ("cancel", &EditorOptionsPageImpl::cancel, &EditorOptionsPageImpl::f_cancel, + "@brief Reimplement this method to receive Escape key events for the page\n" + "This method has been added in version 0.30.6.\n" ), "@brief The plugin framework's editor options page\n" "\n" diff --git a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h index 0e1598898..c679b0d0e 100644 --- a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h +++ b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h @@ -53,9 +53,11 @@ class EditorOptionsPageImpl void call_edited (); virtual void apply (lay::Dispatcher *root); virtual void setup (lay::Dispatcher *root); + virtual void cancel (); gsi::Callback f_apply; gsi::Callback f_setup; + gsi::Callback f_cancel; private: tl::weak_ptr mp_view; @@ -65,6 +67,7 @@ class EditorOptionsPageImpl void apply_impl (lay::Dispatcher *root); void setup_impl (lay::Dispatcher *root); + void cancel_impl (); }; } diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index 291d815db..44bd772f1 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -188,10 +188,17 @@ void EditorOptionsPageWidget::keyPressEvent (QKeyEvent *event) { BEGIN_PROTECTED - if (! is_modal_page () && event->modifiers () == Qt::NoModifier && event->key () == Qt::Key_Return) { - // The Return key on a non-modal page commits the values and gives back the focus - // to the view - apply (dispatcher ()); + if (! is_modal_page () && + event->modifiers () == Qt::NoModifier && + (event->key () == Qt::Key_Return || event->key () == Qt::Key_Enter || event->key () == Qt::Key_Escape)) { + if (event->key () == Qt::Key_Escape) { + // The Escape key creates a call to cancel() + cancel (); + } else { + // The Return key on a non-modal page commits the values and gives back the focus + // to the view + apply (dispatcher ()); + } view ()->set_focus (); event->accept (); } else { @@ -200,6 +207,24 @@ BEGIN_PROTECTED END_PROTECTED } +bool +EditorOptionsPageWidget::event (QEvent *event) +{ + if (event->type () == QEvent::ShortcutOverride) { + QKeyEvent *ke = dynamic_cast (event); + if (ke->key () == Qt::Key_Escape || + ke->key () == Qt::Key_Tab || + ke->key () == Qt::Key_Enter || + ke->key () == Qt::Key_Return || + ke->key () == Qt::Key_Backtab) { + // accept the shortcut override event for some keys, so we can handle + // it in keyPressEvent + ke->accept (); + } + } + return QWidget::event (event); +} + void EditorOptionsPageWidget::set_focus () { @@ -215,6 +240,12 @@ EditorOptionsPageWidget::set_visible (bool visible) setVisible (visible); } +bool +EditorOptionsPageWidget::is_visible () const +{ + return isVisible (); +} + #endif } diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index b4a541e40..20cfb81c1 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -83,13 +83,16 @@ class LAYBASIC_PUBLIC EditorOptionsPage virtual int order () const = 0; virtual const char *name () const { return 0; } virtual void apply (lay::Dispatcher * /*root*/) { } + virtual void cancel () { } virtual void setup (lay::Dispatcher * /*root*/) { } virtual void commit_recent (lay::Dispatcher * /*root*/) { } virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { } virtual void set_focus () { } - virtual void set_visible (bool /*visible*/) { } virtual EditorOptionsPageWidget *widget () { return 0; } + virtual bool is_visible () const { return false; } + virtual void set_visible (bool /*visible*/) { } + bool is_focus_page () const { return m_focus_page; } void set_focus_page (bool f) { m_focus_page = f; } @@ -159,6 +162,7 @@ Q_OBJECT virtual ~EditorOptionsPageWidget (); virtual void set_focus (); + virtual bool is_visible () const; virtual void set_visible (bool visible); virtual EditorOptionsPageWidget *widget () { return this; } @@ -168,6 +172,7 @@ protected slots: protected: virtual bool focusNextPrevChild (bool next); virtual void keyPressEvent (QKeyEvent *event); + virtual bool event (QEvent *event); }; #endif // defined(HAVE_QT) diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index 0783817ba..e56116965 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -369,9 +369,9 @@ EditorServiceBase::focus_page () } bool -EditorServiceBase::key_event (unsigned int key, unsigned int buttons) +EditorServiceBase::key_event (unsigned int key, unsigned int /*buttons*/) { - if (is_active () && key == Qt::Key_Tab && buttons == 0) { + if (is_active () && (key == Qt::Key_Tab || key == Qt::Key_Backtab)) { focus_page_open (); return true; } else { @@ -380,9 +380,13 @@ EditorServiceBase::key_event (unsigned int key, unsigned int buttons) } bool -EditorServiceBase::shortcut_override_event (unsigned int key, unsigned int buttons) +EditorServiceBase::shortcut_override_event (unsigned int key, unsigned int /*buttons*/) { - return is_active () && key == Qt::Key_Tab && buttons == 0 && focus_page (); + auto fp = focus_page (); + return is_active () + && (key == Qt::Key_Tab || key == Qt::Key_Backtab) + && fp + && (fp->is_modal_page () || fp->is_visible ()); } int diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 0664a4bc9..80e63b68d 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -431,33 +431,14 @@ class MoveToolboxPage hide (); } - virtual void keyPressEvent (QKeyEvent *event) + virtual void apply (lay::Dispatcher * /*dispatcher*/) { - if (event->key () == Qt::Key_Escape) { - tl::info << "@@@ Escape!"; - view ()->set_focus (); - } else if (event->key () == Qt::Key_Enter || event->key () == Qt::Key_Return) { - tl::info << "@@@ Accept!"; - view ()->set_focus (); - } - QWidget::keyPressEvent (event); + tl::info << "@@@ Accept!"; } - virtual bool event (QEvent *event) + virtual void cancel () { - if (event->type () == QEvent::ShortcutOverride) { - QKeyEvent *ke = dynamic_cast (event); - if (ke->key () == Qt::Key_Escape || - ke->key () == Qt::Key_Tab || - ke->key () == Qt::Key_Enter || - ke->key () == Qt::Key_Return || - ke->key () == Qt::Key_Backtab) { - // accept the shortcut override event for some keys, so we can handle - // it in keyPressEvent - ke->accept (); - } - } - return QWidget::event (event); + tl::info << "@@@ Escape!"; } private: From 15a5f7f7c2fe812ba14aff8642b537fefacd21c6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Jan 2026 02:13:13 +0100 Subject: [PATCH 11/36] WIP --- src/edt/edt/edtPartialService.cc | 4 --- .../laybasic/gsiDeclLayEditorOptionsPage.cc | 26 ++++++++++++++ .../laybasic/gsiDeclLayEditorOptionsPage.h | 7 ++-- src/laybasic/laybasic/layDispatcher.cc | 8 +++++ src/laybasic/laybasic/layDispatcher.h | 9 +++++ src/laybasic/laybasic/layEditorOptionsPage.cc | 12 ++++--- src/laybasic/laybasic/layEditorOptionsPage.h | 2 ++ src/laybasic/laybasic/layMove.cc | 36 ++++++++++++++++--- src/laybasic/laybasic/layMove.h | 1 + src/laybasic/laybasic/layPlugin.cc | 9 +++++ src/laybasic/laybasic/layPlugin.h | 18 ++++++++++ src/laybasic/laybasic/laySnap.h | 3 +- src/layview/layview/layEditorOptionsPages.cc | 4 +-- 13 files changed, 119 insertions(+), 20 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index b2eb61acd..ac480c732 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2027,8 +2027,6 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo manager ()->transaction (tl::to_string (tr ("Partial move"))); } - // heuristically, if there is just one edge selected: do not confine to the movement - // angle constraint - the edge usually is confined enough db::DTrans move_trans = db::DTrans (m_current - m_start); transform_selection (move_trans); @@ -2506,8 +2504,6 @@ PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type a manager ()->transaction (tl::to_string (tr ("Partial move"))); } - // heuristically, if there is just one edge selected: do not confine to the movement - // angle constraint - the edge usually is confined enough db::DTrans move_trans = db::DTrans (m_current - m_start); transform_selection (move_trans); diff --git a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc index 1d2d335fd..de8619a37 100644 --- a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc +++ b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc @@ -77,6 +77,10 @@ Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWid method ("cancel", &lay::EditorOptionsPage::cancel, "@brief Gets called when the Escape key is pressed on a non-modal page\n" "This method has been introduced in version 0.30.6." + ) + + method ("commit", &lay::EditorOptionsPage::commit, + "@brief Gets called when the Enter key is pressed on a non-modal page\n" + "This method has been introduced in version 0.30.6." ), "@brief The plugin framework's editor options page base class\n" "\n" @@ -168,6 +172,22 @@ EditorOptionsPageImpl::cancel () } } +void +EditorOptionsPageImpl::commit_impl (lay::Dispatcher *root) +{ + lay::EditorOptionsPage::commit (root); +} + +void +EditorOptionsPageImpl::commit (lay::Dispatcher *root) +{ + if (f_commit.can_issue ()) { + f_commit.issue (&EditorOptionsPageImpl::commit_impl, root); + } else { + EditorOptionsPageImpl::commit_impl (root); + } +} + EditorOptionsPageImpl *new_editor_options_page (const std::string &title, int index) { return new EditorOptionsPageImpl (title, index); @@ -204,6 +224,12 @@ Class decl_EditorOptionsPage (decl_EditorOptionsPageBase, callback ("cancel", &EditorOptionsPageImpl::cancel, &EditorOptionsPageImpl::f_cancel, "@brief Reimplement this method to receive Escape key events for the page\n" "This method has been added in version 0.30.6.\n" + ) + + // prevents infinite recursion + method_ext ("commit", &commit_fb, gsi::arg ("dispatcher"), "@hide") + + callback ("commit", &EditorOptionsPageImpl::commit, &EditorOptionsPageImpl::f_commit, gsi::arg ("dispatcher"), + "@brief Reimplement this method to receive Enter key events for the page\n" + "This method has been added in version 0.30.6.\n" ), "@brief The plugin framework's editor options page\n" "\n" diff --git a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h index c679b0d0e..8a8ee1b84 100644 --- a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h +++ b/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h @@ -53,11 +53,13 @@ class EditorOptionsPageImpl void call_edited (); virtual void apply (lay::Dispatcher *root); virtual void setup (lay::Dispatcher *root); - virtual void cancel (); + virtual void cancel (lay::Dispatcher *root); + virtual void commit (); gsi::Callback f_apply; gsi::Callback f_setup; gsi::Callback f_cancel; + gsi::Callback f_commit; private: tl::weak_ptr mp_view; @@ -67,7 +69,8 @@ class EditorOptionsPageImpl void apply_impl (lay::Dispatcher *root); void setup_impl (lay::Dispatcher *root); - void cancel_impl (); + void cancel_impl (lay::Dispatcher *root); + void commit_impl (); }; } diff --git a/src/laybasic/laybasic/layDispatcher.cc b/src/laybasic/laybasic/layDispatcher.cc index b3a1f3b66..2bfdc02d7 100644 --- a/src/laybasic/laybasic/layDispatcher.cc +++ b/src/laybasic/laybasic/layDispatcher.cc @@ -103,6 +103,14 @@ Dispatcher::config_finalize () } } +void +Dispatcher::function (const std::string &symbol, const std::string &args) +{ + if (mp_delegate) { + mp_delegate->function (symbol, args); + } +} + // Writing and Reading of configuration diff --git a/src/laybasic/laybasic/layDispatcher.h b/src/laybasic/laybasic/layDispatcher.h index ab30f9ee3..a94ff0acb 100644 --- a/src/laybasic/laybasic/layDispatcher.h +++ b/src/laybasic/laybasic/layDispatcher.h @@ -87,6 +87,14 @@ class LAYBASIC_PUBLIC DispatcherDelegate // .. this implementation does nothing .. } + /** + * @brief Generic function call + */ + virtual void function (const std::string & /*symbol*/, const std::string & /*args*/) + { + // .. this implementation does nothing .. + } + /** * @brief Receives configuration events */ @@ -250,6 +258,7 @@ class LAYBASIC_PUBLIC Dispatcher // capture the configuration events so we can change the value of the configuration actions virtual bool configure (const std::string &name, const std::string &value); virtual void config_finalize (); + virtual void function (const std::string &symbol, const std::string &args); private: Dispatcher (const Dispatcher &); diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index 44bd772f1..739f27c07 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -72,6 +72,9 @@ EditorOptionsPage::show () { if (! m_active) { return -1; + } else if (m_toolbox_widget) { + set_focus (); + return -1; } else if (mp_owner) { if (! is_modal_page ()) { mp_owner->make_page_current (this); @@ -80,7 +83,6 @@ EditorOptionsPage::show () return mp_owner->exec_modal (this) ? 1 : 0; } } else { - set_focus (); return -1; } } @@ -123,10 +125,10 @@ EditorOptionsPage::activate (bool active) { if (m_active != active) { m_active = active; - if (mp_owner) { - mp_owner->activate_page (this); - } if (m_active) { + if (mp_owner) { + mp_owner->activate_page (this); + } activated (); } else { deactivated (); @@ -197,7 +199,7 @@ BEGIN_PROTECTED } else { // The Return key on a non-modal page commits the values and gives back the focus // to the view - apply (dispatcher ()); + commit (dispatcher ()); } view ()->set_focus (); event->accept (); diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 20cfb81c1..f1d1fa12d 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -84,7 +84,9 @@ class LAYBASIC_PUBLIC EditorOptionsPage virtual const char *name () const { return 0; } virtual void apply (lay::Dispatcher * /*root*/) { } virtual void cancel () { } + virtual void commit (lay::Dispatcher * /*root*/) { } virtual void setup (lay::Dispatcher * /*root*/) { } + virtual void configure (const std::string & /*name*/, const std::string & /*value*/) { } virtual void commit_recent (lay::Dispatcher * /*root*/) { } virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { } virtual void set_focus () { } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 80e63b68d..9cf61e9d8 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -30,7 +30,6 @@ #if defined(HAVE_QT) # include "layEditorOptionsPage.h" # include -# include // @@@ # include # include #endif @@ -38,7 +37,9 @@ namespace lay { -const char *move_editor_options_name = "move-editor-options"; +const std::string move_editor_options_name ("move-editor-options"); +const std::string move_function_name ("move-execute"); +const std::string move_distance_setter_name ("move-distance"); // ------------------------------------------------------------- // MoveService implementation @@ -86,6 +87,19 @@ MoveService::configure (const std::string &name, const std::string &value) return false; // not taken } +void +MoveService::function (const std::string &name, const std::string &value) +{ + if (name == move_function_name) { + try { + db::DVector s; + tl::from_string (value, s); + tl::info << "@@@ move " << s.to_string (); + } catch (...) { + } + } +} + bool MoveService::key_event (unsigned int key, unsigned int buttons) { @@ -418,7 +432,7 @@ class MoveToolboxPage virtual const char *name () const { - return move_editor_options_name; + return move_editor_options_name.c_str (); } virtual int order () const @@ -431,9 +445,21 @@ class MoveToolboxPage hide (); } - virtual void apply (lay::Dispatcher * /*dispatcher*/) + virtual void commit (lay::Dispatcher *dispatcher) { - tl::info << "@@@ Accept!"; + tl::info << "@@@ Commit!"; + + try { + + double dx = 0.0, dy = 0.0; + + tl::from_string (tl::to_string (mp_x_le->text ()), dx); + tl::from_string (tl::to_string (mp_y_le->text ()), dy); + + dispatcher->call_function (move_function_name, db::DVector (dx, dy).to_string ()); + + } catch (...) { + } } virtual void cancel () diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 635c25f31..8ba7d6e4a 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -43,6 +43,7 @@ class LAYBASIC_PUBLIC MoveService : bool start_move (db::Transaction *transaction = 0, bool transient_selection = false); bool configure (const std::string &name, const std::string &value); + void function (const std::string &name, const std::string &value); void finish (); void cancel (); diff --git a/src/laybasic/laybasic/layPlugin.cc b/src/laybasic/laybasic/layPlugin.cc index 48a29f9cd..3b35d2e54 100644 --- a/src/laybasic/laybasic/layPlugin.cc +++ b/src/laybasic/laybasic/layPlugin.cc @@ -523,6 +523,15 @@ Plugin::do_config_set (const std::string &name, const std::string &value, bool f return false; } +void +Plugin::call_function (const std::string &symbol, const std::string &args) +{ + function (symbol, args); + for (tl::weak_collection::iterator c = m_children.begin (); c != m_children.end (); ++c) { + c->call_function (symbol, args); + } +} + // --------------------------------------------------------------------------------------------------- // Menu item generators diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index 2d5ee32d4..730b42772 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -744,6 +744,13 @@ class LAYBASIC_PUBLIC Plugin // .. this implementation does nothing .. } + /** + * @brief Generic function call + * + * This method calls "function" on this plugin and all the children. + */ + virtual void call_function (const std::string &symbol, const std::string &args); + #if defined(HAVE_QT) /** * @brief Return the lay::Browser interface if this object has one @@ -859,6 +866,17 @@ class LAYBASIC_PUBLIC Plugin // .. the default implementation does nothing .. } + /** + * @brief Implements a generic function call + * + * This method can be used to establish a communication between a properties page and + * a plugin for example. + */ + virtual void function (const std::string & /*symbol*/, const std::string & /*args*/) + { + // .. this implementation does nothing .. + } + private: Plugin (const Plugin &); Plugin &operator= (const Plugin &); diff --git a/src/laybasic/laybasic/laySnap.h b/src/laybasic/laybasic/laySnap.h index cfa63ae9b..6bd43e9be 100644 --- a/src/laybasic/laybasic/laySnap.h +++ b/src/laybasic/laybasic/laySnap.h @@ -49,11 +49,12 @@ namespace lay * Any: no angle constraint * Diagonal: vertical, horizontal and 45 degree diagonals * Ortho: vertical and horizontal + * DiagonalOnly: 45 degree diagonals, not vertically or horizontally * Horizontal: horizontal only * Vertical: vertical only * Global: use global setting (templates and ruler specific setting only) */ - enum angle_constraint_type { AC_Any = 0, AC_Diagonal, AC_DiagonalOnly, AC_Ortho, AC_Horizontal, AC_Vertical, AC_Global, AC_NumModes }; + enum angle_constraint_type { AC_Any = 0, AC_Diagonal, AC_Ortho, AC_DiagonalOnly, AC_Horizontal, AC_Vertical, AC_Global, AC_NumModes }; /** * @brief snap a coordinate value to a unit grid diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index ebcca5323..a36190106 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -66,9 +66,7 @@ EditorOptionsPages::EditorOptionsPages (QWidget *parent, lay::LayoutViewBase *vi } for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { - if (! p->is_toolbox_widget ()) { - p->set_owner (this); - } + p->set_owner (this); } update (0); From 18ef4a12e246460f3c1b1e40f3ec2419cd348472 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Jan 2026 09:21:41 +0100 Subject: [PATCH 12/36] Allowing use of arrow keys for move --- src/laybasic/laybasic/layMove.cc | 15 +++++++++++++++ src/laybasic/laybasic/layMove.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 9cf61e9d8..ae677b3ff 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -148,6 +148,21 @@ MoveService::key_event (unsigned int key, unsigned int buttons) } } +bool +MoveService::shortcut_override_event (unsigned int key, unsigned int buttons) +{ + if (! m_dragging) { + if (int (key) == lay::KeyDown || + int (key) == lay::KeyLeft || + int (key) == lay::KeyUp || + int (key) == lay::KeyRight) { + return true; + } + } + + return lay::EditorServiceBase::shortcut_override_event (key, buttons); +} + void MoveService::show_toolbox (bool visible) { diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 8ba7d6e4a..b487a22d8 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -55,6 +55,7 @@ class LAYBASIC_PUBLIC MoveService : virtual bool mouse_release_event (const db::DPoint &p, unsigned int /*buttons*/, bool prio); virtual bool wheel_event (int delta, bool horizontal, const db::DPoint &p, unsigned int buttons, bool prio); virtual bool key_event (unsigned int key, unsigned int buttons); + virtual bool shortcut_override_event (unsigned int key, unsigned int buttons); virtual void drag_cancel (); virtual void deactivated (); void show_toolbox (bool visible); From cabcde3bf582534ddf7520b63c83774a36c62ab6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Jan 2026 10:08:57 +0100 Subject: [PATCH 13/36] [consider merging] Fixing strange snapping behavior on partial move at 45 degree directions --- src/edt/edt/edtPartialService.cc | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index ac480c732..d8e5d59cb 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2430,7 +2430,7 @@ PartialService::snap_marker_to_grid (const db::DVector &v, bool &snapped) const if (snapped) { vr += vv; - return db::DVector (vr.x () * snapped_to.x (), vr.y () * snapped_to.y ()); + return db::DVector (vr.x () * fabs (snapped_to.x ()), vr.y () * fabs (snapped_to.y ())); } else { return db::DVector (); } @@ -3006,14 +3006,7 @@ PartialService::do_selection_to_view () db::DTrans move_trans; if (m_dragging) { - // heuristically, if there is just one edge selected: do not confine to the movement - // angle constraint - the edge usually is confined enough - if (m_selection.size () == 1 && ! m_selection.begin ()->first.is_cell_inst () && m_selection.begin ()->second.size () == 3 /*p1,p2,edge*/) { - move_trans = db::DTrans (m_current - m_start); - } else { - // TODO: DTrans should have a ctor that takes a vector - move_trans = db::DTrans (lay::snap_angle (m_current - m_start, move_ac ())); - } + move_trans = db::DTrans (m_current - m_start); // display vector view ()->message (std::string ("dx: ") + tl::micron_to_string (move_trans.disp ().x ()) + From ee622fc10402bbfa5434281af156609fb59e4d01 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Jan 2026 12:54:10 +0100 Subject: [PATCH 14/36] WIP --- src/ant/ant/antService.cc | 7 ++++- src/edt/edt/edtPartialService.cc | 2 ++ src/edt/edt/edtService.cc | 23 ++++++++------ src/img/img/imgService.cc | 1 + src/laybasic/laybasic/layEditable.cc | 40 +++++++++++++++++++++-- src/laybasic/laybasic/layEditable.h | 47 +++++++++++++++++++++++++++- src/laybasic/laybasic/layMove.cc | 38 +++++++++++++++++++++- 7 files changed, 144 insertions(+), 14 deletions(-) diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 6f6c0e556..b7ead8384 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1598,6 +1598,8 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac; clear_mouse_cursors (); + bool indicate_ruler_as_message = true; + if (m_move_mode == MoveP1) { m_current.seg_p1 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac)); @@ -1653,6 +1655,9 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) m_trans = db::DTrans (dp + (m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1); + propose_move_transformation (m_trans, 1); + indicate_ruler_as_message = false; + snap_rulers (ac_eff); for (std::vector::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) { @@ -1661,7 +1666,7 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) } - if (m_move_mode != MoveSelected) { + if (indicate_ruler_as_message) { show_message (); } } diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index d8e5d59cb..d80a08bcd 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2481,6 +2481,8 @@ PartialService::move (const db::DPoint &p, lay::angle_constraint_type ac) } + propose_move_transformation (db::DTrans (m_current - m_start), 0); + selection_to_view (); m_alt_ac = lay::AC_Global; diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index ededabe19..a044dd441 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -563,15 +563,20 @@ void Service::move (const db::DPoint &pu, lay::angle_constraint_type ac) { m_alt_ac = ac; + if (view ()->is_editable () && m_moving) { + db::DPoint ref = snap (m_move_start); bool snapped = false; db::DPoint p = ref + snap_marker_to_grid (pu - m_move_start, snapped); if (! snapped) { p = ref + snap (pu - m_move_start, false /*move*/); } + move_markers (db::DTrans (p - db::DPoint ()) * db::DTrans (m_move_trans.fp_trans ()) * db::DTrans (db::DPoint () - ref)); + } + m_alt_ac = lay::AC_Global; } @@ -579,15 +584,20 @@ void Service::move_transform (const db::DPoint &pu, db::DFTrans tr, lay::angle_constraint_type ac) { m_alt_ac = ac; + if (view ()->is_editable () && m_moving) { + db::DPoint ref = snap (m_move_start); bool snapped = false; db::DPoint p = ref + snap_marker_to_grid (pu - m_move_start, snapped); if (! snapped) { p = ref + snap (pu - m_move_start, false /*move*/); } + move_markers (db::DTrans (p - db::DPoint ()) * db::DTrans (tr * m_move_trans.fp_trans ()) * db::DTrans (db::DPoint () - ref)); + } + m_alt_ac = lay::AC_Global; } @@ -1701,16 +1711,11 @@ Service::select (const lay::ObjectInstPath &obj, lay::Editable::SelectionMode mo void Service::move_markers (const db::DTrans &t) { - if (m_move_trans != t) { + if (has_selection ()) { + propose_move_transformation (t, 0); + } - // display current move vector - if (has_selection ()) { - std::string pos = std::string ("dx: ") + tl::micron_to_string (t.disp ().x ()) + " dy: " + tl::micron_to_string (t.disp ().y ()); - if (t.rot () != 0) { - pos += std::string (" ") + ((const db::DFTrans &) t).to_string (); - } - view ()->message (pos); - } + if (m_move_trans != t) { for (auto r = m_markers.begin (); r != m_markers.end (); ++r) { diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 9e27d9449..ce0b06961 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -740,6 +740,7 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) m_p1 = p; m_trans = db::DTrans (dp) * m_trans; + propose_move_transformation (m_trans, 2); for (std::vector::iterator r = m_selected_image_views.begin (); r != m_selected_image_views.end (); ++r) { (*r)->transform_by (db::DCplxTrans (m_trans)); diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index 60fd18a19..49060757a 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -47,7 +47,7 @@ struct first_of_pair_cmp_f // Editable implementation Editable::Editable (lay::Editables *editables) - : mp_editables (editables) + : mp_editables (editables), m_move_transformation_priority (-1) { if (editables) { editables->m_editables.push_back (this); @@ -75,6 +75,20 @@ Editable::~Editable () } } +void +Editable::reset_proposed_move_transformation () +{ + m_move_transformation = db::DTrans (); + m_move_transformation_priority = -1; +} + +void +Editable::propose_move_transformation (const db::DTrans &t, unsigned int priority) +{ + m_move_transformation = t; + m_move_transformation_priority = priority; +} + // ---------------------------------------------------------------- // Editables implementation @@ -490,6 +504,9 @@ Editables::begin_move (const db::DPoint &p, lay::angle_constraint_type ac) cancel_edits (); clear_previous_selection (); + m_move_start = p; + m_move_transform = db::DFTrans (); + m_move_selection = false; m_any_move_operation = false; @@ -567,19 +584,38 @@ Editables::begin_move (const db::DPoint &p, lay::angle_constraint_type ac) } } -void +std::pair Editables::move (const db::DPoint &p, lay::angle_constraint_type ac) { + int move_transformation_priority = -1; + db::DTrans move_transformation (p - m_move_start); + move_transformation *= db::DTrans (m_move_transform); + m_any_move_operation = true; + for (iterator e = begin (); e != end (); ++e) { + + e->reset_proposed_move_transformation (); + e->move (p, ac); + + auto pmv = e->proposed_move_transformation (); + if (move_transformation_priority < 0 || (pmv.first >= 0 && pmv.first <= move_transformation_priority)) { + move_transformation_priority = pmv.first; + move_transformation = pmv.second; + } + } + + return std::make_pair (move_transformation_priority, move_transformation); } void Editables::move_transform (const db::DPoint &p, db::DFTrans t, lay::angle_constraint_type ac) { m_any_move_operation = true; + m_move_transform *= t; + for (iterator e = begin (); e != end (); ++e) { e->move_transform (p, t, ac); } diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index cb696600c..9722e2ff3 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -405,13 +405,48 @@ class LAYBASIC_PUBLIC Editable } protected: + friend class lay::Editables; + Editables *editables () { return mp_editables; } + /** + * @brief Resets the proposed move transformation + * + * You should not need to call this method from an Editable implementation. + */ + void reset_proposed_move_transformation (); + + /** + * @brief Proposes a move transformation + * + * On "move", the Editable can propose an actual move transformation that + * may differ from the actual move distance due to implementation-specific + * snapping. + * + * This method proposes a move transformation with a given priority. The + * Editable with the lowest priority value wins. + */ + void propose_move_transformation (const db::DTrans &mv, unsigned int priority); + + /** + * @brief Gets the proposed move transformation and priority + * + * @return A pair with (priority, transformation) + * + * The returned priority is negative if not priority was set. + */ + std::pair proposed_move_transformation () const + { + return std::make_pair (m_move_transformation_priority, m_move_transformation); + } + private: Editables *mp_editables; + int m_move_transformation_priority; + db::DTrans m_move_transformation; }; /** @@ -551,8 +586,16 @@ class LAYBASIC_PUBLIC Editables /** * @brief Continue "move" operation + * + * The return value is the "proposed move transformation", i.e. a representative + * one used for the actual move. As every interface may decide about the + * actual move transformation (due to specific snapping to objects etc.), the + * return value many be ambiguous and should be used for information purposes + * only. + * + * @return A pair (priority, transformation) where priority is negative if no vector was proposed */ - void move (const db::DPoint &p, lay::angle_constraint_type ac); + std::pair move (const db::DPoint &p, lay::angle_constraint_type ac); /** * @brief Transform during a move operation @@ -678,6 +721,8 @@ class LAYBASIC_PUBLIC Editables tl::shared_collection m_editables; std::set m_enabled; + db::DPoint m_move_start; + db::DFTrans m_move_transform; bool m_move_selection; bool m_any_move_operation; db::DBox m_last_selected_point; diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index ae677b3ff..08310c798 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -180,7 +180,26 @@ MoveService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool p if (m_dragging) { set_cursor (lay::Cursor::size_all); - mp_editables->move (p, ac_from_buttons (buttons)); + auto pmv = mp_editables->move (p, ac_from_buttons (buttons)); + + // display the proposed move transformation + if (pmv.first >= 0) { + + std::string pos = std::string ("dx: ") + tl::micron_to_string (pmv.second.disp ().x ()) + " dy: " + tl::micron_to_string (pmv.second.disp ().y ()); + if (pmv.second.rot () != 0) { + pos += std::string (" ") + ((const db::DFTrans &) pmv.second).to_string (); + } + mp_view->message (pos); + + lay::EditorOptionsPage *toolbox_widget = 0; + if (mp_view->editor_options_pages ()) { + toolbox_widget = mp_view->editor_options_pages ()->page_with_name (move_editor_options_name); + } + if (toolbox_widget) { + toolbox_widget->configure (move_distance_setter_name, pmv.second.disp ().to_string ()); + } + + } } else if (prio) { @@ -477,6 +496,23 @@ class MoveToolboxPage } } + virtual void configure (const std::string &name, const std::string &value) + { + if (name == move_distance_setter_name && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) { + + try { + + db::DVector mv; + tl::from_string (value, mv); + + mp_x_le->setText (tl::to_qstring (tl::to_string (mv.x ()))); + mp_y_le->setText (tl::to_qstring (tl::to_string (mv.y ()))); + + } catch (...) { + } + } + } + virtual void cancel () { tl::info << "@@@ Escape!"; From c2aa597022b6ec45a26a7ae2dfbec0b66f88ac11 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Jan 2026 16:21:59 +0100 Subject: [PATCH 15/36] WIP --- src/ant/ant/antService.cc | 7 ++++++ src/ant/ant/antService.h | 5 +++++ src/edt/edt/edtMoveTrackerService.cc | 7 ++++++ src/edt/edt/edtMoveTrackerService.h | 5 +++++ src/edt/edt/edtPartialService.cc | 11 +++++++--- src/edt/edt/edtPartialService.h | 5 +++++ src/edt/edt/edtService.cc | 19 +++++++++++++--- src/edt/edt/edtService.h | 5 +++++ src/img/img/imgService.cc | 9 +++++++- src/img/img/imgService.h | 5 +++++ src/laybasic/laybasic/layEditable.cc | 33 +++++++++++++++++++++++++++- src/laybasic/laybasic/layEditable.h | 20 +++++++++++++++++ src/laybasic/laybasic/layMove.cc | 29 +++++++++++++++--------- 13 files changed, 142 insertions(+), 18 deletions(-) diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index b7ead8384..2a66becba 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1681,6 +1681,13 @@ Service::show_message () view ()->message (pos); } +void +Service::end_move (const db::DVector &v) +{ + m_trans = db::DTrans (v) * db::DTrans (m_trans.fp_trans ()); + end_move (db::DPoint (), lay::AC_Any); +} + void Service::end_move (const db::DPoint &, lay::angle_constraint_type) { diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index 13086eeb4..f270ff914 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -347,6 +347,11 @@ Q_OBJECT */ virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + /** + * @brief Terminate a "move" operation with compulsory move vector + */ + virtual void end_move (const db::DVector &v); + /** * @brief Return the bbox of the selection (reimplementation of lay::Editable interface) */ diff --git a/src/edt/edt/edtMoveTrackerService.cc b/src/edt/edt/edtMoveTrackerService.cc index 32a7e2cfa..c1b8abc34 100644 --- a/src/edt/edt/edtMoveTrackerService.cc +++ b/src/edt/edt/edtMoveTrackerService.cc @@ -133,6 +133,13 @@ MoveTrackerService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_ty move_cancel (); // formally this functionality fits here } +void +MoveTrackerService::end_move (const db::DVector & /*v*/) +{ + call_editor_hooks (m_editor_hooks, &edt::EditorHooks::commit_edit); + move_cancel (); // formally this functionality fits here +} + void MoveTrackerService::edit_cancel () { diff --git a/src/edt/edt/edtMoveTrackerService.h b/src/edt/edt/edtMoveTrackerService.h index d92d9f3aa..48c18bc08 100644 --- a/src/edt/edt/edtMoveTrackerService.h +++ b/src/edt/edt/edtMoveTrackerService.h @@ -68,6 +68,11 @@ class EDT_PUBLIC MoveTrackerService */ virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + /** + * @brief Terminate a "move" operation with compulsory move vector + */ + virtual void end_move (const db::DVector &v); + /** * @brief Access to the view object */ diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index d80a08bcd..0e4210ff7 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2489,14 +2489,19 @@ PartialService::move (const db::DPoint &p, lay::angle_constraint_type ac) } void -PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type ac) +PartialService::end_move (const db::DVector &v) +{ + m_current = m_start + v; + end_move (db::DPoint (), lay::AC_Any); +} + +void +PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type /*ac*/) { if (! m_dragging) { return; } - m_alt_ac = ac; - if (m_current != m_start) { // stop dragging diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 594c73108..e124c2dda 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -287,6 +287,11 @@ Q_OBJECT */ virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + /** + * @brief Terminate a "move" operation with compulsory move vector + */ + virtual void end_move (const db::DVector &v); + /** * @brief Implement the "select" method at least to clear the selection */ diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index a044dd441..7c546c220 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -601,16 +601,29 @@ Service::move_transform (const db::DPoint &pu, db::DFTrans tr, lay::angle_constr m_alt_ac = lay::AC_Global; } -void -Service::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type ac) +void +Service::end_move (const db::DVector &v) +{ + if (view ()->is_editable () && m_moving) { + transform (db::DCplxTrans (db::DTrans (v) * db::DTrans (m_move_trans.fp_trans ()))); + move_cancel (); // formally this functionality fits here + // accept changes to guiding shapes + handle_guiding_shape_changes (true); + } + + m_alt_ac = lay::AC_Global; +} + +void +Service::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type /*ac*/) { - m_alt_ac = ac; if (view ()->is_editable () && m_moving) { transform (db::DCplxTrans (m_move_trans)); move_cancel (); // formally this functionality fits here // accept changes to guiding shapes handle_guiding_shape_changes (true); } + m_alt_ac = lay::AC_Global; } diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 09bb56e96..814e4db93 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -190,6 +190,11 @@ class EDT_PUBLIC Service */ virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + /** + * @brief Terminate a "move" operation with compulsory move vector + */ + virtual void end_move (const db::DVector &v); + /** * @brief Indicates whether objects are selected */ diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index ce0b06961..7d84f281b 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -872,7 +872,14 @@ Service::show_message () */ } -void +void +Service::end_move (const db::DVector &v) +{ + m_trans = db::DTrans (v) * db::DTrans (m_trans.fp_trans ()); + end_move (db::DPoint (), lay::AC_Any); +} + +void Service::end_move (const db::DPoint &, lay::angle_constraint_type) { if (! m_selected_image_views.empty () && ! m_selected.empty ()) { diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index 9e5d1cc14..0d3d27092 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -359,6 +359,11 @@ class IMG_PUBLIC Service */ virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + /** + * @brief Terminate a "move" operation with compulsory move vector + */ + virtual void end_move (const db::DVector &v); + /** * @brief Return the bbox of the selection (reimplementation of lay::Editable interface) */ diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index 49060757a..1e532f800 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -621,7 +621,38 @@ Editables::move_transform (const db::DPoint &p, db::DFTrans t, lay::angle_constr } } -void +void +Editables::end_move (const db::DVector &v, db::Transaction *transaction) +{ + std::unique_ptr trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (tr ("Move")))); + + if (m_any_move_operation) { + + trans_holder->open (); + + // this dummy operation will update the screen: + if (manager ()) { + manager ()->queue (this, new db::Op ()); + } + + for (iterator e = begin (); e != end (); ++e) { + e->end_move (v); + } + + // clear the selection that was set previously + if (m_move_selection) { + clear_selection (); + } + + } else { + + trans_holder->cancel (); + edit_cancel (); + + } +} + +void Editables::end_move (const db::DPoint &p, lay::angle_constraint_type ac, db::Transaction *transaction) { std::unique_ptr trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (tr ("Move")))); diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index 9722e2ff3..f57c1f141 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -326,6 +326,16 @@ class LAYBASIC_PUBLIC Editable // .. by default, nothing is implemented .. } + /** + * @brief Terminate a "move" operation with compulsory shift vector + * + * @param v The move distance to be applied + */ + virtual void end_move (const db::DVector & /*v*/) + { + // .. by default, nothing is implemented .. + } + /** * @brief Cancel any pending operations * @@ -610,6 +620,16 @@ class LAYBASIC_PUBLIC Editables */ void end_move (const db::DPoint &p, lay::angle_constraint_type ac, db::Transaction *transaction = 0); + /** + * @brief End "move" operation with given vector + * + * If a transaction is given, the operation will be appended to this pending transaction + * The Editables object takes ownership over the Transaction object. + * + * The vector is supposed to be taken "as is" and no snapping shall be applied. + */ + void end_move (const db::DVector &v, db::Transaction *transaction = 0); + /** * @brief Indicates how many objects are selected. * diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 08310c798..0adad09d3 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -91,12 +91,28 @@ void MoveService::function (const std::string &name, const std::string &value) { if (name == move_function_name) { + try { + db::DVector s; tl::from_string (value, s); - tl::info << "@@@ move " << s.to_string (); + + m_dragging = false; + + show_toolbox (false); + ui ()->ungrab_mouse (this); + + mp_editables->end_move (s, mp_transaction.release ()); + + if (m_dragging_transient) { + mp_editables->clear_selection (); + } + + drag_cancel (); + } catch (...) { } + } } @@ -481,8 +497,6 @@ class MoveToolboxPage virtual void commit (lay::Dispatcher *dispatcher) { - tl::info << "@@@ Commit!"; - try { double dx = 0.0, dy = 0.0; @@ -505,19 +519,14 @@ class MoveToolboxPage db::DVector mv; tl::from_string (value, mv); - mp_x_le->setText (tl::to_qstring (tl::to_string (mv.x ()))); - mp_y_le->setText (tl::to_qstring (tl::to_string (mv.y ()))); + mp_x_le->setText (tl::to_qstring (tl::micron_to_string (mv.x ()))); + mp_y_le->setText (tl::to_qstring (tl::micron_to_string (mv.y ()))); } catch (...) { } } } - virtual void cancel () - { - tl::info << "@@@ Escape!"; - } - private: QHBoxLayout *mp_layout; QLineEdit *mp_x_le, *mp_y_le; From e058c47c0220734d3a09842a896e5c2fc4355830 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 13 Jan 2026 18:29:32 +0100 Subject: [PATCH 16/36] WIP --- src/edt/edt/edtEditorOptionsPages.h | 1 + src/edt/edt/edtRecentConfigurationPage.h | 2 +- src/laybasic/laybasic/layEditorOptionsPage.cc | 134 ++------------ src/laybasic/laybasic/layEditorOptionsPage.h | 58 +++--- src/laybasic/laybasic/layMove.cc | 98 +--------- src/laybasic/laybasic/layMove.h | 4 + src/layui/layui/layWidgets.cc | 87 ++++++--- src/layui/layui/layWidgets.h | 17 ++ .../layview/layEditorOptionsPageWidget.cc | 171 ++++++++++++++++++ .../layview/layEditorOptionsPageWidget.h | 70 +++++++ src/layview/layview/layEditorOptionsPages.cc | 1 + src/layview/layview/layLayoutView_qt.cc | 1 + .../layview/layMoveEditorOptionsPage.cc | 117 ++++++++++++ .../layview/layMoveEditorOptionsPage.h | 59 ++++++ src/layview/layview/layview.pro | 4 + 15 files changed, 561 insertions(+), 263 deletions(-) create mode 100644 src/layview/layview/layEditorOptionsPageWidget.cc create mode 100644 src/layview/layview/layEditorOptionsPageWidget.h create mode 100644 src/layview/layview/layMoveEditorOptionsPage.cc create mode 100644 src/layview/layview/layMoveEditorOptionsPage.h diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 5080c2dfe..c2056e2c9 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -26,6 +26,7 @@ #define HDR_edtEditorOptionsPages #include "layEditorOptionsPage.h" +#include "layEditorOptionsPageWidget.h" #include diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index dd6ff0e96..5e53d2ec6 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -26,7 +26,7 @@ #define HDR_edtRecentConfigurationPage #include "edtCommon.h" -#include "layEditorOptionsPage.h" +#include "layEditorOptionsPageWidget.h" #include "tlObject.h" #include "tlDeferredExecution.h" diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index 739f27c07..c3955809d 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -23,14 +23,28 @@ #include "tlInternational.h" #include "layEditorOptionsPage.h" #include "layLayoutViewBase.h" +#include "tlClassRegistry.h" #include "tlExceptions.h" -#include -#include - namespace lay { +// ------------------------------------------------------------------ +// EditorOptionsFactoryBase implementation + +lay::EditorOptionsPage * +EditorOptionsPageFactoryBase::create_page_by_name (const std::string &name, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) +{ + auto reg = tl::Registrar::get_instance (); + for (auto i = reg->begin (); i != reg->end (); ++i) { + if (i.current_name () == name) { + return i->create_page (view, dispatcher); + } + } + + return 0; +} + // ------------------------------------------------------------------ // EditorOptionsPageCollection implementation @@ -136,119 +150,5 @@ EditorOptionsPage::activate (bool active) } } -#if defined(HAVE_QT) - -// ------------------------------------------------------------------ -// EditorOptionsPage implementation - -EditorOptionsPageWidget::EditorOptionsPageWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : QWidget (0), EditorOptionsPage (view, dispatcher) -{ - init (view, dispatcher); -} - -EditorOptionsPageWidget::EditorOptionsPageWidget () - : QWidget (0), EditorOptionsPage () -{ - // .. nothing yet .. -} - -EditorOptionsPageWidget::~EditorOptionsPageWidget () -{ - set_owner (0); -} - -void -EditorOptionsPageWidget::edited () -{ - apply (dispatcher ()); -} - -static bool is_parent_widget (QWidget *w, QWidget *parent) -{ - while (w && w != parent) { - w = dynamic_cast (w->parent ()); - } - return w == parent; -} - -bool -EditorOptionsPageWidget::focusNextPrevChild (bool next) -{ - bool res = QWidget::focusNextPrevChild (next); - - // Stop making the focus leave the page - this way we can jump back to the - // view on "enter" - if (res && ! is_modal_page () && ! is_parent_widget (QApplication::focusWidget (), this) && focusWidget ()) { - focusWidget ()->setFocus (); - } - - return res; -} - -void -EditorOptionsPageWidget::keyPressEvent (QKeyEvent *event) -{ -BEGIN_PROTECTED - if (! is_modal_page () && - event->modifiers () == Qt::NoModifier && - (event->key () == Qt::Key_Return || event->key () == Qt::Key_Enter || event->key () == Qt::Key_Escape)) { - if (event->key () == Qt::Key_Escape) { - // The Escape key creates a call to cancel() - cancel (); - } else { - // The Return key on a non-modal page commits the values and gives back the focus - // to the view - commit (dispatcher ()); - } - view ()->set_focus (); - event->accept (); - } else { - QWidget::keyPressEvent (event); - } -END_PROTECTED -} - -bool -EditorOptionsPageWidget::event (QEvent *event) -{ - if (event->type () == QEvent::ShortcutOverride) { - QKeyEvent *ke = dynamic_cast (event); - if (ke->key () == Qt::Key_Escape || - ke->key () == Qt::Key_Tab || - ke->key () == Qt::Key_Enter || - ke->key () == Qt::Key_Return || - ke->key () == Qt::Key_Backtab) { - // accept the shortcut override event for some keys, so we can handle - // it in keyPressEvent - ke->accept (); - } - } - return QWidget::event (event); -} - -void -EditorOptionsPageWidget::set_focus () -{ - if (isVisible ()) { - setFocus (Qt::TabFocusReason); - QWidget::focusNextPrevChild (true); - } -} - -void -EditorOptionsPageWidget::set_visible (bool visible) -{ - setVisible (visible); -} - -bool -EditorOptionsPageWidget::is_visible () const -{ - return isVisible (); -} - -#endif - } diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index f1d1fa12d..1b0e604a2 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -27,10 +27,6 @@ #include "tlObject.h" -#if defined(HAVE_QT) -# include -#endif - namespace db { struct LayerProperties; @@ -149,34 +145,48 @@ class LAYBASIC_PUBLIC EditorOptionsPage void attach_events (); }; -#if defined(HAVE_QT) /** - * @brief The base class for a object properties page + * @brief A basic factory class for editor options pages + * + * We will use it later to provide a registration-based specialized factory + * for Qt-enabled option pages, which we should not link here. */ -class LAYBASIC_PUBLIC EditorOptionsPageWidget - : public QWidget, public EditorOptionsPage +class LAYBASIC_PUBLIC EditorOptionsPageFactoryBase { -Q_OBJECT - public: - EditorOptionsPageWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); - EditorOptionsPageWidget (); - virtual ~EditorOptionsPageWidget (); + EditorOptionsPageFactoryBase () { } + virtual ~EditorOptionsPageFactoryBase () { } - virtual void set_focus (); - virtual bool is_visible () const; - virtual void set_visible (bool visible); - virtual EditorOptionsPageWidget *widget () { return this; } + virtual lay::EditorOptionsPage *create_page (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) = 0; -protected slots: - void edited (); + static lay::EditorOptionsPage *create_page_by_name (const std::string &name, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); +}; -protected: - virtual bool focusNextPrevChild (bool next); - virtual void keyPressEvent (QKeyEvent *event); - virtual bool event (QEvent *event); +/** + * @brief A specialized editor options page factory class for a specific type + * + * Register the factory using: + * + * #include "tlClassRegistry.h" + * static tl::RegisteredClass s_factory (new lay::EditorOptionsPageFactory (), 0, "MyClass"); + * + * Later you can create a page from "MyName" using + * + * page = EditorOptionsPageFactoryBase::create_page_by_name ("MyClass", view, dispatcher); + */ +template +class LAYBASIC_PUBLIC_TEMPLATE EditorOptionsPageFactory + : public EditorOptionsPageFactoryBase +{ +public: + EditorOptionsPageFactory () { } + virtual ~EditorOptionsPageFactory () { } + + virtual lay::EditorOptionsPage *create_page (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + { + return new T (view, dispatcher); + } }; -#endif // defined(HAVE_QT) } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 0adad09d3..56087a660 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -37,9 +37,9 @@ namespace lay { -const std::string move_editor_options_name ("move-editor-options"); -const std::string move_function_name ("move-execute"); -const std::string move_distance_setter_name ("move-distance"); +LAYBASIC_PUBLIC std::string move_editor_options_name ("move-editor-options"); +LAYBASIC_PUBLIC std::string move_function_name ("move-execute"); +LAYBASIC_PUBLIC std::string move_distance_setter_name ("move-distance"); // ------------------------------------------------------------- // MoveService implementation @@ -451,90 +451,6 @@ MoveService::finish () // ---------------------------------------------------------------------------- -#if defined(HAVE_QT) -namespace { - -class MoveToolboxPage - : public lay::EditorOptionsPageWidget -{ -public: - MoveToolboxPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : lay::EditorOptionsPageWidget (view, dispatcher) - { - mp_layout = new QHBoxLayout (this); - - mp_x_le = new QLineEdit (this); - mp_layout->addWidget (mp_x_le); - mp_y_le = new QLineEdit (this); - mp_layout->addWidget (mp_y_le); - mp_layout->addStretch (1); - - hide (); - - set_focus_page (true); - set_toolbox_widget (true); - } - - virtual std::string title () const - { - return "Move Options"; - } - - virtual const char *name () const - { - return move_editor_options_name.c_str (); - } - - virtual int order () const - { - return 0; - } - - virtual void deactivated () - { - hide (); - } - - virtual void commit (lay::Dispatcher *dispatcher) - { - try { - - double dx = 0.0, dy = 0.0; - - tl::from_string (tl::to_string (mp_x_le->text ()), dx); - tl::from_string (tl::to_string (mp_y_le->text ()), dy); - - dispatcher->call_function (move_function_name, db::DVector (dx, dy).to_string ()); - - } catch (...) { - } - } - - virtual void configure (const std::string &name, const std::string &value) - { - if (name == move_distance_setter_name && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) { - - try { - - db::DVector mv; - tl::from_string (value, mv); - - mp_x_le->setText (tl::to_qstring (tl::micron_to_string (mv.x ()))); - mp_y_le->setText (tl::to_qstring (tl::micron_to_string (mv.y ()))); - - } catch (...) { - } - } - } - -private: - QHBoxLayout *mp_layout; - QLineEdit *mp_x_le, *mp_y_le; -}; - -} -#endif - class MoveServiceDeclaration : public lay::PluginDeclaration { @@ -550,13 +466,13 @@ class MoveServiceDeclaration return new MoveService (view); } -#if defined(HAVE_QT) virtual void get_editor_options_pages (std::vector &pages, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) const { - pages.push_back (new MoveToolboxPage (view, dispatcher)); - pages.back ()->set_plugin_declaration (this); + lay::EditorOptionsPage *page = lay::EditorOptionsPageFactoryBase::create_page_by_name (move_editor_options_name, view, dispatcher); + if (page) { + pages.push_back (page); + } } -#endif }; static tl::RegisteredClass move_service_decl (new MoveServiceDeclaration (), -970, "laybasic::MoveServicePlugin"); diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index b487a22d8..b2c08f306 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -31,6 +31,10 @@ namespace lay { +LAYBASIC_PUBLIC extern std::string move_editor_options_name; +LAYBASIC_PUBLIC extern std::string move_function_name; +LAYBASIC_PUBLIC extern std::string move_distance_setter_name; + class LayoutViewBase; class LAYBASIC_PUBLIC MoveService : diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc index d05eeef6b..e2264921e 100644 --- a/src/layui/layui/layWidgets.cc +++ b/src/layui/layui/layWidgets.cc @@ -1473,6 +1473,9 @@ DecoratedLineEdit::DecoratedLineEdit (QWidget *parent) mp_clear_label->setCursor (Qt::ArrowCursor); mp_clear_label->setPixmap (QString::fromUtf8 (":/clear_edit_16px@2x.png")); + mp_front_label = new QLabel (this); + mp_front_label->hide (); + QMargins margins = textMargins (); m_default_left_margin = margins.left (); m_default_right_margin = margins.right (); @@ -1535,43 +1538,57 @@ bool DecoratedLineEdit::focusNextPrevChild (bool next) return QLineEdit::focusNextPrevChild (next); } -void DecoratedLineEdit::set_clear_button_enabled (bool en) +void DecoratedLineEdit::set_margins () { - if (en != m_clear_button_enabled) { + int right_margin = m_default_right_margin; + int left_margin = m_default_left_margin; - m_clear_button_enabled = en; - mp_clear_label->setVisible (en); + // right parts + if (m_clear_button_enabled) { + right_margin += mp_clear_label->sizeHint ().width () + le_decoration_space; + } - QMargins margins = textMargins (); - if (! en) { - margins.setRight (m_default_right_margin); - } else { - margins.setRight (m_default_right_margin + mp_clear_label->sizeHint ().width () + le_decoration_space); - } - setTextMargins (margins); + // left parts + if (! m_label.empty ()) { + left_margin += mp_front_label->sizeHint ().width () + le_decoration_space; + } + if (m_options_button_enabled) { + left_margin += mp_options_label->sizeHint ().width () + le_decoration_space; + } - resizeEvent (0); + QMargins margins = textMargins (); + margins.setRight (right_margin); + margins.setLeft (left_margin); + setTextMargins (margins); + + resizeEvent (0); +} +void DecoratedLineEdit::set_clear_button_enabled (bool en) +{ + if (en != m_clear_button_enabled) { + m_clear_button_enabled = en; + mp_clear_label->setVisible (en); + set_margins (); } } void DecoratedLineEdit::set_options_button_enabled (bool en) { if (en != m_options_button_enabled) { - m_options_button_enabled = en; mp_options_label->setVisible (en); + set_margins (); + } +} - QMargins margins = textMargins (); - if (! en) { - margins.setLeft (m_default_left_margin); - } else { - margins.setLeft (m_default_left_margin + mp_options_label->sizeHint ().width () + le_decoration_space); - } - setTextMargins (margins); - - resizeEvent (0); - +void DecoratedLineEdit::set_label (const std::string &label) +{ + if (label != m_label) { + m_label = label; + mp_front_label->setVisible (! label.empty ()); + mp_front_label->setText (tl::to_qstring (label)); + set_margins (); } } @@ -1621,17 +1638,27 @@ void DecoratedLineEdit::mousePressEvent (QMouseEvent *event) void DecoratedLineEdit::resizeEvent (QResizeEvent *event) { int fw = hasFrame () ? le_frame_width : 0; + QRect r = geometry (); - if (m_clear_button_enabled) { - QSize label_size = mp_clear_label->sizeHint (); - QRect r = geometry (); - mp_clear_label->setGeometry (r.width () - fw - label_size.width (), 0, label_size.width (), r.height ()); - } + int left = fw, right = r.width () - fw; if (m_options_button_enabled) { QSize label_size = mp_options_label->sizeHint (); - QRect r = geometry (); - mp_options_label->setGeometry (fw, 0, label_size.width (), r.height ()); + mp_options_label->setGeometry (left, 0, label_size.width (), r.height ()); + left += label_size.width () + le_decoration_space; + } + + if (! m_label.empty ()) { + QSize label_size = mp_front_label->sizeHint (); + mp_front_label->setGeometry (left, 0, label_size.width (), r.height ()); + left += label_size.width () + le_decoration_space; + } + + if (m_clear_button_enabled) { + QSize label_size = mp_clear_label->sizeHint (); + right -= label_size.width (); + mp_clear_label->setGeometry (right, 0, label_size.width (), r.height ()); + right -= le_decoration_space; } QLineEdit::resizeEvent (event); diff --git a/src/layui/layui/layWidgets.h b/src/layui/layui/layWidgets.h index ccba0d32c..f3e667020 100644 --- a/src/layui/layui/layWidgets.h +++ b/src/layui/layui/layWidgets.h @@ -566,6 +566,19 @@ Q_OBJECT return m_tab_signal_enabled; } + /** + * @brief Sets a label in front of the line edit + */ + void set_label (const std::string &label); + + /** + * @brief Gets the label + */ + const std::string &label () const + { + return m_label; + } + signals: void options_button_clicked (); void esc_pressed (); @@ -588,8 +601,12 @@ Q_OBJECT bool m_tab_signal_enabled; QLabel *mp_options_label; QLabel *mp_clear_label; + QLabel *mp_front_label; QMenu *mp_options_menu; int m_default_left_margin, m_default_right_margin; + std::string m_label; + + void set_margins (); }; /** diff --git a/src/layview/layview/layEditorOptionsPageWidget.cc b/src/layview/layview/layEditorOptionsPageWidget.cc new file mode 100644 index 000000000..34914c61c --- /dev/null +++ b/src/layview/layview/layEditorOptionsPageWidget.cc @@ -0,0 +1,171 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layEditorOptionsPageWidget.h" +#include "layLayoutViewBase.h" +#include "tlExceptions.h" + +#include +#include + +namespace lay +{ + +// ------------------------------------------------------------------ +// EditorOptionsPageWidget implementation + +EditorOptionsPageWidget::EditorOptionsPageWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : QWidget (0), EditorOptionsPage (view, dispatcher), m_is_transparent (false) +{ + init (view, dispatcher); +} + +EditorOptionsPageWidget::EditorOptionsPageWidget () + : QWidget (0), EditorOptionsPage () +{ + // .. nothing yet .. +} + +EditorOptionsPageWidget::~EditorOptionsPageWidget () +{ + set_owner (0); +} + +void +EditorOptionsPageWidget::edited () +{ + apply (dispatcher ()); +} + +static bool is_parent_widget (QWidget *w, QWidget *parent) +{ + while (w && w != parent) { + w = dynamic_cast (w->parent ()); + } + return w == parent; +} + +bool +EditorOptionsPageWidget::focusNextPrevChild (bool next) +{ + bool res = QWidget::focusNextPrevChild (next); + + // Stop making the focus leave the page - this way we can jump back to the + // view on "enter" + if (res && ! is_modal_page () && ! is_parent_widget (QApplication::focusWidget (), this) && focusWidget ()) { + focusWidget ()->setFocus (); + } + + return res; +} + +void +EditorOptionsPageWidget::keyPressEvent (QKeyEvent *event) +{ +BEGIN_PROTECTED + if (! is_modal_page () && + event->modifiers () == Qt::NoModifier && + (event->key () == Qt::Key_Return || event->key () == Qt::Key_Enter || event->key () == Qt::Key_Escape)) { + if (event->key () == Qt::Key_Escape) { + // The Escape key creates a call to cancel() + cancel (); + } else { + // The Return key on a non-modal page commits the values and gives back the focus + // to the view + commit (dispatcher ()); + } + view ()->set_focus (); + event->accept (); + } else { + QWidget::keyPressEvent (event); + } +END_PROTECTED +} + +bool +EditorOptionsPageWidget::event (QEvent *event) +{ + if (event->type () == QEvent::ShortcutOverride) { + QKeyEvent *ke = dynamic_cast (event); + if (ke->key () == Qt::Key_Escape || + ke->key () == Qt::Key_Tab || + ke->key () == Qt::Key_Enter || + ke->key () == Qt::Key_Return || + ke->key () == Qt::Key_Backtab) { + // accept the shortcut override event for some keys, so we can handle + // it in keyPressEvent + ke->accept (); + } + } + return QWidget::event (event); +} + +void +EditorOptionsPageWidget::resizeEvent (QResizeEvent *e) +{ + // makes the widget transparent + // see https://stackoverflow.com/questions/27855137/how-to-disable-the-delivery-of-mouse-events-to-the-widget-but-not-its-children-i + if (e) { + QWidget::resizeEvent (e); + } + + if (m_is_transparent) { + QRegion reg (frameGeometry ()); + reg -= QRegion (geometry ()); + reg += childrenRegion (); + setMask (reg); + } else { + clearMask (); + } +} + +void +EditorOptionsPageWidget::set_transparent (bool f) +{ + if (f != m_is_transparent) { + m_is_transparent = f; + resizeEvent (0); + } +} + +void +EditorOptionsPageWidget::set_focus () +{ + if (isVisible ()) { + setFocus (Qt::TabFocusReason); + QWidget::focusNextPrevChild (true); + } +} + +void +EditorOptionsPageWidget::set_visible (bool visible) +{ + setVisible (visible); +} + +bool +EditorOptionsPageWidget::is_visible () const +{ + return isVisible (); +} + +} diff --git a/src/layview/layview/layEditorOptionsPageWidget.h b/src/layview/layview/layEditorOptionsPageWidget.h new file mode 100644 index 000000000..65c7bd314 --- /dev/null +++ b/src/layview/layview/layEditorOptionsPageWidget.h @@ -0,0 +1,70 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layEditorOptionsPageWidget +#define HDR_layEditorOptionsPageWidget + +#include + +#include "layviewCommon.h" +#include "layEditorOptionsPage.h" + +namespace lay +{ + +/** + * @brief The base class for a object properties page + */ +class LAYVIEW_PUBLIC EditorOptionsPageWidget + : public QWidget, public EditorOptionsPage +{ +Q_OBJECT + +public: + EditorOptionsPageWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + EditorOptionsPageWidget (); + virtual ~EditorOptionsPageWidget (); + + virtual void set_focus (); + virtual bool is_visible () const; + virtual void set_visible (bool visible); + virtual EditorOptionsPageWidget *widget () { return this; } + + void set_transparent (bool f); + bool is_transparent () const { return m_is_transparent; } + +protected slots: + void edited (); + +protected: + virtual bool focusNextPrevChild (bool next); + virtual void keyPressEvent (QKeyEvent *event); + virtual void resizeEvent (QResizeEvent *e); + virtual bool event (QEvent *event); + + bool m_is_transparent; +}; + +} + +#endif + diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index a36190106..4c33c1c83 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -22,6 +22,7 @@ #include "tlInternational.h" #include "layEditorOptionsPages.h" +#include "layEditorOptionsPageWidget.h" #include "tlExceptions.h" #include "layPlugin.h" #include "layLayoutViewBase.h" diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index d1f43c998..894da7c33 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -76,6 +76,7 @@ #include "layBookmarksView.h" #include "layEditorOptionsFrame.h" #include "layEditorOptionsPages.h" +#include "layEditorOptionsPageWidget.h" #include "layUtils.h" #include "layPropertiesDialog.h" #include "layQtTools.h" diff --git a/src/layview/layview/layMoveEditorOptionsPage.cc b/src/layview/layview/layMoveEditorOptionsPage.cc new file mode 100644 index 000000000..7b79ba97f --- /dev/null +++ b/src/layview/layview/layMoveEditorOptionsPage.cc @@ -0,0 +1,117 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layMoveEditorOptionsPage.h" +#include "layWidgets.h" +#include "layDispatcher.h" + +#include +#include + +namespace lay +{ + +MoveEditorOptionsPage::MoveEditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) +{ + mp_layout = new QHBoxLayout (this); + + mp_x_le = new lay::DecoratedLineEdit (this); + mp_x_le->set_label ("dx:"); + mp_layout->addWidget (mp_x_le); + + mp_y_le = new lay::DecoratedLineEdit (this); + mp_y_le->set_label ("dy:"); + mp_layout->addWidget (mp_y_le); + + mp_layout->addStretch (1); + + hide (); + + set_focus_page (true); + set_toolbox_widget (true); + set_transparent (true); +} + +std::string +MoveEditorOptionsPage::title () const +{ + return "Move Options"; +} + +const char * +MoveEditorOptionsPage::name () const +{ + return move_editor_options_name.c_str (); +} + +int +MoveEditorOptionsPage::order () const +{ + return 0; +} + +void +MoveEditorOptionsPage::deactivated () +{ + hide (); +} + +void +MoveEditorOptionsPage::commit (lay::Dispatcher *dispatcher) +{ + try { + + double dx = 0.0, dy = 0.0; + + tl::from_string (tl::to_string (mp_x_le->text ()), dx); + tl::from_string (tl::to_string (mp_y_le->text ()), dy); + + dispatcher->call_function (move_function_name, db::DVector (dx, dy).to_string ()); + + } catch (...) { + } +} + +void +MoveEditorOptionsPage::configure (const std::string &name, const std::string &value) +{ + if (name == move_distance_setter_name && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) { + + try { + + db::DVector mv; + tl::from_string (value, mv); + + mp_x_le->setText (tl::to_qstring (tl::micron_to_string (mv.x ()))); + mp_y_le->setText (tl::to_qstring (tl::micron_to_string (mv.y ()))); + + } catch (...) { + } + + } +} + +// registers the factory for the move plugin +static tl::RegisteredClass s_factory (new lay::EditorOptionsPageFactory (), 0, move_editor_options_name.c_str ()); + +} diff --git a/src/layview/layview/layMoveEditorOptionsPage.h b/src/layview/layview/layMoveEditorOptionsPage.h new file mode 100644 index 000000000..21bf6f28c --- /dev/null +++ b/src/layview/layview/layMoveEditorOptionsPage.h @@ -0,0 +1,59 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layMoveEditorOptionsPage +#define HDR_layMoveEditorOptionsPage + +#include "layviewCommon.h" +#include "layEditorOptionsPageWidget.h" +#include "layMove.h" + +class QHBoxLayout; + +namespace lay { + class DecoratedLineEdit; +} + +namespace lay { + +class MoveEditorOptionsPage + : public lay::EditorOptionsPageWidget +{ +public: + MoveEditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + + virtual std::string title () const; + virtual const char *name () const; + virtual int order () const; + virtual void deactivated (); + virtual void commit (lay::Dispatcher *dispatcher); + virtual void configure (const std::string &name, const std::string &value); + +private: + QHBoxLayout *mp_layout; + lay::DecoratedLineEdit *mp_x_le, *mp_y_le; +}; + +} + +#endif + diff --git a/src/layview/layview/layview.pro b/src/layview/layview/layview.pro index 3d5ab457a..3d20f19e6 100644 --- a/src/layview/layview/layview.pro +++ b/src/layview/layview/layview.pro @@ -9,17 +9,21 @@ DEFINES += MAKE_LAYVIEW_LIBRARY RESOURCES = \ SOURCES = \ + layEditorOptionsPageWidget.cc \ layGridNet.cc \ layEditorOptionsFrame.cc \ layEditorOptionsPages.cc \ + layMoveEditorOptionsPage.cc \ layviewForceLink.cc \ gsiDeclLayAdditional.cc \ HEADERS = \ + layEditorOptionsPageWidget.h \ layGridNet.h \ layEditorOptionsFrame.h \ layEditorOptionsPages.h \ layLayoutView.h \ + layMoveEditorOptionsPage.h \ layviewForceLink.h \ !equals(HAVE_QT, "0") { From cca73a8ebbf73fd6e86deace6ee9406d009305b7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 14 Jan 2026 00:27:53 +0100 Subject: [PATCH 17/36] WIP, some refactoring and bug fixing. Move toolkit widget works --- src/laybasic/laybasic/layMove.cc | 1 + src/laybasic/laybasic/laybasic.pro | 4 ---- .../laybasic => layview/layview}/gsiDeclLayConfigPage.cc | 0 .../laybasic => layview/layview}/gsiDeclLayConfigPage.h | 0 .../layview}/gsiDeclLayEditorOptionsPage.cc | 2 +- .../layview}/gsiDeclLayEditorOptionsPage.h | 4 ++-- src/layview/layview/layEditorOptionsPages.cc | 3 ++- src/layview/layview/layview.pro | 4 ++++ 8 files changed, 10 insertions(+), 8 deletions(-) rename src/{laybasic/laybasic => layview/layview}/gsiDeclLayConfigPage.cc (100%) rename src/{laybasic/laybasic => layview/layview}/gsiDeclLayConfigPage.h (100%) rename src/{laybasic/laybasic => layview/layview}/gsiDeclLayEditorOptionsPage.cc (98%) rename src/{laybasic/laybasic => layview/layview}/gsiDeclLayEditorOptionsPage.h (94%) diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 56087a660..5b759162c 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -470,6 +470,7 @@ class MoveServiceDeclaration { lay::EditorOptionsPage *page = lay::EditorOptionsPageFactoryBase::create_page_by_name (move_editor_options_name, view, dispatcher); if (page) { + page->set_plugin_declaration (this); pages.push_back (page); } } diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 85c921ff0..7a870fa69 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -36,8 +36,6 @@ SOURCES += \ gsiDeclLayTextInfo.cc \ gsiDeclLayTlAdded.cc \ gsiDeclLayRdbAdded.cc \ - gsiDeclLayConfigPage.cc \ - gsiDeclLayEditorOptionsPage.cc \ gsiDeclLayPlugin.cc \ gsiDeclLayPluginFactory.cc \ layAbstractMenu.cc \ @@ -95,8 +93,6 @@ SOURCES += \ layUtils.cc \ HEADERS += \ - gsiDeclLayConfigPage.h \ - gsiDeclLayEditorOptionsPage.h \ gsiDeclLayPlugin.h \ layEditorOptionsPage.h \ layEditorUtils.h \ diff --git a/src/laybasic/laybasic/gsiDeclLayConfigPage.cc b/src/layview/layview/gsiDeclLayConfigPage.cc similarity index 100% rename from src/laybasic/laybasic/gsiDeclLayConfigPage.cc rename to src/layview/layview/gsiDeclLayConfigPage.cc diff --git a/src/laybasic/laybasic/gsiDeclLayConfigPage.h b/src/layview/layview/gsiDeclLayConfigPage.h similarity index 100% rename from src/laybasic/laybasic/gsiDeclLayConfigPage.h rename to src/layview/layview/gsiDeclLayConfigPage.h diff --git a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc b/src/layview/layview/gsiDeclLayEditorOptionsPage.cc similarity index 98% rename from src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc rename to src/layview/layview/gsiDeclLayEditorOptionsPage.cc index de8619a37..112bb5444 100644 --- a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.cc +++ b/src/layview/layview/gsiDeclLayEditorOptionsPage.cc @@ -30,7 +30,7 @@ namespace gsi { -Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWidget) "lay", "EditorOptionsPageBase", +Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWidget) "lay", "EditorOptionsPageBase", method ("view", &lay::EditorOptionsPage::view, "@brief Gets the view object this page is associated with\n" ) + diff --git a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h b/src/layview/layview/gsiDeclLayEditorOptionsPage.h similarity index 94% rename from src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h rename to src/layview/layview/gsiDeclLayEditorOptionsPage.h index 8a8ee1b84..972e27070 100644 --- a/src/laybasic/laybasic/gsiDeclLayEditorOptionsPage.h +++ b/src/layview/layview/gsiDeclLayEditorOptionsPage.h @@ -28,14 +28,14 @@ #include "gsiDecl.h" #include "gsiDeclBasic.h" -#include "layEditorOptionsPage.h" +#include "layEditorOptionsPageWidget.h" #include "layLayoutViewBase.h" namespace gsi { class EditorOptionsPageImpl - : public lay::EditorOptionsPage, public gsi::ObjectBase + : public lay::EditorOptionsPageWidget, public gsi::ObjectBase { public: EditorOptionsPageImpl (const std::string &title, int index); diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index 4c33c1c83..48eca6d81 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -98,7 +98,8 @@ EditorOptionsPages::editor_options_pages (const lay::PluginDeclaration *plugin_d { std::vector pages; for (auto p = m_pages.begin (); p != m_pages.end (); ++p) { - if (p->plugin_declaration () == plugin_declaration) { + if (p->plugin_declaration () == plugin_declaration || + (p->plugin_declaration () == 0 && plugin_declaration->enable_catchall_editor_options_pages ())) { pages.push_back (const_cast (p.operator-> ())); } } diff --git a/src/layview/layview/layview.pro b/src/layview/layview/layview.pro index 3d20f19e6..0a450c59c 100644 --- a/src/layview/layview/layview.pro +++ b/src/layview/layview/layview.pro @@ -9,6 +9,8 @@ DEFINES += MAKE_LAYVIEW_LIBRARY RESOURCES = \ SOURCES = \ + gsiDeclLayEditorOptionsPage.cc \ + gsiDeclLayConfigPage.cc \ layEditorOptionsPageWidget.cc \ layGridNet.cc \ layEditorOptionsFrame.cc \ @@ -18,6 +20,8 @@ SOURCES = \ gsiDeclLayAdditional.cc \ HEADERS = \ + gsiDeclLayEditorOptionsPage.h \ + gsiDeclLayConfigPage.h \ layEditorOptionsPageWidget.h \ layGridNet.h \ layEditorOptionsFrame.h \ From 6666b2b68cf257967a6681fca56aeac1394a122e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 14 Jan 2026 02:37:26 +0100 Subject: [PATCH 18/36] Implementing 'diagonal only' for rulers --- src/ant/ant/RulerConfigPage3.ui | 20 +-- src/ant/ant/RulerConfigPage4.ui | 5 + src/ant/ant/antConfig.cc | 41 ------ src/ant/ant/antConfig.h | 6 - src/ant/ant/antConfigPage.cc | 8 +- src/ant/ant/antObject.cc | 5 +- src/ant/ant/antPlugin.cc | 2 +- src/ant/ant/antService.cc | 2 +- src/ant/ant/antTemplate.cc | 5 +- src/ant/ant/gsiDeclAnt.cc | 6 + src/edt/edt/edtPartialService.cc | 145 ++++++++++------------ src/edt/edt/edtPartialService.h | 2 +- src/laybasic/laybasic/gsiDeclLayPlugin.cc | 4 + src/laybasic/laybasic/layConverters.cc | 16 +++ 14 files changed, 127 insertions(+), 140 deletions(-) diff --git a/src/ant/ant/RulerConfigPage3.ui b/src/ant/ant/RulerConfigPage3.ui index e9d90e434..4e7fed45d 100644 --- a/src/ant/ant/RulerConfigPage3.ui +++ b/src/ant/ant/RulerConfigPage3.ui @@ -7,7 +7,7 @@ 0 0 665 - 103 + 108 @@ -56,6 +56,13 @@ 0 + + + + Diagonal + + + @@ -77,17 +84,17 @@ - - + + - Diagonal + Vertical only - + - Vertical only + Diagonal only @@ -102,7 +109,6 @@ ruler_ortho_rb ruler_diag_rb ruler_hor_rb - ruler_vert_rb diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui index 87e5dc3cc..1f5ec4a29 100644 --- a/src/ant/ant/RulerConfigPage4.ui +++ b/src/ant/ant/RulerConfigPage4.ui @@ -644,6 +644,11 @@ Orthogonal + + + Diagonal only + + Horizontal only diff --git a/src/ant/ant/antConfig.cc b/src/ant/ant/antConfig.cc index bec7c21f4..b2f4c239b 100644 --- a/src/ant/ant/antConfig.cc +++ b/src/ant/ant/antConfig.cc @@ -29,47 +29,6 @@ namespace ant // ------------------------------------------------------------ // Helper functions to get and set the configuration -std::string -ACConverter::to_string (const lay::angle_constraint_type &m) -{ - if (m == lay::AC_Any) { - return "any"; - } else if (m == lay::AC_Diagonal) { - return "diagonal"; - } else if (m == lay::AC_Ortho) { - return "ortho"; - } else if (m == lay::AC_Horizontal) { - return "horizontal"; - } else if (m == lay::AC_Vertical) { - return "vertical"; - } else if (m == lay::AC_Global) { - return "global"; - } else { - return ""; - } -} - -void -ACConverter::from_string (const std::string &tt, lay::angle_constraint_type &m) -{ - std::string t (tl::trim (tt)); - if (t == "any") { - m = lay::AC_Any; - } else if (t == "diagonal") { - m = lay::AC_Diagonal; - } else if (t == "ortho") { - m = lay::AC_Ortho; - } else if (t == "horizontal") { - m = lay::AC_Horizontal; - } else if (t == "vertical") { - m = lay::AC_Vertical; - } else if (t == "global") { - m = lay::AC_Global; - } else { - m = lay::AC_Any; - } -} - std::string StyleConverter::to_string (ant::Object::style_type s) { diff --git a/src/ant/ant/antConfig.h b/src/ant/ant/antConfig.h index cb1bb5e10..4c603ac37 100644 --- a/src/ant/ant/antConfig.h +++ b/src/ant/ant/antConfig.h @@ -51,12 +51,6 @@ extern ANT_PUBLIC const std::string cfg_current_ruler_template; // ------------------------------------------------------------ // Helper functions to get and set the configuration -struct ACConverter -{ - std::string to_string (const lay::angle_constraint_type &m); - void from_string (const std::string &s, lay::angle_constraint_type &m); -}; - struct StyleConverter { std::string to_string (ant::Object::style_type s); diff --git a/src/ant/ant/antConfigPage.cc b/src/ant/ant/antConfigPage.cc index 1dcd1cc69..6647035be 100644 --- a/src/ant/ant/antConfigPage.cc +++ b/src/ant/ant/antConfigPage.cc @@ -159,10 +159,11 @@ ConfigPage3::setup (lay::Dispatcher *root) { // snap mode lay::angle_constraint_type rm = lay::AC_Any; - root->config_get (cfg_ruler_snap_mode, rm, ACConverter ()); + root->config_get (cfg_ruler_snap_mode, rm, lay::ACConverter ()); mp_ui->ruler_any_angle_rb->setChecked (rm == lay::AC_Any); mp_ui->ruler_ortho_rb->setChecked (rm == lay::AC_Ortho); mp_ui->ruler_diag_rb->setChecked (rm == lay::AC_Diagonal); + mp_ui->ruler_diag_only_rb->setChecked (rm == lay::AC_DiagonalOnly); mp_ui->ruler_hor_rb->setChecked (rm == lay::AC_Horizontal); mp_ui->ruler_vert_rb->setChecked (rm == lay::AC_Vertical); } @@ -180,13 +181,16 @@ ConfigPage3::commit (lay::Dispatcher *root) if (mp_ui->ruler_diag_rb->isChecked ()) { rm = lay::AC_Diagonal; } + if (mp_ui->ruler_diag_only_rb->isChecked ()) { + rm = lay::AC_DiagonalOnly; + } if (mp_ui->ruler_hor_rb->isChecked ()) { rm = lay::AC_Horizontal; } if (mp_ui->ruler_vert_rb->isChecked ()) { rm = lay::AC_Vertical; } - root->config_set (cfg_ruler_snap_mode, rm, ACConverter ()); + root->config_set (cfg_ruler_snap_mode, rm, lay::ACConverter ()); } // ------------------------------------------------------------ diff --git a/src/ant/ant/antObject.cc b/src/ant/ant/antObject.cc index 9ce86eed3..de4af2cee 100644 --- a/src/ant/ant/antObject.cc +++ b/src/ant/ant/antObject.cc @@ -24,6 +24,7 @@ #include "antObject.h" #include "antTemplate.h" #include "antConfig.h" +#include "layConverters.h" #include "tlString.h" #include "tlExpression.h" @@ -713,7 +714,7 @@ Object::from_string (const char *s, const char * /*base_dir*/) std::string s; ex.read_word (s); - ant::ACConverter sc; + lay::ACConverter sc; lay::angle_constraint_type sm; sc.from_string (s, sm); angle_constraint (sm); @@ -817,7 +818,7 @@ Object::to_string () const r += ","; r += "angle_constraint="; - ant::ACConverter acc; + lay::ACConverter acc; r += acc.to_string (angle_constraint ()); return r; diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index ab21a915c..aaed7f8d6 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -102,7 +102,7 @@ PluginDeclaration::get_options (std::vector < std::pair (cfg_ruler_snap_range, "8")); options.push_back (std::pair (cfg_ruler_color, lay::ColorConverter ().to_string (tl::Color ()))); options.push_back (std::pair (cfg_ruler_halo, "true")); - options.push_back (std::pair (cfg_ruler_snap_mode, ACConverter ().to_string (lay::AC_Any))); + options.push_back (std::pair (cfg_ruler_snap_mode, lay::ACConverter ().to_string (lay::AC_Any))); options.push_back (std::pair (cfg_ruler_obj_snap, tl::to_string (true))); options.push_back (std::pair (cfg_ruler_grid_snap, tl::to_string (false))); options.push_back (std::pair (cfg_ruler_templates, std::string ())); diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 2a66becba..188b092f0 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1134,7 +1134,7 @@ Service::configure (const std::string &name, const std::string &value) } else if (name == cfg_ruler_snap_mode) { lay::angle_constraint_type sm = lay::AC_Any; - ACConverter ().from_string (value, sm); + lay::ACConverter ().from_string (value, sm); m_snap_mode = sm; } else if (name == cfg_ruler_templates) { diff --git a/src/ant/ant/antTemplate.cc b/src/ant/ant/antTemplate.cc index 58994d619..2c5c7d537 100644 --- a/src/ant/ant/antTemplate.cc +++ b/src/ant/ant/antTemplate.cc @@ -23,6 +23,7 @@ #include "antTemplate.h" #include "antConfig.h" +#include "layConverters.h" #include "tlInternational.h" #include "tlException.h" #include "tlLog.h" @@ -263,7 +264,7 @@ Template::from_string (const std::string &s) } else if (key == "angle_constraint") { - ant::ACConverter sc; + lay::ACConverter sc; lay::angle_constraint_type sm; sc.from_string (s, sm); r.back ().angle_constraint (sm); @@ -373,7 +374,7 @@ Template::to_string (const std::vector