feat(widgets): add Monitor Profile widget (WIP) - #1085
Open
tiagomta wants to merge 5 commits into
Open
Conversation
…creen bar hide - Runtime hotkeys now dispatch through EventService directly with the widget's screen name (BarManager is a QObject, unreachable via topLevelWidgets) - Purge dead widget instances before hotkey dispatch and guard label updates against destroyed QObjects, fixing a crash on config reload - Add a 2s fallback poller in AppBarManager that heals missed ABN_FULLSCREENAPP notifications (fullscreen app running during bar registration) with per-monitor correctness - Drop the bar out of the WS_EX_TOPMOST band (HWND_NOTOPMOST) before HWND_BOTTOM when hiding for fullscreen; restore with HWND_TOPMOST
- Add Rename... action to each profile row's options menu - Refactor the input dialog into a reusable prompt dialog helper - Renaming remaps any hotkey bound to the profile; refuses to overwrite an existing profile name
Author
|
Still testing and waiting feedback.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a new Monitor Profile widget that saves and switches between monitor layout profiles — monitor arrangements (active monitors, positions, resolution, refresh rate, orientation, primary) — similar to the classic "Monitor Profile Switcher" tool. Layouts are captured through the Windows CCD API (QueryDisplayConfig/SetDisplayConfig) and stored as JSON; applying a profile restores the exact arrangement via SetDisplayConfig.
Features
Profiles
Save current layout... — capture the live display configuration to a named profile (%USERPROFILE%.config\yasb\monitor_profiles<name>.json)
Apply — restore the saved arrangement; the label shows the matching profile name, or Custom when the current layout matches nothing saved
Options menu (⋯) per profile — Apply / Set Hotkey / Remove Hotkey / Delete
Two independently styleable card blocks in the popup (profiles + monitors) via .menu-block.profiles-block / .menu-block.monitors-block
Monitor control
Per-monitor disable (turn off without unplugging) and re-enable, via the same ⋯ menu
Disabled-but-connected monitors are detected via QDC_ALL_PATHS + targetAvailable
Section auto-hides when there's only one monitor and nothing disabled (toggle: menu.monitors_section)
Global hotkeys (runtime-registered)
Bind hotkeys to any profile or monitor toggle via a live key-capture dialog (press the combo — supports ctrl/alt/shift/win + letters, digits, F1–F24, named keys)
Hotkeys persist in hotkeys.json and register immediately, no restart; managed by a self-contained RuntimeHotkeyManager that runs its own RegisterHotKey thread and dispatches through the existing EventService → BaseWidget.handle_hotkey_event channel (no core modifications)
Conflicts handled: binding a combo twice unbinds the first action; keys owned by other apps log a warning
Implementation notes
core/utils/win32/bindings/display_config.py (new): ctypes prototypes for the CCD API (GetDisplayConfigBufferSizes, QueryDisplayConfig, SetDisplayConfig, DisplayConfigGetDeviceInfo) with full DISPLAYCONFIG* struct definitions — including the packed 16-bit modeInfoIdx unions used by QDC_VIRTUAL_MODE_AWARE
core/widgets/services/monitor_profile/ — display-config service (capture/apply/validate/enable-disable) + runtime hotkey manager. Key findings baked in: monitor disable works by applying a path-subset config; monitor re-enable requires QDC_DATABASE_CURRENT applied with plain SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_APPLY (other flag combos return ERROR_INVALID_PARAMETER)
Widget follows existing conventions (BaseWidget, Pydantic schema with extra="forbid", PopupWidget, QMenu + apply_qmenu_style for the ⋯ dropdowns, per-row class names for styling)
schema.json regenerated; docs page + sidebar + README widget table updated
Testing