Implemented solution for issue #2176

- On "via" (Key "O"), the application switches into path mode
- A selection menu pops up if multiple layers are possible starting
  points below the cursor

As a side effect, "tap" will only display the layer selection menu
if there is more than one layer to be selected.
This commit is contained in:
Matthias Koefferlein 2025-10-18 22:02:58 +02:00
parent ec5de0ffe8
commit 780615ea9d
9 changed files with 179 additions and 110 deletions

View File

@ -46,8 +46,8 @@ ViaType::init ()
// ---------------------------------------------------------------------------------------
std::vector<SelectedViaDefinition>
find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir)
static std::vector<SelectedViaDefinition>
find_via_definitions_impl (const std::string &technology, const db::LayerProperties &layer, int dir, bool all)
{
std::vector<SelectedViaDefinition> via_defs;
@ -65,7 +65,8 @@ find_via_definitions_for (const std::string &technology, const db::LayerProperti
auto via_types = pcell->via_types ();
for (auto vt = via_types.begin (); vt != via_types.end (); ++vt) {
if ((dir >= 0 && vt->bottom.log_equal (layer) && vt->bottom_wired) ||
if (all ||
(dir >= 0 && vt->bottom.log_equal (layer) && vt->bottom_wired) ||
(dir <= 0 && vt->top.log_equal (layer) && vt->top_wired)) {
via_defs.push_back (SelectedViaDefinition (lib, pc->second, *vt));
}
@ -78,4 +79,16 @@ find_via_definitions_for (const std::string &technology, const db::LayerProperti
return via_defs;
}
std::vector<SelectedViaDefinition>
get_via_definitions (const std::string &technology)
{
return find_via_definitions_impl (technology, db::LayerProperties (), 0, true);
}
std::vector<SelectedViaDefinition>
find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir)
{
return find_via_definitions_impl (technology, layer, dir, false);
}
}

View File

@ -210,6 +210,9 @@ struct SelectedViaDefinition
DB_PUBLIC std::vector<SelectedViaDefinition>
find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir);
DB_PUBLIC std::vector<SelectedViaDefinition>
get_via_definitions (const std::string &technology);
}
#endif

View File

@ -29,8 +29,9 @@
#include "layEditorUtils.h"
#include "layObjectInstPath.h"
#include "layCellView.h"
#include "layLayoutViewBase.h"
#include "layMarker.h"
#include "layFinder.h"
#include "layLayerTreeModel.h"
#include "tlException.h"
#include "tlExceptions.h"
@ -725,6 +726,93 @@ AreaAndPerimeterDialog::exec_dialog (double area, double perimeter)
return exec () != 0;
}
// --------------------------------------------------------------------------------
// popup_tap_layer_menu implementation
lay::LayerPropertiesConstIterator
popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set<db::LayerProperties, db::LPLogicalLessFunc> *filter, int cv_index)
{
QWidget *view_widget = lay::widget_from_view (view);
if (! view_widget) {
return lay::LayerPropertiesConstIterator ();
}
if (! view->canvas ()->mouse_in_window ()) {
return lay::LayerPropertiesConstIterator ();
}
lay::ShapeFinder finder (true, // point mode
false, // all hierarchy levels
db::ShapeIterator::flags_type (db::ShapeIterator::All - db::ShapeIterator::Texts), // do not consider texts - their bounding box may be too large
0, // no excludes
true // capture all shapes
);
// capture all objects in point mode (default: capture one only)
finder.set_catch_all (true);
// go through all visible layers of all cellviews
db::DPoint pt = view->canvas ()->mouse_position_um ();
finder.find (view, db::DBox (pt, pt));
std::set<std::pair<unsigned int, unsigned int> > layers_in_selection;
for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) {
if (cv_index < 0 || f->cv_index () == cv_index) {
const db::Layout &ly = view->cellview (f->cv_index ())->layout ();
// ignore guiding shapes and only provide layers from the filter
if (f->layer () != ly.guiding_shape_layer ()
&& (! filter || filter->find (ly.get_properties (f->layer ())) != filter->end ())) {
layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ()));
}
}
}
std::vector<lay::LayerPropertiesConstIterator> tapped_layers;
for (lay::LayerPropertiesConstIterator lp = view->begin_layers (view->current_layer_list ()); ! lp.at_end (); ++lp) {
const lay::LayerPropertiesNode *ln = lp.operator-> ();
if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) {
tapped_layers.push_back (lp);
}
}
if (tapped_layers.empty ()) {
return lay::LayerPropertiesConstIterator ();
} else if (tapped_layers.size () == 1) {
return tapped_layers.front ();
}
// List the layers under the cursor as pop up a menu
#if QT_VERSION >= 0x050000
double dpr = view_widget->devicePixelRatio ();
#else
double dpr = 1.0;
#endif
std::unique_ptr<QMenu> menu (new QMenu (view_widget));
menu->show ();
int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
db::DPoint mp_local = view->canvas ()->mouse_position ();
QPoint mp = view->canvas ()->widget ()->mapToGlobal (QPoint (mp_local.x (), mp_local.y ()));
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view, icon_size, icon_size, dpr, 0, true), tl::to_qstring ((*l)->display_string (view, true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}
QAction *action = menu->exec (mp);
if (action) {
int index = action->data ().toInt ();
return tapped_layers [index];
} else {
return lay::LayerPropertiesConstIterator ();
}
}
}
#endif

