Merge pull request #23 from Zacharias-Brohn/settingsWindow
Merge settings window to main
This commit was merged in pull request #23.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
./result/
|
||||||
.pyre/
|
.pyre/
|
||||||
.cache/
|
.cache/
|
||||||
.venv/
|
.venv/
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property color color: DynamicColors.palette.m3secondary
|
||||||
|
required property string icon
|
||||||
|
property bool initialized: false
|
||||||
|
readonly property bool isHorizontal: orientation === Qt.Horizontal
|
||||||
|
readonly property bool isVertical: orientation === Qt.Vertical
|
||||||
|
property real multiplier: 100
|
||||||
|
property real oldValue
|
||||||
|
|
||||||
|
// Wrapper components can inject their own track visuals here.
|
||||||
|
property Component trackContent
|
||||||
|
|
||||||
|
// Keep current behavior for existing usages.
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
|
||||||
|
background: CustomRect {
|
||||||
|
id: groove
|
||||||
|
|
||||||
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||||
|
height: root.availableHeight
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
width: root.availableWidth
|
||||||
|
x: root.leftPadding
|
||||||
|
y: root.topPadding
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: trackLoader
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceComponent: root.trackContent
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
item.rootSlider = root;
|
||||||
|
item.groove = groove;
|
||||||
|
item.handleItem = handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle: Item {
|
||||||
|
id: handle
|
||||||
|
|
||||||
|
property alias moving: icon.moving
|
||||||
|
|
||||||
|
implicitHeight: Math.min(root.width, root.height)
|
||||||
|
implicitWidth: Math.min(root.width, root.height)
|
||||||
|
x: root.isHorizontal ? root.leftPadding + root.visualPosition * (root.availableWidth - width) : root.leftPadding + (root.availableWidth - width) / 2
|
||||||
|
y: root.isVertical ? root.topPadding + root.visualPosition * (root.availableHeight - height) : root.topPadding + (root.availableHeight - height) / 2
|
||||||
|
|
||||||
|
Elevation {
|
||||||
|
anchors.fill: parent
|
||||||
|
level: handleInteraction.containsMouse ? 2 : 1
|
||||||
|
radius: rect.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: rect
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.palette.m3inverseSurface
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: handleInteraction
|
||||||
|
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
property bool moving
|
||||||
|
|
||||||
|
function update(): void {
|
||||||
|
animate = !moving;
|
||||||
|
binding.when = moving;
|
||||||
|
font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger;
|
||||||
|
font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: DynamicColors.palette.m3inverseOnSurface
|
||||||
|
text: root.icon
|
||||||
|
|
||||||
|
onMovingChanged: anim.restart()
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
id: binding
|
||||||
|
|
||||||
|
property: "text"
|
||||||
|
target: icon
|
||||||
|
value: Math.round(root.value * root.multiplier)
|
||||||
|
when: false
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: anim
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal / 2
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
|
property: "scale"
|
||||||
|
target: icon
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptAction {
|
||||||
|
script: icon.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal / 2
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
|
property: "scale"
|
||||||
|
target: icon
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on value {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressedChanged: handle.moving = pressed
|
||||||
|
onValueChanged: {
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
oldValue = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property real arcStartAngle: 0.75 * Math.PI
|
||||||
|
readonly property real arcSweep: 1.5 * Math.PI
|
||||||
|
property real currentHue: 0
|
||||||
|
property bool dragActive: false
|
||||||
|
required property var drawing
|
||||||
|
readonly property real handleAngle: hueToAngle(currentHue)
|
||||||
|
readonly property real handleCenterX: width / 2 + radius * Math.cos(handleAngle)
|
||||||
|
readonly property real handleCenterY: height / 2 + radius * Math.sin(handleAngle)
|
||||||
|
property real handleSize: 32
|
||||||
|
property real lastChromaticHue: 0
|
||||||
|
readonly property real radius: (Math.min(width, height) - handleSize) / 2
|
||||||
|
readonly property int segmentCount: 240
|
||||||
|
readonly property color thumbColor: DynamicColors.palette.m3inverseSurface
|
||||||
|
readonly property color thumbContentColor: DynamicColors.palette.m3inverseOnSurface
|
||||||
|
readonly property color trackColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||||
|
|
||||||
|
function hueToAngle(hue) {
|
||||||
|
return arcStartAngle + arcSweep * hue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAngle(angle) {
|
||||||
|
const tau = Math.PI * 2;
|
||||||
|
let a = angle % tau;
|
||||||
|
if (a < 0)
|
||||||
|
a += tau;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointIsOnTrack(x, y) {
|
||||||
|
const cx = width / 2;
|
||||||
|
const cy = height / 2;
|
||||||
|
const dx = x - cx;
|
||||||
|
const dy = y - cy;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
return distance >= radius - handleSize / 2 && distance <= radius + handleSize / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncFromPenColor() {
|
||||||
|
if (!drawing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const c = drawing.penColor;
|
||||||
|
|
||||||
|
if (c.hsvSaturation > 0) {
|
||||||
|
currentHue = c.hsvHue;
|
||||||
|
lastChromaticHue = c.hsvHue;
|
||||||
|
} else {
|
||||||
|
currentHue = lastChromaticHue;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.requestPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHueFromPoint(x, y, force = false) {
|
||||||
|
const cx = width / 2;
|
||||||
|
const cy = height / 2;
|
||||||
|
const dx = x - cx;
|
||||||
|
const dy = y - cy;
|
||||||
|
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (!force && (distance < radius - handleSize / 2 || distance > radius + handleSize / 2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const angle = normalizeAngle(Math.atan2(dy, dx));
|
||||||
|
const start = normalizeAngle(arcStartAngle);
|
||||||
|
|
||||||
|
let relative = angle - start;
|
||||||
|
if (relative < 0)
|
||||||
|
relative += Math.PI * 2;
|
||||||
|
|
||||||
|
if (relative > arcSweep) {
|
||||||
|
const gap = Math.PI * 2 - arcSweep;
|
||||||
|
relative = relative < arcSweep + gap / 2 ? arcSweep : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentHue = relative / arcSweep;
|
||||||
|
lastChromaticHue = currentHue;
|
||||||
|
drawing.penColor = Qt.hsva(currentHue, drawing.penColor.hsvSaturation, drawing.penColor.hsvValue, drawing.penColor.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: 180
|
||||||
|
implicitWidth: 220
|
||||||
|
|
||||||
|
Component.onCompleted: syncFromPenColor()
|
||||||
|
onCurrentHueChanged: canvas.requestPaint()
|
||||||
|
onDrawingChanged: syncFromPenColor()
|
||||||
|
onHandleSizeChanged: canvas.requestPaint()
|
||||||
|
onHeightChanged: canvas.requestPaint()
|
||||||
|
onWidthChanged: canvas.requestPaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onPenColorChanged() {
|
||||||
|
root.syncFromPenColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root.drawing
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: canvas
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
renderStrategy: Canvas.Threaded
|
||||||
|
renderTarget: Canvas.Image
|
||||||
|
|
||||||
|
Component.onCompleted: requestPaint()
|
||||||
|
onPaint: {
|
||||||
|
const ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
const cx = width / 2;
|
||||||
|
const cy = height / 2;
|
||||||
|
const radius = root.radius;
|
||||||
|
const trackWidth = root.handleSize;
|
||||||
|
|
||||||
|
// Background track: always show the full hue spectrum
|
||||||
|
for (let i = 0; i < root.segmentCount; ++i) {
|
||||||
|
const t1 = i / root.segmentCount;
|
||||||
|
const t2 = (i + 1) / root.segmentCount;
|
||||||
|
const a1 = root.arcStartAngle + root.arcSweep * t1;
|
||||||
|
const a2 = root.arcStartAngle + root.arcSweep * t2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, radius, a1, a2);
|
||||||
|
ctx.lineWidth = trackWidth;
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
ctx.strokeStyle = Qt.hsla(t1, 1.0, 0.5, 1.0);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: handle
|
||||||
|
|
||||||
|
height: root.handleSize
|
||||||
|
width: root.handleSize
|
||||||
|
x: root.handleCenterX - width / 2
|
||||||
|
y: root.handleCenterY - height / 2
|
||||||
|
z: 1
|
||||||
|
|
||||||
|
Elevation {
|
||||||
|
anchors.fill: parent
|
||||||
|
level: handleHover.containsMouse ? 2 : 1
|
||||||
|
radius: rect.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rect
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
color: root.thumbColor
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: handleHover
|
||||||
|
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: root.drawing ? root.drawing.penColor : Qt.hsla(root.currentHue, 1.0, 0.5, 1.0)
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
width: parent.width - 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: dragArea
|
||||||
|
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onCanceled: {
|
||||||
|
root.dragActive = false;
|
||||||
|
}
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if ((mouse.buttons & Qt.LeftButton) && root.dragActive)
|
||||||
|
root.updateHueFromPoint(mouse.x, mouse.y, true);
|
||||||
|
}
|
||||||
|
onPressed: mouse => {
|
||||||
|
root.dragActive = root.pointIsOnTrack(mouse.x, mouse.y);
|
||||||
|
if (root.dragActive)
|
||||||
|
root.updateHueFromPoint(mouse.x, mouse.y);
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
root.dragActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
required property color bgColor
|
property color bgColor: DynamicColors.palette.m3primary
|
||||||
property int radius: 4
|
property int radius: 4
|
||||||
required property color textColor
|
property color textColor: DynamicColors.palette.m3onPrimary
|
||||||
|
|
||||||
background: CustomRect {
|
background: CustomRect {
|
||||||
color: control.bgColor
|
color: control.bgColor
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import qs.Config
|
|||||||
RadioButton {
|
RadioButton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
font.pointSize: 12
|
font.pointSize: Appearance.font.size.normal
|
||||||
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
|
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
|
||||||
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
|
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ RowLayout {
|
|||||||
CustomTextField {
|
CustomTextField {
|
||||||
id: textField
|
id: textField
|
||||||
|
|
||||||
|
implicitHeight: upButton.implicitHeight
|
||||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||||
leftPadding: Appearance.padding.normal
|
leftPadding: Appearance.padding.normal
|
||||||
padding: Appearance.padding.small
|
padding: Appearance.padding.small
|
||||||
@@ -37,7 +38,7 @@ RowLayout {
|
|||||||
background: CustomRect {
|
background: CustomRect {
|
||||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||||
implicitWidth: 100
|
implicitWidth: 100
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.full
|
||||||
}
|
}
|
||||||
validator: DoubleValidator {
|
validator: DoubleValidator {
|
||||||
bottom: root.min
|
bottom: root.min
|
||||||
@@ -82,10 +83,12 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
|
id: upButton
|
||||||
|
|
||||||
color: DynamicColors.palette.m3primary
|
color: DynamicColors.palette.m3primary
|
||||||
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
|
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
|
||||||
implicitWidth: implicitHeight
|
implicitWidth: implicitHeight
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
id: upState
|
id: upState
|
||||||
@@ -119,7 +122,7 @@ RowLayout {
|
|||||||
color: DynamicColors.palette.m3primary
|
color: DynamicColors.palette.m3primary
|
||||||
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
|
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
|
||||||
implicitWidth: implicitHeight
|
implicitWidth: implicitHeight
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
id: downState
|
id: downState
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: root
|
id: root
|
||||||
@@ -29,8 +30,29 @@ Row {
|
|||||||
property int type: CustomSplitButton.Filled
|
property int type: CustomSplitButton.Filled
|
||||||
property real verticalPadding: Appearance.padding.smaller
|
property real verticalPadding: Appearance.padding.smaller
|
||||||
|
|
||||||
|
function closeDropdown(): void {
|
||||||
|
SettingsDropdowns.close(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDropdown(): void {
|
||||||
|
if (root.disabled)
|
||||||
|
return;
|
||||||
|
SettingsDropdowns.open(menu, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDropdown(): void {
|
||||||
|
if (root.disabled)
|
||||||
|
return;
|
||||||
|
SettingsDropdowns.toggle(menu, root);
|
||||||
|
}
|
||||||
|
|
||||||
spacing: Math.floor(Appearance.spacing.small / 2)
|
spacing: Math.floor(Appearance.spacing.small / 2)
|
||||||
|
|
||||||
|
onExpandedChanged: {
|
||||||
|
if (!expanded)
|
||||||
|
SettingsDropdowns.forget(menu);
|
||||||
|
}
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
bottomRightRadius: Appearance.rounding.small / 2
|
bottomRightRadius: Appearance.rounding.small / 2
|
||||||
color: root.disabled ? root.disabledColor : root.color
|
color: root.disabled ? root.disabledColor : root.color
|
||||||
@@ -109,7 +131,7 @@ Row {
|
|||||||
id: expandStateLayer
|
id: expandStateLayer
|
||||||
|
|
||||||
function onClicked(): void {
|
function onClicked(): void {
|
||||||
root.expanded = !root.expanded;
|
root.toggleDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
color: root.textColor
|
color: root.textColor
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import QtQuick
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
CustomRect {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias active: splitButton.active
|
property alias active: splitButton.active
|
||||||
@@ -18,38 +18,40 @@ CustomRect {
|
|||||||
signal selected(item: MenuItem)
|
signal selected(item: MenuItem)
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
|
||||||
clip: false
|
clip: false
|
||||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
z: root.expanded ? expandedZ : -1
|
||||||
implicitHeight: row.implicitHeight + Appearance.padding.large * 2
|
|
||||||
opacity: enabled ? 1.0 : 0.5
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.left: parent.left
|
||||||
anchors.margins: Appearance.padding.large
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
CustomText {
|
CustomText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
|
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
text: root.label
|
text: root.label
|
||||||
|
z: root.expanded ? root.expandedZ : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomSplitButton {
|
CustomSplitButton {
|
||||||
id: splitButton
|
id: splitButton
|
||||||
|
|
||||||
enabled: root.enabled
|
enabled: root.enabled
|
||||||
menu.z: 1
|
|
||||||
type: CustomSplitButton.Filled
|
type: CustomSplitButton.Filled
|
||||||
|
z: root.expanded ? root.expandedZ : -1
|
||||||
|
|
||||||
menu.onItemSelected: item => {
|
menu.onItemSelected: item => {
|
||||||
root.selected(item);
|
root.selected(item);
|
||||||
|
splitButton.closeDropdown();
|
||||||
}
|
}
|
||||||
stateLayer.onClicked: {
|
stateLayer.onClicked: {
|
||||||
splitButton.expanded = !splitButton.expanded;
|
splitButton.toggleDropdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
selectedTextColor: DynamicColors.palette.m3onSecondaryContainer
|
||||||
|
selectionColor: DynamicColors.tPalette.colSecondaryContainer
|
||||||
|
|
||||||
|
font {
|
||||||
|
family: Appearance?.font.family.sans ?? "sans-serif"
|
||||||
|
hintingPreference: Font.PreferFullHinting
|
||||||
|
pixelSize: Appearance?.font.size.normal ?? 15
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
required property string name
|
||||||
|
|
||||||
|
WlrLayershell.namespace: `ZShell-${name}`
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
+16
-128
@@ -1,141 +1,29 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Templates
|
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
Slider {
|
BaseStyledSlider {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property color color: DynamicColors.palette.m3secondary
|
trackContent: Component {
|
||||||
required property string icon
|
Item {
|
||||||
property bool initialized
|
property var groove
|
||||||
property real oldValue
|
readonly property real handleHeight: handleItem ? handleItem.height : 0
|
||||||
|
property var handleItem
|
||||||
|
readonly property real handleWidth: handleItem ? handleItem.width : 0
|
||||||
|
|
||||||
orientation: Qt.Vertical
|
// Set by BaseStyledSlider's Loader
|
||||||
|
property var rootSlider
|
||||||
background: CustomRect {
|
|
||||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
color: root.color
|
|
||||||
implicitHeight: parent.height - y
|
|
||||||
radius: parent.radius
|
|
||||||
y: root.handle.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handle: Item {
|
|
||||||
id: handle
|
|
||||||
|
|
||||||
property alias moving: icon.moving
|
|
||||||
|
|
||||||
implicitHeight: root.width
|
|
||||||
implicitWidth: root.width
|
|
||||||
y: root.visualPosition * (root.availableHeight - height)
|
|
||||||
|
|
||||||
Elevation {
|
|
||||||
anchors.fill: parent
|
|
||||||
level: handleInteraction.containsMouse ? 2 : 1
|
|
||||||
radius: rect.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: rect
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: DynamicColors.palette.m3inverseSurface
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
|
|
||||||
MouseArea {
|
CustomRect {
|
||||||
id: handleInteraction
|
color: rootSlider?.color
|
||||||
|
height: rootSlider?.isVertical ? handleHeight + (1 - rootSlider?.visualPosition) * (groove?.height - handleHeight) : groove?.height
|
||||||
acceptedButtons: Qt.NoButton
|
radius: groove?.radius
|
||||||
anchors.fill: parent
|
width: rootSlider?.isHorizontal ? handleWidth + rootSlider?.visualPosition * (groove?.width - handleWidth) : groove?.width
|
||||||
cursorShape: Qt.PointingHandCursor
|
x: rootSlider?.isHorizontal ? (rootSlider?.mirrored ? groove?.width - width : 0) : 0
|
||||||
hoverEnabled: true
|
y: rootSlider?.isVertical ? groove?.height - height : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
id: icon
|
|
||||||
|
|
||||||
property bool moving
|
|
||||||
|
|
||||||
function update(): void {
|
|
||||||
animate = !moving;
|
|
||||||
binding.when = moving;
|
|
||||||
font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger;
|
|
||||||
font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material;
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: DynamicColors.palette.m3inverseOnSurface
|
|
||||||
text: root.icon
|
|
||||||
|
|
||||||
onMovingChanged: anim.restart()
|
|
||||||
|
|
||||||
Binding {
|
|
||||||
id: binding
|
|
||||||
|
|
||||||
property: "text"
|
|
||||||
target: icon
|
|
||||||
value: Math.round(root.value * 100)
|
|
||||||
when: false
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: anim
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.normal / 2
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
|
||||||
property: "scale"
|
|
||||||
target: icon
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptAction {
|
|
||||||
script: icon.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.normal / 2
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
|
||||||
property: "scale"
|
|
||||||
target: icon
|
|
||||||
to: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on value {
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.large
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressedChanged: handle.moving = pressed
|
|
||||||
onValueChanged: {
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
BaseStyledSlider {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real alpha: 1.0
|
||||||
|
property real brightness: 1.0
|
||||||
|
property string channel: "saturation"
|
||||||
|
readonly property color currentColor: Qt.hsva(hue, channel === "saturation" ? value : saturation, channel === "brightness" ? value : brightness, alpha)
|
||||||
|
property real hue: 0.0
|
||||||
|
property real saturation: 1.0
|
||||||
|
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
|
||||||
|
trackContent: Component {
|
||||||
|
Item {
|
||||||
|
property var groove
|
||||||
|
property var handleItem
|
||||||
|
property var rootSlider
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: true
|
||||||
|
color: "transparent"
|
||||||
|
radius: groove?.radius ?? 0
|
||||||
|
|
||||||
|
gradient: Gradient {
|
||||||
|
orientation: rootSlider?.isHorizontal ? Gradient.Horizontal : Gradient.Vertical
|
||||||
|
|
||||||
|
GradientStop {
|
||||||
|
color: root.channel === "saturation" ? Qt.hsva(root.hue, 0.0, root.brightness, root.alpha) : Qt.hsva(root.hue, root.saturation, 0.0, root.alpha)
|
||||||
|
position: 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
GradientStop {
|
||||||
|
color: root.channel === "saturation" ? Qt.hsva(root.hue, 1.0, root.brightness, root.alpha) : Qt.hsva(root.hue, root.saturation, 1.0, root.alpha)
|
||||||
|
position: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ CustomText {
|
|||||||
property int grade: DynamicColors.light ? 0 : -25
|
property int grade: DynamicColors.light ? 0 : -25
|
||||||
|
|
||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pointSize: 15
|
font.pointSize: Appearance.font.size.larger
|
||||||
font.variableAxes: ({
|
font.variableAxes: ({
|
||||||
FILL: fill.toFixed(1),
|
FILL: fill.toFixed(1),
|
||||||
GRAD: grade,
|
GRAD: grade,
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Path {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property real viewHeight
|
||||||
|
required property real viewWidth
|
||||||
|
|
||||||
|
startX: root.viewWidth / 2
|
||||||
|
startY: 0
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 0.25
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
x: root.viewWidth / 2
|
||||||
|
y: root.viewHeight * (1 / 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 0.45
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
x: root.viewWidth / 2
|
||||||
|
y: root.viewHeight * (2 / 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 0.70
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
x: root.viewWidth / 2
|
||||||
|
y: root.viewHeight * (3 / 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 1.00
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
x: root.viewWidth / 2
|
||||||
|
y: root.viewHeight * (4 / 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 0.70
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
x: root.viewWidth / 2
|
||||||
|
y: root.viewHeight * (5 / 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 0.45
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
x: root.viewWidth / 2
|
||||||
|
y: root.viewHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
PathAttribute {
|
||||||
|
name: "itemOpacity"
|
||||||
|
value: 0.25
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Elevation {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property int currentIndex
|
||||||
|
property bool expanded
|
||||||
|
required property int from
|
||||||
|
property color insideTextColor: DynamicColors.palette.m3onPrimary
|
||||||
|
property int itemHeight
|
||||||
|
property int listHeight: 200
|
||||||
|
property color outsideTextColor: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
readonly property var spinnerModel: root.range(root.from, root.to)
|
||||||
|
required property int to
|
||||||
|
property Item triggerItem
|
||||||
|
|
||||||
|
signal itemSelected(item: int)
|
||||||
|
|
||||||
|
function range(first, last) {
|
||||||
|
let out = [];
|
||||||
|
for (let i = first; i <= last; ++i)
|
||||||
|
out.push(i);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: root.expanded ? view.implicitHeight : 0
|
||||||
|
level: root.expanded ? 2 : 0
|
||||||
|
radius: itemHeight / 2
|
||||||
|
visible: implicitHeight > 0
|
||||||
|
|
||||||
|
Behavior on implicitHeight {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpandedChanged: {
|
||||||
|
if (!root.expanded)
|
||||||
|
root.itemSelected(view.currentIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: spinnerDelegate
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: wrapper
|
||||||
|
|
||||||
|
readonly property color delegateTextColor: wrapper.PathView.view ? wrapper.PathView.view.delegateTextColor : "white"
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
height: root.itemHeight
|
||||||
|
opacity: wrapper.PathView.itemOpacity
|
||||||
|
visible: wrapper.PathView.onPath
|
||||||
|
width: wrapper.PathView.view ? wrapper.PathView.view.width : 0
|
||||||
|
z: wrapper.PathView.isCurrentItem ? 100 : Math.round(wrapper.PathView.itemScale * 100)
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: wrapper.delegateTextColor
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: wrapper.modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.palette.m3surfaceContainer
|
||||||
|
radius: parent.radius
|
||||||
|
|
||||||
|
// Main visible spinner: normal/outside text color
|
||||||
|
PathView {
|
||||||
|
id: view
|
||||||
|
|
||||||
|
property color delegateTextColor: root.outsideTextColor
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
clip: true
|
||||||
|
currentIndex: root.currentIndex - 1
|
||||||
|
delegate: spinnerDelegate
|
||||||
|
dragMargin: width
|
||||||
|
highlightRangeMode: PathView.StrictlyEnforceRange
|
||||||
|
implicitHeight: root.listHeight
|
||||||
|
model: root.spinnerModel
|
||||||
|
pathItemCount: 7
|
||||||
|
preferredHighlightBegin: 0.5
|
||||||
|
preferredHighlightEnd: 0.5
|
||||||
|
snapMode: PathView.SnapToItem
|
||||||
|
|
||||||
|
path: PathMenu {
|
||||||
|
viewHeight: view.height
|
||||||
|
viewWidth: view.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The selection rectangle itself
|
||||||
|
CustomRect {
|
||||||
|
id: selectionRect
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
height: root.itemHeight
|
||||||
|
radius: root.itemHeight / 2
|
||||||
|
width: parent.width
|
||||||
|
z: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hidden source: same PathView, but with the "inside selection" text color
|
||||||
|
Item {
|
||||||
|
id: selectedTextSource
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
PathView {
|
||||||
|
id: selectedTextView
|
||||||
|
|
||||||
|
property color delegateTextColor: root.insideTextColor
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
clip: true
|
||||||
|
currentIndex: view.currentIndex
|
||||||
|
delegate: spinnerDelegate
|
||||||
|
dragMargin: view.dragMargin
|
||||||
|
highlightRangeMode: view.highlightRangeMode
|
||||||
|
implicitHeight: root.listHeight
|
||||||
|
interactive: false
|
||||||
|
model: view.model
|
||||||
|
|
||||||
|
// Keep this PathView visually locked to the real one
|
||||||
|
offset: view.offset
|
||||||
|
pathItemCount: view.pathItemCount
|
||||||
|
preferredHighlightBegin: view.preferredHighlightBegin
|
||||||
|
preferredHighlightEnd: view.preferredHighlightEnd
|
||||||
|
snapMode: view.snapMode
|
||||||
|
|
||||||
|
path: PathMenu {
|
||||||
|
viewHeight: selectedTextView.height
|
||||||
|
viewWidth: selectedTextView.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask matching the selection rectangle
|
||||||
|
Item {
|
||||||
|
id: selectionMask
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
color: "white"
|
||||||
|
height: selectionRect.height
|
||||||
|
radius: selectionRect.radius
|
||||||
|
width: selectionRect.width
|
||||||
|
x: selectionRect.x
|
||||||
|
y: selectionRect.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only show the "inside selection" text where the mask exists
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: selectedTextSource
|
||||||
|
maskEnabled: true
|
||||||
|
maskInverted: false
|
||||||
|
maskSource: selectionMask
|
||||||
|
source: selectedTextSource
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,6 +71,7 @@ JsonObject {
|
|||||||
property real scale: 1
|
property real scale: 1
|
||||||
property int small: 5 * scale
|
property int small: 5 * scale
|
||||||
property int smaller: 7 * scale
|
property int smaller: 7 * scale
|
||||||
|
property int smallest: 2 * scale
|
||||||
}
|
}
|
||||||
component Rounding: JsonObject {
|
component Rounding: JsonObject {
|
||||||
property int full: 1000 * scale
|
property int full: 1000 * scale
|
||||||
@@ -78,6 +79,7 @@ JsonObject {
|
|||||||
property int normal: 17 * scale
|
property int normal: 17 * scale
|
||||||
property real scale: 1
|
property real scale: 1
|
||||||
property int small: 12 * scale
|
property int small: 12 * scale
|
||||||
|
property int smallest: 8 * scale
|
||||||
}
|
}
|
||||||
component Spacing: JsonObject {
|
component Spacing: JsonObject {
|
||||||
property int large: 20 * scale
|
property int large: 20 * scale
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Quickshell.Io
|
|||||||
|
|
||||||
JsonObject {
|
JsonObject {
|
||||||
property bool autoHide: false
|
property bool autoHide: false
|
||||||
|
property int border: 8
|
||||||
property list<var> entries: [
|
property list<var> entries: [
|
||||||
{
|
{
|
||||||
id: "workspaces",
|
id: "workspaces",
|
||||||
@@ -60,6 +61,7 @@ JsonObject {
|
|||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
property int height: 34
|
||||||
property Popouts popouts: Popouts {
|
property Popouts popouts: Popouts {
|
||||||
}
|
}
|
||||||
property int rounding: 8
|
property int rounding: 8
|
||||||
|
|||||||
+12
-5
@@ -33,6 +33,10 @@ Singleton {
|
|||||||
recentSaveCooldown.restart();
|
recentSaveCooldown.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveNoToast(): void {
|
||||||
|
saveTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
function serializeAppearance(): var {
|
function serializeAppearance(): var {
|
||||||
return {
|
return {
|
||||||
rounding: {
|
rounding: {
|
||||||
@@ -56,8 +60,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
anim: {
|
anim: {
|
||||||
mediaGifSpeedAdjustment: 300,
|
mediaGifSpeedAdjustment: appearance.anim.mediaGifSpeedAdjustment,
|
||||||
sessionGifSpeed: 0.7,
|
sessionGifSpeed: appearance.anim.sessionGifSpeed,
|
||||||
durations: {
|
durations: {
|
||||||
scale: appearance.anim.durations.scale
|
scale: appearance.anim.durations.scale
|
||||||
}
|
}
|
||||||
@@ -81,6 +85,8 @@ Singleton {
|
|||||||
return {
|
return {
|
||||||
autoHide: barConfig.autoHide,
|
autoHide: barConfig.autoHide,
|
||||||
rounding: barConfig.rounding,
|
rounding: barConfig.rounding,
|
||||||
|
border: barConfig.border,
|
||||||
|
height: barConfig.height,
|
||||||
popouts: {
|
popouts: {
|
||||||
tray: barConfig.popouts.tray,
|
tray: barConfig.popouts.tray,
|
||||||
audio: barConfig.popouts.audio,
|
audio: barConfig.popouts.audio,
|
||||||
@@ -155,10 +161,10 @@ Singleton {
|
|||||||
return {
|
return {
|
||||||
enable: dock.enable,
|
enable: dock.enable,
|
||||||
height: dock.height,
|
height: dock.height,
|
||||||
hoverRegionHeight: dock.hoverRegionHeight,
|
|
||||||
hoverToReveal: dock.hoverToReveal,
|
hoverToReveal: dock.hoverToReveal,
|
||||||
pinnedApps: dock.pinnedApps,
|
pinnedApps: dock.pinnedApps,
|
||||||
pinnedOnStartup: dock.pinnedOnStartup
|
pinnedOnStartup: dock.pinnedOnStartup,
|
||||||
|
ignoredAppRegexes: dock.ignoredAppRegexes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +172,7 @@ Singleton {
|
|||||||
return {
|
return {
|
||||||
logo: general.logo,
|
logo: general.logo,
|
||||||
wallpaperPath: general.wallpaperPath,
|
wallpaperPath: general.wallpaperPath,
|
||||||
|
desktopIcons: general.desktopIcons,
|
||||||
color: {
|
color: {
|
||||||
wallust: general.color.wallust,
|
wallust: general.color.wallust,
|
||||||
mode: general.color.mode,
|
mode: general.color.mode,
|
||||||
@@ -182,7 +189,7 @@ Singleton {
|
|||||||
explorer: general.apps.explorer
|
explorer: general.apps.explorer
|
||||||
},
|
},
|
||||||
idle: {
|
idle: {
|
||||||
timouts: general.idle.timeouts
|
timeouts: general.idle.timeouts
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import Quickshell.Io
|
|||||||
JsonObject {
|
JsonObject {
|
||||||
property bool enable: false
|
property bool enable: false
|
||||||
property real height: 60
|
property real height: 60
|
||||||
property real hoverRegionHeight: 2
|
|
||||||
property bool hoverToReveal: true
|
property bool hoverToReveal: true
|
||||||
|
property list<string> ignoredAppRegexes: []
|
||||||
property list<string> pinnedApps: ["org.kde.dolphin", "kitty",]
|
property list<string> pinnedApps: ["org.kde.dolphin", "kitty",]
|
||||||
property bool pinnedOnStartup: false
|
property bool pinnedOnStartup: false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ JsonObject {
|
|||||||
}
|
}
|
||||||
property Color color: Color {
|
property Color color: Color {
|
||||||
}
|
}
|
||||||
|
property bool desktopIcons: false
|
||||||
property Idle idle: Idle {
|
property Idle idle: Idle {
|
||||||
}
|
}
|
||||||
property string logo: ""
|
property string logo: ""
|
||||||
@@ -29,10 +30,12 @@ JsonObject {
|
|||||||
component Idle: JsonObject {
|
component Idle: JsonObject {
|
||||||
property list<var> timeouts: [
|
property list<var> timeouts: [
|
||||||
{
|
{
|
||||||
|
name: "Lock",
|
||||||
timeout: 180,
|
timeout: 180,
|
||||||
idleAction: "lock"
|
idleAction: "lock"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "Screen",
|
||||||
timeout: 300,
|
timeout: 300,
|
||||||
idleAction: "dpms off",
|
idleAction: "dpms off",
|
||||||
activeAction: "dpms on"
|
activeAction: "dpms on"
|
||||||
|
|||||||
+19
-7
@@ -11,8 +11,9 @@ import qs.Modules.Dashboard as Dashboard
|
|||||||
import qs.Modules.Osd as Osd
|
import qs.Modules.Osd as Osd
|
||||||
import qs.Modules.Launcher as Launcher
|
import qs.Modules.Launcher as Launcher
|
||||||
import qs.Modules.Resources as Resources
|
import qs.Modules.Resources as Resources
|
||||||
|
import qs.Modules.Drawing as Drawing
|
||||||
import qs.Modules.Settings as Settings
|
import qs.Modules.Settings as Settings
|
||||||
|
import qs.Modules.Dock as Dock
|
||||||
|
|
||||||
Shape {
|
Shape {
|
||||||
id: root
|
id: root
|
||||||
@@ -22,13 +23,15 @@ Shape {
|
|||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
// anchors.margins: 8
|
anchors.margins: Config.barConfig.border
|
||||||
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? 0 : bar.implicitHeight
|
anchors.topMargin: bar.implicitHeight
|
||||||
|
asynchronous: true
|
||||||
preferredRendererType: Shape.CurveRenderer
|
preferredRendererType: Shape.CurveRenderer
|
||||||
|
|
||||||
Behavior on anchors.topMargin {
|
Drawing.Background {
|
||||||
Anim {
|
startX: 0
|
||||||
}
|
startY: wrapper.y - rounding
|
||||||
|
wrapper: root.panels.drawing
|
||||||
}
|
}
|
||||||
|
|
||||||
Resources.Background {
|
Resources.Background {
|
||||||
@@ -45,7 +48,8 @@ Shape {
|
|||||||
|
|
||||||
Modules.Background {
|
Modules.Background {
|
||||||
invertBottomRounding: wrapper.x <= 0
|
invertBottomRounding: wrapper.x <= 0
|
||||||
startX: wrapper.x - 8
|
rounding: root.panels.popouts.currentName.startsWith("updates") ? Appearance.rounding.normal : Appearance.rounding.smallest
|
||||||
|
startX: wrapper.x - rounding
|
||||||
startY: wrapper.y
|
startY: wrapper.y
|
||||||
wrapper: root.panels.popouts
|
wrapper: root.panels.popouts
|
||||||
}
|
}
|
||||||
@@ -92,4 +96,12 @@ Shape {
|
|||||||
startY: 0
|
startY: 0
|
||||||
wrapper: root.panels.settings
|
wrapper: root.panels.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dock.Background {
|
||||||
|
id: dock
|
||||||
|
|
||||||
|
startX: (root.width - wrapper.width) / 2 - rounding
|
||||||
|
startY: root.height
|
||||||
|
wrapper: root.panels.dock
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property rect dirtyRect: Qt.rect(0, 0, 0, 0)
|
||||||
|
property bool frameQueued: false
|
||||||
|
property bool fullRepaintPending: true
|
||||||
|
property point lastPoint: Qt.point(0, 0)
|
||||||
|
property real minPointDistance: 2.0
|
||||||
|
property color penColor: "white"
|
||||||
|
property real penWidth: 4
|
||||||
|
property var pendingSegments: []
|
||||||
|
property bool strokeActive: false
|
||||||
|
property var strokes: []
|
||||||
|
|
||||||
|
function appendPoint(x, y) {
|
||||||
|
if (!strokeActive || strokes.length === 0)
|
||||||
|
return;
|
||||||
|
const dx = x - lastPoint.x;
|
||||||
|
const dy = y - lastPoint.y;
|
||||||
|
|
||||||
|
if ((dx * dx + dy * dy) < (minPointDistance * minPointDistance))
|
||||||
|
return;
|
||||||
|
const x1 = lastPoint.x;
|
||||||
|
const y1 = lastPoint.y;
|
||||||
|
const x2 = x;
|
||||||
|
const y2 = y;
|
||||||
|
|
||||||
|
strokes[strokes.length - 1].push(Qt.point(x2, y2));
|
||||||
|
|
||||||
|
pendingSegments.push({
|
||||||
|
dot: false,
|
||||||
|
x1: x1,
|
||||||
|
y1: y1,
|
||||||
|
x2: x2,
|
||||||
|
y2: y2
|
||||||
|
});
|
||||||
|
|
||||||
|
lastPoint = Qt.point(x2, y2);
|
||||||
|
queueDirty(segmentDirtyRect(x1, y1, x2, y2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginStroke(x, y) {
|
||||||
|
const p = Qt.point(x, y);
|
||||||
|
strokes.push([p]);
|
||||||
|
lastPoint = p;
|
||||||
|
strokeActive = true;
|
||||||
|
|
||||||
|
pendingSegments.push({
|
||||||
|
dot: true,
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
});
|
||||||
|
|
||||||
|
queueDirty(pointDirtyRect(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
strokes = [];
|
||||||
|
pendingSegments = [];
|
||||||
|
dirtyRect = Qt.rect(0, 0, 0, 0);
|
||||||
|
fullRepaintPending = true;
|
||||||
|
markDirty(Qt.rect(0, 0, width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDot(ctx, x, y) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, penWidth / 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSegment(ctx, x1, y1, x2, y2) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function endStroke() {
|
||||||
|
strokeActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointDirtyRect(x, y) {
|
||||||
|
const pad = penWidth + 2;
|
||||||
|
return Qt.rect(x - pad, y - pad, pad * 2, pad * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueDirty(r) {
|
||||||
|
dirtyRect = unionRects(dirtyRect, r);
|
||||||
|
|
||||||
|
if (frameQueued)
|
||||||
|
return;
|
||||||
|
frameQueued = true;
|
||||||
|
|
||||||
|
requestAnimationFrame(function () {
|
||||||
|
frameQueued = false;
|
||||||
|
|
||||||
|
if (dirtyRect.width > 0 && dirtyRect.height > 0) {
|
||||||
|
markDirty(dirtyRect);
|
||||||
|
dirtyRect = Qt.rect(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function replayAll(ctx) {
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
for (const stroke of strokes) {
|
||||||
|
if (!stroke || stroke.length === 0)
|
||||||
|
continue;
|
||||||
|
if (stroke.length === 1) {
|
||||||
|
const p = stroke[0];
|
||||||
|
drawDot(ctx, p.x, p.y);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(stroke[0].x, stroke[0].y);
|
||||||
|
for (let i = 1; i < stroke.length; ++i)
|
||||||
|
ctx.lineTo(stroke[i].x, stroke[i].y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestFullRepaint() {
|
||||||
|
fullRepaintPending = true;
|
||||||
|
markDirty(Qt.rect(0, 0, width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
function segmentDirtyRect(x1, y1, x2, y2) {
|
||||||
|
const pad = penWidth + 2;
|
||||||
|
const left = Math.min(x1, x2) - pad;
|
||||||
|
const top = Math.min(y1, y2) - pad;
|
||||||
|
const right = Math.max(x1, x2) + pad;
|
||||||
|
const bottom = Math.max(y1, y2) + pad;
|
||||||
|
return Qt.rect(left, top, right - left, bottom - top);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unionRects(a, b) {
|
||||||
|
if (a.width <= 0 || a.height <= 0)
|
||||||
|
return b;
|
||||||
|
if (b.width <= 0 || b.height <= 0)
|
||||||
|
return a;
|
||||||
|
|
||||||
|
const left = Math.min(a.x, b.x);
|
||||||
|
const top = Math.min(a.y, b.y);
|
||||||
|
const right = Math.max(a.x + a.width, b.x + b.width);
|
||||||
|
const bottom = Math.max(a.y + a.height, b.y + b.height);
|
||||||
|
|
||||||
|
return Qt.rect(left, top, right - left, bottom - top);
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
contextType: "2d"
|
||||||
|
renderStrategy: Canvas.Threaded
|
||||||
|
renderTarget: Canvas.Image
|
||||||
|
|
||||||
|
onHeightChanged: requestFullRepaint()
|
||||||
|
onPaint: region => {
|
||||||
|
const ctx = getContext("2d");
|
||||||
|
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
ctx.lineJoin = "round";
|
||||||
|
ctx.lineWidth = penWidth;
|
||||||
|
ctx.strokeStyle = penColor;
|
||||||
|
ctx.fillStyle = penColor;
|
||||||
|
|
||||||
|
if (fullRepaintPending) {
|
||||||
|
fullRepaintPending = false;
|
||||||
|
replayAll(ctx);
|
||||||
|
pendingSegments = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const seg of pendingSegments) {
|
||||||
|
if (seg.dot)
|
||||||
|
drawDot(ctx, seg.x, seg.y);
|
||||||
|
else
|
||||||
|
drawSegment(ctx, seg.x1, seg.y1, seg.x2, seg.y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingSegments = [];
|
||||||
|
}
|
||||||
|
onWidthChanged: requestFullRepaint()
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomMouseArea {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var bar
|
||||||
|
required property Drawing drawing
|
||||||
|
required property Panels panels
|
||||||
|
required property var popout
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
function inLeftPanel(panel: Item, x: real, y: real): bool {
|
||||||
|
return x < panel.x + panel.width + Config.barConfig.border && withinPanelHeight(panel, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function withinPanelHeight(panel: Item, x: real, y: real): bool {
|
||||||
|
const panelY = panel.y + bar.implicitHeight;
|
||||||
|
return y >= panelY && y <= panelY + panel.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
anchors.fill: root.visibilities.isDrawing ? parent : undefined
|
||||||
|
hoverEnabled: true
|
||||||
|
visible: root.visibilities.isDrawing
|
||||||
|
|
||||||
|
onPositionChanged: event => {
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
|
||||||
|
if (event.buttons & Qt.LeftButton)
|
||||||
|
root.drawing.appendPoint(x, y);
|
||||||
|
|
||||||
|
if (root.inLeftPanel(root.popout, x, y)) {
|
||||||
|
root.z = -2;
|
||||||
|
root.panels.drawing.expanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressed: event => {
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
|
||||||
|
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
|
||||||
|
root.panels.drawing.expanded = false;
|
||||||
|
root.drawing.beginStroke(x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.buttons & Qt.RightButton)
|
||||||
|
root.drawing.clear();
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
if (root.visibilities.isDrawing)
|
||||||
|
root.drawing.endStroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Config
|
||||||
|
import qs.Components
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Item bar
|
||||||
|
required property ShellScreen screen
|
||||||
|
|
||||||
|
ExclusionZone {
|
||||||
|
anchors.top: true
|
||||||
|
exclusiveZone: root.bar.exclusiveZone
|
||||||
|
}
|
||||||
|
|
||||||
|
ExclusionZone {
|
||||||
|
anchors.left: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ExclusionZone {
|
||||||
|
anchors.right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ExclusionZone {
|
||||||
|
anchors.bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
component ExclusionZone: CustomWindow {
|
||||||
|
exclusiveZone: Config.barConfig.border
|
||||||
|
implicitHeight: 1
|
||||||
|
implicitWidth: 1
|
||||||
|
name: "Bar-Exclusion"
|
||||||
|
screen: root.screen
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
-129
@@ -10,6 +10,8 @@ CustomMouseArea {
|
|||||||
required property Item bar
|
required property Item bar
|
||||||
property bool dashboardShortcutActive
|
property bool dashboardShortcutActive
|
||||||
property point dragStart
|
property point dragStart
|
||||||
|
required property Drawing drawing
|
||||||
|
required property DrawingInput input
|
||||||
property bool osdShortcutActive
|
property bool osdShortcutActive
|
||||||
required property Panels panels
|
required property Panels panels
|
||||||
required property BarPopouts.Wrapper popouts
|
required property BarPopouts.Wrapper popouts
|
||||||
@@ -18,15 +20,15 @@ CustomMouseArea {
|
|||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
function inBottomPanel(panel: Item, x: real, y: real): bool {
|
function inBottomPanel(panel: Item, x: real, y: real): bool {
|
||||||
return y > root.height - panel.height && withinPanelWidth(panel, x, y);
|
return y > root.height - panel.height - Config.barConfig.border && withinPanelWidth(panel, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function inLeftPanel(panel: Item, x: real, y: real): bool {
|
function inLeftPanel(panel: Item, x: real, y: real): bool {
|
||||||
return x < panel.x + panel.width && withinPanelHeight(panel, x, y);
|
return x < panel.x + panel.width + Config.barConfig.border && withinPanelHeight(panel, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function inRightPanel(panel: Item, x: real, y: real): bool {
|
function inRightPanel(panel: Item, x: real, y: real): bool {
|
||||||
return x > panel.x && withinPanelHeight(panel, x, y);
|
return x > panel.x - Config.barConfig.border && withinPanelHeight(panel, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function inTopPanel(panel: Item, x: real, y: real): bool {
|
function inTopPanel(panel: Item, x: real, y: real): bool {
|
||||||
@@ -51,20 +53,10 @@ CustomMouseArea {
|
|||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
propagateComposedEvents: true
|
||||||
// onPressed: event => {
|
|
||||||
// if ( root.popouts.hasCurrent && !inTopPanel( root.popouts, event.x, event.y )) {
|
|
||||||
// root.popouts.hasCurrent = false;
|
|
||||||
// } else if (root.visibilities.sidebar && !inRightPanel( panels.sidebar, event.x, event.y )) {
|
|
||||||
// root.visibilities.sidebar = false;
|
|
||||||
// } else if (root.visibilities.dashboard && !inTopPanel( panels.dashboard, event.x, event.y )) {
|
|
||||||
// root.visibilities.dashboard = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
onContainsMouseChanged: {
|
||||||
if (!containsMouse) {
|
if (!containsMouse) {
|
||||||
// Only hide if not activated by shortcut
|
|
||||||
if (!osdShortcutActive) {
|
if (!osdShortcutActive) {
|
||||||
visibilities.osd = false;
|
visibilities.osd = false;
|
||||||
root.panels.osd.hovered = false;
|
root.panels.osd.hovered = false;
|
||||||
@@ -74,8 +66,8 @@ CustomMouseArea {
|
|||||||
popouts.hasCurrent = false;
|
popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.barConfig.autoHide && !root.visibilities.sidebar && !root.visibilities.dashboard)
|
if (Config.barConfig.autoHide)
|
||||||
root.visibilities.bar = false;
|
bar.isHovered = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPositionChanged: event => {
|
onPositionChanged: event => {
|
||||||
@@ -87,142 +79,69 @@ CustomMouseArea {
|
|||||||
const dragX = x - dragStart.x;
|
const dragX = x - dragStart.x;
|
||||||
const dragY = y - dragStart.y;
|
const dragY = y - dragStart.y;
|
||||||
|
|
||||||
// Show bar in non-exclusive mode on hover
|
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
|
||||||
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight + bar.anchors.topMargin)
|
root.input.z = 2;
|
||||||
visibilities.bar = true;
|
root.panels.drawing.expanded = false;
|
||||||
|
|
||||||
if (panels.sidebar.width === 0) {
|
|
||||||
// Show osd on hover
|
|
||||||
const showOsd = inRightPanel(panels.osd, x, y);
|
|
||||||
|
|
||||||
// // Always update visibility based on hover if not in shortcut mode
|
|
||||||
if (!osdShortcutActive) {
|
|
||||||
visibilities.osd = showOsd;
|
|
||||||
root.panels.osd.hovered = showOsd;
|
|
||||||
} else if (showOsd) {
|
|
||||||
// If hovering over OSD area while in shortcut mode, transition to hover control
|
|
||||||
osdShortcutActive = false;
|
|
||||||
root.panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const showSidebar = pressed && dragStart.x > bar.implicitWidth + panels.sidebar.x;
|
|
||||||
//
|
|
||||||
// // Show/hide session on drag
|
|
||||||
// if (pressed && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
|
|
||||||
// if (dragX < -Config.session.dragThreshold)
|
|
||||||
// visibilities.session = true;
|
|
||||||
// else if (dragX > Config.session.dragThreshold)
|
|
||||||
// visibilities.session = false;
|
|
||||||
//
|
|
||||||
// // Show sidebar on drag if in session area and session is nearly fully visible
|
|
||||||
// if (showSidebar && panels.session.width >= panels.session.nonAnimWidth && dragX < -Config.sidebar.dragThreshold)
|
|
||||||
// visibilities.sidebar = true;
|
|
||||||
// } else if (showSidebar && dragX < -Config.sidebar.dragThreshold) {
|
|
||||||
// // Show sidebar on drag if not in session area
|
|
||||||
// visibilities.sidebar = true;
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
const outOfSidebar = x < width - panels.sidebar.width;
|
|
||||||
// Show osd on hover
|
|
||||||
const showOsd = outOfSidebar && inRightPanel(panels.osd, x, y);
|
|
||||||
|
|
||||||
// Always update visibility based on hover if not in shortcut mode
|
|
||||||
if (!osdShortcutActive) {
|
|
||||||
visibilities.osd = showOsd;
|
|
||||||
root.panels.osd.hovered = showOsd;
|
|
||||||
} else if (showOsd) {
|
|
||||||
// If hovering over OSD area while in shortcut mode, transition to hover control
|
|
||||||
osdShortcutActive = false;
|
|
||||||
root.panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// // Show/hide session on drag
|
|
||||||
// if (pressed && outOfSidebar && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
|
|
||||||
// if (dragX < -Config.session.dragThreshold)
|
|
||||||
// visibilities.session = true;
|
|
||||||
// else if (dragX > Config.session.dragThreshold)
|
|
||||||
// visibilities.session = false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Hide sidebar on drag
|
|
||||||
// if (pressed && inRightPanel(panels.sidebar, dragStart.x, 0) && dragX > Config.sidebar.dragThreshold)
|
|
||||||
// visibilities.sidebar = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show launcher on hover, or show/hide on drag if hover is disabled
|
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight)
|
||||||
// if (Config.launcher.showOnHover) {
|
bar.isHovered = true;
|
||||||
// if (!visibilities.launcher && inBottomPanel(panels.launcher, x, y))
|
|
||||||
// visibilities.launcher = true;
|
|
||||||
// } else if (pressed && inBottomPanel(panels.launcher, dragStart.x, dragStart.y) && withinPanelWidth(panels.launcher, x, y)) {
|
|
||||||
// if (dragY < -Config.launcher.dragThreshold)
|
|
||||||
// visibilities.launcher = true;
|
|
||||||
// else if (dragY > Config.launcher.dragThreshold)
|
|
||||||
// visibilities.launcher = false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Show dashboard on hover
|
|
||||||
// const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboard, x, y);
|
|
||||||
//
|
|
||||||
// // Always update visibility based on hover if not in shortcut mode
|
|
||||||
// if (!dashboardShortcutActive) {
|
|
||||||
// visibilities.dashboard = showDashboard;
|
|
||||||
// } else if (showDashboard) {
|
|
||||||
// // If hovering over dashboard area while in shortcut mode, transition to hover control
|
|
||||||
// dashboardShortcutActive = false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Show/hide dashboard on drag (for touchscreen devices)
|
|
||||||
// if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) {
|
|
||||||
// if (dragY > Config.dashboard.dragThreshold)
|
|
||||||
// visibilities.dashboard = true;
|
|
||||||
// else if (dragY < -Config.dashboard.dragThreshold)
|
|
||||||
// visibilities.dashboard = false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Show utilities on hover
|
|
||||||
// const showUtilities = inBottomPanel(panels.utilities, x, y);
|
|
||||||
//
|
|
||||||
// // Always update visibility based on hover if not in shortcut mode
|
|
||||||
// if (!utilitiesShortcutActive) {
|
|
||||||
// visibilities.utilities = showUtilities;
|
|
||||||
// } else if (showUtilities) {
|
|
||||||
// // If hovering over utilities area while in shortcut mode, transition to hover control
|
|
||||||
// utilitiesShortcutActive = false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Show popouts on hover
|
if (panels.sidebar.width === 0) {
|
||||||
if (y < bar.implicitHeight) {
|
const showOsd = inRightPanel(panels.osd, x, y);
|
||||||
bar.checkPopout(x);
|
|
||||||
|
if (showOsd) {
|
||||||
|
osdShortcutActive = false;
|
||||||
|
root.panels.osd.hovered = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const outOfSidebar = x < width - panels.sidebar.width;
|
||||||
|
const showOsd = outOfSidebar && inRightPanel(panels.osd, x, y);
|
||||||
|
|
||||||
|
if (!osdShortcutActive) {
|
||||||
|
visibilities.osd = showOsd;
|
||||||
|
root.panels.osd.hovered = showOsd;
|
||||||
|
} else if (showOsd) {
|
||||||
|
osdShortcutActive = false;
|
||||||
|
root.panels.osd.hovered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y))
|
||||||
|
visibilities.dock = true;
|
||||||
|
|
||||||
|
if (y < root.bar.implicitHeight) {
|
||||||
|
root.bar.checkPopout(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monitor individual visibility changes
|
|
||||||
Connections {
|
Connections {
|
||||||
function onDashboardChanged() {
|
function onDashboardChanged() {
|
||||||
if (root.visibilities.dashboard) {
|
if (root.visibilities.dashboard) {
|
||||||
// Dashboard became visible, immediately check if this should be shortcut mode
|
|
||||||
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
|
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
|
||||||
if (!inDashboardArea) {
|
if (!inDashboardArea) {
|
||||||
root.dashboardShortcutActive = true;
|
root.dashboardShortcutActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.visibilities.settings = false;
|
||||||
root.visibilities.sidebar = false;
|
root.visibilities.sidebar = false;
|
||||||
root.popouts.hasCurrent = false;
|
root.popouts.hasCurrent = false;
|
||||||
} else {
|
} else {
|
||||||
// Dashboard hidden, clear shortcut flag
|
|
||||||
root.dashboardShortcutActive = false;
|
root.dashboardShortcutActive = false;
|
||||||
// root.visibilities.bar = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onIsDrawingChanged() {
|
||||||
|
if (!root.visibilities.isDrawing)
|
||||||
|
root.drawing.clear();
|
||||||
|
}
|
||||||
|
|
||||||
function onLauncherChanged() {
|
function onLauncherChanged() {
|
||||||
// If launcher is hidden, clear shortcut flags for dashboard and OSD
|
|
||||||
if (!root.visibilities.launcher) {
|
if (!root.visibilities.launcher) {
|
||||||
root.dashboardShortcutActive = false;
|
root.dashboardShortcutActive = false;
|
||||||
root.osdShortcutActive = false;
|
root.osdShortcutActive = false;
|
||||||
root.utilitiesShortcutActive = false;
|
root.utilitiesShortcutActive = false;
|
||||||
|
|
||||||
// Also hide dashboard and OSD if they're not being hovered
|
|
||||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
||||||
|
|
||||||
if (!inOsdArea) {
|
if (!inOsdArea) {
|
||||||
@@ -230,17 +149,20 @@ CustomMouseArea {
|
|||||||
root.panels.osd.hovered = false;
|
root.panels.osd.hovered = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.visibilities.launcher) {
|
||||||
|
root.visibilities.dock = false;
|
||||||
|
root.visibilities.settings = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOsdChanged() {
|
function onOsdChanged() {
|
||||||
if (root.visibilities.osd) {
|
if (root.visibilities.osd) {
|
||||||
// OSD became visible, immediately check if this should be shortcut mode
|
|
||||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
||||||
if (!inOsdArea) {
|
if (!inOsdArea) {
|
||||||
root.osdShortcutActive = true;
|
root.osdShortcutActive = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// OSD hidden, clear shortcut flag
|
|
||||||
root.osdShortcutActive = false;
|
root.osdShortcutActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,6 +171,18 @@ CustomMouseArea {
|
|||||||
if (root.visibilities.resources && root.popouts.currentName.startsWith("audio")) {
|
if (root.visibilities.resources && root.popouts.currentName.startsWith("audio")) {
|
||||||
root.popouts.hasCurrent = false;
|
root.popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.visibilities.resources)
|
||||||
|
root.visibilities.settings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSettingsChanged() {
|
||||||
|
if (root.visibilities.settings) {
|
||||||
|
root.visibilities.resources = false;
|
||||||
|
root.visibilities.dashboard = false;
|
||||||
|
root.panels.popouts.hasCurrent = false;
|
||||||
|
root.visibilities.launcher = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSidebarChanged() {
|
function onSidebarChanged() {
|
||||||
@@ -260,13 +194,11 @@ CustomMouseArea {
|
|||||||
|
|
||||||
function onUtilitiesChanged() {
|
function onUtilitiesChanged() {
|
||||||
if (root.visibilities.utilities) {
|
if (root.visibilities.utilities) {
|
||||||
// Utilities became visible, immediately check if this should be shortcut mode
|
|
||||||
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY);
|
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY);
|
||||||
if (!inUtilitiesArea) {
|
if (!inUtilitiesArea) {
|
||||||
root.utilitiesShortcutActive = true;
|
root.utilitiesShortcutActive = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Utilities hidden, clear shortcut flag
|
|
||||||
root.utilitiesShortcutActive = false;
|
root.utilitiesShortcutActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-7
@@ -11,6 +11,8 @@ import qs.Components.Toast as Toasts
|
|||||||
import qs.Modules.Launcher as Launcher
|
import qs.Modules.Launcher as Launcher
|
||||||
import qs.Modules.Resources as Resources
|
import qs.Modules.Resources as Resources
|
||||||
import qs.Modules.Settings as Settings
|
import qs.Modules.Settings as Settings
|
||||||
|
import qs.Modules.Drawing as Drawing
|
||||||
|
import qs.Modules.Dock as Dock
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -18,6 +20,9 @@ Item {
|
|||||||
|
|
||||||
required property Item bar
|
required property Item bar
|
||||||
readonly property alias dashboard: dashboard
|
readonly property alias dashboard: dashboard
|
||||||
|
readonly property alias dock: dock
|
||||||
|
readonly property alias drawing: drawing
|
||||||
|
required property Canvas drawingItem
|
||||||
readonly property alias launcher: launcher
|
readonly property alias launcher: launcher
|
||||||
readonly property alias notifications: notifications
|
readonly property alias notifications: notifications
|
||||||
readonly property alias osd: osd
|
readonly property alias osd: osd
|
||||||
@@ -31,13 +36,8 @@ Item {
|
|||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
// anchors.margins: 8
|
anchors.margins: Config.barConfig.border
|
||||||
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? 0 : bar.implicitHeight
|
anchors.topMargin: bar.implicitHeight
|
||||||
|
|
||||||
Behavior on anchors.topMargin {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Resources.Wrapper {
|
Resources.Wrapper {
|
||||||
id: resources
|
id: resources
|
||||||
@@ -47,6 +47,16 @@ Item {
|
|||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Drawing.Wrapper {
|
||||||
|
id: drawing
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
drawing: root.drawingItem
|
||||||
|
screen: root.screen
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
|
||||||
Osd.Wrapper {
|
Osd.Wrapper {
|
||||||
id: osd
|
id: osd
|
||||||
|
|
||||||
@@ -133,6 +143,17 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
panels: root
|
panels: root
|
||||||
|
screen: root.screen
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
|
||||||
|
Dock.Wrapper {
|
||||||
|
id: dock
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
panels: root
|
||||||
|
screen: root.screen
|
||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,30 +21,25 @@ Variants {
|
|||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
PanelWindow {
|
Exclusions {
|
||||||
id: bar
|
bar: bar
|
||||||
|
screen: scope.modelData
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomWindow {
|
||||||
|
id: win
|
||||||
|
|
||||||
|
readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2)
|
||||||
property var root: Quickshell.shellDir
|
property var root: Quickshell.shellDir
|
||||||
property bool trayMenuVisible: false
|
|
||||||
|
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.resources ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: visibilities.dock || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.resources ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||||
WlrLayershell.namespace: "ZShell-Bar"
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
contentItem.focus: true
|
contentItem.focus: true
|
||||||
|
mask: visibilities.isDrawing ? null : region
|
||||||
|
name: "Bar"
|
||||||
screen: scope.modelData
|
screen: scope.modelData
|
||||||
|
|
||||||
mask: Region {
|
|
||||||
id: region
|
|
||||||
|
|
||||||
height: bar.screen.height - backgroundRect.implicitHeight
|
|
||||||
intersection: Intersection.Xor
|
|
||||||
regions: popoutRegions.instances
|
|
||||||
width: bar.width
|
|
||||||
x: 0
|
|
||||||
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : backgroundRect.height
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem.Keys.onEscapePressed: {
|
contentItem.Keys.onEscapePressed: {
|
||||||
if (Config.barConfig.autoHide)
|
if (Config.barConfig.autoHide)
|
||||||
visibilities.bar = false;
|
visibilities.bar = false;
|
||||||
@@ -54,22 +49,23 @@ Variants {
|
|||||||
visibilities.settings = false;
|
visibilities.settings = false;
|
||||||
visibilities.resources = false;
|
visibilities.resources = false;
|
||||||
}
|
}
|
||||||
|
onHasFullscreenChanged: {
|
||||||
|
visibilities.launcher = false;
|
||||||
|
visibilities.dashboard = false;
|
||||||
|
visibilities.osd = false;
|
||||||
|
visibilities.settings = false;
|
||||||
|
visibilities.resources = false;
|
||||||
|
}
|
||||||
|
|
||||||
PanelWindow {
|
Region {
|
||||||
id: exclusionZone
|
id: region
|
||||||
|
|
||||||
WlrLayershell.exclusionMode: Config.barConfig.autoHide ? ExclusionMode.Ignore : ExclusionMode.Auto
|
height: win.height - bar.implicitHeight - Config.barConfig.border
|
||||||
WlrLayershell.layer: WlrLayer.Bottom
|
intersection: Intersection.Xor
|
||||||
WlrLayershell.namespace: "ZShell-Bar-Exclusion"
|
regions: popoutRegions.instances
|
||||||
color: "transparent"
|
width: win.width - Config.barConfig.border * 2
|
||||||
implicitHeight: backgroundRect.height
|
x: Config.barConfig.border
|
||||||
screen: bar.screen
|
y: bar.implicitHeight
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
top: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -90,16 +86,16 @@ Variants {
|
|||||||
height: modelData.height
|
height: modelData.height
|
||||||
intersection: Intersection.Subtract
|
intersection: Intersection.Subtract
|
||||||
width: modelData.width
|
width: modelData.width
|
||||||
x: modelData.x
|
x: modelData.x + Config.barConfig.border
|
||||||
y: modelData.y + backgroundRect.implicitHeight
|
y: modelData.y + bar.implicitHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
|
|
||||||
active: visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
|
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
|
||||||
windows: [bar]
|
windows: [win]
|
||||||
|
|
||||||
onCleared: {
|
onCleared: {
|
||||||
visibilities.launcher = false;
|
visibilities.launcher = false;
|
||||||
@@ -108,6 +104,7 @@ Variants {
|
|||||||
visibilities.osd = false;
|
visibilities.osd = false;
|
||||||
visibilities.settings = false;
|
visibilities.settings = false;
|
||||||
visibilities.resources = false;
|
visibilities.resources = false;
|
||||||
|
visibilities.dock = false;
|
||||||
panels.popouts.hasCurrent = false;
|
panels.popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +114,8 @@ Variants {
|
|||||||
|
|
||||||
property bool bar
|
property bool bar
|
||||||
property bool dashboard
|
property bool dashboard
|
||||||
|
property bool dock
|
||||||
|
property bool isDrawing
|
||||||
property bool launcher
|
property bool launcher
|
||||||
property bool notif: NotifServer.popups.length > 0
|
property bool notif: NotifServer.popups.length > 0
|
||||||
property bool osd
|
property bool osd
|
||||||
@@ -130,7 +129,7 @@ Variants {
|
|||||||
Binding {
|
Binding {
|
||||||
property: "bar"
|
property: "bar"
|
||||||
target: visibilities
|
target: visibilities
|
||||||
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || visibilities.notif || visibilities.resources
|
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || visibilities.notif || visibilities.resources || visibilities.settings || bar.isHovered
|
||||||
when: Config.barConfig.autoHide
|
when: Config.barConfig.autoHide
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,68 +145,66 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Border {
|
Border {
|
||||||
bar: backgroundRect
|
bar: bar
|
||||||
visibilities: visibilities
|
visibilities: visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
Backgrounds {
|
Backgrounds {
|
||||||
bar: backgroundRect
|
bar: bar
|
||||||
panels: panels
|
panels: panels
|
||||||
visibilities: visibilities
|
visibilities: visibilities
|
||||||
|
z: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Drawing {
|
||||||
|
id: drawing
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawingInput {
|
||||||
|
id: input
|
||||||
|
|
||||||
|
bar: bar
|
||||||
|
drawing: drawing
|
||||||
|
panels: panels
|
||||||
|
popout: panels.drawing
|
||||||
|
visibilities: visibilities
|
||||||
|
z: 2
|
||||||
|
}
|
||||||
|
|
||||||
Interactions {
|
Interactions {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
bar: barLoader
|
bar: bar
|
||||||
|
drawing: drawing
|
||||||
|
input: input
|
||||||
panels: panels
|
panels: panels
|
||||||
popouts: panels.popouts
|
popouts: panels.popouts
|
||||||
screen: scope.modelData
|
screen: scope.modelData
|
||||||
visibilities: visibilities
|
visibilities: visibilities
|
||||||
|
z: 1
|
||||||
|
|
||||||
Panels {
|
Panels {
|
||||||
id: panels
|
id: panels
|
||||||
|
|
||||||
bar: backgroundRect
|
bar: bar
|
||||||
|
drawingItem: drawing
|
||||||
screen: scope.modelData
|
screen: scope.modelData
|
||||||
visibilities: visibilities
|
visibilities: visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
BarLoader {
|
||||||
id: backgroundRect
|
id: bar
|
||||||
|
|
||||||
property Wrapper popouts: panels.popouts
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
popouts: panels.popouts
|
||||||
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? -30 : 0
|
screen: scope.modelData
|
||||||
color: "transparent"
|
visibilities: visibilities
|
||||||
implicitHeight: barLoader.implicitHeight
|
|
||||||
radius: 0
|
|
||||||
|
|
||||||
Behavior on anchors.topMargin {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BarLoader {
|
|
||||||
id: barLoader
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
bar: bar
|
|
||||||
popouts: panels.popouts
|
|
||||||
screen: scope.modelData
|
|
||||||
visibilities: visibilities
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import "../scripts/levendist.js" as Levendist
|
||||||
|
import "../scripts/fuzzysort.js" as Fuzzy
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property list<DesktopEntry> list: Array.from(DesktopEntries.applications.values).filter((app, index, self) => index === self.findIndex(t => (t.id === app.id)))
|
||||||
|
readonly property var preppedIcons: list.map(a => ({
|
||||||
|
name: Fuzzy.prepare(`${a.icon} `),
|
||||||
|
entry: a
|
||||||
|
}))
|
||||||
|
readonly property var preppedNames: list.map(a => ({
|
||||||
|
name: Fuzzy.prepare(`${a.name} `),
|
||||||
|
entry: a
|
||||||
|
}))
|
||||||
|
property var regexSubstitutions: [
|
||||||
|
{
|
||||||
|
"regex": /^steam_app_(\d+)$/,
|
||||||
|
"replace": "steam_icon_$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": /Minecraft.*/,
|
||||||
|
"replace": "minecraft"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": /.*polkit.*/,
|
||||||
|
"replace": "system-lock-screen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": /gcr.prompter/,
|
||||||
|
"replace": "system-lock-screen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
readonly property real scoreGapThreshold: 0.1
|
||||||
|
readonly property real scoreThreshold: 0.6
|
||||||
|
property var substitutions: ({
|
||||||
|
"code-url-handler": "visual-studio-code",
|
||||||
|
"Code": "visual-studio-code",
|
||||||
|
"gnome-tweaks": "org.gnome.tweaks",
|
||||||
|
"pavucontrol-qt": "pavucontrol",
|
||||||
|
"wps": "wps-office2019-kprometheus",
|
||||||
|
"wpsoffice": "wps-office2019-kprometheus",
|
||||||
|
"footclient": "foot"
|
||||||
|
})
|
||||||
|
|
||||||
|
function bestFuzzyEntry(search: string, preppedList: list<var>, key: string): var {
|
||||||
|
const results = Fuzzy.go(search, preppedList, {
|
||||||
|
key: key,
|
||||||
|
threshold: root.scoreThreshold,
|
||||||
|
limit: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!results || results.length === 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const best = results[0];
|
||||||
|
const second = results.length > 1 ? results[1] : null;
|
||||||
|
|
||||||
|
if (second && (best.score - second.score) < root.scoreGapThreshold)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return best.obj.entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fuzzyQuery(search: string, preppedList: list<var>): var {
|
||||||
|
const entry = bestFuzzyEntry(search, preppedList, "name");
|
||||||
|
return entry ? [entry] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKebabNormalizedAppName(str: string): string {
|
||||||
|
return str.toLowerCase().replace(/\s+/g, "-");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReverseDomainNameAppName(str: string): string {
|
||||||
|
return str.split('.').slice(-1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUndescoreToKebabAppName(str: string): string {
|
||||||
|
return str.toLowerCase().replace(/_/g, "-");
|
||||||
|
}
|
||||||
|
|
||||||
|
function guessIcon(str) {
|
||||||
|
if (!str || str.length == 0)
|
||||||
|
return "image-missing";
|
||||||
|
|
||||||
|
if (iconExists(str))
|
||||||
|
return str;
|
||||||
|
|
||||||
|
const entry = DesktopEntries.byId(str);
|
||||||
|
if (entry)
|
||||||
|
return entry.icon;
|
||||||
|
|
||||||
|
const heuristicEntry = DesktopEntries.heuristicLookup(str);
|
||||||
|
if (heuristicEntry)
|
||||||
|
return heuristicEntry.icon;
|
||||||
|
|
||||||
|
if (substitutions[str])
|
||||||
|
return substitutions[str];
|
||||||
|
if (substitutions[str.toLowerCase()])
|
||||||
|
return substitutions[str.toLowerCase()];
|
||||||
|
|
||||||
|
for (let i = 0; i < regexSubstitutions.length; i++) {
|
||||||
|
const substitution = regexSubstitutions[i];
|
||||||
|
const replacedName = str.replace(substitution.regex, substitution.replace);
|
||||||
|
if (replacedName != str)
|
||||||
|
return replacedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lowercased = str.toLowerCase();
|
||||||
|
if (iconExists(lowercased))
|
||||||
|
return lowercased;
|
||||||
|
|
||||||
|
const reverseDomainNameAppName = getReverseDomainNameAppName(str);
|
||||||
|
if (iconExists(reverseDomainNameAppName))
|
||||||
|
return reverseDomainNameAppName;
|
||||||
|
|
||||||
|
const lowercasedDomainNameAppName = reverseDomainNameAppName.toLowerCase();
|
||||||
|
if (iconExists(lowercasedDomainNameAppName))
|
||||||
|
return lowercasedDomainNameAppName;
|
||||||
|
|
||||||
|
const kebabNormalizedGuess = getKebabNormalizedAppName(str);
|
||||||
|
if (iconExists(kebabNormalizedGuess))
|
||||||
|
return kebabNormalizedGuess;
|
||||||
|
|
||||||
|
const undescoreToKebabGuess = getUndescoreToKebabAppName(str);
|
||||||
|
if (iconExists(undescoreToKebabGuess))
|
||||||
|
return undescoreToKebabGuess;
|
||||||
|
|
||||||
|
const iconSearchResult = fuzzyQuery(str, preppedIcons);
|
||||||
|
if (iconSearchResult && iconExists(iconSearchResult.icon))
|
||||||
|
return iconSearchResult.icon;
|
||||||
|
|
||||||
|
const nameSearchResult = root.fuzzyQuery(str, preppedNames);
|
||||||
|
if (nameSearchResult && iconExists(nameSearchResult.icon))
|
||||||
|
return nameSearchResult.icon;
|
||||||
|
|
||||||
|
return "application-x-executable";
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconExists(iconName) {
|
||||||
|
if (!iconName || iconName.length == 0)
|
||||||
|
return false;
|
||||||
|
return (Quickshell.iconPath(iconName, true).length > 0) && !iconName.includes("image-missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function getAppId(fileName) {
|
||||||
|
return fileName.endsWith(".desktop") ? fileName.replace(".desktop", "") : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileType(fileName, isDir) {
|
||||||
|
if (isDir)
|
||||||
|
return "directory";
|
||||||
|
let ext = fileName.includes('.') ? fileName.split('.').pop().toLowerCase() : "";
|
||||||
|
if (ext === "desktop")
|
||||||
|
return "desktop";
|
||||||
|
|
||||||
|
const map = {
|
||||||
|
"image": ["png", "jpg", "jpeg", "svg", "gif", "bmp", "webp", "ico", "tiff", "tif", "heic", "heif", "raw", "psd", "ai", "xcf"],
|
||||||
|
"video": ["mp4", "mkv", "webm", "avi", "mov", "flv", "wmv", "m4v", "mpg", "mpeg", "3gp", "vob", "ogv", "ts"],
|
||||||
|
"audio": ["mp3", "wav", "flac", "aac", "ogg", "m4a", "wma", "opus", "alac", "mid", "midi", "amr"],
|
||||||
|
"archive": ["zip", "tar", "gz", "rar", "7z", "xz", "bz2", "tgz", "iso", "img", "dmg", "deb", "rpm", "apk"],
|
||||||
|
"document": ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "odt", "ods", "odp", "rtf", "epub", "mobi", "djvu"],
|
||||||
|
"text": ["txt", "md", "rst", "tex", "log", "json", "xml", "yaml", "yml", "toml", "ini", "conf", "cfg", "env", "csv", "tsv"],
|
||||||
|
"code": ["qml", "cpp", "c", "h", "hpp", "py", "js", "ts", "jsx", "tsx", "java", "rs", "go", "rb", "php", "cs", "swift", "kt", "sh", "bash", "zsh", "fish", "html", "htm", "css", "scss", "sass", "less", "vue", "svelte", "sql", "graphql", "lua", "pl", "dart", "r", "dockerfile", "make"],
|
||||||
|
"executable": ["exe", "msi", "bat", "cmd", "appimage", "run", "bin", "out", "so", "dll"],
|
||||||
|
"font": ["ttf", "otf", "woff", "woff2"]
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [type, extensions] of Object.entries(map)) {
|
||||||
|
if (extensions.includes(ext))
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIconName(fileName, isDir) {
|
||||||
|
if (isDir)
|
||||||
|
return "folder";
|
||||||
|
let ext = fileName.includes('.') ? fileName.split('.').pop().toLowerCase() : "";
|
||||||
|
|
||||||
|
const map = {
|
||||||
|
// Images
|
||||||
|
"png": "image-x-generic",
|
||||||
|
"jpg": "image-x-generic",
|
||||||
|
"jpeg": "image-x-generic",
|
||||||
|
"svg": "image-svg+xml",
|
||||||
|
"gif": "image-x-generic",
|
||||||
|
"bmp": "image-x-generic",
|
||||||
|
"webp": "image-x-generic",
|
||||||
|
"ico": "image-x-generic",
|
||||||
|
"tiff": "image-x-generic",
|
||||||
|
"tif": "image-x-generic",
|
||||||
|
"heic": "image-x-generic",
|
||||||
|
"heif": "image-x-generic",
|
||||||
|
"raw": "image-x-generic",
|
||||||
|
"psd": "image-vnd.adobe.photoshop",
|
||||||
|
"ai": "application-illustrator",
|
||||||
|
"xcf": "image-x-xcf",
|
||||||
|
|
||||||
|
// Vidéos
|
||||||
|
"mp4": "video-x-generic",
|
||||||
|
"mkv": "video-x-generic",
|
||||||
|
"webm": "video-x-generic",
|
||||||
|
"avi": "video-x-generic",
|
||||||
|
"mov": "video-x-generic",
|
||||||
|
"flv": "video-x-generic",
|
||||||
|
"wmv": "video-x-generic",
|
||||||
|
"m4v": "video-x-generic",
|
||||||
|
"mpg": "video-x-generic",
|
||||||
|
"mpeg": "video-x-generic",
|
||||||
|
"3gp": "video-x-generic",
|
||||||
|
"vob": "video-x-generic",
|
||||||
|
"ogv": "video-x-generic",
|
||||||
|
"ts": "video-x-generic",
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
"mp3": "audio-x-generic",
|
||||||
|
"wav": "audio-x-generic",
|
||||||
|
"flac": "audio-x-generic",
|
||||||
|
"aac": "audio-x-generic",
|
||||||
|
"ogg": "audio-x-generic",
|
||||||
|
"m4a": "audio-x-generic",
|
||||||
|
"wma": "audio-x-generic",
|
||||||
|
"opus": "audio-x-generic",
|
||||||
|
"alac": "audio-x-generic",
|
||||||
|
"mid": "audio-midi",
|
||||||
|
"midi": "audio-midi",
|
||||||
|
"amr": "audio-x-generic",
|
||||||
|
|
||||||
|
// Archives & Images
|
||||||
|
"zip": "application-zip",
|
||||||
|
"tar": "application-x-tar",
|
||||||
|
"gz": "application-gzip",
|
||||||
|
"rar": "application-vnd.rar",
|
||||||
|
"7z": "application-x-7z-compressed",
|
||||||
|
"xz": "application-x-xz",
|
||||||
|
"bz2": "application-x-bzip2",
|
||||||
|
"tgz": "application-x-compressed-tar",
|
||||||
|
"iso": "application-x-cd-image",
|
||||||
|
"img": "application-x-cd-image",
|
||||||
|
"dmg": "application-x-apple-diskimage",
|
||||||
|
"deb": "application-vnd.debian.binary-package",
|
||||||
|
"rpm": "application-x-rpm",
|
||||||
|
"apk": "application-vnd.android.package-archive",
|
||||||
|
|
||||||
|
// Documents
|
||||||
|
"pdf": "application-pdf",
|
||||||
|
"doc": "application-msword",
|
||||||
|
"docx": "application-vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"xls": "application-vnd.ms-excel",
|
||||||
|
"xlsx": "application-vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"ppt": "application-vnd.ms-powerpoint",
|
||||||
|
"pptx": "application-vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
"odt": "application-vnd.oasis.opendocument.text",
|
||||||
|
"ods": "application-vnd.oasis.opendocument.spreadsheet",
|
||||||
|
"odp": "application-vnd.oasis.opendocument.presentation",
|
||||||
|
"rtf": "application-rtf",
|
||||||
|
"epub": "application-epub+zip",
|
||||||
|
"mobi": "application-x-mobipocket-ebook",
|
||||||
|
"djvu": "image-vnd.djvu",
|
||||||
|
"csv": "text-csv",
|
||||||
|
"tsv": "text-tab-separated-values",
|
||||||
|
|
||||||
|
// Data & Config
|
||||||
|
"txt": "text-x-generic",
|
||||||
|
"md": "text-markdown",
|
||||||
|
"rst": "text-x-rst",
|
||||||
|
"tex": "text-x-tex",
|
||||||
|
"log": "text-x-log",
|
||||||
|
"json": "application-json",
|
||||||
|
"xml": "text-xml",
|
||||||
|
"yaml": "text-x-yaml",
|
||||||
|
"yml": "text-x-yaml",
|
||||||
|
"toml": "text-x-toml",
|
||||||
|
"ini": "text-x-generic",
|
||||||
|
"conf": "text-x-generic",
|
||||||
|
"cfg": "text-x-generic",
|
||||||
|
"env": "text-x-generic",
|
||||||
|
|
||||||
|
// Code
|
||||||
|
"qml": "text-x-qml",
|
||||||
|
"cpp": "text-x-c++src",
|
||||||
|
"c": "text-x-csrc",
|
||||||
|
"h": "text-x-chdr",
|
||||||
|
"hpp": "text-x-c++hdr",
|
||||||
|
"py": "text-x-python",
|
||||||
|
"js": "text-x-javascript",
|
||||||
|
"ts": "text-x-typescript",
|
||||||
|
"jsx": "text-x-javascript",
|
||||||
|
"tsx": "text-x-typescript",
|
||||||
|
"java": "text-x-java",
|
||||||
|
"rs": "text-x-rust",
|
||||||
|
"go": "text-x-go",
|
||||||
|
"rb": "text-x-ruby",
|
||||||
|
"php": "application-x-php",
|
||||||
|
"cs": "text-x-csharp",
|
||||||
|
"swift": "text-x-swift",
|
||||||
|
"kt": "text-x-kotlin",
|
||||||
|
"sh": "application-x-shellscript",
|
||||||
|
"bash": "application-x-shellscript",
|
||||||
|
"zsh": "application-x-shellscript",
|
||||||
|
"fish": "application-x-shellscript",
|
||||||
|
"html": "text-html",
|
||||||
|
"htm": "text-html",
|
||||||
|
"css": "text-css",
|
||||||
|
"scss": "text-x-scss",
|
||||||
|
"sass": "text-x-sass",
|
||||||
|
"less": "text-x-less",
|
||||||
|
"vue": "text-html",
|
||||||
|
"svelte": "text-html",
|
||||||
|
"sql": "application-x-sql",
|
||||||
|
"graphql": "text-x-generic",
|
||||||
|
"lua": "text-x-lua",
|
||||||
|
"pl": "text-x-perl",
|
||||||
|
"dart": "text-x-dart",
|
||||||
|
"r": "text-x-r",
|
||||||
|
"dockerfile": "text-x-generic",
|
||||||
|
"make": "text-x-makefile",
|
||||||
|
|
||||||
|
// Executables
|
||||||
|
"exe": "application-x-executable",
|
||||||
|
"msi": "application-x-msi",
|
||||||
|
"bat": "application-x-ms-dos-executable",
|
||||||
|
"cmd": "application-x-ms-dos-executable",
|
||||||
|
"appimage": "application-x-executable",
|
||||||
|
"run": "application-x-executable",
|
||||||
|
"bin": "application-x-executable",
|
||||||
|
"out": "application-x-executable",
|
||||||
|
"so": "application-x-sharedlib",
|
||||||
|
"dll": "application-x-sharedlib",
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
"ttf": "font-x-generic",
|
||||||
|
"otf": "font-x-generic",
|
||||||
|
"woff": "font-x-generic",
|
||||||
|
"woff2": "font-x-generic"
|
||||||
|
};
|
||||||
|
return map[ext] || "text-x-generic";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function fileNameForPath(str) {
|
||||||
|
if (typeof str !== "string")
|
||||||
|
return "";
|
||||||
|
const trimmed = trimFileProtocol(str);
|
||||||
|
return trimmed.split(/[\\/]/).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimFileExt(str) {
|
||||||
|
if (typeof str !== "string")
|
||||||
|
return "";
|
||||||
|
const trimmed = trimFileProtocol(str);
|
||||||
|
const lastDot = trimmed.lastIndexOf(".");
|
||||||
|
if (lastDot > -1 && lastDot > trimmed.lastIndexOf("/")) {
|
||||||
|
return trimmed.slice(0, lastDot);
|
||||||
|
}
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimFileProtocol(str) {
|
||||||
|
return str.startsWith("file://") ? str.slice(7) : str;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyLightMode() {
|
function applyLightMode() {
|
||||||
if (Config.general.color.neovimColors) {
|
if (Config.general.color.schemeGeneration) {
|
||||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${WallpaperPath.currentWallpaperPath}`, "--thumbnail-path", `${Paths.cache}/imagecache/thumbnail.jpg`, "--output", `${Paths.state}/scheme.json`, "--scheme", `${Config.colors.schemeType}`, "--mode", "light"]);
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${WallpaperPath.currentWallpaperPath}`, "--thumbnail-path", `${Paths.cache}/imagecache/thumbnail.jpg`, "--output", `${Paths.state}/scheme.json`, "--scheme", `${Config.colors.schemeType}`, "--mode", "light"]);
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${DynamicColors.scheme}:${DynamicColors.flavour}`, "--output", `${Paths.state}/scheme.json`, "--mode", "light"]);
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${DynamicColors.scheme}:${DynamicColors.flavour}`, "--output", `${Paths.state}/scheme.json`, "--mode", "light"]);
|
||||||
|
|||||||
+17
-4
@@ -141,7 +141,7 @@ MouseArea {
|
|||||||
sy = ssy;
|
sy = ssy;
|
||||||
ex = x;
|
ex = x;
|
||||||
ey = y;
|
ey = y;
|
||||||
} else {
|
} else if (!saveTimer.running) {
|
||||||
checkClientRects(x, y);
|
checkClientRects(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ MouseArea {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (root.loader.freeze) {
|
if (root.loader.freeze) {
|
||||||
save();
|
saveTimer.start();
|
||||||
} else {
|
} else {
|
||||||
overlay.visible = border.visible = false;
|
overlay.visible = border.visible = false;
|
||||||
screencopy.visible = false;
|
screencopy.visible = false;
|
||||||
@@ -162,6 +162,16 @@ MouseArea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: saveTimer
|
||||||
|
|
||||||
|
interval: 25
|
||||||
|
repeat: false
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onTriggered: root.save()
|
||||||
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: closeAnim
|
id: closeAnim
|
||||||
|
|
||||||
@@ -217,9 +227,10 @@ MouseArea {
|
|||||||
paintCursor: false
|
paintCursor: false
|
||||||
|
|
||||||
onHasContentChanged: {
|
onHasContentChanged: {
|
||||||
if (hasContent && !root.loader.freeze) {
|
if (hasContent) {
|
||||||
overlay.visible = border.visible = true;
|
overlay.visible = border.visible = true;
|
||||||
root.save();
|
if (!root.loader.freeze)
|
||||||
|
root.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,6 +244,7 @@ MouseArea {
|
|||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
opacity: 0.3
|
opacity: 0.3
|
||||||
radius: root.realRounding
|
radius: root.realRounding
|
||||||
|
visible: false
|
||||||
|
|
||||||
layer.effect: MultiEffect {
|
layer.effect: MultiEffect {
|
||||||
maskEnabled: true
|
maskEnabled: true
|
||||||
@@ -270,6 +282,7 @@ MouseArea {
|
|||||||
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
|
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
|
||||||
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
|
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
|
||||||
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
|
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
|
||||||
|
visible: false
|
||||||
x: selectionRect.x - root.realBorderWidth
|
x: selectionRect.x - root.realBorderWidth
|
||||||
y: selectionRect.y - root.realBorderWidth
|
y: selectionRect.y - root.realBorderWidth
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property Item activeMenu: null
|
||||||
|
property Item activeTrigger: null
|
||||||
|
|
||||||
|
function close(menu) {
|
||||||
|
if (!menu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (activeMenu === menu) {
|
||||||
|
activeMenu = null;
|
||||||
|
activeTrigger = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.expanded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeActive() {
|
||||||
|
if (activeMenu)
|
||||||
|
activeMenu.expanded = false;
|
||||||
|
|
||||||
|
activeMenu = null;
|
||||||
|
activeTrigger = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forget(menu) {
|
||||||
|
if (activeMenu === menu) {
|
||||||
|
activeMenu = null;
|
||||||
|
activeTrigger = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hit(item, scenePos) {
|
||||||
|
if (!item || !item.visible)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const p = item.mapFromItem(null, scenePos.x, scenePos.y);
|
||||||
|
return item.contains(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function open(menu, trigger) {
|
||||||
|
if (activeMenu && activeMenu !== menu)
|
||||||
|
activeMenu.expanded = false;
|
||||||
|
|
||||||
|
activeMenu = menu;
|
||||||
|
activeTrigger = trigger || null;
|
||||||
|
menu.expanded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(menu, trigger) {
|
||||||
|
if (activeMenu === menu && menu.expanded)
|
||||||
|
close(menu);
|
||||||
|
else
|
||||||
|
open(menu, trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
-12
@@ -17,7 +17,6 @@ Singleton {
|
|||||||
property var disks: []
|
property var disks: []
|
||||||
property real gpuMemTotal: 0
|
property real gpuMemTotal: 0
|
||||||
property real gpuMemUsed
|
property real gpuMemUsed
|
||||||
property string gpuName: ""
|
|
||||||
property real gpuPerc
|
property real gpuPerc
|
||||||
property real gpuTemp
|
property real gpuTemp
|
||||||
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
|
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
|
||||||
@@ -80,8 +79,29 @@ Singleton {
|
|||||||
onTriggered: {
|
onTriggered: {
|
||||||
stat.reload();
|
stat.reload();
|
||||||
meminfo.reload();
|
meminfo.reload();
|
||||||
|
if (root.gpuType === "GENERIC")
|
||||||
|
gpuUsage.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 60000 * 120
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
storage.running = true;
|
storage.running = true;
|
||||||
gpuUsage.running = true;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: Config.dashboard.resourceUpdateInterval * 5
|
||||||
|
repeat: true
|
||||||
|
running: root.refCount > 0
|
||||||
|
triggeredOnStart: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
sensors.running = true;
|
sensors.running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,10 +132,13 @@ Singleton {
|
|||||||
|
|
||||||
const totalDiff = total - root.lastCpuTotal;
|
const totalDiff = total - root.lastCpuTotal;
|
||||||
const idleDiff = idle - root.lastCpuIdle;
|
const idleDiff = idle - root.lastCpuIdle;
|
||||||
root.cpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
|
const newCpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
|
||||||
|
|
||||||
root.lastCpuTotal = total;
|
root.lastCpuTotal = total;
|
||||||
root.lastCpuIdle = idle;
|
root.lastCpuIdle = idle;
|
||||||
|
|
||||||
|
if (Math.abs(newCpuPerc - root.cpuPerc) >= 0.01)
|
||||||
|
root.cpuPerc = newCpuPerc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,8 +150,14 @@ Singleton {
|
|||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
const data = text();
|
const data = text();
|
||||||
root.memTotal = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1;
|
const total = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1;
|
||||||
root.memUsed = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0;
|
const used = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0;
|
||||||
|
|
||||||
|
if (root.memTotal !== total)
|
||||||
|
root.memTotal = total;
|
||||||
|
|
||||||
|
if (Math.abs(used - root.memUsed) >= 16384)
|
||||||
|
root.memUsed = used;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,22 +303,54 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: gpuUsageNvidia
|
||||||
|
|
||||||
|
command: ["/usr/bin/nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu,memory.used", "--format=csv,noheader,nounits", "-lms", "1000"]
|
||||||
|
running: root.refCount > 0 && root.gpuType === "NVIDIA"
|
||||||
|
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: data => {
|
||||||
|
const parts = String(data).trim().split(/\s*,\s*/);
|
||||||
|
if (parts.length < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const usageRaw = parseInt(parts[0], 10);
|
||||||
|
const tempRaw = parseInt(parts[1], 10);
|
||||||
|
const memRaw = parseInt(parts[2], 10);
|
||||||
|
|
||||||
|
if (!Number.isFinite(usageRaw) || !Number.isFinite(tempRaw) || !Number.isFinite(memRaw))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const newGpuPerc = Math.max(0, Math.min(1, usageRaw / 100));
|
||||||
|
const newGpuTemp = tempRaw;
|
||||||
|
const newGpuMemUsed = root.gpuMemTotal > 0 ? Math.max(0, Math.min(1, memRaw / root.gpuMemTotal)) : 0;
|
||||||
|
|
||||||
|
// Only publish meaningful changes to avoid needless binding churn / repaints
|
||||||
|
if (Math.abs(root.gpuPerc - newGpuPerc) >= 0.01)
|
||||||
|
root.gpuPerc = newGpuPerc;
|
||||||
|
|
||||||
|
if (Math.abs(root.gpuTemp - newGpuTemp) >= 1)
|
||||||
|
root.gpuTemp = newGpuTemp;
|
||||||
|
|
||||||
|
if (Math.abs(root.gpuMemUsed - newGpuMemUsed) >= 0.01)
|
||||||
|
root.gpuMemUsed = newGpuMemUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: gpuUsage
|
id: gpuUsage
|
||||||
|
|
||||||
command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : root.gpuType === "NVIDIA" ? ["nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu,memory.used", "--format=csv,noheader,nounits"] : ["echo"]
|
command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : ["echo"]
|
||||||
|
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
|
console.log("this is running");
|
||||||
if (root.gpuType === "GENERIC") {
|
if (root.gpuType === "GENERIC") {
|
||||||
const percs = text.trim().split("\n");
|
const percs = text.trim().split("\n");
|
||||||
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
|
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
|
||||||
root.gpuPerc = sum / percs.length / 100;
|
root.gpuPerc = sum / percs.length / 100;
|
||||||
} else if (root.gpuType === "NVIDIA") {
|
|
||||||
const [usage, temp, mem] = text.trim().split(",");
|
|
||||||
root.gpuPerc = parseInt(usage, 10) / 100;
|
|
||||||
root.gpuTemp = parseInt(temp, 10);
|
|
||||||
root.gpuMemUsed = parseInt(mem, 10) / root.gpuMemTotal;
|
|
||||||
} else {
|
} else {
|
||||||
root.gpuPerc = 0;
|
root.gpuPerc = 0;
|
||||||
root.gpuTemp = 0;
|
root.gpuTemp = 0;
|
||||||
@@ -314,7 +375,7 @@ Singleton {
|
|||||||
// If AMD Tdie pattern failed, try fallback on Tctl
|
// If AMD Tdie pattern failed, try fallback on Tctl
|
||||||
cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/);
|
cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/);
|
||||||
|
|
||||||
if (cpuTemp)
|
if (cpuTemp && Math.abs(parseFloat(cpuTemp[1]) - root.cpuTemp) >= 0.5)
|
||||||
root.cpuTemp = parseFloat(cpuTemp[1]);
|
root.cpuTemp = parseFloat(cpuTemp[1]);
|
||||||
|
|
||||||
if (root.gpuType !== "GENERIC")
|
if (root.gpuType !== "GENERIC")
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var apps: {
|
||||||
|
const pinnedApps = uniq((Config.dock.pinnedApps ?? []).map(normalizeId));
|
||||||
|
const openMap = buildOpenMap();
|
||||||
|
const openIds = [...openMap.keys()];
|
||||||
|
const sessionOrder = uniq(root.unpinnedOrder.map(normalizeId));
|
||||||
|
|
||||||
|
const orderedUnpinned = sessionOrder.filter(id => openIds.includes(id) && !pinnedApps.includes(id)).concat(openIds.filter(id => !pinnedApps.includes(id) && !sessionOrder.includes(id)));
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
for (const appId of pinnedApps) {
|
||||||
|
out.push({
|
||||||
|
appId,
|
||||||
|
pinned: true,
|
||||||
|
toplevels: openMap.get(appId) ?? []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinnedApps.length > 0) {
|
||||||
|
out.push({
|
||||||
|
appId: root.separatorId,
|
||||||
|
pinned: false,
|
||||||
|
toplevels: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const appId of orderedUnpinned) {
|
||||||
|
out.push({
|
||||||
|
appId,
|
||||||
|
pinned: false,
|
||||||
|
toplevels: openMap.get(appId) ?? []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
readonly property string separatorId: "__dock_separator__"
|
||||||
|
property var unpinnedOrder: []
|
||||||
|
|
||||||
|
function buildOpenMap() {
|
||||||
|
const ignoredRegexes = (Config.dock.ignoredAppRegexes ?? []).map(pattern => new RegExp(pattern, "i"));
|
||||||
|
|
||||||
|
return ToplevelManager.toplevels.values.reduce((map, toplevel) => {
|
||||||
|
if (ignoredRegexes.some(re => re.test(toplevel.appId)))
|
||||||
|
return map;
|
||||||
|
|
||||||
|
const appId = normalizeId(toplevel.appId);
|
||||||
|
if (!appId)
|
||||||
|
return map;
|
||||||
|
|
||||||
|
map.set(appId, (map.get(appId) ?? []).concat([toplevel]));
|
||||||
|
return map;
|
||||||
|
}, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitVisualOrder(ids) {
|
||||||
|
const orderedIds = uniq(ids.map(normalizeId));
|
||||||
|
const separatorIndex = orderedIds.indexOf(root.separatorId);
|
||||||
|
|
||||||
|
const pinnedApps = (separatorIndex === -1 ? [] : orderedIds.slice(0, separatorIndex)).filter(id => id !== root.separatorId);
|
||||||
|
|
||||||
|
const visibleUnpinned = orderedIds.slice(separatorIndex === -1 ? 0 : separatorIndex + 1).filter(id => id !== root.separatorId);
|
||||||
|
|
||||||
|
Config.dock.pinnedApps = pinnedApps;
|
||||||
|
root.unpinnedOrder = visibleUnpinned.concat(root.unpinnedOrder.map(normalizeId).filter(id => !pinnedApps.includes(id) && !visibleUnpinned.includes(id)));
|
||||||
|
Config.saveNoToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPinned(appId) {
|
||||||
|
return uniq((Config.dock.pinnedApps ?? []).map(normalizeId)).includes(normalizeId(appId));
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeId(appId) {
|
||||||
|
if (appId === root.separatorId)
|
||||||
|
return root.separatorId;
|
||||||
|
|
||||||
|
return String(appId ?? "").toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePin(appId) {
|
||||||
|
const id = normalizeId(appId);
|
||||||
|
const pinnedApps = uniq((Config.dock.pinnedApps ?? []).map(normalizeId));
|
||||||
|
const pinned = pinnedApps.includes(id);
|
||||||
|
|
||||||
|
Config.dock.pinnedApps = pinned ? pinnedApps.filter(x => x !== id) : pinnedApps.concat([id]);
|
||||||
|
|
||||||
|
root.unpinnedOrder = pinned ? [id].concat(root.unpinnedOrder.map(normalizeId).filter(x => x !== id)) : root.unpinnedOrder.map(normalizeId).filter(x => x !== id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniq(ids) {
|
||||||
|
return (ids ?? []).filter((id, i, arr) => id && arr.indexOf(id) === i);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Paths
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int availableUpdates: 0
|
||||||
|
property bool loaded
|
||||||
|
property double now: Date.now()
|
||||||
|
property var updates: ({})
|
||||||
|
|
||||||
|
function formatUpdateTime(timestamp) {
|
||||||
|
const diffMs = root.now - timestamp;
|
||||||
|
const minuteMs = 60 * 1000;
|
||||||
|
const hourMs = 60 * minuteMs;
|
||||||
|
const dayMs = 24 * hourMs;
|
||||||
|
|
||||||
|
if (diffMs < minuteMs)
|
||||||
|
return "just now";
|
||||||
|
|
||||||
|
if (diffMs < hourMs)
|
||||||
|
return Math.floor(diffMs / minuteMs) + " min ago";
|
||||||
|
|
||||||
|
if (diffMs < 48 * hourMs)
|
||||||
|
return Math.floor(diffMs / hourMs) + " hr ago";
|
||||||
|
|
||||||
|
return Qt.formatDateTime(new Date(timestamp), "dd hh:mm");
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdatesChanged: {
|
||||||
|
if (!root.loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
saveTimer.restart();
|
||||||
|
availableUpdates = Object.keys(updates).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
if (!root.loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
updatesProc.running = true;
|
||||||
|
interval = 5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 60000
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
|
||||||
|
onTriggered: root.now = Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: updatesProc
|
||||||
|
|
||||||
|
command: ["checkupdates"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
const output = this.text;
|
||||||
|
const lines = output.trim().split("\n").filter(line => line.length > 0);
|
||||||
|
|
||||||
|
const oldMap = root.updates;
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
root.updates = lines.reduce((acc, pkg) => {
|
||||||
|
acc[pkg] = oldMap[pkg] ?? now;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
root.availableUpdates = lines.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: saveTimer
|
||||||
|
|
||||||
|
interval: 1000
|
||||||
|
|
||||||
|
onTriggered: storage.setText(JSON.stringify(root.updates))
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: storage
|
||||||
|
|
||||||
|
path: `${Paths.state}/updates.json`
|
||||||
|
|
||||||
|
onLoadFailed: err => {
|
||||||
|
if (err === FileViewError.FileNotFound) {
|
||||||
|
root.updates = ({});
|
||||||
|
root.loaded = true;
|
||||||
|
setText("{}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.updates = ({});
|
||||||
|
root.loaded = true;
|
||||||
|
}
|
||||||
|
onLoaded: {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(text());
|
||||||
|
root.updates = data && typeof data === "object" && !Array.isArray(data) ? data : {};
|
||||||
|
} catch (e) {
|
||||||
|
root.updates = ({});
|
||||||
|
}
|
||||||
|
|
||||||
|
root.loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,20 +19,20 @@ Searcher {
|
|||||||
function preview(path: string): void {
|
function preview(path: string): void {
|
||||||
previewPath = path;
|
previewPath = path;
|
||||||
if (Config.general.color.schemeGeneration)
|
if (Config.general.color.schemeGeneration)
|
||||||
Quickshell.execDetached(["sh", "-c", `zshell-cli scheme generate --image-path ${previewPath} --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${previewPath}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
|
||||||
showPreview = true;
|
showPreview = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaper(path: string): void {
|
function setWallpaper(path: string): void {
|
||||||
actualCurrent = path;
|
actualCurrent = path;
|
||||||
WallpaperPath.currentWallpaperPath = path;
|
WallpaperPath.currentWallpaperPath = path;
|
||||||
Quickshell.execDetached(["sh", "-c", `zshell-cli wallpaper lockscreen --input-image=${root.actualCurrent} --output-path=${Paths.state}/lockscreen_bg.png --blur-amount=${Config.lock.blurAmount}`]);
|
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopPreview(): void {
|
function stopPreview(): void {
|
||||||
showPreview = false;
|
showPreview = false;
|
||||||
if (Config.general.color.schemeGeneration)
|
if (Config.general.color.schemeGeneration)
|
||||||
Quickshell.execDetached(["sh", "-c", `zshell-cli scheme generate --image-path ${root.actualCurrent} --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
extraOpts: useFuzzy ? ({}) : ({
|
extraOpts: useFuzzy ? ({}) : ({
|
||||||
|
|||||||
+11
-17
@@ -7,15 +7,16 @@ import qs.Modules
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
Item {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property color barColor: DynamicColors.palette.m3primary
|
property color barColor: DynamicColors.palette.m3primary
|
||||||
property color textColor: DynamicColors.palette.m3onSurface
|
property color textColor: DynamicColors.palette.m3onSurface
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
anchors.top: parent.top
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
implicitWidth: 150
|
implicitWidth: 150
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -24,34 +25,27 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
Component.onCompleted: console.log(root.height, root.implicitHeight)
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
|
||||||
radius: height / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Appearance.padding.small
|
anchors.leftMargin: Appearance.padding.small
|
||||||
anchors.rightMargin: Appearance.padding.small * 2
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: root.implicitWidth - Appearance.padding.small * 3
|
||||||
|
|
||||||
MaterialIcon {
|
MaterialIcon {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
animate: true
|
animate: true
|
||||||
color: Audio.muted ? DynamicColors.palette.m3error : root.textColor
|
color: Audio.muted ? DynamicColors.palette.m3error : root.textColor
|
||||||
font.pointSize: 14
|
font.pointSize: Appearance.font.size.larger
|
||||||
text: Audio.muted ? "volume_off" : "volume_up"
|
text: Audio.muted ? "volume_off" : "volume_up"
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: "#50ffffff"
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||||
implicitHeight: 4
|
implicitHeight: 4
|
||||||
radius: 20
|
radius: 20
|
||||||
|
|
||||||
@@ -74,13 +68,13 @@ Item {
|
|||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
animate: true
|
animate: true
|
||||||
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor
|
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor
|
||||||
font.pointSize: 14
|
font.pointSize: Appearance.font.size.larger
|
||||||
text: Audio.sourceMuted ? "mic_off" : "mic"
|
text: Audio.sourceMuted ? "mic_off" : "mic"
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: "#50ffffff"
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||||
implicitHeight: 4
|
implicitHeight: 4
|
||||||
radius: 20
|
radius: 20
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ ShapePath {
|
|||||||
readonly property bool flatten: wrapper.height < rounding * 2
|
readonly property bool flatten: wrapper.height < rounding * 2
|
||||||
property real ibr: invertBottomRounding ? -1 : 1
|
property real ibr: invertBottomRounding ? -1 : 1
|
||||||
required property bool invertBottomRounding
|
required property bool invertBottomRounding
|
||||||
readonly property real rounding: 8
|
property real rounding: Appearance.rounding.smallest
|
||||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||||
required property Wrapper wrapper
|
required property Wrapper wrapper
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,218 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
import qs.Modules.UPower
|
||||||
|
import qs.Modules.Network
|
||||||
|
import qs.Modules.Updates
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Wrapper popouts
|
||||||
|
required property ShellScreen screen
|
||||||
|
readonly property int vPadding: 6
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
function checkPopout(x: real): void {
|
||||||
|
const ch = childAt(x, height / 2) as WrappedLoader;
|
||||||
|
|
||||||
|
if (!ch || ch?.id === "spacer") {
|
||||||
|
if (!popouts.currentName.startsWith("traymenu"))
|
||||||
|
popouts.hasCurrent = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibilities.sidebar || visibilities.dashboard || visibilities.resources || visibilities.settings)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const id = ch.id;
|
||||||
|
const top = ch.x;
|
||||||
|
const item = ch.item;
|
||||||
|
const itemWidth = item.implicitWidth;
|
||||||
|
|
||||||
|
if (id === "audio" && Config.barConfig.popouts.audio) {
|
||||||
|
popouts.currentName = "audio";
|
||||||
|
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||||
|
popouts.hasCurrent = true;
|
||||||
|
} else if (id === "network" && Config.barConfig.popouts.network) {
|
||||||
|
popouts.currentName = "network";
|
||||||
|
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||||
|
popouts.hasCurrent = true;
|
||||||
|
} else if (id === "upower" && Config.barConfig.popouts.upower) {
|
||||||
|
popouts.currentName = "upower";
|
||||||
|
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||||
|
popouts.hasCurrent = true;
|
||||||
|
} else if (id === "updates") {
|
||||||
|
popouts.currentName = "updates";
|
||||||
|
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||||
|
popouts.hasCurrent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
|
||||||
|
// model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index)
|
||||||
|
model: Config.barConfig.entries
|
||||||
|
|
||||||
|
DelegateChooser {
|
||||||
|
role: "id"
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "spacer"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "workspaces"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: Workspaces {
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "audio"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: AudioWidget {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "tray"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: TrayWidget {
|
||||||
|
loader: root
|
||||||
|
popouts: root.popouts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "resources"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: Resources {
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "updates"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: UpdatesWidget {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "notifBell"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: NotifBell {
|
||||||
|
popouts: root.popouts
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "clock"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: Clock {
|
||||||
|
loader: root
|
||||||
|
popouts: root.popouts
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "activeWindow"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: WindowTitle {
|
||||||
|
bar: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "upower"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: UPowerWidget {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "network"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: NetworkWidget {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "media"
|
||||||
|
|
||||||
|
delegate: WrappedLoader {
|
||||||
|
sourceComponent: MediaWidget {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component WrappedLoader: Loader {
|
||||||
|
required property bool enabled
|
||||||
|
required property string id
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
function findFirstEnabled(): Item {
|
||||||
|
const count = repeater.count;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const item = repeater.itemAt(i);
|
||||||
|
if (item?.enabled)
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLastEnabled(): Item {
|
||||||
|
for (let i = repeater.count - 1; i >= 0; i--) {
|
||||||
|
const item = repeater.itemAt(i);
|
||||||
|
if (item?.enabled)
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: findFirstEnabled() === this ? root.vPadding : 0
|
||||||
|
Layout.rightMargin: findLastEnabled() === this ? root.vPadding : 0
|
||||||
|
active: enabled
|
||||||
|
visible: enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
-197
@@ -11,224 +11,72 @@ import qs.Helpers
|
|||||||
import qs.Modules.UPower
|
import qs.Modules.UPower
|
||||||
import qs.Modules.Network
|
import qs.Modules.Network
|
||||||
|
|
||||||
RowLayout {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property PanelWindow bar
|
readonly property int contentHeight: Config.barConfig.height + padding * 2
|
||||||
|
readonly property int exclusiveZone: Config.barConfig.autoHide ? Config.barConfig.border : contentHeight
|
||||||
|
property bool isHovered
|
||||||
|
readonly property int padding: Math.max(Appearance.padding.smaller, Config.barConfig.border)
|
||||||
required property Wrapper popouts
|
required property Wrapper popouts
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
|
readonly property bool shouldBeVisible: (!Config.barConfig.autoHide || visibilities.bar || isHovered)
|
||||||
readonly property int vPadding: 6
|
readonly property int vPadding: 6
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
function checkPopout(x: real): void {
|
function checkPopout(x: real): void {
|
||||||
const ch = childAt(x, 2) as WrappedLoader;
|
content.item?.checkPopout(x);
|
||||||
|
|
||||||
if (!ch || ch?.id === "spacer") {
|
|
||||||
if (!popouts.currentName.startsWith("traymenu"))
|
|
||||||
popouts.hasCurrent = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visibilities.sidebar || visibilities.dashboard || visibilities.resources)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const id = ch.id;
|
|
||||||
const top = ch.x;
|
|
||||||
const item = ch.item;
|
|
||||||
const itemWidth = item.implicitWidth;
|
|
||||||
|
|
||||||
if (id === "audio" && Config.barConfig.popouts.audio) {
|
|
||||||
popouts.currentName = "audio";
|
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
|
||||||
popouts.hasCurrent = true;
|
|
||||||
} else if (id === "network" && Config.barConfig.popouts.network) {
|
|
||||||
popouts.currentName = "network";
|
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
|
||||||
popouts.hasCurrent = true;
|
|
||||||
} else if (id === "upower" && Config.barConfig.popouts.upower) {
|
|
||||||
popouts.currentName = "upower";
|
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
|
||||||
popouts.hasCurrent = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitHeight: 34
|
implicitHeight: Config.barConfig.border
|
||||||
|
visible: height > Config.barConfig.border
|
||||||
|
|
||||||
CustomShortcut {
|
states: State {
|
||||||
name: "toggle-overview"
|
name: "visible"
|
||||||
|
when: root.shouldBeVisible
|
||||||
|
|
||||||
onPressed: {
|
PropertyChanges {
|
||||||
Hyprland.refreshWorkspaces();
|
root.implicitHeight: root.contentHeight
|
||||||
Hyprland.refreshMonitors();
|
|
||||||
if (root.popouts.hasCurrent && root.popouts.currentName === "overview") {
|
|
||||||
root.popouts.hasCurrent = false;
|
|
||||||
} else {
|
|
||||||
root.popouts.currentName = "overview";
|
|
||||||
root.popouts.currentCenter = root.width / 2;
|
|
||||||
root.popouts.hasCurrent = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: ""
|
||||||
|
to: "visible"
|
||||||
|
|
||||||
Repeater {
|
Anim {
|
||||||
id: repeater
|
duration: MaterialEasing.expressiveEffectsTime
|
||||||
|
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||||
// model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index)
|
property: "implicitHeight"
|
||||||
model: Config.barConfig.entries
|
target: root
|
||||||
|
|
||||||
DelegateChooser {
|
|
||||||
role: "id"
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "spacer"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
from: "visible"
|
||||||
|
to: ""
|
||||||
|
|
||||||
DelegateChoice {
|
Anim {
|
||||||
roleValue: "workspaces"
|
duration: MaterialEasing.expressiveEffectsTime
|
||||||
|
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||||
delegate: WrappedLoader {
|
property: "implicitHeight"
|
||||||
sourceComponent: Workspaces {
|
target: root
|
||||||
bar: root.bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "audio"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: AudioWidget {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "tray"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: TrayWidget {
|
|
||||||
bar: root.bar
|
|
||||||
loader: root
|
|
||||||
popouts: root.popouts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "resources"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: Resources {
|
|
||||||
visibilities: root.visibilities
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "updates"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: UpdatesWidget {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "notifBell"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: NotifBell {
|
|
||||||
popouts: root.popouts
|
|
||||||
visibilities: root.visibilities
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "clock"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: Clock {
|
|
||||||
loader: root
|
|
||||||
popouts: root.popouts
|
|
||||||
visibilities: root.visibilities
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "activeWindow"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: WindowTitle {
|
|
||||||
bar: root
|
|
||||||
monitor: Brightness.getMonitorForScreen(root.screen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "upower"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: UPowerWidget {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "network"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: NetworkWidget {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "media"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: MediaWidget {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
|
|
||||||
component WrappedLoader: Loader {
|
Loader {
|
||||||
required property bool enabled
|
id: content
|
||||||
required property string id
|
|
||||||
required property int index
|
|
||||||
|
|
||||||
function findFirstEnabled(): Item {
|
active: root.shouldBeVisible || root.visible
|
||||||
const count = repeater.count;
|
anchors.bottom: parent.bottom
|
||||||
for (let i = 0; i < count; i++) {
|
anchors.left: parent.left
|
||||||
const item = repeater.itemAt(i);
|
anchors.right: parent.right
|
||||||
if (item?.enabled)
|
|
||||||
return item;
|
sourceComponent: Bar {
|
||||||
}
|
height: root.contentHeight
|
||||||
return null;
|
popouts: root.popouts
|
||||||
|
screen: root.screen
|
||||||
|
visibilities: root.visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLastEnabled(): Item {
|
|
||||||
for (let i = repeater.count - 1; i >= 0; i--) {
|
|
||||||
const item = repeater.itemAt(i);
|
|
||||||
if (item?.enabled)
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.leftMargin: findFirstEnabled() === this ? root.vPadding : 0
|
|
||||||
Layout.rightMargin: findLastEnabled() === this ? root.vPadding : 0
|
|
||||||
active: enabled
|
|
||||||
visible: enabled
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-10
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import qs.Modules
|
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
@@ -17,7 +16,8 @@ Item {
|
|||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Config.barConfig.autoHide && !root.visibilities.bar ? "transparent" : DynamicColors.palette.m3surface
|
anchors.margins: -1
|
||||||
|
color: DynamicColors.palette.m3surface
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
|
|
||||||
layer.effect: MultiEffect {
|
layer.effect: MultiEffect {
|
||||||
@@ -38,14 +38,11 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: Config.barConfig.autoHide && !root.visibilities.bar ? 4 : root.bar.implicitHeight
|
anchors.margins: Config.barConfig.border + 1
|
||||||
topLeftRadius: 8
|
anchors.topMargin: root.bar.implicitHeight + 1
|
||||||
topRightRadius: 8
|
radius: Config.barConfig.border > 0 ? Config.barConfig.rounding : 0
|
||||||
|
topLeftRadius: Config.barConfig.rounding
|
||||||
Behavior on anchors.topMargin {
|
topRightRadius: Config.barConfig.rounding
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
Layout.preferredWidth: 40
|
|
||||||
color: "transparent"
|
|
||||||
radius: 1000
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
fill: 1
|
|
||||||
font.pointSize: 24
|
|
||||||
text: "arrow_back_2"
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
onClicked: {
|
|
||||||
if (Calendar.displayMonth === 0) {
|
|
||||||
Calendar.displayMonth = 11;
|
|
||||||
Calendar.displayYear -= 1;
|
|
||||||
} else {
|
|
||||||
Calendar.displayMonth -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: 14
|
|
||||||
font.weight: 600
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: new Date(Calendar.displayYear, Calendar.displayMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy")
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
Layout.preferredWidth: 40
|
|
||||||
color: "transparent"
|
|
||||||
radius: 1000
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
fill: 1
|
|
||||||
font.pointSize: 24
|
|
||||||
rotation: 180
|
|
||||||
text: "arrow_back_2"
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
onClicked: {
|
|
||||||
if (Calendar.displayMonth === 11) {
|
|
||||||
Calendar.displayMonth = 0;
|
|
||||||
Calendar.displayYear += 1;
|
|
||||||
} else {
|
|
||||||
Calendar.displayMonth += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property Item wrapper
|
|
||||||
|
|
||||||
implicitHeight: layout.childrenRect.height + layout.anchors.margins * 2
|
|
||||||
implicitWidth: layout.childrenRect.width + layout.anchors.margins * 2
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: layout
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
anchors.margins: 16
|
|
||||||
spacing: 16
|
|
||||||
|
|
||||||
// Header with month/year and navigation
|
|
||||||
CalendarHeader {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calendar grid
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
|
||||||
Layout.preferredWidth: weekNumberColumn.width
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.preferredHeight: dayOfWeekRow.height
|
|
||||||
}
|
|
||||||
|
|
||||||
WeekNumberColumn {
|
|
||||||
id: weekNumberColumn
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
Layout.preferredHeight: weekNumbers.values.length * 44
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
DayOfWeekRow {
|
|
||||||
id: dayOfWeekRow
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 30
|
|
||||||
locale: Qt.locale()
|
|
||||||
}
|
|
||||||
|
|
||||||
MonthGrid {
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
|
||||||
Layout.preferredWidth: childrenRect.width
|
|
||||||
locale: Qt.locale()
|
|
||||||
wrapper: root.wrapper
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property var locale
|
|
||||||
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: 7
|
|
||||||
|
|
||||||
Item {
|
|
||||||
readonly property string dayName: {
|
|
||||||
// Get the day name for this column
|
|
||||||
const dayIndex = (index + Calendar.weekStartDay) % 7;
|
|
||||||
return root.locale.dayName(dayIndex, Locale.ShortFormat);
|
|
||||||
}
|
|
||||||
required property int index
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 30
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
font.pointSize: 11
|
|
||||||
font.weight: 500
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
opacity: 0.8
|
|
||||||
text: parent.dayName
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property var locale
|
|
||||||
required property Item wrapper
|
|
||||||
|
|
||||||
columnSpacing: 4
|
|
||||||
columns: 7
|
|
||||||
rowSpacing: 4
|
|
||||||
uniformCellHeights: true
|
|
||||||
uniformCellWidths: true
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: repeater
|
|
||||||
|
|
||||||
model: ScriptModel {
|
|
||||||
values: Calendar.getWeeksForMonth(Calendar.displayMonth, Calendar.displayYear)
|
|
||||||
|
|
||||||
Behavior on values {
|
|
||||||
SequentialAnimation {
|
|
||||||
id: switchAnim
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
Anim {
|
|
||||||
from: 1.0
|
|
||||||
property: "opacity"
|
|
||||||
to: 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
from: 1.0
|
|
||||||
property: "scale"
|
|
||||||
to: 0.8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyAction {
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
Anim {
|
|
||||||
from: 0.0
|
|
||||||
property: "opacity"
|
|
||||||
to: 1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
from: 0.8
|
|
||||||
property: "scale"
|
|
||||||
to: 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
required property int index
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
Layout.preferredHeight: width
|
|
||||||
Layout.preferredWidth: 40
|
|
||||||
color: {
|
|
||||||
if (modelData.isToday) {
|
|
||||||
return DynamicColors.palette.m3primaryContainer;
|
|
||||||
}
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
radius: 1000
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: {
|
|
||||||
if (parent.modelData.isToday) {
|
|
||||||
return DynamicColors.palette.m3onPrimaryContainer;
|
|
||||||
}
|
|
||||||
return DynamicColors.palette.m3onSurface;
|
|
||||||
}
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
opacity: parent.modelData.isCurrentMonth ? 1.0 : 0.4
|
|
||||||
text: parent.modelData.day.toString()
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component Anim: NumberAnimation {
|
|
||||||
duration: MaterialEasing.expressiveEffectsTime
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
target: root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
readonly property var weekNumbers: Calendar.getWeekNumbers(Calendar.displayMonth, Calendar.displayYear)
|
|
||||||
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: root.weekNumbers
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: weekItem
|
|
||||||
|
|
||||||
required property int index
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
Layout.preferredWidth: 20
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: weekText
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
font.pointSize: 10
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
opacity: 0.5
|
|
||||||
text: weekItem.modelData
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+20
-27
@@ -6,43 +6,36 @@ import qs.Modules
|
|||||||
import qs.Helpers as Helpers
|
import qs.Helpers as Helpers
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
Item {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property RowLayout loader
|
required property RowLayout loader
|
||||||
required property Wrapper popouts
|
required property Wrapper popouts
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
anchors.top: parent.top
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
implicitWidth: timeText.contentWidth + 5 * 2
|
implicitWidth: timeText.contentWidth + Appearance.padding.normal * 2
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
CustomRect {
|
CustomText {
|
||||||
anchors.bottomMargin: 3
|
id: timeText
|
||||||
anchors.fill: parent
|
|
||||||
anchors.topMargin: 3
|
|
||||||
color: "transparent"
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
CustomText {
|
anchors.centerIn: parent
|
||||||
id: timeText
|
color: DynamicColors.palette.m3onSurface
|
||||||
|
text: Time.dateStr
|
||||||
|
|
||||||
anchors.centerIn: parent
|
Behavior on color {
|
||||||
color: DynamicColors.palette.m3onSurface
|
CAnim {
|
||||||
text: Time.dateStr
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.visibilities.dashboard = !root.visibilities.dashboard;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.visibilities.dashboard = !root.visibilities.dashboard;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -5,10 +5,10 @@ import Quickshell.Services.SystemTray
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Modules.Calendar
|
|
||||||
import qs.Modules.WSOverview
|
import qs.Modules.WSOverview
|
||||||
import qs.Modules.Network
|
import qs.Modules.Network
|
||||||
import qs.Modules.UPower
|
import qs.Modules.UPower
|
||||||
|
import qs.Modules.Updates
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -69,14 +69,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popout {
|
|
||||||
name: "calendar"
|
|
||||||
|
|
||||||
sourceComponent: CalendarPopup {
|
|
||||||
wrapper: root.wrapper
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Popout {
|
Popout {
|
||||||
name: "overview"
|
name: "overview"
|
||||||
|
|
||||||
@@ -101,6 +93,14 @@ Item {
|
|||||||
wrapper: root.wrapper
|
wrapper: root.wrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Popout {
|
||||||
|
name: "updates"
|
||||||
|
|
||||||
|
sourceComponent: UpdatesPopout {
|
||||||
|
wrapper: root.wrapper
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component Popout: Loader {
|
component Popout: Loader {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ ShapePath {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool flatten: wrapper.height < rounding * 2
|
readonly property bool flatten: wrapper.height < rounding * 2
|
||||||
readonly property real rounding: 8
|
readonly property real rounding: Appearance.rounding.normal
|
||||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||||
required property Wrapper wrapper
|
required property Wrapper wrapper
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: 6
|
radius: Appearance.rounding.normal - anchors.margins
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: view
|
id: view
|
||||||
|
|||||||
@@ -72,17 +72,21 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
CustomClippingRect {
|
||||||
id: content
|
anchors.fill: parent
|
||||||
|
|
||||||
active: true
|
Loader {
|
||||||
anchors.bottom: parent.bottom
|
id: content
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
sourceComponent: Content {
|
active: true
|
||||||
state: root.dashState
|
anchors.bottom: parent.bottom
|
||||||
visibilities: root.visibilities
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
sourceComponent: Content {
|
||||||
|
state: root.dashState
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
import qs.Paths
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 998
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
property real menuX: 0
|
||||||
|
property real menuY: 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
id: popupBackground
|
||||||
|
readonly property real padding: 4
|
||||||
|
|
||||||
|
x: root.menuX
|
||||||
|
y: root.menuY
|
||||||
|
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
|
implicitWidth: menuLayout.implicitWidth + padding * 2
|
||||||
|
implicitHeight: menuLayout.implicitHeight + padding * 2
|
||||||
|
|
||||||
|
Behavior on opacity { Anim {} }
|
||||||
|
opacity: root.visible ? 1 : 0
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: menuLayout
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: openTerminalRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: openTerminalRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: "terminal"; font.pointSize: 20 }
|
||||||
|
CustomText { text: "Open terminal"; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Quickshell.execDetached([Config.general.apps.terminal, "--working-directory", FileUtils.trimFileProtocol(Paths.desktop)])
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: DynamicColors.palette.m3outlineVariant
|
||||||
|
Layout.topMargin: 4
|
||||||
|
Layout.bottomMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: settingsRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: settingsRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: "settings"; font.pointSize: 20 }
|
||||||
|
CustomText { text: "ZShell settings"; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
const visibilities = Visibilities.getForActive();
|
||||||
|
visibilities.settings = true;
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: DynamicColors.palette.m3outlineVariant
|
||||||
|
Layout.topMargin: 4
|
||||||
|
Layout.bottomMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: logoutRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: logoutRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: "logout"; font.pointSize: 20 }
|
||||||
|
CustomText { text: "Logout"; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Hyprland.dispatch("global quickshell:sessionOpen")
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomRect {
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// implicitHeight: 1
|
||||||
|
// color: DynamicColors.palette.m3outlineVariant
|
||||||
|
// Layout.topMargin: 4
|
||||||
|
// Layout.bottomMargin: 4
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// CustomRect {
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// radius: popupBackground.radius - popupBackground.padding
|
||||||
|
// implicitHeight: desktopIconsRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
//
|
||||||
|
// RowLayout {
|
||||||
|
// id: desktopIconsRow
|
||||||
|
// spacing: 8
|
||||||
|
// anchors.fill: parent
|
||||||
|
// anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
//
|
||||||
|
// MaterialIcon { text: Config.options.background.showDesktopIcons ? "visibility_off" : "visibility"; font.pointSize: 20 }
|
||||||
|
// CustomText { text: Config.options.background.showDesktopIcons ? "Hide icons" : "Show icons"; Layout.fillWidth: true }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// StateLayer {
|
||||||
|
// anchors.fill: parent
|
||||||
|
//
|
||||||
|
// onClicked: {
|
||||||
|
// Config.options.background.showDesktopIcons = !Config.options.background.showDesktopIcons
|
||||||
|
// root.close()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAt(mouseX, mouseY, parentW, parentH) {
|
||||||
|
menuX = Math.floor(Math.min(mouseX, parentW - popupBackground.implicitWidth))
|
||||||
|
menuY = Math.floor(Math.min(mouseY, parentH - popupBackground.implicitHeight))
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contextMenu
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 999
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
property string targetFilePath: ""
|
||||||
|
property bool targetIsDir: false
|
||||||
|
property var targetAppEntry: null
|
||||||
|
|
||||||
|
property var targetPaths: []
|
||||||
|
|
||||||
|
signal openFileRequested(string path, bool isDir)
|
||||||
|
signal renameRequested(string path)
|
||||||
|
|
||||||
|
property real menuX: 0
|
||||||
|
property real menuY: 0
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
id: popupBackground
|
||||||
|
readonly property real padding: Appearance.padding.small
|
||||||
|
|
||||||
|
x: contextMenu.menuX
|
||||||
|
y: contextMenu.menuY
|
||||||
|
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
|
implicitWidth: menuLayout.implicitWidth + padding * 2
|
||||||
|
implicitHeight: menuLayout.implicitHeight + padding * 2
|
||||||
|
|
||||||
|
Behavior on opacity { Anim {} }
|
||||||
|
opacity: contextMenu.visible ? 1 : 0
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: menuLayout
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredWidth: 160
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: openRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: openRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: "open_in_new"; font.pointSize: 20 }
|
||||||
|
CustomText { text: "Open"; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
for (let i = 0; i < contextMenu.targetPaths.length; i++) {
|
||||||
|
let p = contextMenu.targetPaths[i];
|
||||||
|
if (p === contextMenu.targetFilePath) {
|
||||||
|
if (p.endsWith(".desktop") && contextMenu.targetAppEntry) contextMenu.targetAppEntry.execute()
|
||||||
|
else contextMenu.openFileRequested(p, contextMenu.targetIsDir)
|
||||||
|
} else {
|
||||||
|
Quickshell.execDetached(["xdg-open", p])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextMenu.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: openWithRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: openWithRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: contextMenu.targetIsDir ? "terminal" : "apps"; font.pointSize: 20 }
|
||||||
|
CustomText { text: contextMenu.targetIsDir ? "Open in terminal" : "Open with..."; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (contextMenu.targetIsDir) {
|
||||||
|
Quickshell.execDetached([Config.general.apps.terminal, "--working-directory", contextMenu.targetFilePath])
|
||||||
|
} else {
|
||||||
|
Quickshell.execDetached(["xdg-open", contextMenu.targetFilePath])
|
||||||
|
}
|
||||||
|
contextMenu.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: DynamicColors.palette.m3outlineVariant
|
||||||
|
Layout.topMargin: 4
|
||||||
|
Layout.bottomMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: copyPathRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: copyPathRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: "content_copy"; font.pointSize: 20 }
|
||||||
|
CustomText { text: "Copy path"; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Quickshell.execDetached(["wl-copy", contextMenu.targetPaths.join("\n")])
|
||||||
|
contextMenu.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: contextMenu.targetPaths.length === 1
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: renameRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: renameRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon { text: "edit"; font.pointSize: 20 }
|
||||||
|
CustomText { text: "Rename"; Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
contextMenu.renameRequested(contextMenu.targetFilePath)
|
||||||
|
contextMenu.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: DynamicColors.palette.m3outlineVariant
|
||||||
|
Layout.topMargin: 4
|
||||||
|
Layout.bottomMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
radius: popupBackground.radius - popupBackground.padding
|
||||||
|
implicitHeight: deleteRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: deleteRow
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.smaller
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
text: "delete"
|
||||||
|
font.pointSize: 20
|
||||||
|
color: deleteButton.hovered ? DynamicColors.palette.m3onError : DynamicColors.palette.m3error
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: "Move to trash"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: deleteButton.hovered ? DynamicColors.palette.m3onError : DynamicColors.palette.m3error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: deleteButton
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.tPalette.m3error
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
let cmd = ["gio", "trash"].concat(contextMenu.targetPaths)
|
||||||
|
Quickshell.execDetached(cmd)
|
||||||
|
contextMenu.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAt(mouseX, mouseY, path, isDir, appEnt, parentW, parentH, selectionArray) {
|
||||||
|
targetFilePath = path
|
||||||
|
targetIsDir = isDir
|
||||||
|
targetAppEntry = appEnt
|
||||||
|
|
||||||
|
targetPaths = (selectionArray && selectionArray.length > 0) ? selectionArray : [path]
|
||||||
|
|
||||||
|
menuX = Math.floor(Math.min(mouseX, parentW - popupBackground.implicitWidth))
|
||||||
|
menuY = Math.floor(Math.min(mouseY, parentH - popupBackground.implicitHeight))
|
||||||
|
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs.Config
|
||||||
|
import qs.Components
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: delegateRoot
|
||||||
|
|
||||||
|
property var appEntry: fileName.endsWith(".desktop") ? DesktopEntries.byId(DesktopUtils.getAppId(fileName)) : null
|
||||||
|
property bool fileIsDir: model.isDir
|
||||||
|
property string fileName: model.fileName
|
||||||
|
property string filePath: model.filePath
|
||||||
|
property int gridX: model.gridX
|
||||||
|
property int gridY: model.gridY
|
||||||
|
property bool isSnapping: snapAnimX.running || snapAnimY.running
|
||||||
|
property bool lassoActive
|
||||||
|
property string resolvedIcon: {
|
||||||
|
if (fileName.endsWith(".desktop")) {
|
||||||
|
if (appEntry && appEntry.icon && appEntry.icon !== "")
|
||||||
|
return appEntry.icon;
|
||||||
|
return AppSearch.guessIcon(DesktopUtils.getAppId(fileName));
|
||||||
|
} else if (DesktopUtils.getFileType(fileName, fileIsDir) === "image") {
|
||||||
|
return "file://" + filePath;
|
||||||
|
} else {
|
||||||
|
return DesktopUtils.getIconName(fileName, fileIsDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compensateAndSnap(absVisX, absVisY) {
|
||||||
|
dragContainer.x = absVisX - delegateRoot.x;
|
||||||
|
dragContainer.y = absVisY - delegateRoot.y;
|
||||||
|
snapAnimX.start();
|
||||||
|
snapAnimY.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDragX() {
|
||||||
|
return dragContainer.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDragY() {
|
||||||
|
return dragContainer.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: root.cellHeight
|
||||||
|
width: root.cellWidth
|
||||||
|
x: gridX * root.cellWidth
|
||||||
|
y: gridY * root.cellHeight
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
enabled: !mouseArea.drag.active && !isSnapping && !root.selectedIcons.includes(filePath)
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on y {
|
||||||
|
enabled: !mouseArea.drag.active && !isSnapping && !root.selectedIcons.includes(filePath)
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dragContainer
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
when: mouseArea.drag.active
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
opacity: 0.8
|
||||||
|
scale: 1.1
|
||||||
|
target: dragContainer
|
||||||
|
z: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transform: Translate {
|
||||||
|
x: (root.selectedIcons.includes(filePath) && root.dragLeader !== "" && root.dragLeader !== filePath) ? root.groupDragX : 0
|
||||||
|
y: (root.selectedIcons.includes(filePath) && root.dragLeader !== "" && root.dragLeader !== filePath) ? root.groupDragY : 0
|
||||||
|
}
|
||||||
|
transitions: Transition {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onXChanged: {
|
||||||
|
if (mouseArea.drag.active) {
|
||||||
|
root.dragLeader = filePath;
|
||||||
|
root.groupDragX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onYChanged: {
|
||||||
|
if (mouseArea.drag.active) {
|
||||||
|
root.dragLeader = filePath;
|
||||||
|
root.groupDragY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: snapAnimX
|
||||||
|
|
||||||
|
duration: 250
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
property: "x"
|
||||||
|
target: dragContainer
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: snapAnimY
|
||||||
|
|
||||||
|
duration: 250
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
property: "y"
|
||||||
|
target: dragContainer
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
implicitSize: 48
|
||||||
|
source: {
|
||||||
|
if (delegateRoot.resolvedIcon.startsWith("file://") || delegateRoot.resolvedIcon.startsWith("/")) {
|
||||||
|
return delegateRoot.resolvedIcon;
|
||||||
|
} else {
|
||||||
|
return Quickshell.iconPath(delegateRoot.resolvedIcon, fileIsDir ? "folder" : "text-x-generic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: 40
|
||||||
|
width: 88
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "white"
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
maximumLineCount: 2
|
||||||
|
style: Text.Outline
|
||||||
|
styleColor: "black"
|
||||||
|
text: (appEntry && appEntry.name !== "") ? appEntry.name : fileName
|
||||||
|
visible: !renameLoader.active
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: renameLoader
|
||||||
|
|
||||||
|
active: root.editingFilePath === filePath
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: 24
|
||||||
|
width: 110
|
||||||
|
|
||||||
|
sourceComponent: CustomTextInput {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
color: "white"
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: fileName
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
forceActiveFocus();
|
||||||
|
selectAll();
|
||||||
|
}
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
|
if (text.trim() !== "" && text !== fileName) {
|
||||||
|
let newName = text.trim();
|
||||||
|
let newPath = filePath.substring(0, filePath.lastIndexOf('/') + 1) + newName;
|
||||||
|
|
||||||
|
Quickshell.execDetached(["mv", filePath, newPath]);
|
||||||
|
}
|
||||||
|
root.editingFilePath = "";
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.key === Qt.Key_Escape) {
|
||||||
|
root.editingFilePath = "";
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (!activeFocus && root.editingFilePath === filePath) {
|
||||||
|
root.editingFilePath = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
color: "white"
|
||||||
|
opacity: root.selectedIcons.includes(filePath) ? 0.2 : 0.0
|
||||||
|
radius: 8
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: root.lassoActive ? undefined : Qt.PointingHandCursor
|
||||||
|
drag.target: dragContainer
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onClicked: mouse => {
|
||||||
|
root.forceActiveFocus();
|
||||||
|
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
if (!root.selectedIcons.includes(filePath)) {
|
||||||
|
root.selectedIcons = [filePath];
|
||||||
|
}
|
||||||
|
let pos = mapToItem(root, mouse.x, mouse.y);
|
||||||
|
root.contextMenu.openAt(pos.x, pos.y, filePath, fileIsDir, appEntry, root.width, root.height, root.selectedIcons);
|
||||||
|
} else {
|
||||||
|
root.selectedIcons = [filePath];
|
||||||
|
root.contextMenu.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDoubleClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
if (filePath.endsWith(".desktop") && appEntry)
|
||||||
|
appEntry.execute();
|
||||||
|
else
|
||||||
|
root.exec(filePath, fileIsDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressed: mouse => {
|
||||||
|
if (mouse.button === Qt.LeftButton && !root.selectedIcons.includes(filePath)) {
|
||||||
|
root.selectedIcons = [filePath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
if (drag.active) {
|
||||||
|
let absoluteX = delegateRoot.x + dragContainer.x;
|
||||||
|
let absoluteY = delegateRoot.y + dragContainer.y;
|
||||||
|
let snapX = Math.max(0, Math.round(absoluteX / root.cellWidth));
|
||||||
|
let snapY = Math.max(0, Math.round(absoluteY / root.cellHeight));
|
||||||
|
|
||||||
|
root.performMassDrop(filePath, snapX, snapY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
color: "white"
|
||||||
|
opacity: parent.containsMouse ? 0.1 : 0.0
|
||||||
|
radius: 8
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Modules
|
||||||
|
import qs.Helpers
|
||||||
|
import qs.Config
|
||||||
|
import qs.Components
|
||||||
|
import qs.Paths
|
||||||
|
import ZShell.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int cellHeight: 110
|
||||||
|
property int cellWidth: 100
|
||||||
|
property var contextMenu: desktopMenu
|
||||||
|
property string dragLeader: ""
|
||||||
|
property string editingFilePath: ""
|
||||||
|
property real groupDragX: 0
|
||||||
|
property real groupDragY: 0
|
||||||
|
property bool lassoActive: false
|
||||||
|
property var selectedIcons: []
|
||||||
|
property real startX: 0
|
||||||
|
property real startY: 0
|
||||||
|
|
||||||
|
function exec(filePath, isDir) {
|
||||||
|
const cmd = ["xdg-open", filePath];
|
||||||
|
Quickshell.execDetached(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
function performMassDrop(leaderPath, targetX, targetY) {
|
||||||
|
let maxCol = Math.max(0, Math.floor(gridArea.width / cellWidth) - 1);
|
||||||
|
let maxRow = Math.max(0, Math.floor(gridArea.height / cellHeight) - 1);
|
||||||
|
|
||||||
|
let visuals = [];
|
||||||
|
for (let i = 0; i < gridArea.children.length; i++) {
|
||||||
|
let child = gridArea.children[i];
|
||||||
|
if (child.filePath && root.selectedIcons.includes(child.filePath)) {
|
||||||
|
let isLeader = (root.dragLeader === child.filePath);
|
||||||
|
let offsetX = isLeader ? child.getDragX() : root.groupDragX;
|
||||||
|
let offsetY = isLeader ? child.getDragY() : root.groupDragY;
|
||||||
|
visuals.push({
|
||||||
|
childRef: child,
|
||||||
|
absX: child.x + offsetX,
|
||||||
|
absY: child.y + offsetY
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
desktopModel.massMove(root.selectedIcons, leaderPath, targetX, targetY, maxCol, maxRow);
|
||||||
|
|
||||||
|
for (let i = 0; i < visuals.length; i++) {
|
||||||
|
visuals[i].childRef.compensateAndSnap(visuals[i].absX, visuals[i].absY);
|
||||||
|
}
|
||||||
|
|
||||||
|
root.dragLeader = "";
|
||||||
|
root.groupDragX = 0;
|
||||||
|
root.groupDragY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.key === Qt.Key_F2 && selectedIcons.length > 0)
|
||||||
|
editingFilePath = selectedIcons[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopModel {
|
||||||
|
id: desktopModel
|
||||||
|
|
||||||
|
Component.onCompleted: loadDirectory(FileUtils.trimFileProtocol(Paths.desktop))
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: lasso
|
||||||
|
|
||||||
|
function hideLasso() {
|
||||||
|
fadeIn.stop();
|
||||||
|
fadeOut.start();
|
||||||
|
root.lassoActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLasso() {
|
||||||
|
root.lassoActive = true;
|
||||||
|
fadeOut.stop();
|
||||||
|
visible = true;
|
||||||
|
fadeIn.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
border.color: DynamicColors.palette.m3primary
|
||||||
|
border.width: 1
|
||||||
|
color: DynamicColors.tPalette.m3primary
|
||||||
|
opacity: 0
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
visible: false
|
||||||
|
z: 99
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: fadeIn
|
||||||
|
|
||||||
|
duration: 120
|
||||||
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
|
target: lasso
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: fadeOut
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
from: lasso.opacity
|
||||||
|
property: "opacity"
|
||||||
|
target: lasso
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptAction {
|
||||||
|
script: lasso.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if (lasso.visible) {
|
||||||
|
lasso.x = Math.floor(Math.min(mouse.x, root.startX));
|
||||||
|
lasso.y = Math.floor(Math.min(mouse.y, root.startY));
|
||||||
|
lasso.width = Math.floor(Math.abs(mouse.x - root.startX));
|
||||||
|
lasso.height = Math.floor(Math.abs(mouse.y - root.startY));
|
||||||
|
|
||||||
|
let minCol = Math.floor((lasso.x - gridArea.x) / cellWidth);
|
||||||
|
let maxCol = Math.floor((lasso.x + lasso.width - gridArea.x) / cellWidth);
|
||||||
|
let minRow = Math.floor((lasso.y - gridArea.y) / cellHeight);
|
||||||
|
let maxRow = Math.floor((lasso.y + lasso.height - gridArea.y) / cellHeight);
|
||||||
|
|
||||||
|
let newSelection = [];
|
||||||
|
for (let i = 0; i < gridArea.children.length; i++) {
|
||||||
|
let child = gridArea.children[i];
|
||||||
|
if (child.filePath !== undefined && child.gridX >= minCol && child.gridX <= maxCol && child.gridY >= minRow && child.gridY <= maxRow) {
|
||||||
|
newSelection.push(child.filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.selectedIcons = newSelection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressed: mouse => {
|
||||||
|
root.editingFilePath = "";
|
||||||
|
desktopMenu.close();
|
||||||
|
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
root.selectedIcons = [];
|
||||||
|
bgContextMenu.openAt(mouse.x, mouse.y, root.width, root.height);
|
||||||
|
} else {
|
||||||
|
bgContextMenu.close();
|
||||||
|
root.selectedIcons = [];
|
||||||
|
root.startX = Math.floor(mouse.x);
|
||||||
|
root.startY = Math.floor(mouse.y);
|
||||||
|
lasso.x = Math.floor(mouse.x);
|
||||||
|
lasso.y = Math.floor(mouse.y);
|
||||||
|
lasso.width = 0;
|
||||||
|
lasso.height = 0;
|
||||||
|
lasso.showLasso();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
lasso.hideLasso();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: gridArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 20
|
||||||
|
anchors.topMargin: 40
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: desktopModel
|
||||||
|
|
||||||
|
delegate: DesktopIconDelegate {
|
||||||
|
property int itemIndex: index
|
||||||
|
|
||||||
|
lassoActive: root.lassoActive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopIconContextMenu {
|
||||||
|
id: desktopMenu
|
||||||
|
|
||||||
|
onOpenFileRequested: (path, isDir) => root.exec(path, isDir)
|
||||||
|
onRenameRequested: path => {
|
||||||
|
root.editingFilePath = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundContextMenu {
|
||||||
|
id: bgContextMenu
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+297
-5
@@ -2,6 +2,8 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQml.Models
|
||||||
|
import qs.Modules.Dock.Parts
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Config
|
import qs.Config
|
||||||
@@ -9,20 +11,310 @@ import qs.Config
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property int dockContentWidth: TaskbarApps.apps.reduce((sum, app, i) => sum + (app.appId === TaskbarApps.separatorId ? 1 : Config.dock.height) + (i > 0 ? dockRow.spacing : 0), 0)
|
||||||
|
property bool dragActive: false
|
||||||
|
property real dragHeight: Config.dock.height
|
||||||
|
property real dragStartX: 0
|
||||||
|
property real dragStartY: 0
|
||||||
|
property real dragWidth: Config.dock.height
|
||||||
|
property real dragX: 0
|
||||||
|
property real dragY: 0
|
||||||
|
property string draggedAppId: ""
|
||||||
|
property var draggedModelData: null
|
||||||
|
property bool dropAnimating: false
|
||||||
readonly property int padding: Appearance.padding.small
|
readonly property int padding: Appearance.padding.small
|
||||||
required property var panels
|
required property var panels
|
||||||
|
property var pendingCommitIds: []
|
||||||
readonly property int rounding: Appearance.rounding.large
|
readonly property int rounding: Appearance.rounding.large
|
||||||
|
required property ShellScreen screen
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
property var visualIds: []
|
||||||
|
|
||||||
|
function beginVisualDrag(appId, modelData, item) {
|
||||||
|
const pos = item.mapToItem(root, 0, 0);
|
||||||
|
|
||||||
|
root.visualIds = TaskbarApps.apps.map(app => app.appId);
|
||||||
|
root.draggedAppId = appId;
|
||||||
|
root.draggedModelData = modelData;
|
||||||
|
root.dragWidth = item.width;
|
||||||
|
root.dragHeight = item.height;
|
||||||
|
root.dragStartX = pos.x;
|
||||||
|
root.dragStartY = pos.y;
|
||||||
|
root.dragX = pos.x;
|
||||||
|
root.dragY = pos.y;
|
||||||
|
root.dragActive = true;
|
||||||
|
root.dropAnimating = false;
|
||||||
|
root.pendingCommitIds = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function endVisualDrag() {
|
||||||
|
const ids = root.visualIds.slice();
|
||||||
|
const finalIndex = root.visualIds.indexOf(root.draggedAppId);
|
||||||
|
const finalItem = dockRow.itemAtIndex(finalIndex);
|
||||||
|
|
||||||
|
// Stop sending drag events now, but keep the proxy alive while it settles.
|
||||||
|
root.dragActive = false;
|
||||||
|
|
||||||
|
// In a dock, the destination delegate should normally be instantiated.
|
||||||
|
// If not, just finish immediately.
|
||||||
|
if (!finalItem) {
|
||||||
|
root.pendingCommitIds = ids;
|
||||||
|
root.finishVisualDrag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pos = finalItem.mapToItem(root, 0, 0);
|
||||||
|
|
||||||
|
root.pendingCommitIds = ids;
|
||||||
|
root.dropAnimating = true;
|
||||||
|
|
||||||
|
settleX.to = pos.x;
|
||||||
|
settleY.to = pos.y;
|
||||||
|
settleAnim.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishVisualDrag() {
|
||||||
|
const ids = root.pendingCommitIds.slice();
|
||||||
|
|
||||||
|
root.dragActive = false;
|
||||||
|
root.dropAnimating = false;
|
||||||
|
root.draggedAppId = "";
|
||||||
|
root.draggedModelData = null;
|
||||||
|
root.visualIds = [];
|
||||||
|
root.pendingCommitIds = [];
|
||||||
|
|
||||||
|
TaskbarApps.commitVisualOrder(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveArrayItem(list, from, to) {
|
||||||
|
const next = list.slice();
|
||||||
|
const [item] = next.splice(from, 1);
|
||||||
|
next.splice(to, 0, item);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewVisualMove(from, hovered, before) {
|
||||||
|
let to = hovered + (before ? 0 : 1);
|
||||||
|
|
||||||
|
if (to > from)
|
||||||
|
to -= 1;
|
||||||
|
|
||||||
|
to = Math.max(0, Math.min(visualModel.items.count - 1, to));
|
||||||
|
|
||||||
|
if (from === to)
|
||||||
|
return;
|
||||||
|
|
||||||
|
visualModel.items.move(from, to);
|
||||||
|
root.visualIds = moveArrayItem(root.visualIds, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
implicitHeight: Config.dock.height + root.padding * 2
|
implicitHeight: Config.dock.height + root.padding * 2
|
||||||
implicitWidth: dockRow.implicitWidth + root.padding * 2
|
implicitWidth: dockRow.contentWidth + root.padding * 2
|
||||||
|
|
||||||
RowLayout {
|
ParallelAnimation {
|
||||||
|
id: settleAnim
|
||||||
|
|
||||||
|
onFinished: root.finishVisualDrag()
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
id: settleX
|
||||||
|
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "dragX"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
id: settleY
|
||||||
|
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "dragY"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: dockDelegate
|
||||||
|
|
||||||
|
DropArea {
|
||||||
|
id: slot
|
||||||
|
|
||||||
|
readonly property string appId: modelData.appId
|
||||||
|
readonly property bool isSeparator: appId === TaskbarApps.separatorId
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
function previewReorder(drag) {
|
||||||
|
const source = drag.source;
|
||||||
|
if (!source || !source.appId || source.appId === appId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const from = source.visualIndex;
|
||||||
|
const hovered = slot.DelegateModel.itemsIndex;
|
||||||
|
|
||||||
|
if (from < 0 || hovered < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.previewVisualMove(from, hovered, drag.x < width / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
height: Config.dock.height
|
||||||
|
width: isSeparator ? 1 : Config.dock.height
|
||||||
|
|
||||||
|
ListView.onRemove: removeAnim.start()
|
||||||
|
onEntered: drag => previewReorder(drag)
|
||||||
|
onPositionChanged: drag => previewReorder(drag)
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: removeAnim
|
||||||
|
|
||||||
|
ScriptAction {
|
||||||
|
script: slot.ListView.delayRemove = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
Anim {
|
||||||
|
property: "opacity"
|
||||||
|
target: slot
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
property: "scale"
|
||||||
|
target: slot
|
||||||
|
to: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptAction {
|
||||||
|
script: {
|
||||||
|
slot.ListView.delayRemove = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAppButton {
|
||||||
|
id: button
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
appListRoot: root
|
||||||
|
appToplevel: modelData
|
||||||
|
visibilities: root.visibilities
|
||||||
|
visible: root.draggedAppId !== slot.appId
|
||||||
|
}
|
||||||
|
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
|
||||||
|
enabled: !slot.isSeparator
|
||||||
|
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||||
|
target: null
|
||||||
|
xAxis.enabled: true
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active) {
|
||||||
|
root.beginVisualDrag(slot.appId, slot.modelData, button);
|
||||||
|
} else if (root.draggedAppId === slot.appId) {
|
||||||
|
dragProxy.Drag.drop();
|
||||||
|
root.endVisualDrag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onActiveTranslationChanged: {
|
||||||
|
if (!active || root.draggedAppId !== slot.appId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.dragX = root.dragStartX + activeTranslation.x;
|
||||||
|
root.dragY = root.dragStartY + activeTranslation.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateModel {
|
||||||
|
id: visualModel
|
||||||
|
|
||||||
|
delegate: dockDelegate
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
objectProp: "appId"
|
||||||
|
values: TaskbarApps.apps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomListView {
|
||||||
id: dockRow
|
id: dockRow
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
property bool enableAddAnimation: false
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: Appearance.spacing.small
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
height: Config.dock.height
|
||||||
|
implicitWidth: root.dockContentWidth + Config.dock.height
|
||||||
|
interactive: !(root.dragActive || root.dropAnimating)
|
||||||
|
model: visualModel
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
spacing: Appearance.padding.smaller
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
ParallelAnimation {
|
||||||
|
Anim {
|
||||||
|
duration: dockRow.enableAddAnimation ? Appearance.anim.durations.normal : 0
|
||||||
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: dockRow.enableAddAnimation ? Appearance.anim.durations.normal : 0
|
||||||
|
from: 0.5
|
||||||
|
property: "scale"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
displaced: Transition {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
properties: "x,y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
move: Transition {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
properties: "x,y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Qt.callLater(() => enableAddAnimation = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dragProxy
|
||||||
|
|
||||||
|
property string appId: root.draggedAppId
|
||||||
|
property int visualIndex: root.visualIds.indexOf(root.draggedAppId)
|
||||||
|
|
||||||
|
Drag.active: root.dragActive
|
||||||
|
Drag.hotSpot.x: width / 2
|
||||||
|
Drag.hotSpot.y: height / 2
|
||||||
|
Drag.source: dragProxy
|
||||||
|
height: root.dragHeight
|
||||||
|
visible: (root.dragActive || root.dropAnimating) && !!root.draggedModelData
|
||||||
|
width: root.dragWidth
|
||||||
|
x: root.dragX
|
||||||
|
y: root.dragY
|
||||||
|
z: 9999
|
||||||
|
|
||||||
|
DockAppButton {
|
||||||
|
anchors.fill: parent
|
||||||
|
appListRoot: root
|
||||||
|
appToplevel: root.draggedModelData
|
||||||
|
enabled: false
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs.Components
|
||||||
|
import qs.Helpers
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool appIsActive: appToplevel?.toplevels.find(t => (t.activated == true)) !== undefined
|
||||||
|
property var appListRoot
|
||||||
|
property var appToplevel
|
||||||
|
property real countDotHeight: 4
|
||||||
|
property real countDotWidth: 10
|
||||||
|
property var desktopEntry: DesktopEntries.heuristicLookup(appToplevel?.appId)
|
||||||
|
property real iconSize: implicitHeight - 20
|
||||||
|
readonly property bool isSeparator: appToplevel?.appId === "__dock_separator__"
|
||||||
|
property int lastFocused: -1
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
implicitHeight: Config.dock.height
|
||||||
|
implicitWidth: isSeparator ? 1 : implicitHeight
|
||||||
|
radius: Appearance.rounding.normal - Appearance.padding.small
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: !isSeparator
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
sourceComponent: ColumnLayout {
|
||||||
|
IconImage {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
implicitSize: root.iconSize
|
||||||
|
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel?.appId), "image-missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Math.min(appToplevel?.toplevels.length, 3)
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
color: appIsActive ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3primary
|
||||||
|
implicitHeight: root.countDotHeight
|
||||||
|
implicitWidth: (appToplevel?.toplevels.length <= 3) ? root.countDotWidth : root.countDotHeight // Circles when too many
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
onClicked: {
|
||||||
|
if (appToplevel?.toplevels.length === 0) {
|
||||||
|
root.desktopEntry?.execute();
|
||||||
|
root.visibilities.dock = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastFocused = (lastFocused + 1) % appToplevel?.toplevels.length;
|
||||||
|
appToplevel?.toplevels[lastFocused].activate();
|
||||||
|
root.visibilities.dock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onApplicationsChanged() {
|
||||||
|
root.desktopEntry = DesktopEntries.heuristicLookup(appToplevel?.appId);
|
||||||
|
}
|
||||||
|
|
||||||
|
target: DesktopEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: isSeparator
|
||||||
|
|
||||||
|
sourceComponent: DockSeparator {
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.bottomMargin: dockRow.padding + Appearance.rounding.normal
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: dockRow.padding + Appearance.rounding.normal
|
||||||
|
color: DynamicColors.palette.m3outlineVariant
|
||||||
|
implicitWidth: 1
|
||||||
|
}
|
||||||
@@ -18,6 +18,12 @@ Item {
|
|||||||
implicitWidth: content.implicitWidth
|
implicitWidth: content.implicitWidth
|
||||||
visible: height > 0
|
visible: height > 0
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onShouldBeActiveChanged: {
|
onShouldBeActiveChanged: {
|
||||||
if (shouldBeActive) {
|
if (shouldBeActive) {
|
||||||
timer.stop();
|
timer.stop();
|
||||||
@@ -84,12 +90,13 @@ Item {
|
|||||||
id: content
|
id: content
|
||||||
|
|
||||||
active: false
|
active: false
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
sourceComponent: Content {
|
sourceComponent: Content {
|
||||||
panels: root.panels
|
panels: root.panels
|
||||||
|
screen: root.screen
|
||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
|
|
||||||
Component.onCompleted: root.contentHeight = implicitHeight
|
Component.onCompleted: root.contentHeight = implicitHeight
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property bool flatten: wrapper.width < rounding * 2
|
||||||
|
readonly property real rounding: Appearance.rounding.normal
|
||||||
|
readonly property real roundingX: flatten ? wrapper.width / 2 : rounding
|
||||||
|
required property Wrapper wrapper
|
||||||
|
|
||||||
|
fillColor: DynamicColors.palette.m3surface
|
||||||
|
strokeWidth: -1
|
||||||
|
|
||||||
|
Behavior on fillColor {
|
||||||
|
CAnim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||||
|
radiusY: root.rounding
|
||||||
|
relativeX: root.roundingX
|
||||||
|
relativeY: root.rounding
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: root.wrapper.width - root.roundingX * 2
|
||||||
|
relativeY: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||||
|
radiusY: root.rounding
|
||||||
|
relativeX: root.roundingX
|
||||||
|
relativeY: root.rounding
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: 0
|
||||||
|
relativeY: root.wrapper.height - root.rounding * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||||
|
radiusY: root.rounding
|
||||||
|
relativeX: -root.roundingX
|
||||||
|
relativeY: root.rounding
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: -(root.wrapper.width - root.roundingX * 2)
|
||||||
|
relativeY: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||||
|
radiusY: root.rounding
|
||||||
|
relativeX: -root.roundingX
|
||||||
|
relativeY: root.rounding
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Config
|
||||||
|
import qs.Components
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property var colors: ["#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#a855f7", "#ec4899", "#ffffff", "#000000"]
|
||||||
|
required property Canvas drawing
|
||||||
|
required property var visibilities
|
||||||
|
|
||||||
|
function syncFromPenColor() {
|
||||||
|
if (!drawing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!saturationSlider.pressed)
|
||||||
|
saturationSlider.value = drawing.penColor.hsvSaturation;
|
||||||
|
|
||||||
|
if (!brightnessSlider.pressed)
|
||||||
|
brightnessSlider.value = drawing.penColor.hsvValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePenColorFromHsv() {
|
||||||
|
if (!drawing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
drawing.penColor = Qt.hsva(huePicker.currentHue, saturationSlider.value, brightnessSlider.value, drawing.penColor.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: column.height + Appearance.padding.larger * 2
|
||||||
|
implicitWidth: huePicker.implicitWidth + Appearance.padding.normal * 2
|
||||||
|
|
||||||
|
Component.onCompleted: syncFromPenColor()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onPenColorChanged() {
|
||||||
|
root.syncFromPenColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root.drawing
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
ColorArcPicker {
|
||||||
|
id: huePicker
|
||||||
|
|
||||||
|
drawing: root.drawing
|
||||||
|
}
|
||||||
|
|
||||||
|
GradientSlider {
|
||||||
|
id: saturationSlider
|
||||||
|
|
||||||
|
brightness: brightnessSlider.value
|
||||||
|
channel: "saturation"
|
||||||
|
from: 0
|
||||||
|
hue: huePicker.currentHue
|
||||||
|
icon: "\ue40a"
|
||||||
|
implicitHeight: 30
|
||||||
|
implicitWidth: palette.width
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
to: 1
|
||||||
|
|
||||||
|
onMoved: root.updatePenColorFromHsv()
|
||||||
|
}
|
||||||
|
|
||||||
|
GradientSlider {
|
||||||
|
id: brightnessSlider
|
||||||
|
|
||||||
|
channel: "brightness"
|
||||||
|
from: 0
|
||||||
|
hue: huePicker.currentHue
|
||||||
|
icon: "\ue1ac"
|
||||||
|
implicitHeight: 30
|
||||||
|
implicitWidth: palette.width
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
saturation: saturationSlider.value
|
||||||
|
to: 1
|
||||||
|
|
||||||
|
onMoved: root.updatePenColorFromHsv()
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: palette
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
columns: 5
|
||||||
|
rowSpacing: 8
|
||||||
|
rows: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.colors
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: colorCircle
|
||||||
|
|
||||||
|
required property color modelData
|
||||||
|
readonly property bool selected: Qt.colorEqual(root.drawing.penColor, modelData)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 28
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
border.color: Qt.rgba(0, 0, 0, 0.25)
|
||||||
|
border.width: Qt.colorEqual(modelData, "#ffffff") ? 1 : 0
|
||||||
|
color: colorCircle.modelData
|
||||||
|
height: 20
|
||||||
|
radius: width / 2
|
||||||
|
width: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
border.color: selected ? "#ffffff" : Qt.rgba(1, 1, 1, 0.28)
|
||||||
|
border.width: selected ? 3 : 1
|
||||||
|
color: "transparent"
|
||||||
|
height: parent.height
|
||||||
|
radius: width / 2
|
||||||
|
width: parent.height
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
onClicked: root.drawing.penColor = colorCircle.modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilledSlider {
|
||||||
|
from: 1
|
||||||
|
icon: "border_color"
|
||||||
|
implicitHeight: 30
|
||||||
|
implicitWidth: palette.width
|
||||||
|
multiplier: 1
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
to: 45
|
||||||
|
value: root.drawing.penWidth
|
||||||
|
|
||||||
|
onMoved: root.drawing.penWidth = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Components
|
||||||
|
import qs.Helpers
|
||||||
|
import qs.Config
|
||||||
|
import qs.Daemons
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Canvas drawing
|
||||||
|
property bool expanded: true
|
||||||
|
required property ShellScreen screen
|
||||||
|
readonly property bool shouldBeActive: visibilities.isDrawing
|
||||||
|
required property var visibilities
|
||||||
|
|
||||||
|
implicitHeight: content.implicitHeight
|
||||||
|
implicitWidth: 0
|
||||||
|
visible: width > 0
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hidden"
|
||||||
|
when: !root.shouldBeActive
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
root.implicitWidth: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
icon.opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
content.opacity: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "collapsed"
|
||||||
|
when: root.shouldBeActive && !root.expanded
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
root.implicitWidth: icon.implicitWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
icon.opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
content.opacity: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "visible"
|
||||||
|
when: root.shouldBeActive && root.expanded
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
root.implicitWidth: content.implicitWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
icon.opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
content.opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "*"
|
||||||
|
to: "*"
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
Anim {
|
||||||
|
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||||
|
property: "implicitWidth"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
property: "opacity"
|
||||||
|
target: icon
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
property: "opacity"
|
||||||
|
target: content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!visible)
|
||||||
|
root.expanded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
active: Qt.binding(() => root.shouldBeActive || root.visible)
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: content.contentItem.height
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
sourceComponent: MaterialIcon {
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: "arrow_forward_ios"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
sourceComponent: Content {
|
||||||
|
drawing: root.drawing
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: active = Qt.binding(() => root.shouldBeActive || root.visible)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-18
@@ -5,30 +5,22 @@ import qs.Daemons
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
|
|
||||||
Item {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property string currentMedia: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
|
readonly property string currentMedia: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
|
||||||
readonly property int textWidth: Math.min(metrics.width, 200)
|
readonly property int textWidth: Math.min(metrics.width, 200)
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
anchors.top: parent.top
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
implicitWidth: layout.implicitWidth + Appearance.padding.normal * 2
|
implicitWidth: layout.implicitWidth + Appearance.padding.normal * 2
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
Anim {
|
Anim {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: metrics
|
id: metrics
|
||||||
|
|
||||||
@@ -39,11 +31,7 @@ Item {
|
|||||||
RowLayout {
|
RowLayout {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.centerIn: parent
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Appearance.padding.normal
|
|
||||||
anchors.top: parent.top
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
Anim {
|
Anim {
|
||||||
@@ -53,7 +41,7 @@ Item {
|
|||||||
MaterialIcon {
|
MaterialIcon {
|
||||||
animate: true
|
animate: true
|
||||||
color: Players.active?.isPlaying ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
color: Players.active?.isPlaying ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||||
font.pointSize: 14
|
font.pointSize: Appearance.font.size.larger
|
||||||
text: Players.active?.isPlaying ? "music_note" : "music_off"
|
text: Players.active?.isPlaying ? "music_note" : "music_off"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-30
@@ -5,46 +5,39 @@ import qs.Config
|
|||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
Item {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Wrapper popouts
|
required property Wrapper popouts
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
anchors.top: parent.top
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
implicitWidth: 30
|
implicitWidth: implicitHeight
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
CustomRect {
|
MaterialIcon {
|
||||||
anchors.bottomMargin: 3
|
id: notificationCenterIcon
|
||||||
anchors.fill: parent
|
|
||||||
anchors.topMargin: 3
|
|
||||||
color: "transparent"
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
MaterialIcon {
|
property color iconColor: DynamicColors.palette.m3onSurface
|
||||||
id: notificationCenterIcon
|
|
||||||
|
|
||||||
property color iconColor: DynamicColors.palette.m3onSurface
|
anchors.centerIn: parent
|
||||||
|
color: iconColor
|
||||||
|
font.family: "Material Symbols Rounded"
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
||||||
|
|
||||||
anchors.centerIn: parent
|
Behavior on color {
|
||||||
color: iconColor
|
CAnim {
|
||||||
font.family: "Material Symbols Rounded"
|
|
||||||
font.pointSize: 16
|
|
||||||
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.visibilities.sidebar = !root.visibilities.sidebar;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.visibilities.sidebar = !root.visibilities.sidebar;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ ShapePath {
|
|||||||
readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
|
readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
|
||||||
required property Wrapper wrapper
|
required property Wrapper wrapper
|
||||||
|
|
||||||
fillColor: flatten ? "transparent" : DynamicColors.palette.m3surface
|
fillColor: DynamicColors.palette.m3surface
|
||||||
strokeWidth: -1
|
strokeWidth: -1
|
||||||
|
|
||||||
Behavior on fillColor {
|
Behavior on fillColor {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Item {
|
|||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
name: "hidden"
|
name: "hidden"
|
||||||
when: root.visibilities.sidebar
|
when: root.visibilities.sidebar || root.visibilities.dashboard || (root.panels.popouts.hasCurrent && root.panels.popouts.currentName.startsWith("traymenu"))
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
root.implicitHeight: 0
|
root.implicitHeight: 0
|
||||||
|
|||||||
+18
-14
@@ -113,22 +113,26 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
CustomClippingRect {
|
||||||
id: content
|
anchors.fill: parent
|
||||||
|
|
||||||
anchors.left: parent.left
|
Loader {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
id: content
|
||||||
|
|
||||||
sourceComponent: Content {
|
anchors.left: parent.left
|
||||||
brightness: root.brightness
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
monitor: root.monitor
|
|
||||||
muted: root.muted
|
sourceComponent: Content {
|
||||||
sourceMuted: root.sourceMuted
|
brightness: root.brightness
|
||||||
sourceVolume: root.sourceVolume
|
monitor: root.monitor
|
||||||
visibilities: root.visibilities
|
muted: root.muted
|
||||||
volume: root.volume
|
sourceMuted: root.sourceMuted
|
||||||
|
sourceVolume: root.sourceVolume
|
||||||
|
visibilities: root.visibilities
|
||||||
|
volume: root.volume
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: active = Qt.binding(() => root.shouldBeActive || root.visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: active = Qt.binding(() => root.shouldBeActive || root.visible)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Shapes
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property color borderColor: warning ? DynamicColors.palette.m3onError : mainColor
|
|
||||||
required property color mainColor
|
|
||||||
required property double percentage
|
|
||||||
property bool shown: true
|
|
||||||
property color usageColor: warning ? DynamicColors.palette.m3error : mainColor
|
|
||||||
property bool warning: percentage * 100 >= warningThreshold
|
|
||||||
property int warningThreshold: 100
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
implicitHeight: 22
|
|
||||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
|
||||||
visible: width > 0 && height > 0
|
|
||||||
|
|
||||||
Behavior on percentage {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: resourceRowLayout
|
|
||||||
|
|
||||||
spacing: 2
|
|
||||||
x: shown ? 0 : -resourceRowLayout.width
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
implicitHeight: root.implicitHeight
|
|
||||||
implicitWidth: 14
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: backgroundCircle
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
border.color: "#404040"
|
|
||||||
border.width: 1
|
|
||||||
color: "#40000000"
|
|
||||||
height: 14
|
|
||||||
radius: height / 2
|
|
||||||
width: 14
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape {
|
|
||||||
anchors.fill: backgroundCircle
|
|
||||||
preferredRendererType: Shape.CurveRenderer
|
|
||||||
smooth: true
|
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
fillColor: root.usageColor
|
|
||||||
startX: backgroundCircle.width / 2
|
|
||||||
startY: backgroundCircle.height / 2
|
|
||||||
strokeWidth: 0
|
|
||||||
|
|
||||||
Behavior on fillColor {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: backgroundCircle.width / 2
|
|
||||||
y: 0 + (1 / 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAngleArc {
|
|
||||||
centerX: backgroundCircle.width / 2
|
|
||||||
centerY: backgroundCircle.height / 2
|
|
||||||
radiusX: backgroundCircle.width / 2 - (1 / 2)
|
|
||||||
radiusY: backgroundCircle.height / 2 - (1 / 2)
|
|
||||||
startAngle: -90
|
|
||||||
sweepAngle: 360 * root.percentage
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: backgroundCircle.width / 2
|
|
||||||
y: backgroundCircle.height / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
capStyle: ShapePath.FlatCap
|
|
||||||
fillColor: "transparent"
|
|
||||||
strokeColor: root.borderColor
|
|
||||||
strokeWidth: 1
|
|
||||||
|
|
||||||
Behavior on strokeColor {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAngleArc {
|
|
||||||
centerX: backgroundCircle.width / 2
|
|
||||||
centerY: backgroundCircle.height / 2
|
|
||||||
radiusX: backgroundCircle.width / 2 - (1 / 2)
|
|
||||||
radiusY: backgroundCircle.height / 2 - (1 / 2)
|
|
||||||
startAngle: -90
|
|
||||||
sweepAngle: 360 * root.percentage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+30
-57
@@ -4,7 +4,7 @@ import QtQuick.Shapes
|
|||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property color accentColor: warning ? DynamicColors.palette.m3error : mainColor
|
property color accentColor: warning ? DynamicColors.palette.m3error : mainColor
|
||||||
@@ -19,75 +19,48 @@ Item {
|
|||||||
property bool warning: percentage * 100 >= warningThreshold
|
property bool warning: percentage * 100 >= warningThreshold
|
||||||
property int warningThreshold: 80
|
property int warningThreshold: 80
|
||||||
|
|
||||||
clip: true
|
|
||||||
height: implicitHeight
|
|
||||||
implicitHeight: 34
|
|
||||||
implicitWidth: 34
|
|
||||||
percentage: 0
|
percentage: 0
|
||||||
visible: width > 0 && height > 0
|
|
||||||
width: implicitWidth
|
|
||||||
|
|
||||||
Behavior on animatedPercentage {
|
Behavior on animatedPercentage {
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.large
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: animatedPercentage = percentage
|
Component.onCompleted: animatedPercentage = percentage
|
||||||
onPercentageChanged: animatedPercentage = percentage
|
onPercentageChanged: {
|
||||||
|
const next = percentage;
|
||||||
|
|
||||||
Canvas {
|
if (Math.abs(next - animatedPercentage) >= 0.05)
|
||||||
id: gaugeCanvas
|
animatedPercentage = next;
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
height: width
|
|
||||||
width: Math.min(parent.width, parent.height)
|
|
||||||
|
|
||||||
Component.onCompleted: requestPaint()
|
|
||||||
onPaint: {
|
|
||||||
const ctx = getContext("2d");
|
|
||||||
ctx.reset();
|
|
||||||
const cx = width / 2;
|
|
||||||
const cy = (height / 2) + 1;
|
|
||||||
const radius = (Math.min(width, height) - 12) / 2;
|
|
||||||
const lineWidth = 3;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep);
|
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
ctx.lineCap = "round";
|
|
||||||
ctx.strokeStyle = DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
|
|
||||||
ctx.stroke();
|
|
||||||
if (root.animatedPercentage > 0) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep * root.animatedPercentage);
|
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
ctx.lineCap = "round";
|
|
||||||
ctx.strokeStyle = root.accentColor;
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onAnimatedPercentageChanged() {
|
|
||||||
gaugeCanvas.requestPaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: root
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onPaletteChanged() {
|
|
||||||
gaugeCanvas.requestPaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: DynamicColors
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialIcon {
|
MaterialIcon {
|
||||||
anchors.centerIn: parent
|
id: icon
|
||||||
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
color: DynamicColors.palette.m3onSurface
|
||||||
font.pointSize: 12
|
font.pointSize: Appearance.font.size.larger
|
||||||
text: root.icon
|
text: root.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
Layout.preferredHeight: root.height - Appearance.padding.small
|
||||||
|
Layout.preferredWidth: 4
|
||||||
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: fill
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: false
|
||||||
|
color: root.mainColor
|
||||||
|
implicitHeight: Math.ceil(root.percentage * parent.height)
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
transform: Scale {
|
||||||
|
origin.y: fill.height
|
||||||
|
yScale: Math.max(0.001, root.animatedPercentage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Config
|
|
||||||
import qs.Components
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property color barColor: DynamicColors.palette.m3primary
|
|
||||||
required property string details
|
|
||||||
required property string iconString
|
|
||||||
required property double percentage
|
|
||||||
required property string resourceName
|
|
||||||
property color textColor: DynamicColors.palette.m3onSurface
|
|
||||||
property color warningBarColor: DynamicColors.palette.m3error
|
|
||||||
required property int warningThreshold
|
|
||||||
|
|
||||||
Layout.preferredHeight: columnLayout.implicitHeight
|
|
||||||
Layout.preferredWidth: 158
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: columnLayout
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
Row {
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 6
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
color: root.textColor
|
|
||||||
font.family: "Material Symbols Rounded"
|
|
||||||
font.pointSize: 28
|
|
||||||
text: root.iconString
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: root.textColor
|
|
||||||
font.pointSize: 12
|
|
||||||
text: root.resourceName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 6
|
|
||||||
color: "#40000000"
|
|
||||||
radius: height / 2
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: root.percentage * 100 >= root.warningThreshold ? root.warningBarColor : root.barColor
|
|
||||||
height: parent.height
|
|
||||||
radius: height / 2
|
|
||||||
width: parent.width * Math.min(root.percentage, 1)
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
Anim {
|
|
||||||
duration: MaterialEasing.expressiveEffectsTime
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
color: root.textColor
|
|
||||||
font.pointSize: 10
|
|
||||||
text: root.details
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: popoutWindow
|
|
||||||
|
|
||||||
required property var wrapper
|
|
||||||
|
|
||||||
implicitHeight: contentColumn.implicitHeight + 10
|
|
||||||
implicitWidth: contentColumn.implicitWidth + 10 * 2
|
|
||||||
|
|
||||||
// ShadowRect {
|
|
||||||
// anchors.fill: contentRect
|
|
||||||
// radius: 8
|
|
||||||
// }
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: contentColumn
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
ResourceDetail {
|
|
||||||
details: qsTr("%1 of %2 MB used").arg(Math.round(ResourceUsage.memoryUsed * 0.001)).arg(Math.round(ResourceUsage.memoryTotal * 0.001))
|
|
||||||
iconString: "\uf7a3"
|
|
||||||
percentage: ResourceUsage.memoryUsedPercentage
|
|
||||||
resourceName: qsTr("Memory Usage")
|
|
||||||
warningThreshold: 95
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDetail {
|
|
||||||
details: qsTr("%1% used").arg(Math.round(ResourceUsage.cpuUsage * 100))
|
|
||||||
iconString: "\ue322"
|
|
||||||
percentage: ResourceUsage.cpuUsage
|
|
||||||
resourceName: qsTr("CPU Usage")
|
|
||||||
warningThreshold: 95
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDetail {
|
|
||||||
details: qsTr("%1% used").arg(Math.round(ResourceUsage.gpuUsage * 100))
|
|
||||||
iconString: "\ue30f"
|
|
||||||
percentage: ResourceUsage.gpuUsage
|
|
||||||
resourceName: qsTr("GPU Usage")
|
|
||||||
warningThreshold: 95
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDetail {
|
|
||||||
details: qsTr("%1% used").arg(Math.round(ResourceUsage.gpuMemUsage * 100))
|
|
||||||
iconString: "\ue30d"
|
|
||||||
percentage: ResourceUsage.gpuMemUsage
|
|
||||||
resourceName: qsTr("VRAM Usage")
|
|
||||||
warningThreshold: 95
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string autoGpuType: "NONE"
|
|
||||||
property double cpuUsage: 0
|
|
||||||
property double gpuMemUsage: 0
|
|
||||||
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
|
|
||||||
property double gpuUsage: 0
|
|
||||||
property double memoryFree: 1
|
|
||||||
property double memoryTotal: 1
|
|
||||||
property double memoryUsed: memoryTotal - memoryFree
|
|
||||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
|
||||||
property var previousCpuStats
|
|
||||||
property double swapFree: 1
|
|
||||||
property double swapTotal: 1
|
|
||||||
property double swapUsed: swapTotal - swapFree
|
|
||||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
|
||||||
property double totalMem: 0
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: 1
|
|
||||||
repeat: true
|
|
||||||
running: true
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
// Reload files
|
|
||||||
fileMeminfo.reload();
|
|
||||||
fileStat.reload();
|
|
||||||
|
|
||||||
// Parse memory and swap usage
|
|
||||||
const textMeminfo = fileMeminfo.text();
|
|
||||||
memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)?.[1] ?? 1);
|
|
||||||
memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)?.[1] ?? 0);
|
|
||||||
swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)?.[1] ?? 1);
|
|
||||||
swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)?.[1] ?? 0);
|
|
||||||
|
|
||||||
// Parse CPU usage
|
|
||||||
const textStat = fileStat.text();
|
|
||||||
const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
|
|
||||||
if (cpuLine) {
|
|
||||||
const stats = cpuLine.slice(1).map(Number);
|
|
||||||
const total = stats.reduce((a, b) => a + b, 0);
|
|
||||||
const idle = stats[3];
|
|
||||||
|
|
||||||
if (previousCpuStats) {
|
|
||||||
const totalDiff = total - previousCpuStats.total;
|
|
||||||
const idleDiff = idle - previousCpuStats.idle;
|
|
||||||
cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
previousCpuStats = {
|
|
||||||
total,
|
|
||||||
idle
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (root.gpuType === "NVIDIA") {
|
|
||||||
processGpu.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
interval = 3000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: fileMeminfo
|
|
||||||
|
|
||||||
path: "/proc/meminfo"
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: fileStat
|
|
||||||
|
|
||||||
path: "/proc/stat"
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gpuTypeCheck
|
|
||||||
|
|
||||||
command: ["sh", "-c", "if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then echo NVIDIA; elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC; else echo NONE; fi"]
|
|
||||||
running: !Config.services.gpuType
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: root.autoGpuType = text.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: oneshotMem
|
|
||||||
|
|
||||||
command: ["nvidia-smi", "--query-gpu=memory.total", "--format=csv,noheader,nounits"]
|
|
||||||
running: root.gpuType === "NVIDIA" && totalMem === 0
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
totalMem = Number(this.text.trim());
|
|
||||||
oneshotMem.running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: processGpu
|
|
||||||
|
|
||||||
command: ["nvidia-smi", "--query-gpu=utilization.gpu,memory.used", "--format=csv,noheader,nounits"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
const parts = this.text.trim().split(", ");
|
|
||||||
gpuUsage = Number(parts[0]) / 100;
|
|
||||||
gpuMemUsage = Number(parts[1]) / totalMem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+13
-21
@@ -8,39 +8,27 @@ import qs.Modules
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
Item {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.top: parent.top
|
|
||||||
clip: true
|
clip: true
|
||||||
implicitWidth: rowLayout.implicitWidth + Appearance.padding.small * 2
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
|
implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2
|
||||||
|
radius: height / 2
|
||||||
|
|
||||||
CustomRect {
|
StateLayer {
|
||||||
id: backgroundRect
|
onClicked: root.visibilities.resources = !root.visibilities.resources
|
||||||
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
|
||||||
radius: height / 2
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
onClicked: root.visibilities.resources = !root.visibilities.resources
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 0
|
implicitHeight: root.implicitHeight
|
||||||
|
spacing: Appearance.spacing.smaller
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: SystemUsage
|
service: SystemUsage
|
||||||
@@ -48,6 +36,7 @@ Item {
|
|||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
icon: "memory"
|
icon: "memory"
|
||||||
mainColor: DynamicColors.palette.m3primary
|
mainColor: DynamicColors.palette.m3primary
|
||||||
percentage: SystemUsage.cpuPerc
|
percentage: SystemUsage.cpuPerc
|
||||||
@@ -55,6 +44,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
Layout.fillHeight: true
|
||||||
icon: "memory_alt"
|
icon: "memory_alt"
|
||||||
mainColor: DynamicColors.palette.m3secondary
|
mainColor: DynamicColors.palette.m3secondary
|
||||||
percentage: SystemUsage.memPerc
|
percentage: SystemUsage.memPerc
|
||||||
@@ -62,12 +52,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
Layout.fillHeight: true
|
||||||
icon: "gamepad"
|
icon: "gamepad"
|
||||||
mainColor: DynamicColors.palette.m3tertiary
|
mainColor: DynamicColors.palette.m3tertiary
|
||||||
percentage: SystemUsage.gpuPerc
|
percentage: SystemUsage.gpuPerc
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
Layout.fillHeight: true
|
||||||
icon: "developer_board"
|
icon: "developer_board"
|
||||||
mainColor: DynamicColors.palette.m3primary
|
mainColor: DynamicColors.palette.m3primary
|
||||||
percentage: SystemUsage.gpuMemUsed
|
percentage: SystemUsage.gpuMemUsed
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ ShapePath {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool flatten: wrapper.height < rounding * 2
|
readonly property bool flatten: wrapper.height < rounding * 2
|
||||||
readonly property real rounding: 8
|
readonly property real rounding: Appearance.rounding.large
|
||||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||||
required property Wrapper wrapper
|
required property Wrapper wrapper
|
||||||
|
|
||||||
|
|||||||
@@ -22,83 +22,92 @@ Item {
|
|||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "settings"
|
icon: "settings"
|
||||||
|
key: "general"
|
||||||
name: "General"
|
name: "General"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "wallpaper"
|
icon: "wallpaper"
|
||||||
|
key: "wallpaper"
|
||||||
name: "Wallpaper"
|
name: "Wallpaper"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "settop_component"
|
icon: "settop_component"
|
||||||
|
key: "bar"
|
||||||
name: "Bar"
|
name: "Bar"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "lock"
|
icon: "lock"
|
||||||
|
key: "lockscreen"
|
||||||
name: "Lockscreen"
|
name: "Lockscreen"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "build_circle"
|
icon: "build_circle"
|
||||||
|
key: "services"
|
||||||
name: "Services"
|
name: "Services"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "notifications"
|
icon: "notifications"
|
||||||
|
key: "notifications"
|
||||||
name: "Notifications"
|
name: "Notifications"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "view_sidebar"
|
icon: "view_sidebar"
|
||||||
|
key: "sidebar"
|
||||||
name: "Sidebar"
|
name: "Sidebar"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "handyman"
|
icon: "handyman"
|
||||||
|
key: "utilities"
|
||||||
name: "Utilities"
|
name: "Utilities"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "dashboard"
|
icon: "dashboard"
|
||||||
|
key: "dashboard"
|
||||||
name: "Dashboard"
|
name: "Dashboard"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "colors"
|
icon: "colors"
|
||||||
|
key: "appearance"
|
||||||
name: "Appearance"
|
name: "Appearance"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "display_settings"
|
icon: "display_settings"
|
||||||
|
key: "osd"
|
||||||
name: "On screen display"
|
name: "On screen display"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
ListElement {
|
||||||
icon: "rocket_launch"
|
icon: "rocket_launch"
|
||||||
|
key: "launcher"
|
||||||
name: "Launcher"
|
name: "Launcher"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
|
||||||
icon: "colors"
|
|
||||||
name: "Colors"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRect {
|
CustomClippingRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
radius: 4
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
CustomListView {
|
CustomListView {
|
||||||
id: clayout
|
id: clayout
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.bottom: parent.bottom
|
||||||
contentHeight: contentItem.childrenRect.height
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.margins: Appearance.padding.smaller
|
||||||
|
anchors.top: parent.top
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
contentWidth: contentItem.childrenRect.width
|
contentWidth: contentItem.childrenRect.width
|
||||||
highlightFollowsCurrentItem: false
|
highlightFollowsCurrentItem: false
|
||||||
implicitHeight: contentItem.childrenRect.height
|
|
||||||
implicitWidth: contentItem.childrenRect.width
|
implicitWidth: contentItem.childrenRect.width
|
||||||
model: listModel
|
model: listModel
|
||||||
spacing: 5
|
spacing: 5
|
||||||
@@ -109,7 +118,7 @@ Item {
|
|||||||
color: DynamicColors.palette.m3primary
|
color: DynamicColors.palette.m3primary
|
||||||
implicitHeight: clayout.currentItem?.implicitHeight ?? 0
|
implicitHeight: clayout.currentItem?.implicitHeight ?? 0
|
||||||
implicitWidth: clayout.width
|
implicitWidth: clayout.width
|
||||||
radius: 4
|
radius: Appearance.rounding.normal - Appearance.padding.smaller
|
||||||
y: clayout.currentItem?.y ?? 0
|
y: clayout.currentItem?.y ?? 0
|
||||||
|
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
@@ -127,11 +136,12 @@ Item {
|
|||||||
|
|
||||||
required property string icon
|
required property string icon
|
||||||
required property int index
|
required property int index
|
||||||
|
required property string key
|
||||||
required property string name
|
required property string name
|
||||||
|
|
||||||
implicitHeight: 42
|
implicitHeight: 42
|
||||||
implicitWidth: 200
|
implicitWidth: 200
|
||||||
radius: 4
|
radius: Appearance.rounding.normal - Appearance.padding.smaller
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: layout
|
id: layout
|
||||||
@@ -148,7 +158,7 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.preferredWidth: icon.contentWidth
|
Layout.preferredWidth: icon.contentWidth
|
||||||
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||||
font.pointSize: 22
|
font.pointSize: Appearance.font.size.small * 2
|
||||||
text: categoryItem.icon
|
text: categoryItem.icon
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
@@ -170,7 +180,7 @@ Item {
|
|||||||
id: layer
|
id: layer
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.content.currentCategory = categoryItem.name.toLowerCase();
|
root.content.currentCategory = categoryItem.key;
|
||||||
clayout.currentIndex = categoryItem.index;
|
clayout.currentIndex = categoryItem.index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +1,159 @@
|
|||||||
import Quickshell
|
|
||||||
import Quickshell.Widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Modules as Modules
|
|
||||||
import qs.Modules.Settings.Controls
|
import qs.Modules.Settings.Controls
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
CustomRect {
|
SettingsPage {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
ColumnLayout {
|
SettingsSection {
|
||||||
id: clayout
|
SettingsHeader {
|
||||||
|
name: "Scale"
|
||||||
|
}
|
||||||
|
|
||||||
anchors.left: parent.left
|
SettingSpinBox {
|
||||||
anchors.right: parent.right
|
name: "Rounding scale"
|
||||||
|
object: Config.appearance.rounding
|
||||||
|
setting: "scale"
|
||||||
|
step: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
CustomRect {
|
Separator {
|
||||||
Layout.fillWidth: true
|
}
|
||||||
Layout.preferredHeight: colorLayout.implicitHeight
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
|
|
||||||
ColumnLayout {
|
SettingSpinBox {
|
||||||
id: colorLayout
|
name: "Spacing scale"
|
||||||
|
object: Config.appearance.spacing
|
||||||
|
setting: "scale"
|
||||||
|
step: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
anchors.left: parent.left
|
Separator {
|
||||||
anchors.margins: Appearance.padding.large
|
}
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
Settings {
|
SettingSpinBox {
|
||||||
name: "smth"
|
name: "Padding scale"
|
||||||
}
|
object: Config.appearance.padding
|
||||||
|
setting: "scale"
|
||||||
|
step: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
SettingSwitch {
|
Separator {
|
||||||
name: "wallust"
|
}
|
||||||
object: Config.general.color
|
|
||||||
setting: "wallust"
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomSplitButtonRow {
|
SettingSpinBox {
|
||||||
enabled: true
|
name: "Font size scale"
|
||||||
label: qsTr("Scheme mode")
|
object: Config.appearance.font.size
|
||||||
|
setting: "scale"
|
||||||
|
step: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
menuItems: [
|
Separator {
|
||||||
MenuItem {
|
}
|
||||||
property string val: "light"
|
|
||||||
|
|
||||||
icon: "light_mode"
|
SettingSpinBox {
|
||||||
text: qsTr("Light")
|
name: "Animation duration scale"
|
||||||
},
|
object: Config.appearance.anim.durations
|
||||||
MenuItem {
|
setting: "scale"
|
||||||
property string val: "dark"
|
step: 0.1
|
||||||
|
|
||||||
icon: "dark_mode"
|
|
||||||
text: qsTr("Dark")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (Config.general.color.mode === "light")
|
|
||||||
active = menuItems[0];
|
|
||||||
else
|
|
||||||
active = menuItems[1];
|
|
||||||
}
|
|
||||||
onSelected: item => {
|
|
||||||
Config.general.color.mode = item.val;
|
|
||||||
Config.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component Settings: CustomRect {
|
SettingsSection {
|
||||||
id: settingsItem
|
SettingsHeader {
|
||||||
|
name: "Fonts"
|
||||||
|
}
|
||||||
|
|
||||||
required property string name
|
SettingInput {
|
||||||
|
name: "Sans family"
|
||||||
|
object: Config.appearance.font.family
|
||||||
|
setting: "sans"
|
||||||
|
}
|
||||||
|
|
||||||
Layout.preferredHeight: 42
|
Separator {
|
||||||
Layout.preferredWidth: 200
|
}
|
||||||
radius: 4
|
|
||||||
|
|
||||||
CustomText {
|
SettingInput {
|
||||||
id: text
|
name: "Monospace family"
|
||||||
|
object: Config.appearance.font.family
|
||||||
|
setting: "mono"
|
||||||
|
}
|
||||||
|
|
||||||
anchors.left: parent.left
|
Separator {
|
||||||
anchors.margins: Appearance.padding.smaller
|
}
|
||||||
anchors.right: parent.right
|
|
||||||
font.bold: true
|
SettingInput {
|
||||||
font.pointSize: 32
|
name: "Material family"
|
||||||
text: settingsItem.name
|
object: Config.appearance.font.family
|
||||||
verticalAlignment: Text.AlignVCenter
|
setting: "material"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Clock family"
|
||||||
|
object: Config.appearance.font.family
|
||||||
|
setting: "clock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Animation"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Media GIF speed adjustment"
|
||||||
|
object: Config.appearance.anim
|
||||||
|
setting: "mediaGifSpeedAdjustment"
|
||||||
|
step: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Session GIF speed"
|
||||||
|
max: 5
|
||||||
|
min: 0
|
||||||
|
object: Config.appearance.anim
|
||||||
|
setting: "sessionGifSpeed"
|
||||||
|
step: 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Transparency"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable transparency"
|
||||||
|
object: Config.appearance.transparency
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Base opacity"
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
object: Config.appearance.transparency
|
||||||
|
setting: "base"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Layer opacity"
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
object: Config.appearance.transparency
|
||||||
|
setting: "layers"
|
||||||
|
step: 0.05
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,29 @@
|
|||||||
import Quickshell
|
import qs.Modules.Settings.Controls
|
||||||
import Quickshell.Widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Modules as Modules
|
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
CustomRect {
|
SettingsPage {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Wallpaper"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable wallpaper rendering"
|
||||||
|
object: Config.background
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Fade duration"
|
||||||
|
min: 0
|
||||||
|
object: Config.background
|
||||||
|
setting: "wallFadeDuration"
|
||||||
|
step: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Auto hide"
|
||||||
|
object: Config.barConfig
|
||||||
|
setting: "autoHide"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Height"
|
||||||
|
min: 1
|
||||||
|
object: Config.barConfig
|
||||||
|
setting: "height"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Rounding"
|
||||||
|
min: 0
|
||||||
|
object: Config.barConfig
|
||||||
|
setting: "rounding"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Border"
|
||||||
|
min: 0
|
||||||
|
object: Config.barConfig
|
||||||
|
setting: "border"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Popouts"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Tray"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "tray"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Audio"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "audio"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Active window"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "activeWindow"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Resources"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "resources"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Clock"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "clock"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Network"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "network"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Power"
|
||||||
|
object: Config.barConfig.popouts
|
||||||
|
setting: "upower"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Entries"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingBarEntryList {
|
||||||
|
name: "Bar entries"
|
||||||
|
object: Config.barConfig
|
||||||
|
setting: "entries"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Dock"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable dock"
|
||||||
|
object: Config.dock
|
||||||
|
setting: "enable"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Dock height"
|
||||||
|
min: 1
|
||||||
|
object: Config.dock
|
||||||
|
setting: "height"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Hover to reveal"
|
||||||
|
object: Config.dock
|
||||||
|
setting: "hoverToReveal"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Pin on startup"
|
||||||
|
object: Config.dock
|
||||||
|
setting: "pinnedOnStartup"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
name: "Pinned apps"
|
||||||
|
addLabel: qsTr("Add pinned app")
|
||||||
|
object: Config.dock
|
||||||
|
setting: "pinnedApps"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
name: "Ignored app regexes"
|
||||||
|
addLabel: qsTr("Add ignored regex")
|
||||||
|
object: Config.dock
|
||||||
|
setting: "ignoredAppRegexes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Dashboard"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable dashboard"
|
||||||
|
object: Config.dashboard
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Media update interval"
|
||||||
|
min: 0
|
||||||
|
object: Config.dashboard
|
||||||
|
setting: "mediaUpdateInterval"
|
||||||
|
step: 50
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Resource update interval"
|
||||||
|
min: 0
|
||||||
|
object: Config.dashboard
|
||||||
|
setting: "resourceUpdateInterval"
|
||||||
|
step: 50
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Drag threshold"
|
||||||
|
min: 0
|
||||||
|
object: Config.dashboard
|
||||||
|
setting: "dragThreshold"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Performance"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show battery"
|
||||||
|
object: Config.dashboard.performance
|
||||||
|
setting: "showBattery"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show GPU"
|
||||||
|
object: Config.dashboard.performance
|
||||||
|
setting: "showGpu"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show CPU"
|
||||||
|
object: Config.dashboard.performance
|
||||||
|
setting: "showCpu"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show memory"
|
||||||
|
object: Config.dashboard.performance
|
||||||
|
setting: "showMemory"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show storage"
|
||||||
|
object: Config.dashboard.performance
|
||||||
|
setting: "showStorage"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show network"
|
||||||
|
object: Config.dashboard.performance
|
||||||
|
setting: "showNetwork"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Layout Sizes"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Tab indicator height"
|
||||||
|
value: String(Config.dashboard.sizes.tabIndicatorHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Tab indicator spacing"
|
||||||
|
value: String(Config.dashboard.sizes.tabIndicatorSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Info width"
|
||||||
|
value: String(Config.dashboard.sizes.infoWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Info icon size"
|
||||||
|
value: String(Config.dashboard.sizes.infoIconSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Date time width"
|
||||||
|
value: String(Config.dashboard.sizes.dateTimeWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Media width"
|
||||||
|
value: String(Config.dashboard.sizes.mediaWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Media progress sweep"
|
||||||
|
value: String(Config.dashboard.sizes.mediaProgressSweep)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Media progress thickness"
|
||||||
|
value: String(Config.dashboard.sizes.mediaProgressThickness)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Resource progress thickness"
|
||||||
|
value: String(Config.dashboard.sizes.resourceProgessThickness)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Weather width"
|
||||||
|
value: String(Config.dashboard.sizes.weatherWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Media cover art size"
|
||||||
|
value: String(Config.dashboard.sizes.mediaCoverArtSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Media visualiser size"
|
||||||
|
value: String(Config.dashboard.sizes.mediaVisualiserSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingReadOnly {
|
||||||
|
name: "Resource size"
|
||||||
|
value: String(Config.dashboard.sizes.resourceSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +1,212 @@
|
|||||||
import Quickshell
|
import qs.Modules.Settings.Controls
|
||||||
import Quickshell.Widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Modules as Modules
|
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
import qs.Components
|
||||||
|
|
||||||
CustomRect {
|
SettingsPage {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
ColumnLayout {
|
function schemeTypeItem(items, value) {
|
||||||
id: clayout
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
anchors.fill: parent
|
if (item.value === value)
|
||||||
|
return item;
|
||||||
Settings {
|
|
||||||
name: "apps"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
return items[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "General"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Logo"
|
||||||
|
object: Config.general
|
||||||
|
setting: "logo"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Wallpaper path"
|
||||||
|
object: Config.general
|
||||||
|
setting: "wallpaperPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Desktop icons"
|
||||||
|
object: Config.general
|
||||||
|
setting: "desktopIcons"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component Settings: CustomRect {
|
SettingsSection {
|
||||||
id: settingsItem
|
SettingsHeader {
|
||||||
|
name: "Color"
|
||||||
|
}
|
||||||
|
|
||||||
required property string name
|
CustomSplitButtonRow {
|
||||||
|
active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1]
|
||||||
|
label: qsTr("Scheme mode")
|
||||||
|
|
||||||
implicitHeight: 42
|
menuItems: [
|
||||||
implicitWidth: 200
|
MenuItem {
|
||||||
radius: 4
|
icon: "light_mode"
|
||||||
|
text: qsTr("Light")
|
||||||
|
value: "light"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "dark_mode"
|
||||||
|
text: qsTr("Dark")
|
||||||
|
value: "dark"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
RowLayout {
|
onSelected: item => {
|
||||||
id: layout
|
Config.general.color.mode = item.value;
|
||||||
|
Config.save();
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: Appearance.padding.smaller
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: text
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: Appearance.spacing.normal
|
|
||||||
text: settingsItem.name
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSplitButtonRow {
|
||||||
|
id: schemeType
|
||||||
|
|
||||||
|
active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
|
||||||
|
label: qsTr("Scheme type")
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
menuItems: [
|
||||||
|
MenuItem {
|
||||||
|
icon: "palette"
|
||||||
|
text: qsTr("Vibrant")
|
||||||
|
value: "vibrant"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "gesture"
|
||||||
|
text: qsTr("Expressive")
|
||||||
|
value: "expressive"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "contrast"
|
||||||
|
text: qsTr("Monochrome")
|
||||||
|
value: "monochrome"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "tonality"
|
||||||
|
text: qsTr("Neutral")
|
||||||
|
value: "neutral"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "gradient"
|
||||||
|
text: qsTr("Tonal spot")
|
||||||
|
value: "tonalSpot"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "target"
|
||||||
|
text: qsTr("Fidelity")
|
||||||
|
value: "fidelity"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "article"
|
||||||
|
text: qsTr("Content")
|
||||||
|
value: "content"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "colors"
|
||||||
|
text: qsTr("Rainbow")
|
||||||
|
value: "rainbow"
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
icon: "nutrition"
|
||||||
|
text: qsTr("Fruit salad")
|
||||||
|
value: "fruitSalad"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onSelected: item => {
|
||||||
|
Config.colors.schemeType = item.value;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Automatic color scheme"
|
||||||
|
object: Config.general.color
|
||||||
|
setting: "schemeGeneration"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Smart color scheme"
|
||||||
|
object: Config.general.color
|
||||||
|
setting: "smart"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinner {
|
||||||
|
name: "Schedule dark mode"
|
||||||
|
object: Config.general.color
|
||||||
|
settings: ["scheduleDarkStart", "scheduleDarkEnd"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
z: -1
|
||||||
|
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Default Apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
addLabel: qsTr("Add terminal command")
|
||||||
|
name: "Terminal"
|
||||||
|
object: Config.general.apps
|
||||||
|
setting: "terminal"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
addLabel: qsTr("Add audio command")
|
||||||
|
name: "Audio"
|
||||||
|
object: Config.general.apps
|
||||||
|
setting: "audio"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
addLabel: qsTr("Add playback command")
|
||||||
|
name: "Playback"
|
||||||
|
object: Config.general.apps
|
||||||
|
setting: "playback"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
addLabel: qsTr("Add explorer command")
|
||||||
|
name: "Explorer"
|
||||||
|
object: Config.general.apps
|
||||||
|
setting: "explorer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Launcher"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Max apps shown"
|
||||||
|
min: 1
|
||||||
|
object: Config.launcher
|
||||||
|
setting: "maxAppsShown"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Max wallpapers shown"
|
||||||
|
min: 1
|
||||||
|
object: Config.launcher
|
||||||
|
setting: "maxWallpapers"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Action prefix"
|
||||||
|
object: Config.launcher
|
||||||
|
setting: "actionPrefix"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Special prefix"
|
||||||
|
object: Config.launcher
|
||||||
|
setting: "specialPrefix"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Fuzzy Search"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Apps"
|
||||||
|
object: Config.launcher.useFuzzy
|
||||||
|
setting: "apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Actions"
|
||||||
|
object: Config.launcher.useFuzzy
|
||||||
|
setting: "actions"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Schemes"
|
||||||
|
object: Config.launcher.useFuzzy
|
||||||
|
setting: "schemes"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Variants"
|
||||||
|
object: Config.launcher.useFuzzy
|
||||||
|
setting: "variants"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Wallpapers"
|
||||||
|
object: Config.launcher.useFuzzy
|
||||||
|
setting: "wallpapers"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Sizes"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Item width"
|
||||||
|
min: 1
|
||||||
|
object: Config.launcher.sizes
|
||||||
|
setting: "itemWidth"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Item height"
|
||||||
|
min: 1
|
||||||
|
object: Config.launcher.sizes
|
||||||
|
setting: "itemHeight"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Wallpaper width"
|
||||||
|
min: 1
|
||||||
|
object: Config.launcher.sizes
|
||||||
|
setting: "wallpaperWidth"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Wallpaper height"
|
||||||
|
min: 1
|
||||||
|
object: Config.launcher.sizes
|
||||||
|
setting: "wallpaperHeight"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Actions"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingActionList {
|
||||||
|
name: "Launcher actions"
|
||||||
|
object: Config.launcher
|
||||||
|
setting: "actions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import qs.Modules.Settings.Categories.Lockscreen
|
||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Lockscreen"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Recolor logo"
|
||||||
|
object: Config.lock
|
||||||
|
setting: "recolorLogo"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable fingerprint"
|
||||||
|
object: Config.lock
|
||||||
|
setting: "enableFprint"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Max fingerprint tries"
|
||||||
|
min: 1
|
||||||
|
object: Config.lock
|
||||||
|
setting: "maxFprintTries"
|
||||||
|
step: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Blur amount"
|
||||||
|
min: 0
|
||||||
|
object: Config.lock
|
||||||
|
setting: "blurAmount"
|
||||||
|
step: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Height multiplier"
|
||||||
|
max: 2
|
||||||
|
min: 0.1
|
||||||
|
object: Config.lock.sizes
|
||||||
|
setting: "heightMult"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Aspect ratio"
|
||||||
|
max: 4
|
||||||
|
min: 0.5
|
||||||
|
object: Config.lock.sizes
|
||||||
|
setting: "ratio"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Center width"
|
||||||
|
min: 100
|
||||||
|
object: Config.lock.sizes
|
||||||
|
setting: "centerWidth"
|
||||||
|
step: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
Idle {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function addTimeoutEntry() {
|
||||||
|
let list = [...Config.general.idle.timeouts];
|
||||||
|
|
||||||
|
list.push({
|
||||||
|
name: "New Entry",
|
||||||
|
timeout: 600,
|
||||||
|
idleAction: "lock"
|
||||||
|
});
|
||||||
|
|
||||||
|
Config.general.idle.timeouts = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTimeoutEntry(i, key, value) {
|
||||||
|
const list = [...Config.general.idle.timeouts];
|
||||||
|
let entry = list[i];
|
||||||
|
|
||||||
|
entry[key] = value;
|
||||||
|
list[i] = entry;
|
||||||
|
|
||||||
|
Config.general.idle.timeouts = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Appearance.spacing.smaller
|
||||||
|
|
||||||
|
Settings {
|
||||||
|
name: "Idle Monitors"
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [...Config.general.idle.timeouts]
|
||||||
|
|
||||||
|
SettingList {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
onAddActiveActionRequested: {
|
||||||
|
root.updateTimeoutEntry(index, "activeAction", "");
|
||||||
|
}
|
||||||
|
onFieldEdited: function (key, value) {
|
||||||
|
root.updateTimeoutEntry(index, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "add"
|
||||||
|
|
||||||
|
onClicked: root.addTimeoutEntry()
|
||||||
|
}
|
||||||
|
|
||||||
|
component Settings: CustomRect {
|
||||||
|
id: settingsItem
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
|
||||||
|
Layout.preferredHeight: 60
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.bold: true
|
||||||
|
font.pointSize: Appearance.font.size.large * 2
|
||||||
|
text: settingsItem.name
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Notifications"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Expire notifications"
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "expire"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Default expire timeout"
|
||||||
|
min: 0
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "defaultExpireTimeout"
|
||||||
|
step: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "App notification cooldown"
|
||||||
|
min: 0
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "appNotifCooldown"
|
||||||
|
step: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Clear threshold"
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "clearThreshold"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Expand threshold"
|
||||||
|
min: 0
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "expandThreshold"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Action on click"
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "actionOnClick"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Group preview count"
|
||||||
|
min: 1
|
||||||
|
object: Config.notifs
|
||||||
|
setting: "groupPreviewNum"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Sizes"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Width"
|
||||||
|
min: 1
|
||||||
|
object: Config.notifs.sizes
|
||||||
|
setting: "width"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Image size"
|
||||||
|
min: 1
|
||||||
|
object: Config.notifs.sizes
|
||||||
|
setting: "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Badge size"
|
||||||
|
min: 1
|
||||||
|
object: Config.notifs.sizes
|
||||||
|
setting: "badge"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "On Screen Display"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable OSD"
|
||||||
|
object: Config.osd
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Hide delay"
|
||||||
|
min: 0
|
||||||
|
object: Config.osd
|
||||||
|
setting: "hideDelay"
|
||||||
|
step: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable brightness OSD"
|
||||||
|
object: Config.osd
|
||||||
|
setting: "enableBrightness"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable microphone OSD"
|
||||||
|
object: Config.osd
|
||||||
|
setting: "enableMicrophone"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Brightness on all monitors"
|
||||||
|
object: Config.osd
|
||||||
|
setting: "allMonBrightness"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Sizes"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Slider width"
|
||||||
|
min: 1
|
||||||
|
object: Config.osd.sizes
|
||||||
|
setting: "sliderWidth"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Slider height"
|
||||||
|
min: 1
|
||||||
|
object: Config.osd.sizes
|
||||||
|
setting: "sliderHeight"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Services"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Weather location"
|
||||||
|
object: Config.services
|
||||||
|
setting: "weatherLocation"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Use Fahrenheit"
|
||||||
|
object: Config.services
|
||||||
|
setting: "useFahrenheit"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Use twelve hour clock"
|
||||||
|
object: Config.services
|
||||||
|
setting: "useTwelveHourClock"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable ddcutil service"
|
||||||
|
object: Config.services
|
||||||
|
setting: "ddcutilService"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "GPU type"
|
||||||
|
object: Config.services
|
||||||
|
setting: "gpuType"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Media"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Audio increment"
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
object: Config.services
|
||||||
|
setting: "audioIncrement"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Brightness increment"
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
object: Config.services
|
||||||
|
setting: "brightnessIncrement"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Max volume"
|
||||||
|
max: 5
|
||||||
|
min: 0
|
||||||
|
object: Config.services
|
||||||
|
setting: "maxVolume"
|
||||||
|
step: 0.05
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingInput {
|
||||||
|
name: "Default player"
|
||||||
|
object: Config.services
|
||||||
|
setting: "defaultPlayer"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Visualizer bars"
|
||||||
|
min: 1
|
||||||
|
object: Config.services
|
||||||
|
setting: "visualizerBars"
|
||||||
|
step: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingAliasList {
|
||||||
|
name: "Player aliases"
|
||||||
|
object: Config.services
|
||||||
|
setting: "playerAliases"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Sidebar"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable sidebar"
|
||||||
|
object: Config.sidebar
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Width"
|
||||||
|
min: 1
|
||||||
|
object: Config.sidebar.sizes
|
||||||
|
setting: "width"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import qs.Modules.Settings.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Utilities"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable utilities"
|
||||||
|
object: Config.utilities
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Max toasts"
|
||||||
|
min: 1
|
||||||
|
object: Config.utilities
|
||||||
|
setting: "maxToasts"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Panel width"
|
||||||
|
min: 1
|
||||||
|
object: Config.utilities.sizes
|
||||||
|
setting: "width"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
name: "Toast width"
|
||||||
|
min: 1
|
||||||
|
object: Config.utilities.sizes
|
||||||
|
setting: "toastWidth"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "Toasts"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Config loaded"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "configLoaded"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Charging changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "chargingChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Game mode changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "gameModeChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Do not disturb changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "dndChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Audio output changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "audioOutputChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Audio input changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "audioInputChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Caps lock changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "capsLockChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Num lock changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "numLockChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Keyboard layout changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "kbLayoutChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "VPN changed"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "vpnChanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Now playing"
|
||||||
|
object: Config.utilities.toasts
|
||||||
|
setting: "nowPlaying"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection {
|
||||||
|
SettingsHeader {
|
||||||
|
name: "VPN"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Enable VPN integration"
|
||||||
|
object: Config.utilities.vpn
|
||||||
|
setting: "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingStringList {
|
||||||
|
name: "Provider"
|
||||||
|
addLabel: qsTr("Add VPN provider")
|
||||||
|
object: Config.utilities.vpn
|
||||||
|
setting: "provider"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,9 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string currentCategory: "general"
|
property string currentCategory: "general"
|
||||||
readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2
|
readonly property real nonAnimHeight: Math.floor(screen.height / 1.5) + viewWrapper.anchors.margins * 2
|
||||||
readonly property real nonAnimWidth: view.implicitWidth + 500 + viewWrapper.anchors.margins * 2
|
readonly property real nonAnimWidth: view.implicitWidth + Math.floor(screen.width / 2) + viewWrapper.anchors.margins * 2
|
||||||
|
required property ShellScreen screen
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
implicitHeight: nonAnimHeight
|
implicitHeight: nonAnimHeight
|
||||||
@@ -22,24 +23,41 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
function onCurrentCategoryChanged() {
|
function onCurrentCategoryChanged() {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
if (currentCategory === "general") {
|
if (currentCategory === "general")
|
||||||
stack.push(general);
|
stack.push(general);
|
||||||
} else if (currentCategory === "wallpaper") {
|
else if (currentCategory === "wallpaper")
|
||||||
stack.push(background);
|
stack.push(background);
|
||||||
} else if (currentCategory === "appearance") {
|
else if (currentCategory === "bar")
|
||||||
|
stack.push(bar);
|
||||||
|
else if (currentCategory === "appearance")
|
||||||
stack.push(appearance);
|
stack.push(appearance);
|
||||||
}
|
else if (currentCategory === "lockscreen")
|
||||||
|
stack.push(lockscreen);
|
||||||
|
else if (currentCategory === "services")
|
||||||
|
stack.push(services);
|
||||||
|
else if (currentCategory === "notifications")
|
||||||
|
stack.push(notifications);
|
||||||
|
else if (currentCategory === "sidebar")
|
||||||
|
stack.push(sidebar);
|
||||||
|
else if (currentCategory === "utilities")
|
||||||
|
stack.push(utilities);
|
||||||
|
else if (currentCategory === "dashboard")
|
||||||
|
stack.push(dashboard);
|
||||||
|
else if (currentCategory === "osd")
|
||||||
|
stack.push(osd);
|
||||||
|
else if (currentCategory === "launcher")
|
||||||
|
stack.push(launcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: root
|
target: root
|
||||||
}
|
}
|
||||||
|
|
||||||
ClippingRectangle {
|
CustomClippingRect {
|
||||||
id: viewWrapper
|
id: viewWrapper
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Appearance.padding.smaller
|
anchors.margins: Appearance.padding.smaller
|
||||||
color: "transparent"
|
radius: Appearance.rounding.large - Appearance.padding.smaller
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: view
|
id: view
|
||||||
@@ -47,7 +65,6 @@ Item {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
implicitHeight: layout.implicitHeight
|
|
||||||
implicitWidth: layout.implicitWidth
|
implicitWidth: layout.implicitWidth
|
||||||
|
|
||||||
Categories {
|
Categories {
|
||||||
@@ -67,7 +84,7 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
radius: 4
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: stack
|
id: stack
|
||||||
@@ -99,4 +116,67 @@ Item {
|
|||||||
Cat.Appearance {
|
Cat.Appearance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: bar
|
||||||
|
|
||||||
|
Cat.Bar {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: lockscreen
|
||||||
|
|
||||||
|
Cat.Lockscreen {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: services
|
||||||
|
|
||||||
|
Cat.Services {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: notifications
|
||||||
|
|
||||||
|
Cat.Notifications {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: sidebar
|
||||||
|
|
||||||
|
Cat.Sidebar {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: utilities
|
||||||
|
|
||||||
|
Cat.Utilities {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: dashboard
|
||||||
|
|
||||||
|
Cat.Dashboard {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: osd
|
||||||
|
|
||||||
|
Cat.Osd {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: launcher
|
||||||
|
|
||||||
|
Cat.Launcher {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: DynamicColors.tPalette.m3outlineVariant
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property string setting
|
||||||
|
|
||||||
|
function addAction() {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
list.push({
|
||||||
|
name: "New Action",
|
||||||
|
icon: "bolt",
|
||||||
|
description: "",
|
||||||
|
command: [],
|
||||||
|
enabled: true,
|
||||||
|
dangerous: false
|
||||||
|
});
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAction(index) {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
list.splice(index, 1);
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAction(index, key, value) {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
const entry = list[index];
|
||||||
|
entry[key] = value;
|
||||||
|
list[index] = entry;
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Appearance.spacing.smaller
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [...root.object[root.setting]]
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.normal * 2
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.normal
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: modelData.name ?? qsTr("Action")
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "delete"
|
||||||
|
type: IconButton.Tonal
|
||||||
|
|
||||||
|
onClicked: root.removeAction(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Name")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: 350
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
text: modelData.name ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: root.updateAction(index, "name", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Icon")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: 350
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
text: modelData.icon ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: root.updateAction(index, "icon", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Description")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: 350
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
text: modelData.description ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: root.updateAction(index, "description", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringListEditor {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
addLabel: qsTr("Add command argument")
|
||||||
|
values: [...(modelData.command ?? [])]
|
||||||
|
|
||||||
|
onListEdited: function (values) {
|
||||||
|
root.updateAction(index, "command", values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
checked: modelData.enabled ?? true
|
||||||
|
|
||||||
|
onToggled: root.updateAction(index, "enabled", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Dangerous")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
checked: modelData.dangerous ?? false
|
||||||
|
|
||||||
|
onToggled: root.updateAction(index, "dangerous", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "add"
|
||||||
|
|
||||||
|
onClicked: root.addAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Add action")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property string setting
|
||||||
|
|
||||||
|
function addAlias() {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
list.push({
|
||||||
|
from: "",
|
||||||
|
to: ""
|
||||||
|
});
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAlias(index) {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
list.splice(index, 1);
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAlias(index, key, value) {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
const entry = [...list[index]];
|
||||||
|
entry[key] = value;
|
||||||
|
list[index] = entry;
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Appearance.spacing.smaller
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [...root.object[root.setting]]
|
||||||
|
|
||||||
|
Item {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: -(Appearance.spacing.smaller / 2)
|
||||||
|
color: DynamicColors.tPalette.m3outlineVariant
|
||||||
|
implicitHeight: 1
|
||||||
|
visible: index !== 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("From")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
text: modelData.from ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: root.updateAlias(index, "from", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "delete"
|
||||||
|
type: IconButton.Tonal
|
||||||
|
|
||||||
|
onClicked: root.removeAlias(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("To")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
text: modelData.to ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: root.updateAlias(index, "to", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "add"
|
||||||
|
|
||||||
|
onClicked: root.addAlias()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Add alias")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,523 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQml.Models
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool dragActive: false
|
||||||
|
property real dragHeight: 0
|
||||||
|
property real dragStartX: 0
|
||||||
|
property real dragStartY: 0
|
||||||
|
property real dragX: 0
|
||||||
|
property real dragY: 0
|
||||||
|
property var draggedModelData: null
|
||||||
|
property string draggedUid: ""
|
||||||
|
property bool dropAnimating: false
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
property var pendingCommitEntries: []
|
||||||
|
required property string setting
|
||||||
|
property int uidCounter: 0
|
||||||
|
property var visualEntries: []
|
||||||
|
|
||||||
|
function beginVisualDrag(uid, modelData, item) {
|
||||||
|
const pos = item.mapToItem(root, 0, 0);
|
||||||
|
|
||||||
|
root.draggedUid = uid;
|
||||||
|
root.draggedModelData = modelData;
|
||||||
|
root.dragHeight = item.height;
|
||||||
|
root.dragStartX = pos.x;
|
||||||
|
root.dragStartY = pos.y;
|
||||||
|
root.dragX = pos.x;
|
||||||
|
root.dragY = pos.y;
|
||||||
|
root.dragActive = true;
|
||||||
|
root.dropAnimating = false;
|
||||||
|
root.pendingCommitEntries = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitVisualOrder(entries) {
|
||||||
|
const list = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < entries.length; i++)
|
||||||
|
list.push(entries[i].entry);
|
||||||
|
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
root.rebuildVisualEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
function endVisualDrag() {
|
||||||
|
const entries = root.visualEntries.slice();
|
||||||
|
const finalIndex = root.indexForUid(root.draggedUid);
|
||||||
|
const finalItem = listView.itemAtIndex(finalIndex);
|
||||||
|
|
||||||
|
root.dragActive = false;
|
||||||
|
|
||||||
|
if (!finalItem) {
|
||||||
|
root.pendingCommitEntries = entries;
|
||||||
|
root.finishVisualDrag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pos = finalItem.mapToItem(root, 0, 0);
|
||||||
|
|
||||||
|
root.pendingCommitEntries = entries;
|
||||||
|
root.dropAnimating = true;
|
||||||
|
settleX.to = pos.x;
|
||||||
|
settleY.to = pos.y;
|
||||||
|
settleAnim.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureVisualEntries() {
|
||||||
|
if (!root.dragActive && !root.dropAnimating)
|
||||||
|
root.rebuildVisualEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishVisualDrag() {
|
||||||
|
const entries = root.pendingCommitEntries.slice();
|
||||||
|
|
||||||
|
root.dragActive = false;
|
||||||
|
root.dropAnimating = false;
|
||||||
|
root.draggedUid = "";
|
||||||
|
root.draggedModelData = null;
|
||||||
|
root.pendingCommitEntries = [];
|
||||||
|
root.dragHeight = 0;
|
||||||
|
|
||||||
|
root.commitVisualOrder(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconForId(id) {
|
||||||
|
switch (id) {
|
||||||
|
case "workspaces":
|
||||||
|
return "dashboard";
|
||||||
|
case "audio":
|
||||||
|
return "volume_up";
|
||||||
|
case "media":
|
||||||
|
return "play_arrow";
|
||||||
|
case "resources":
|
||||||
|
return "monitoring";
|
||||||
|
case "updates":
|
||||||
|
return "system_update";
|
||||||
|
case "dash":
|
||||||
|
return "space_dashboard";
|
||||||
|
case "spacer":
|
||||||
|
return "horizontal_rule";
|
||||||
|
case "activeWindow":
|
||||||
|
return "web_asset";
|
||||||
|
case "tray":
|
||||||
|
return "widgets";
|
||||||
|
case "upower":
|
||||||
|
return "battery_full";
|
||||||
|
case "network":
|
||||||
|
return "wifi";
|
||||||
|
case "clock":
|
||||||
|
return "schedule";
|
||||||
|
case "notifBell":
|
||||||
|
return "notifications";
|
||||||
|
default:
|
||||||
|
return "drag_indicator";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexForUid(uid) {
|
||||||
|
for (let i = 0; i < root.visualEntries.length; i++) {
|
||||||
|
if (root.visualEntries[i].uid === uid)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function labelForId(id) {
|
||||||
|
switch (id) {
|
||||||
|
case "workspaces":
|
||||||
|
return qsTr("Workspaces");
|
||||||
|
case "audio":
|
||||||
|
return qsTr("Audio");
|
||||||
|
case "media":
|
||||||
|
return qsTr("Media");
|
||||||
|
case "resources":
|
||||||
|
return qsTr("Resources");
|
||||||
|
case "updates":
|
||||||
|
return qsTr("Updates");
|
||||||
|
case "dash":
|
||||||
|
return qsTr("Dash");
|
||||||
|
case "spacer":
|
||||||
|
return qsTr("Spacer");
|
||||||
|
case "activeWindow":
|
||||||
|
return qsTr("Active window");
|
||||||
|
case "tray":
|
||||||
|
return qsTr("Tray");
|
||||||
|
case "upower":
|
||||||
|
return qsTr("Power");
|
||||||
|
case "network":
|
||||||
|
return qsTr("Network");
|
||||||
|
case "clock":
|
||||||
|
return qsTr("Clock");
|
||||||
|
case "notifBell":
|
||||||
|
return qsTr("Notification bell");
|
||||||
|
default:
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveArrayItem(list, from, to) {
|
||||||
|
const next = list.slice();
|
||||||
|
const [item] = next.splice(from, 1);
|
||||||
|
next.splice(to, 0, item);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewVisualMove(from, hovered, before) {
|
||||||
|
let to = hovered + (before ? 0 : 1);
|
||||||
|
|
||||||
|
if (to > from)
|
||||||
|
to -= 1;
|
||||||
|
|
||||||
|
to = Math.max(0, Math.min(visualModel.items.count - 1, to));
|
||||||
|
|
||||||
|
if (from === to)
|
||||||
|
return;
|
||||||
|
|
||||||
|
visualModel.items.move(from, to);
|
||||||
|
root.visualEntries = root.moveArrayItem(root.visualEntries, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildVisualEntries() {
|
||||||
|
const entries = root.object[root.setting] ?? [];
|
||||||
|
const next = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < entries.length; i++) {
|
||||||
|
const entry = entries[i];
|
||||||
|
let existing = null;
|
||||||
|
|
||||||
|
for (let j = 0; j < root.visualEntries.length; j++) {
|
||||||
|
if (root.visualEntries[j].entry === entry) {
|
||||||
|
existing = root.visualEntries[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing)
|
||||||
|
next.push(existing);
|
||||||
|
else
|
||||||
|
next.push({
|
||||||
|
uid: `entry-${root.uidCounter++}`,
|
||||||
|
entry
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
root.visualEntries = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEntry(index, value) {
|
||||||
|
const list = [...root.object[root.setting]];
|
||||||
|
const entry = list[index];
|
||||||
|
entry.enabled = value;
|
||||||
|
list[index] = entry;
|
||||||
|
root.object[root.setting] = list;
|
||||||
|
Config.save();
|
||||||
|
root.ensureVisualEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: layout.implicitHeight
|
||||||
|
|
||||||
|
Component.onCompleted: root.rebuildVisualEntries()
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: settleAnim
|
||||||
|
|
||||||
|
onFinished: root.finishVisualDrag()
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
id: settleX
|
||||||
|
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "dragX"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
id: settleY
|
||||||
|
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "dragY"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: Appearance.spacing.smaller
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateModel {
|
||||||
|
id: visualModel
|
||||||
|
|
||||||
|
delegate: entryDelegate
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
objectProp: "uid"
|
||||||
|
values: root.visualEntries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: contentHeight
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: false
|
||||||
|
implicitHeight: contentHeight
|
||||||
|
implicitWidth: width
|
||||||
|
interactive: !(root.dragActive || root.dropAnimating)
|
||||||
|
model: visualModel
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
Anim {
|
||||||
|
properties: "opacity,scale"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addDisplaced: Transition {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
displaced: Transition {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
move: Transition {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeDisplaced: Transition {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.dragActive || root.dropAnimating
|
||||||
|
asynchronous: false
|
||||||
|
|
||||||
|
sourceComponent: Item {
|
||||||
|
Drag.active: root.dragActive
|
||||||
|
Drag.hotSpot.x: width / 2
|
||||||
|
Drag.hotSpot.y: height / 2
|
||||||
|
height: proxyRect.implicitHeight
|
||||||
|
implicitHeight: proxyRect.implicitHeight
|
||||||
|
implicitWidth: listView.width
|
||||||
|
visible: root.draggedModelData !== null
|
||||||
|
width: listView.width
|
||||||
|
x: root.dragX
|
||||||
|
y: root.dragY
|
||||||
|
z: 100
|
||||||
|
|
||||||
|
Drag.source: QtObject {
|
||||||
|
property string uid: root.draggedUid
|
||||||
|
property int visualIndex: root.indexForUid(root.draggedUid)
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: proxyRect
|
||||||
|
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
implicitHeight: proxyRow.implicitHeight + Appearance.padding.small * 2
|
||||||
|
implicitWidth: parent.width
|
||||||
|
opacity: 0.95
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: proxyRow
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.12)
|
||||||
|
implicitHeight: 32
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: "drag_indicator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: root.iconForId(root.draggedModelData?.entry?.id ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.labelForId(root.draggedModelData?.entry?.id ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
checked: root.draggedModelData?.entry?.enabled ?? true
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: entryDelegate
|
||||||
|
|
||||||
|
DropArea {
|
||||||
|
id: slot
|
||||||
|
|
||||||
|
readonly property var entryData: modelData.entry
|
||||||
|
required property var modelData
|
||||||
|
readonly property string uid: modelData.uid
|
||||||
|
|
||||||
|
function previewReorder(drag) {
|
||||||
|
const source = drag.source;
|
||||||
|
if (!source || !source.uid || source.uid === slot.uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const from = source.visualIndex;
|
||||||
|
const hovered = slot.DelegateModel.itemsIndex;
|
||||||
|
|
||||||
|
if (from < 0 || hovered < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.previewVisualMove(from, hovered, drag.y < height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
height: entryRow.implicitHeight
|
||||||
|
implicitHeight: entryRow.implicitHeight
|
||||||
|
implicitWidth: listView.width
|
||||||
|
width: ListView.view ? ListView.view.width : listView.width
|
||||||
|
|
||||||
|
onEntered: drag => previewReorder(drag)
|
||||||
|
onPositionChanged: drag => previewReorder(drag)
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: entryRow
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
implicitHeight: entryLayout.implicitHeight + Appearance.padding.small * 2
|
||||||
|
implicitWidth: parent.width
|
||||||
|
opacity: root.draggedUid === slot.uid ? 0 : 1
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: entryLayout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: handle
|
||||||
|
|
||||||
|
color: Qt.alpha(DynamicColors.palette.m3onSurface, handleDrag.active ? 0.12 : handleHover.hovered ? 0.09 : 0.06)
|
||||||
|
implicitHeight: 32
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: "drag_indicator"
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: handleHover
|
||||||
|
|
||||||
|
cursorShape: handleDrag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
DragHandler {
|
||||||
|
id: handleDrag
|
||||||
|
|
||||||
|
enabled: true
|
||||||
|
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||||
|
target: null
|
||||||
|
xAxis.enabled: false
|
||||||
|
yAxis.enabled: true
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active) {
|
||||||
|
root.beginVisualDrag(slot.uid, slot.modelData, entryRow);
|
||||||
|
} else if (root.draggedUid === slot.uid) {
|
||||||
|
root.endVisualDrag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onActiveTranslationChanged: {
|
||||||
|
if (!active || root.draggedUid !== slot.uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.dragX = root.dragStartX;
|
||||||
|
root.dragY = root.dragStartY + activeTranslation.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: root.iconForId(slot.entryData.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.labelForId(slot.entryData.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
Layout.rightMargin: Appearance.padding.small
|
||||||
|
checked: slot.entryData.enabled ?? true
|
||||||
|
|
||||||
|
onToggled: root.updateEntry(slot.DelegateModel.itemsIndex, checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property string setting
|
||||||
|
|
||||||
|
function formattedValue(): string {
|
||||||
|
const value = root.object[root.setting];
|
||||||
|
|
||||||
|
if (value === null || value === undefined)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: rect
|
||||||
|
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: Math.max(Math.min(textField.contentWidth + Appearance.padding.normal * 2, 550), 50)
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
id: textField
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
implicitWidth: Math.min(contentWidth, 550)
|
||||||
|
text: root.formattedValue()
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
root.object[root.setting] = textField.text;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
signal addActiveActionRequested
|
||||||
|
signal fieldEdited(string key, var value)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: -(Appearance.spacing.smaller / 2)
|
||||||
|
color: DynamicColors.tPalette.m3outlineVariant
|
||||||
|
implicitHeight: 1
|
||||||
|
visible: root.index !== 0
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Appearance.spacing.large
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: nameCell
|
||||||
|
|
||||||
|
property string draftName: ""
|
||||||
|
property bool editing: false
|
||||||
|
|
||||||
|
function beginEdit() {
|
||||||
|
draftName = root.modelData.name ?? "";
|
||||||
|
editing = true;
|
||||||
|
nameEditor.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEdit() {
|
||||||
|
draftName = root.modelData.name ?? "";
|
||||||
|
editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitEdit() {
|
||||||
|
editing = false;
|
||||||
|
|
||||||
|
if (draftName !== (root.modelData.name ?? "")) {
|
||||||
|
root.fieldEdited("name", draftName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: root.width / 2
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: nameHover
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: editButton.left
|
||||||
|
anchors.rightMargin: Appearance.spacing.small
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
elide: Text.ElideRight // enable if CustomText supports it
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.modelData.name
|
||||||
|
visible: !nameCell.editing
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: editButton
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "edit"
|
||||||
|
visible: nameHover.hovered && !nameCell.editing
|
||||||
|
|
||||||
|
onClicked: nameCell.beginEdit()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
visible: nameCell.editing
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
id: nameEditor
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
text: nameCell.draftName
|
||||||
|
|
||||||
|
Keys.onEscapePressed: {
|
||||||
|
nameCell.cancelEdit();
|
||||||
|
}
|
||||||
|
onEditingFinished: {
|
||||||
|
nameCell.commitEdit();
|
||||||
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
nameCell.draftName = nameEditor.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VSeparator {
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: cLayout
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: Math.max(Math.min(timeField.contentWidth + Appearance.padding.normal * 3, 200), 50)
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
id: timeField
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: String(root.modelData.timeout ?? "")
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
root.fieldEdited("timeout", Number(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Idle Action")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: Math.max(Math.min(idleField.contentWidth + Appearance.padding.normal * 3, 200), 50)
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
id: idleField
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: root.modelData.idleAction ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
root.fieldEdited("idleAction", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: activeActionRow.visible ? activeActionRow.implicitHeight : addButtonRow.implicitHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: activeActionRow
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
visible: root.modelData.activeAction !== undefined
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Active Action")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.preferredHeight: 33
|
||||||
|
Layout.preferredWidth: Math.max(Math.min(actionField.contentWidth + Appearance.padding.normal * 3, 200), 50)
|
||||||
|
color: DynamicColors.tPalette.m3surface
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
CustomTextField {
|
||||||
|
id: actionField
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: root.modelData.activeAction ?? ""
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
root.fieldEdited("activeAction", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: addButtonRow
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
visible: root.modelData.activeAction === undefined
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: button
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
icon: "add"
|
||||||
|
|
||||||
|
onClicked: root.addActiveActionRequested()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
text: qsTr("Add active action")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property string value
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.family: Appearance.font.family.mono
|
||||||
|
font.pointSize: Appearance.font.size.normal
|
||||||
|
text: root.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property string setting
|
||||||
|
property real max: Infinity
|
||||||
|
property real min: -Infinity
|
||||||
|
property real step: 1
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSpinBox {
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
max: root.max
|
||||||
|
min: root.min
|
||||||
|
step: root.step
|
||||||
|
value: Number(root.object[root.setting] ?? 0)
|
||||||
|
|
||||||
|
onValueModified: function (value) {
|
||||||
|
root.object[root.setting] = value;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property list<string> settings
|
||||||
|
|
||||||
|
function commitChoice(choice: int, setting: string): void {
|
||||||
|
root.object[setting] = choice;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formattedValue(setting: string): string {
|
||||||
|
const value = root.object[setting];
|
||||||
|
|
||||||
|
if (value === null || value === undefined)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hourToAmPm(hour) {
|
||||||
|
var h = Number(hour) % 24;
|
||||||
|
var d = new Date(2000, 0, 1, h, 0, 0);
|
||||||
|
return Qt.formatTime(d, "h AP");
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.normal
|
||||||
|
text: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(root.hourToAmPm(root.object[root.settings[0]])).arg(root.hourToAmPm(root.object[root.settings[1]]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinnerButton {
|
||||||
|
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
|
||||||
|
Layout.preferredWidth: height * 2
|
||||||
|
currentIndex: root.object[root.settings[0]]
|
||||||
|
text: root.formattedValue(root.settings[0])
|
||||||
|
|
||||||
|
menu.onItemSelected: item => {
|
||||||
|
root.commitChoice(item, root.settings[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinnerButton {
|
||||||
|
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
|
||||||
|
Layout.preferredWidth: height * 2
|
||||||
|
currentIndex: root.object[root.settings[1]]
|
||||||
|
text: root.formattedValue(root.settings[1])
|
||||||
|
|
||||||
|
menu.onItemSelected: item => {
|
||||||
|
root.commitChoice(item, root.settings[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property string setting
|
||||||
|
property string addLabel: qsTr("Add entry")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: layout.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
StringListEditor {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
addLabel: root.addLabel
|
||||||
|
values: [...(root.object[root.setting] ?? [])]
|
||||||
|
|
||||||
|
onListEdited: function (values) {
|
||||||
|
root.object[root.setting] = values;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user