Added Enter to finish shape edits, added numerical entries for paths and polygons

This commit is contained in:
Matthias Koefferlein 2026-01-19 22:41:52 +01:00
parent 280d79a02d
commit 8300e2ee57
19 changed files with 243 additions and 40 deletions

View File

@ -88,7 +88,7 @@ BoxService::function (const std::string &name, const std::string &value)
}
m_p2 = m_p1 + dim;
finish_editing ();
finish_editing (true);
} catch (...) {
}
@ -209,7 +209,7 @@ BoxService::do_mouse_click (const db::DPoint &p)
}
void
BoxService::do_finish_edit ()
BoxService::do_finish_edit (bool /*accept*/)
{
deliver_shape (get_box ());
commit_recent ();

View File

@ -48,7 +48,7 @@ public:
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_finish_edit (bool accept);
virtual void do_cancel_edit ();
virtual bool selection_applies (const lay::ObjectInstPath &sel) const;
virtual void function (const std::string &name, const std::string &value);

View File

@ -900,9 +900,9 @@ EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector <tl::Var
}
// ------------------------------------------------------------------
// Box Toolbox widget
// Box toolbox widget
BoxToolkitWidget::BoxToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
BoxToolboxWidget::BoxToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
: lay::EditorOptionsPageWidget (view, dispatcher)
{
mp_layout = new QHBoxLayout (this);
@ -923,25 +923,25 @@ BoxToolkitWidget::BoxToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *
set_transparent (true);
}
BoxToolkitWidget::~BoxToolkitWidget ()
BoxToolboxWidget::~BoxToolboxWidget ()
{
// .. nothing yet ..
}
std::string
BoxToolkitWidget::title () const
BoxToolboxWidget::title () const
{
return "Box Options";
}
void
BoxToolkitWidget::deactivated ()
BoxToolboxWidget::deactivated ()
{
hide ();
}
void
BoxToolkitWidget::commit (lay::Dispatcher *dispatcher)
BoxToolboxWidget::commit (lay::Dispatcher *dispatcher)
{
try {
@ -957,7 +957,7 @@ BoxToolkitWidget::commit (lay::Dispatcher *dispatcher)
}
void
BoxToolkitWidget::configure (const std::string &name, const std::string &value)
BoxToolboxWidget::configure (const std::string &name, const std::string &value)
{
if (name == BoxService::configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) {
@ -975,6 +975,87 @@ BoxToolkitWidget::configure (const std::string &name, const std::string &value)
}
}
// ------------------------------------------------------------------
// Connections toolbox widget (for paths and polygons)
ConnectionToolboxWidget::ConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
: lay::EditorOptionsPageWidget (view, dispatcher), m_in_commit (false)
{
mp_layout = new QHBoxLayout (this);
mp_x_le = new lay::DecoratedLineEdit (this);
mp_x_le->set_label ("dx:");
mp_layout->addWidget (mp_x_le);
mp_y_le = new lay::DecoratedLineEdit (this);
mp_y_le->set_label ("dy:");
mp_layout->addWidget (mp_y_le);
mp_layout->addStretch (1);
hide ();
set_toolbox_widget (true);
set_transparent (true);
}
ConnectionToolboxWidget::~ConnectionToolboxWidget ()
{
// .. nothing yet ..
}
std::string
ConnectionToolboxWidget::title () const
{
return "Connection Options";
}
void
ConnectionToolboxWidget::deactivated ()
{
hide ();
}
void
ConnectionToolboxWidget::commit (lay::Dispatcher *dispatcher)
{
m_in_commit = true;
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 (ShapeEditService::connection_function_name (), db::DVector (dx, dy).to_string ());
} catch (...) {
}
m_in_commit = false;
}
void
ConnectionToolboxWidget::configure (const std::string &name, const std::string &value)
{
if (name == ShapeEditService::connection_configure_name () &&
((! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) || m_in_commit)) {
try {
db::DVector mv;
tl::from_string (value, mv);
mp_x_le->setText (tl::to_qstring (tl::micron_to_string (mv.x ())));
mp_y_le->setText (tl::to_qstring (tl::micron_to_string (mv.y ())));
} catch (...) {
}
}
}
// ------------------------------------------------------------------
// Registrations
@ -987,7 +1068,9 @@ static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_factory_insts (n
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_factory_insts_pcell (new lay::EditorOptionsPageFactory<edt::EditorOptionsInst> ("edt::Service(CellInstances)"), 0);
// toolkit widgets
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_box_tookit_widget_factory (new lay::EditorOptionsPageFactory<BoxToolkitWidget> ("edt::Service(Boxes)"), 0);
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_box_tookit_widget_factory (new lay::EditorOptionsPageFactory<BoxToolboxWidget> ("edt::Service(Boxes)"), 0);
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_connection_tookit_widget_factory_paths (new lay::EditorOptionsPageFactory<ConnectionToolboxWidget> ("edt::Service(Paths)"), 0);
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_connection_tookit_widget_factory_polygons (new lay::EditorOptionsPageFactory<ConnectionToolboxWidget> ("edt::Service(Polygons)"), 0);
}

