spacr.qt.widgets.drawer

EdgeDrawer — a panel that slides in from the left edge on hover.

The full app list used to be a permanent 220 px column. It is now a reveal: a 6 px strip along the left edge that, when the pointer rests against it, slides the whole panel in over the page and slides it out again when the pointer leaves.

Three things make the difference between a reveal that feels deliberate and one that fires while you are aiming at something else:

  • Dwell, not touch. Entering the strip starts EdgeDrawer.OPEN_DELAY_MS; a pointer that crosses the strip on its way somewhere else has left again before the timer fires. This is the whole reason a hot corner is usable at all.

  • A generous close. Leaving does not close immediately — EdgeDrawer.CLOSE_DELAY_MS covers the gap between the panel and wherever the pointer wobbled to, and any re-entry cancels it. Panels that snap shut the instant the pointer strays are unusable with a trackpad.

  • A pin. Clicking any row while the drawer is open would otherwise race the close animation. hold() keeps it open until something explicitly releases it, which is also how the keyboard path works.

It is not mouse-only. open_for_keyboard() opens the drawer, pins it, and focuses the first row; Escape (or focus leaving the panel) closes it again. The window installs a shortcut for this — a reveal you can only reach by hovering is a reveal a keyboard user cannot reach at all.

Classes

EdgeDrawer

Hosts panel as a slide-in overlay on the left of host.

Module Contents

class spacr.qt.widgets.drawer.EdgeDrawer(host: PySide6.QtWidgets.QWidget, panel: PySide6.QtWidgets.QWidget, width: int = 0, parent=None)[source]

Bases: PySide6.QtWidgets.QWidget

Hosts panel as a slide-in overlay on the left of host.

The drawer is a child of host (not a top-level window), so it clips to the page, scrolls with nothing, and needs no platform window flags. It is raised above its siblings whenever it opens.

Parameters:
  • host – the widget the drawer overlays. Must outlive the drawer.

  • panel – the content — typically the app Sidebar.

  • width – drawn width in px; defaults to the panel’s own.

  • parent – parent widget; ownership only.

Build the sliding drawer and its edge trigger.

The drawer starts fully off-screen rather than hidden: a hidden widget reports no geometry, and the tutorial overlay needs a rectangle to point at. Whether it is open is tracked as its own flag rather than derived from position – the slide takes 170 ms, and for those 170 ms a position-derived answer would call an opening drawer closed, which is how a caller ends up racing the animation.

Parameters:
  • host – the widget the drawer slides over; also where the hot strip lives.

  • panel – the contents; re-parented into the drawer.

  • width – the drawer’s width; 0 takes the panel’s.

  • parent – parent widget; defaults to host.

adopt(panel: PySide6.QtWidgets.QWidget) → None[source]

Take panel back after something else re-parented it.

The locked dock moves the sidebar into the window’s own layout; switching back to the reveal has to move the same object here again — building a second Sidebar would leave the tutorial, the command palette and every test pointing at the dead one.

Parameters:

panel – the panel widget to re-parent into the drawer; it is moved to the top-left, sized to the drawer’s width and height and shown.

arm() → None[source]

Pointer entered the hot strip — start the dwell timer.

close() → None[source]

Slide the panel back out and unpin it.

disarm() → None[source]

Pointer left the hot strip before the dwell elapsed.

enterEvent(event)[source]

Open on hover.

Parameters:

event – the Qt enter event.

eventFilter(obj, event)[source]

Watch the host for the events that open and close the drawer.

Parameters:
  • obj – the object the event is for.

  • event – the event.

Returns:

True to stop the event going further.

hold(held: bool = True) → None[source]

Pin the drawer open (a click landed in it, or the keyboard did).

is_enabled() → bool[source]

True while the edge reveal is armed.

is_fully_open() → bool[source]

True only once the slide has finished.

is_held() → bool[source]

Whether the drawer is pinned open.

HELD IS NOT OPEN. A hovered drawer is open and closes when the pointer leaves; a held one stays until it is released.

Returns:

True when pinned.

is_open() → bool[source]

True while the panel is on screen, or sliding on.

keyPressEvent(event)[source]

Close on Escape.

Parameters:

event – the Qt key event.

leaveEvent(event)[source]

Close on leave, unless the drawer is held open.

Parameters:

event – the Qt leave event.

open() → None[source]

Slide the panel in.

open_for_keyboard() → None[source]

Open, pin, and move focus into the panel.

Without this the reveal is mouse-only, which makes every app that is not on the tab currently open unreachable without a pointer.

relayout() → None[source]

Re-fit to the host. Called on every host resize.

relayout_for_open() → None[source]

Resize to the host’s full height and lay the panel out.

Called on open rather than on construction, because the host can be resized while the drawer is closed and a drawer sized to a stale height opens as a stripe down part of the window.

schedule_close() → None[source]

Close after the grace period, unless re-entered or held.

set_enabled(enabled: bool) → None[source]

Arm or disarm the whole reveal.

Disabled, the hot strip is hidden and the dwell/close timers are stopped, so nothing can slide in — which is what both the “locked” dock (the panel is a column elsewhere; a second copy sliding over it would be absurd) and the “hidden” dock need.

Not the same as setEnabled: that greys a widget out and leaves it on screen. This takes the trigger away and leaves open() working, so a caller that means to open it anyway still can.

Parameters:

enabled – truthy to arm the reveal, falsy to hide the hot strip and stop the open and close timers.

toggle() → None[source]

Keyboard entry point: open+pin, or close if already open.