From c5381c5194adf97c240acb98eb4c4c950633b325 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:45:15 +1000 Subject: internal: refactor widgets folder Split into subdirs and rename to components --- components/MaterialIcon.qml | 16 ++ components/StateLayer.qml | 103 ++++++++++ components/StyledClippingRect.qml | 17 ++ components/StyledRect.qml | 16 ++ components/StyledText.qml | 52 +++++ components/containers/StyledFlickable.qml | 17 ++ components/containers/StyledListView.qml | 17 ++ components/containers/StyledWindow.qml | 11 + components/controls/CustomMouseArea.qml | 21 ++ components/controls/CustomSpinBox.qml | 108 ++++++++++ components/controls/StyledBusyIndicator.qml | 90 +++++++++ components/controls/StyledScrollBar.qml | 36 ++++ components/controls/StyledSwitch.qml | 160 +++++++++++++++ components/controls/StyledTextField.qml | 86 ++++++++ components/controls/VerticalSlider.qml | 137 +++++++++++++ components/effects/Colouriser.qml | 16 ++ components/effects/Elevation.qml | 22 ++ components/effects/InnerBorder.qml | 44 ++++ components/filedialog/CurrentItem.qml | 107 ++++++++++ components/filedialog/DialogButtons.qml | 93 +++++++++ components/filedialog/FileDialog.qml | 106 ++++++++++ components/filedialog/FolderContents.qml | 224 +++++++++++++++++++++ components/filedialog/HeaderBar.qml | 142 +++++++++++++ components/filedialog/Sidebar.qml | 117 +++++++++++ components/filedialog/Sizes.qml | 8 + components/images/CachingIconImage.qml | 31 +++ components/images/CachingImage.qml | 41 ++++ components/misc/CustomShortcut.qml | 5 + components/misc/Ref.qml | 9 + components/widgets/ExtraIndicator.qml | 54 +++++ modules/Shortcuts.qml | 2 +- modules/areapicker/AreaPicker.qml | 3 +- modules/areapicker/Picker.qml | 2 +- modules/background/Background.qml | 2 +- modules/background/DesktopClock.qml | 2 +- modules/background/Wallpaper.qml | 5 +- modules/bar/Bar.qml | 3 +- modules/bar/components/ActiveWindow.qml | 3 +- modules/bar/components/Clock.qml | 2 +- modules/bar/components/OsIcon.qml | 2 +- modules/bar/components/Power.qml | 2 +- modules/bar/components/StatusIcons.qml | 2 +- .../bar/components/workspaces/ActiveIndicator.qml | 3 +- modules/bar/components/workspaces/OccupiedBg.qml | 2 +- modules/bar/components/workspaces/Workspace.qml | 2 +- modules/bar/components/workspaces/Workspaces.qml | 1 - modules/bar/popouts/ActiveWindow.qml | 2 +- modules/bar/popouts/Audio.qml | 3 +- modules/bar/popouts/Battery.qml | 2 +- modules/bar/popouts/Bluetooth.qml | 3 +- modules/bar/popouts/Network.qml | 3 +- modules/bar/popouts/TrayMenu.qml | 2 +- modules/dashboard/Dash.qml | 2 +- modules/dashboard/Media.qml | 4 +- modules/dashboard/Performance.qml | 3 +- modules/dashboard/Tabs.qml | 3 +- modules/dashboard/Wrapper.qml | 2 +- modules/dashboard/dash/Calendar.qml | 4 +- modules/dashboard/dash/DateTime.qml | 2 +- modules/dashboard/dash/Media.qml | 2 +- modules/dashboard/dash/Resources.qml | 3 +- modules/dashboard/dash/User.qml | 4 +- modules/dashboard/dash/Weather.qml | 2 +- modules/detachedcontent/DetachedContent.qml | 3 +- modules/detachedcontent/NavRail.qml | 2 +- modules/detachedcontent/Panes.qml | 3 +- modules/detachedcontent/bluetooth/BtPane.qml | 3 +- modules/detachedcontent/bluetooth/Details.qml | 5 +- modules/detachedcontent/bluetooth/DeviceList.qml | 4 +- modules/detachedcontent/bluetooth/Settings.qml | 4 +- modules/drawers/Border.qml | 2 +- modules/drawers/Drawers.qml | 3 +- modules/drawers/Exclusions.qml | 2 +- modules/launcher/AppList.qml | 4 +- modules/launcher/Content.qml | 3 +- modules/launcher/ContentList.qml | 2 +- modules/launcher/items/ActionItem.qml | 2 +- modules/launcher/items/AppItem.qml | 2 +- modules/launcher/items/CalcItem.qml | 2 +- modules/launcher/items/SchemeItem.qml | 2 +- modules/launcher/items/VariantItem.qml | 2 +- modules/launcher/items/WallpaperItem.qml | 4 +- modules/lock/Backgrounds.qml | 2 +- modules/lock/Buttons.qml | 2 +- modules/lock/Clock.qml | 2 +- modules/lock/Input.qml | 3 +- modules/lock/Lock.qml | 2 +- modules/lock/MediaPlaying.qml | 2 +- modules/lock/Notification.qml | 3 +- modules/lock/Status.qml | 3 +- modules/lock/WeatherInfo.qml | 2 +- modules/notifications/Content.qml | 3 +- modules/notifications/Notification.qml | 67 +++--- modules/osd/Content.qml | 2 +- modules/session/Content.qml | 2 +- modules/utilities/Content.qml | 2 +- modules/windowinfo/Buttons.qml | 2 +- modules/windowinfo/Details.qml | 2 +- modules/windowinfo/Preview.qml | 2 +- modules/windowinfo/WindowInfo.qml | 2 +- services/Brightness.qml | 2 +- services/Notifs.qml | 2 +- services/Players.qml | 2 +- widgets/CachingIconImage.qml | 31 --- widgets/CachingImage.qml | 41 ---- widgets/Colouriser.qml | 16 -- widgets/CustomMouseArea.qml | 21 -- widgets/CustomShortcut.qml | 5 - widgets/CustomSpinBox.qml | 107 ---------- widgets/Elevation.qml | 22 -- widgets/ExtraIndicator.qml | 52 ----- widgets/InnerBorder.qml | 43 ---- widgets/MaterialIcon.qml | 16 -- widgets/Ref.qml | 9 - widgets/StateLayer.qml | 104 ---------- widgets/StyledBusyIndicator.qml | 90 --------- widgets/StyledClippingRect.qml | 17 -- widgets/StyledFlickable.qml | 17 -- widgets/StyledListView.qml | 17 -- widgets/StyledRect.qml | 16 -- widgets/StyledScrollBar.qml | 35 ---- widgets/StyledSwitch.qml | 159 --------------- widgets/StyledText.qml | 52 ----- widgets/StyledTextField.qml | 85 -------- widgets/StyledWindow.qml | 11 - widgets/VerticalSlider.qml | 136 ------------- widgets/filedialog/CurrentItem.qml | 107 ---------- widgets/filedialog/DialogButtons.qml | 93 --------- widgets/filedialog/FileDialog.qml | 106 ---------- widgets/filedialog/FolderContents.qml | 222 -------------------- widgets/filedialog/HeaderBar.qml | 142 ------------- widgets/filedialog/Sidebar.qml | 117 ----------- widgets/filedialog/Sizes.qml | 8 - 133 files changed, 2047 insertions(+), 2005 deletions(-) create mode 100644 components/MaterialIcon.qml create mode 100644 components/StateLayer.qml create mode 100644 components/StyledClippingRect.qml create mode 100644 components/StyledRect.qml create mode 100644 components/StyledText.qml create mode 100644 components/containers/StyledFlickable.qml create mode 100644 components/containers/StyledListView.qml create mode 100644 components/containers/StyledWindow.qml create mode 100644 components/controls/CustomMouseArea.qml create mode 100644 components/controls/CustomSpinBox.qml create mode 100644 components/controls/StyledBusyIndicator.qml create mode 100644 components/controls/StyledScrollBar.qml create mode 100644 components/controls/StyledSwitch.qml create mode 100644 components/controls/StyledTextField.qml create mode 100644 components/controls/VerticalSlider.qml create mode 100644 components/effects/Colouriser.qml create mode 100644 components/effects/Elevation.qml create mode 100644 components/effects/InnerBorder.qml create mode 100644 components/filedialog/CurrentItem.qml create mode 100644 components/filedialog/DialogButtons.qml create mode 100644 components/filedialog/FileDialog.qml create mode 100644 components/filedialog/FolderContents.qml create mode 100644 components/filedialog/HeaderBar.qml create mode 100644 components/filedialog/Sidebar.qml create mode 100644 components/filedialog/Sizes.qml create mode 100644 components/images/CachingIconImage.qml create mode 100644 components/images/CachingImage.qml create mode 100644 components/misc/CustomShortcut.qml create mode 100644 components/misc/Ref.qml create mode 100644 components/widgets/ExtraIndicator.qml delete mode 100644 widgets/CachingIconImage.qml delete mode 100644 widgets/CachingImage.qml delete mode 100644 widgets/Colouriser.qml delete mode 100644 widgets/CustomMouseArea.qml delete mode 100644 widgets/CustomShortcut.qml delete mode 100644 widgets/CustomSpinBox.qml delete mode 100644 widgets/Elevation.qml delete mode 100644 widgets/ExtraIndicator.qml delete mode 100644 widgets/InnerBorder.qml delete mode 100644 widgets/MaterialIcon.qml delete mode 100644 widgets/Ref.qml delete mode 100644 widgets/StateLayer.qml delete mode 100644 widgets/StyledBusyIndicator.qml delete mode 100644 widgets/StyledClippingRect.qml delete mode 100644 widgets/StyledFlickable.qml delete mode 100644 widgets/StyledListView.qml delete mode 100644 widgets/StyledRect.qml delete mode 100644 widgets/StyledScrollBar.qml delete mode 100644 widgets/StyledSwitch.qml delete mode 100644 widgets/StyledText.qml delete mode 100644 widgets/StyledTextField.qml delete mode 100644 widgets/StyledWindow.qml delete mode 100644 widgets/VerticalSlider.qml delete mode 100644 widgets/filedialog/CurrentItem.qml delete mode 100644 widgets/filedialog/DialogButtons.qml delete mode 100644 widgets/filedialog/FileDialog.qml delete mode 100644 widgets/filedialog/FolderContents.qml delete mode 100644 widgets/filedialog/HeaderBar.qml delete mode 100644 widgets/filedialog/Sidebar.qml delete mode 100644 widgets/filedialog/Sizes.qml diff --git a/components/MaterialIcon.qml b/components/MaterialIcon.qml new file mode 100644 index 0000000..a1d19d3 --- /dev/null +++ b/components/MaterialIcon.qml @@ -0,0 +1,16 @@ +import qs.services +import qs.config + +StyledText { + property real fill + property int grade: Colours.light ? 0 : -25 + + font.family: Appearance.font.family.material + font.pointSize: Appearance.font.size.larger + font.variableAxes: ({ + FILL: fill.toFixed(1), + GRAD: grade, + opsz: fontInfo.pixelSize, + wght: fontInfo.weight + }) +} diff --git a/components/StateLayer.qml b/components/StateLayer.qml new file mode 100644 index 0000000..da30217 --- /dev/null +++ b/components/StateLayer.qml @@ -0,0 +1,103 @@ +import qs.services +import qs.config +import QtQuick + +MouseArea { + id: root + + property bool disabled + property color color: Colours.palette.m3onSurface + property real radius: parent?.radius ?? 0 + + function onClicked(): void { + } + + anchors.fill: parent + + enabled: !disabled + cursorShape: disabled ? undefined : Qt.PointingHandCursor + hoverEnabled: true + + onPressed: event => { + if (disabled) + return; + + rippleAnim.x = event.x; + rippleAnim.y = event.y; + + const dist = (ox, oy) => ox * ox + oy * oy; + rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y))); + + rippleAnim.restart(); + } + + onClicked: event => !disabled && onClicked(event) + + SequentialAnimation { + id: rippleAnim + + property real x + property real y + property real radius + + PropertyAction { + target: ripple + property: "x" + value: rippleAnim.x + } + PropertyAction { + target: ripple + property: "y" + value: rippleAnim.y + } + PropertyAction { + target: ripple + property: "opacity" + value: 0.08 + } + Anim { + target: ripple + properties: "implicitWidth,implicitHeight" + from: 0 + to: rippleAnim.radius * 2 + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: ripple + property: "opacity" + to: 0 + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + StyledClippingRect { + id: hoverLayer + + anchors.fill: parent + + color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0) + radius: root.radius + + StyledRect { + id: ripple + + radius: Appearance.rounding.full + color: root.color + opacity: 0 + + transform: Translate { + x: -ripple.width / 2 + y: -ripple.height / 2 + } + } + } + + component Anim: NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } +} diff --git a/components/StyledClippingRect.qml b/components/StyledClippingRect.qml new file mode 100644 index 0000000..8e15c4c --- /dev/null +++ b/components/StyledClippingRect.qml @@ -0,0 +1,17 @@ +import qs.config +import Quickshell.Widgets +import QtQuick + +ClippingRectangle { + id: root + + color: "transparent" + + Behavior on color { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/StyledRect.qml b/components/StyledRect.qml new file mode 100644 index 0000000..c052b2a --- /dev/null +++ b/components/StyledRect.qml @@ -0,0 +1,16 @@ +import qs.config +import QtQuick + +Rectangle { + id: root + + color: "transparent" + + Behavior on color { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/StyledText.qml b/components/StyledText.qml new file mode 100644 index 0000000..554c20c --- /dev/null +++ b/components/StyledText.qml @@ -0,0 +1,52 @@ +pragma ComponentBehavior: Bound + +import qs.services +import qs.config +import QtQuick + +Text { + id: root + + property bool animate: false + property string animateProp: "scale" + property real animateFrom: 0 + property real animateTo: 1 + property int animateDuration: Appearance.anim.durations.normal + + renderType: Text.NativeRendering + textFormat: Text.PlainText + color: Colours.palette.m3onSurface + font.family: Appearance.font.family.sans + font.pointSize: Appearance.font.size.smaller + + Behavior on color { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + Behavior on text { + enabled: root.animate + + SequentialAnimation { + Anim { + to: root.animateFrom + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + PropertyAction {} + Anim { + to: root.animateTo + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + + component Anim: NumberAnimation { + target: root + property: root.animateProp + duration: root.animateDuration / 2 + easing.type: Easing.BezierSpline + } +} diff --git a/components/containers/StyledFlickable.qml b/components/containers/StyledFlickable.qml new file mode 100644 index 0000000..7b2a75e --- /dev/null +++ b/components/containers/StyledFlickable.qml @@ -0,0 +1,17 @@ +import qs.config +import QtQuick + +Flickable { + id: root + + maximumFlickVelocity: 3000 + + rebound: Transition { + NumberAnimation { + properties: "x,y" + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/containers/StyledListView.qml b/components/containers/StyledListView.qml new file mode 100644 index 0000000..b59eca8 --- /dev/null +++ b/components/containers/StyledListView.qml @@ -0,0 +1,17 @@ +import qs.config +import QtQuick + +ListView { + id: root + + maximumFlickVelocity: 3000 + + rebound: Transition { + NumberAnimation { + properties: "x,y" + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/containers/StyledWindow.qml b/components/containers/StyledWindow.qml new file mode 100644 index 0000000..3b402b5 --- /dev/null +++ b/components/containers/StyledWindow.qml @@ -0,0 +1,11 @@ +import qs.utils +import qs.config +import Quickshell +import Quickshell.Wayland + +PanelWindow { + required property string name + + WlrLayershell.namespace: `caelestia-${name}` + color: "transparent" +} diff --git a/components/controls/CustomMouseArea.qml b/components/controls/CustomMouseArea.qml new file mode 100644 index 0000000..7c973c2 --- /dev/null +++ b/components/controls/CustomMouseArea.qml @@ -0,0 +1,21 @@ +import QtQuick + +MouseArea { + property int scrollAccumulatedY: 0 + + function onWheel(event: WheelEvent): void { + } + + onWheel: event => { + // Update accumulated scroll + if (Math.sign(event.angleDelta.y) !== Math.sign(scrollAccumulatedY)) + scrollAccumulatedY = 0; + scrollAccumulatedY += event.angleDelta.y; + + // Trigger handler and reset if above threshold + if (Math.abs(scrollAccumulatedY) >= 120) { + onWheel(event); + scrollAccumulatedY = 0; + } + } +} diff --git a/components/controls/CustomSpinBox.qml b/components/controls/CustomSpinBox.qml new file mode 100644 index 0000000..4611bed --- /dev/null +++ b/components/controls/CustomSpinBox.qml @@ -0,0 +1,108 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + property int value + property real max: Infinity + property real min: -Infinity + property alias repeatRate: timer.interval + + signal valueModified(value: int) + + spacing: Appearance.spacing.small + + StyledTextField { + inputMethodHints: Qt.ImhFormattedNumbersOnly + text: root.value + onAccepted: root.valueModified(text) + + padding: Appearance.padding.small + leftPadding: Appearance.padding.normal + rightPadding: Appearance.padding.normal + + background: StyledRect { + implicitWidth: 100 + radius: Appearance.rounding.small + color: Colours.palette.m3surfaceContainerHigh + } + } + + StyledRect { + radius: Appearance.rounding.small + color: Colours.palette.m3primary + + implicitWidth: implicitHeight + implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + id: upState + + color: Colours.palette.m3onPrimary + + onPressAndHold: timer.start() + onReleased: timer.stop() + + function onClicked(): void { + root.valueModified(Math.min(root.max, root.value + 1)); + } + } + + MaterialIcon { + id: upIcon + + anchors.centerIn: parent + text: "keyboard_arrow_up" + color: Colours.palette.m3onPrimary + } + } + + StyledRect { + radius: Appearance.rounding.small + color: Colours.palette.m3primary + + implicitWidth: implicitHeight + implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + id: downState + + color: Colours.palette.m3onPrimary + + onPressAndHold: timer.start() + onReleased: timer.stop() + + function onClicked(): void { + root.valueModified(Math.max(root.min, root.value - 1)); + } + } + + MaterialIcon { + id: downIcon + + anchors.centerIn: parent + text: "keyboard_arrow_down" + color: Colours.palette.m3onPrimary + } + } + + Timer { + id: timer + + interval: 100 + repeat: true + triggeredOnStart: true + onTriggered: { + if (upState.pressed) + upState.onClicked(); + else if (downState.pressed) + downState.onClicked(); + } + } +} diff --git a/components/controls/StyledBusyIndicator.qml b/components/controls/StyledBusyIndicator.qml new file mode 100644 index 0000000..060870f --- /dev/null +++ b/components/controls/StyledBusyIndicator.qml @@ -0,0 +1,90 @@ +pragma ComponentBehavior: Bound + +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls +import QtQuick.Shapes + +BusyIndicator { + id: root + + property color fgColour: Colours.palette.m3onPrimaryContainer + property color bgColour: Colours.palette.m3primaryContainer + + background: null + + contentItem: Shape { + id: shape + + preferredRendererType: Shape.CurveRenderer + asynchronous: true + + RotationAnimator on rotation { + from: 0 + to: 180 + running: root.visible && root.running + loops: Animation.Infinite + duration: Appearance.anim.durations.extraLarge + easing.type: Easing.Linear + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + + ShapePath { + strokeWidth: Math.min(root.implicitWidth, root.implicitHeight) * 0.18 + strokeColor: root.bgColour + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + PathAngleArc { + centerX: shape.width / 2 + centerY: shape.height / 2 + radiusX: root.implicitWidth / 2 + radiusY: root.implicitHeight / 2 + startAngle: 0 + sweepAngle: 360 + } + + Behavior on strokeColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + ShapePath { + strokeWidth: Math.min(root.implicitWidth, root.implicitHeight) * 0.18 + strokeColor: root.fgColour + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + PathAngleArc { + centerX: shape.width / 2 + centerY: shape.height / 2 + radiusX: root.implicitWidth / 2 + radiusY: root.implicitHeight / 2 + startAngle: -sweepAngle / 2 + sweepAngle: 60 + } + + PathAngleArc { + centerX: shape.width / 2 + centerY: shape.height / 2 + radiusX: root.implicitWidth / 2 + radiusY: root.implicitHeight / 2 + startAngle: 180 - sweepAngle / 2 + sweepAngle: 60 + } + + Behavior on strokeColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + } +} diff --git a/components/controls/StyledScrollBar.qml b/components/controls/StyledScrollBar.qml new file mode 100644 index 0000000..61ddc6d --- /dev/null +++ b/components/controls/StyledScrollBar.qml @@ -0,0 +1,36 @@ +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls + +ScrollBar { + id: root + + contentItem: StyledRect { + implicitWidth: 6 + opacity: root.pressed ? 1 : root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1) ? 0.8 : 0 + radius: Appearance.rounding.full + color: Colours.palette.m3secondary + + Behavior on opacity { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + CustomMouseArea { + z: -1 + anchors.fill: parent + + function onWheel(event: WheelEvent): void { + if (event.angleDelta.y > 0) + root.decrease(); + else if (event.angleDelta.y < 0) + root.increase(); + } + } +} diff --git a/components/controls/StyledSwitch.qml b/components/controls/StyledSwitch.qml new file mode 100644 index 0000000..c9d7330 --- /dev/null +++ b/components/controls/StyledSwitch.qml @@ -0,0 +1,160 @@ +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls +import QtQuick.Shapes + +Switch { + id: root + + implicitWidth: implicitIndicatorWidth + implicitHeight: implicitIndicatorHeight + + indicator: StyledRect { + radius: Appearance.rounding.full + color: root.checked ? Colours.palette.m3primary : Colours.palette.m3surfaceContainerHighest + + implicitWidth: implicitHeight * 1.7 + implicitHeight: Appearance.font.size.normal + Appearance.padding.smaller * 2 + + StyledRect { + readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight + + radius: Appearance.rounding.full + color: root.checked ? Colours.palette.m3onPrimary : Colours.palette.m3outline + + x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.small / 2 : Appearance.padding.small / 2 + implicitWidth: nonAnimWidth + implicitHeight: parent.implicitHeight - Appearance.padding.small + anchors.verticalCenter: parent.verticalCenter + + StyledRect { + anchors.fill: parent + radius: parent.radius + + color: root.checked ? Colours.palette.m3primary : Colours.palette.m3onSurface + opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0 + + Behavior on opacity { + NumberAnim {} + } + } + + Shape { + id: icon + + property point start1: { + if (root.pressed) + return Qt.point(width * 0.2, height / 2); + if (root.checked) + return Qt.point(width * 0.15, height / 2); + return Qt.point(width * 0.15, height * 0.15); + } + property point end1: { + if (root.pressed) { + if (root.checked) + return Qt.point(width * 0.4, height / 2); + return Qt.point(width * 0.8, height / 2); + } + if (root.checked) + return Qt.point(width * 0.4, height * 0.7); + return Qt.point(width * 0.85, height * 0.85); + } + property point start2: { + if (root.pressed) { + if (root.checked) + return Qt.point(width * 0.4, height / 2); + return Qt.point(width * 0.2, height / 2); + } + if (root.checked) + return Qt.point(width * 0.4, height * 0.7); + return Qt.point(width * 0.15, height * 0.85); + } + property point end2: { + if (root.pressed) + return Qt.point(width * 0.8, height / 2); + if (root.checked) + return Qt.point(width * 0.85, height * 0.2); + return Qt.point(width * 0.85, height * 0.15); + } + + anchors.centerIn: parent + width: height + height: parent.implicitHeight - Appearance.padding.small * 2 + preferredRendererType: Shape.CurveRenderer + asynchronous: true + + ShapePath { + strokeWidth: Appearance.font.size.larger * 0.15 + strokeColor: root.checked ? Colours.palette.m3primary : Colours.palette.m3surfaceContainerHighest + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + startX: icon.start1.x + startY: icon.start1.y + + PathLine { + x: icon.end1.x + y: icon.end1.y + } + PathMove { + x: icon.start2.x + y: icon.start2.y + } + PathLine { + x: icon.end2.x + y: icon.end2.y + } + + Behavior on strokeColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + Behavior on start1 { + PropAnim {} + } + Behavior on end1 { + PropAnim {} + } + Behavior on start2 { + PropAnim {} + } + Behavior on end2 { + PropAnim {} + } + } + + Behavior on x { + NumberAnim {} + } + + Behavior on implicitWidth { + NumberAnim {} + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + enabled: false + } + + component NumberAnim: NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + + component PropAnim: PropertyAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } +} diff --git a/components/controls/StyledTextField.qml b/components/controls/StyledTextField.qml new file mode 100644 index 0000000..30db314 --- /dev/null +++ b/components/controls/StyledTextField.qml @@ -0,0 +1,86 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls + +TextField { + id: root + + color: Colours.palette.m3onSurface + placeholderTextColor: Colours.palette.m3outline + font.family: Appearance.font.family.sans + font.pointSize: Appearance.font.size.smaller + renderType: TextField.NativeRendering + cursorVisible: !readOnly + + background: null + + cursorDelegate: StyledRect { + id: cursor + + property bool disableBlink + + implicitWidth: 2 + color: Colours.palette.m3primary + radius: Appearance.rounding.normal + + Connections { + target: root + + function onCursorPositionChanged(): void { + if (root.activeFocus && root.cursorVisible) { + cursor.opacity = 1; + cursor.disableBlink = true; + enableBlink.restart(); + } + } + } + + Timer { + id: enableBlink + + interval: 100 + onTriggered: cursor.disableBlink = false + } + + Timer { + running: root.activeFocus && root.cursorVisible && !cursor.disableBlink + repeat: true + triggeredOnStart: true + interval: 500 + onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1 + } + + Binding { + when: !root.activeFocus || !root.cursorVisible + cursor.opacity: 0 + } + + Behavior on opacity { + NumberAnimation { + duration: Appearance.anim.durations.small + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + Behavior on color { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + Behavior on placeholderTextColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/controls/VerticalSlider.qml b/components/controls/VerticalSlider.qml new file mode 100644 index 0000000..306cc24 --- /dev/null +++ b/components/controls/VerticalSlider.qml @@ -0,0 +1,137 @@ +import ".." +import "../effects" +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls + +Slider { + id: root + + required property string icon + property real oldValue + + orientation: Qt.Vertical + + background: StyledRect { + color: Colours.alpha(Colours.palette.m3surfaceContainer, true) + radius: Appearance.rounding.full + + StyledRect { + anchors.left: parent.left + anchors.right: parent.right + + y: root.handle.y + implicitHeight: parent.height - y + + color: Colours.alpha(Colours.palette.m3secondary, true) + radius: Appearance.rounding.full + } + } + + handle: Item { + id: handle + + property bool moving + + y: root.visualPosition * (root.availableHeight - height) + implicitWidth: root.width + implicitHeight: root.width + + Elevation { + anchors.fill: parent + radius: rect.radius + level: handleInteraction.containsMouse ? 2 : 1 + } + + StyledRect { + id: rect + + anchors.fill: parent + + color: Colours.alpha(Colours.palette.m3inverseSurface, true) + radius: Appearance.rounding.full + + MouseArea { + id: handleInteraction + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.NoButton + } + + MaterialIcon { + id: icon + + property bool moving: handle.moving + + function update(): void { + animate = !moving; + text = moving ? Qt.binding(() => Math.round(root.value * 100)) : Qt.binding(() => root.icon); + font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger; + font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material; + } + + animate: true + text: root.icon + color: Colours.palette.m3inverseOnSurface + anchors.centerIn: parent + + Behavior on moving { + SequentialAnimation { + NumberAnimation { + target: icon + property: "scale" + from: 1 + to: 0 + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + ScriptAction { + script: icon.update() + } + NumberAnimation { + target: icon + property: "scale" + from: 0 + to: 1 + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + } + } + + onPressedChanged: handle.moving = pressed + + onValueChanged: { + if (Math.abs(value - oldValue) < 0.01) + return; + oldValue = value; + handle.moving = true; + stateChangeDelay.restart(); + } + + Timer { + id: stateChangeDelay + + interval: 500 + onTriggered: { + if (!root.pressed) + handle.moving = false; + } + } + + Behavior on value { + NumberAnimation { + duration: Appearance.anim.durations.large + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/effects/Colouriser.qml b/components/effects/Colouriser.qml new file mode 100644 index 0000000..b621ecd --- /dev/null +++ b/components/effects/Colouriser.qml @@ -0,0 +1,16 @@ +import qs.config +import QtQuick +import QtQuick.Effects + +MultiEffect { + colorization: 1 + brightness: colorizationColor.hslLightness + + Behavior on colorizationColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/effects/Elevation.qml b/components/effects/Elevation.qml new file mode 100644 index 0000000..999b199 --- /dev/null +++ b/components/effects/Elevation.qml @@ -0,0 +1,22 @@ +import qs.services +import qs.config +import QtQuick +import QtQuick.Effects + +RectangularShadow { + property int level + property real dp: [0, 1, 3, 6, 8, 12][level] + + color: Qt.alpha(Colours.palette.m3shadow, 0.7) + blur: (dp * 5) ** 0.7 + spread: -dp * 0.3 + (dp * 0.1) ** 2 + offset.y: dp / 2 + + Behavior on dp { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/effects/InnerBorder.qml b/components/effects/InnerBorder.qml new file mode 100644 index 0000000..e5092ca --- /dev/null +++ b/components/effects/InnerBorder.qml @@ -0,0 +1,44 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Effects + +StyledRect { + property alias innerRadius: maskInner.radius + property alias thickness: maskInner.anchors.margins + property alias leftThickness: maskInner.anchors.leftMargin + property alias topThickness: maskInner.anchors.topMargin + property alias rightThickness: maskInner.anchors.rightMargin + property alias bottomThickness: maskInner.anchors.bottomMargin + + anchors.fill: parent + color: Colours.palette.m3surfaceContainer + + layer.enabled: true + layer.effect: MultiEffect { + maskSource: mask + maskEnabled: true + maskInverted: true + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + + Item { + id: mask + + anchors.fill: parent + layer.enabled: true + visible: false + + Rectangle { + id: maskInner + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + radius: Appearance.rounding.small + } + } +} diff --git a/components/filedialog/CurrentItem.qml b/components/filedialog/CurrentItem.qml new file mode 100644 index 0000000..e042445 --- /dev/null +++ b/components/filedialog/CurrentItem.qml @@ -0,0 +1,107 @@ +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Shapes + +Item { + id: root + + required property var currentItem + + implicitWidth: content.implicitWidth + Appearance.padding.larger + content.anchors.rightMargin + implicitHeight: currentItem ? content.implicitHeight + Appearance.padding.normal + content.anchors.bottomMargin : 0 + + Shape { + preferredRendererType: Shape.CurveRenderer + + ShapePath { + id: path + + readonly property real rounding: Appearance.rounding.small + readonly property bool flatten: root.implicitHeight < rounding * 2 + readonly property real roundingY: flatten ? root.implicitHeight / 2 : rounding + + strokeWidth: -1 + fillColor: Colours.palette.m3surfaceContainer + + startX: root.implicitWidth + startY: root.implicitHeight + + PathLine { + relativeX: -(root.implicitWidth + path.rounding) + relativeY: 0 + } + PathArc { + relativeX: path.rounding + relativeY: -path.roundingY + radiusX: path.rounding + radiusY: Math.min(path.rounding, root.implicitHeight) + direction: PathArc.Counterclockwise + } + PathLine { + relativeX: 0 + relativeY: -(root.implicitHeight - path.roundingY * 2) + } + PathArc { + relativeX: path.rounding + relativeY: -path.roundingY + radiusX: path.rounding + radiusY: Math.min(path.rounding, root.implicitHeight) + } + PathLine { + relativeX: root.implicitHeight > 0 ? root.implicitWidth - path.rounding * 2 : root.implicitWidth + relativeY: 0 + } + PathArc { + relativeX: path.rounding + relativeY: -path.rounding + radiusX: path.rounding + radiusY: path.rounding + direction: PathArc.Counterclockwise + } + + Behavior on fillColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + } + + Item { + anchors.fill: parent + clip: true + + StyledText { + id: content + + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: Appearance.padding.larger - Appearance.padding.small + anchors.bottomMargin: Appearance.padding.normal - Appearance.padding.small + + text: qsTr(`"%1" selected`).arg(root.currentItem?.fileName) + } + } + + Behavior on implicitWidth { + enabled: !!root.currentItem + + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + Behavior on implicitHeight { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/components/filedialog/DialogButtons.qml b/components/filedialog/DialogButtons.qml new file mode 100644 index 0000000..a64195a --- /dev/null +++ b/components/filedialog/DialogButtons.qml @@ -0,0 +1,93 @@ +import ".." +import qs.services +import qs.config +import QtQuick.Layouts + +StyledRect { + id: root + + required property var dialog + required property FolderContents folder + + implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 + + color: Colours.palette.m3surfaceContainer + + RowLayout { + id: inner + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Filter:") + } + + StyledRect { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.rightMargin: Appearance.spacing.normal + + color: Colours.palette.m3surfaceContainerHigh + radius: Appearance.rounding.small + + StyledText { + anchors.fill: parent + anchors.margins: Appearance.padding.normal + + text: `${root.dialog.filterLabel} (${root.dialog.filters.map(f => `*.${f}`).join(", ")})` + } + } + + StyledRect { + color: Colours.palette.m3surfaceContainerHigh + radius: Appearance.rounding.small + + implicitWidth: cancelText.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: cancelText.implicitHeight + Appearance.padding.normal * 2 + + StateLayer { + disabled: !root.dialog.selectionValid + + function onClicked(): void { + root.dialog.accepted(root.folder.currentItem.filePath); + } + } + + StyledText { + id: selectText + + anchors.centerIn: parent + anchors.margins: Appearance.padding.normal + + text: qsTr("Select") + color: root.dialog.selectionValid ? Colours.palette.m3onSurface : Colours.palette.m3outline + } + } + + StyledRect { + color: Colours.palette.m3surfaceContainerHigh + radius: Appearance.rounding.small + + implicitWidth: cancelText.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: cancelText.implicitHeight + Appearance.padding.normal * 2 + + StateLayer { + function onClicked(): void { + root.dialog.rejected(); + } + } + + StyledText { + id: cancelText + + anchors.centerIn: parent + anchors.margins: Appearance.padding.normal + + text: qsTr("Cancel") + } + } + } +} diff --git a/components/filedialog/FileDialog.qml b/components/filedialog/FileDialog.qml new file mode 100644 index 0000000..a533243 --- /dev/null +++ b/components/filedialog/FileDialog.qml @@ -0,0 +1,106 @@ +pragma ComponentBehavior: Bound + +import qs.services +import qs.config +import Quickshell +import QtQuick +import QtQuick.Layouts + +LazyLoader { + id: loader + + property list cwd: ["Home"] + property string filterLabel: "All files" + property list filters: ["*"] + property string title: qsTr("Select a file") + + signal accepted(path: string) + signal rejected + + function open(): void { + activeAsync = true; + } + + function close(): void { + rejected(); + } + + onAccepted: activeAsync = false + onRejected: activeAsync = false + + FloatingWindow { + id: root + + property list cwd: loader.cwd + property string filterLabel: loader.filterLabel + property list filters: loader.filters + + readonly property bool selectionValid: { + const item = folderContents.currentItem; + return item && !item.fileIsDir && (filters.includes("*") || filters.includes(item.fileSuffix)); + } + + function accepted(path: string): void { + loader.accepted(path); + } + + function rejected(): void { + loader.rejected(); + } + + implicitWidth: 1000 + implicitHeight: 600 + color: Colours.palette.m3surface + title: loader.title + + onVisibleChanged: { + if (!visible) + rejected(); + } + + RowLayout { + anchors.fill: parent + + spacing: 0 + + Sidebar { + Layout.fillHeight: true + dialog: root + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + + spacing: 0 + + HeaderBar { + Layout.fillWidth: true + dialog: root + } + + FolderContents { + id: folderContents + + Layout.fillWidth: true + Layout.fillHeight: true + dialog: root + } + + DialogButtons { + Layout.fillWidth: true + dialog: root + folder: folderContents + } + } + } + + Behavior on color { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } +} diff --git a/components/filedialog/FolderContents.qml b/components/filedialog/FolderContents.qml new file mode 100644 index 0000000..45930ba --- /dev/null +++ b/components/filedialog/FolderContents.qml @@ -0,0 +1,224 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../controls" +import "../images" +import qs.services +import qs.config +import qs.utils +import Quickshell +import Quickshell.Io +import QtQuick +import QtQuick.Layouts +import QtQuick.Effects +import QtQuick.Controls +import Qt.labs.folderlistmodel + +Item { + id: root + + required property var dialog + property alias currentItem: view.currentItem + + StyledRect { + anchors.fill: parent + color: Colours.palette.m3surfaceContainer + + layer.enabled: true + layer.effect: MultiEffect { + maskSource: mask + maskEnabled: true + maskInverted: true + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + } + + Item { + id: mask + + anchors.fill: parent + layer.enabled: true + visible: false + + Rectangle { + anchors.fill: parent + anchors.margins: Appearance.padding.small + radius: Appearance.rounding.small + } + } + + Loader { + anchors.centerIn: parent + active: view.count === 0 + asynchronous: true + sourceComponent: ColumnLayout { + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "scan_delete" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.extraLarge * 2 + font.weight: 500 + } + + StyledText { + text: qsTr("This folder is empty") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + } + } + + GridView { + id: view + + anchors.fill: parent + anchors.margins: Appearance.padding.small + Appearance.padding.normal + + cellWidth: Sizes.itemWidth + Appearance.spacing.small + cellHeight: Sizes.itemWidth + Appearance.spacing.small * 2 + Appearance.padding.normal * 2 + 1 + + clip: true + focus: true + currentIndex: -1 + Keys.onEscapePressed: currentIndex = -1 + + Keys.onReturnPressed: { + if (root.dialog.selectionValid) + root.dialog.accepted(currentItem.filePath); + } + Keys.onEnterPressed: { + if (root.dialog.selectionValid) + root.dialog.accepted(currentItem.filePath); + } + + ScrollBar.vertical: StyledScrollBar {} + + model: FolderListModel { + showDirsFirst: true + folder: { + let url = "file://"; + if (root.dialog.cwd[0] === "Home") + url += `${Paths.strip(Paths.home)}/${root.dialog.cwd.slice(1).join("/")}`; + else + url += root.dialog.cwd.join("/"); + return url; + } + onFolderChanged: view.currentIndex = -1 + } + + delegate: StyledRect { + id: item + + required property int index + required property string fileName + required property string filePath + required property url fileUrl + required property string fileSuffix + required property bool fileIsDir + + readonly property real nonAnimHeight: icon.implicitHeight + name.anchors.topMargin + name.implicitHeight + Appearance.padding.normal * 2 + + implicitWidth: Sizes.itemWidth + implicitHeight: nonAnimHeight + + radius: Appearance.rounding.normal + color: GridView.isCurrentItem ? Colours.palette.m3surfaceContainerHighest : "transparent" + z: GridView.isCurrentItem || implicitHeight !== nonAnimHeight ? 1 : 0 + clip: true + + StateLayer { + onDoubleClicked: { + if (item.fileIsDir) + root.dialog.cwd.push(item.fileName); + else if (root.dialog.selectionValid) + root.dialog.accepted(item.filePath); + } + + function onClicked(): void { + view.currentIndex = item.index; + } + } + + CachingIconImage { + id: icon + + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: Appearance.padding.normal + + asynchronous: true + implicitSize: Sizes.itemWidth - Appearance.padding.normal * 2 + source: { + if (!item.fileIsDir) + return Quickshell.iconPath("application-x-zerosize"); + + const name = item.fileName; + if (root.dialog.cwd.length === 1 && ["Desktop", "Documents", "Downloads", "Music", "Pictures", "Public", "Templates", "Videos"].includes(name)) + return Quickshell.iconPath(`folder-${name.toLowerCase()}`); + + return Quickshell.iconPath("inode-directory"); + } + + onStatusChanged: { + if (status === Image.Error) + source = Quickshell.iconPath("error"); + } + + Process { + running: !item.fileIsDir + command: ["file", "--mime", "-b", item.filePath] + stdout: StdioCollector { + onStreamFinished: { + const mime = text.split(";")[0].replace("/", "-"); + icon.source = Images.validImageTypes.some(t => mime === `image-${t}`) ? item.fileUrl : Quickshell.iconPath(mime, "image-missing"); + } + } + } + } + + StyledText { + id: name + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: icon.bottom + anchors.topMargin: Appearance.spacing.small + anchors.margins: Appearance.padding.normal + + horizontalAlignment: Text.AlignHCenter + text: item.fileName + elide: item.GridView.isCurrentItem ? Text.ElideNone : Text.ElideRight + wrapMode: item.GridView.isCurrentItem ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap + } + + Behavior on implicitHeight { + Anim {} + } + } + + populate: Transition { + Anim { + property: "scale" + from: 0.7 + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + + CurrentItem { + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: Appearance.padding.small + + currentItem: view.currentItem + } + + component Anim: NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } +} diff --git a/components/filedialog/HeaderBar.qml b/components/filedialog/HeaderBar.qml new file mode 100644 index 0000000..4af9672 --- /dev/null +++ b/components/filedialog/HeaderBar.qml @@ -0,0 +1,142 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +StyledRect { + id: root + + required property var dialog + + implicitWidth: inner.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 + + color: Colours.palette.m3surfaceContainer + + RowLayout { + id: inner + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + spacing: Appearance.spacing.small + + Item { + implicitWidth: implicitHeight + implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + radius: Appearance.rounding.small + disabled: root.dialog.cwd.length === 1 + + function onClicked(): void { + root.dialog.cwd.pop(); + } + } + + MaterialIcon { + id: upIcon + + anchors.centerIn: parent + text: "drive_folder_upload" + color: root.dialog.cwd.length === 1 ? Colours.palette.m3outline : Colours.palette.m3onSurface + grade: 200 + } + } + + StyledRect { + Layout.fillWidth: true + + radius: Appearance.rounding.small + color: Colours.palette.m3surfaceContainerHigh + + implicitHeight: pathComponents.implicitHeight + pathComponents.anchors.margins * 2 + + RowLayout { + id: pathComponents + + anchors.fill: parent + anchors.margins: Appearance.padding.small / 2 + anchors.leftMargin: 0 + + spacing: Appearance.spacing.small + + Repeater { + model: root.dialog.cwd + + RowLayout { + id: folder + + required property string modelData + required property int index + + spacing: 0 + + Loader { + Layout.rightMargin: Appearance.spacing.small + active: folder.index > 0 + asynchronous: true + sourceComponent: StyledText { + text: "/" + color: Colours.palette.m3onSurfaceVariant + font.bold: true + } + } + + Item { + implicitWidth: homeIcon.implicitWidth + (homeIcon.active ? Appearance.padding.small : 0) + folderName.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: folderName.implicitHeight + Appearance.padding.small * 2 + + Loader { + anchors.fill: parent + active: folder.index < root.dialog.cwd.length - 1 + asynchronous: true + sourceComponent: StateLayer { + radius: Appearance.rounding.small + + function onClicked(): void { + root.dialog.cwd = root.dialog.cwd.slice(0, folder.index + 1); + } + } + } + + Loader { + id: homeIcon + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: Appearance.padding.normal + + active: folder.index === 0 && folder.modelData === "Home" + asynchronous: true + sourceComponent: MaterialIcon { + text: "home" + color: root.dialog.cwd.length === 1 ? Colours.palette.m3onSurface : Colours.palette.m3onSurfaceVariant + fill: 1 + } + } + + StyledText { + id: folderName + + anchors.left: homeIcon.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: homeIcon.active ? Appearance.padding.small : 0 + + text: folder.modelData + color: folder.index < root.dialog.cwd.length - 1 ? Colours.palette.m3onSurfaceVariant : Colours.palette.m3onSurface + font.bold: true + } + } + } + } + + Item { + Layout.fillWidth: true + } + } + } + } +} diff --git a/components/filedialog/Sidebar.qml b/components/filedialog/Sidebar.qml new file mode 100644 index 0000000..82a1dd5 --- /dev/null +++ b/components/filedialog/Sidebar.qml @@ -0,0 +1,117 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +StyledRect { + id: root + + required property var dialog + + implicitWidth: Sizes.sidebarWidth + implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 + + color: Colours.palette.m3surfaceContainer + + ColumnLayout { + id: inner + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.normal + spacing: Appearance.spacing.small / 2 + + StyledText { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: Appearance.padding.small / 2 + Layout.bottomMargin: Appearance.spacing.normal + text: qsTr("Files") + color: Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.larger + font.bold: true + } + + Repeater { + model: ["Home", "Downloads", "Desktop", "Documents", "Music", "Pictures", "Videos"] + + StyledRect { + id: place + + required property string modelData + readonly property bool selected: modelData === root.dialog.cwd[root.dialog.cwd.length - 1] + + Layout.fillWidth: true + implicitHeight: placeInner.implicitHeight + Appearance.padding.normal * 2 + + radius: Appearance.rounding.full + color: selected ? Colours.palette.m3secondaryContainer : "transparent" + + StateLayer { + color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + + function onClicked(): void { + if (place.modelData === "Home") + root.dialog.cwd = ["Home"]; + else + root.dialog.cwd = ["Home", place.modelData]; + } + } + + RowLayout { + id: placeInner + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + + spacing: Appearance.spacing.normal + + MaterialIcon { + text: { + const p = place.modelData; + if (p === "Home") + return "home"; + if (p === "Downloads") + return "file_download"; + if (p === "Desktop") + return "desktop_windows"; + if (p === "Documents") + return "description"; + if (p === "Music") + return "music_note"; + if (p === "Pictures") + return "image"; + if (p === "Videos") + return "video_library"; + return "folder"; + } + color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.large + fill: place.selected ? 1 : 0 + + Behavior on fill { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + StyledText { + Layout.fillWidth: true + text: place.modelData + color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.normal + elide: Text.ElideRight + } + } + } + } + } +} diff --git a/components/filedialog/Sizes.qml b/components/filedialog/Sizes.qml new file mode 100644 index 0000000..2ad31f9 --- /dev/null +++ b/components/filedialog/Sizes.qml @@ -0,0 +1,8 @@ +pragma Singleton + +import Quickshell + +Singleton { + property int itemWidth: 103 + property int sidebarWidth: 200 +} diff --git a/components/images/CachingIconImage.qml b/components/images/CachingIconImage.qml new file mode 100644 index 0000000..522a947 --- /dev/null +++ b/components/images/CachingIconImage.qml @@ -0,0 +1,31 @@ +import QtQuick + +Item { + property alias asynchronous: image.asynchronous + property alias status: image.status + property alias mipmap: image.mipmap + property alias backer: image + + property real implicitSize + readonly property real actualSize: Math.min(width, height) + + property url source + + onSourceChanged: { + if (source?.toString().startsWith("image://icon/")) + // Directly skip the path prop and treat like a normal Image component + image.source = source; + else if (source) + image.path = source; + } + + implicitWidth: implicitSize + implicitHeight: implicitSize + + CachingImage { + id: image + + anchors.fill: parent + fillMode: Image.PreserveAspectFit + } +} diff --git a/components/images/CachingImage.qml b/components/images/CachingImage.qml new file mode 100644 index 0000000..1d42238 --- /dev/null +++ b/components/images/CachingImage.qml @@ -0,0 +1,41 @@ +import qs.utils +import Quickshell.Io +import QtQuick + +Image { + id: root + + property string path + property string hash + readonly property string cachePath: `${Paths.stringify(Paths.imagecache)}/${hash}@${width}x${height}.png` + + asynchronous: true + fillMode: Image.PreserveAspectCrop + sourceSize.width: width + sourceSize.height: height + + onPathChanged: shaProc.exec(["sha256sum", Paths.strip(path)]) + + onCachePathChanged: { + if (hash) + source = cachePath; + } + + onStatusChanged: { + if (source == cachePath && status === Image.Error) + source = path; + else if (source == path && status === Image.Ready) { + Paths.mkdir(Paths.imagecache); + const grabPath = cachePath; + grabToImage(res => res.saveToFile(grabPath)); + } + } + + Process { + id: shaProc + + stdout: StdioCollector { + onStreamFinished: root.hash = text.split(" ")[0] + } + } +} diff --git a/components/misc/CustomShortcut.qml b/components/misc/CustomShortcut.qml new file mode 100644 index 0000000..aa35ed8 --- /dev/null +++ b/components/misc/CustomShortcut.qml @@ -0,0 +1,5 @@ +import Quickshell.Hyprland + +GlobalShortcut { + appid: "caelestia" +} diff --git a/components/misc/Ref.qml b/components/misc/Ref.qml new file mode 100644 index 0000000..679f52f --- /dev/null +++ b/components/misc/Ref.qml @@ -0,0 +1,9 @@ +import Quickshell +import QtQuick + +QtObject { + required property Singleton service + + Component.onCompleted: service.refCount++ + Component.onDestruction: service.refCount-- +} diff --git a/components/widgets/ExtraIndicator.qml b/components/widgets/ExtraIndicator.qml new file mode 100644 index 0000000..6034b33 --- /dev/null +++ b/components/widgets/ExtraIndicator.qml @@ -0,0 +1,54 @@ +import ".." +import "../effects" +import qs.services +import qs.config +import QtQuick + +StyledRect { + required property int extra + + anchors.right: parent.right + anchors.margins: Appearance.padding.normal + + color: Colours.palette.m3tertiary + radius: Appearance.rounding.small + + implicitWidth: count.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: count.implicitHeight + Appearance.padding.small * 2 + + opacity: extra > 0 ? 1 : 0 + scale: extra > 0 ? 1 : 0.5 + + Elevation { + anchors.fill: parent + radius: parent.radius + opacity: parent.opacity + z: -1 + level: 2 + } + + StyledText { + id: count + + anchors.centerIn: parent + animate: parent.opacity > 0 + text: qsTr("+%1").arg(parent.extra) + color: Colours.palette.m3onTertiary + } + + Behavior on opacity { + NumberAnimation { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + Behavior on scale { + NumberAnimation { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } +} diff --git a/modules/Shortcuts.qml b/modules/Shortcuts.qml index 61ff11d..628a8ce 100644 --- a/modules/Shortcuts.qml +++ b/modules/Shortcuts.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components.misc import qs.services import Quickshell import Quickshell.Io diff --git a/modules/areapicker/AreaPicker.qml b/modules/areapicker/AreaPicker.qml index a670100..7ff051f 100644 --- a/modules/areapicker/AreaPicker.qml +++ b/modules/areapicker/AreaPicker.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components.containers +import qs.components.misc import Quickshell import Quickshell.Wayland import Quickshell.Io diff --git a/modules/areapicker/Picker.qml b/modules/areapicker/Picker.qml index bcde0ca..2f0bafc 100644 --- a/modules/areapicker/Picker.qml +++ b/modules/areapicker/Picker.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/background/Background.qml b/modules/background/Background.qml index 09eed3c..3da8bf9 100644 --- a/modules/background/Background.qml +++ b/modules/background/Background.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components.containers import qs.config import Quickshell import Quickshell.Wayland diff --git a/modules/background/DesktopClock.qml b/modules/background/DesktopClock.qml index a0e3f13..6dc6b6b 100644 --- a/modules/background/DesktopClock.qml +++ b/modules/background/DesktopClock.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/background/Wallpaper.qml b/modules/background/Wallpaper.qml index 2ec4927..a427d4f 100644 --- a/modules/background/Wallpaper.qml +++ b/modules/background/Wallpaper.qml @@ -1,7 +1,8 @@ pragma ComponentBehavior: Bound -import qs.widgets -import qs.widgets.filedialog +import qs.components +import qs.components.images +import qs.components.filedialog import qs.services import qs.config import qs.utils diff --git a/modules/bar/Bar.qml b/modules/bar/Bar.qml index 8d6f2bb..d661b86 100644 --- a/modules/bar/Bar.qml +++ b/modules/bar/Bar.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import "popouts" as BarPopouts diff --git a/modules/bar/components/ActiveWindow.qml b/modules/bar/components/ActiveWindow.qml index 1ce33ff..ed4c90d 100644 --- a/modules/bar/components/ActiveWindow.qml +++ b/modules/bar/components/ActiveWindow.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.utils import qs.config diff --git a/modules/bar/components/Clock.qml b/modules/bar/components/Clock.qml index f045f9d..46f0e47 100644 --- a/modules/bar/components/Clock.qml +++ b/modules/bar/components/Clock.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/bar/components/OsIcon.qml b/modules/bar/components/OsIcon.qml index 4ba9417..ecc69b5 100644 --- a/modules/bar/components/OsIcon.qml +++ b/modules/bar/components/OsIcon.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.utils import qs.config diff --git a/modules/bar/components/Power.qml b/modules/bar/components/Power.qml index 05c9dd5..71dc296 100644 --- a/modules/bar/components/Power.qml +++ b/modules/bar/components/Power.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/bar/components/StatusIcons.qml b/modules/bar/components/StatusIcons.qml index f5b3d8a..ad8c35e 100644 --- a/modules/bar/components/StatusIcons.qml +++ b/modules/bar/components/StatusIcons.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.utils import qs.config diff --git a/modules/bar/components/workspaces/ActiveIndicator.qml b/modules/bar/components/workspaces/ActiveIndicator.qml index a338d4e..b44ba87 100644 --- a/modules/bar/components/workspaces/ActiveIndicator.qml +++ b/modules/bar/components/workspaces/ActiveIndicator.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.effects import qs.services import qs.config import QtQuick diff --git a/modules/bar/components/workspaces/OccupiedBg.qml b/modules/bar/components/workspaces/OccupiedBg.qml index 10cd6c0..567510a 100644 --- a/modules/bar/components/workspaces/OccupiedBg.qml +++ b/modules/bar/components/workspaces/OccupiedBg.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/bar/components/workspaces/Workspace.qml b/modules/bar/components/workspaces/Workspace.qml index dc410ba..fcf8ba4 100644 --- a/modules/bar/components/workspaces/Workspace.qml +++ b/modules/bar/components/workspaces/Workspace.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.utils import qs.config diff --git a/modules/bar/components/workspaces/Workspaces.qml b/modules/bar/components/workspaces/Workspaces.qml index bc49a33..0a806f7 100644 --- a/modules/bar/components/workspaces/Workspaces.qml +++ b/modules/bar/components/workspaces/Workspaces.qml @@ -1,6 +1,5 @@ pragma ComponentBehavior: Bound -import qs.widgets import qs.services import qs.config import QtQuick diff --git a/modules/bar/popouts/ActiveWindow.qml b/modules/bar/popouts/ActiveWindow.qml index 0c48798..cad2bd8 100644 --- a/modules/bar/popouts/ActiveWindow.qml +++ b/modules/bar/popouts/ActiveWindow.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.utils import qs.config diff --git a/modules/bar/popouts/Audio.qml b/modules/bar/popouts/Audio.qml index 6714eb8..e6e5514 100644 --- a/modules/bar/popouts/Audio.qml +++ b/modules/bar/popouts/Audio.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import QtQuick.Layouts diff --git a/modules/bar/popouts/Battery.qml b/modules/bar/popouts/Battery.qml index df2f95d..254e2af 100644 --- a/modules/bar/popouts/Battery.qml +++ b/modules/bar/popouts/Battery.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell.Services.UPower diff --git a/modules/bar/popouts/Bluetooth.qml b/modules/bar/popouts/Bluetooth.qml index 97cb01e..09ed768 100644 --- a/modules/bar/popouts/Bluetooth.qml +++ b/modules/bar/popouts/Bluetooth.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import qs.utils diff --git a/modules/bar/popouts/Network.qml b/modules/bar/popouts/Network.qml index 80e1732..6c21f24 100644 --- a/modules/bar/popouts/Network.qml +++ b/modules/bar/popouts/Network.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import qs.utils diff --git a/modules/bar/popouts/TrayMenu.qml b/modules/bar/popouts/TrayMenu.qml index 59f310b..67305b5 100644 --- a/modules/bar/popouts/TrayMenu.qml +++ b/modules/bar/popouts/TrayMenu.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/dashboard/Dash.qml b/modules/dashboard/Dash.qml index 2331f95..09d7e93 100644 --- a/modules/dashboard/Dash.qml +++ b/modules/dashboard/Dash.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import "dash" diff --git a/modules/dashboard/Media.qml b/modules/dashboard/Media.qml index 25364f6..c12db22 100644 --- a/modules/dashboard/Media.qml +++ b/modules/dashboard/Media.qml @@ -1,6 +1,8 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.effects +import qs.components.misc import qs.services import qs.utils import qs.config diff --git a/modules/dashboard/Performance.qml b/modules/dashboard/Performance.qml index 4717b30..2667a1e 100644 --- a/modules/dashboard/Performance.qml +++ b/modules/dashboard/Performance.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.misc import qs.services import qs.config import QtQuick diff --git a/modules/dashboard/Tabs.qml b/modules/dashboard/Tabs.qml index d29ee42..a041bf3 100644 --- a/modules/dashboard/Tabs.qml +++ b/modules/dashboard/Tabs.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import Quickshell diff --git a/modules/dashboard/Wrapper.qml b/modules/dashboard/Wrapper.qml index 184a2ab..9863e01 100644 --- a/modules/dashboard/Wrapper.qml +++ b/modules/dashboard/Wrapper.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets.filedialog +import qs.components.filedialog import qs.config import qs.utils import Quickshell diff --git a/modules/dashboard/dash/Calendar.qml b/modules/dashboard/dash/Calendar.qml index 836b202..51771cc 100644 --- a/modules/dashboard/dash/Calendar.qml +++ b/modules/dashboard/dash/Calendar.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick @@ -55,7 +55,7 @@ Column { implicitHeight: parent.implicitHeight radius: Appearance.rounding.full - color: model.today ? Colours.palette.m3primary : "transparent" + color: day.model.today ? Colours.palette.m3primary : "transparent" StyledText { id: text diff --git a/modules/dashboard/dash/DateTime.qml b/modules/dashboard/dash/DateTime.qml index 9d81741..cdba4db 100644 --- a/modules/dashboard/dash/DateTime.qml +++ b/modules/dashboard/dash/DateTime.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/dashboard/dash/Media.qml b/modules/dashboard/dash/Media.qml index 1346886..cdb2c1a 100644 --- a/modules/dashboard/dash/Media.qml +++ b/modules/dashboard/dash/Media.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import qs.utils diff --git a/modules/dashboard/dash/Resources.qml b/modules/dashboard/dash/Resources.qml index 3e15a99..3edba0c 100644 --- a/modules/dashboard/dash/Resources.qml +++ b/modules/dashboard/dash/Resources.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.misc import qs.services import qs.config import QtQuick diff --git a/modules/dashboard/dash/User.qml b/modules/dashboard/dash/User.qml index 7bc225b..9b029ba 100644 --- a/modules/dashboard/dash/User.qml +++ b/modules/dashboard/dash/User.qml @@ -1,11 +1,11 @@ -import qs.widgets +import qs.components +import qs.components.images import qs.services import qs.config import qs.utils import Quickshell import Quickshell.Io import QtQuick -import QtQuick.Dialogs Row { id: root diff --git a/modules/dashboard/dash/Weather.qml b/modules/dashboard/dash/Weather.qml index 6bb9792..0160229 100644 --- a/modules/dashboard/dash/Weather.qml +++ b/modules/dashboard/dash/Weather.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import qs.utils diff --git a/modules/detachedcontent/DetachedContent.qml b/modules/detachedcontent/DetachedContent.qml index c77a7da..cf564cb 100644 --- a/modules/detachedcontent/DetachedContent.qml +++ b/modules/detachedcontent/DetachedContent.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import Quickshell diff --git a/modules/detachedcontent/NavRail.qml b/modules/detachedcontent/NavRail.qml index de654d2..826e8ca 100644 --- a/modules/detachedcontent/NavRail.qml +++ b/modules/detachedcontent/NavRail.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/detachedcontent/Panes.qml b/modules/detachedcontent/Panes.qml index d26a71d..d1ec581 100644 --- a/modules/detachedcontent/Panes.qml +++ b/modules/detachedcontent/Panes.qml @@ -1,7 +1,8 @@ pragma ComponentBehavior: Bound import "bluetooth" -import qs.widgets +import qs.components +import qs.components.effects import qs.services import qs.config import Quickshell.Widgets diff --git a/modules/detachedcontent/bluetooth/BtPane.qml b/modules/detachedcontent/bluetooth/BtPane.qml index d0ea8b0..c6dbbcc 100644 --- a/modules/detachedcontent/bluetooth/BtPane.qml +++ b/modules/detachedcontent/bluetooth/BtPane.qml @@ -1,7 +1,8 @@ pragma ComponentBehavior: Bound import ".." -import qs.widgets +import qs.components.effects +import qs.components.containers import qs.config import Quickshell.Bluetooth import QtQuick diff --git a/modules/detachedcontent/bluetooth/Details.qml b/modules/detachedcontent/bluetooth/Details.qml index 9d434c0..f856002 100644 --- a/modules/detachedcontent/bluetooth/Details.qml +++ b/modules/detachedcontent/bluetooth/Details.qml @@ -1,7 +1,10 @@ pragma ComponentBehavior: Bound import ".." -import qs.widgets +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers import qs.services import qs.config import qs.utils diff --git a/modules/detachedcontent/bluetooth/DeviceList.qml b/modules/detachedcontent/bluetooth/DeviceList.qml index 8876be7..d975e17 100644 --- a/modules/detachedcontent/bluetooth/DeviceList.qml +++ b/modules/detachedcontent/bluetooth/DeviceList.qml @@ -1,7 +1,9 @@ pragma ComponentBehavior: Bound import ".." -import qs.widgets +import qs.components +import qs.components.controls +import qs.components.containers import qs.services import qs.config import qs.utils diff --git a/modules/detachedcontent/bluetooth/Settings.qml b/modules/detachedcontent/bluetooth/Settings.qml index 663badc..f298432 100644 --- a/modules/detachedcontent/bluetooth/Settings.qml +++ b/modules/detachedcontent/bluetooth/Settings.qml @@ -1,7 +1,9 @@ pragma ComponentBehavior: Bound import ".." -import qs.widgets +import qs.components +import qs.components.controls +import qs.components.effects import qs.services import qs.config import Quickshell.Bluetooth diff --git a/modules/drawers/Border.qml b/modules/drawers/Border.qml index 5aa8465..b7cb374 100644 --- a/modules/drawers/Border.qml +++ b/modules/drawers/Border.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml index 86c2a1a..8b043a1 100644 --- a/modules/drawers/Drawers.qml +++ b/modules/drawers/Drawers.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.containers import qs.services import qs.config import qs.modules.bar diff --git a/modules/drawers/Exclusions.qml b/modules/drawers/Exclusions.qml index 778c9f6..b214408 100644 --- a/modules/drawers/Exclusions.qml +++ b/modules/drawers/Exclusions.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components.containers import qs.config import Quickshell import QtQuick diff --git a/modules/launcher/AppList.qml b/modules/launcher/AppList.qml index 3317cd0..2ede78e 100644 --- a/modules/launcher/AppList.qml +++ b/modules/launcher/AppList.qml @@ -2,7 +2,9 @@ pragma ComponentBehavior: Bound import "items" import "services" -import qs.widgets +import qs.components +import qs.components.controls +import qs.components.containers import qs.services import qs.config import Quickshell diff --git a/modules/launcher/Content.qml b/modules/launcher/Content.qml index 0c41456..d7acd55 100644 --- a/modules/launcher/Content.qml +++ b/modules/launcher/Content.qml @@ -1,7 +1,8 @@ pragma ComponentBehavior: Bound import "services" -import qs.widgets +import qs.components +import qs.components.controls import qs.services import qs.config import Quickshell diff --git a/modules/launcher/ContentList.qml b/modules/launcher/ContentList.qml index be7b4d3..e363c7b 100644 --- a/modules/launcher/ContentList.qml +++ b/modules/launcher/ContentList.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import qs.utils diff --git a/modules/launcher/items/ActionItem.qml b/modules/launcher/items/ActionItem.qml index acacede..102b3fd 100644 --- a/modules/launcher/items/ActionItem.qml +++ b/modules/launcher/items/ActionItem.qml @@ -1,5 +1,5 @@ import "../services" -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/launcher/items/AppItem.qml b/modules/launcher/items/AppItem.qml index 6c77166..6012356 100644 --- a/modules/launcher/items/AppItem.qml +++ b/modules/launcher/items/AppItem.qml @@ -1,5 +1,5 @@ import "../services" -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/launcher/items/CalcItem.qml b/modules/launcher/items/CalcItem.qml index 84c66a7..5e578d8 100644 --- a/modules/launcher/items/CalcItem.qml +++ b/modules/launcher/items/CalcItem.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/launcher/items/SchemeItem.qml b/modules/launcher/items/SchemeItem.qml index 94c1818..8cc2e33 100644 --- a/modules/launcher/items/SchemeItem.qml +++ b/modules/launcher/items/SchemeItem.qml @@ -1,5 +1,5 @@ import "../services" -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/launcher/items/VariantItem.qml b/modules/launcher/items/VariantItem.qml index 973564a..4d33a38 100644 --- a/modules/launcher/items/VariantItem.qml +++ b/modules/launcher/items/VariantItem.qml @@ -1,5 +1,5 @@ import "../services" -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/launcher/items/WallpaperItem.qml b/modules/launcher/items/WallpaperItem.qml index 401e852..103bc38 100644 --- a/modules/launcher/items/WallpaperItem.qml +++ b/modules/launcher/items/WallpaperItem.qml @@ -1,4 +1,6 @@ -import qs.widgets +import qs.components +import qs.components.effects +import qs.components.images import qs.services import qs.config import Quickshell diff --git a/modules/lock/Backgrounds.qml b/modules/lock/Backgrounds.qml index aee3f64..eba7c7b 100644 --- a/modules/lock/Backgrounds.qml +++ b/modules/lock/Backgrounds.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/lock/Buttons.qml b/modules/lock/Buttons.qml index 1995151..78675ec 100644 --- a/modules/lock/Buttons.qml +++ b/modules/lock/Buttons.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/lock/Clock.qml b/modules/lock/Clock.qml index 53c1b36..d024dd4 100644 --- a/modules/lock/Clock.qml +++ b/modules/lock/Clock.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/lock/Input.qml b/modules/lock/Input.qml index d988164..b1d2ae3 100644 --- a/modules/lock/Input.qml +++ b/modules/lock/Input.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.images import qs.services import qs.config import qs.utils diff --git a/modules/lock/Lock.qml b/modules/lock/Lock.qml index c618705..d0f27ec 100644 --- a/modules/lock/Lock.qml +++ b/modules/lock/Lock.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components.misc import Quickshell import Quickshell.Io import Quickshell.Wayland diff --git a/modules/lock/MediaPlaying.qml b/modules/lock/MediaPlaying.qml index d3bcdf9..08f1490 100644 --- a/modules/lock/MediaPlaying.qml +++ b/modules/lock/MediaPlaying.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell.Widgets diff --git a/modules/lock/Notification.qml b/modules/lock/Notification.qml index 145645f..c06db56 100644 --- a/modules/lock/Notification.qml +++ b/modules/lock/Notification.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.effects import qs.services import qs.config import qs.utils diff --git a/modules/lock/Status.qml b/modules/lock/Status.qml index 08e396a..d273628 100644 --- a/modules/lock/Status.qml +++ b/modules/lock/Status.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components +import qs.components.widgets import qs.services import qs.config import qs.utils diff --git a/modules/lock/WeatherInfo.qml b/modules/lock/WeatherInfo.qml index c5d1cf5..b944d2f 100644 --- a/modules/lock/WeatherInfo.qml +++ b/modules/lock/WeatherInfo.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import QtQuick diff --git a/modules/notifications/Content.qml b/modules/notifications/Content.qml index a41933d..2ece7f4 100644 --- a/modules/notifications/Content.qml +++ b/modules/notifications/Content.qml @@ -1,4 +1,5 @@ -import qs.widgets +import qs.components.containers +import qs.components.widgets import qs.services import qs.config import Quickshell diff --git a/modules/notifications/Notification.qml b/modules/notifications/Notification.qml index f9940b3..cdb6804 100644 --- a/modules/notifications/Notification.qml +++ b/modules/notifications/Notification.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components +import qs.components.effects import qs.services import qs.config import qs.utils @@ -441,49 +442,49 @@ StyledRect { } } } + } - component Action: StyledRect { - id: action + component Action: StyledRect { + id: action - required property var modelData + required property var modelData - radius: Appearance.rounding.full - color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.palette.m3surfaceContainerHigh + radius: Appearance.rounding.full + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.palette.m3surfaceContainerHigh - Layout.preferredWidth: actionText.width + Appearance.padding.normal * 2 - Layout.preferredHeight: actionText.height + Appearance.padding.small * 2 - implicitWidth: actionText.width + Appearance.padding.normal * 2 - implicitHeight: actionText.height + Appearance.padding.small * 2 + Layout.preferredWidth: actionText.width + Appearance.padding.normal * 2 + Layout.preferredHeight: actionText.height + Appearance.padding.small * 2 + implicitWidth: actionText.width + Appearance.padding.normal * 2 + implicitHeight: actionText.height + Appearance.padding.small * 2 - StateLayer { - radius: Appearance.rounding.full - color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurface + StateLayer { + radius: Appearance.rounding.full + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurface - function onClicked(): void { - action.modelData.invoke(); - } + function onClicked(): void { + action.modelData.invoke(); } + } - StyledText { - id: actionText + StyledText { + id: actionText - anchors.centerIn: parent - text: actionTextMetrics.elidedText - color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.small - } + anchors.centerIn: parent + text: actionTextMetrics.elidedText + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + } - TextMetrics { - id: actionTextMetrics + TextMetrics { + id: actionTextMetrics - text: action.modelData.text - font.family: actionText.font.family - font.pointSize: actionText.font.pointSize - elide: Text.ElideRight - elideWidth: { - const numActions = root.modelData.actions.length + 1; - return (inner.width - actions.spacing * (numActions - 1)) / numActions - Appearance.padding.normal * 2; - } + text: action.modelData.text + font.family: actionText.font.family + font.pointSize: actionText.font.pointSize + elide: Text.ElideRight + elideWidth: { + const numActions = root.modelData.actions.length + 1; + return (inner.width - actions.spacing * (numActions - 1)) / numActions - Appearance.padding.normal * 2; } } } diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml index a707fb4..3177ee9 100644 --- a/modules/osd/Content.qml +++ b/modules/osd/Content.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components.controls import qs.services import qs.config import QtQuick diff --git a/modules/session/Content.qml b/modules/session/Content.qml index 4c0bc88..a75de10 100644 --- a/modules/session/Content.qml +++ b/modules/session/Content.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import qs.utils diff --git a/modules/utilities/Content.qml b/modules/utilities/Content.qml index 02e198a..49fdaa4 100644 --- a/modules/utilities/Content.qml +++ b/modules/utilities/Content.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/windowinfo/Buttons.qml b/modules/windowinfo/Buttons.qml index dd933ae..dea5059 100644 --- a/modules/windowinfo/Buttons.qml +++ b/modules/windowinfo/Buttons.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell.Widgets diff --git a/modules/windowinfo/Details.qml b/modules/windowinfo/Details.qml index 6f83ac1..f9ee66a 100644 --- a/modules/windowinfo/Details.qml +++ b/modules/windowinfo/Details.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell.Hyprland diff --git a/modules/windowinfo/Preview.qml b/modules/windowinfo/Preview.qml index 7bbf16e..2cae647 100644 --- a/modules/windowinfo/Preview.qml +++ b/modules/windowinfo/Preview.qml @@ -1,6 +1,6 @@ pragma ComponentBehavior: Bound -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/modules/windowinfo/WindowInfo.qml b/modules/windowinfo/WindowInfo.qml index 4349d93..713122a 100644 --- a/modules/windowinfo/WindowInfo.qml +++ b/modules/windowinfo/WindowInfo.qml @@ -1,4 +1,4 @@ -import qs.widgets +import qs.components import qs.services import qs.config import Quickshell diff --git a/services/Brightness.qml b/services/Brightness.qml index cbd1739..954f76d 100644 --- a/services/Brightness.qml +++ b/services/Brightness.qml @@ -1,7 +1,7 @@ pragma Singleton pragma ComponentBehavior: Bound -import qs.widgets +import qs.components.misc import Quickshell import Quickshell.Io import QtQuick diff --git a/services/Notifs.qml b/services/Notifs.qml index a24c4aa..87ecca5 100644 --- a/services/Notifs.qml +++ b/services/Notifs.qml @@ -1,7 +1,7 @@ pragma Singleton pragma ComponentBehavior: Bound -import qs.widgets +import qs.components.misc import qs.config import Quickshell import Quickshell.Io diff --git a/services/Players.qml b/services/Players.qml index 3e259d3..9c197f1 100644 --- a/services/Players.qml +++ b/services/Players.qml @@ -1,6 +1,6 @@ pragma Singleton -import qs.widgets +import qs.components.misc import Quickshell import Quickshell.Io import Quickshell.Services.Mpris diff --git a/widgets/CachingIconImage.qml b/widgets/CachingIconImage.qml deleted file mode 100644 index 522a947..0000000 --- a/widgets/CachingIconImage.qml +++ /dev/null @@ -1,31 +0,0 @@ -import QtQuick - -Item { - property alias asynchronous: image.asynchronous - property alias status: image.status - property alias mipmap: image.mipmap - property alias backer: image - - property real implicitSize - readonly property real actualSize: Math.min(width, height) - - property url source - - onSourceChanged: { - if (source?.toString().startsWith("image://icon/")) - // Directly skip the path prop and treat like a normal Image component - image.source = source; - else if (source) - image.path = source; - } - - implicitWidth: implicitSize - implicitHeight: implicitSize - - CachingImage { - id: image - - anchors.fill: parent - fillMode: Image.PreserveAspectFit - } -} diff --git a/widgets/CachingImage.qml b/widgets/CachingImage.qml deleted file mode 100644 index 1d42238..0000000 --- a/widgets/CachingImage.qml +++ /dev/null @@ -1,41 +0,0 @@ -import qs.utils -import Quickshell.Io -import QtQuick - -Image { - id: root - - property string path - property string hash - readonly property string cachePath: `${Paths.stringify(Paths.imagecache)}/${hash}@${width}x${height}.png` - - asynchronous: true - fillMode: Image.PreserveAspectCrop - sourceSize.width: width - sourceSize.height: height - - onPathChanged: shaProc.exec(["sha256sum", Paths.strip(path)]) - - onCachePathChanged: { - if (hash) - source = cachePath; - } - - onStatusChanged: { - if (source == cachePath && status === Image.Error) - source = path; - else if (source == path && status === Image.Ready) { - Paths.mkdir(Paths.imagecache); - const grabPath = cachePath; - grabToImage(res => res.saveToFile(grabPath)); - } - } - - Process { - id: shaProc - - stdout: StdioCollector { - onStreamFinished: root.hash = text.split(" ")[0] - } - } -} diff --git a/widgets/Colouriser.qml b/widgets/Colouriser.qml deleted file mode 100644 index b621ecd..0000000 --- a/widgets/Colouriser.qml +++ /dev/null @@ -1,16 +0,0 @@ -import qs.config -import QtQuick -import QtQuick.Effects - -MultiEffect { - colorization: 1 - brightness: colorizationColor.hslLightness - - Behavior on colorizationColor { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/CustomMouseArea.qml b/widgets/CustomMouseArea.qml deleted file mode 100644 index 7c973c2..0000000 --- a/widgets/CustomMouseArea.qml +++ /dev/null @@ -1,21 +0,0 @@ -import QtQuick - -MouseArea { - property int scrollAccumulatedY: 0 - - function onWheel(event: WheelEvent): void { - } - - onWheel: event => { - // Update accumulated scroll - if (Math.sign(event.angleDelta.y) !== Math.sign(scrollAccumulatedY)) - scrollAccumulatedY = 0; - scrollAccumulatedY += event.angleDelta.y; - - // Trigger handler and reset if above threshold - if (Math.abs(scrollAccumulatedY) >= 120) { - onWheel(event); - scrollAccumulatedY = 0; - } - } -} diff --git a/widgets/CustomShortcut.qml b/widgets/CustomShortcut.qml deleted file mode 100644 index aa35ed8..0000000 --- a/widgets/CustomShortcut.qml +++ /dev/null @@ -1,5 +0,0 @@ -import Quickshell.Hyprland - -GlobalShortcut { - appid: "caelestia" -} diff --git a/widgets/CustomSpinBox.qml b/widgets/CustomSpinBox.qml deleted file mode 100644 index 7bd4d2b..0000000 --- a/widgets/CustomSpinBox.qml +++ /dev/null @@ -1,107 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -RowLayout { - id: root - - property int value - property real max: Infinity - property real min: -Infinity - property alias repeatRate: timer.interval - - signal valueModified(value: int) - - spacing: Appearance.spacing.small - - StyledTextField { - inputMethodHints: Qt.ImhFormattedNumbersOnly - text: root.value - onAccepted: root.valueModified(text) - - padding: Appearance.padding.small - leftPadding: Appearance.padding.normal - rightPadding: Appearance.padding.normal - - background: StyledRect { - implicitWidth: 100 - radius: Appearance.rounding.small - color: Colours.palette.m3surfaceContainerHigh - } - } - - StyledRect { - radius: Appearance.rounding.small - color: Colours.palette.m3primary - - implicitWidth: implicitHeight - implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 - - StateLayer { - id: upState - - color: Colours.palette.m3onPrimary - - onPressAndHold: timer.start() - onReleased: timer.stop() - - function onClicked(): void { - root.valueModified(Math.min(root.max, root.value + 1)); - } - } - - MaterialIcon { - id: upIcon - - anchors.centerIn: parent - text: "keyboard_arrow_up" - color: Colours.palette.m3onPrimary - } - } - - StyledRect { - radius: Appearance.rounding.small - color: Colours.palette.m3primary - - implicitWidth: implicitHeight - implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 - - StateLayer { - id: downState - - color: Colours.palette.m3onPrimary - - onPressAndHold: timer.start() - onReleased: timer.stop() - - function onClicked(): void { - root.valueModified(Math.max(root.min, root.value - 1)); - } - } - - MaterialIcon { - id: downIcon - - anchors.centerIn: parent - text: "keyboard_arrow_down" - color: Colours.palette.m3onPrimary - } - } - - Timer { - id: timer - - interval: 100 - repeat: true - triggeredOnStart: true - onTriggered: { - if (upState.pressed) - upState.onClicked(); - else if (downState.pressed) - downState.onClicked(); - } - } -} diff --git a/widgets/Elevation.qml b/widgets/Elevation.qml deleted file mode 100644 index 999b199..0000000 --- a/widgets/Elevation.qml +++ /dev/null @@ -1,22 +0,0 @@ -import qs.services -import qs.config -import QtQuick -import QtQuick.Effects - -RectangularShadow { - property int level - property real dp: [0, 1, 3, 6, 8, 12][level] - - color: Qt.alpha(Colours.palette.m3shadow, 0.7) - blur: (dp * 5) ** 0.7 - spread: -dp * 0.3 + (dp * 0.1) ** 2 - offset.y: dp / 2 - - Behavior on dp { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/ExtraIndicator.qml b/widgets/ExtraIndicator.qml deleted file mode 100644 index fd6f6ee..0000000 --- a/widgets/ExtraIndicator.qml +++ /dev/null @@ -1,52 +0,0 @@ -import qs.services -import qs.config -import QtQuick - -StyledRect { - required property int extra - - anchors.right: parent.right - anchors.margins: Appearance.padding.normal - - color: Colours.palette.m3tertiary - radius: Appearance.rounding.small - - implicitWidth: count.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: count.implicitHeight + Appearance.padding.small * 2 - - opacity: extra > 0 ? 1 : 0 - scale: extra > 0 ? 1 : 0.5 - - Elevation { - anchors.fill: parent - radius: parent.radius - opacity: parent.opacity - z: -1 - level: 2 - } - - StyledText { - id: count - - anchors.centerIn: parent - animate: parent.opacity > 0 - text: qsTr("+%1").arg(parent.extra) - color: Colours.palette.m3onTertiary - } - - Behavior on opacity { - NumberAnimation { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - Behavior on scale { - NumberAnimation { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } -} diff --git a/widgets/InnerBorder.qml b/widgets/InnerBorder.qml deleted file mode 100644 index 83d4e96..0000000 --- a/widgets/InnerBorder.qml +++ /dev/null @@ -1,43 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import QtQuick -import QtQuick.Effects - -StyledRect { - property alias innerRadius: maskInner.radius - property alias thickness: maskInner.anchors.margins - property alias leftThickness: maskInner.anchors.leftMargin - property alias topThickness: maskInner.anchors.topMargin - property alias rightThickness: maskInner.anchors.rightMargin - property alias bottomThickness: maskInner.anchors.bottomMargin - - anchors.fill: parent - color: Colours.palette.m3surfaceContainer - - layer.enabled: true - layer.effect: MultiEffect { - maskSource: mask - maskEnabled: true - maskInverted: true - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } - - Item { - id: mask - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - id: maskInner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - radius: Appearance.rounding.small - } - } -} diff --git a/widgets/MaterialIcon.qml b/widgets/MaterialIcon.qml deleted file mode 100644 index a1d19d3..0000000 --- a/widgets/MaterialIcon.qml +++ /dev/null @@ -1,16 +0,0 @@ -import qs.services -import qs.config - -StyledText { - property real fill - property int grade: Colours.light ? 0 : -25 - - font.family: Appearance.font.family.material - font.pointSize: Appearance.font.size.larger - font.variableAxes: ({ - FILL: fill.toFixed(1), - GRAD: grade, - opsz: fontInfo.pixelSize, - wght: fontInfo.weight - }) -} diff --git a/widgets/Ref.qml b/widgets/Ref.qml deleted file mode 100644 index 679f52f..0000000 --- a/widgets/Ref.qml +++ /dev/null @@ -1,9 +0,0 @@ -import Quickshell -import QtQuick - -QtObject { - required property Singleton service - - Component.onCompleted: service.refCount++ - Component.onDestruction: service.refCount-- -} diff --git a/widgets/StateLayer.qml b/widgets/StateLayer.qml deleted file mode 100644 index 6d263cb..0000000 --- a/widgets/StateLayer.qml +++ /dev/null @@ -1,104 +0,0 @@ -import qs.widgets -import qs.services -import qs.config -import QtQuick - -MouseArea { - id: root - - property bool disabled - property color color: Colours.palette.m3onSurface - property real radius: parent?.radius ?? 0 - - function onClicked(): void { - } - - anchors.fill: parent - - enabled: !disabled - cursorShape: disabled ? undefined : Qt.PointingHandCursor - hoverEnabled: true - - onPressed: event => { - if (disabled) - return; - - rippleAnim.x = event.x; - rippleAnim.y = event.y; - - const dist = (ox, oy) => ox * ox + oy * oy; - rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y))); - - rippleAnim.restart(); - } - - onClicked: event => !disabled && onClicked(event) - - SequentialAnimation { - id: rippleAnim - - property real x - property real y - property real radius - - PropertyAction { - target: ripple - property: "x" - value: rippleAnim.x - } - PropertyAction { - target: ripple - property: "y" - value: rippleAnim.y - } - PropertyAction { - target: ripple - property: "opacity" - value: 0.08 - } - Anim { - target: ripple - properties: "implicitWidth,implicitHeight" - from: 0 - to: rippleAnim.radius * 2 - duration: Appearance.anim.durations.normal - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - Anim { - target: ripple - property: "opacity" - to: 0 - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - StyledClippingRect { - id: hoverLayer - - anchors.fill: parent - - color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0) - radius: root.radius - - StyledRect { - id: ripple - - radius: Appearance.rounding.full - color: root.color - opacity: 0 - - transform: Translate { - x: -ripple.width / 2 - y: -ripple.height / 2 - } - } - } - - component Anim: NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } -} diff --git a/widgets/StyledBusyIndicator.qml b/widgets/StyledBusyIndicator.qml deleted file mode 100644 index 060870f..0000000 --- a/widgets/StyledBusyIndicator.qml +++ /dev/null @@ -1,90 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import QtQuick -import QtQuick.Controls -import QtQuick.Shapes - -BusyIndicator { - id: root - - property color fgColour: Colours.palette.m3onPrimaryContainer - property color bgColour: Colours.palette.m3primaryContainer - - background: null - - contentItem: Shape { - id: shape - - preferredRendererType: Shape.CurveRenderer - asynchronous: true - - RotationAnimator on rotation { - from: 0 - to: 180 - running: root.visible && root.running - loops: Animation.Infinite - duration: Appearance.anim.durations.extraLarge - easing.type: Easing.Linear - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - - ShapePath { - strokeWidth: Math.min(root.implicitWidth, root.implicitHeight) * 0.18 - strokeColor: root.bgColour - fillColor: "transparent" - capStyle: ShapePath.RoundCap - - PathAngleArc { - centerX: shape.width / 2 - centerY: shape.height / 2 - radiusX: root.implicitWidth / 2 - radiusY: root.implicitHeight / 2 - startAngle: 0 - sweepAngle: 360 - } - - Behavior on strokeColor { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - ShapePath { - strokeWidth: Math.min(root.implicitWidth, root.implicitHeight) * 0.18 - strokeColor: root.fgColour - fillColor: "transparent" - capStyle: ShapePath.RoundCap - - PathAngleArc { - centerX: shape.width / 2 - centerY: shape.height / 2 - radiusX: root.implicitWidth / 2 - radiusY: root.implicitHeight / 2 - startAngle: -sweepAngle / 2 - sweepAngle: 60 - } - - PathAngleArc { - centerX: shape.width / 2 - centerY: shape.height / 2 - radiusX: root.implicitWidth / 2 - radiusY: root.implicitHeight / 2 - startAngle: 180 - sweepAngle / 2 - sweepAngle: 60 - } - - Behavior on strokeColor { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - } -} diff --git a/widgets/StyledClippingRect.qml b/widgets/StyledClippingRect.qml deleted file mode 100644 index 8e15c4c..0000000 --- a/widgets/StyledClippingRect.qml +++ /dev/null @@ -1,17 +0,0 @@ -import qs.config -import Quickshell.Widgets -import QtQuick - -ClippingRectangle { - id: root - - color: "transparent" - - Behavior on color { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/StyledFlickable.qml b/widgets/StyledFlickable.qml deleted file mode 100644 index 7b2a75e..0000000 --- a/widgets/StyledFlickable.qml +++ /dev/null @@ -1,17 +0,0 @@ -import qs.config -import QtQuick - -Flickable { - id: root - - maximumFlickVelocity: 3000 - - rebound: Transition { - NumberAnimation { - properties: "x,y" - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/StyledListView.qml b/widgets/StyledListView.qml deleted file mode 100644 index b59eca8..0000000 --- a/widgets/StyledListView.qml +++ /dev/null @@ -1,17 +0,0 @@ -import qs.config -import QtQuick - -ListView { - id: root - - maximumFlickVelocity: 3000 - - rebound: Transition { - NumberAnimation { - properties: "x,y" - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/StyledRect.qml b/widgets/StyledRect.qml deleted file mode 100644 index c052b2a..0000000 --- a/widgets/StyledRect.qml +++ /dev/null @@ -1,16 +0,0 @@ -import qs.config -import QtQuick - -Rectangle { - id: root - - color: "transparent" - - Behavior on color { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/StyledScrollBar.qml b/widgets/StyledScrollBar.qml deleted file mode 100644 index 9e1f891..0000000 --- a/widgets/StyledScrollBar.qml +++ /dev/null @@ -1,35 +0,0 @@ -import qs.services -import qs.config -import QtQuick -import QtQuick.Controls - -ScrollBar { - id: root - - contentItem: StyledRect { - implicitWidth: 6 - opacity: root.pressed ? 1 : root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1) ? 0.8 : 0 - radius: Appearance.rounding.full - color: Colours.palette.m3secondary - - Behavior on opacity { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - CustomMouseArea { - z: -1 - anchors.fill: parent - - function onWheel(event: WheelEvent): void { - if (event.angleDelta.y > 0) - root.decrease(); - else if (event.angleDelta.y < 0) - root.increase(); - } - } -} diff --git a/widgets/StyledSwitch.qml b/widgets/StyledSwitch.qml deleted file mode 100644 index 9a7448c..0000000 --- a/widgets/StyledSwitch.qml +++ /dev/null @@ -1,159 +0,0 @@ -import qs.services -import qs.config -import QtQuick -import QtQuick.Controls -import QtQuick.Shapes - -Switch { - id: root - - implicitWidth: implicitIndicatorWidth - implicitHeight: implicitIndicatorHeight - - indicator: StyledRect { - radius: Appearance.rounding.full - color: root.checked ? Colours.palette.m3primary : Colours.palette.m3surfaceContainerHighest - - implicitWidth: implicitHeight * 1.7 - implicitHeight: Appearance.font.size.normal + Appearance.padding.smaller * 2 - - StyledRect { - readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight - - radius: Appearance.rounding.full - color: root.checked ? Colours.palette.m3onPrimary : Colours.palette.m3outline - - x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.small / 2 : Appearance.padding.small / 2 - implicitWidth: nonAnimWidth - implicitHeight: parent.implicitHeight - Appearance.padding.small - anchors.verticalCenter: parent.verticalCenter - - StyledRect { - anchors.fill: parent - radius: parent.radius - - color: root.checked ? Colours.palette.m3primary : Colours.palette.m3onSurface - opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0 - - Behavior on opacity { - NumberAnim {} - } - } - - Shape { - id: icon - - property point start1: { - if (root.pressed) - return Qt.point(width * 0.2, height / 2); - if (root.checked) - return Qt.point(width * 0.15, height / 2); - return Qt.point(width * 0.15, height * 0.15); - } - property point end1: { - if (root.pressed) { - if (root.checked) - return Qt.point(width * 0.4, height / 2); - return Qt.point(width * 0.8, height / 2); - } - if (root.checked) - return Qt.point(width * 0.4, height * 0.7); - return Qt.point(width * 0.85, height * 0.85); - } - property point start2: { - if (root.pressed) { - if (root.checked) - return Qt.point(width * 0.4, height / 2); - return Qt.point(width * 0.2, height / 2); - } - if (root.checked) - return Qt.point(width * 0.4, height * 0.7); - return Qt.point(width * 0.15, height * 0.85); - } - property point end2: { - if (root.pressed) - return Qt.point(width * 0.8, height / 2); - if (root.checked) - return Qt.point(width * 0.85, height * 0.2); - return Qt.point(width * 0.85, height * 0.15); - } - - anchors.centerIn: parent - width: height - height: parent.implicitHeight - Appearance.padding.small * 2 - preferredRendererType: Shape.CurveRenderer - asynchronous: true - - ShapePath { - strokeWidth: Appearance.font.size.larger * 0.15 - strokeColor: root.checked ? Colours.palette.m3primary : Colours.palette.m3surfaceContainerHighest - fillColor: "transparent" - capStyle: ShapePath.RoundCap - - startX: icon.start1.x - startY: icon.start1.y - - PathLine { - x: icon.end1.x - y: icon.end1.y - } - PathMove { - x: icon.start2.x - y: icon.start2.y - } - PathLine { - x: icon.end2.x - y: icon.end2.y - } - - Behavior on strokeColor { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - Behavior on start1 { - PropAnim {} - } - Behavior on end1 { - PropAnim {} - } - Behavior on start2 { - PropAnim {} - } - Behavior on end2 { - PropAnim {} - } - } - - Behavior on x { - NumberAnim {} - } - - Behavior on implicitWidth { - NumberAnim {} - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - enabled: false - } - - component NumberAnim: NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - - component PropAnim: PropertyAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } -} diff --git a/widgets/StyledText.qml b/widgets/StyledText.qml deleted file mode 100644 index 554c20c..0000000 --- a/widgets/StyledText.qml +++ /dev/null @@ -1,52 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import QtQuick - -Text { - id: root - - property bool animate: false - property string animateProp: "scale" - property real animateFrom: 0 - property real animateTo: 1 - property int animateDuration: Appearance.anim.durations.normal - - renderType: Text.NativeRendering - textFormat: Text.PlainText - color: Colours.palette.m3onSurface - font.family: Appearance.font.family.sans - font.pointSize: Appearance.font.size.smaller - - Behavior on color { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - Behavior on text { - enabled: root.animate - - SequentialAnimation { - Anim { - to: root.animateFrom - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - PropertyAction {} - Anim { - to: root.animateTo - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - - component Anim: NumberAnimation { - target: root - property: root.animateProp - duration: root.animateDuration / 2 - easing.type: Easing.BezierSpline - } -} diff --git a/widgets/StyledTextField.qml b/widgets/StyledTextField.qml deleted file mode 100644 index d4a470a..0000000 --- a/widgets/StyledTextField.qml +++ /dev/null @@ -1,85 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import QtQuick -import QtQuick.Controls - -TextField { - id: root - - color: Colours.palette.m3onSurface - placeholderTextColor: Colours.palette.m3outline - font.family: Appearance.font.family.sans - font.pointSize: Appearance.font.size.smaller - renderType: TextField.NativeRendering - cursorVisible: !readOnly - - background: null - - cursorDelegate: StyledRect { - id: cursor - - property bool disableBlink - - implicitWidth: 2 - color: Colours.palette.m3primary - radius: Appearance.rounding.normal - - Connections { - target: root - - function onCursorPositionChanged(): void { - if (root.activeFocus && root.cursorVisible) { - cursor.opacity = 1; - cursor.disableBlink = true; - enableBlink.restart(); - } - } - } - - Timer { - id: enableBlink - - interval: 100 - onTriggered: cursor.disableBlink = false - } - - Timer { - running: root.activeFocus && root.cursorVisible && !cursor.disableBlink - repeat: true - triggeredOnStart: true - interval: 500 - onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1 - } - - Binding { - when: !root.activeFocus || !root.cursorVisible - cursor.opacity: 0 - } - - Behavior on opacity { - NumberAnimation { - duration: Appearance.anim.durations.small - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - Behavior on color { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - Behavior on placeholderTextColor { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/StyledWindow.qml b/widgets/StyledWindow.qml deleted file mode 100644 index 3b402b5..0000000 --- a/widgets/StyledWindow.qml +++ /dev/null @@ -1,11 +0,0 @@ -import qs.utils -import qs.config -import Quickshell -import Quickshell.Wayland - -PanelWindow { - required property string name - - WlrLayershell.namespace: `caelestia-${name}` - color: "transparent" -} diff --git a/widgets/VerticalSlider.qml b/widgets/VerticalSlider.qml deleted file mode 100644 index 9b55a0d..0000000 --- a/widgets/VerticalSlider.qml +++ /dev/null @@ -1,136 +0,0 @@ -import qs.widgets -import qs.services -import qs.config -import QtQuick -import QtQuick.Controls - -Slider { - id: root - - required property string icon - property real oldValue - - orientation: Qt.Vertical - - background: StyledRect { - color: Colours.alpha(Colours.palette.m3surfaceContainer, true) - radius: Appearance.rounding.full - - StyledRect { - anchors.left: parent.left - anchors.right: parent.right - - y: root.handle.y - implicitHeight: parent.height - y - - color: Colours.alpha(Colours.palette.m3secondary, true) - radius: Appearance.rounding.full - } - } - - handle: Item { - id: handle - - property bool moving - - y: root.visualPosition * (root.availableHeight - height) - implicitWidth: root.width - implicitHeight: root.width - - Elevation { - anchors.fill: parent - radius: rect.radius - level: handleInteraction.containsMouse ? 2 : 1 - } - - StyledRect { - id: rect - - anchors.fill: parent - - color: Colours.alpha(Colours.palette.m3inverseSurface, true) - radius: Appearance.rounding.full - - MouseArea { - id: handleInteraction - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - acceptedButtons: Qt.NoButton - } - - MaterialIcon { - id: icon - - property bool moving: handle.moving - - function update(): void { - animate = !moving; - text = moving ? Qt.binding(() => Math.round(root.value * 100)) : Qt.binding(() => root.icon); - font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger; - font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material; - } - - animate: true - text: root.icon - color: Colours.palette.m3inverseOnSurface - anchors.centerIn: parent - - Behavior on moving { - SequentialAnimation { - NumberAnimation { - target: icon - property: "scale" - from: 1 - to: 0 - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - ScriptAction { - script: icon.update() - } - NumberAnimation { - target: icon - property: "scale" - from: 0 - to: 1 - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - } - } - } - - onPressedChanged: handle.moving = pressed - - onValueChanged: { - if (Math.abs(value - oldValue) < 0.01) - return; - oldValue = value; - handle.moving = true; - stateChangeDelay.restart(); - } - - Timer { - id: stateChangeDelay - - interval: 500 - onTriggered: { - if (!root.pressed) - handle.moving = false; - } - } - - Behavior on value { - NumberAnimation { - duration: Appearance.anim.durations.large - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/filedialog/CurrentItem.qml b/widgets/filedialog/CurrentItem.qml deleted file mode 100644 index e042445..0000000 --- a/widgets/filedialog/CurrentItem.qml +++ /dev/null @@ -1,107 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Shapes - -Item { - id: root - - required property var currentItem - - implicitWidth: content.implicitWidth + Appearance.padding.larger + content.anchors.rightMargin - implicitHeight: currentItem ? content.implicitHeight + Appearance.padding.normal + content.anchors.bottomMargin : 0 - - Shape { - preferredRendererType: Shape.CurveRenderer - - ShapePath { - id: path - - readonly property real rounding: Appearance.rounding.small - readonly property bool flatten: root.implicitHeight < rounding * 2 - readonly property real roundingY: flatten ? root.implicitHeight / 2 : rounding - - strokeWidth: -1 - fillColor: Colours.palette.m3surfaceContainer - - startX: root.implicitWidth - startY: root.implicitHeight - - PathLine { - relativeX: -(root.implicitWidth + path.rounding) - relativeY: 0 - } - PathArc { - relativeX: path.rounding - relativeY: -path.roundingY - radiusX: path.rounding - radiusY: Math.min(path.rounding, root.implicitHeight) - direction: PathArc.Counterclockwise - } - PathLine { - relativeX: 0 - relativeY: -(root.implicitHeight - path.roundingY * 2) - } - PathArc { - relativeX: path.rounding - relativeY: -path.roundingY - radiusX: path.rounding - radiusY: Math.min(path.rounding, root.implicitHeight) - } - PathLine { - relativeX: root.implicitHeight > 0 ? root.implicitWidth - path.rounding * 2 : root.implicitWidth - relativeY: 0 - } - PathArc { - relativeX: path.rounding - relativeY: -path.rounding - radiusX: path.rounding - radiusY: path.rounding - direction: PathArc.Counterclockwise - } - - Behavior on fillColor { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - } - - Item { - anchors.fill: parent - clip: true - - StyledText { - id: content - - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.rightMargin: Appearance.padding.larger - Appearance.padding.small - anchors.bottomMargin: Appearance.padding.normal - Appearance.padding.small - - text: qsTr(`"%1" selected`).arg(root.currentItem?.fileName) - } - } - - Behavior on implicitWidth { - enabled: !!root.currentItem - - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/widgets/filedialog/DialogButtons.qml b/widgets/filedialog/DialogButtons.qml deleted file mode 100644 index a64195a..0000000 --- a/widgets/filedialog/DialogButtons.qml +++ /dev/null @@ -1,93 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick.Layouts - -StyledRect { - id: root - - required property var dialog - required property FolderContents folder - - implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 - - color: Colours.palette.m3surfaceContainer - - RowLayout { - id: inner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.small - - StyledText { - text: qsTr("Filter:") - } - - StyledRect { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.rightMargin: Appearance.spacing.normal - - color: Colours.palette.m3surfaceContainerHigh - radius: Appearance.rounding.small - - StyledText { - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - text: `${root.dialog.filterLabel} (${root.dialog.filters.map(f => `*.${f}`).join(", ")})` - } - } - - StyledRect { - color: Colours.palette.m3surfaceContainerHigh - radius: Appearance.rounding.small - - implicitWidth: cancelText.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: cancelText.implicitHeight + Appearance.padding.normal * 2 - - StateLayer { - disabled: !root.dialog.selectionValid - - function onClicked(): void { - root.dialog.accepted(root.folder.currentItem.filePath); - } - } - - StyledText { - id: selectText - - anchors.centerIn: parent - anchors.margins: Appearance.padding.normal - - text: qsTr("Select") - color: root.dialog.selectionValid ? Colours.palette.m3onSurface : Colours.palette.m3outline - } - } - - StyledRect { - color: Colours.palette.m3surfaceContainerHigh - radius: Appearance.rounding.small - - implicitWidth: cancelText.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: cancelText.implicitHeight + Appearance.padding.normal * 2 - - StateLayer { - function onClicked(): void { - root.dialog.rejected(); - } - } - - StyledText { - id: cancelText - - anchors.centerIn: parent - anchors.margins: Appearance.padding.normal - - text: qsTr("Cancel") - } - } - } -} diff --git a/widgets/filedialog/FileDialog.qml b/widgets/filedialog/FileDialog.qml deleted file mode 100644 index a533243..0000000 --- a/widgets/filedialog/FileDialog.qml +++ /dev/null @@ -1,106 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import Quickshell -import QtQuick -import QtQuick.Layouts - -LazyLoader { - id: loader - - property list cwd: ["Home"] - property string filterLabel: "All files" - property list filters: ["*"] - property string title: qsTr("Select a file") - - signal accepted(path: string) - signal rejected - - function open(): void { - activeAsync = true; - } - - function close(): void { - rejected(); - } - - onAccepted: activeAsync = false - onRejected: activeAsync = false - - FloatingWindow { - id: root - - property list cwd: loader.cwd - property string filterLabel: loader.filterLabel - property list filters: loader.filters - - readonly property bool selectionValid: { - const item = folderContents.currentItem; - return item && !item.fileIsDir && (filters.includes("*") || filters.includes(item.fileSuffix)); - } - - function accepted(path: string): void { - loader.accepted(path); - } - - function rejected(): void { - loader.rejected(); - } - - implicitWidth: 1000 - implicitHeight: 600 - color: Colours.palette.m3surface - title: loader.title - - onVisibleChanged: { - if (!visible) - rejected(); - } - - RowLayout { - anchors.fill: parent - - spacing: 0 - - Sidebar { - Layout.fillHeight: true - dialog: root - } - - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - - spacing: 0 - - HeaderBar { - Layout.fillWidth: true - dialog: root - } - - FolderContents { - id: folderContents - - Layout.fillWidth: true - Layout.fillHeight: true - dialog: root - } - - DialogButtons { - Layout.fillWidth: true - dialog: root - folder: folderContents - } - } - } - - Behavior on color { - ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } -} diff --git a/widgets/filedialog/FolderContents.qml b/widgets/filedialog/FolderContents.qml deleted file mode 100644 index 4a24ef5..0000000 --- a/widgets/filedialog/FolderContents.qml +++ /dev/null @@ -1,222 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import qs.utils -import Quickshell -import Quickshell.Io -import QtQuick -import QtQuick.Layouts -import QtQuick.Effects -import QtQuick.Controls -import Qt.labs.folderlistmodel - -Item { - id: root - - required property var dialog - property alias currentItem: view.currentItem - - StyledRect { - anchors.fill: parent - color: Colours.palette.m3surfaceContainer - - layer.enabled: true - layer.effect: MultiEffect { - maskSource: mask - maskEnabled: true - maskInverted: true - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } - } - - Item { - id: mask - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - anchors.fill: parent - anchors.margins: Appearance.padding.small - radius: Appearance.rounding.small - } - } - - Loader { - anchors.centerIn: parent - active: view.count === 0 - asynchronous: true - sourceComponent: ColumnLayout { - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "scan_delete" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.extraLarge * 2 - font.weight: 500 - } - - StyledText { - text: qsTr("This folder is empty") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - } - } - - GridView { - id: view - - anchors.fill: parent - anchors.margins: Appearance.padding.small + Appearance.padding.normal - - cellWidth: Sizes.itemWidth + Appearance.spacing.small - cellHeight: Sizes.itemWidth + Appearance.spacing.small * 2 + Appearance.padding.normal * 2 + 1 - - clip: true - focus: true - currentIndex: -1 - Keys.onEscapePressed: currentIndex = -1 - - Keys.onReturnPressed: { - if (root.dialog.selectionValid) - root.dialog.accepted(currentItem.filePath); - } - Keys.onEnterPressed: { - if (root.dialog.selectionValid) - root.dialog.accepted(currentItem.filePath); - } - - ScrollBar.vertical: StyledScrollBar {} - - model: FolderListModel { - showDirsFirst: true - folder: { - let url = "file://"; - if (root.dialog.cwd[0] === "Home") - url += `${Paths.strip(Paths.home)}/${root.dialog.cwd.slice(1).join("/")}`; - else - url += root.dialog.cwd.join("/"); - return url; - } - onFolderChanged: view.currentIndex = -1 - } - - delegate: StyledRect { - id: item - - required property int index - required property string fileName - required property string filePath - required property url fileUrl - required property string fileSuffix - required property bool fileIsDir - - readonly property real nonAnimHeight: icon.implicitHeight + name.anchors.topMargin + name.implicitHeight + Appearance.padding.normal * 2 - - implicitWidth: Sizes.itemWidth - implicitHeight: nonAnimHeight - - radius: Appearance.rounding.normal - color: GridView.isCurrentItem ? Colours.palette.m3surfaceContainerHighest : "transparent" - z: GridView.isCurrentItem || implicitHeight !== nonAnimHeight ? 1 : 0 - clip: true - - StateLayer { - onDoubleClicked: { - if (item.fileIsDir) - root.dialog.cwd.push(item.fileName); - else if (root.dialog.selectionValid) - root.dialog.accepted(item.filePath); - } - - function onClicked(): void { - view.currentIndex = item.index; - } - } - - CachingIconImage { - id: icon - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: Appearance.padding.normal - - asynchronous: true - implicitSize: Sizes.itemWidth - Appearance.padding.normal * 2 - source: { - if (!item.fileIsDir) - return Quickshell.iconPath("application-x-zerosize"); - - const name = item.fileName; - if (root.dialog.cwd.length === 1 && ["Desktop", "Documents", "Downloads", "Music", "Pictures", "Public", "Templates", "Videos"].includes(name)) - return Quickshell.iconPath(`folder-${name.toLowerCase()}`); - - return Quickshell.iconPath("inode-directory"); - } - - onStatusChanged: { - if (status === Image.Error) - source = Quickshell.iconPath("error"); - } - - Process { - running: !item.fileIsDir - command: ["file", "--mime", "-b", item.filePath] - stdout: StdioCollector { - onStreamFinished: { - const mime = text.split(";")[0].replace("/", "-"); - icon.source = Images.validImageTypes.some(t => mime === `image-${t}`) ? item.fileUrl : Quickshell.iconPath(mime, "image-missing"); - } - } - } - } - - StyledText { - id: name - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: icon.bottom - anchors.topMargin: Appearance.spacing.small - anchors.margins: Appearance.padding.normal - - horizontalAlignment: Text.AlignHCenter - text: item.fileName - elide: item.GridView.isCurrentItem ? Text.ElideNone : Text.ElideRight - wrapMode: item.GridView.isCurrentItem ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap - } - - Behavior on implicitHeight { - Anim {} - } - } - - populate: Transition { - Anim { - property: "scale" - from: 0.7 - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - - CurrentItem { - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: Appearance.padding.small - - currentItem: view.currentItem - } - - component Anim: NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } -} diff --git a/widgets/filedialog/HeaderBar.qml b/widgets/filedialog/HeaderBar.qml deleted file mode 100644 index 4af9672..0000000 --- a/widgets/filedialog/HeaderBar.qml +++ /dev/null @@ -1,142 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - required property var dialog - - implicitWidth: inner.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 - - color: Colours.palette.m3surfaceContainer - - RowLayout { - id: inner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.small - - Item { - implicitWidth: implicitHeight - implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 - - StateLayer { - radius: Appearance.rounding.small - disabled: root.dialog.cwd.length === 1 - - function onClicked(): void { - root.dialog.cwd.pop(); - } - } - - MaterialIcon { - id: upIcon - - anchors.centerIn: parent - text: "drive_folder_upload" - color: root.dialog.cwd.length === 1 ? Colours.palette.m3outline : Colours.palette.m3onSurface - grade: 200 - } - } - - StyledRect { - Layout.fillWidth: true - - radius: Appearance.rounding.small - color: Colours.palette.m3surfaceContainerHigh - - implicitHeight: pathComponents.implicitHeight + pathComponents.anchors.margins * 2 - - RowLayout { - id: pathComponents - - anchors.fill: parent - anchors.margins: Appearance.padding.small / 2 - anchors.leftMargin: 0 - - spacing: Appearance.spacing.small - - Repeater { - model: root.dialog.cwd - - RowLayout { - id: folder - - required property string modelData - required property int index - - spacing: 0 - - Loader { - Layout.rightMargin: Appearance.spacing.small - active: folder.index > 0 - asynchronous: true - sourceComponent: StyledText { - text: "/" - color: Colours.palette.m3onSurfaceVariant - font.bold: true - } - } - - Item { - implicitWidth: homeIcon.implicitWidth + (homeIcon.active ? Appearance.padding.small : 0) + folderName.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: folderName.implicitHeight + Appearance.padding.small * 2 - - Loader { - anchors.fill: parent - active: folder.index < root.dialog.cwd.length - 1 - asynchronous: true - sourceComponent: StateLayer { - radius: Appearance.rounding.small - - function onClicked(): void { - root.dialog.cwd = root.dialog.cwd.slice(0, folder.index + 1); - } - } - } - - Loader { - id: homeIcon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.padding.normal - - active: folder.index === 0 && folder.modelData === "Home" - asynchronous: true - sourceComponent: MaterialIcon { - text: "home" - color: root.dialog.cwd.length === 1 ? Colours.palette.m3onSurface : Colours.palette.m3onSurfaceVariant - fill: 1 - } - } - - StyledText { - id: folderName - - anchors.left: homeIcon.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: homeIcon.active ? Appearance.padding.small : 0 - - text: folder.modelData - color: folder.index < root.dialog.cwd.length - 1 ? Colours.palette.m3onSurfaceVariant : Colours.palette.m3onSurface - font.bold: true - } - } - } - } - - Item { - Layout.fillWidth: true - } - } - } - } -} diff --git a/widgets/filedialog/Sidebar.qml b/widgets/filedialog/Sidebar.qml deleted file mode 100644 index 82a1dd5..0000000 --- a/widgets/filedialog/Sidebar.qml +++ /dev/null @@ -1,117 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - required property var dialog - - implicitWidth: Sizes.sidebarWidth - implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 - - color: Colours.palette.m3surfaceContainer - - ColumnLayout { - id: inner - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.small / 2 - - StyledText { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Appearance.padding.small / 2 - Layout.bottomMargin: Appearance.spacing.normal - text: qsTr("Files") - color: Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.larger - font.bold: true - } - - Repeater { - model: ["Home", "Downloads", "Desktop", "Documents", "Music", "Pictures", "Videos"] - - StyledRect { - id: place - - required property string modelData - readonly property bool selected: modelData === root.dialog.cwd[root.dialog.cwd.length - 1] - - Layout.fillWidth: true - implicitHeight: placeInner.implicitHeight + Appearance.padding.normal * 2 - - radius: Appearance.rounding.full - color: selected ? Colours.palette.m3secondaryContainer : "transparent" - - StateLayer { - color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - - function onClicked(): void { - if (place.modelData === "Home") - root.dialog.cwd = ["Home"]; - else - root.dialog.cwd = ["Home", place.modelData]; - } - } - - RowLayout { - id: placeInner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large - - spacing: Appearance.spacing.normal - - MaterialIcon { - text: { - const p = place.modelData; - if (p === "Home") - return "home"; - if (p === "Downloads") - return "file_download"; - if (p === "Desktop") - return "desktop_windows"; - if (p === "Documents") - return "description"; - if (p === "Music") - return "music_note"; - if (p === "Pictures") - return "image"; - if (p === "Videos") - return "video_library"; - return "folder"; - } - color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.large - fill: place.selected ? 1 : 0 - - Behavior on fill { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - StyledText { - Layout.fillWidth: true - text: place.modelData - color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.normal - elide: Text.ElideRight - } - } - } - } - } -} diff --git a/widgets/filedialog/Sizes.qml b/widgets/filedialog/Sizes.qml deleted file mode 100644 index 2ad31f9..0000000 --- a/widgets/filedialog/Sizes.qml +++ /dev/null @@ -1,8 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - property int itemWidth: 103 - property int sidebarWidth: 200 -} -- cgit v1.2.3-freya