View File

@ -198,14 +198,14 @@ private:
/**
* @brief The toolbox widget for boxes
*/
class BoxToolkitWidget
class BoxToolboxWidget
: public lay::EditorOptionsPageWidget
{
Q_OBJECT
public:
BoxToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~BoxToolkitWidget ();
BoxToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~BoxToolboxWidget ();
virtual std::string title () const;
virtual int order () const { return 0; }
@ -218,6 +218,30 @@ private:
lay::DecoratedLineEdit *mp_x_le, *mp_y_le;
};
/**
* @brief The toolbox widget for connections (path, polygon edges)
*/
class ConnectionToolboxWidget
: public lay::EditorOptionsPageWidget
{
Q_OBJECT
public:
ConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~ConnectionToolboxWidget ();
virtual std::string title () const;
virtual int order () const { return 0; }
virtual void configure (const std::string &name, const std::string &value);
virtual void commit (lay::Dispatcher *root);
virtual void deactivated ();
private:
QHBoxLayout *mp_layout;
lay::DecoratedLineEdit *mp_x_le, *mp_y_le;
bool m_in_commit;
};
}
#endif

View File

@ -417,7 +417,7 @@ InstService::do_mouse_click (const db::DPoint &p)
}
void
InstService::do_finish_edit ()
InstService::do_finish_edit (bool /*accept*/)
{
try {

View File

@ -53,7 +53,7 @@ public:
virtual void do_mouse_move (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans);
virtual void do_finish_edit ();
virtual void do_finish_edit (bool accept);
virtual void do_cancel_edit ();
virtual bool do_activated ();
#if defined(HAVE_QT)

View File

@ -26,6 +26,7 @@
#include "edtPropertiesPages.h"
#include "layLayoutViewBase.h"
#include "layEditorOptionsPage.h"
#include "layFinder.h"
namespace edt
@ -160,13 +161,15 @@ PathService::do_delete ()
}
void
PathService::do_finish_edit ()
PathService::do_finish_edit (bool accept)
{
// one point is reserved for the "current one"
if (m_points.size () < 3) {
// one point is reserved for the "current one" if accept is false
if (! accept && ! m_points.empty ()) {
m_points.pop_back ();
}
if (m_points.size () < 2) {
throw tl::Exception (tl::to_string (tr ("A path must have at least 2 points")));
}
m_points.pop_back ();
deliver_shape (get_path ());
@ -175,6 +178,32 @@ PathService::do_finish_edit ()
close_editor_hooks (true);
}
void
PathService::function (const std::string &name, const std::string &value)
{
if (name == ShapeEditService::connection_function_name ()) {
try {
db::DVector dim;
tl::from_string (value, dim);
if (m_points.size () >= 2) {
m_last = m_points.back () = m_points.end () [-2] + dim;
m_points.push_back (m_last);
update_marker ();
update_via ();
}
} catch (...) {
}
}
}
void
PathService::update_marker ()
{
@ -185,12 +214,17 @@ PathService::update_marker ()
marker->set (path, db::VCplxTrans (1.0 / layout ().dbu ()) * trans ().inverted ());
if (m_points.size () >= 2) {
db::DVector dim = m_points.back () - m_points.end () [-2];
view ()->message (std::string ("lx: ") +
tl::micron_to_string (m_points.back ().x () - m_points.end () [-2].x ()) +
tl::micron_to_string (dim.x ()) +
std::string (" ly: ") +
tl::micron_to_string (m_points.back ().y () - m_points.end () [-2].y ()) +
tl::micron_to_string (dim.y ()) +
std::string (" l: ") +
tl::micron_to_string (m_points.back ().distance (m_points.end () [-2])));
tl::micron_to_string (dim.length ()));
auto tb = toolbox_widget ();
if (tb) {
tb->configure (ShapeEditService::connection_configure_name (), dim.to_string ());
}
}
}

View File

@ -47,11 +47,12 @@ public:
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual void do_delete ();
virtual void do_finish_edit ();
virtual void do_finish_edit (bool accept);
virtual void do_cancel_edit ();
virtual bool do_activated ();
virtual void via (int dir);
virtual bool selection_applies (const lay::ObjectInstPath &sel) const;
virtual void function (const std::string &name, const std::string &value);
protected:
bool configure (const std::string &name, const std::string &value);

View File

@ -124,7 +124,7 @@ PointService::do_mouse_click (const db::DPoint &p)
}
void
PointService::do_finish_edit ()
PointService::do_finish_edit (bool /*accept*/)
{
deliver_shape (get_point ());
commit_recent ();

View File

@ -45,7 +45,7 @@ public:
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_finish_edit (bool);
virtual void do_cancel_edit ();
virtual bool selection_applies (const lay::ObjectInstPath &sel) const;

View File

@ -24,6 +24,7 @@
#include "edtPolygonService.h"
#include "layLayoutViewBase.h"
#include "layEditorOptionsPage.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
@ -135,13 +136,44 @@ PolygonService::do_mouse_click (const db::DPoint &p)
}
void
PolygonService::do_finish_edit ()
PolygonService::do_finish_edit (bool accept)
{
if (accept) {
// add a dummy point in this case for the current one
m_last = m_points.back ();
m_points.push_back (db::DPoint ());
}
deliver_shape (get_polygon (false));
commit_recent ();
close_editor_hooks (true);
}
void
PolygonService::function (const std::string &name, const std::string &value)
{
if (name == ShapeEditService::connection_function_name ()) {
try {
db::DVector dim;
tl::from_string (value, dim);
if (m_points.size () >= 2) {
m_last = m_points.back () = m_points.end () [-2] + dim;
m_points.push_back (m_last);
update_marker ();
}
} catch (...) {
}
}
}
db::Polygon
PolygonService::get_polygon (bool editing) const
{
@ -363,12 +395,17 @@ PolygonService::update_marker ()
}
if (m_points.size () >= 2) {
db::DVector dim = m_points.back () - m_points [m_points.size () - 2];
view ()->message (std::string ("lx: ") +
tl::micron_to_string (m_points.back ().x () - m_points.end () [-2].x ()) +
tl::micron_to_string (dim.x ()) +
std::string (" ly: ") +
tl::micron_to_string (m_points.back ().y () - m_points.end () [-2].y ()) +
tl::micron_to_string (dim.y ()) +
std::string (" l: ") +
tl::micron_to_string (m_points.back ().distance (m_points.end () [-2])));
tl::micron_to_string (dim.length ()));
auto tb = toolbox_widget ();
if (tb) {
tb->configure (ShapeEditService::connection_configure_name (), dim.to_string ());
}
}
// call hooks with new shape

