// The focus tab: the modes that quiet this machine, and the timed session that // is deliberately not one of them. // // Modes are conditions rather than alarms -- a mode is on because something is // true right now -- so the editor never asks anyone to schedule an event. It // asks what has to be true. Order is priority, which is why the list can be // reordered and says so. import QtQuick import qs.config import qs.services SettingsPage { id: root // One mode is open for editing at a time; the point is a card you can read, // not every mode unfolded at once. property string expandedMode: "" // The exception list belongs to the page rather than to the row: the // applications it is chosen from are the same universe the per-application // notification rules use, and the page is what reads it. function toggleAllowed(mode: var, appId: string, allowed: bool): void { const current = Array.isArray(mode.allow) ? mode.allow.map(String) : []; const at = current.indexOf(appId); if (allowed && at < 0) current.push(appId); else if (!allowed && at >= 0) current.splice(at, 1); FocusModes.update(String(mode.id), { allow: current }); } title: "Focus" lede: "Modes quiet this machine on their own terms — first matching mode wins, and the order below is the priority." SettingsCard { title: "Focus modes" subtitle: FocusModes.active ? FocusModes.activeName + " is on because " + FocusModes.activeReason + "." : "Drag a mode by its grip, or press up and down on it, to change which one wins." TextRow { visible: FocusModes.modes.length === 0 label: "No focus modes" detail: "Add one below and give it something to react to." divider: false } Repeater { model: FocusModes.modes delegate: FocusModeRow { id: modeRow required property var modelData width: parent.width mode: modeRow.modelData knownApps: Notifs.applications expanded: root.expandedMode === String(modeRow.modelData.id ?? "") running: FocusModes.activeMode?.id === String(modeRow.modelData.id ?? "") onActivated: root.expandedMode = modeRow.expanded ? "" : String(modeRow.modelData.id ?? "") onAllowToggled: (appId, allowed) => root.toggleAllowed(modeRow.modelData, appId, allowed) } } Item { width: parent.width height: 58 Rectangle { id: newMode anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter height: 44 radius: Theme.cardRadius color: Theme.alpha(Theme.fg, newModeHover.hovered ? 0.06 : 0.02) border.width: 1 border.color: Theme.alpha(Theme.fg, 0.16) Text { anchors.centerIn: parent text: "+ New focus mode" color: newModeHover.hovered ? Theme.fg : Theme.fgDim font.family: Theme.fontFamily font.pixelSize: Theme.fontSize font.weight: Font.Medium } HoverHandler { id: newModeHover cursorShape: Qt.PointingHandCursor } // Created open, so the first thing anyone sees is the editor // rather than a row named "New mode" with nothing to react to. TapHandler { onTapped: root.expandedMode = String(FocusModes.createMode("New mode")) } } } } SettingsCard { title: "Focus sessions" subtitle: "A timed, workspace-bound sprint — separate from modes, and it owns Do Not Disturb while it runs." SegmentRow { label: "Default duration" detail: "Used by Super+Shift+F and Quick Settings" options: [ { value: 25, label: "25 min" }, { value: 45, label: "45 min" }, { value: 60, label: "60 min" }, { value: 90, label: "90 min" } ] value: DesktopPreferences.get("focusDurationMinutes") onSelected: value => SystemSettings.commitPreference("focusDurationMinutes", value) } SettingRow { label: "Keep the machine awake" detail: Caffeine.enabled ? "The display will not blank or lock while this is on" : "Caffeine — outside of sessions and modes too" controlWidth: 48 SettingsToggle { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter checked: Caffeine.enabled onToggled: value => Caffeine.enabled = value } } SettingRow { label: FocusSession.active ? `Active on ${FocusSession.workspaceLabel}` : "No session running" detail: FocusSession.active ? `${FocusSession.remainingText} remaining` : "Start one without leaving Settings" divider: false controlWidth: 120 SettingsButton { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter text: FocusSession.active ? "Show controls" : "Start focus" tone: FocusSession.active ? "normal" : "accent" onClicked: FocusSession.reveal() } } } }