diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml
index ae997e4ea8a7a3ea038a874fd0685cd1329cccbd..23b5b87679a483d13448f7ce4301ffb031f79aa1 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 215a24fe0ef7d91d6b7ded63a01ad29a7b27d5e1..cd2e62ec19160036f22a69c374054aa75089601e 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 a45d9c119c37494793c0ce15b24b6bd228be518f..0f1ded2514931cc61f63b301b12e6c07e215ece6 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 064604a0df6fc53a1f6752a0f798a36a266f6520..70c111a07bffeb6ead583f8302e4b457cb2be1db 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 8f075c70f0e53c68a5b5c2061f336a2e316ba2e6..e5e12675269b3ddc49b31e58babb87d7afcbf9df 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 996d459d1a74a39bc7f444c6c8923bdf4dab5aab..252ea7d5002cfb7f38037a3bec8ebb2ea8e789e0 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 cac515b64c4d114454188d4a5095494884320764..9ec1f4d9926b05a1580f230e49330e949839b65f 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 bed0e678c516cfcc0192043f3f520dd57f1203c0..7301a39bed829af3d749b29645e24802d5748182 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 8b4690ebfba4349d4afd5c818f735a602e4c21ae..5a83d0d2604a8f255862648591b33ef94f782273 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 3e7d9f2d7b4601957d46bfe8346b05ef86de706b..77863125de7c4e0194177c94931d8d48bbd6603c 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 1fd67c552c3b7c46bf5fe4cf716c8194fdd2830c..6c018ead9d172ce31be103f7026e5f4384f732d3 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 582bda3e829dfb8e6a7495fd42ab7735adaaa33a..a091b8caf5d401dffc4d316578be771f98b8cf63 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 69ca7855a004c786a09bcfa3722a69178af104bc..e90c898339212f966cc0ed4c038a0482c0011916 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 9d040cc81ec053f890cbb1fb4b41ee7514ec6d12..2bb22d05ddb7277cbb5c3a2e6f0c9f638e2eb01c 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 2d15d2986726a0a7d03884fc1f1fcbedb3e69353..448f6d87e05d25f5d59d54a54a3cf1026a95d7a9 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 207c39abf736ae1622e7a94c91d5cf7a47c49da2..2ed401df5eac6a9492ad8a779521b829dba3d384 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 22b443ad57fe568084721ddf993afcd7505d403e..219be4cd45c586c3cbb4ce3d53af383cf7438349 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 7dd4637f116dbbd0004f63aca45b9cc7d6387046..336e16049f2d6a5bdde806fa17a5888c7401cf32 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 76a43655e328010bd3ecb079541e4d234ee032e2..d1a1b2398cba49929ec4efc352930b8119f1ff30 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 6d88bcff85692f9b2fe97af79111bf4dbfabfdb4..cbd00333bcf021067056974279803f81c5907768 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 fd3eb1e9690042a3967762ad185f6c627a0d6a7a..ccb20326a403f1e27cf2c62a1ea899834ebddd2f 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; }