Working towards parity with GNOME Settings, which splits pointing devices into their own panel. Panama had pointer speed and focus-follows buried under a page called "Input & Shortcuts", and had nothing at all for scroll direction, acceleration profile, scroll speed, left-handed buttons, or any touchpad setting -- all of which could only be changed by editing hypr/input.lua by hand, which is the thing this app exists to stop. Every new mapping was read back off the running compositor rather than assumed, and two were not what they look like: touchpad drag lock is an int with three states, not a switch, and scroll factors are floats even at their default of exactly 1. Getting either wrong makes every write to that setting look rejected. The shape contract now covers 35 mapped options, up from 23. The touchpad card renders only when a touchpad is attached, which is what InputDevices is for. On a desktop it would be worse than useless: every switch on it would appear to work, because the preference is stored and Hyprland accepts an option for a device class it has no member of, so the settings would silently affect nothing. Keyboard layout was read-only text, justified by a note saying changes needed a compositor reload. That is not true in 0.56.2 -- setting input:kb_variant through hl.config re-keymaps attached keyboards immediately, verified by watching active_keymap on a real keyboard change to "English (US, intl., with dead keys)" and back. So layout, variant, and options are now real controls, joined by a TextEntryRow that commits on Enter or focus loss rather than per keystroke, since half a layout name is a valid string meaning something else. Rejected input is shown as rejected rather than sanitised: these strings are serialised into an hl.config payload, where stripping an unexpected character would turn a typo into a different working setting. Verified each new pointer option applies and reverts against the live compositor. Schema, search, commit/reset, and system contracts pass. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
117 lines
4.6 KiB
QML
117 lines
4.6 KiB
QML
pragma Singleton
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Shared UI state.
|
|
//
|
|
// Every overlay in the shell is mutually exclusive with the others -- opening
|
|
// the overview should close the quick settings, and so on. Centralising that
|
|
// here means no module needs a reference to any other module, and the IPC
|
|
// handlers in shell.qml have exactly one thing to talk to.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
import Quickshell
|
|
import QtQuick
|
|
import qs.config
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
// Exactly one of these may be non-empty at a time.
|
|
// "" | "overview" | "quicksettings" | "notifications" | "clipboard" | "capture" | "activity" | "powermenu"
|
|
property string activeOverlay: ""
|
|
|
|
readonly property bool overviewOpen: activeOverlay === "overview"
|
|
readonly property bool quickSettingsOpen: activeOverlay === "quicksettings"
|
|
readonly property bool notificationsOpen: activeOverlay === "notifications"
|
|
readonly property bool clipboardOpen: activeOverlay === "clipboard"
|
|
readonly property bool captureOpen: activeOverlay === "capture"
|
|
readonly property bool activityOpen: activeOverlay === "activity"
|
|
readonly property bool powerMenuOpen: activeOverlay === "powermenu"
|
|
|
|
// Settings is a normal application window rather than a transient overlay.
|
|
// It can stay open while Quick Settings or the notification center appears.
|
|
property bool settingsOpen: false
|
|
property string settingsPage: "home"
|
|
|
|
readonly property bool anyOverlayOpen: activeOverlay !== ""
|
|
|
|
// The date menu keeps calendar context fixed while its right pane switches
|
|
// between the schedule, persistent activity and message history.
|
|
property string dateMenuPage: "agenda"
|
|
|
|
// 0 means "use the currently focused workspace". A positive value lets a
|
|
// contextual surface, such as Focus, open Mission Control at its target.
|
|
property int overviewWorkspaceId: 0
|
|
property string overviewQuery: ""
|
|
|
|
function toggle(name: string): void {
|
|
root.activeOverlay = (root.activeOverlay === name) ? "" : name;
|
|
}
|
|
|
|
function open(name: string): void {
|
|
root.activeOverlay = name;
|
|
}
|
|
|
|
function openDateMenu(page: string): void {
|
|
root.dateMenuPage = root.normalizedDateMenuPage(page);
|
|
root.activeOverlay = "notifications";
|
|
}
|
|
|
|
function toggleDateMenu(page: string): void {
|
|
const target = root.normalizedDateMenuPage(page);
|
|
if (root.activeOverlay === "notifications" && root.dateMenuPage === target) {
|
|
root.activeOverlay = "";
|
|
return;
|
|
}
|
|
root.dateMenuPage = target;
|
|
root.activeOverlay = "notifications";
|
|
}
|
|
|
|
function normalizedDateMenuPage(page: string): string {
|
|
return ["agenda", "ongoing", "notifications"].indexOf(page) >= 0 ? page : "agenda";
|
|
}
|
|
|
|
function openOverview(workspaceId: int): void {
|
|
root.overviewWorkspaceId = Math.max(0, workspaceId);
|
|
root.overviewQuery = "";
|
|
root.activeOverlay = "overview";
|
|
}
|
|
|
|
function searchOverview(query: string): void {
|
|
root.overviewWorkspaceId = 0;
|
|
root.overviewQuery = query;
|
|
root.activeOverlay = "overview";
|
|
}
|
|
|
|
function close(): void {
|
|
if (root.activeOverlay === "overview") {
|
|
root.overviewWorkspaceId = 0;
|
|
root.overviewQuery = "";
|
|
}
|
|
root.activeOverlay = "";
|
|
}
|
|
|
|
function openSettings(page: string): void {
|
|
const allowed = ["home", "appearance", "displays", "connectivity", "home-phone", "desktop", "sound", "notifications", "screen-intelligence", "shortcuts", "mouse", "accessibility", "power", "datetime", "applications", "services", "about"];
|
|
root.settingsPage = allowed.indexOf(page) >= 0 ? page : "home";
|
|
DesktopPreferences.set("lastPage", root.settingsPage);
|
|
root.settingsOpen = true;
|
|
}
|
|
|
|
function toggleSettings(): void {
|
|
if (root.settingsOpen) {
|
|
root.closeSettings();
|
|
return;
|
|
}
|
|
root.openSettings(DesktopPreferences.get("lastPage") || "home");
|
|
}
|
|
|
|
function closeSettings(): void {
|
|
root.settingsOpen = false;
|
|
}
|
|
|
|
// Set by Dock.qml so the bar can avoid fighting it for pointer grabs, and
|
|
// read by the capture overlay so the dock isn't in the screenshot.
|
|
property bool dockRevealed: false
|
|
}
|