Files
create/docs/src/quicknav/SPEC.md
forbes-0023 e73c5fc750
All checks were successful
Build and Test / build (pull_request) Successful in 29m58s
feat: add QuickNav addon — Phase 1 core infrastructure (#320)
Add quicknav submodule and create-side integration for keyboard-driven
command navigation.

Submodule: mods/quicknav (https://git.kindred-systems.com/kindred/quicknav)

Create-side changes:
- CMakeLists.txt: add quicknav install rules
- test_kindred_pure.py: add 16 workbench_map validation tests
- docs/src/quicknav/SPEC.md: QuickNav specification

QuickNav provides numbered-key access to workbenches (Ctrl+1-5),
command groupings (Shift+1-9), and individual commands (1-9), with
a navigation bar toolbar and input-widget safety guards.
2026-02-23 14:12:02 -06:00

18 KiB

QuickNav — Keyboard Navigation Addon Specification

Addon name: QuickNav Type: Pure Python FreeCAD addon (no C++ required) Compatibility: FreeCAD 1.0+, Kindred Create 0.1+ Location: mods/quicknav/


1. Overview

QuickNav provides keyboard-driven command access for FreeCAD and Kindred Create. It replaces mouse-heavy toolbar navigation with a numbered key system organized by workbench and command grouping. The addon is activated by loading its workbench and toggled on/off with the 0 key.

Design Goals

  • Numbers 1-9 execute commands within the active command grouping
  • Shift+1-9 switches command grouping within the active workbench
  • Ctrl+1-9 switches workbench context
  • All groupings and workbenches are ordered by most-recently-used (MRU) history
  • History is unlimited internally, top 9 shown, remainder scrollable/clickable
  • Mouse interaction remains fully functional — QuickNav is purely additive
  • Configuration persisted via FreeCAD.ParamGet()

2. Terminology

Term Definition
Workbench A FreeCAD workbench (Sketcher, PartDesign, Assembly, etc.). Fixed assignment to Ctrl+N slots.
Command Grouping A logical group of commands within a workbench, mapped from existing FreeCAD toolbar groupings. Max 9 per tier.
Active Grouping The left-most visible grouping in the navigation bar. Its commands are accessible via 1-9.
Navigation Bar Bottom toolbar displaying the current state: active workbench, groupings, and numbered commands.
MRU Stack Most-recently-used ordering. Position 0 = currently active, 1 = previously active, etc.
Tier When a workbench has >9 command groupings, they are split: Tier 1 (most common 9), Tier 2 (next 9).

3. Key Bindings

3.1 Mode Toggle

Key Action
0 Toggle QuickNav on/off. When off, all QuickNav key interception is disabled and the navigation bar hides.

3.2 Command Execution

Key Action
1-9 Execute the Nth command in the active grouping. If the command is auto-executable (e.g., Pad after closed sketch), execute immediately. Otherwise, enter tool mode (same as clicking the toolbar button).

3.3 Grouping Navigation

Key Action
Shift+1-9 Switch to the Nth command grouping (MRU ordered) within the current workbench. The newly activated grouping moves to position 0 in the MRU stack.
Shift+Left/Right Scroll through groupings beyond the visible 9.

3.4 Workbench Navigation

Key Action
Ctrl+1 Sketcher
Ctrl+2 Part Design
Ctrl+3 Assembly
Ctrl+4 Spreadsheet
Ctrl+5 TechDraw
Ctrl+6-9 User-configurable / additional workbenches

Switching workbench via Ctrl+N also restores that workbench's last-active command grouping.


4. Navigation Bar

The navigation bar is a QToolBar positioned at the bottom of the main window (replacing or sitting alongside FreeCAD's default bottom toolbar area).

4.1 Layout

┌─────────────────────────────────────────────────────────────────────┐
│ [WB: Sketcher] │ ❶ Primitives │ ② Constraints │ ③ Dimensions │ ◀▶ │
│                │ 1:Line 2:Rect 3:Circle 4:Arc 5:Point 6:Slot ...  │
└─────────────────────────────────────────────────────────────────────┘
  • Left section: Current workbench name with Ctrl+N hint
  • Middle section (top row): Command groupings, MRU ordered. Active grouping is ❶ (filled circle), others are ②③ etc. Scrollable horizontally if >9.
  • Middle section (bottom row): Commands within the active grouping, numbered 1-9
  • Right section: Scroll arrows for overflow groupings

4.2 Visual States

  • Active grouping: Bold text, filled number badge, Catppuccin Mocha blue (#89b4fa) accent
  • Inactive groupings: Normal text, outlined number badge, surface1 (#45475a) text
  • Hovered command: surface2 (#585b70) background highlight
  • Active command (tool in use): green (#a6e3a1) underline indicator

4.3 Mouse Interaction

  • Click any grouping to activate it (equivalent to Shift+N)
  • Click any command to execute it (equivalent to pressing N)
  • Scroll wheel on grouping area to cycle through overflow groupings
  • Click scroll arrows to page through overflow

5. Workbench Command Groupings

Each workbench's existing FreeCAD toolbars map to command groupings. Where a workbench has >9 toolbars, split into Tier 1 (default, most common) and Tier 2 (accessible via scrolling or Shift+Left/Right).

5.1 Sketcher (Ctrl+1)

Grouping Commands (1-9)
Primitives Line, Rectangle, Circle, Arc, Point, Slot, B-Spline, Polyline, Ellipse
Constraints Coincident, Horizontal, Vertical, Parallel, Perpendicular, Tangent, Equal, Symmetric, Block
Dimensions Distance, Horizontal Distance, Vertical Distance, Radius, Diameter, Angle, Lock, Constrain Refraction
Construction Toggle Construction, External Geometry, Carbon Copy, Offset, Trim, Extend, Split
Tools Mirror, Array (Linear), Array (Polar), Move, Rotate, Scale, Close Shape, Connect Edges

5.2 Part Design (Ctrl+2)

Grouping Commands (1-9)
Additive Pad, Revolution, Additive Loft, Additive Pipe, Additive Helix, Additive Box, Additive Cylinder, Additive Sphere, Additive Cone
Subtractive Pocket, Hole, Groove, Subtractive Loft, Subtractive Pipe, Subtractive Helix, Subtractive Box, Subtractive Cylinder, Subtractive Sphere
Datums New Sketch, Datum Plane, Datum Line, Datum Point, Shape Binder, Sub-Shape Binder, ZTools Datum Creator, ZTools Datum Manager
Transformations Mirrored, Linear Pattern, Polar Pattern, MultiTransform, ZTools Rotated Linear Pattern
Modeling Fillet, Chamfer, Draft, Thickness, Boolean, ZTools Enhanced Pocket

5.3 Assembly (Ctrl+3)

Grouping Commands (1-9)
Components Insert Component, Create Part, Create Assembly, Ground, BOM
Joints Fixed, Revolute, Cylindrical, Slider, Ball, Planar, Distance, Angle, Parallel
Patterns ZTools Linear Pattern, ZTools Polar Pattern

5.4 Spreadsheet (Ctrl+4)

Grouping Commands (1-9)
Editing Merge Cells, Split Cell, Alias, Import CSV, Export CSV
Formatting Bold, Italic, Underline, Align Left, Align Center, Align Right, BG Color, Text Color, Quick Alias

5.5 TechDraw (Ctrl+5)

Groupings derived from TechDraw's existing toolbars at runtime.

Note: The exact command lists above are initial defaults. The addon discovers available commands from each workbench's toolbar structure at activation time and falls back to these defaults only if discovery fails.


6. MRU History Behavior

6.1 Grouping History (per workbench)

Each workbench maintains its own grouping MRU stack.

  • When a grouping is activated (via Shift+N or mouse click), it moves to position 0
  • The previously active grouping moves to position 1, everything else shifts down
  • Position 0 is always the active grouping (already selected, shown leftmost)
  • Shift+1 is a no-op (already active), Shift+2 activates the previous grouping, etc.

6.2 Workbench History

  • Workbenches have fixed Ctrl+N assignments (not MRU ordered)
  • However, each workbench remembers its last-active grouping
  • Switching to a workbench restores its last-active grouping as position 0

6.3 Persistence

Stored in FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/QuickNav"):

Parameter Type Description
Enabled Bool Whether QuickNav is currently active
GroupHistory/<Workbench> String Semicolon-delimited list of grouping names in MRU order
LastGrouping/<Workbench> String Name of the last-active grouping per workbench
CustomSlots/Ctrl6 through Ctrl9 String Workbench names for user-configurable slots

7. Auto-Execution Logic

When a command is invoked via number key, QuickNav checks if the command can be auto-executed:

7.1 Auto-Execute Conditions

A command auto-executes (runs and completes without entering a persistent mode) when:

  1. Pad/Pocket after closed sketch: If the active body has a sketch that was just closed (sketch edit mode exited with a closed profile), pressing the Pad or Pocket command key creates the feature with default parameters. The task panel still opens for parameter adjustment.
  2. Boolean operations: If exactly two bodies/shapes are selected, boolean commands execute with defaults.
  3. Constraint application: If appropriate geometry is pre-selected in Sketcher, constraint commands apply immediately.

7.2 Mode-Entry (Default)

All other commands enter their standard FreeCAD tool mode — identical to clicking the toolbar button. The user interacts with the 3D view and/or task panel as normal.


8. Key Event Handling

8.1 Event Filter Architecture

class QuickNavEventFilter(QObject):
    """Installed on FreeCAD's main window via installEventFilter().

    Intercepts KeyPress events when QuickNav is active.
    Passes through all events when QuickNav is inactive.
    """

    def eventFilter(self, obj, event):
        if event.type() != QEvent.KeyPress:
            return False
        if not self._active:
            return False

        # Don't intercept when a text input widget has focus
        focused = QApplication.focusWidget()
        if isinstance(focused, (QLineEdit, QTextEdit, QPlainTextEdit, QSpinBox, QDoubleSpinBox)):
            return False

        # Don't intercept when task panel input fields are focused
        if self._is_task_panel_input(focused):
            return False

        key = event.key()
        modifiers = event.modifiers()

        if key == Qt.Key_0 and modifiers == Qt.NoModifier:
            self.toggle_active()
            return True

        if key >= Qt.Key_1 and key <= Qt.Key_9:
            n = key - Qt.Key_0
            if modifiers == Qt.ControlModifier:
                self.switch_workbench(n)
                return True
            elif modifiers == Qt.ShiftModifier:
                self.switch_grouping(n)
                return True
            elif modifiers == Qt.NoModifier:
                self.execute_command(n)
                return True

        return False  # Pass through all other keys

8.2 Conflict Resolution

QuickNav's event filter takes priority when active. FreeCAD's existing keybindings for Ctrl+1 through Ctrl+9 (if any) are overridden while QuickNav is enabled. The original bindings are restored when QuickNav is toggled off or unloaded.

Existing Shift+ and bare number key bindings in FreeCAD are similarly overridden only while QuickNav is active. This is safe because:

  • FreeCAD does not use bare number keys as shortcuts by default
  • Shift+number is not commonly bound in default FreeCAD

8.3 Input Widget Safety

The event filter must NOT intercept keys when the user is:

  • Typing in the Python console
  • Entering values in the task panel (dimensions, parameters)
  • Editing spreadsheet cells
  • Typing in any QLineEdit, QTextEdit, QSpinBox, or QDoubleSpinBox
  • Using the Sketcher's inline dimension input

9. Addon Structure

mods/quicknav/
├── package.xml              # FreeCAD addon manifest with <kindred> extension
├── Init.py                  # Non-GUI initialization (no-op)
├── InitGui.py               # Registers QuickNavWorkbench
├── quicknav/
│   ├── __init__.py
│   ├── core.py              # QuickNavManager singleton — orchestrates state
│   ├── event_filter.py      # QuickNavEventFilter (QObject)
│   ├── nav_bar.py           # NavigationBar (QToolBar subclass)
│   ├── workbench_map.py     # Fixed workbench → Ctrl+N mapping + grouping discovery
│   ├── history.py           # MRU stack with ParamGet persistence
│   ├── auto_exec.py         # Auto-execution condition checks
│   ├── commands.py          # FreeCAD command wrappers (QuickNav_Toggle, etc.)
│   └── resources/
│       ├── icons/           # Number badge SVGs, QuickNav icon
│       └── theme.py         # Catppuccin Mocha color tokens
└── tests/
    └── test_history.py      # MRU stack unit tests

9.1 Manifest

<?xml version="1.0" encoding="UTF-8"?>
<package format="1">
  <name>QuickNav</name>
  <description>Keyboard-driven toolbar navigation</description>
  <version>0.1.0</version>
  <maintainer email="dev@kindred-systems.com">Kindred Systems</maintainer>
  <license>LGPL-2.1</license>
  <content>
    <workbench>
      <classname>QuickNavWorkbench</classname>
    </workbench>
  </content>
  <kindred>
    <min_create_version>0.1.0</min_create_version>
    <load_priority>10</load_priority>
    <pure_python>true</pure_python>
    <dependencies>
      <dependency>sdk</dependency>
    </dependencies>
  </kindred>
</package>

9.2 Activation

QuickNav activates when its workbench is loaded (via the addon loader or manual activation). It installs the event filter on the main window and creates the navigation bar. The workbench itself is invisible — it does not add its own toolbars or menus beyond the navigation bar. It acts as a transparent overlay on whatever workbench the user is actually working in.

class QuickNavWorkbench(Gui.Workbench):
    """Invisible workbench that installs QuickNav on load.

    QuickNav doesn't replace the active workbench — it layers on top.
    Loading QuickNav installs the event filter and nav bar, then
    immediately re-activates the previously active workbench.
    """

    def Initialize(self):
        QuickNavManager.instance().install()

    def Activated(self):
        # Re-activate the previous workbench so QuickNav is transparent
        prev = QuickNavManager.instance().previous_workbench
        if prev:
            Gui.activateWorkbench(prev)

    def Deactivated(self):
        pass

    def GetClassName(self):
        return "Gui::PythonWorkbench"

Alternative (preferred for Create): Instead of a workbench, QuickNav can be activated directly from Create/InitGui.py at boot, gated by the Enabled preference. This avoids the workbench-switching dance entirely. The QuickNavWorkbench registration is kept for standalone FreeCAD compatibility.


10. Command Discovery

At activation time, QuickNav introspects each workbench's toolbars to build the command grouping map.

def discover_groupings(workbench_name: str) -> list[CommandGrouping]:
    """Discover command groupings from a workbench's toolbar structure.

    1. Temporarily activate the workbench (if not already active)
    2. Enumerate QToolBars from the main window
    3. Map toolbar name → list of QAction names
    4. Filter out non-command actions (separators, widgets)
    5. Split into tiers if >9 groupings
    6. Restore the previously active workbench
    """

10.1 Fallback Defaults

If toolbar discovery fails (workbench not initialized, empty toolbars), QuickNav falls back to the hardcoded groupings in Section 5. These are stored as a Python dict in workbench_map.py.

10.2 ZTools Integration

ZTools commands injected via WorkbenchManipulator appear in the discovered toolbars and are automatically included in the relevant groupings. No special handling is needed — QuickNav discovers commands after all manipulators have run.


11. FreeCAD Compatibility

QuickNav is designed as a standalone FreeCAD addon that works without Kindred Create or the SDK.

Feature FreeCAD Kindred Create
Core navigation (keys, nav bar)
Catppuccin Mocha theming (uses Qt defaults) (via SDK theme tokens)
Auto-boot on startup (manual workbench activation) (via addon loader)
ZTools commands in groupings (not present) (discovered from manipulated toolbars)

The SDK dependency is optional — QuickNav checks for kindred_sdk availability and degrades gracefully:

try:
    from kindred_sdk.theme import get_theme_tokens
    THEME = get_theme_tokens()
except ImportError:
    THEME = None  # Use Qt default palette

12. Implementation Phases

Phase 1: Core Infrastructure

  • Event filter with key interception and input widget safety
  • QuickNavManager singleton with toggle on/off
  • Navigation bar widget (QToolBar) with basic layout
  • Hardcoded workbench/grouping maps from Section 5
  • ParamGet persistence for enabled state

Phase 2: Dynamic Discovery

  • Toolbar introspection for command grouping discovery
  • MRU history with persistence
  • Grouping overflow scrolling
  • Workbench restore (last-active grouping per workbench)

Phase 3: Auto-Execution

  • Context-aware auto-execute logic
  • Sketcher closed-profile detection for Pad/Pocket
  • Pre-selection constraint application

Phase 4: Polish

  • Number badge SVG icons
  • Catppuccin Mocha theming (conditional on SDK)
  • Scroll animations
  • Settings dialog (custom Ctrl+6-9 assignments)
  • FreeCAD standalone packaging

13. Open Questions

  1. Tier switching UX: When a workbench has >9 groupings split into tiers, should Shift+0 toggle between tiers, or should tiers be purely a scroll/mouse concept?

  2. Visual number badges: Should the commands in the nav bar show keycap-style badges (like ⌨ 1) or just prepend the number (1: Line)?

  3. Sketcher inline dimension input: FreeCAD's Sketcher has an inline dimension entry that isn't a standard QLineEdit. Need to verify the event filter correctly identifies and skips this widget.

  4. Ctrl+N conflicts with Create shortcuts: Verify that Create/Silo don't already bind Ctrl+1 through Ctrl+9. The Silo toggle uses Ctrl+O/S/N, so these should be clear.