spacr.qt.widgets.dock¶
The left navigation dock: an icon, a name, and a category heading.
A row is a button with an icon and its name, always both. The only thing the pointer changes is the colour, and the explanation goes to the strip along the bottom of the window rather than into a popup.
WHAT WAS REMOVED, AND WHY EACH ONE WAS THE BUG. The dock this replaces was
1,116 lines across Sidebar and _DockRow in spacr.qt.app, and
nearly all of it was machinery that existed to defeat itself:
a translucent slab painted in
paintEvent— the “black box” of four separate commits, which turned out to be the dock painting itself rather than any stylesheet;a per-row icon-size model (
resting_icon_px,_place_icon,_set_icon_px,_forget_icon_sizes,_rest_every_icon) that grew and shrank icons under the pointer, which is what made hovering relayout the column and blink;the name painted only while hovered, so a resting dock was a column of unlabelled glyphs;
a second, indented level of folded modules with its own expand state (
_fold_children,_open_hosts), which is the “sub categories”.
None of that is here. A row is a button with an icon and its name, always both. The only thing the pointer changes is the colour.
WHERE THE EXPLANATION WENT. Not into a popup tooltip — those are explicitly
unwanted — but into the strip along the bottom of the window, which already
exists as spacr.qt.widgets.module_hint_bar and already holds the last
hovered module for thirty seconds with its API and tutorial links. This dock
only says which module is under the pointer, via Dock.module_hovered;
the bar decides how to explain it.
WHAT IS KEPT, BECAUSE SOMETHING ELSE READS IT. Categories still collapse —
the list is longer than a short screen. Rows still carry navKey and
headers are still SidebarSection, because the theme, the tutorial script
and the maturity tests all reach the dock through those names. And
refresh_visibility() still applies the Alpha/Beta maturity filter and
hides a heading whose modules are all filtered out, which is a separate
reason for a row to be absent from its section being shut.
NOTHING HERE IMPORTS spacr.qt.app. The registry lives there and would
be a circular import, so the rows, the icon lookup and the maturity
predicate are all injected.
Classes¶
The navigation column: categories, each holding icon+name rows. |
|
The strip along the dock's right edge that drags its width. |
|
One module: its icon, then its name, both always drawn. |
|
A category heading. A label rather than a button, because it is |
Module Contents¶
- class spacr.qt.widgets.dock.Dock(rows: Iterable[Row], icon_for: Callable[[str], object] | None = None, is_visible: Callable[[str], bool] | None = None, parent=None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetThe navigation column: categories, each holding icon+name rows.
- Parameters:
rows – the modules to draw, in order, as
(key, name, desc, section). Grouping IS ordering: a new heading starts whenever the section changes, so a row out of place draws its heading twice.icon_for – optional
key -> QIcon | Nonefor the row icons.is_visible – optional
key -> boolmaturity predicate. Injected rather than imported so this module does not depend onspacr.qt.app, which is what defines the registry.parent – parent widget; ownership only.
Build the dock column: section headings with their module rows under them.
The container is made transparent rather than coloured. The application sheet carries a blanket
QWidget { background-color: bg }, so any untagged container paints an opaque rectangle – and a plain widget holding a rounded panel is exactly that, a square of window colour behind rounded corners. Colouring it only changes which colour the rectangle is.The rounded panel is a child frame rather than this widget’s own background, which satisfies both constraints at once: the container stays transparent where the theme pins it transparent, and the panel is still drawn.
- Parameters:
rows – the module rows to list, in order.
icon_for – called with a key for that module’s icon.
is_visible – called with a key to decide whether to list it.
parent – parent widget, or
None.
- apply_theme() None[source]¶
Paint the rounded panel, and the one rule hover uses.
THE PANEL IS THE ONLY THING THAT PAINTS. Everything inside it is transparent on purpose: a title or a heading carrying a fill of its own would draw a square corner over the rounded one directly beneath it, which is the exact shape this was asked to stop being.
The three values come from HomePanelBox rather than being chosen again here –
pane_surface('surface_alt'),border_softand an 8 px radius – so the dock and that box stay the same material when either is restyled.
- clamp_width(width: int) int[source]¶
widthheld betweenDRAG_MINandDRAG_MAX.- Parameters:
width – logical pixels.
- clipped_items() list[source]¶
Rows whose name had to be shortened to fit.
Empty in a healthy layout, and a test asserts that.
- column_width() int[source]¶
The width the column wears: the one the user dragged, else fitting.
A width the user chose is kept through every refresh, every hide and show, and every session; with none stored the column fits its longest name, as
fitting_width()has always done.
- eventFilter(watched, event)[source]¶
Light a heading under the pointer, and toggle it on release.
ON RELEASE, NOT PRESS: a press that toggled would fire while the pointer was still down, so a drag that began on a heading and ended elsewhere would still have shut the section.
The hover state is a PROPERTY rather than a colour set from here, because the stylesheet is the one place that decides what the dock looks like.
- Parameters:
watched – the object the event is for; only a
SectionHeaderis handled here.event – the event; Enter and Leave set the header’s
hoveredproperty, and a left-button release toggles its section.
- expand_host(host_key: str) None[source]¶
Accepted and does nothing: there are no folded child rows.
The second level was removed on request. This remains so the callers that opened a host on navigation do not have to know that, and because a method that quietly disappeared would fail at the call site rather than here, where the reason is written down.
- Parameters:
host_key – the app key of the host; ignored.
- fitting_width() int[source]¶
Width that shows the longest visible name in full, within bounds.
Font scale moves both bounds; the widest visible row moves the result between them. Public because the locked dock re-applies it after being re-parented out of the drawer, which had resized it.
- host_is_expanded(host_key: str) bool[source]¶
Always
False: there are no folded child rows to expand.- Parameters:
host_key – the app key of the host; ignored.
- leaveEvent(event)[source]¶
The pointer left the column: no row is lit.
The rows’ own Leave covers a pointer stepping between them; this covers one that leaves the dock altogether, including straight off the bottom row onto the empty stretch below it, where no other row will ever be entered.
- Parameters:
event – the leave event, passed to the base class after every row is unlit.
- refresh_icons() None[source]¶
Re-ask the provider for every row’s icon.
A QIcon bakes its pixmap when it is built, so re-applying the stylesheet does not recolour icons that already exist.
THE SIZE IS FIXED PER SCALE, NOT PER ROW STATE. The old dock grew and shrank an icon on hover, which relayed out the whole column under the pointer; that is what
ICON_PXbeing one number stops. It still has to follow the interface scale, so the base is recorded on each row and re-derived from there – seespacr.qt.preferences._set_scaled_icon_size()for why it is never recomputed from the size the row is already wearing.
- refresh_visibility() None[source]¶
Show a row if its category is open AND maturity allows it.
Two separate reasons for a row to be absent, and they are kept separate: a shut section hides rows that are perfectly mature, and the Alpha/Beta filter hides rows inside an open one. A heading stays put whether its section is open or shut — it is what you click to open it — and hides only when every module beneath it is filtered out.
- section_is_open(section: str) bool[source]¶
Whether
section’s rows are currently shown.- Parameters:
section – the category name.
- set_column_width(width: int, *, remember: bool = True) int[source]¶
Give the column
widthwithin its bounds and maybe remember it.- Parameters:
width – logical pixels; 0 or less goes back to the fitting width and forgets the dragged one.
remember – store it for the next session.
- Returns:
the width applied.
- class spacr.qt.widgets.dock.DockEdge(dock: Dock, parent=None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetThe strip along the dock’s right edge that drags its width.
Dragging it sets the width of the dock while the dock is shown. A sibling of the dock’s slot rather than a splitter handle, because the dock is a fixed-width layout member and everything that measures it (the drawer, the backdrop, the fitting width) reads that fixed width. Dragging sets it; releasing stores it; a double-click forgets it and the column fits its names again. It is shown and hidden with the dock, so a hidden dock has no edge to catch.
- Parameters:
dock – the
Dockit resizes.parent – parent widget; ownership only.
Build the edge for
dock; hidden until the dock is shown.- keyPressEvent(event) None[source]¶
Let a keyboard user activate the same collapse control.
- Parameters:
event – key press; Space, Return and Enter toggle the dock.
- mouseDoubleClickEvent(event) None[source]¶
Forget the dragged width; the column fits its names again.
- Parameters:
event – the event.
- mouseMoveEvent(event) None[source]¶
Follow the pointer, within the dock’s bounds; stored on release.
- Parameters:
event – the event.
- mousePressEvent(event) None[source]¶
Begin a drag from the dock’s present width.
- Parameters:
event – the event.
- mouseReleaseEvent(event) None[source]¶
End the drag and remember the width it left.
- Parameters:
event – the event.
- paintEvent(_event) None[source]¶
Draw the one-pixel line a splitter handle draws.
- Parameters:
_event – the paint event; the whole strip is drawn.
- class spacr.qt.widgets.dock.DockRow(key: str, name: str, desc: str = '', parent=None)[source]¶
Bases:
spacr.qt.widgets.eliding.ElidingPushButtonOne module: its icon, then its name, both always drawn.
The row paints nothing of its own — the colour comes from the stylesheet
Dockinstalls, so there is one place that decides what hover looks like and nopaintEventto disagree with it.Build one module row.
- Parameters:
key – the module’s registry key. Stamped onto the row three times over – as
navKey, asmoduleAppKeyand as the attribute – because three different readers ask for it: the icon refresh, the bottom hint strip’s filter, and this module.name – the module’s name, drawn beside the icon and set as the accessible name so a screen reader still gets the whole of it when the column elides it.
desc – the one-line summary. Not drawn here at all: it is stamped as
moduleSummarySourcefor the strip along the bottom of the window, which is where descriptions go.parent – parent widget.
- class spacr.qt.widgets.dock.SectionHeader(section: str, parent=None)[source]¶
Bases:
PySide6.QtWidgets.QLabelA category heading. A label rather than a button, because it is already styled as a heading and a button would have to be un-styled back into one; the click arrives through
Dock.eventFilter().- Parameters:
section – the category name. Shown as the heading AND kept on the
sectionNameproperty, which is how the dock finds the rows a click should fold – the visible text is translated, the property is not.parent – parent widget; ownership only.
Build one dock section heading.
The section name is also stored under its legacy property name, which is what the theme styles and what the maturity test looks headers up by.
- Parameters:
section – the section’s name.
parent – parent widget, or
None.