Toolkit widget solution enhanced

- Bring text and path options to front when changing mode
- Path width and text string can be configure in toolkit widget
- Enter key finishes move and partial move
This commit is contained in:
Matthias Koefferlein 2026-02-02 22:05:45 +01:00
parent a6eb598abd
commit 91ab72e8e5
10 changed files with 447 additions and 17 deletions

View File

@ -976,7 +976,7 @@ BoxToolboxWidget::configure (const std::string &name, const std::string &value)
}
// ------------------------------------------------------------------
// Connections toolbox widget (for paths and polygons)
// Connections toolbox widget (for polygons)
ConnectionToolboxWidget::ConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
: lay::EditorOptionsPageWidget (view, dispatcher), m_in_commit (false)
@ -1056,6 +1056,174 @@ ConnectionToolboxWidget::configure (const std::string &name, const std::string &
}
}
// ------------------------------------------------------------------
// Connections toolbox widget (for paths)
PathConnectionToolboxWidget::PathConnectionToolboxWidget (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_width = new lay::DecoratedLineEdit (this);
mp_width->set_label ("w:");
mp_layout->addWidget (mp_width);
mp_layout->addStretch (1);
hide ();
set_toolbox_widget (true);
set_transparent (true);
}
PathConnectionToolboxWidget::~PathConnectionToolboxWidget ()
{
// .. nothing yet ..
}
std::string
PathConnectionToolboxWidget::title () const
{
return "Path Connection Options";
}
void
PathConnectionToolboxWidget::deactivated ()
{
hide ();
}
void
PathConnectionToolboxWidget::commit (lay::Dispatcher *dispatcher)
{
m_in_commit = true;
try {
if (mp_x_le->hasFocus () || mp_y_le->hasFocus ()) {
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 ());
} else if (mp_width->hasFocus ()) {
double w = 0.0;
tl::from_string (tl::to_string (mp_width->text ()), w);
// NOTE: going the way through "configure" makes the width part of the recent path configuration
dispatcher->config_set (cfg_edit_path_width, tl::to_string (w));
}
} catch (...) {
}
m_in_commit = false;
}
void
PathConnectionToolboxWidget::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 (...) {
}
} else if (name == cfg_edit_path_width &&
(! mp_width->hasFocus () || m_in_commit)) {
try {
double w = 0.0;
tl::from_string (value, w);
mp_width->setText (tl::to_qstring (tl::micron_to_string (w)));
} catch (...) {
}
}
}
// ------------------------------------------------------------------
// Connections toolbox widget (for texts)
TextToolboxWidget::TextToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
: lay::EditorOptionsPageWidget (view, dispatcher), m_in_commit (false)
{
mp_layout = new QHBoxLayout (this);
mp_text = new lay::DecoratedLineEdit (this);
mp_text->set_label ("l:");
mp_layout->addWidget (mp_text);
mp_layout->addStretch (1);
hide ();
set_toolbox_widget (true);
set_transparent (true);
}
TextToolboxWidget::~TextToolboxWidget ()
{
// .. nothing yet ..
}
std::string
TextToolboxWidget::title () const
{
return "Text Options";
}
void
TextToolboxWidget::deactivated ()
{
hide ();
}
void
TextToolboxWidget::commit (lay::Dispatcher *dispatcher)
{
m_in_commit = true;
try {
dispatcher->config_set (cfg_edit_text_string, tl::to_string (mp_text->text ()));
} catch (...) {
}
m_in_commit = false;
}
void
TextToolboxWidget::configure (const std::string &name, const std::string &value)
{
if (name == cfg_edit_text_string && (! mp_text->hasFocus () || m_in_commit)) {
mp_text->setText (tl::to_qstring (value));
}
}
// ------------------------------------------------------------------
// Registrations
@ -1069,8 +1237,9 @@ static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_factory_insts_pc
// toolkit widgets
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_path_connection_tookit_widget_factory (new lay::EditorOptionsPageFactory<PathConnectionToolboxWidget> ("edt::Service(Paths)"), 0);
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_connection_tookit_widget_factory_polygons (new lay::EditorOptionsPageFactory<ConnectionToolboxWidget> ("edt::Service(Polygons)"), 0);
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_text_tookit_widget_factory (new lay::EditorOptionsPageFactory<TextToolboxWidget> ("edt::Service(Texts)"), 0);
}

