diff --git a/components/widgets/WavyTopRect.qml b/components/widgets/WavyTopRect.qml new file mode 100644 index 00000000..4d42e7b7 --- /dev/null +++ b/components/widgets/WavyTopRect.qml @@ -0,0 +1,47 @@ +import QtQuick +import QtQuick.Shapes +import qs.components +import qs.services + +Shape { + id: root + + property color color: Colours.palette.m3surfaceContainer + property int waves: 4 + property real amplitude: 3 + + readonly property real waveHeight: amplitude * 2 + + preferredRendererType: Shape.CurveRenderer + asynchronous: true + + ShapePath { + strokeWidth: 0 + strokeColor: "transparent" + fillColor: root.color + + PathSvg { + path: { + const w = root.width; + const h = root.height; + const a = root.amplitude; + const n = Math.max(1, root.waves); + const wl = w / n; + const half = wl / 2; + + let d = `M 0,${a} `; + for (let i = 0; i < n; ++i) { + const x = i * wl; + d += `Q ${x + half / 2},${-a} ${x + half},${a} `; + d += `Q ${x + half + half / 2},${3 * a} ${x + wl},${a} `; + } + d += `L ${w},${h} L 0,${h} Z`; + return d; + } + } + + Behavior on fillColor { + CAnim {} + } + } +} diff --git a/modules/lock/Content.qml b/modules/lock/Content.qml index d4e50bff..50dd0027 100644 --- a/modules/lock/Content.qml +++ b/modules/lock/Content.qml @@ -44,17 +44,8 @@ RowLayout { Layout.fillWidth: true spacing: Tokens.spacing.medium - StyledRect { + Resources { Layout.fillWidth: true - implicitHeight: resources.implicitHeight - - topRightRadius: Tokens.rounding.extraLarge - radius: Tokens.rounding.medium - color: Colours.tPalette.m3surfaceContainer - - Resources { - id: resources - } } StyledRect { diff --git a/modules/lock/Resources.qml b/modules/lock/Resources.qml index 093c6dab..caa39e5b 100644 --- a/modules/lock/Resources.qml +++ b/modules/lock/Resources.qml @@ -1,22 +1,26 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts +import M3Shapes import Caelestia.Config import Caelestia.Services import qs.components -import qs.components.controls +import qs.components.effects +import qs.components.widgets import qs.services -GridLayout { +StyledRect { id: root - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: Tokens.padding.large + readonly property real fontScale: { + const diff = width / 391 - 1; // 391 is the width at 1080 height screen + return 1 + Math.pow(Math.abs(diff), 0.8) * Math.sign(diff); + } - rowSpacing: Tokens.spacing.largeIncreased - columnSpacing: Tokens.spacing.largeIncreased - rows: 2 - columns: 2 + implicitHeight: layout.implicitHeight + layout.anchors.margins * 2 + radius: Tokens.rounding.extraLarge + color: Colours.tPalette.m3surfaceContainer ServiceRef { service: Cpu @@ -30,71 +34,150 @@ GridLayout { service: Storage } - Resource { - Layout.topMargin: Tokens.padding.large - icon: "memory" - value: Cpu.percentage - colour: Colours.palette.m3primary + RowLayout { + id: layout + + anchors.fill: parent + anchors.margins: Tokens.padding.large + spacing: Tokens.spacing.large + + Resource { + id: cpu + + icon: "memory" + value: Math.round(Cpu.percentage * 100) + "%" + fillValue: Cpu.percentage + colour: Colours.palette.m3primary + shapeColour: Colours.palette.m3primaryContainer + fillColour: Qt.alpha(Colours.palette.m3secondary, 0.3) + shape: MaterialShape.Pentagon + + MaterialShape { + x: cpu.mShape.pointAtAngle(45).x - implicitSize / 2 + Tokens.padding.medium + y: cpu.mShape.pointAtAngle(45).y - implicitSize / 2 + + shape: Cpu.temperature > 90 ? MaterialShape.SoftBurst : MaterialShape.Circle + color: Cpu.temperature > 90 ? Colours.palette.m3errorContainer : Colours.palette.m3secondaryContainer + implicitSize: { + const size = Math.round(tempLabel.implicitHeight * 2); + return size % 2 === 0 ? size : size + 1; // Ensure even size so center works properly + } + + Behavior on color { + CAnim {} + } + + StyledText { + id: tempLabel + + anchors.centerIn: parent + anchors.verticalCenterOffset: Math.round(fontInfo.pointSize * 0.04) + + text: { + const temp = Cpu.temperature; + const useF = GlobalConfig.services.useFahrenheitPerformance; + return `${Math.ceil(useF ? temp * 1.8 + 32 : temp)}°${useF ? "F" : "C"}`; + } + color: Cpu.temperature > 90 ? Colours.palette.m3onErrorContainer : Colours.palette.m3secondary + font: Tokens.font.title.builders.medium.scale(cpu.width / 112).width(50).build() + } + } + } + + Resource { + icon: "memory_alt" + value: Math.round(Memory.percentage * 100) + "%" + fillValue: Memory.percentage + colour: Colours.palette.m3tertiary + shapeColour: Colours.palette.m3onTertiary + fillColour: Qt.alpha(Colours.palette.m3tertiary, 0.3) + shape: MaterialShape.Slanted + } + + Resource { + icon: "hard_disk" + value: Math.round(Storage.percentage * 100) + "%" + fillValue: Storage.percentage + colour: Colours.palette.m3secondary + shapeColour: Colours.palette.m3secondaryContainer + fillColour: Qt.alpha(Colours.palette.m3secondary, 0.4) + shape: MaterialShape.Gem + } } - Resource { - Layout.topMargin: Tokens.padding.large - icon: "thermostat" - value: Math.min(1, Cpu.temperature / 90) - colour: Colours.palette.m3secondary - } - - Resource { - Layout.bottomMargin: Tokens.padding.large - icon: "memory_alt" - value: Memory.percentage - colour: Colours.palette.m3secondary - } - - Resource { - Layout.bottomMargin: Tokens.padding.large - icon: "hard_disk" - value: Storage.percentage - colour: Colours.palette.m3tertiary - } - - component Resource: StyledRect { + component Resource: Item { id: res required property string icon - required property real value + required property string value required property color colour + required property color shapeColour + property color fillColour + property real fillValue: -1 + property alias shape: shape.shape + readonly property alias mShape: shape Layout.fillWidth: true implicitHeight: width - color: Colours.layer(Colours.palette.m3surfaceContainerHigh, 2) - radius: Tokens.rounding.extraLarge - - CircularProgress { - id: circ - - anchors.fill: parent - value: res.value - padding: Tokens.padding.extraExtraLarge - fgColour: res.colour - bgColour: Colours.layer(Colours.palette.m3surfaceContainerHighest, 3) - strokeWidth: width < 200 ? Tokens.padding.small : Tokens.padding.medium + Behavior on shapeColour { + CAnim {} } - MaterialIcon { - id: icon + MaterialShape { + id: shape - anchors.centerIn: parent - text: res.icon - color: res.colour - fontStyle: Tokens.font.icon.size((circ.arcRadius * 0.7) || 1).weight(Font.DemiBold).build() + implicitSize: res.width + color: Qt.alpha(res.shapeColour, 1) + opacity: res.shapeColour.a + layer.enabled: true } - Behavior on value { - Anim { - type: Anim.StandardLarge + Loader { + id: fillLoader + + anchors.fill: shape + active: res.fillValue >= 0 + asynchronous: true + + layer.enabled: active + layer.effect: Mask { + maskSource: shape } + + sourceComponent: Item { + WavyTopRect { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + implicitHeight: shape.implicitSize * res.fillValue + color: res.fillColour + } + } + } + + ColumnLayout { + anchors.centerIn: parent + spacing: -Tokens.spacing.extraSmall + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: res.icon + color: Colours.palette.m3secondary + fontStyle: Tokens.font.icon.builders.medium.scale(root.fontScale).build() + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: res.value + color: res.colour + font: Tokens.font.headline.builders.large.scale(root.fontScale).width(50).build() + } + } + + Behavior on fillValue { + Anim {} } } }