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/
|
||||
.cache/
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -13,11 +13,11 @@ Elevation {
|
||||
|
||||
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)
|
||||
level: 2
|
||||
opacity: root.expanded ? 1 : 0
|
||||
radius: Appearance.rounding.small / 2
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
@@ -41,7 +41,8 @@ Elevation {
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 5
|
||||
|
||||
Repeater {
|
||||
model: root.items
|
||||
@@ -54,10 +55,16 @@ Elevation {
|
||||
required property MenuItem modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
|
||||
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.small
|
||||
anchors.rightMargin: Appearance.padding.small
|
||||
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
|
||||
radius: Appearance.rounding.normal - Appearance.padding.small
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.itemSelected(item.modelData);
|
||||
@@ -68,6 +75,7 @@ Elevation {
|
||||
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
disabled: !root.expanded
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: menuOptionRow
|
||||
|
||||
+17
-1
@@ -15,6 +15,7 @@ Singleton {
|
||||
property alias barConfig: adapter.barConfig
|
||||
property alias colors: adapter.colors
|
||||
property alias dashboard: adapter.dashboard
|
||||
property alias dock: adapter.dock
|
||||
property alias general: adapter.general
|
||||
property alias launcher: adapter.launcher
|
||||
property alias lock: adapter.lock
|
||||
@@ -113,7 +114,8 @@ Singleton {
|
||||
osd: serializeOsd(),
|
||||
background: serializeBackground(),
|
||||
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 {
|
||||
return {
|
||||
logo: general.logo,
|
||||
@@ -156,6 +169,7 @@ Singleton {
|
||||
color: {
|
||||
wallust: general.color.wallust,
|
||||
mode: general.color.mode,
|
||||
smart: general.color.smart,
|
||||
schemeGeneration: general.color.schemeGeneration,
|
||||
scheduleDarkStart: general.color.scheduleDarkStart,
|
||||
scheduleDarkEnd: general.color.scheduleDarkEnd,
|
||||
@@ -381,6 +395,8 @@ Singleton {
|
||||
}
|
||||
property DashboardConfig dashboard: DashboardConfig {
|
||||
}
|
||||
property DockConfig dock: DockConfig {
|
||||
}
|
||||
property General general: General {
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
function setMode(mode: string): void {
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
|
||||
Config.general.color.mode = mode;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
FileView {
|
||||
path: `${Paths.state}/scheme.json`
|
||||
watchChanges: true
|
||||
|
||||
@@ -23,6 +23,7 @@ JsonObject {
|
||||
property int scheduleDarkEnd: 0
|
||||
property int scheduleDarkStart: 0
|
||||
property bool schemeGeneration: true
|
||||
property bool smart: false
|
||||
property bool wallust: false
|
||||
}
|
||||
component Idle: JsonObject {
|
||||
|
||||
+26
-2
@@ -6,11 +6,27 @@ JsonObject {
|
||||
{
|
||||
name: "Calculator",
|
||||
icon: "calculate",
|
||||
description: "Do simple math equations (powered by Qalc)",
|
||||
description: "Do simple math equations",
|
||||
command: ["autocomplete", "calc"],
|
||||
enabled: true,
|
||||
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",
|
||||
icon: "image",
|
||||
@@ -19,6 +35,14 @@ JsonObject {
|
||||
enabled: true,
|
||||
dangerous: false
|
||||
},
|
||||
{
|
||||
name: "Variant",
|
||||
icon: "colors",
|
||||
description: "Change the current scheme variant",
|
||||
command: ["autocomplete", "variant"],
|
||||
enabled: true,
|
||||
dangerous: false
|
||||
},
|
||||
{
|
||||
name: "Shutdown",
|
||||
icon: "power_settings_new",
|
||||
@@ -37,7 +61,7 @@ JsonObject {
|
||||
},
|
||||
{
|
||||
name: "Logout",
|
||||
icon: "exit_to_app",
|
||||
icon: "logout",
|
||||
description: "Log out of the current session",
|
||||
command: ["loginctl", "terminate-user", ""],
|
||||
enabled: true,
|
||||
|
||||
@@ -12,7 +12,7 @@ import qs.Modules.Osd as Osd
|
||||
import qs.Modules.Launcher as Launcher
|
||||
import qs.Modules.Resources as Resources
|
||||
|
||||
// import qs.Modules.Settings as Settings
|
||||
import qs.Modules.Settings as Settings
|
||||
|
||||
Shape {
|
||||
id: root
|
||||
@@ -85,11 +85,11 @@ Shape {
|
||||
wrapper: root.panels.sidebar
|
||||
}
|
||||
|
||||
// Settings.Background {
|
||||
// id: settings
|
||||
//
|
||||
// startX: (root.width - wrapper.width) / 2 - rounding
|
||||
// startY: 0
|
||||
// wrapper: root.panels.settings
|
||||
// }
|
||||
Settings.Background {
|
||||
id: settings
|
||||
|
||||
startX: (root.width - wrapper.width) / 2 - rounding
|
||||
startY: 0
|
||||
wrapper: root.panels.settings
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -42,7 +42,7 @@ Variants {
|
||||
regions: popoutRegions.instances
|
||||
width: bar.width
|
||||
x: 0
|
||||
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : 34
|
||||
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : backgroundRect.height
|
||||
}
|
||||
|
||||
contentItem.Keys.onEscapePressed: {
|
||||
@@ -62,7 +62,7 @@ Variants {
|
||||
WlrLayershell.layer: WlrLayer.Bottom
|
||||
WlrLayershell.namespace: "ZShell-Bar-Exclusion"
|
||||
color: "transparent"
|
||||
implicitHeight: 34
|
||||
implicitHeight: backgroundRect.height
|
||||
screen: bar.screen
|
||||
|
||||
anchors {
|
||||
@@ -185,7 +185,7 @@ Variants {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? -30 : 0
|
||||
color: "transparent"
|
||||
implicitHeight: 34
|
||||
implicitHeight: barLoader.implicitHeight
|
||||
radius: 0
|
||||
|
||||
Behavior on anchors.topMargin {
|
||||
@@ -200,7 +200,9 @@ Variants {
|
||||
BarLoader {
|
||||
id: barLoader
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
bar: bar
|
||||
popouts: panels.popouts
|
||||
screen: scope.modelData
|
||||
|
||||
@@ -223,12 +223,8 @@ CustomMouseArea {
|
||||
root.utilitiesShortcutActive = false;
|
||||
|
||||
// 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);
|
||||
|
||||
if (!inDashboardArea) {
|
||||
root.visibilities.dashboard = false;
|
||||
}
|
||||
if (!inOsdArea) {
|
||||
root.visibilities.osd = 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() {
|
||||
if (root.visibilities.sidebar) {
|
||||
root.visibilities.dashboard = false;
|
||||
|
||||
+10
-10
@@ -10,7 +10,7 @@ import qs.Modules.Osd as Osd
|
||||
import qs.Components.Toast as Toasts
|
||||
import qs.Modules.Launcher as Launcher
|
||||
import qs.Modules.Resources as Resources
|
||||
// import qs.Modules.Settings as Settings
|
||||
import qs.Modules.Settings as Settings
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
@@ -24,7 +24,7 @@ Item {
|
||||
readonly property alias popouts: popouts
|
||||
readonly property alias resources: resources
|
||||
required property ShellScreen screen
|
||||
// readonly property alias settings: settings
|
||||
readonly property alias settings: settings
|
||||
readonly property alias sidebar: sidebar
|
||||
readonly property alias toasts: toasts
|
||||
readonly property alias utilities: utilities
|
||||
@@ -127,12 +127,12 @@ Item {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
// Settings.Wrapper {
|
||||
// id: settings
|
||||
//
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// anchors.top: parent.top
|
||||
// panels: root
|
||||
// visibilities: root.visibilities
|
||||
// }
|
||||
Settings.Wrapper {
|
||||
id: settings
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
panels: root
|
||||
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 {
|
||||
previewPath = path;
|
||||
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;
|
||||
}
|
||||
|
||||
function setWallpaper(path: string): void {
|
||||
actualCurrent = 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}`]);
|
||||
}
|
||||
|
||||
function stopPreview(): void {
|
||||
showPreview = false;
|
||||
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 ? ({}) : ({
|
||||
|
||||
@@ -29,7 +29,7 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
height: 22
|
||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
implicitHeight: 34
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-overview"
|
||||
@@ -73,6 +73,7 @@ RowLayout {
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
// model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index)
|
||||
model: Config.barConfig.entries
|
||||
|
||||
DelegateChooser {
|
||||
|
||||
@@ -45,16 +45,15 @@ ShapePath {
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
relativeY: -(root.wrapper.height)
|
||||
}
|
||||
|
||||
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]
|
||||
root.delegate: calcItem
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "variant"
|
||||
|
||||
PropertyChanges {
|
||||
model.values: SchemeVariants.query(search.text)
|
||||
root.delegate: variantItem
|
||||
}
|
||||
}
|
||||
]
|
||||
transitions: Transition {
|
||||
@@ -211,4 +219,12 @@ CustomListView {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: variantItem
|
||||
|
||||
VariantItem {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,11 +123,6 @@ Item {
|
||||
search.text = "";
|
||||
}
|
||||
|
||||
function onSessionChanged(): void {
|
||||
if (!root.visibilities.session)
|
||||
search.forceActiveFocus();
|
||||
}
|
||||
|
||||
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]} `;
|
||||
} else if (command[0] === "setMode" && command.length > 1) {
|
||||
list.visibilities.launcher = false;
|
||||
Colours.setMode(command[1]);
|
||||
DynamicColors.setMode(command[1]);
|
||||
} else {
|
||||
list.visibilities.launcher = false;
|
||||
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
|
||||
readonly property real maxHeight: {
|
||||
let max = screen.height - Appearance.spacing.large;
|
||||
if (visibilities.dashboard)
|
||||
let max = screen.height - Appearance.spacing.large * 2;
|
||||
if (visibilities.resources)
|
||||
max -= panels.resources.nonAnimHeight;
|
||||
if (visibilities.dashboard && panels.dashboard.x < root.x + root.implicitWidth)
|
||||
max -= panels.dashboard.nonAnimHeight;
|
||||
return max;
|
||||
}
|
||||
|
||||
@@ -164,6 +164,21 @@ WlSessionLockSurface {
|
||||
implicitWidth: size
|
||||
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 {
|
||||
id: lockBg
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 22
|
||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
-79
@@ -7,110 +7,87 @@ import qs.Config
|
||||
Item {
|
||||
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 double percentage
|
||||
property bool shown: true
|
||||
property color usageColor: warning ? DynamicColors.palette.m3error : mainColor
|
||||
property bool warning: percentage * 100 >= warningThreshold
|
||||
property int warningThreshold: 100
|
||||
property int warningThreshold: 80
|
||||
|
||||
clip: true
|
||||
implicitHeight: 22
|
||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
||||
height: implicitHeight
|
||||
implicitHeight: 34
|
||||
implicitWidth: 34
|
||||
percentage: 0
|
||||
visible: width > 0 && height > 0
|
||||
width: implicitWidth
|
||||
|
||||
Behavior on percentage {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
Behavior on animatedPercentage {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: resourceRowLayout
|
||||
Component.onCompleted: animatedPercentage = percentage
|
||||
onPercentageChanged: animatedPercentage = percentage
|
||||
|
||||
spacing: 2
|
||||
x: shown ? 0 : -resourceRowLayout.width
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitHeight: root.implicitHeight
|
||||
implicitWidth: 14
|
||||
|
||||
Rectangle {
|
||||
id: backgroundCircle
|
||||
Canvas {
|
||||
id: gaugeCanvas
|
||||
|
||||
anchors.centerIn: parent
|
||||
border.color: "#404040"
|
||||
border.width: 1
|
||||
color: "#40000000"
|
||||
height: 14
|
||||
radius: height / 2
|
||||
width: 14
|
||||
}
|
||||
height: width
|
||||
width: Math.min(parent.width, parent.height)
|
||||
|
||||
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 {
|
||||
Component.onCompleted: requestPaint()
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
const cx = width / 2;
|
||||
const cy = (height / 2) + 1;
|
||||
const radius = (Math.min(width, height) - 12) / 2;
|
||||
const lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep);
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.lineCap = "round";
|
||||
ctx.strokeStyle = DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
|
||||
ctx.stroke();
|
||||
if (root.animatedPercentage > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep * root.animatedPercentage);
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.lineCap = "round";
|
||||
ctx.strokeStyle = root.accentColor;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: 0 + (1 / 2)
|
||||
Connections {
|
||||
function onAnimatedPercentageChanged() {
|
||||
gaugeCanvas.requestPaint();
|
||||
}
|
||||
|
||||
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
|
||||
target: root
|
||||
}
|
||||
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: backgroundCircle.height / 2
|
||||
Connections {
|
||||
function onPaletteChanged() {
|
||||
gaugeCanvas.requestPaint();
|
||||
}
|
||||
|
||||
target: DynamicColors
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 Quickshell
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Wayland
|
||||
import qs.Helpers
|
||||
import qs.Modules
|
||||
import qs.Config
|
||||
import qs.Effects
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
@@ -14,15 +13,16 @@ Item {
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
clip: true
|
||||
implicitHeight: 34
|
||||
implicitWidth: rowLayout.implicitWidth + Appearance.padding.small * 2
|
||||
|
||||
CustomRect {
|
||||
id: backgroundRect
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 22
|
||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||
radius: height / 2
|
||||
|
||||
anchors {
|
||||
@@ -40,57 +40,37 @@ Item {
|
||||
id: rowLayout
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
spacing: 0
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 14
|
||||
text: "memory_alt"
|
||||
Ref {
|
||||
service: SystemUsage
|
||||
}
|
||||
|
||||
Resource {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon: "memory"
|
||||
mainColor: DynamicColors.palette.m3primary
|
||||
percentage: ResourceUsage.memoryUsedPercentage
|
||||
percentage: SystemUsage.cpuPerc
|
||||
warningThreshold: 95
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 14
|
||||
text: "memory"
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "memory_alt"
|
||||
mainColor: DynamicColors.palette.m3secondary
|
||||
percentage: ResourceUsage.cpuUsage
|
||||
percentage: SystemUsage.memPerc
|
||||
warningThreshold: 80
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 14
|
||||
text: "gamepad"
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "gamepad"
|
||||
mainColor: DynamicColors.palette.m3tertiary
|
||||
percentage: ResourceUsage.gpuUsage
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 14
|
||||
text: "developer_board"
|
||||
percentage: SystemUsage.gpuPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "developer_board"
|
||||
mainColor: DynamicColors.palette.m3primary
|
||||
percentage: ResourceUsage.gpuMemUsage
|
||||
percentage: SystemUsage.gpuMemUsed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,14 @@ ShapePath {
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
relativeY: root.wrapper.height
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-1
@@ -4,8 +4,10 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Row {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PanelWindow bar
|
||||
@@ -15,6 +17,21 @@ Row {
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitHeight: 34
|
||||
implicitWidth: row.width + Appearance.padding.small * 2
|
||||
|
||||
CustomClippingRect {
|
||||
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
|
||||
|
||||
Row {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
@@ -38,3 +55,5 @@ Row {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 22
|
||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
|
||||
+10
-16
@@ -11,17 +11,19 @@ Item {
|
||||
required property var bar
|
||||
property color colour: DynamicColors.palette.m3primary
|
||||
property Title current: text1
|
||||
readonly property int maxHeight: {
|
||||
const otherModules = bar.children.filter(c => c.id && c.item !== this && c.id !== "spacer");
|
||||
const otherHeight = otherModules.reduce((acc, curr) => acc + (curr.item.nonAnimHeight ?? curr.height), 0);
|
||||
// Length - 2 cause repeater counts as a child
|
||||
return bar.height - otherHeight - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2;
|
||||
// readonly property int maxWidth: 300
|
||||
readonly property int maxWidth: {
|
||||
const otherModules = bar.children.filter(c => c.enabled && c.id && c.item !== this && c.id !== "spacer");
|
||||
const otherWidth = otherModules.reduce((acc, curr) => {
|
||||
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
|
||||
|
||||
clip: true
|
||||
implicitHeight: current.implicitHeight
|
||||
implicitWidth: current.implicitWidth + current.anchors.leftMargin
|
||||
implicitWidth: Math.min(current.implicitWidth, root.maxWidth)
|
||||
|
||||
Behavior on implicitWidth {
|
||||
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 {
|
||||
id: text1
|
||||
|
||||
@@ -53,6 +45,8 @@ Item {
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
elide: Qt.ElideRight
|
||||
elideWidth: root.maxWidth
|
||||
font.family: "Rubik"
|
||||
font.pointSize: 12
|
||||
text: Hypr.activeToplevel?.title ?? qsTr("Desktop")
|
||||
|
||||
+156
-87
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
@@ -10,119 +9,189 @@ import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: itemRoot
|
||||
id: root
|
||||
|
||||
property real activeWorkspaceMargin: Math.ceil(Appearance.padding.small / 2)
|
||||
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.top: parent.top
|
||||
implicitWidth: workspacesRow.implicitWidth + 10
|
||||
implicitWidth: (root.workspaceButtonWidth * root.workspacesShown) + root.activeWorkspaceMargin * 2
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property HyprlandMonitor monitor: Hyprland.monitorFor(itemRoot.bar?.screen)
|
||||
|
||||
function shouldShow(monitor) {
|
||||
Hyprland.refreshWorkspaces();
|
||||
Hyprland.refreshMonitors();
|
||||
if (monitor === root.monitor) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
CustomRect {
|
||||
id: bgRect
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 22
|
||||
implicitHeight: root.parent.height - ((Appearance.padding.small - 1) * 2)
|
||||
radius: height / 2
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: workspacesRow
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: Hyprland.workspaces
|
||||
|
||||
RowLayout {
|
||||
id: workspaceIndicator
|
||||
|
||||
required property var modelData
|
||||
|
||||
visible: root.shouldShow(workspaceIndicator.modelData.monitor)
|
||||
|
||||
CustomText {
|
||||
color: workspaceIndicator.modelData.id === Hyprland.focusedWorkspace.id ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 12
|
||||
text: workspaceIndicator.modelData.name
|
||||
visible: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
border.color: workspaceIndicator.modelData.id === Hyprland.focusedWorkspace.id ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||
border.width: 1
|
||||
color: "transparent"
|
||||
implicitHeight: 14
|
||||
implicitWidth: 14
|
||||
opacity: 1.0
|
||||
radius: height / 2
|
||||
scale: 1.0
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
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.centerIn: parent
|
||||
color: workspaceIndicator.modelData.id === Hyprland.focusedWorkspace.id ? DynamicColors.palette.m3primary : "transparent"
|
||||
implicitHeight: 8
|
||||
implicitWidth: 8
|
||||
radius: implicitHeight / 2
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
Grid {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.activeWorkspaceMargin
|
||||
columnSpacing: 0
|
||||
columns: root.workspacesShown
|
||||
rowSpacing: 0
|
||||
rows: 1
|
||||
z: 3
|
||||
|
||||
onClicked: {
|
||||
workspaceIndicator.modelData.activate();
|
||||
Repeater {
|
||||
model: root.workspaces
|
||||
|
||||
Button {
|
||||
id: button
|
||||
|
||||
required property int index
|
||||
required property HyprlandWorkspace modelData
|
||||
|
||||
implicitHeight: indicator.indicatorThickness
|
||||
implicitWidth: indicator.indicatorThickness
|
||||
width: root.workspaceButtonWidth
|
||||
|
||||
background: Item {
|
||||
id: workspaceButtonBackground
|
||||
|
||||
implicitHeight: root.workspaceButtonWidth
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch(`workspace ${button.modelData.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: activeTextSource
|
||||
|
||||
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
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: activeWorkspace.modelData.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
requests.hpp requests.cpp
|
||||
toaster.hpp toaster.cpp
|
||||
qalculator.hpp qalculator.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::Quick
|
||||
Qt::Concurrent
|
||||
Qt::Sql
|
||||
PkgConfig::Qalculate
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
**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.
|
||||
|
||||
```nix
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from typing import Annotated, Optional
|
||||
import typer
|
||||
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 pathlib import Path
|
||||
from PIL import Image
|
||||
@@ -9,6 +12,8 @@ from materialyoucolor.quantize import QuantizeCelebi
|
||||
from materialyoucolor.score.score import Score
|
||||
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
||||
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()
|
||||
|
||||
@@ -18,47 +23,179 @@ def generate(
|
||||
# image inputs (optional - used for image mode)
|
||||
image_path: Optional[Path] = typer.Option(
|
||||
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(
|
||||
"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: Optional[str] = typer.Option(
|
||||
None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"),
|
||||
mode: str = typer.Option(
|
||||
"dark", help="Mode of the preset scheme (dark or light)."),
|
||||
# output (required)
|
||||
output: Path = typer.Option(..., help="Output JSON path.")
|
||||
mode: Optional[str] = typer.Option(
|
||||
None, help="Mode of the preset scheme (dark or light)."),
|
||||
):
|
||||
if preset is None and image_path is None:
|
||||
raise typer.BadParameter(
|
||||
"Either --image-path or --preset must be provided.")
|
||||
|
||||
HOME = str(os.getenv("HOME"))
|
||||
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:
|
||||
raise typer.BadParameter(
|
||||
"Use either --image-path or --preset, not both.")
|
||||
|
||||
match scheme:
|
||||
def get_scheme_class(scheme_name: str):
|
||||
match scheme_name:
|
||||
case "fruit-salad":
|
||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme
|
||||
case 'expressive':
|
||||
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive as Scheme
|
||||
case 'monochrome':
|
||||
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome as Scheme
|
||||
case 'rainbow':
|
||||
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow as Scheme
|
||||
case 'tonal-spot':
|
||||
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot as Scheme
|
||||
case 'neutral':
|
||||
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral as Scheme
|
||||
case 'fidelity':
|
||||
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity as Scheme
|
||||
case 'content':
|
||||
from materialyoucolor.scheme.scheme_content import SchemeContent as Scheme
|
||||
case 'vibrant':
|
||||
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant as Scheme
|
||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
||||
return SchemeFruitSalad
|
||||
case "expressive":
|
||||
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
|
||||
return SchemeExpressive
|
||||
case "monochrome":
|
||||
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
|
||||
return SchemeMonochrome
|
||||
case "rainbow":
|
||||
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
|
||||
return SchemeRainbow
|
||||
case "tonal-spot":
|
||||
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
||||
return SchemeTonalSpot
|
||||
case "neutral":
|
||||
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
|
||||
return SchemeNeutral
|
||||
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 as Scheme
|
||||
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)):
|
||||
thumbnail_file = Path(thumbnail_path)
|
||||
@@ -70,6 +207,170 @@ def generate(
|
||||
thumbnail_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
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:
|
||||
image = Image.open(image_path)
|
||||
pixel_len = image.width * image.height
|
||||
@@ -88,11 +389,11 @@ def generate(
|
||||
raise typer.BadParameter(
|
||||
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"
|
||||
|
||||
scheme = Scheme(
|
||||
scheme = scheme_class(
|
||||
seed,
|
||||
is_dark,
|
||||
0.0
|
||||
@@ -111,27 +412,67 @@ def generate(
|
||||
return "#{:06X}".format(argb_int & 0xFFFFFF)
|
||||
|
||||
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:
|
||||
seed = seed_from_preset(preset)
|
||||
colors = generate_color_scheme(seed, mode)
|
||||
effective_mode = mode or config_mode
|
||||
name, flavor = preset.split(":")
|
||||
else:
|
||||
generate_thumbnail(image_path, str(thumbnail_path))
|
||||
seed = seed_from_image(thumbnail_path)
|
||||
colors = generate_color_scheme(seed, mode)
|
||||
image_path = image_path or Path(WALL_PATH)
|
||||
generate_thumbnail(image_path, str(THUMB_PATH))
|
||||
seed = seed_from_image(THUMB_PATH)
|
||||
name = "dynamic"
|
||||
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 = {
|
||||
"name": name,
|
||||
"flavor": flavor,
|
||||
"mode": mode,
|
||||
"mode": effective_mode,
|
||||
"variant": scheme,
|
||||
"colors": colors
|
||||
"colors": colors,
|
||||
"seed": seed.to_int()
|
||||
}
|
||||
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output, "w") as f:
|
||||
if TEMPLATE_DIR is not None:
|
||||
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)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
pkgs, # To ensure the nixpkgs version of app2unit
|
||||
pkgs,
|
||||
fetchFromGitHub,
|
||||
...
|
||||
}:
|
||||
pkgs.app2unit.overrideAttrs (
|
||||
final: prev: rec {
|
||||
version = "1.0.3"; # Fix old issue related to missing env var
|
||||
version = "1.0.3";
|
||||
src = fetchFromGitHub {
|
||||
owner = "Vladimir-csp";
|
||||
repo = "app2unit";
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
pkg-config,
|
||||
pythonEnv,
|
||||
zshell-cli,
|
||||
ddcutil,
|
||||
brightnessctl,
|
||||
}:
|
||||
let
|
||||
version = "1.0.0";
|
||||
@@ -39,6 +41,8 @@ let
|
||||
bash
|
||||
hyprland
|
||||
zshell-cli
|
||||
ddcutil
|
||||
brightnessctl
|
||||
];
|
||||
|
||||
fontconfig = makeFontsConf {
|
||||
|
||||
@@ -16,6 +16,7 @@ python3.pkgs.buildPythonApplication {
|
||||
dependencies = with python3.pkgs; [
|
||||
materialyoucolor
|
||||
pillow
|
||||
jinja2
|
||||
typer
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user