View File

@ -76,7 +76,7 @@ public:
~EditorOptionsGeneric ();
virtual std::string title () const;
virtual int order () const { return 0; }
virtual int order () const { return -10; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
@ -99,7 +99,7 @@ public:
~EditorOptionsText ();
virtual std::string title () const;
virtual int order () const { return 10; }
virtual int order () const { return 0; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
@ -120,7 +120,7 @@ public:
~EditorOptionsPath ();
virtual std::string title () const;
virtual int order () const { return 30; }
virtual int order () const { return 0; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
@ -144,7 +144,7 @@ public:
~EditorOptionsInst ();
virtual std::string title () const;
virtual int order () const { return 20; }
virtual int order () const { return 0; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
@ -176,7 +176,7 @@ public:
~EditorOptionsInstPCellParam ();
virtual std::string title () const;
virtual int order () const { return 21; }
virtual int order () const { return 1; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
@ -207,7 +207,7 @@ public:
~BoxToolboxWidget ();
virtual std::string title () const;
virtual int order () const { return 0; }
virtual int order () const { return 200; }
virtual void configure (const std::string &name, const std::string &value);
virtual void commit (lay::Dispatcher *root);
virtual void deactivated ();
@ -218,7 +218,7 @@ private:
};
/**
* @brief The toolbox widget for connections (path, polygon edges)
* @brief The toolbox widget for connections (polygon edges)
*/
class ConnectionToolboxWidget
: public lay::EditorOptionsPageWidget
@ -230,7 +230,7 @@ public:
~ConnectionToolboxWidget ();
virtual std::string title () const;
virtual int order () const { return 0; }
virtual int order () const { return 200; }
virtual void configure (const std::string &name, const std::string &value);
virtual void commit (lay::Dispatcher *root);
virtual void deactivated ();
@ -241,6 +241,54 @@ private:
bool m_in_commit;
};
/**
* @brief The toolbox widget for connections (path segments)
*/
class PathConnectionToolboxWidget
: public lay::EditorOptionsPageWidget
{
Q_OBJECT
public:
PathConnectionToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~PathConnectionToolboxWidget ();
virtual std::string title () const;
virtual int order () const { return 200; }
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, *mp_width;
bool m_in_commit;
};
/**
* @brief The toolbox widget for texts
*/
class TextToolboxWidget
: public lay::EditorOptionsPageWidget
{
Q_OBJECT
public:
TextToolboxWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~TextToolboxWidget ();
virtual std::string title () const;
virtual int order () const { return 200; }
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_text;
bool m_in_commit;
};
}
#endif

View File

@ -2283,6 +2283,17 @@ PartialService::mouse_release_event (const db::DPoint &p, unsigned int buttons,
return false;
}
bool
PartialService::key_event (unsigned int key, unsigned int buttons)
{
if (m_moving && buttons == 0 && (key == lay::KeyEnter || key == lay::KeyReturn)) {
mp_view->move_service ()->end_move ();
return true;
} else {
return false;
}
}
bool
PartialService::begin_move (MoveMode mode, const db::DPoint &p, lay::angle_constraint_type ac)
{

View File

@ -234,6 +234,11 @@ public:
*/
virtual bool mouse_release_event (const db::DPoint &p, unsigned int buttons, bool prio);
/**
* @brief Key handling event
*/
virtual bool key_event (unsigned int key, unsigned int buttons);
/**
* @brief Transforms the selection
*

View File

@ -729,6 +729,11 @@ PathService::pop_segment ()
bool
PathService::configure (const std::string &name, const std::string &value)
{
auto tb = toolbox_widget ();
if (tb) {
tb->configure (name, value);
}
if (name == cfg_edit_path_width) {
tl::from_string (value, m_width);
m_needs_update = true;

View File

@ -25,6 +25,7 @@
#include "layLayoutViewBase.h"
#include "layConverters.h"
#include "layEditorOptionsPage.h"
#if defined(HAVE_QT)
# include "edtPropertiesPages.h"
@ -199,6 +200,11 @@ TextService::selection_applies (const lay::ObjectInstPath &sel) const
bool
TextService::configure (const std::string &name, const std::string &value)
{
auto tb = toolbox_widget ();
if (tb) {
tb->configure (name, value);
}
if (name == cfg_edit_text_size) {
double size (0);
tl::from_string (value, size);

View File

@ -67,43 +67,180 @@ public:
};
/**
* @brief The base class for a object properties page
* @brief The base class for a editor options page
*
* The object properties page is shown in the editor options panel
* for the active plugin.
*
* Pages can be toolbox widgets, i.e. they are shown in the drawing area
* at the top of the canvas, instead of being shown in the editor options
* panel.
*/
class LAYBASIC_PUBLIC EditorOptionsPage
: public tl::Object
{
public:
/**
* @brief Constructor
*/
EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
/**
* @brief Default constructor
*/
EditorOptionsPage ();
/**
* @brief Destructor
*/
virtual ~EditorOptionsPage ();
/**
* @brief The title of the page
* This title is used for the tab title the page appears
*/
virtual std::string title () const = 0;
/**
* @brief The order in which the pages appear
* This index specifies the position of the page. The page with the
* lower index appears left.
* The page with order 0 is the default page, picked when the plugin
* becomes active.
*/
virtual int order () const = 0;
/**
* @brief The page name
* Giving a page name allows looking up a page by name.
* The page name is optional and if not specified, a zero pointer
* should be returned.
*/
virtual const char *name () const { return 0; }
/**
* @brief A callback to apply all values
* The page is expected to issue "config_set" calls to the dispatcher
* to deliver the settings.
* This callback is not used for toolbox widgets.
*/
virtual void apply (lay::Dispatcher * /*root*/) { }
virtual void cancel () { }
virtual void commit (lay::Dispatcher * /*root*/) { }
/**
* @brief A callback to setup the page
* This callback is expected to set up the page values from
* the configuration, stored inside the dispatcher.
* This callback is not used for toolbox widgets.
*/
virtual void setup (lay::Dispatcher * /*root*/) { }
/**
* @brief A callback to cancel the page edits
* This callback is used for toolbox widgets if the user presses
* "Escape".
*/
virtual void cancel () { }
/**
* @brief A callback to commit the values
* This callback is used for toolbox widgets if the user presses
* "Enter". It can either commit values to the dispatcher
* through "config_set", or perform other functions.
*/
virtual void commit (lay::Dispatcher * /*root*/) { }
/**
* @brief Configure the page
* This interface can be used by plugin implementation to transfer
* data from the plugin to a toolbox widget. This method is not
* used by the system directly.
*/
virtual void configure (const std::string & /*name*/, const std::string & /*value*/) { }
/**
* @brief Called by the system to commit the current settings into some "recently used" list
*/
virtual void commit_recent (lay::Dispatcher * /*root*/) { }
/**
* @brief Called by the system to restore recent settings for a given layer
*/
virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { }
/**
* @brief Sets the focus to the page
* This function is called by the system to establish the focus on this page.
*/
virtual void set_focus () { }
/**
* @brief Returns the widget for the page
* The page itself is not a Qt object. To fetch the corresponding widget, use this method.
*/
virtual EditorOptionsPageWidget *widget () { return 0; }
/**
* @brief Gets a value indicating whether the page is visible
*/
virtual bool is_visible () const { return false; }
/**
* @brief Changes the visibility of the page
*/
virtual void set_visible (bool /*visible*/) { }
/**
* @brief Returns a flag indicating whether the page is a focus page
* Focus pages are pages that are activated when the user presses the Tab
* key in the canvas. Toolbox widgets receive the focus and modal pages
* are shown modally.
*/
bool is_focus_page () const { return m_focus_page; }
/**
* @brief Sets a flag indicating whether the page is a focus page
*/
void set_focus_page (bool f) { m_focus_page = f; }
/**
* @brief Returns a flag indicating whether the page is a modal page
* Modal pages are shown in a modal dialog when they receive the focus.
* Otherwise they remain invisible.
*/
bool is_modal_page () const { return m_modal_page; }
/**
* @brief Sets a flag indicating whether the page is a modal page
*/
void set_modal_page (bool f) { m_modal_page = f; }
/**
* @brief Returns a flag indicating whether the page is a toolbox widget
*/
bool is_toolbox_widget () const { return m_toolbox_widget; }
/**
* @brief Sets a flag indicating whether the page is a toolbox widget
*/
void set_toolbox_widget (bool f) { m_toolbox_widget = f; }
/**
* @brief Gets a value indicating whether the page is active
* A page is active when the corresponding plugin is active
*/
bool active () const { return m_active; }
/**
* @brief Activates a page
* This function is called when the system activates a page because the
* corresponding plugin was activate.
*/
void activate (bool active);
/**
* @brief Sets the owner of the page
* This function is used by the system and must not be used otherwise.
*/
void set_owner (EditorOptionsPageCollection *owner);
/**
@ -112,35 +249,56 @@ public:
*/
int show ();
/**
* @brief Gets a value indicating whether the page is for that specific plugin (given by a declaration object)
*/
bool for_plugin_declaration (const lay::PluginDeclaration *pd)
{
return m_plugin_declarations.find (pd) != m_plugin_declarations.end ();
}
/**
* @brief Sets the plugin the page is associated with
* This function is used by the system and must not be used otherwise.
*/
void set_plugin_declaration (const lay::PluginDeclaration *pd)
{
m_plugin_declarations.clear ();
m_plugin_declarations.insert (pd);
}
/**
* @brief Sets the plugins the page is associated with
* This function is used by the system and must not be used otherwise.
*/
void set_plugin_declarations (const std::vector<const lay::PluginDeclaration *> &pd)
{
m_plugin_declarations.clear ();
m_plugin_declarations.insert (pd.begin (), pd.end ());
}
void init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
/**
* @brief Gets the dispatcher the page is connected to
*/
lay::Dispatcher *dispatcher () const
{
return mp_dispatcher;
}
/**
* @brief Gets the view the page is connected to
*/
lay::LayoutViewBase *view () const
{
return mp_view;
}
/**
* @brief Initializes the page
* This function is used by the system and must not be used otherwise.
*/
void init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
protected:
virtual void active_cellview_changed () { }
virtual void technology_changed (const std::string & /*tech*/) { }

View File

@ -117,6 +117,11 @@ MoveService::key_event (unsigned int key, unsigned int buttons)
return true;
}
if (buttons == 0 && (key == lay::KeyEnter || key == lay::KeyReturn)) {
end_move ();
return true;
}
double dx = 0.0, dy = 0.0;
if (int (key) == lay::KeyDown) {
dy = -1.0;
@ -369,6 +374,14 @@ MoveService::start_move (db::Transaction *transaction, bool transient_selection)
return handle_click (pstart, 0, drag_transient, trans_holder.release ());
}
void
MoveService::end_move ()
{
if (m_dragging) {
handle_click (m_mouse_pos, 0, false, 0);
}
}
bool
MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction)
{

View File

@ -45,6 +45,7 @@ public:
~MoveService ();
bool start_move (db::Transaction *transaction = 0, bool transient_selection = false);
void end_move ();
bool configure (const std::string &name, const std::string &value);
void function (const std::string &name, const std::string &value);

View File

@ -162,14 +162,28 @@ EditorOptionsPages::activate (const lay::Plugin *plugin)
{
m_update_enabled = false;
lay::EditorOptionsPage *page = 0;
for (auto op = m_pages.begin (); op != m_pages.end (); ++op) {
BEGIN_PROTECTED
op->activate (plugin && op->for_plugin_declaration (plugin->plugin_declaration ()));
bool is_active = plugin && op->for_plugin_declaration (plugin->plugin_declaration ());
// The zero order page is picked as the initial one
if (is_active && ! op->active () && op->order () == 0 && page == 0) {
page = op.operator-> ();
}
op->activate (is_active);
END_PROTECTED
}
m_update_enabled = true;
update (0);
update (page);
}
void