Enabling toolbox widgets for box service. Plus add-on: pressing Shift while dragging a box makes it centered.

This commit is contained in:
Matthias Koefferlein 2026-01-16 01:03:36 +01:00
parent 900ac4bc0f
commit 6b5dbb1442
6 changed files with 246 additions and 17 deletions

View File

@ -24,6 +24,7 @@
#include "edtBoxService.h"
#include "layLayoutViewBase.h"
#include "layEditorOptionsPage.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
@ -35,8 +36,11 @@ namespace edt
// -----------------------------------------------------------------------------
// BoxService implementation
const char *BoxService::configure_name () { return "box-toolkit-widget-value"; }
const char *BoxService::function_name () { return "box-toolkit-widget-commit"; }
BoxService::BoxService (db::Manager *manager, lay::LayoutViewBase *view)
: ShapeEditService (manager, view, db::ShapeIterator::Boxes)
: ShapeEditService (manager, view, db::ShapeIterator::Boxes), m_centered (false)
{
// .. nothing yet ..
}
@ -65,10 +69,46 @@ BoxService::do_begin_edit (const db::DPoint &p)
update_marker ();
}
void
BoxService::function (const std::string &name, const std::string &value)
{
if (name == function_name ()) {
try {
db::DVector dim;
tl::from_string (value, dim);
if (! m_centered) {
// Adjust the direction so it reflects the current
if (m_p2.x () < m_p1.x ()) {
dim.set_x (-fabs (dim.x ()));
}
if (m_p2.y () < m_p1.y ()) {
dim.set_y (-fabs (dim.y ()));
}
} else {
dim = db::DVector (fabs (dim.x ()) * 0.5, fabs (dim.y ()) * 0.5);
}
m_p2 = m_p1 + dim;
finish_editing ();
} catch (...) {
}
}
}
db::Box
BoxService::get_box () const
{
return db::Box (trans () * m_p1, trans () * m_p2);
if (m_centered) {
db::DVector d = m_p2 - m_p1;
return db::Box (trans () * (m_p1 - d), trans () * (m_p1 + d));
} else {
return db::Box (trans () * m_p1, trans () * m_p2);
}
}
void
@ -79,10 +119,18 @@ BoxService::update_marker ()
marker->set (get_box (), db::VCplxTrans (1.0 / layout ().dbu ()) * trans ().inverted ());
db::DVector d = m_p2 - m_p1;
db::DVector dim = db::DVector (fabs (d.x ()), fabs (d.y ())) * (m_centered ? 2.0 : 1.0);
view ()->message (std::string ("lx: ") +
tl::micron_to_string (m_p2.x () - m_p1.x ()) +
tl::micron_to_string (dim.x ()) +
std::string (" ly: ") +
tl::micron_to_string (m_p2.y () - m_p1.y ()));
tl::micron_to_string (dim.y ()));
auto p = toolbox_widget ();
if (p) {
p->configure (configure_name (), dim.to_string ());
}
}
@ -109,12 +157,13 @@ void
BoxService::do_mouse_move (const db::DPoint &p)
{
// snap to square if Ctrl button is pressed
bool snap_square = alt_ac () == lay::AC_Diagonal;
bool snap_square = (mouse_buttons () & lay::ControlButton) != 0;
bool centered = (mouse_buttons () & lay::ShiftButton) != 0;
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) {
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject && ! m_centered) {
clear_mouse_cursors ();
@ -152,6 +201,7 @@ BoxService::do_mouse_move (const db::DPoint &p)
set_cursor (lay::Cursor::cross);
m_p2 = ps;
m_centered = centered;
update_marker ();
}

View File

@ -36,6 +36,9 @@ class BoxService
: public ShapeEditService
{
public:
static const char *configure_name ();
static const char *function_name ();
BoxService (db::Manager *manager, lay::LayoutViewBase *view);
#if defined(HAVE_QT)
@ -48,9 +51,11 @@ public:
virtual void do_finish_edit ();
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:
db::DPoint m_p1, m_p2;
bool m_centered;
void update_marker ();
db::Box get_box () const;

View File

@ -29,6 +29,7 @@
#include "edtPCellParametersPage.h"
#include "edtConfig.h"
#include "edtService.h"
#include "edtBoxService.h"
#include "edtEditorOptionsPages.h"
#include "edtPropertiesPageUtils.h"
#include "tlExceptions.h"
@ -898,6 +899,82 @@ EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector <tl::Var
}
}
// ------------------------------------------------------------------
// Box Toolbox widget
BoxToolkitWidget::BoxToolkitWidget (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 ("w:");
mp_layout->addWidget (mp_x_le);
mp_y_le = new lay::DecoratedLineEdit (this);
mp_y_le->set_label ("h:");
mp_layout->addWidget (mp_y_le);
mp_layout->addStretch (1);
hide ();
set_toolbox_widget (true);
set_transparent (true);
}
BoxToolkitWidget::~BoxToolkitWidget ()
{
// .. nothing yet ..
}
std::string
BoxToolkitWidget::title () const
{
return "Box Options";
}
void
BoxToolkitWidget::deactivated ()
{
hide ();
}
void
BoxToolkitWidget::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 (BoxService::function_name (), db::DVector (dx, dy).to_string ());
} catch (...) {
}
}
void
BoxToolkitWidget::configure (const std::string &name, const std::string &value)
{
if (name == BoxService::configure_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 (...) {
}
}
}
// ------------------------------------------------------------------
// Registrations
@ -909,6 +986,9 @@ static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_factory_paths (n
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_factory_insts (new lay::EditorOptionsPageFactory<edt::EditorOptionsInstPCellParam> ("edt::Service(CellInstances)"), 0);
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> (), 0);
}
#endif