View File

@ -46,9 +46,10 @@ public:
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_finish_edit (bool);
virtual void do_cancel_edit ();
virtual bool selection_applies (const lay::ObjectInstPath &sel) const;
virtual void function (const std::string &name, const std::string &value);
private:
std::vector <db::DPoint> m_points;

View File

@ -956,7 +956,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio
} else {
if (do_mouse_click (p)) {
finish_editing ();
finish_editing (false);
}
}
@ -993,7 +993,7 @@ Service::mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bo
if (m_editing && prio && (buttons & lay::LeftButton) != 0) {
m_alt_ac = lay::ac_from_buttons (buttons);
finish_editing ();
finish_editing (false);
return true;
} else {
return false;
@ -1022,15 +1022,19 @@ Service::key_event (unsigned int key, unsigned int buttons)
if (view ()->is_editable () && m_editing && buttons == 0 && key == lay::KeyBackspace) {
do_delete ();
return true;
} else if (view ()->is_editable () && m_editing && buttons == 0 && (key == lay::KeyEnter || key == lay::KeyReturn)) {
m_alt_ac = lay::AC_Global;
finish_editing (true);
return true;
} else {
return false;
}
}
void
Service::finish_editing ()
Service::finish_editing (bool accept)
{
do_finish_edit ();
do_finish_edit (accept);
m_editing = false;
show_toolbox (false);

View File

@ -518,9 +518,12 @@ protected:
/**
* @brief Reimplemented by the specific implementation of the shape editors
*
* This method is called when the object is finished
* This method is called when the object is finished.
*
* 'accept' is set to true if triggered by the Enter/Return key, false if triggered by a mouse click.
* In the latter case, first the mouse click is delivered and then "do_finish_edit" is called.
*/
virtual void do_finish_edit () { }
virtual void do_finish_edit (bool /*accept*/) { }
/**
* @brief Reimplemented by the specific implementation of the shape editors
@ -669,8 +672,9 @@ protected:
* @brief Finishes the edit operation
*
* Calls do_finish_edit() and terminates the editing operation.
* See "do_finish_edit" for an explanation of the "accept" parameter.
*/
void finish_editing ();
void finish_editing (bool accept);
/**
* @brief Gets the toolbox widget or 0 if none is registered

View File

@ -39,6 +39,9 @@ namespace edt
// -----------------------------------------------------------------------------
// ShapeEditService implementation
const char *ShapeEditService::connection_configure_name () { return "connection-toolkit-widget-value"; }
const char *ShapeEditService::connection_function_name () { return "connection-toolkit-widget-commit"; }
ShapeEditService::ShapeEditService (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIterator::flags_type shape_types)
: edt::Service (manager, view, shape_types),
m_layer (0), m_cv_index (0), mp_cell (0), mp_layout (0), m_combine_mode (CM_Add), m_update_edit_layer_enabled (true)

View File

@ -37,6 +37,9 @@ class ShapeEditService
: public edt::Service
{
public:
static const char *connection_configure_name ();
static const char *connection_function_name ();
ShapeEditService (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIterator::flags_type shape_types);
protected:

View File

@ -155,7 +155,7 @@ TextService::get_text () const
}
void
TextService::do_finish_edit ()
TextService::do_finish_edit (bool /*accept*/)
{
{
db::Transaction transaction (manager (), tl::to_string (tr ("Create text")));

View File

@ -47,7 +47,7 @@ public:
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_finish_edit (bool);
virtual void do_cancel_edit ();
virtual bool do_activated ();
virtual bool selection_applies (const lay::ObjectInstPath &sel) const;

View File

@ -82,9 +82,11 @@ 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 ();
@ -93,11 +95,15 @@ BEGIN_PROTECTED
// to the view
commit (dispatcher ());
}
view ()->set_focus ();
event->accept ();
} else {
QWidget::keyPressEvent (event);
}
END_PROTECTED
}
@ -105,6 +111,7 @@ bool
EditorOptionsPageWidget::event (QEvent *event)
{
if (event->type () == QEvent::ShortcutOverride) {
QKeyEvent *ke = dynamic_cast<QKeyEvent *> (event);
if (ke->key () == Qt::Key_Escape ||
ke->key () == Qt::Key_Tab ||
@ -115,7 +122,9 @@ EditorOptionsPageWidget::event (QEvent *event)
// it in keyPressEvent
ke->accept ();
}
}
return QWidget::event (event);
}