Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4a3206ffd | |||
| 7a61cc4280 | |||
| f4c5ce08d2 | |||
| 08b18880a0 | |||
| 042a92a881 | |||
| 42c4b66399 | |||
| 9bfa2f6cca | |||
| 1d84248295 | |||
| c0f4434fd4 | |||
| 49b489e7d9 | |||
| 5d27d603e5 | |||
| 889c5993df | |||
| eac056cf1e | |||
| 62b250303a | |||
| 6d872c4f8d | |||
| 74e417a49c | |||
| 6371fa56f8 | |||
| 79b5220081 | |||
| 3ecddee5e9 | |||
| de91e36228 | |||
| 467fdc5e5e | |||
| f5a3b6f98f | |||
| 2baf91552d | |||
| 76d5508072 | |||
| 533f268184 | |||
| 0b6b5d0491 | |||
| 75df8e1134 | |||
| bab9554a60 | |||
| e195f58125 | |||
| 71b871c976 | |||
| c87443e72c | |||
| 531dd35f50 | |||
| f6a45d6a21 | |||
| e31fb851fa | |||
| 62b623c16d | |||
| 52cf956323 | |||
| 52a9049abb | |||
| a2c9ad6e29 | |||
| 5c428b211f | |||
| 9c8c48c5ee | |||
| d6ce5af55c | |||
| fb1cc51eda | |||
| 6e967f8fd6 | |||
| e5595d8b5d | |||
| 7451c52684 | |||
| 9065d693ef | |||
| f0afc7c75a | |||
| 9040713231 | |||
| cda00f91a3 | |||
| 749358dd5b | |||
| f989f74282 | |||
| 9fd3f8fd9e | |||
| a8dd730808 | |||
| dcfaa21e32 | |||
| e124819dcb | |||
| 7861ba5c51 | |||
| 6faebc986d |
@@ -1,3 +1,4 @@
|
|||||||
|
./result/
|
||||||
.pyre/
|
.pyre/
|
||||||
.cache/
|
.cache/
|
||||||
.venv/
|
.venv/
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real idx1: index
|
||||||
|
property int idx1Duration: 100
|
||||||
|
property real idx2: index
|
||||||
|
property int idx2Duration: 300
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
Behavior on idx1 {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.idx1Duration
|
||||||
|
easing.type: Easing.OutSine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on idx2 {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.idx2Duration
|
||||||
|
easing.type: Easing.OutSine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int cornerRadius: Appearance.rounding.normal
|
||||||
|
property int fieldHeight: 42
|
||||||
|
property bool filled: true
|
||||||
|
property real focusRingOpacity: 0.70
|
||||||
|
property int hPadding: 16
|
||||||
|
property int menuCornerRadius: 16
|
||||||
|
property int menuRowHeight: 46
|
||||||
|
property int menuVisibleRows: 7
|
||||||
|
property bool preferPopupWindow: false
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
implicitHeight: fieldHeight
|
||||||
|
implicitWidth: 240
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// ---------- Field background (filled/outlined + state layers + focus ring) ----------
|
||||||
|
background: Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: container
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.palette.m3surfaceVariant
|
||||||
|
radius: root.cornerRadius
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Content ----------
|
||||||
|
contentItem: RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: root.hPadding
|
||||||
|
anchors.rightMargin: root.hPadding
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
// Display text
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: root.currentText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicator chevron (simple, replace with your icon system)
|
||||||
|
CustomText {
|
||||||
|
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
rotation: root.popup.visible ? 180 : 0
|
||||||
|
text: "▾"
|
||||||
|
transformOrigin: Item.Center
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 140
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popup: Popup {
|
||||||
|
id: p
|
||||||
|
|
||||||
|
implicitHeight: list.contentItem.height + Appearance.padding.small * 2
|
||||||
|
implicitWidth: root.width
|
||||||
|
modal: true
|
||||||
|
popupType: root.preferPopupWindow ? Popup.Window : Popup.Item
|
||||||
|
y: -list.currentIndex * (root.menuRowHeight + Appearance.spacing.small) - Appearance.padding.small
|
||||||
|
|
||||||
|
background: CustomRect {
|
||||||
|
color: DynamicColors.palette.m3surface
|
||||||
|
radius: root.menuCornerRadius
|
||||||
|
}
|
||||||
|
contentItem: ListView {
|
||||||
|
id: list
|
||||||
|
|
||||||
|
anchors.bottomMargin: Appearance.padding.small
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: Appearance.padding.small
|
||||||
|
clip: true
|
||||||
|
currentIndex: root.currentIndex
|
||||||
|
model: root.delegateModel
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
delegate: CustomRect {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: (index === root.currentIndex) ? DynamicColors.palette.m3primary : "transparent"
|
||||||
|
implicitHeight: root.menuRowHeight
|
||||||
|
implicitWidth: p.implicitWidth - Appearance.padding.small * 2
|
||||||
|
radius: Appearance.rounding.normal - Appearance.padding.small
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3onSurface
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pixelSize: 15
|
||||||
|
text: modelData
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: "✓"
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
visible: index === root.currentIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
onClicked: {
|
||||||
|
root.currentIndex = index;
|
||||||
|
p.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expressive-ish open/close motion: subtle scale+fade (tune to taste). :contentReference[oaicite:5]{index=5}
|
||||||
|
enter: Transition {
|
||||||
|
Anim {
|
||||||
|
from: 0
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
from: 0.98
|
||||||
|
property: "scale"
|
||||||
|
to: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit: Transition {
|
||||||
|
Anim {
|
||||||
|
from: 1
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Elevation {
|
||||||
|
anchors.fill: parent
|
||||||
|
level: 2
|
||||||
|
radius: root.menuCornerRadius
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
Filled,
|
||||||
|
Tonal
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias active: menu.active
|
||||||
|
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
|
||||||
|
property bool disabled
|
||||||
|
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
|
||||||
|
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
|
||||||
|
property alias expanded: menu.expanded
|
||||||
|
property string fallbackIcon
|
||||||
|
property string fallbackText
|
||||||
|
property real horizontalPadding: Appearance.padding.normal
|
||||||
|
property alias iconLabel: iconLabel
|
||||||
|
property alias label: label
|
||||||
|
property alias menu: menu
|
||||||
|
property alias menuItems: menu.items
|
||||||
|
property bool menuOnTop
|
||||||
|
property alias stateLayer: stateLayer
|
||||||
|
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
|
||||||
|
property int type: CustomSplitButton.Filled
|
||||||
|
property real verticalPadding: Appearance.padding.smaller
|
||||||
|
|
||||||
|
spacing: Math.floor(Appearance.spacing.small / 2)
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
bottomRightRadius: Appearance.rounding.small / 2
|
||||||
|
color: root.disabled ? root.disabledColor : root.color
|
||||||
|
implicitHeight: expandBtn.implicitHeight
|
||||||
|
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
|
||||||
|
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
|
||||||
|
topRightRadius: Appearance.rounding.small / 2
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: stateLayer
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
root.active?.clicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
color: root.textColor
|
||||||
|
disabled: root.disabled
|
||||||
|
rect.bottomRightRadius: parent.bottomRightRadius
|
||||||
|
rect.topRightRadius: parent.topRightRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: textRow
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4)
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: iconLabel
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
animate: true
|
||||||
|
color: root.disabled ? root.disabledTextColor : root.textColor
|
||||||
|
fill: 1
|
||||||
|
text: root.active?.activeIcon ?? root.fallbackIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: implicitWidth
|
||||||
|
animate: true
|
||||||
|
clip: true
|
||||||
|
color: root.disabled ? root.disabledTextColor : root.textColor
|
||||||
|
text: root.active?.activeText ?? root.fallbackText
|
||||||
|
|
||||||
|
Behavior on Layout.preferredWidth {
|
||||||
|
Anim {
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: expandBtn
|
||||||
|
|
||||||
|
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
|
||||||
|
|
||||||
|
bottomLeftRadius: rad
|
||||||
|
color: root.disabled ? root.disabledColor : root.color
|
||||||
|
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
|
||||||
|
topLeftRadius: rad
|
||||||
|
|
||||||
|
Behavior on rad {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: expandStateLayer
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
root.expanded = !root.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: root.textColor
|
||||||
|
disabled: root.disabled
|
||||||
|
rect.bottomLeftRadius: parent.bottomLeftRadius
|
||||||
|
rect.topLeftRadius: parent.topLeftRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: expandIcon
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
|
||||||
|
color: root.disabled ? root.disabledTextColor : root.textColor
|
||||||
|
rotation: root.expanded ? 180 : 0
|
||||||
|
text: "expand_more"
|
||||||
|
|
||||||
|
Behavior on anchors.horizontalCenterOffset {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on rotation {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: menu
|
||||||
|
|
||||||
|
anchors.bottomMargin: Appearance.spacing.small
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.bottom
|
||||||
|
anchors.topMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
when: root.menuOnTop
|
||||||
|
|
||||||
|
AnchorChanges {
|
||||||
|
anchors.bottom: expandBtn.top
|
||||||
|
anchors.top: undefined
|
||||||
|
target: menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias active: splitButton.active
|
||||||
|
property bool enabled: true
|
||||||
|
property alias expanded: splitButton.expanded
|
||||||
|
property int expandedZ: 100
|
||||||
|
required property string label
|
||||||
|
property alias menuItems: splitButton.menuItems
|
||||||
|
property alias type: splitButton.type
|
||||||
|
|
||||||
|
signal selected(item: MenuItem)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
clip: false
|
||||||
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||||
|
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 {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.large
|
||||||
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: root.label
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSplitButton {
|
||||||
|
id: splitButton
|
||||||
|
|
||||||
|
enabled: root.enabled
|
||||||
|
menu.z: 1
|
||||||
|
type: CustomSplitButton.Filled
|
||||||
|
|
||||||
|
menu.onItemSelected: item => {
|
||||||
|
root.selected(item);
|
||||||
|
}
|
||||||
|
stateLayer.onClicked: {
|
||||||
|
splitButton.expanded = !splitButton.expanded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-12
@@ -13,11 +13,11 @@ Elevation {
|
|||||||
|
|
||||||
signal itemSelected(item: MenuItem)
|
signal itemSelected(item: MenuItem)
|
||||||
|
|
||||||
implicitHeight: root.expanded ? column.implicitHeight : 0
|
implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0
|
||||||
implicitWidth: Math.max(200, column.implicitWidth)
|
implicitWidth: Math.max(200, column.implicitWidth)
|
||||||
level: 2
|
level: 2
|
||||||
opacity: root.expanded ? 1 : 0
|
opacity: root.expanded ? 1 : 0
|
||||||
radius: Appearance.rounding.small / 2
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
Anim {
|
Anim {
|
||||||
@@ -41,7 +41,8 @@ Elevation {
|
|||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.items
|
model: root.items
|
||||||
@@ -54,19 +55,26 @@ Elevation {
|
|||||||
required property MenuItem modelData
|
required property MenuItem modelData
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
|
|
||||||
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
|
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
|
||||||
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
|
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
|
||||||
|
|
||||||
StateLayer {
|
CustomRect {
|
||||||
function onClicked(): void {
|
anchors.fill: parent
|
||||||
root.itemSelected(item.modelData);
|
anchors.leftMargin: Appearance.padding.small
|
||||||
root.active = item.modelData;
|
anchors.rightMargin: Appearance.padding.small
|
||||||
root.expanded = false;
|
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
|
||||||
}
|
radius: Appearance.rounding.normal - Appearance.padding.small
|
||||||
|
|
||||||
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
StateLayer {
|
||||||
disabled: !root.expanded
|
function onClicked(): void {
|
||||||
|
root.itemSelected(item.modelData);
|
||||||
|
root.active = item.modelData;
|
||||||
|
root.expanded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||||
|
disabled: !root.expanded
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|||||||
+17
-1
@@ -15,6 +15,7 @@ Singleton {
|
|||||||
property alias barConfig: adapter.barConfig
|
property alias barConfig: adapter.barConfig
|
||||||
property alias colors: adapter.colors
|
property alias colors: adapter.colors
|
||||||
property alias dashboard: adapter.dashboard
|
property alias dashboard: adapter.dashboard
|
||||||
|
property alias dock: adapter.dock
|
||||||
property alias general: adapter.general
|
property alias general: adapter.general
|
||||||
property alias launcher: adapter.launcher
|
property alias launcher: adapter.launcher
|
||||||
property alias lock: adapter.lock
|
property alias lock: adapter.lock
|
||||||
@@ -113,7 +114,8 @@ Singleton {
|
|||||||
osd: serializeOsd(),
|
osd: serializeOsd(),
|
||||||
background: serializeBackground(),
|
background: serializeBackground(),
|
||||||
launcher: serializeLauncher(),
|
launcher: serializeLauncher(),
|
||||||
colors: serializeColors()
|
colors: serializeColors(),
|
||||||
|
dock: serializeDock()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +151,17 @@ Singleton {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function serializeDock(): var {
|
||||||
|
return {
|
||||||
|
enable: dock.enable,
|
||||||
|
height: dock.height,
|
||||||
|
hoverRegionHeight: dock.hoverRegionHeight,
|
||||||
|
hoverToReveal: dock.hoverToReveal,
|
||||||
|
pinnedApps: dock.pinnedApps,
|
||||||
|
pinnedOnStartup: dock.pinnedOnStartup
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function serializeGeneral(): var {
|
function serializeGeneral(): var {
|
||||||
return {
|
return {
|
||||||
logo: general.logo,
|
logo: general.logo,
|
||||||
@@ -156,6 +169,7 @@ Singleton {
|
|||||||
color: {
|
color: {
|
||||||
wallust: general.color.wallust,
|
wallust: general.color.wallust,
|
||||||
mode: general.color.mode,
|
mode: general.color.mode,
|
||||||
|
smart: general.color.smart,
|
||||||
schemeGeneration: general.color.schemeGeneration,
|
schemeGeneration: general.color.schemeGeneration,
|
||||||
scheduleDarkStart: general.color.scheduleDarkStart,
|
scheduleDarkStart: general.color.scheduleDarkStart,
|
||||||
scheduleDarkEnd: general.color.scheduleDarkEnd,
|
scheduleDarkEnd: general.color.scheduleDarkEnd,
|
||||||
@@ -381,6 +395,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
property DashboardConfig dashboard: DashboardConfig {
|
property DashboardConfig dashboard: DashboardConfig {
|
||||||
}
|
}
|
||||||
|
property DockConfig dock: DockConfig {
|
||||||
|
}
|
||||||
property General general: General {
|
property General general: General {
|
||||||
}
|
}
|
||||||
property Launcher launcher: Launcher {
|
property Launcher launcher: Launcher {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
JsonObject {
|
||||||
|
property bool enable: false
|
||||||
|
property real height: 60
|
||||||
|
property real hoverRegionHeight: 2
|
||||||
|
property bool hoverToReveal: true
|
||||||
|
property list<string> pinnedApps: ["org.kde.dolphin", "kitty",]
|
||||||
|
property bool pinnedOnStartup: false
|
||||||
|
}
|
||||||
@@ -78,6 +78,12 @@ Singleton {
|
|||||||
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
|
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setMode(mode: string): void {
|
||||||
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
|
||||||
|
Config.general.color.mode = mode;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
path: `${Paths.state}/scheme.json`
|
path: `${Paths.state}/scheme.json`
|
||||||
watchChanges: true
|
watchChanges: true
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ JsonObject {
|
|||||||
property int scheduleDarkEnd: 0
|
property int scheduleDarkEnd: 0
|
||||||
property int scheduleDarkStart: 0
|
property int scheduleDarkStart: 0
|
||||||
property bool schemeGeneration: true
|
property bool schemeGeneration: true
|
||||||
|
property bool smart: false
|
||||||
property bool wallust: false
|
property bool wallust: false
|
||||||
}
|
}
|
||||||
component Idle: JsonObject {
|
component Idle: JsonObject {
|
||||||
|
|||||||
+26
-2
@@ -6,11 +6,27 @@ JsonObject {
|
|||||||
{
|
{
|
||||||
name: "Calculator",
|
name: "Calculator",
|
||||||
icon: "calculate",
|
icon: "calculate",
|
||||||
description: "Do simple math equations (powered by Qalc)",
|
description: "Do simple math equations",
|
||||||
command: ["autocomplete", "calc"],
|
command: ["autocomplete", "calc"],
|
||||||
enabled: true,
|
enabled: true,
|
||||||
dangerous: false
|
dangerous: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Light",
|
||||||
|
icon: "light_mode",
|
||||||
|
description: "Change to light mode",
|
||||||
|
command: ["setMode", "light"],
|
||||||
|
enabled: true,
|
||||||
|
dangerous: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dark",
|
||||||
|
icon: "dark_mode",
|
||||||
|
description: "Change to dark mode",
|
||||||
|
command: ["setMode", "dark"],
|
||||||
|
enabled: true,
|
||||||
|
dangerous: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Wallpaper",
|
name: "Wallpaper",
|
||||||
icon: "image",
|
icon: "image",
|
||||||
@@ -19,6 +35,14 @@ JsonObject {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
dangerous: false
|
dangerous: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Variant",
|
||||||
|
icon: "colors",
|
||||||
|
description: "Change the current scheme variant",
|
||||||
|
command: ["autocomplete", "variant"],
|
||||||
|
enabled: true,
|
||||||
|
dangerous: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Shutdown",
|
name: "Shutdown",
|
||||||
icon: "power_settings_new",
|
icon: "power_settings_new",
|
||||||
@@ -37,7 +61,7 @@ JsonObject {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Logout",
|
name: "Logout",
|
||||||
icon: "exit_to_app",
|
icon: "logout",
|
||||||
description: "Log out of the current session",
|
description: "Log out of the current session",
|
||||||
command: ["loginctl", "terminate-user", ""],
|
command: ["loginctl", "terminate-user", ""],
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ 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.Settings as Settings
|
import qs.Modules.Settings as Settings
|
||||||
|
|
||||||
Shape {
|
Shape {
|
||||||
id: root
|
id: root
|
||||||
@@ -85,11 +85,11 @@ Shape {
|
|||||||
wrapper: root.panels.sidebar
|
wrapper: root.panels.sidebar
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings.Background {
|
Settings.Background {
|
||||||
// id: settings
|
id: settings
|
||||||
//
|
|
||||||
// startX: (root.width - wrapper.width) / 2 - rounding
|
startX: (root.width - wrapper.width) / 2 - rounding
|
||||||
// startY: 0
|
startY: 0
|
||||||
// wrapper: root.panels.settings
|
wrapper: root.panels.settings
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -42,7 +42,7 @@ Variants {
|
|||||||
regions: popoutRegions.instances
|
regions: popoutRegions.instances
|
||||||
width: bar.width
|
width: bar.width
|
||||||
x: 0
|
x: 0
|
||||||
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : 34
|
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : backgroundRect.height
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem.Keys.onEscapePressed: {
|
contentItem.Keys.onEscapePressed: {
|
||||||
@@ -62,7 +62,7 @@ Variants {
|
|||||||
WlrLayershell.layer: WlrLayer.Bottom
|
WlrLayershell.layer: WlrLayer.Bottom
|
||||||
WlrLayershell.namespace: "ZShell-Bar-Exclusion"
|
WlrLayershell.namespace: "ZShell-Bar-Exclusion"
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
implicitHeight: 34
|
implicitHeight: backgroundRect.height
|
||||||
screen: bar.screen
|
screen: bar.screen
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -185,7 +185,7 @@ Variants {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? -30 : 0
|
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? -30 : 0
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
implicitHeight: 34
|
implicitHeight: barLoader.implicitHeight
|
||||||
radius: 0
|
radius: 0
|
||||||
|
|
||||||
Behavior on anchors.topMargin {
|
Behavior on anchors.topMargin {
|
||||||
@@ -200,7 +200,9 @@ Variants {
|
|||||||
BarLoader {
|
BarLoader {
|
||||||
id: barLoader
|
id: barLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
bar: bar
|
bar: bar
|
||||||
popouts: panels.popouts
|
popouts: panels.popouts
|
||||||
screen: scope.modelData
|
screen: scope.modelData
|
||||||
|
|||||||
@@ -223,12 +223,8 @@ CustomMouseArea {
|
|||||||
root.utilitiesShortcutActive = false;
|
root.utilitiesShortcutActive = false;
|
||||||
|
|
||||||
// Also hide dashboard and OSD if they're not being hovered
|
// Also hide dashboard and OSD if they're not being hovered
|
||||||
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
|
|
||||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
||||||
|
|
||||||
if (!inDashboardArea) {
|
|
||||||
root.visibilities.dashboard = false;
|
|
||||||
}
|
|
||||||
if (!inOsdArea) {
|
if (!inOsdArea) {
|
||||||
root.visibilities.osd = false;
|
root.visibilities.osd = false;
|
||||||
root.panels.osd.hovered = false;
|
root.panels.osd.hovered = false;
|
||||||
@@ -249,6 +245,12 @@ CustomMouseArea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onResourcesChanged() {
|
||||||
|
if (root.visibilities.resources && root.popouts.currentName.startsWith("audio")) {
|
||||||
|
root.popouts.hasCurrent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onSidebarChanged() {
|
function onSidebarChanged() {
|
||||||
if (root.visibilities.sidebar) {
|
if (root.visibilities.sidebar) {
|
||||||
root.visibilities.dashboard = false;
|
root.visibilities.dashboard = false;
|
||||||
|
|||||||
+10
-10
@@ -10,7 +10,7 @@ import qs.Modules.Osd as Osd
|
|||||||
import qs.Components.Toast as Toasts
|
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.Config
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -24,7 +24,7 @@ Item {
|
|||||||
readonly property alias popouts: popouts
|
readonly property alias popouts: popouts
|
||||||
readonly property alias resources: resources
|
readonly property alias resources: resources
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
// readonly property alias settings: settings
|
readonly property alias settings: settings
|
||||||
readonly property alias sidebar: sidebar
|
readonly property alias sidebar: sidebar
|
||||||
readonly property alias toasts: toasts
|
readonly property alias toasts: toasts
|
||||||
readonly property alias utilities: utilities
|
readonly property alias utilities: utilities
|
||||||
@@ -127,12 +127,12 @@ Item {
|
|||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings.Wrapper {
|
Settings.Wrapper {
|
||||||
// id: settings
|
id: settings
|
||||||
//
|
|
||||||
// anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
// anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
// panels: root
|
panels: root
|
||||||
// visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property list<var> apps: {
|
||||||
|
var map = new Map();
|
||||||
|
|
||||||
|
const pinnedApps = Config.dock?.pinnedApps ?? [];
|
||||||
|
for (const appId of pinnedApps) {
|
||||||
|
if (!map.has(appId.toLowerCase()))
|
||||||
|
map.set(appId.toLowerCase(), ({
|
||||||
|
pinned: true,
|
||||||
|
toplevels: []
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinnedApps.length > 0) {
|
||||||
|
map.set("SEPARATOR", {
|
||||||
|
pinned: false,
|
||||||
|
toplevels: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = [];
|
||||||
|
|
||||||
|
for (const [key, value] of map) {
|
||||||
|
values.push(appEntryComp.createObject(null, {
|
||||||
|
appId: key,
|
||||||
|
toplevels: value.toplevels,
|
||||||
|
pinned: value.pinned
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPinned(appId) {
|
||||||
|
return Config.dock.pinnedApps.indexOf(appId) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePin(appId) {
|
||||||
|
if (root.isPinned(appId)) {
|
||||||
|
Config.dock.pinnedApps = Config.dock.pinnedApps.filter(id => id !== appId);
|
||||||
|
} else {
|
||||||
|
Config.dock.pinnedApps = Config.dock.pinnedApps.concat([appId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: appEntryComp
|
||||||
|
|
||||||
|
TaskbarAppEntry {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component TaskbarAppEntry: QtObject {
|
||||||
|
id: wrapper
|
||||||
|
|
||||||
|
required property string appId
|
||||||
|
required property bool pinned
|
||||||
|
required property list<var> toplevels
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,22 +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} --thumbnail-path ${Paths.cache}/imagecache/thumbnail.jpg --output ${Paths.state}/scheme.json --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
|
Quickshell.execDetached(["sh", "-c", `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;
|
||||||
if (Config.general.color.wallust)
|
|
||||||
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
|
|
||||||
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(["sh", "-c", `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} --thumbnail-path ${Paths.cache}/imagecache/thumbnail.jpg --output ${Paths.state}/scheme.json --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
|
Quickshell.execDetached(["sh", "-c", `zshell-cli scheme generate --image-path ${root.actualCurrent} --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
extraOpts: useFuzzy ? ({}) : ({
|
extraOpts: useFuzzy ? ({}) : ({
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
height: 22
|
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
implicitHeight: 34
|
||||||
|
|
||||||
CustomShortcut {
|
CustomShortcut {
|
||||||
name: "toggle-overview"
|
name: "toggle-overview"
|
||||||
@@ -73,6 +73,7 @@ RowLayout {
|
|||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
|
// model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index)
|
||||||
model: Config.barConfig.entries
|
model: Config.barConfig.entries
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
|
|||||||
@@ -45,16 +45,15 @@ ShapePath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PathArc {
|
PathArc {
|
||||||
direction: PathArc.Counterclockwise
|
|
||||||
radiusX: root.rounding
|
radiusX: root.rounding
|
||||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
relativeX: root.rounding
|
relativeX: root.rounding
|
||||||
relativeY: -root.roundingY
|
relativeY: root.roundingY
|
||||||
}
|
}
|
||||||
|
|
||||||
PathLine {
|
PathLine {
|
||||||
relativeX: 0
|
relativeX: 0
|
||||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
relativeY: -(root.wrapper.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
PathArc {
|
PathArc {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property bool flatten: wrapper.height < rounding * 2
|
||||||
|
readonly property real rounding: Appearance.rounding.normal
|
||||||
|
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||||
|
required property Wrapper wrapper
|
||||||
|
|
||||||
|
fillColor: DynamicColors.palette.m3surface
|
||||||
|
strokeWidth: -1
|
||||||
|
|
||||||
|
Behavior on fillColor {
|
||||||
|
CAnim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: -root.roundingY
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: 0
|
||||||
|
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: -root.roundingY
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: root.wrapper.width - root.rounding * 2
|
||||||
|
relativeY: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: root.roundingY
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: 0
|
||||||
|
relativeY: root.wrapper.height - root.roundingY * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: root.roundingY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Components
|
||||||
|
import qs.Helpers
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property int padding: Appearance.padding.small
|
||||||
|
required property var panels
|
||||||
|
readonly property int rounding: Appearance.rounding.large
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
implicitHeight: Config.dock.height + root.padding * 2
|
||||||
|
implicitWidth: dockRow.implicitWidth + root.padding * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: dockRow
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int contentHeight
|
||||||
|
required property var panels
|
||||||
|
required property ShellScreen screen
|
||||||
|
readonly property bool shouldBeActive: visibilities.dock
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
implicitHeight: 0
|
||||||
|
implicitWidth: content.implicitWidth
|
||||||
|
visible: height > 0
|
||||||
|
|
||||||
|
onShouldBeActiveChanged: {
|
||||||
|
if (shouldBeActive) {
|
||||||
|
timer.stop();
|
||||||
|
hideAnim.stop();
|
||||||
|
showAnim.start();
|
||||||
|
} else {
|
||||||
|
showAnim.stop();
|
||||||
|
hideAnim.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: showAnim
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||||
|
property: "implicitHeight"
|
||||||
|
target: root
|
||||||
|
to: root.contentHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptAction {
|
||||||
|
script: root.implicitHeight = Qt.binding(() => content.implicitHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: hideAnim
|
||||||
|
|
||||||
|
ScriptAction {
|
||||||
|
script: root.implicitHeight = root.implicitHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||||
|
property: "implicitHeight"
|
||||||
|
target: root
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
|
||||||
|
interval: Appearance.anim.durations.small
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if (running && !root.shouldBeActive) {
|
||||||
|
content.visible = false;
|
||||||
|
content.active = true;
|
||||||
|
} else {
|
||||||
|
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
||||||
|
content.visible = true;
|
||||||
|
if (showAnim.running) {
|
||||||
|
showAnim.stop();
|
||||||
|
showAnim.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
active: false
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
sourceComponent: Content {
|
||||||
|
panels: root.panels
|
||||||
|
visibilities: root.visibilities
|
||||||
|
|
||||||
|
Component.onCompleted: root.contentHeight = implicitHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: timer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -131,6 +131,14 @@ CustomListView {
|
|||||||
model.values: [0]
|
model.values: [0]
|
||||||
root.delegate: calcItem
|
root.delegate: calcItem
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "variant"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
model.values: SchemeVariants.query(search.text)
|
||||||
|
root.delegate: variantItem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
@@ -211,4 +219,12 @@ CustomListView {
|
|||||||
list: root
|
list: root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: variantItem
|
||||||
|
|
||||||
|
VariantItem {
|
||||||
|
list: root
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,11 +123,6 @@ Item {
|
|||||||
search.text = "";
|
search.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSessionChanged(): void {
|
|
||||||
if (!root.visibilities.session)
|
|
||||||
search.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: root.visibilities
|
target: root.visibilities
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules.Launcher.Services
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var list
|
||||||
|
required property SchemeVariants.Variant modelData
|
||||||
|
|
||||||
|
anchors.left: parent?.left
|
||||||
|
anchors.right: parent?.right
|
||||||
|
implicitHeight: Config.launcher.sizes.itemHeight
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
function onClicked(): void {
|
||||||
|
root.modelData?.onClicked(root.list);
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.larger
|
||||||
|
anchors.margins: Appearance.padding.smaller
|
||||||
|
anchors.rightMargin: Appearance.padding.larger
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pointSize: Appearance.font.size.extraLarge
|
||||||
|
text: root.modelData?.icon ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.left: icon.right
|
||||||
|
anchors.leftMargin: Appearance.spacing.larger
|
||||||
|
anchors.verticalCenter: icon.verticalCenter
|
||||||
|
spacing: 0
|
||||||
|
width: parent.width - icon.width - anchors.leftMargin - (current.active ? current.width + Appearance.spacing.normal : 0)
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
font.pointSize: Appearance.font.size.normal
|
||||||
|
text: root.modelData?.name ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
color: DynamicColors.palette.m3outline
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
text: root.modelData?.description ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: current
|
||||||
|
|
||||||
|
active: root.modelData?.variant === Config.colors.schemeType
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
sourceComponent: MaterialIcon {
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: "check"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ Searcher {
|
|||||||
list.search.text = `${Config.launcher.actionPrefix}${command[1]} `;
|
list.search.text = `${Config.launcher.actionPrefix}${command[1]} `;
|
||||||
} else if (command[0] === "setMode" && command.length > 1) {
|
} else if (command[0] === "setMode" && command.length > 1) {
|
||||||
list.visibilities.launcher = false;
|
list.visibilities.launcher = false;
|
||||||
Colours.setMode(command[1]);
|
DynamicColors.setMode(command[1]);
|
||||||
} else {
|
} else {
|
||||||
list.visibilities.launcher = false;
|
list.visibilities.launcher = false;
|
||||||
Quickshell.execDetached(command);
|
Quickshell.execDetached(command);
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Modules.Launcher
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Searcher {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function transformSearch(search: string): string {
|
||||||
|
return search.slice(`${Config.launcher.actionPrefix}variant `.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
useFuzzy: Config.launcher.useFuzzy.variants
|
||||||
|
|
||||||
|
list: [
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Maximum chroma at each tone.")
|
||||||
|
icon: "sentiment_very_dissatisfied"
|
||||||
|
name: qsTr("Vibrant")
|
||||||
|
variant: "vibrant"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Pastel palette with a low chroma.")
|
||||||
|
icon: "android"
|
||||||
|
name: qsTr("Tonal Spot")
|
||||||
|
variant: "tonalspot"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Hue-shifted, artistic or playful colors.")
|
||||||
|
icon: "compare_arrows"
|
||||||
|
name: qsTr("Expressive")
|
||||||
|
variant: "expressive"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Preserve source color exactly.")
|
||||||
|
icon: "compare"
|
||||||
|
name: qsTr("Fidelity")
|
||||||
|
variant: "fidelity"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Almost identical to fidelity.")
|
||||||
|
icon: "sentiment_calm"
|
||||||
|
name: qsTr("Content")
|
||||||
|
variant: "content"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("The seed colour's hue does not appear in the theme.")
|
||||||
|
icon: "nutrition"
|
||||||
|
name: qsTr("Fruit Salad")
|
||||||
|
variant: "fruit-salad"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Like Fruit Salad but different hues.")
|
||||||
|
icon: "looks"
|
||||||
|
name: qsTr("Rainbow")
|
||||||
|
variant: "rainbow"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("Close to grayscale, a hint of chroma.")
|
||||||
|
icon: "contrast"
|
||||||
|
name: qsTr("Neutral")
|
||||||
|
variant: "neutral"
|
||||||
|
},
|
||||||
|
Variant {
|
||||||
|
description: qsTr("All colours are grayscale, no chroma.")
|
||||||
|
icon: "filter_b_and_w"
|
||||||
|
name: qsTr("Monochrome")
|
||||||
|
variant: "monochrome"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
component Variant: QtObject {
|
||||||
|
required property string description
|
||||||
|
required property string icon
|
||||||
|
required property string name
|
||||||
|
required property string variant
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
list.visibilities.launcher = false;
|
||||||
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--scheme", variant]);
|
||||||
|
Config.colors.schemeType = variant;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,10 @@ Item {
|
|||||||
|
|
||||||
property int contentHeight
|
property int contentHeight
|
||||||
readonly property real maxHeight: {
|
readonly property real maxHeight: {
|
||||||
let max = screen.height - Appearance.spacing.large;
|
let max = screen.height - Appearance.spacing.large * 2;
|
||||||
if (visibilities.dashboard)
|
if (visibilities.resources)
|
||||||
|
max -= panels.resources.nonAnimHeight;
|
||||||
|
if (visibilities.dashboard && panels.dashboard.x < root.x + root.implicitWidth)
|
||||||
max -= panels.dashboard.nonAnimHeight;
|
max -= panels.dashboard.nonAnimHeight;
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,21 @@ WlSessionLockSurface {
|
|||||||
implicitWidth: size
|
implicitWidth: size
|
||||||
scale: 0
|
scale: 0
|
||||||
|
|
||||||
|
// MultiEffect {
|
||||||
|
// anchors.fill: lockBg
|
||||||
|
// autoPaddingEnabled: false
|
||||||
|
// blur: 1
|
||||||
|
// blurEnabled: true
|
||||||
|
// blurMax: 64
|
||||||
|
// maskEnabled: true
|
||||||
|
// maskSource: lockBg
|
||||||
|
//
|
||||||
|
// source: ShaderEffectSource {
|
||||||
|
// sourceItem: background
|
||||||
|
// sourceRect: Qt.rect(lockBg.x, lockBg.y, lockBg.width, lockBg, height)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: lockBg
|
id: lockBg
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: 22
|
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
-88
@@ -7,110 +7,87 @@ import qs.Config
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property color borderColor: warning ? DynamicColors.palette.m3onError : mainColor
|
property color accentColor: warning ? DynamicColors.palette.m3error : mainColor
|
||||||
|
property real animatedPercentage: 0
|
||||||
|
readonly property real arcStartAngle: 0.75 * Math.PI
|
||||||
|
readonly property real arcSweep: 1.5 * Math.PI
|
||||||
|
property string icon
|
||||||
required property color mainColor
|
required property color mainColor
|
||||||
required property double percentage
|
required property double percentage
|
||||||
property bool shown: true
|
property bool shown: true
|
||||||
property color usageColor: warning ? DynamicColors.palette.m3error : mainColor
|
property color usageColor: warning ? DynamicColors.palette.m3error : mainColor
|
||||||
property bool warning: percentage * 100 >= warningThreshold
|
property bool warning: percentage * 100 >= warningThreshold
|
||||||
property int warningThreshold: 100
|
property int warningThreshold: 80
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
implicitHeight: 22
|
height: implicitHeight
|
||||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
implicitHeight: 34
|
||||||
|
implicitWidth: 34
|
||||||
|
percentage: 0
|
||||||
visible: width > 0 && height > 0
|
visible: width > 0 && height > 0
|
||||||
|
width: implicitWidth
|
||||||
|
|
||||||
Behavior on percentage {
|
Behavior on animatedPercentage {
|
||||||
NumberAnimation {
|
Anim {
|
||||||
duration: 300
|
duration: Appearance.anim.durations.large
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
Component.onCompleted: animatedPercentage = percentage
|
||||||
id: resourceRowLayout
|
onPercentageChanged: animatedPercentage = percentage
|
||||||
|
|
||||||
spacing: 2
|
Canvas {
|
||||||
x: shown ? 0 : -resourceRowLayout.width
|
id: gaugeCanvas
|
||||||
|
|
||||||
anchors {
|
anchors.centerIn: parent
|
||||||
verticalCenter: parent.verticalCenter
|
height: width
|
||||||
}
|
width: Math.min(parent.width, parent.height)
|
||||||
|
|
||||||
Item {
|
Component.onCompleted: requestPaint()
|
||||||
Layout.alignment: Qt.AlignVCenter
|
onPaint: {
|
||||||
implicitHeight: root.implicitHeight
|
const ctx = getContext("2d");
|
||||||
implicitWidth: 14
|
ctx.reset();
|
||||||
|
const cx = width / 2;
|
||||||
Rectangle {
|
const cy = (height / 2) + 1;
|
||||||
id: backgroundCircle
|
const radius = (Math.min(width, height) - 12) / 2;
|
||||||
|
const lineWidth = 3;
|
||||||
anchors.centerIn: parent
|
ctx.beginPath();
|
||||||
border.color: "#404040"
|
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep);
|
||||||
border.width: 1
|
ctx.lineWidth = lineWidth;
|
||||||
color: "#40000000"
|
ctx.lineCap = "round";
|
||||||
height: 14
|
ctx.strokeStyle = DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
|
||||||
radius: height / 2
|
ctx.stroke();
|
||||||
width: 14
|
if (root.animatedPercentage > 0) {
|
||||||
}
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep * root.animatedPercentage);
|
||||||
Shape {
|
ctx.lineWidth = lineWidth;
|
||||||
anchors.fill: backgroundCircle
|
ctx.lineCap = "round";
|
||||||
preferredRendererType: Shape.CurveRenderer
|
ctx.strokeStyle = root.accentColor;
|
||||||
smooth: true
|
ctx.stroke();
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onAnimatedPercentageChanged() {
|
||||||
|
gaugeCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onPaletteChanged() {
|
||||||
|
gaugeCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: DynamicColors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: DynamicColors.palette.m3onSurface
|
||||||
|
font.pointSize: 12
|
||||||
|
text: root.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-35
@@ -3,10 +3,9 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell.Wayland
|
import qs.Helpers
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Effects
|
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -14,15 +13,16 @@ Item {
|
|||||||
|
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.top: parent.top
|
||||||
clip: true
|
clip: true
|
||||||
implicitHeight: 34
|
|
||||||
implicitWidth: rowLayout.implicitWidth + Appearance.padding.small * 2
|
implicitWidth: rowLayout.implicitWidth + Appearance.padding.small * 2
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: 22
|
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -40,57 +40,37 @@ Item {
|
|||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 6
|
spacing: 0
|
||||||
|
|
||||||
MaterialIcon {
|
Ref {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
service: SystemUsage
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: 14
|
|
||||||
text: "memory_alt"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
icon: "memory"
|
||||||
mainColor: DynamicColors.palette.m3primary
|
mainColor: DynamicColors.palette.m3primary
|
||||||
percentage: ResourceUsage.memoryUsedPercentage
|
percentage: SystemUsage.cpuPerc
|
||||||
warningThreshold: 95
|
warningThreshold: 95
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: 14
|
|
||||||
text: "memory"
|
|
||||||
}
|
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
icon: "memory_alt"
|
||||||
mainColor: DynamicColors.palette.m3secondary
|
mainColor: DynamicColors.palette.m3secondary
|
||||||
percentage: ResourceUsage.cpuUsage
|
percentage: SystemUsage.memPerc
|
||||||
warningThreshold: 80
|
warningThreshold: 80
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: 14
|
|
||||||
text: "gamepad"
|
|
||||||
}
|
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
icon: "gamepad"
|
||||||
mainColor: DynamicColors.palette.m3tertiary
|
mainColor: DynamicColors.palette.m3tertiary
|
||||||
percentage: ResourceUsage.gpuUsage
|
percentage: SystemUsage.gpuPerc
|
||||||
}
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: 14
|
|
||||||
text: "developer_board"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
icon: "developer_board"
|
||||||
mainColor: DynamicColors.palette.m3primary
|
mainColor: DynamicColors.palette.m3primary
|
||||||
percentage: ResourceUsage.gpuMemUsage
|
percentage: SystemUsage.gpuMemUsed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,15 +28,14 @@ ShapePath {
|
|||||||
|
|
||||||
PathLine {
|
PathLine {
|
||||||
relativeX: 0
|
relativeX: 0
|
||||||
relativeY: root.wrapper.height - root.roundingY * 2
|
relativeY: root.wrapper.height
|
||||||
}
|
}
|
||||||
|
|
||||||
PathArc {
|
PathArc {
|
||||||
direction: PathArc.Counterclockwise
|
|
||||||
radiusX: root.rounding
|
radiusX: root.rounding
|
||||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
relativeX: root.rounding
|
relativeX: root.rounding
|
||||||
relativeY: root.roundingY
|
relativeY: -root.roundingY
|
||||||
}
|
}
|
||||||
|
|
||||||
PathLine {
|
PathLine {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property bool flatten: wrapper.height < rounding * 2
|
||||||
|
readonly property real rounding: 8
|
||||||
|
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||||
|
required property Wrapper wrapper
|
||||||
|
|
||||||
|
fillColor: DynamicColors.palette.m3surface
|
||||||
|
strokeWidth: -1
|
||||||
|
|
||||||
|
Behavior on fillColor {
|
||||||
|
CAnim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.roundingY, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: root.roundingY
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: 0
|
||||||
|
relativeY: root.wrapper.height - root.roundingY * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: root.roundingY
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: root.wrapper.width - root.rounding * 2
|
||||||
|
relativeY: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
direction: PathArc.Counterclockwise
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: -root.roundingY
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLine {
|
||||||
|
relativeX: 0
|
||||||
|
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
PathArc {
|
||||||
|
radiusX: root.rounding
|
||||||
|
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||||
|
relativeX: root.rounding
|
||||||
|
relativeY: -root.roundingY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules as Modules
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Item content
|
||||||
|
|
||||||
|
implicitHeight: clayout.contentHeight + Appearance.padding.smaller * 2
|
||||||
|
implicitWidth: clayout.contentWidth + Appearance.padding.smaller * 2
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: listModel
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "settings"
|
||||||
|
name: "General"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "wallpaper"
|
||||||
|
name: "Wallpaper"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "settop_component"
|
||||||
|
name: "Bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "lock"
|
||||||
|
name: "Lockscreen"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "build_circle"
|
||||||
|
name: "Services"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "notifications"
|
||||||
|
name: "Notifications"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "view_sidebar"
|
||||||
|
name: "Sidebar"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "handyman"
|
||||||
|
name: "Utilities"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "dashboard"
|
||||||
|
name: "Dashboard"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "colors"
|
||||||
|
name: "Appearance"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "display_settings"
|
||||||
|
name: "On screen display"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "rocket_launch"
|
||||||
|
name: "Launcher"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "colors"
|
||||||
|
name: "Colors"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
CustomListView {
|
||||||
|
id: clayout
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
contentHeight: contentItem.childrenRect.height
|
||||||
|
contentWidth: contentItem.childrenRect.width
|
||||||
|
highlightFollowsCurrentItem: false
|
||||||
|
implicitHeight: contentItem.childrenRect.height
|
||||||
|
implicitWidth: contentItem.childrenRect.width
|
||||||
|
model: listModel
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
delegate: Category {
|
||||||
|
}
|
||||||
|
highlight: CustomRect {
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
implicitHeight: clayout.currentItem?.implicitHeight ?? 0
|
||||||
|
implicitWidth: clayout.width
|
||||||
|
radius: 4
|
||||||
|
y: clayout.currentItem?.y ?? 0
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Category: CustomRect {
|
||||||
|
id: categoryItem
|
||||||
|
|
||||||
|
required property string icon
|
||||||
|
required property int index
|
||||||
|
required property string name
|
||||||
|
|
||||||
|
implicitHeight: 42
|
||||||
|
implicitWidth: 200
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.smaller
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: icon.contentWidth
|
||||||
|
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||||
|
font.pointSize: 22
|
||||||
|
text: categoryItem.icon
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Appearance.spacing.normal
|
||||||
|
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||||
|
text: categoryItem.name
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: layer
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.content.currentCategory = categoryItem.name.toLowerCase();
|
||||||
|
clayout.currentIndex = categoryItem.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
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.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: clayout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: colorLayout.implicitHeight
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: colorLayout
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.large
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Settings {
|
||||||
|
name: "smth"
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "wallust"
|
||||||
|
object: Config.general.color
|
||||||
|
setting: "wallust"
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSplitButtonRow {
|
||||||
|
enabled: true
|
||||||
|
label: qsTr("Scheme mode")
|
||||||
|
|
||||||
|
menuItems: [
|
||||||
|
MenuItem {
|
||||||
|
property string val: "light"
|
||||||
|
|
||||||
|
icon: "light_mode"
|
||||||
|
text: qsTr("Light")
|
||||||
|
},
|
||||||
|
MenuItem {
|
||||||
|
property string val: "dark"
|
||||||
|
|
||||||
|
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 {
|
||||||
|
id: settingsItem
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
|
||||||
|
Layout.preferredHeight: 42
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.smaller
|
||||||
|
anchors.right: parent.right
|
||||||
|
font.bold: true
|
||||||
|
font.pointSize: 32
|
||||||
|
text: settingsItem.name
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules as Modules
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules as Modules
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: clayout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Settings {
|
||||||
|
name: "apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Settings: CustomRect {
|
||||||
|
id: settingsItem
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
|
||||||
|
implicitHeight: 42
|
||||||
|
implicitWidth: 200
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules as Modules
|
||||||
|
import qs.Modules.Settings.Categories as Cat
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string currentCategory: "general"
|
||||||
|
readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2
|
||||||
|
readonly property real nonAnimWidth: view.implicitWidth + 500 + viewWrapper.anchors.margins * 2
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
implicitHeight: nonAnimHeight
|
||||||
|
implicitWidth: nonAnimWidth
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onCurrentCategoryChanged() {
|
||||||
|
stack.pop();
|
||||||
|
if (currentCategory === "general") {
|
||||||
|
stack.push(general);
|
||||||
|
} else if (currentCategory === "wallpaper") {
|
||||||
|
stack.push(background);
|
||||||
|
} else if (currentCategory === "appearance") {
|
||||||
|
stack.push(appearance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
|
||||||
|
ClippingRectangle {
|
||||||
|
id: viewWrapper
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.smaller
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: view
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
implicitHeight: layout.implicitHeight
|
||||||
|
implicitWidth: layout.implicitWidth
|
||||||
|
|
||||||
|
Categories {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
content: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
id: categoryContent
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: view.right
|
||||||
|
anchors.leftMargin: Appearance.spacing.smaller
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: stack
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.smaller
|
||||||
|
initialItem: general
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: general
|
||||||
|
|
||||||
|
Cat.General {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: background
|
||||||
|
|
||||||
|
Cat.Background {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: appearance
|
||||||
|
|
||||||
|
Cat.Appearance {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var object
|
||||||
|
required property string setting
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 42
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pointSize: 16
|
||||||
|
text: root.name
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
id: cswitch
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
checked: root.object[root.setting]
|
||||||
|
|
||||||
|
onToggled: {
|
||||||
|
root.object[root.setting] = checked;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var panels
|
||||||
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
|
implicitHeight: 0
|
||||||
|
implicitWidth: content.implicitWidth
|
||||||
|
visible: height > 0
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "visible"
|
||||||
|
when: root.visibilities.settings
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
root.implicitHeight: content.implicitHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: ""
|
||||||
|
to: "visible"
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: MaterialEasing.expressiveEffectsTime
|
||||||
|
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||||
|
property: "implicitHeight"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
from: "visible"
|
||||||
|
to: ""
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||||
|
property: "implicitHeight"
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
active: true
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
sourceComponent: Content {
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
-16
@@ -4,8 +4,10 @@ import QtQuick
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
Row {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property PanelWindow bar
|
required property PanelWindow bar
|
||||||
@@ -15,26 +17,43 @@ Row {
|
|||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 0
|
implicitHeight: 34
|
||||||
|
implicitWidth: row.width + Appearance.padding.small * 2
|
||||||
|
|
||||||
Repeater {
|
CustomClippingRect {
|
||||||
id: repeater
|
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
|
||||||
|
|
||||||
model: SystemTray.items
|
Row {
|
||||||
|
id: row
|
||||||
|
|
||||||
TrayItem {
|
anchors.centerIn: parent
|
||||||
id: trayItem
|
spacing: 0
|
||||||
|
|
||||||
required property int index
|
Repeater {
|
||||||
required property SystemTrayItem modelData
|
id: repeater
|
||||||
|
|
||||||
bar: root.bar
|
model: SystemTray.items
|
||||||
implicitHeight: 34
|
|
||||||
implicitWidth: 34
|
TrayItem {
|
||||||
ind: index
|
id: trayItem
|
||||||
item: modelData
|
|
||||||
loader: root.loader
|
required property int index
|
||||||
popouts: root.popouts
|
required property SystemTrayItem modelData
|
||||||
|
|
||||||
|
bar: root.bar
|
||||||
|
implicitHeight: 34
|
||||||
|
implicitWidth: 34
|
||||||
|
ind: index
|
||||||
|
item: modelData
|
||||||
|
loader: root.loader
|
||||||
|
popouts: root.popouts
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: 22
|
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-16
@@ -11,17 +11,19 @@ Item {
|
|||||||
required property var bar
|
required property var bar
|
||||||
property color colour: DynamicColors.palette.m3primary
|
property color colour: DynamicColors.palette.m3primary
|
||||||
property Title current: text1
|
property Title current: text1
|
||||||
readonly property int maxHeight: {
|
// readonly property int maxWidth: 300
|
||||||
const otherModules = bar.children.filter(c => c.id && c.item !== this && c.id !== "spacer");
|
readonly property int maxWidth: {
|
||||||
const otherHeight = otherModules.reduce((acc, curr) => acc + (curr.item.nonAnimHeight ?? curr.height), 0);
|
const otherModules = bar.children.filter(c => c.enabled && c.id && c.item !== this && c.id !== "spacer");
|
||||||
// Length - 2 cause repeater counts as a child
|
const otherWidth = otherModules.reduce((acc, curr) => {
|
||||||
return bar.height - otherHeight - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2;
|
return acc + (curr.item?.nonAnimWidth ?? curr.width ?? 0);
|
||||||
|
}, 0);
|
||||||
|
return bar.width - otherWidth - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2;
|
||||||
}
|
}
|
||||||
required property Brightness.Monitor monitor
|
required property Brightness.Monitor monitor
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
implicitHeight: current.implicitHeight
|
implicitHeight: current.implicitHeight
|
||||||
implicitWidth: current.implicitWidth + current.anchors.leftMargin
|
implicitWidth: Math.min(current.implicitWidth, root.maxWidth)
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
Anim {
|
Anim {
|
||||||
@@ -30,16 +32,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaterialIcon {
|
|
||||||
// id: icon
|
|
||||||
//
|
|
||||||
// anchors.verticalCenter: parent.verticalCenter
|
|
||||||
//
|
|
||||||
// animate: true
|
|
||||||
// text: Icons.getAppCategoryIcon(Hypr.activeToplevel?.lastIpcObject.class, "desktop_windows")
|
|
||||||
// color: root.colour
|
|
||||||
// }
|
|
||||||
|
|
||||||
Title {
|
Title {
|
||||||
id: text1
|
id: text1
|
||||||
|
|
||||||
@@ -53,6 +45,8 @@ Item {
|
|||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: metrics
|
id: metrics
|
||||||
|
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
elideWidth: root.maxWidth
|
||||||
font.family: "Rubik"
|
font.family: "Rubik"
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
text: Hypr.activeToplevel?.title ?? qsTr("Desktop")
|
text: Hypr.activeToplevel?.title ?? qsTr("Desktop")
|
||||||
|
|||||||
+148
-79
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
@@ -10,119 +9,189 @@ import qs.Config
|
|||||||
import qs.Components
|
import qs.Components
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: itemRoot
|
id: root
|
||||||
|
|
||||||
|
property real activeWorkspaceMargin: Math.ceil(Appearance.padding.small / 2)
|
||||||
required property PanelWindow bar
|
required property PanelWindow bar
|
||||||
|
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
|
||||||
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.bar.screen)
|
||||||
|
property int workspaceButtonWidth: bgRect.implicitHeight - root.activeWorkspaceMargin * 2
|
||||||
|
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
|
||||||
|
readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)
|
||||||
|
readonly property int workspacesShown: workspaces.length
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
implicitWidth: workspacesRow.implicitWidth + 10
|
implicitWidth: (root.workspaceButtonWidth * root.workspacesShown) + root.activeWorkspaceMargin * 2
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
NumberAnimation {
|
Anim {
|
||||||
duration: MaterialEasing.expressiveEffectsTime
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
CustomRect {
|
||||||
id: root
|
id: bgRect
|
||||||
|
|
||||||
property HyprlandMonitor monitor: Hyprland.monitorFor(itemRoot.bar?.screen)
|
|
||||||
|
|
||||||
function shouldShow(monitor) {
|
|
||||||
Hyprland.refreshWorkspaces();
|
|
||||||
Hyprland.refreshMonitors();
|
|
||||||
if (monitor === root.monitor) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: 22
|
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
|
|
||||||
Behavior on color {
|
CustomRect {
|
||||||
CAnim {
|
id: indicator
|
||||||
|
|
||||||
|
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth
|
||||||
|
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
|
||||||
|
property real indicatorThickness: root.workspaceButtonWidth
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
implicitHeight: indicatorThickness
|
||||||
|
implicitWidth: indicatorLength
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
x: indicatorPosition
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
AnimatedTabIndexPair {
|
||||||
|
id: idxPair
|
||||||
|
|
||||||
|
index: root.workspaces.findIndex(w => w.active)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
Grid {
|
||||||
id: workspacesRow
|
anchors.fill: parent
|
||||||
|
anchors.margins: root.activeWorkspaceMargin
|
||||||
anchors.left: parent.left
|
columnSpacing: 0
|
||||||
anchors.leftMargin: 6
|
columns: root.workspacesShown
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
rowSpacing: 0
|
||||||
spacing: 8
|
rows: 1
|
||||||
|
z: 3
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Hyprland.workspaces
|
model: root.workspaces
|
||||||
|
|
||||||
RowLayout {
|
Button {
|
||||||
id: workspaceIndicator
|
id: button
|
||||||
|
|
||||||
required property var modelData
|
required property int index
|
||||||
|
required property HyprlandWorkspace modelData
|
||||||
|
|
||||||
visible: root.shouldShow(workspaceIndicator.modelData.monitor)
|
implicitHeight: indicator.indicatorThickness
|
||||||
|
implicitWidth: indicator.indicatorThickness
|
||||||
|
width: root.workspaceButtonWidth
|
||||||
|
|
||||||
CustomText {
|
background: Item {
|
||||||
color: workspaceIndicator.modelData.id === Hyprland.focusedWorkspace.id ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
id: workspaceButtonBackground
|
||||||
font.pointSize: 12
|
|
||||||
text: workspaceIndicator.modelData.name
|
implicitHeight: root.workspaceButtonWidth
|
||||||
visible: true
|
implicitWidth: root.workspaceButtonWidth
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: DynamicColors.palette.m3onSecondaryContainer
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: button.modelData.name
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
z: 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
onPressed: {
|
||||||
border.color: workspaceIndicator.modelData.id === Hyprland.focusedWorkspace.id ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
Hyprland.dispatch(`workspace ${button.modelData.name}`);
|
||||||
border.width: 1
|
}
|
||||||
color: "transparent"
|
}
|
||||||
implicitHeight: 14
|
}
|
||||||
implicitWidth: 14
|
}
|
||||||
opacity: 1.0
|
|
||||||
radius: height / 2
|
|
||||||
scale: 1.0
|
|
||||||
|
|
||||||
Behavior on border.color {
|
Item {
|
||||||
ColorAnimation {
|
id: activeTextSource
|
||||||
duration: 150
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NumberAnimation on opacity {
|
|
||||||
duration: 200
|
|
||||||
from: 0.0
|
|
||||||
to: 1.0
|
|
||||||
}
|
|
||||||
NumberAnimation on scale {
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.OutBack
|
|
||||||
from: 0.0
|
|
||||||
to: 1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
anchors.fill: parent
|
||||||
|
anchors.margins: root.activeWorkspaceMargin
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
z: 4
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
anchors.fill: parent
|
||||||
|
columnSpacing: 0
|
||||||
|
columns: root.workspacesShown
|
||||||
|
rowSpacing: 0
|
||||||
|
rows: 1
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.workspaces
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: activeWorkspace
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property HyprlandWorkspace modelData
|
||||||
|
|
||||||
|
implicitHeight: indicator.indicatorThickness
|
||||||
|
implicitWidth: indicator.indicatorThickness
|
||||||
|
width: root.workspaceButtonWidth
|
||||||
|
|
||||||
|
CustomText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: workspaceIndicator.modelData.id === Hyprland.focusedWorkspace.id ? DynamicColors.palette.m3primary : "transparent"
|
color: DynamicColors.palette.m3onPrimary
|
||||||
implicitHeight: 8
|
elide: Text.ElideRight
|
||||||
implicitWidth: 8
|
horizontalAlignment: Text.AlignHCenter
|
||||||
radius: implicitHeight / 2
|
text: activeWorkspace.modelData.name
|
||||||
}
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
workspaceIndicator.modelData.activate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShaderEffectSource {
|
||||||
|
id: activeTextTex
|
||||||
|
|
||||||
|
anchors.fill: bgRect
|
||||||
|
anchors.margins: root.activeWorkspaceMargin
|
||||||
|
hideSource: true
|
||||||
|
live: true
|
||||||
|
recursive: true
|
||||||
|
sourceItem: activeTextSource
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: indicatorMask
|
||||||
|
|
||||||
|
anchors.fill: bgRect
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
color: "white"
|
||||||
|
height: indicator.height
|
||||||
|
radius: indicator.radius
|
||||||
|
width: indicator.width
|
||||||
|
x: indicator.x
|
||||||
|
y: indicator.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffectSource {
|
||||||
|
id: indicatorMaskEffect
|
||||||
|
|
||||||
|
anchors.fill: activeTextSource
|
||||||
|
live: true
|
||||||
|
sourceItem: indicatorMask
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: activeTextSource
|
||||||
|
maskEnabled: true
|
||||||
|
maskInverted: false
|
||||||
|
maskSource: indicatorMaskEffect
|
||||||
|
source: activeTextTex
|
||||||
|
z: 5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,13 @@ qml_module(ZShell
|
|||||||
imageanalyser.hpp imageanalyser.cpp
|
imageanalyser.hpp imageanalyser.cpp
|
||||||
requests.hpp requests.cpp
|
requests.hpp requests.cpp
|
||||||
toaster.hpp toaster.cpp
|
toaster.hpp toaster.cpp
|
||||||
|
qalculator.hpp qalculator.cpp
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
Qt::Gui
|
Qt::Gui
|
||||||
Qt::Quick
|
Qt::Quick
|
||||||
Qt::Concurrent
|
Qt::Concurrent
|
||||||
Qt::Sql
|
Qt::Sql
|
||||||
|
PkgConfig::Qalculate
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(Models)
|
add_subdirectory(Models)
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#include "qalculator.hpp"
|
||||||
|
|
||||||
|
#include <libqalculate/qalculate.h>
|
||||||
|
|
||||||
|
namespace ZShell {
|
||||||
|
|
||||||
|
Qalculator::Qalculator(QObject* parent)
|
||||||
|
: QObject(parent) {
|
||||||
|
if (!CALCULATOR) {
|
||||||
|
new Calculator();
|
||||||
|
CALCULATOR->loadExchangeRates();
|
||||||
|
CALCULATOR->loadGlobalDefinitions();
|
||||||
|
CALCULATOR->loadLocalDefinitions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Qalculator::eval(const QString& expr, bool printExpr) const {
|
||||||
|
if (expr.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
EvaluationOptions eo;
|
||||||
|
PrintOptions po;
|
||||||
|
|
||||||
|
std::string parsed;
|
||||||
|
std::string result = CALCULATOR->calculateAndPrint(
|
||||||
|
CALCULATOR->unlocalizeExpression(expr.toStdString(), eo.parse_options), 100, eo, po, &parsed);
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
while (CALCULATOR->message()) {
|
||||||
|
if (!CALCULATOR->message()->message().empty()) {
|
||||||
|
if (CALCULATOR->message()->type() == MESSAGE_ERROR) {
|
||||||
|
error += "error: ";
|
||||||
|
} else if (CALCULATOR->message()->type() == MESSAGE_WARNING) {
|
||||||
|
error += "warning: ";
|
||||||
|
}
|
||||||
|
error += CALCULATOR->message()->message();
|
||||||
|
}
|
||||||
|
CALCULATOR->nextMessage();
|
||||||
|
}
|
||||||
|
if (!error.empty()) {
|
||||||
|
return QString::fromStdString(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (printExpr) {
|
||||||
|
return QString("%1 = %2").arg(parsed).arg(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString::fromStdString(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZShell
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
|
||||||
|
namespace ZShell {
|
||||||
|
|
||||||
|
class Qalculator : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Qalculator(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString eval(const QString& expr, bool printExpr = true) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ZShell
|
||||||
@@ -43,6 +43,8 @@ This installs the QML plugin to `/usr/lib/qt6/qml`.
|
|||||||
|
|
||||||
### NixOS
|
### NixOS
|
||||||
|
|
||||||
|
**Note that not all features work well. This is due to limited testing on NixOS.**
|
||||||
|
|
||||||
In your flake.nix file, add the following in your inputs.
|
In your flake.nix file, add the following in your inputs.
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
from typing import Annotated, Optional
|
|
||||||
import typer
|
import typer
|
||||||
import json
|
import json
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
|
||||||
|
from jinja2 import Environment, FileSystemLoader, StrictUndefined, Undefined
|
||||||
|
from typing import Any, Optional, Tuple
|
||||||
from zshell.utils.schemepalettes import PRESETS
|
from zshell.utils.schemepalettes import PRESETS
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -9,6 +12,8 @@ from materialyoucolor.quantize import QuantizeCelebi
|
|||||||
from materialyoucolor.score.score import Score
|
from materialyoucolor.score.score import Score
|
||||||
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
||||||
from materialyoucolor.hct.hct import Hct
|
from materialyoucolor.hct.hct import Hct
|
||||||
|
from materialyoucolor.utils.color_utils import argb_from_rgb
|
||||||
|
from materialyoucolor.utils.math_utils import difference_degrees, rotation_direction, sanitize_degrees_double
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
@@ -18,47 +23,179 @@ def generate(
|
|||||||
# image inputs (optional - used for image mode)
|
# image inputs (optional - used for image mode)
|
||||||
image_path: Optional[Path] = typer.Option(
|
image_path: Optional[Path] = typer.Option(
|
||||||
None, help="Path to source image. Required for image mode."),
|
None, help="Path to source image. Required for image mode."),
|
||||||
thumbnail_path: Optional[Path] = typer.Option(
|
|
||||||
Path("thumb.jpg"), help="Path to temporary thumbnail (image mode)."),
|
|
||||||
scheme: Optional[str] = typer.Option(
|
scheme: Optional[str] = typer.Option(
|
||||||
"fruit-salad", help="Color scheme algorithm to use for image mode. Ignored in preset mode."),
|
None, help="Color scheme algorithm to use for image mode. Ignored in preset mode."),
|
||||||
# preset inputs (optional - used for preset mode)
|
# preset inputs (optional - used for preset mode)
|
||||||
preset: Optional[str] = typer.Option(
|
preset: Optional[str] = typer.Option(
|
||||||
None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"),
|
None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"),
|
||||||
mode: str = typer.Option(
|
mode: Optional[str] = typer.Option(
|
||||||
"dark", help="Mode of the preset scheme (dark or light)."),
|
None, help="Mode of the preset scheme (dark or light)."),
|
||||||
# output (required)
|
|
||||||
output: Path = typer.Option(..., help="Output JSON path.")
|
|
||||||
):
|
):
|
||||||
if preset is None and image_path is None:
|
|
||||||
raise typer.BadParameter(
|
HOME = str(os.getenv("HOME"))
|
||||||
"Either --image-path or --preset must be provided.")
|
OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json")
|
||||||
|
SEQ_STATE = Path(HOME + "/.local/state/zshell/sequences.txt")
|
||||||
|
THUMB_PATH = Path(HOME +
|
||||||
|
"/.cache/zshell/imagecache/thumbnail.jpg")
|
||||||
|
WALL_DIR_PATH = Path(HOME +
|
||||||
|
"/.local/state/zshell/wallpaper_path.json")
|
||||||
|
|
||||||
|
TEMPLATE_DIR = Path(HOME + "/.config/zshell/templates")
|
||||||
|
WALL_PATH = Path()
|
||||||
|
CONFIG = Path(HOME + "/.config/zshell/config.json")
|
||||||
|
|
||||||
if preset is not None and image_path is not None:
|
if preset is not None and image_path is not None:
|
||||||
raise typer.BadParameter(
|
raise typer.BadParameter(
|
||||||
"Use either --image-path or --preset, not both.")
|
"Use either --image-path or --preset, not both.")
|
||||||
|
|
||||||
match scheme:
|
def get_scheme_class(scheme_name: str):
|
||||||
case "fruit-salad":
|
match scheme_name:
|
||||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme
|
case "fruit-salad":
|
||||||
case 'expressive':
|
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
||||||
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive as Scheme
|
return SchemeFruitSalad
|
||||||
case 'monochrome':
|
case "expressive":
|
||||||
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome as Scheme
|
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
|
||||||
case 'rainbow':
|
return SchemeExpressive
|
||||||
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow as Scheme
|
case "monochrome":
|
||||||
case 'tonal-spot':
|
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
|
||||||
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot as Scheme
|
return SchemeMonochrome
|
||||||
case 'neutral':
|
case "rainbow":
|
||||||
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral as Scheme
|
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
|
||||||
case 'fidelity':
|
return SchemeRainbow
|
||||||
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity as Scheme
|
case "tonal-spot":
|
||||||
case 'content':
|
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
||||||
from materialyoucolor.scheme.scheme_content import SchemeContent as Scheme
|
return SchemeTonalSpot
|
||||||
case 'vibrant':
|
case "neutral":
|
||||||
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant as Scheme
|
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
|
||||||
case _:
|
return SchemeNeutral
|
||||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme
|
case "fidelity":
|
||||||
|
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity
|
||||||
|
return SchemeFidelity
|
||||||
|
case "content":
|
||||||
|
from materialyoucolor.scheme.scheme_content import SchemeContent
|
||||||
|
return SchemeContent
|
||||||
|
case "vibrant":
|
||||||
|
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant
|
||||||
|
return SchemeVibrant
|
||||||
|
case _:
|
||||||
|
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
||||||
|
return SchemeFruitSalad
|
||||||
|
|
||||||
|
def hex_to_hct(hex_color: str) -> Hct:
|
||||||
|
s = hex_color.strip()
|
||||||
|
if s.startswith("#"):
|
||||||
|
s = s[1:]
|
||||||
|
if len(s) != 6:
|
||||||
|
raise ValueError(f"Expected 6-digit hex color, got: {hex_color!r}")
|
||||||
|
return Hct.from_int(int("0xFF" + s, 16))
|
||||||
|
|
||||||
|
LIGHT_GRUVBOX = list(
|
||||||
|
map(
|
||||||
|
hex_to_hct,
|
||||||
|
[
|
||||||
|
"FDF9F3",
|
||||||
|
"FF6188",
|
||||||
|
"A9DC76",
|
||||||
|
"FC9867",
|
||||||
|
"FFD866",
|
||||||
|
"F47FD4",
|
||||||
|
"78DCE8",
|
||||||
|
"333034",
|
||||||
|
"121212",
|
||||||
|
"FF6188",
|
||||||
|
"A9DC76",
|
||||||
|
"FC9867",
|
||||||
|
"FFD866",
|
||||||
|
"F47FD4",
|
||||||
|
"78DCE8",
|
||||||
|
"333034",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
DARK_GRUVBOX = list(
|
||||||
|
map(
|
||||||
|
hex_to_hct,
|
||||||
|
[
|
||||||
|
"282828",
|
||||||
|
"CC241D",
|
||||||
|
"98971A",
|
||||||
|
"D79921",
|
||||||
|
"458588",
|
||||||
|
"B16286",
|
||||||
|
"689D6A",
|
||||||
|
"A89984",
|
||||||
|
"928374",
|
||||||
|
"FB4934",
|
||||||
|
"B8BB26",
|
||||||
|
"FABD2F",
|
||||||
|
"83A598",
|
||||||
|
"D3869B",
|
||||||
|
"8EC07C",
|
||||||
|
"EBDBB2",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
with WALL_DIR_PATH.open() as f:
|
||||||
|
path = json.load(f)["currentWallpaperPath"]
|
||||||
|
WALL_PATH = path
|
||||||
|
|
||||||
|
def lighten(color: Hct, amount: float) -> Hct:
|
||||||
|
diff = (100 - color.tone) * amount
|
||||||
|
tone = max(0.0, min(100.0, color.tone + diff))
|
||||||
|
chroma = max(0.0, color.chroma + diff / 5)
|
||||||
|
return Hct.from_hct(color.hue, chroma, tone)
|
||||||
|
|
||||||
|
def darken(color: Hct, amount: float) -> Hct:
|
||||||
|
diff = color.tone * amount
|
||||||
|
tone = max(0.0, min(100.0, color.tone - diff))
|
||||||
|
chroma = max(0.0, color.chroma - diff / 5)
|
||||||
|
return Hct.from_hct(color.hue, chroma, tone)
|
||||||
|
|
||||||
|
def grayscale(color: Hct, light: bool) -> Hct:
|
||||||
|
color = darken(color, 0.35) if light else lighten(color, 0.65)
|
||||||
|
color.chroma = 0
|
||||||
|
return color
|
||||||
|
|
||||||
|
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
|
||||||
|
diff = difference_degrees(from_hct.hue, to_hct.hue)
|
||||||
|
rotation = min(diff * 0.8, 100)
|
||||||
|
output_hue = sanitize_degrees_double(
|
||||||
|
from_hct.hue
|
||||||
|
+ rotation * rotation_direction(from_hct.hue, to_hct.hue)
|
||||||
|
)
|
||||||
|
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
|
||||||
|
return Hct.from_hct(output_hue, from_hct.chroma, tone)
|
||||||
|
|
||||||
|
def terminal_palette(
|
||||||
|
colors: dict[str, str], mode: str, variant: str
|
||||||
|
) -> dict[str, str]:
|
||||||
|
light = mode.lower() == "light"
|
||||||
|
|
||||||
|
key_hex = (
|
||||||
|
colors.get("primary_paletteKeyColor")
|
||||||
|
or colors.get("primaryPaletteKeyColor")
|
||||||
|
or colors.get("primary")
|
||||||
|
or int_to_hex(seed.to_int())
|
||||||
|
)
|
||||||
|
key_hct = hex_to_hct(key_hex)
|
||||||
|
|
||||||
|
base = LIGHT_GRUVBOX if light else DARK_GRUVBOX
|
||||||
|
out: dict[str, str] = {}
|
||||||
|
|
||||||
|
is_mono = variant.lower() == "monochrome"
|
||||||
|
|
||||||
|
for i, base_hct in enumerate(base):
|
||||||
|
if is_mono:
|
||||||
|
h = grayscale(base_hct, light)
|
||||||
|
else:
|
||||||
|
tone_boost = (0.35 if i < 8 else 0.2) * (-1 if light else 1)
|
||||||
|
h = harmonize(base_hct, key_hct, tone_boost)
|
||||||
|
|
||||||
|
out[f"term{i}"] = int_to_hex(h.to_int())
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
def generate_thumbnail(image_path, thumbnail_path, size=(128, 128)):
|
def generate_thumbnail(image_path, thumbnail_path, size=(128, 128)):
|
||||||
thumbnail_file = Path(thumbnail_path)
|
thumbnail_file = Path(thumbnail_path)
|
||||||
@@ -70,6 +207,170 @@ def generate(
|
|||||||
thumbnail_file.parent.mkdir(parents=True, exist_ok=True)
|
thumbnail_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
image.save(thumbnail_path, "JPEG")
|
image.save(thumbnail_path, "JPEG")
|
||||||
|
|
||||||
|
def apply_terms(sequences: str, sequences_tmux: str, state_path: Path) -> None:
|
||||||
|
state_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
state_path.write_text(sequences, encoding="utf-8")
|
||||||
|
|
||||||
|
pts_path = Path("/dev/pts")
|
||||||
|
if not pts_path.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
O_NOCTTY = getattr(os, "O_NOCTTY", 0)
|
||||||
|
|
||||||
|
for pt in pts_path.iterdir():
|
||||||
|
if not pt.name.isdigit():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
fd = os.open(str(pt), os.O_WRONLY | os.O_NONBLOCK | O_NOCTTY)
|
||||||
|
try:
|
||||||
|
os.write(fd, sequences_tmux.encode())
|
||||||
|
os.write(fd, sequences.encode())
|
||||||
|
finally:
|
||||||
|
os.close(fd)
|
||||||
|
except (PermissionError, OSError, BlockingIOError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def smart_mode(image_path: Path) -> str:
|
||||||
|
is_dark = ""
|
||||||
|
|
||||||
|
with Image.open(image_path) as img:
|
||||||
|
img.thumbnail((1, 1), Image.LANCZOS)
|
||||||
|
hct = Hct.from_int(argb_from_rgb(*img.getpixel((0, 0))))
|
||||||
|
is_dark = "light" if hct.tone > 50 else "dark"
|
||||||
|
|
||||||
|
return is_dark
|
||||||
|
|
||||||
|
def build_template_context(
|
||||||
|
*,
|
||||||
|
colors: dict[str, str],
|
||||||
|
seed: Hct,
|
||||||
|
mode: str,
|
||||||
|
wallpaper_path: str,
|
||||||
|
name: str,
|
||||||
|
flavor: str,
|
||||||
|
variant: str,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
ctx: dict[str, Any] = {
|
||||||
|
"mode": mode,
|
||||||
|
"wallpaper_path": wallpaper_path,
|
||||||
|
"source_color": int_to_hex(seed.to_int()),
|
||||||
|
"name": name,
|
||||||
|
"seed": seed.to_int(),
|
||||||
|
"flavor": flavor,
|
||||||
|
"variant": variant,
|
||||||
|
"colors": colors
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v in colors.items():
|
||||||
|
ctx[k] = v
|
||||||
|
ctx[f"m3{k}"] = v
|
||||||
|
|
||||||
|
term = terminal_palette(colors, mode, variant)
|
||||||
|
ctx.update(term)
|
||||||
|
ctx["term"] = [term[f"term{i}"] for i in range(16)]
|
||||||
|
|
||||||
|
seq = make_sequences(
|
||||||
|
term=term,
|
||||||
|
foreground=ctx["m3onSurface"],
|
||||||
|
background=ctx["m3surface"],
|
||||||
|
)
|
||||||
|
ctx["sequences"] = seq
|
||||||
|
ctx["sequences_tmux"] = tmux_wrap_sequences(seq)
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
def make_sequences(
|
||||||
|
*,
|
||||||
|
term: dict[str, str],
|
||||||
|
foreground: str,
|
||||||
|
background: str,
|
||||||
|
) -> str:
|
||||||
|
ESC = "\x1b"
|
||||||
|
ST = ESC + "\\"
|
||||||
|
|
||||||
|
parts: list[str] = []
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
parts.append(f"{ESC}]4;{i};{term[f'term{i}']}{ST}")
|
||||||
|
|
||||||
|
parts.append(f"{ESC}]10;{foreground}{ST}")
|
||||||
|
parts.append(f"{ESC}]11;{background}{ST}")
|
||||||
|
|
||||||
|
return "".join(parts)
|
||||||
|
|
||||||
|
def tmux_wrap_sequences(seq: str) -> str:
|
||||||
|
ESC = "\x1b"
|
||||||
|
return f"{ESC}Ptmux;{seq.replace(ESC, ESC+ESC)}{ESC}\\"
|
||||||
|
|
||||||
|
def parse_output_directive(first_line: str) -> Optional[Path]:
|
||||||
|
s = first_line.strip()
|
||||||
|
if not s.startswith("#") or s.startswith("#!"):
|
||||||
|
return None
|
||||||
|
|
||||||
|
target = s[1:].strip()
|
||||||
|
if not target:
|
||||||
|
return None
|
||||||
|
|
||||||
|
expanded = os.path.expandvars(os.path.expanduser(target))
|
||||||
|
return Path(expanded)
|
||||||
|
|
||||||
|
def split_directive_and_body(text: str) -> Tuple[Optional[Path], str]:
|
||||||
|
lines = text.splitlines(keepends=True)
|
||||||
|
if not lines:
|
||||||
|
return None, ""
|
||||||
|
|
||||||
|
out_path = parse_output_directive(lines[0])
|
||||||
|
if out_path is None:
|
||||||
|
return None, text
|
||||||
|
|
||||||
|
body = "".join(lines[1:])
|
||||||
|
return out_path, body
|
||||||
|
|
||||||
|
def render_all_templates(
|
||||||
|
templates_dir: Path,
|
||||||
|
context: dict[str, object],
|
||||||
|
*,
|
||||||
|
strict: bool = True,
|
||||||
|
) -> list[Path]:
|
||||||
|
undefined_cls = StrictUndefined if strict else Undefined
|
||||||
|
env = Environment(
|
||||||
|
loader=FileSystemLoader(str(templates_dir)),
|
||||||
|
autoescape=False,
|
||||||
|
keep_trailing_newline=True,
|
||||||
|
undefined=undefined_cls,
|
||||||
|
)
|
||||||
|
|
||||||
|
rendered_outputs: list[Path] = []
|
||||||
|
|
||||||
|
for tpl_path in sorted(p for p in templates_dir.rglob("*") if p.is_file()):
|
||||||
|
rel = tpl_path.relative_to(templates_dir)
|
||||||
|
|
||||||
|
if any(part.startswith(".") for part in rel.parts):
|
||||||
|
continue
|
||||||
|
|
||||||
|
raw = tpl_path.read_text(encoding="utf-8")
|
||||||
|
out_path, body = split_directive_and_body(raw)
|
||||||
|
|
||||||
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
template = env.from_string(body)
|
||||||
|
text = template.render(**context)
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Template render failed for '{rel}': {e}") from e
|
||||||
|
|
||||||
|
out_path.write_text(text, encoding="utf-8")
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.copymode(tpl_path, out_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
rendered_outputs.append(out_path)
|
||||||
|
|
||||||
|
return rendered_outputs
|
||||||
|
|
||||||
def seed_from_image(image_path: Path) -> Hct:
|
def seed_from_image(image_path: Path) -> Hct:
|
||||||
image = Image.open(image_path)
|
image = Image.open(image_path)
|
||||||
pixel_len = image.width * image.height
|
pixel_len = image.width * image.height
|
||||||
@@ -88,11 +389,11 @@ def generate(
|
|||||||
raise typer.BadParameter(
|
raise typer.BadParameter(
|
||||||
f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}")
|
f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}")
|
||||||
|
|
||||||
def generate_color_scheme(seed: Hct, mode: str) -> dict[str, str]:
|
def generate_color_scheme(seed: Hct, mode: str, scheme_class) -> dict[str, str]:
|
||||||
|
|
||||||
is_dark = mode.lower() == "dark"
|
is_dark = mode.lower() == "dark"
|
||||||
|
|
||||||
scheme = Scheme(
|
scheme = scheme_class(
|
||||||
seed,
|
seed,
|
||||||
is_dark,
|
is_dark,
|
||||||
0.0
|
0.0
|
||||||
@@ -111,27 +412,67 @@ def generate(
|
|||||||
return "#{:06X}".format(argb_int & 0xFFFFFF)
|
return "#{:06X}".format(argb_int & 0xFFFFFF)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
with CONFIG.open() as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
scheme = scheme or config["colors"]["schemeType"]
|
||||||
|
config_mode = config["general"]["color"]["mode"]
|
||||||
|
smart = bool(config["general"]["color"].get("smart", False))
|
||||||
|
scheme_class = get_scheme_class(scheme)
|
||||||
|
|
||||||
if preset:
|
if preset:
|
||||||
seed = seed_from_preset(preset)
|
seed = seed_from_preset(preset)
|
||||||
colors = generate_color_scheme(seed, mode)
|
effective_mode = mode or config_mode
|
||||||
name, flavor = preset.split(":")
|
name, flavor = preset.split(":")
|
||||||
else:
|
else:
|
||||||
generate_thumbnail(image_path, str(thumbnail_path))
|
image_path = image_path or Path(WALL_PATH)
|
||||||
seed = seed_from_image(thumbnail_path)
|
generate_thumbnail(image_path, str(THUMB_PATH))
|
||||||
colors = generate_color_scheme(seed, mode)
|
seed = seed_from_image(THUMB_PATH)
|
||||||
name = "dynamic"
|
name = "dynamic"
|
||||||
flavor = "default"
|
flavor = "default"
|
||||||
|
|
||||||
|
if smart:
|
||||||
|
effective_mode = smart_mode(THUMB_PATH)
|
||||||
|
elif mode is not None:
|
||||||
|
effective_mode = mode
|
||||||
|
else:
|
||||||
|
effective_mode = config_mode
|
||||||
|
|
||||||
|
colors = generate_color_scheme(seed, effective_mode, scheme_class)
|
||||||
|
|
||||||
output_dict = {
|
output_dict = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"flavor": flavor,
|
"flavor": flavor,
|
||||||
"mode": mode,
|
"mode": effective_mode,
|
||||||
"variant": scheme,
|
"variant": scheme,
|
||||||
"colors": colors
|
"colors": colors,
|
||||||
|
"seed": seed.to_int()
|
||||||
}
|
}
|
||||||
|
|
||||||
output.parent.mkdir(parents=True, exist_ok=True)
|
if TEMPLATE_DIR is not None:
|
||||||
with open(output, "w") as f:
|
wp = str(WALL_PATH)
|
||||||
|
ctx = build_template_context(
|
||||||
|
colors=colors,
|
||||||
|
seed=seed,
|
||||||
|
mode=effective_mode,
|
||||||
|
wallpaper_path=wp,
|
||||||
|
name=name,
|
||||||
|
flavor=flavor,
|
||||||
|
variant=scheme
|
||||||
|
)
|
||||||
|
|
||||||
|
rendered = render_all_templates(
|
||||||
|
templates_dir=TEMPLATE_DIR,
|
||||||
|
context=ctx,
|
||||||
|
)
|
||||||
|
|
||||||
|
apply_terms(ctx["sequences"], ctx["sequences_tmux"], SEQ_STATE)
|
||||||
|
|
||||||
|
for p in rendered:
|
||||||
|
print(f"rendered: {p}")
|
||||||
|
|
||||||
|
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(OUTPUT, "w") as f:
|
||||||
json.dump(output_dict, f, indent=4)
|
json.dump(output_dict, f, indent=4)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error: {e}")
|
print(f"Error: {e}")
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
pkgs, # To ensure the nixpkgs version of app2unit
|
pkgs,
|
||||||
fetchFromGitHub,
|
fetchFromGitHub,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
pkgs.app2unit.overrideAttrs (
|
pkgs.app2unit.overrideAttrs (
|
||||||
final: prev: rec {
|
final: prev: rec {
|
||||||
version = "1.0.3"; # Fix old issue related to missing env var
|
version = "1.0.3";
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "Vladimir-csp";
|
owner = "Vladimir-csp";
|
||||||
repo = "app2unit";
|
repo = "app2unit";
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
pkg-config,
|
pkg-config,
|
||||||
pythonEnv,
|
pythonEnv,
|
||||||
zshell-cli,
|
zshell-cli,
|
||||||
|
ddcutil,
|
||||||
|
brightnessctl,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
version = "1.0.0";
|
version = "1.0.0";
|
||||||
@@ -39,6 +41,8 @@ let
|
|||||||
bash
|
bash
|
||||||
hyprland
|
hyprland
|
||||||
zshell-cli
|
zshell-cli
|
||||||
|
ddcutil
|
||||||
|
brightnessctl
|
||||||
];
|
];
|
||||||
|
|
||||||
fontconfig = makeFontsConf {
|
fontconfig = makeFontsConf {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ python3.pkgs.buildPythonApplication {
|
|||||||
dependencies = with python3.pkgs; [
|
dependencies = with python3.pkgs; [
|
||||||
materialyoucolor
|
materialyoucolor
|
||||||
pillow
|
pillow
|
||||||
|
jinja2
|
||||||
typer
|
typer
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user