View File

@ -36,6 +36,7 @@
class QTabWidget;
class QLabel;
class QHBoxLayout;
namespace Ui
{
@ -55,6 +56,7 @@ namespace lay
class Dispatcher;
class LayoutViewBase;
class Plugin;
class DecoratedLineEdit;
}
namespace edt
@ -193,6 +195,29 @@ private:
virtual void technology_changed (const std::string &);
};
/**
* @brief The toolbox widget for boxes
*/
class BoxToolkitWidget
: public lay::EditorOptionsPageWidget
{
Q_OBJECT
public:
BoxToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~BoxToolkitWidget ();
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;
};
}
#endif

View File

@ -46,7 +46,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter
db::Object (manager),
mp_view (view),
mp_transient_marker (0),
m_mouse_in_view (false), m_editing (false), m_immediate (false),
m_mouse_buttons (0), m_mouse_in_view (false), m_editing (false), m_immediate (false),
m_selection_maybe_invalid (false),
m_cell_inst_service (false),
m_flags (flags),
@ -70,7 +70,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
db::Object (manager),
mp_view (view),
mp_transient_marker (0),
m_mouse_in_view (false), m_editing (false), m_immediate (false),
m_mouse_buttons (0), m_mouse_in_view (false), m_editing (false), m_immediate (false),
m_selection_maybe_invalid (false),
m_cell_inst_service (true),
m_flags (db::ShapeIterator::Nothing),
@ -902,6 +902,7 @@ bool
Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
m_mouse_pos = p;
m_mouse_buttons = buttons;
if (view ()->is_editable () && prio) {
@ -938,6 +939,9 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
bool
Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
m_mouse_pos = p;
m_mouse_buttons = buttons;
if (view ()->is_editable () && prio) {
if ((buttons & lay::LeftButton) != 0) {
@ -952,9 +956,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio
} else {
if (do_mouse_click (p)) {
m_editing = false;
set_edit_marker (0);
do_finish_edit ();
finish_editing ();
}
}
@ -984,14 +986,14 @@ Service::enter_event (bool /*prio*/)
}
bool
Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio)
Service::mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
m_mouse_pos = p;
m_mouse_buttons = buttons;
if (m_editing && prio && (buttons & lay::LeftButton) != 0) {
m_alt_ac = lay::ac_from_buttons (buttons);
do_finish_edit ();
m_editing = false;
set_edit_marker (0);
m_alt_ac = lay::AC_Global;
finish_editing ();
return true;
} else {
return false;
@ -1001,6 +1003,9 @@ Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int button
bool
Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
m_mouse_pos = p;
m_mouse_buttons = buttons;
if (view ()->is_editable () && prio && (buttons & lay::RightButton) != 0 && m_editing) {
m_alt_ac = lay::ac_from_buttons (buttons);
do_mouse_transform (p, db::DFTrans (db::DFTrans::r90));
@ -1022,6 +1027,17 @@ Service::key_event (unsigned int key, unsigned int buttons)
}
}
void
Service::finish_editing ()
{
do_finish_edit ();
m_editing = false;
show_toolbox (false);
set_edit_marker (0);
m_alt_ac = lay::AC_Global;
}
void
Service::activated ()
{
@ -1749,6 +1765,7 @@ void
Service::begin_edit (const db::DPoint &p)
{
do_begin_edit (p);
show_toolbox (true);
m_editing = true;
}
@ -2055,6 +2072,33 @@ Service::commit_recent ()
}
}
void
Service::show_toolbox (bool visible)
{
auto p = toolbox_widget ();
if (p) {
p->set_visible (visible);
}
}
lay::EditorOptionsPage *
Service::toolbox_widget ()
{
lay::EditorOptionsPageCollection *eo_pages = view ()->editor_options_pages ();
if (! eo_pages) {
return 0;
}
auto pages = eo_pages->editor_options_pages (plugin_declaration ());
for (auto op = pages.begin (); op != pages.end (); ++op) {
if ((*op)->is_toolbox_widget ()) {
return *op;
}
}
return 0;
}
// -------------------------------------------------------------
// Implementation of EditableSelectionIterator

View File

@ -650,11 +650,33 @@ protected:
return m_mouse_pos;
}
int mouse_buttons () const
{
return m_mouse_buttons;
}
/**
* @brief Commits the current configuration to the recent attributes list
*/
void commit_recent ();
/**
* @brief Shows the toolbox widget in case one is registered for this plugin
*/
void show_toolbox (bool visible);
/**
* @brief Finishes the edit operation
*
* Calls do_finish_edit() and terminates the editing operation.
*/
void finish_editing ();
/**
* @brief Gets the toolbox widget or 0 if none is registered
*/
lay::EditorOptionsPage *toolbox_widget ();
/**
* @brief Point snapping with detailed return value
*/
@ -685,9 +707,12 @@ private:
// The marker representing the object to be edited
std::vector<lay::ViewObject *> m_edit_markers;
// The last mouse position
// The last mouse position of the current mouse move/press/click event
db::DPoint m_mouse_pos;
// The buttons flag of the current mouse move/press/click event
int m_mouse_buttons;
// A flag indicating whether the mouse is inside the view
bool m_mouse_in_view;