View File

@ -34,6 +34,9 @@
#include "dbLayout.h"
#include "dbPoint.h"
#include "dbLayerProperties.h"
#include "layLayoutView.h"
#include "ui_InstantiationForm.h"
#include "ui_ChangeLayerOptionsDialog.h"
@ -47,7 +50,6 @@
namespace lay
{
class LayoutViewBase;
class Marker;
class ObjectInstPath;
}
@ -222,6 +224,17 @@ public:
bool exec_dialog (double area, double perimeter);
};
/**
* @brief Obtains an layer iterator for one of the layers present under the mouse cursor
*
* This is not really a dialog, but a popup menu.
*
* @param view The LayoutView object
* @param filter An optional set of layers to show. Only layers from this set are shown.
* @return A layer iterator which is at_end if no specific layer was selected
*/
lay::LayerPropertiesConstIterator popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set<db::LayerProperties, db::LPLogicalLessFunc> *filter = 0, int cv_index = -1);
} // namespace edt
#endif

View File

@ -47,7 +47,6 @@
#if defined(HAVE_QT)
# include "layDialogs.h"
# include "layLayerTreeModel.h"
# include "layCellSelectionForm.h"
# include "edtDialogs.h"
# include "edtEditorOptionsPages.h"
@ -2361,88 +2360,16 @@ void
MainService::cm_tap ()
{
#if ! defined(HAVE_QT)
tl_assert (false); // see TODO
#endif
#if defined(HAVE_QT)
QWidget *view_widget = lay::widget_from_view (view ());
if (! view_widget) {
return;
}
#endif
if (! view ()->canvas ()->mouse_in_window ()) {
return;
}
lay::ShapeFinder finder (true, // point mode
false, // all hierarchy levels
db::ShapeIterator::flags_type (db::ShapeIterator::All - db::ShapeIterator::Texts), // do not consider texts - their bounding box may be too large
0, // no excludes
true // capture all shapes
);
// capture all objects in point mode (default: capture one only)
finder.set_catch_all (true);
// go through all visible layers of all cellviews
db::DPoint pt = view ()->canvas ()->mouse_position_um ();
finder.find (view (), db::DBox (pt, pt));
std::set<std::pair<unsigned int, unsigned int> > layers_in_selection;
for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) {
// ignore guiding shapes
if (f->layer () != view ()->cellview (f->cv_index ())->layout ().guiding_shape_layer ()) {
layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ()));
}
}
std::vector<lay::LayerPropertiesConstIterator> tapped_layers;
for (lay::LayerPropertiesConstIterator lp = view ()->begin_layers (view ()->current_layer_list ()); ! lp.at_end (); ++lp) {
const lay::LayerPropertiesNode *ln = lp.operator-> ();
if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) {
tapped_layers.push_back (lp);
}
}
if (tapped_layers.empty ()) {
return;
}
// List the layers under the cursor as pop up a menu
#if defined(HAVE_QT)
// TODO: what to do here in Qt-less case? Store results in configuration so they can be retrieved externally?
#if QT_VERSION >= 0x050000
double dpr = view_widget->devicePixelRatio ();
tl_assert (false); // TODO
#else
double dpr = 1.0;
#endif
lay::LayerPropertiesConstIterator iter = edt::popup_tap_layer_menu (view ());
if (! iter.at_end ()) {
std::unique_ptr<QMenu> menu (new QMenu (view_widget));
menu->show ();
int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
db::DPoint mp_local = view ()->canvas ()->mouse_position ();
QPoint mp = view ()->canvas ()->widget ()->mapToGlobal (QPoint (mp_local.x (), mp_local.y ()));
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, dpr, 0, true), tl::to_qstring ((*l)->display_string (view (), true, true /*with source*/)));
a->setData (int (l - tapped_layers.begin ()));
}
QAction *action = menu->exec (mp);
if (action) {
int index = action->data ().toInt ();
lay::LayerPropertiesConstIterator iter = tapped_layers [index];
view ()->set_current_layer (iter);
edt::Service *es = dynamic_cast<edt::Service *> (view ()->canvas ()->active_service ());
if (es) {
db::DPoint pt = view ()->canvas ()->mouse_position_um ();
es->tap (pt);
}
@ -2486,9 +2413,11 @@ MainService::via_impl (int dir)
return;
}
edt::Service *es = dynamic_cast<edt::Service *> (view ()->canvas ()->active_service ());
if (es) {
es->via (dir);
// via generation is delegated to the path service always
edt::PathService *ps = view ()->get_plugin<edt::PathService> ();
if (ps) {
view ()->switch_mode (ps->plugin_declaration ()->id ());
ps->via (dir);
}
}

