Add the four components the Focus commit referenced but forgot to ship
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user