Files
kadence/desktop/kadence_desktop/qml/Main.qml
T
timka123 77b26757bf refactor: enhance TimerPage layout and functionality; update theme and tokens for improved design consistency
- Refactored TimerPage component to improve layout and user experience.
- Updated theme configuration to utilize design tokens for better maintainability.
- Introduced new tokens for colors and typography to align with design principles.
- Simplified state management and improved timer functionality.
- Enhanced accessibility and responsiveness of UI elements.
2026-10-18 23:57:36 +03:00

261 lines
9.8 KiB
QML

// The window.
//
// A quiet left column — wordmark, navigation as words, and the two things worth
// knowing from anywhere (what the timer is doing, whether the blocker is on) —
// with the pages on the right. Qt Quick Controls supply the behaviour; the
// chrome comes from `Theme`, so the desktop app and the web app are the same
// product rather than two takes on it.
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import "."
import "components"
ApplicationWindow {
id: window
width: 1180
height: 820
minimumWidth: 460
minimumHeight: 600
visible: false
color: Theme.paper
title: controller.running
? controller.clockText + " · " + controller.kindLabel + " — Kadence"
: "Kadence"
Material.theme: Theme.dark ? Material.Dark : Material.Light
Material.primary: Theme.ember
Material.accent: Theme.ember
Material.background: Theme.paper
Material.foreground: Theme.ink
readonly property bool wide: width >= 820
property int page: 0
onClosing: function (close) {
if (controller.config.close_to_tray && controller.config.tray_icon) {
close.accepted = false
window.hide()
}
}
Connections {
target: controller
function onToast(message) { snackbar.show(message) }
function onShowWindowRequested() { window.show(); window.raise(); window.requestActivate() }
}
Shortcut { sequence: "Space"; onActivated: if (controller.timerEnabled) controller.toggle() }
Shortcut { sequence: "Ctrl+Return"; onActivated: if (controller.timerEnabled) controller.complete() }
Shortcut { sequence: "Ctrl+E"; onActivated: if (controller.timerEnabled) controller.extend(5) }
Shortcut { sequence: "Ctrl+R"; onActivated: controller.refreshSources() }
Shortcut { sequence: "Ctrl+1"; onActivated: window.page = 0 }
Shortcut { sequence: "Ctrl+2"; onActivated: window.page = 1 }
Shortcut { sequence: "Ctrl+3"; onActivated: window.page = 2 }
Shortcut { sequence: "Ctrl+4"; onActivated: window.page = 3 }
Shortcut { sequence: "Ctrl+5"; onActivated: window.page = 4 }
Shortcut { sequence: "Ctrl+W"; onActivated: window.hide() }
Shortcut { sequence: "Ctrl+M"; onActivated: controller.toggleMini() }
RowLayout {
anchors.fill: parent
spacing: 0
// ---- the rail --------------------------------------------------------
Item {
visible: window.wide
Layout.fillHeight: true
Layout.preferredWidth: 212
Rectangle {
anchors.right: parent.right
width: 1
height: parent.height
color: Theme.rule
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 22
spacing: 2
ColumnLayout {
Layout.bottomMargin: 26
spacing: 0
Label {
text: "Kadence"
color: Theme.ink
font.pixelSize: 23
font.family: Theme.displayFamily
font.letterSpacing: -0.5
}
Label {
text: "find your rhythm"
color: Theme.muted
font.pixelSize: 11
font.family: Theme.sansFamily
}
}
Repeater {
model: ["Timer", "Tasks", "Blocker", "Report", "Settings"]
delegate: Item {
required property int index
required property string modelData
Layout.fillWidth: true
implicitHeight: 34
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2
height: 16
radius: 1
color: Theme.ember
visible: window.page === index
}
Label {
anchors.left: parent.left
anchors.leftMargin: 14
anchors.verticalCenter: parent.verticalCenter
text: modelData
color: window.page === index ? Theme.ink : (mouse.containsMouse ? Theme.ink : Theme.muted)
font.pixelSize: 14
font.weight: window.page === index ? Font.DemiBold : Font.Normal
font.family: Theme.sansFamily
}
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: window.page = index
}
}
}
Item { Layout.fillHeight: true }
Rectangle { Layout.fillWidth: true; height: 1; color: Theme.rule; Layout.bottomMargin: 10 }
RowLayout {
Layout.fillWidth: true
spacing: 8
Rectangle {
width: 7; height: 7; radius: 3.5
color: controller.running ? Theme.kindColor(controller.kind) : Theme.faint
SequentialAnimation on opacity {
running: controller.running && controller.status === "running"
loops: Animation.Infinite
NumberAnimation { to: 0.35; duration: 900 }
NumberAnimation { to: 1.0; duration: 900 }
}
}
Label {
text: controller.connected
? (controller.timerEnabled
? (controller.running ? controller.clockText : "Idle")
: "View mode")
: "Offline"
color: Theme.ink
font.pixelSize: 13
font.weight: Font.Medium
font.family: Theme.monoFamily
}
Item { Layout.fillWidth: true }
Label {
text: controller.completedToday + "/" + controller.goal
color: Theme.muted
font.pixelSize: 12
font.family: Theme.sansFamily
}
}
Label {
visible: controller.blockerActive
text: "● BLOCKING"
color: Theme.clay
font.pixelSize: 10
font.letterSpacing: 1.1
font.weight: Font.DemiBold
font.family: Theme.sansFamily
}
}
}
// ---- pages -----------------------------------------------------------
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 0
ToolBar {
visible: !window.wide
Layout.fillWidth: true
background: Rectangle {
color: Theme.paper
Rectangle { anchors.bottom: parent.bottom; width: parent.width; height: 1; color: Theme.rule }
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
Label {
text: "Kadence"
color: Theme.ink
font.pixelSize: 18
font.family: Theme.displayFamily
Layout.fillWidth: true
}
Label {
text: controller.connected
? (controller.timerEnabled
? (controller.running ? controller.clockText : "Idle")
: "View mode")
: "Offline"
color: Theme.ink
font.pixelSize: 13
font.family: Theme.monoFamily
}
}
}
StackLayout {
Layout.fillWidth: true
Layout.fillHeight: true
currentIndex: window.page
TimerPage {}
TasksPage {}
BlockerPage {}
ReportPage {}
SettingsPage {}
}
TabBar {
visible: !window.wide
Layout.fillWidth: true
currentIndex: window.page
onCurrentIndexChanged: window.page = currentIndex
background: Rectangle {
color: Theme.paper
Rectangle { width: parent.width; height: 1; color: Theme.rule }
}
TabButton { text: "Timer" }
TabButton { text: "Tasks" }
TabButton { text: "Blocker" }
TabButton { text: "Report" }
TabButton { text: "Settings" }
}
}
}
Snackbar { id: snackbar }
// The floating mini timer is created by app.py as its own top-level window:
// nested inside this one it would be transient, and a transient window
// cannot be shown while its parent is hidden — which is precisely when the
// mini timer earns its keep.
}