View File

@ -22,15 +22,12 @@
#include "edtPathService.h"
#include "edtDialogs.h"
#include "edtPropertiesPages.h"
#include "layLayoutViewBase.h"
#include "layFinder.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
# include "layLayoutView.h"
#endif
namespace edt
{
@ -268,6 +265,35 @@ PathService::via (int dir)
#endif
}
db::LayerProperties
PathService::get_layer_for_via (unsigned int cv_index)
{
#if defined(HAVE_QT)
const lay::CellView &cv = view ()->cellview (cv_index);
if (! cv.is_valid ()) {
return db::LayerProperties ();
}
std::vector<db::SelectedViaDefinition> via_defs = db::get_via_definitions (cv->layout ().technology_name ());
std::set<db::LayerProperties, db::LPLogicalLessFunc> lp_filter;
for (auto vd = via_defs.begin (); vd != via_defs.end (); ++vd) {
lp_filter.insert (vd->via_type.bottom);
lp_filter.insert (vd->via_type.top);
}
lay::LayerPropertiesConstIterator iter;
iter = edt::popup_tap_layer_menu (view (), &lp_filter, cv_index);
if (iter.at_end ()) {
return db::LayerProperties ();
}
return cv->layout ().get_properties (iter->layer_index ());
#else
return db::LayerProperties ();
#endif
}
bool
PathService::get_via_for (const db::LayerProperties &lp, unsigned int cv_index, int dir, db::SelectedViaDefinition &via_def)
{
@ -364,19 +390,20 @@ PathService::via_initial (int dir)
return;
}
const lay::CellView &cv = view ()->cellview (r->cv_index ());
if (! cv.is_valid ()) {
// the first found provides the cellview index
int cv_index = r->cv_index ();
db::LayerProperties lp = get_layer_for_via (cv_index);
if (lp.is_null ()) {
return;
}
db::LayerProperties lp = cv->layout ().get_properties (r->layer ());
db::SelectedViaDefinition via_def;
if (! get_via_for (lp, r->cv_index (), dir, via_def)) {
if (! get_via_for (lp, cv_index, dir, via_def)) {
return;
}
set_layer (lp, r->cv_index ());
set_layer (lp, cv_index);
bool is_bottom = via_def.via_type.bottom.log_equal (lp);
db::LayerProperties lp_new = is_bottom ? via_def.via_type.top : via_def.via_type.bottom;

View File

@ -89,6 +89,7 @@ private:
void via_initial (int dir);
void via_editing (int dir);
bool get_via_for (const db::LayerProperties &lp, unsigned int cv_index, int dir, db::SelectedViaDefinition &via_def);
db::LayerProperties get_layer_for_via (unsigned int cv_index);
void push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t transaction_id);
void pop_segment ();
};

View File

@ -26,17 +26,13 @@
#include "dbLibrary.h"
#include "edtPlugin.h"
#include "edtService.h"
#if defined(HAVE_QT)
# include "edtEditorOptionsPages.h"
# include "edtDialogs.h"
#endif
#include "edtEditorOptionsPages.h"
#include "edtDialogs.h"
#include "layFinder.h"
#include "layLayoutView.h"
#include "laySnap.h"
#include "layConverters.h"
#if defined(HAVE_QT)
# include "layEditorOptionsPages.h"
#endif
#include "layEditorOptionsPages.h"
#include "tlProgress.h"
#include "tlTimer.h"
@ -1709,13 +1705,13 @@ Service::begin_edit (const db::DPoint &p)
}
void
Service::tap (const db::DPoint & /*initial*/)
Service::tap (const db::DPoint & /*pt*/)
{
// .. nothing here ..
}
void
Service::via (int)
Service::via (int /*dir*/)
{
// .. nothing here ..
}

View File

@ -22,15 +22,14 @@
#include "edtShapeService.h"
#include "edtMainService.h"
#include "edtPathService.h"
#include "edtPropertiesPages.h"
#include "layLayoutView.h"
#include "dbEdgeProcessor.h"
#include "dbPolygonTools.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
# include "layTipDialog.h"
# include "layEditorOptionsPages.h"
#endif
#include "layTipDialog.h"
#include "layEditorOptionsPages.h"
namespace edt
{