Files
Panama/config/dot/quickshell/services/ShellState.qml
T
Gabriel Brown 9202697734 Introduce the desktop to somebody who has just met it
Thirty settings pages is the opposite of the usual problem: a person
arriving from GNOME, macOS or Windows cannot tell which few things
matter. This is those few, once, on the first start.

Not a tour. Nobody reads a tour, and a multi-step wizard on a desktop
somebody just installed is one more thing between them and using it.
One card, five keys, and a way out.

The chords come from the live keymap rather than being written here, so
a machine whose owner has already rebound something teaches what they
actually have. A welcome screen is the one surface read by somebody
with no way to tell it is wrong, which is exactly why it must not be.

Two deliberate departures from how every other surface behaves. It does
not close on a click outside, because a stray click in the first thirty
seconds would throw away the only explanation on offer. And dismissing
by any route marks it seen, Escape included, because a desktop that
reintroduces itself every login has failed to take no for an answer.
It stays reachable from the launcher afterwards, since the moment
somebody wants it again is exactly when a one-shot has thrown it away.

Also teaches the keymap to spell punctuation: slash, period, comma and
the rest were rendering as their raw keysym names, so the welcome
screen offered "Super + slash" and the cheatsheet agreed with it.
2026-08-22 01:00:18 -04:00

143 lines
5.8 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" | "cheatsheet" | "welcome"
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"
readonly property bool cheatsheetOpen: activeOverlay === "cheatsheet"
readonly property bool welcomeOpen: activeOverlay === "welcome"
// 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 = "";
}
// A section within the page, for pages that have tabs. Consumed once by the
// page and cleared, rather than bound to -- a binding would pin the tab and
// stop anyone changing it by hand once they arrived.
property string settingsSection: ""
function takeSettingsSection(): string {
const section = root.settingsSection;
root.settingsSection = "";
return section;
}
function openSettings(page: string): void {
root.settingsSection = "";
root.showSettings(page);
}
// Opening straight to a tab within a page. A separate function rather than a
// default argument: QML has no default parameter values, and writing one
// fails the whole configuration -- which takes the shell down with it.
function openSettingsSection(page: string, section: string): void {
root.settingsSection = section;
root.showSettings(page);
}
function showSettings(page: string): void {
const allowed = ["home", "appearance", "displays", "connectivity", "home-phone", "desktop", "sound", "gaming", "notifications", "screen-intelligence", "shortcuts", "mouse", "privacy", "region", "accounts", "accessibility", "power", "datetime", "applications", "updates", "storage", "snapshots", "users", "sharing", "firewall", "printers", "containers", "ssh-keys", "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
}