From 9a034973499b782f9cba6b530c6ab3e45bbd050d Mon Sep 17 00:00:00 2001 From: Heapnotizer Date: Thu, 30 Oct 2025 23:36:07 +0530 Subject: [PATCH] Move Alt+X to shortcuts Alt+X now recursively searches on toolbar for the first available input(Entry, Spinbutton, Combobox) widget and then focuses it. --- share/keys/inkscape.xml | 2 + src/ui/toolbar/toolbars.cpp | 6 ++ src/ui/toolbar/toolbars.h | 1 + src/ui/tools/arc-tool.cpp | 8 --- src/ui/tools/box3d-tool.cpp | 4 -- src/ui/tools/eraser-tool.cpp | 8 --- src/ui/tools/gradient-tool.cpp | 8 --- src/ui/tools/rect-tool.cpp | 7 --- src/ui/tools/select-tool.cpp | 8 --- src/ui/tools/shortcuts.cpp | 1 + src/ui/tools/spiral-tool.cpp | 8 --- src/ui/tools/spray-tool.cpp | 7 --- src/ui/tools/star-tool.cpp | 8 --- src/ui/tools/text-tool.cpp | 8 --- src/ui/tools/tool-base.cpp | 61 ++++++++++++++++++++- src/ui/tools/tool-base.h | 2 + src/ui/tools/tweak-tool.cpp | 7 --- src/ui/widget/combo-box-entry-tool-item.cpp | 5 ++ src/ui/widget/combo-box-entry-tool-item.h | 1 + src/ui/widget/desktop-widget.cpp | 5 ++ src/ui/widget/desktop-widget.h | 2 + 21 files changed, 85 insertions(+), 82 deletions(-) diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index ae997e4ea8..23b5b87679 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -58,6 +58,8 @@ override) the bindings in the main default.xml. + + diff --git a/src/ui/toolbar/toolbars.cpp b/src/ui/toolbar/toolbars.cpp index 215a24fe0e..cd2e62ec19 100644 --- a/src/ui/toolbar/toolbars.cpp +++ b/src/ui/toolbar/toolbars.cpp @@ -152,6 +152,12 @@ void Toolbars::setActiveUnit(Util::Unit const *unit) } } +Toolbar *Toolbars::get_current_toolbar() +{ + return _current_toolbar; +} + + } // namespace Inkscape::UI::Toolbar /* diff --git a/src/ui/toolbar/toolbars.h b/src/ui/toolbar/toolbars.h index a45d9c119c..0f1ded2514 100644 --- a/src/ui/toolbar/toolbars.h +++ b/src/ui/toolbar/toolbars.h @@ -39,6 +39,7 @@ public: void setTool(Tools::ToolBase *tool); void setActiveUnit(Util::Unit const *unit); + Toolbar *get_current_toolbar(); private: std::unordered_map> _toolbars; diff --git a/src/ui/tools/arc-tool.cpp b/src/ui/tools/arc-tool.cpp index 064604a0df..70c111a07b 100644 --- a/src/ui/tools/arc-tool.cpp +++ b/src/ui/tools/arc-tool.cpp @@ -204,14 +204,6 @@ bool ArcTool::root_handler(CanvasEvent const &event) } break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("arc-rx"); - ret = true; - } - break; - case GDK_KEY_Escape: if (dragging) { dragging = false; diff --git a/src/ui/tools/box3d-tool.cpp b/src/ui/tools/box3d-tool.cpp index 8f075c70f0..e5e1267526 100644 --- a/src/ui/tools/box3d-tool.cpp +++ b/src/ui/tools/box3d-tool.cpp @@ -347,10 +347,6 @@ bool Box3dTool::root_handler(CanvasEvent const &event) case GDK_KEY_x: case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("box3d-angle-x"); - ret = true; - } if (mod_shift_only(event)) { Persp3D::toggle_VPs(selection->perspList(), Proj::X); _vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically? diff --git a/src/ui/tools/eraser-tool.cpp b/src/ui/tools/eraser-tool.cpp index 996d459d1a..252ea7d500 100644 --- a/src/ui/tools/eraser-tool.cpp +++ b/src/ui/tools/eraser-tool.cpp @@ -518,14 +518,6 @@ bool EraserTool::_handleKeypress(KeyPressEvent const &key) ret = true; break; - case GDK_KEY_x: - case GDK_KEY_X: - if (just_alt) { - _desktop->setToolboxFocusTo("eraser-width"); - ret = true; - } - break; - case GDK_KEY_Escape: if (mode == EraserToolMode::DELETE) { Inkscape::Rubberband::get(_desktop)->stop(); diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp index cac515b64c..9ec1f4d992 100644 --- a/src/ui/tools/gradient-tool.cpp +++ b/src/ui/tools/gradient-tool.cpp @@ -588,14 +588,6 @@ bool GradientTool::root_handler(CanvasEvent const &event) nullptr); break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("altx-grad"); - ret = true; - } - break; - case GDK_KEY_A: case GDK_KEY_a: if (mod_ctrl_only(event) && _grdrag->isNonEmpty()) { diff --git a/src/ui/tools/rect-tool.cpp b/src/ui/tools/rect-tool.cpp index bed0e678c5..7301a39bed 100644 --- a/src/ui/tools/rect-tool.cpp +++ b/src/ui/tools/rect-tool.cpp @@ -221,13 +221,6 @@ bool RectTool::root_handler(CanvasEvent const &event) _("Alt: use with Ctrl to make square")); } break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("rect-width"); - ret = true; - } - break; case GDK_KEY_g: case GDK_KEY_G: diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 8b4690ebfb..5a83d0d260 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -918,14 +918,6 @@ bool SelectTool::root_handler(CanvasEvent const &event) } break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("select-x"); - ret = true; - } - break; - case GDK_KEY_Return: if (mod_ctrl_only(event)) { if (selection->singleItem()) { diff --git a/src/ui/tools/shortcuts.cpp b/src/ui/tools/shortcuts.cpp index 3e7d9f2d7b..77863125de 100644 --- a/src/ui/tools/shortcuts.cpp +++ b/src/ui/tools/shortcuts.cpp @@ -20,6 +20,7 @@ std::vector> raw_data_tools_shortcuts = { {"tool.all.quick-preview", N_("Quick Preview"), "Tools", N_("Preview how the document will look while the key is pressed.") } , {"tool.all.quick-zoom", N_("Quick Zoom"), "Tools", N_("Zoom into the selected objects while the key is pressed.") } , {"tool.all.quick-pan", N_("Quick Pan Canvas"), "Tools", N_("Pan the canvas with the mouse while the key is pressed.") } + , {"tool.all.focus-first-widget", N_("Focus First Widget"), "Tools", N_("Focus the first input widget in the active tool's toolbar.") } , {"tool.pen.to-line", N_("Pen Segment To Line"), "Tools", N_("Convert the last pen segment to a straight line.") } , {"tool.pen.to-curve", N_("Pen Segment To Curve"), "Tools", N_("Convert the last pen segment to a curved line.") } diff --git a/src/ui/tools/spiral-tool.cpp b/src/ui/tools/spiral-tool.cpp index 1fd67c552c..6c018ead9d 100644 --- a/src/ui/tools/spiral-tool.cpp +++ b/src/ui/tools/spiral-tool.cpp @@ -205,14 +205,6 @@ bool SpiralTool::root_handler(CanvasEvent const &event) _("Alt: lock spiral radius")); break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("spiral-revolutions"); - ret = true; - } - break; - case GDK_KEY_Escape: if (dragging) { dragging = false; diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index 582bda3e82..a091b8caf5 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -1394,13 +1394,6 @@ bool SprayTool::root_handler(CanvasEvent const &event) sp_spray_update_area(this); ret = true; break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("spray-width"); - ret = true; - } - break; case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: update_cursor(true); diff --git a/src/ui/tools/star-tool.cpp b/src/ui/tools/star-tool.cpp index 69ca7855a0..e90c898339 100644 --- a/src/ui/tools/star-tool.cpp +++ b/src/ui/tools/star-tool.cpp @@ -212,14 +212,6 @@ bool StarTool::root_handler(CanvasEvent const &event) nullptr); break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("altx-star"); - ret = true; - } - break; - case GDK_KEY_Escape: if (dragging) { dragging = false; diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp index 9d040cc81e..2bb22d05dd 100644 --- a/src/ui/tools/text-tool.cpp +++ b/src/ui/tools/text-tool.cpp @@ -696,14 +696,6 @@ bool TextTool::root_handler(CanvasEvent const &event) // Neither unimode nor IM consumed key; process text tool shortcuts. switch (group0_keyval) { - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("TextFontFamilyAction_entry"); - ret = true; - return; - } - break; case GDK_KEY_space: if (mod_ctrl_only(event)) { // No-break space diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index 2d15d29867..448f6d87e0 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -41,6 +41,8 @@ #include "ui/knot/knot.h" #include "ui/modifiers.h" #include "ui/popup-menu.h" +#include "ui/toolbar/toolbars.h" +#include "ui/toolbar/toolbar.h" #include "ui/shape-editor.h" #include "ui/shortcuts.h" #include "ui/tool/control-point.h" @@ -51,6 +53,9 @@ #include "ui/widget/canvas-grid.h" #include "ui/widget/canvas.h" #include "ui/widget/desktop-widget.h" +#include "ui/widget/combo-box-entry-tool-item.h" +#include +#include #include "ui/widget/events/canvas-event.h" #include "ui/widget/events/debug.h" @@ -100,6 +105,7 @@ ToolBase::ToolBase(SPDesktop *desktop, std::string &&prefs_path, std::string &&c , _acc_quick_preview{"tool.all.quick-preview"} , _acc_quick_zoom{"tool.all.quick-zoom"} , _acc_quick_pan{"tool.all.quick-pan"} + , _acc_focus_first_widget{"tool.all.focus-first-widget"} { pref_observer = Inkscape::Preferences::PreferencesObserver::create(_prefs_path, [this] (auto &val) { set(val); }); set_cursor(_cursor_default); @@ -123,6 +129,55 @@ ToolBase::~ToolBase() enableSelectionCue(false); } +/** + * @brief Helper function to recursively search a Gtk::Widget container for the first focusable and sensitive widget. + * @param container The Gtk::Widget (which might be a container) to search within. + * @return A pointer to the first focusable widget found, or nullptr if none is found. + */ +static Gtk::Widget* find_first_focusable_input(Gtk::Widget* container) { + if (!container || !container->get_sensitive() || !container->get_visible()) { + return nullptr; + } + + if (auto* combo = dynamic_cast(container)) { + Gtk::Entry* entry = combo->get_entry(); + return (entry && entry->get_sensitive()) ? entry : nullptr; + } + + if (dynamic_cast(container) || dynamic_cast(container)) { + return (container->get_focusable() && container->get_sensitive()) ? container : nullptr; + } + + + // If the widget is not an input type itself, it might be a generic container. Search its children. + for (auto child = container->get_first_child(); child != nullptr; child = child->get_next_sibling()) { + if (auto* found_widget = find_first_focusable_input(child)) { + return found_widget; + } + } + + return nullptr; +} + +/** + * @brief Find the first focusable widget on the tool's toolbar and grab focus. + * This is the generic implementation that will be used by all tools unless they override it. + */ +void ToolBase::focus_first_widget() +{ + if (!_desktop) return; + + auto* dt_widget = _desktop->getDesktopWidget(); + if (!dt_widget) return; + + auto* curr_toolbar = dt_widget->get_current_toolbar(); + if (!curr_toolbar) return; + + if (auto* first_focusable = find_first_focusable_input(curr_toolbar)) { + first_focusable->grab_focus(); + } +} + /** * Called by our pref_observer if a preference has been changed. */ @@ -603,7 +658,11 @@ bool ToolBase::root_handler(CanvasEvent const &event) message_context->set(Inkscape::INFORMATION_MESSAGE, _("Space+mouse move to pan canvas")); ret = true; } - + if (_acc_focus_first_widget.isTriggeredBy(event)) { + focus_first_widget(); + ret = true; + return; + } switch (get_latin_keyval(event)) { // GDK insists on stealing the tab keys for cycling widgets in the diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h index 207c39abf7..2ed401df5e 100644 --- a/src/ui/tools/tool-base.h +++ b/src/ui/tools/tool-base.h @@ -102,6 +102,7 @@ public: virtual bool item_handler(SPItem *item, CanvasEvent const &event); virtual void menu_popup(CanvasEvent const &event, SPObject *obj = nullptr); virtual bool can_undo(bool redo = false) { return false; } + virtual void focus_first_widget(); virtual bool is_ready() const { return true; } virtual void switching_away(std::string const &new_tool) {} @@ -217,6 +218,7 @@ protected: Util::ActionAccel _acc_quick_preview; Util::ActionAccel _acc_quick_zoom; Util::ActionAccel _acc_quick_pan; + Util::ActionAccel _acc_focus_first_widget; private: void _filterEventForSnapping(SPItem *item, CanvasEvent const &event, DelayedSnapEvent::Origin origin); bool _keyboardMove(KeyEvent const &event, Geom::Point const &dir); diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp index 22b443ad57..219be4cd45 100644 --- a/src/ui/tools/tweak-tool.cpp +++ b/src/ui/tools/tweak-tool.cpp @@ -1307,13 +1307,6 @@ bool TweakTool::root_handler(CanvasEvent const &event) sp_tweak_update_area(this); ret = true; break; - case GDK_KEY_x: - case GDK_KEY_X: - if (mod_alt_only(event)) { - _desktop->setToolboxFocusTo("tweak-width"); - ret = true; - } - break; case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: diff --git a/src/ui/widget/combo-box-entry-tool-item.cpp b/src/ui/widget/combo-box-entry-tool-item.cpp index 7dd4637f11..336e16049f 100644 --- a/src/ui/widget/combo-box-entry-tool-item.cpp +++ b/src/ui/widget/combo-box-entry-tool-item.cpp @@ -129,6 +129,11 @@ ComboBoxEntryToolItem::ComboBoxEntryToolItem(Glib::ustring name, * Text is not in the list store (i.e. default font-family is not on system): * In this case we have a row number of -1, and the text must be set by hand. */ + +Gtk::Entry* ComboBoxEntryToolItem::get_entry() const { + return _entry; +} + bool ComboBoxEntryToolItem::set_active_text(Glib::ustring text, int row) { _text = std::move(text); diff --git a/src/ui/widget/combo-box-entry-tool-item.h b/src/ui/widget/combo-box-entry-tool-item.h index 76a43655e3..d1a1b2398c 100644 --- a/src/ui/widget/combo-box-entry-tool-item.h +++ b/src/ui/widget/combo-box-entry-tool-item.h @@ -67,6 +67,7 @@ public: void set_tooltip (Glib::ustring const &tooltip); // Accessor methods + Gtk::Entry* get_entry() const; int get_active() const { return _active; } sigc::connection connectChanged(sigc::slot &&slot) { return _signal_changed.connect(std::move(slot)); } diff --git a/src/ui/widget/desktop-widget.cpp b/src/ui/widget/desktop-widget.cpp index 6d88bcff85..cbd00333bc 100644 --- a/src/ui/widget/desktop-widget.cpp +++ b/src/ui/widget/desktop-widget.cpp @@ -333,6 +333,11 @@ void SPDesktopWidget::switchDesktop(SPDesktop *desktop) _window->setActiveTab(_desktop); } +Inkscape::UI::Toolbar::Toolbar *SPDesktopWidget::get_current_toolbar() +{ + return tool_toolbars->get_current_toolbar(); +} + void SPDesktopWidget::advanceTab(int by) { auto tabs = _canvas_grid->getTabsWidget(); diff --git a/src/ui/widget/desktop-widget.h b/src/ui/widget/desktop-widget.h index fd3eb1e969..ccb20326a4 100644 --- a/src/ui/widget/desktop-widget.h +++ b/src/ui/widget/desktop-widget.h @@ -70,6 +70,7 @@ class SwatchesPanel; namespace Toolbar { class Toolbars; class CommandToolbar; +class Toolbar; class SnapToolbar; class ToolToolbar; } // namespace Toolbars @@ -93,6 +94,7 @@ public: SPDesktopWidget(InkscapeWindow *inkscape_window); ~SPDesktopWidget() override; + Inkscape::UI::Toolbar::Toolbar* get_current_toolbar(); Inkscape::UI::Widget::CanvasGrid *get_canvas_grid() { return _canvas_grid; } // Temp, I hope! Inkscape::UI::Widget::Canvas *get_canvas() { return _canvas; } std::vector const &get_desktops() const { return _desktops; } -- GitLab