4 Commits

119 changed files with 3258 additions and 5769 deletions
-42
View File
@@ -1,42 +0,0 @@
name: Lint & Format (JS/TS)
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git
- name: Prettier
continue-on-error: true
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
npx --yes prettier --check "**/*.{js,jsx,ts,tsx,mjs,cjs}" --ignore-path .prettierignore
else
echo "No JS/TS files found"
fi
- name: ESLint
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
if [ -f package.json ]; then
npm install --no-audit --no-fund
fi
if [ -f eslint.config.js ] || [ -f eslint.config.mjs ] || [ -f eslint.config.cjs ] || [ -f .eslintrc ] || [ -f .eslintrc.js ] || [ -f .eslintrc.cjs ] || [ -f .eslintrc.json ] || [ -f .eslintrc.yaml ] || [ -f .eslintrc.yml ]; then
npx --yes eslint . && echo "ESLint passed" || echo "ESLint failed"
else
echo "No eslint config found"
fi
else
echo "No JS/TS files found"
fi
-34
View File
@@ -1,34 +0,0 @@
name: Lint & Format (Python)
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git \
python3 \
py3-pip
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir ruff
- name: Format check
continue-on-error: true
run: |
. .venv/bin/activate
ruff format --check .
- name: Lint
run: |
. .venv/bin/activate
ruff check .
-72
View File
@@ -1,72 +0,0 @@
name: Lint & Format (Rust)
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rustfmt \
rust-clippy
- name: Format check
continue-on-error: true
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
echo "$manifest: needs formatting"
done
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
- name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" --all-targets --all-features -- -D warnings && \
echo "$manifest: Clippy passed" || \
{ echo "$manifest: Clippy failed"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
+1 -2
View File
@@ -1,4 +1,4 @@
**/__pycache__/ ./__pycache__/
./result/ ./result/
.pyre/ .pyre/
.cache/ .cache/
@@ -12,4 +12,3 @@ pkg/
uv.lock uv.lock
.qtcreator/ .qtcreator/
dist/ dist/
**/target/
-3
View File
@@ -1,3 +0,0 @@
.venv/
scripts/fzf.js
scripts/fuzzysort.js
-13
View File
@@ -1,13 +0,0 @@
{
"semi": true,
"singleQuote": false,
"jsxSingleQuote": false,
"tabWidth": 4,
"printWidth": 80,
"trailingComma": "es5",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"endOfLine": "lf",
"proseWrap": "preserve"
}
+2 -2
View File
@@ -2,7 +2,7 @@ import QtQuick
import qs.Config import qs.Config
NumberAnimation { NumberAnimation {
duration: Appearance.anim.durations.normal duration: MaterialEasing.standardTime
easing.bezierCurve: Appearance.anim.curves.standard easing.bezierCurve: MaterialEasing.standard
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
} }
-33
View File
@@ -1,33 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Config
IconButton {
id: root
required property bool shouldBeVisible
opacity: 0
scale: 0
visible: root.scale > 0
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
Behavior on scale {
Anim {
}
}
onShouldBeVisibleChanged: {
if (root.shouldBeVisible) {
root.opacity = 1;
root.scale = 1;
} else {
root.opacity = 0;
root.scale = 0;
}
}
}
+2 -1
View File
@@ -41,11 +41,12 @@ CustomRect {
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2 implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
Behavior on radius { Behavior on radius {
Anim { Anim {
id: radiusAnim id: radiusAnim
} }
} }
-4
View File
@@ -7,8 +7,4 @@ JsonObject {
property real alignX: 0.5 property real alignX: 0.5
property real alignY: 0.5 property real alignY: 0.5
property real zoom: 1.0 property real zoom: 1.0
property real sourceClipX: 0
property real sourceClipY: 0
property real sourceClipW: 0
property real sourceClipH: 0
} }
+12 -1
View File
@@ -8,6 +8,10 @@ JsonObject {
id: "workspaces", id: "workspaces",
enabled: true enabled: true
}, },
{
id: "audio",
enabled: true
},
{ {
id: "media", id: "media",
enabled: true enabled: true
@@ -20,6 +24,10 @@ JsonObject {
id: "updates", id: "updates",
enabled: true enabled: true
}, },
{
id: "dash",
enabled: true
},
{ {
id: "spacer", id: "spacer",
enabled: true enabled: true
@@ -40,6 +48,10 @@ JsonObject {
id: "tray", id: "tray",
enabled: true enabled: true
}, },
{
id: "upower",
enabled: false
},
{ {
id: "network", id: "network",
enabled: false enabled: false
@@ -58,7 +70,6 @@ JsonObject {
property Popouts popouts: Popouts { property Popouts popouts: Popouts {
} }
property int rounding: 8 property int rounding: 8
property int smoothing: 32
component Popouts: JsonObject { component Popouts: JsonObject {
property bool activeWindow: true property bool activeWindow: true
+1 -27
View File
@@ -23,7 +23,6 @@ Singleton {
property alias osd: adapter.osd property alias osd: adapter.osd
property alias overview: adapter.overview property alias overview: adapter.overview
property bool recentlySaved: false property bool recentlySaved: false
property alias screenshot: adapter.screenshot
property alias services: adapter.services property alias services: adapter.services
property alias sidebar: adapter.sidebar property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities property alias utilities: adapter.utilities
@@ -83,10 +82,6 @@ Singleton {
wallFadeDuration: background.wallFadeDuration, wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled, enabled: background.enabled,
alignX: background.alignX, alignX: background.alignX,
sourceClipX: background.sourceClipX,
sourceClipY: background.sourceClipY,
sourceClipW: background.sourceClipW,
sourceClipH: background.sourceClipH,
alignY: background.alignY, alignY: background.alignY,
zoom: background.zoom zoom: background.zoom
}; };
@@ -98,7 +93,6 @@ Singleton {
hideWhenNotif: barConfig.hideWhenNotif, hideWhenNotif: barConfig.hideWhenNotif,
rounding: barConfig.rounding, rounding: barConfig.rounding,
border: barConfig.border, border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height, height: barConfig.height,
popouts: { popouts: {
tray: barConfig.popouts.tray, tray: barConfig.popouts.tray,
@@ -134,8 +128,7 @@ Singleton {
background: serializeBackground(), background: serializeBackground(),
launcher: serializeLauncher(), launcher: serializeLauncher(),
colors: serializeColors(), colors: serializeColors(),
dock: serializeDock(), dock: serializeDock()
screenshot: serializeScreenshot()
}; };
} }
@@ -187,7 +180,6 @@ Singleton {
logo: general.logo, logo: general.logo,
wallpaperPath: general.wallpaperPath, wallpaperPath: general.wallpaperPath,
desktopIcons: general.desktopIcons, desktopIcons: general.desktopIcons,
dateFormat: general.dateFormat,
color: { color: {
mode: general.color.mode, mode: general.color.mode,
smart: general.color.smart, smart: general.color.smart,
@@ -241,8 +233,6 @@ Singleton {
return { return {
recolorLogo: lock.recolorLogo, recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint, enableFprint: lock.enableFprint,
showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries, maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount, blurAmount: lock.blurAmount,
sizes: { sizes: {
@@ -284,20 +274,6 @@ Singleton {
}; };
} }
function serializeScreenshot(): var {
return {
enable_pp: screenshot.enable_pp,
mode: screenshot.mode,
corner_radius: screenshot.corner_radius,
drop_shadow: screenshot.drop_shadow,
rounded_corners: screenshot.rounded_corners,
shadow_blur_radius: screenshot.shadow_blur_radius,
shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y
};
}
function serializeServices(): var { function serializeServices(): var {
return { return {
weatherLocation: services.weatherLocation, weatherLocation: services.weatherLocation,
@@ -453,8 +429,6 @@ Singleton {
} }
property Overview overview: Overview { property Overview overview: Overview {
} }
property Screenshot screenshot: Screenshot {
}
property Services services: Services { property Services services: Services {
} }
property SidebarConfig sidebar: SidebarConfig { property SidebarConfig sidebar: SidebarConfig {
+4 -35
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Hyprland
import QtQuick import QtQuick
import ZShell import ZShell
import qs.Helpers import qs.Helpers
@@ -80,32 +79,10 @@ Singleton {
} }
function reloadHyprRules(): void { function reloadHyprRules(): void {
const blur = transparency.enabled ? 1 : 0; const barStr = "keyword layerrule %1 %2, match:namespace ZShell-Bar";
const alpha = transparency.base - 0.03; const authStr = "keyword layerrule %1 %2, match:namespace ZShell-Auth";
Hypr.extras.batchMessage([barStr.arg("blur").arg(transparency.enabled ? 1 : 0), barStr.arg("ignore_alpha").arg(transparency.base - 0.03)]);
const rules = ` Hypr.extras.batchMessage([authStr.arg("blur").arg(transparency.enabled ? 1 : 0), authStr.arg("ignore_alpha").arg(transparency.base - 0.03)]);
hl.layer_rule({
match = { namespace = "ZShell-Bar" },
blur = ${blur}
})
hl.layer_rule({
match = { namespace = "ZShell-Bar" },
ignore_alpha = ${alpha}
})
hl.layer_rule({
match = { namespace = "ZShell-Auth" },
blur = ${blur}
})
hl.layer_rule({
match = { namespace = "ZShell-Auth" },
ignore_alpha = ${alpha}
})
`;
Hypr.extras.message(`eval ${rules}`);
} }
function setMode(mode: string): void { function setMode(mode: string): void {
@@ -116,14 +93,6 @@ Singleton {
Component.onCompleted: debounceTimer.triggered() Component.onCompleted: debounceTimer.triggered()
Connections {
function onUsingLuaChanged(): void {
root.reloadHyprRules();
}
target: Hyprland
}
Connections { Connections {
function onConfigReloaded(): void { function onConfigReloaded(): void {
root.reloadHyprRules(); root.reloadHyprRules();
-1
View File
@@ -6,7 +6,6 @@ JsonObject {
} }
property Color color: Color { property Color color: Color {
} }
property string dateFormat: "ddd d MMM - hh:mm:ss"
property bool desktopIcons: false property bool desktopIcons: false
property Idle idle: Idle { property Idle idle: Idle {
} }
-2
View File
@@ -5,8 +5,6 @@ JsonObject {
property bool enableFprint: true property bool enableFprint: true
property int maxFprintTries: 3 property int maxFprintTries: 3
property bool recolorLogo: false property bool recolorLogo: false
property bool showNotifContent: false
property bool showNotifIcon: true
property Sizes sizes: Sizes { property Sizes sizes: Sizes {
} }
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Io
JsonObject {
property real corner_radius: 12.0
property bool drop_shadow: true
property bool enable_pp: true
property string mode: "manual"
property bool rounded_corners: false
property real shadow_blur_radius: 22.0
property list<int> shadow_color: [0, 0, 0, 160]
property real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0
}
+173 -22
View File
@@ -3,33 +3,184 @@ import QtQuick
Canvas { Canvas {
id: root id: root
property rect dirtyRect: Qt.rect(0, 0, 0, 0)
property bool frameQueued: false
property bool fullRepaintPending: true
property point lastPoint: Qt.point(0, 0)
property real minPointDistance: 2.0
property color penColor: "white" property color penColor: "white"
property real penWidth: 4 property real penWidth: 4
property var points: [] property var pendingSegments: []
property bool strokeActive: false
property var strokes: []
function clear(): void { function appendPoint(x, y) {
var ctx = getContext('2d'); if (!strokeActive || strokes.length === 0)
root.points = [];
ctx.reset();
root.requestPaint();
}
renderStrategy: Canvas.Cooperative
onPaint: {
if (points.length < 2)
return; return;
var ctx = root.getContext('2d'); const dx = x - lastPoint.x;
ctx.save(); const dy = y - lastPoint.y;
ctx.lineWidth = root.penWidth;
ctx.strokeStyle = root.penColor; if ((dx * dx + dy * dy) < (minPointDistance * minPointDistance))
ctx.lineCap = "round"; return;
const x1 = lastPoint.x;
const y1 = lastPoint.y;
const x2 = x;
const y2 = y;
strokes[strokes.length - 1].push(Qt.point(x2, y2));
pendingSegments.push({
dot: false,
x1: x1,
y1: y1,
x2: x2,
y2: y2
});
lastPoint = Qt.point(x2, y2);
queueDirty(segmentDirtyRect(x1, y1, x2, y2));
}
function beginStroke(x, y) {
const p = Qt.point(x, y);
strokes.push([p]);
lastPoint = p;
strokeActive = true;
pendingSegments.push({
dot: true,
x: x,
y: y
});
queueDirty(pointDirtyRect(x, y));
}
function clear() {
strokes = [];
pendingSegments = [];
dirtyRect = Qt.rect(0, 0, 0, 0);
fullRepaintPending = true;
markDirty(Qt.rect(0, 0, width, height));
}
function drawDot(ctx, x, y) {
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y); ctx.arc(x, y, penWidth / 2, 0, Math.PI * 2);
for (var i = 1; i < points.length; i++) ctx.fill();
ctx.lineTo(points[i].x, points[i].y); }
function drawSegment(ctx, x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
function endStroke() {
strokeActive = false;
}
function pointDirtyRect(x, y) {
const pad = penWidth + 2;
return Qt.rect(x - pad, y - pad, pad * 2, pad * 2);
}
function queueDirty(r) {
dirtyRect = unionRects(dirtyRect, r);
if (frameQueued)
return;
frameQueued = true;
requestAnimationFrame(function () {
frameQueued = false;
if (dirtyRect.width > 0 && dirtyRect.height > 0) {
markDirty(dirtyRect);
dirtyRect = Qt.rect(0, 0, 0, 0);
}
});
}
function replayAll(ctx) {
ctx.clearRect(0, 0, width, height);
for (const stroke of strokes) {
if (!stroke || stroke.length === 0)
continue;
if (stroke.length === 1) {
const p = stroke[0];
drawDot(ctx, p.x, p.y);
continue;
}
ctx.beginPath();
ctx.moveTo(stroke[0].x, stroke[0].y);
for (let i = 1; i < stroke.length; ++i)
ctx.lineTo(stroke[i].x, stroke[i].y);
ctx.stroke(); ctx.stroke();
points = points.slice(points.length - 2);
ctx.restore();
} }
} }
function requestFullRepaint() {
fullRepaintPending = true;
markDirty(Qt.rect(0, 0, width, height));
}
function segmentDirtyRect(x1, y1, x2, y2) {
const pad = penWidth + 2;
const left = Math.min(x1, x2) - pad;
const top = Math.min(y1, y2) - pad;
const right = Math.max(x1, x2) + pad;
const bottom = Math.max(y1, y2) + pad;
return Qt.rect(left, top, right - left, bottom - top);
}
function unionRects(a, b) {
if (a.width <= 0 || a.height <= 0)
return b;
if (b.width <= 0 || b.height <= 0)
return a;
const left = Math.min(a.x, b.x);
const top = Math.min(a.y, b.y);
const right = Math.max(a.x + a.width, b.x + b.width);
const bottom = Math.max(a.y + a.height, b.y + b.height);
return Qt.rect(left, top, right - left, bottom - top);
}
anchors.fill: parent
contextType: "2d"
renderStrategy: Canvas.Threaded
renderTarget: Canvas.Image
onHeightChanged: requestFullRepaint()
onPaint: region => {
const ctx = getContext("2d");
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.lineWidth = penWidth;
ctx.strokeStyle = penColor;
ctx.fillStyle = penColor;
if (fullRepaintPending) {
fullRepaintPending = false;
replayAll(ctx);
pendingSegments = [];
return;
}
for (const seg of pendingSegments) {
if (seg.dot)
drawDot(ctx, seg.x, seg.y);
else
drawSegment(ctx, seg.x1, seg.y1, seg.x2, seg.y2);
}
pendingSegments = [];
}
onWidthChanged: requestFullRepaint()
}
+5 -7
View File
@@ -30,10 +30,8 @@ CustomMouseArea {
const x = event.x; const x = event.x;
const y = event.y; const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) { if (event.buttons & Qt.LeftButton)
root.drawing.points.push(Qt.point(x, y)); root.drawing.appendPoint(x, y);
root.drawing.requestPaint();
}
if (root.inLeftPanel(root.popout, x, y)) { if (root.inLeftPanel(root.popout, x, y)) {
root.z = -2; root.z = -2;
@@ -46,8 +44,7 @@ CustomMouseArea {
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) { if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.panels.drawing.expanded = false; root.panels.drawing.expanded = false;
root.drawing.points.push(Qt.point(x, y)); root.drawing.beginStroke(x, y);
root.drawing.requestPaint();
return; return;
} }
@@ -55,6 +52,7 @@ CustomMouseArea {
root.drawing.clear(); root.drawing.clear();
} }
onReleased: { onReleased: {
root.drawing.points = []; if (root.visibilities.isDrawing)
root.drawing.endStroke();
} }
} }
-15
View File
@@ -12,37 +12,22 @@ Scope {
required property ShellScreen screen required property ShellScreen screen
ExclusionZone { ExclusionZone {
id: top
anchors.top: true anchors.top: true
exclusiveZone: root.bar.exclusiveZone exclusiveZone: root.bar.exclusiveZone
} }
ExclusionZone { ExclusionZone {
id: left
anchors.left: true anchors.left: true
} }
ExclusionZone { ExclusionZone {
id: right
anchors.right: true anchors.right: true
} }
ExclusionZone { ExclusionZone {
id: bottom
anchors.bottom: true anchors.bottom: true
} }
Timer {
interval: 5000
running: true
onTriggered: console.log("top height:", top.exclusiveZone, "left width:", left.exclusiveZone, "right width:", right.exclusiveZone, "bottom height:", bottom.exclusiveZone)
}
component ExclusionZone: CustomWindow { component ExclusionZone: CustomWindow {
exclusiveZone: Config.barConfig.border exclusiveZone: Config.barConfig.border
implicitHeight: 1 implicitHeight: 1
+1 -1
View File
@@ -78,7 +78,7 @@ CustomMouseArea {
const dragY = y - dragStart.y; const dragY = y - dragStart.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) { if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
// root.input.z = 2; root.input.z = 2;
root.panels.drawing.expanded = false; root.panels.drawing.expanded = false;
} }
-11
View File
@@ -34,7 +34,6 @@ Item {
readonly property alias resourcesWrapper: resourcesWrapper readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen required property ShellScreen screen
readonly property alias settings: settings readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar readonly property alias sidebar: sidebar
readonly property alias toasts: toasts readonly property alias toasts: toasts
readonly property alias utilities: utilities readonly property alias utilities: utilities
@@ -177,15 +176,6 @@ Item {
visibilities: root.visibilities visibilities: root.visibilities
} }
Item {
id: settingsWrapper
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
clip: true
implicitHeight: settings.implicitHeight * (1 - settings.offsetScale)
implicitWidth: settings.implicitWidth
Settings.Wrapper { Settings.Wrapper {
id: settings id: settings
@@ -196,7 +186,6 @@ Item {
screen: root.screen screen: root.screen
visibilities: root.visibilities visibilities: root.visibilities
} }
}
Dock.Wrapper { Dock.Wrapper {
id: dock id: dock
+18 -40
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
import ZShell.Blobs import ZShell.Blobs
@@ -128,14 +127,6 @@ Variants {
Component.onCompleted: Visibilities.load(scope.modelData, this) Component.onCompleted: Visibilities.load(scope.modelData, this)
} }
IpcHandler {
function toggleLauncher(fix: string): void {
visibilities.launcher = !visibilities.launcher;
}
target: "visibilities"
}
Binding { Binding {
property: "bar" property: "bar"
target: visibilities target: visibilities
@@ -158,7 +149,6 @@ Variants {
id: blobGroup id: blobGroup
color: DynamicColors.palette.m3surface color: DynamicColors.palette.m3surface
smoothing: Config.barConfig.smoothing
Behavior on color { Behavior on color {
CAnim { CAnim {
@@ -180,34 +170,28 @@ Variants {
PanelBg { PanelBg {
id: dashBg id: dashBg
property real extraHeight: 0.2 deformAmount: 0.08 * Config.appearance.deform.scale
implicitHeight: panels.dashboard.height
deformAmount: 0.06
implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper panel: panels.dashboard
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight
} }
PanelBg { PanelBg {
id: launcherBg id: launcherBg
property real extraHeight: 0.2 deformAmount: 0.08 * Config.appearance.deform.scale
deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher panel: panels.launcher
radius: Appearance.rounding.smallest + 5 radius: Appearance.rounding.smallest + 5
y: panels.launcher.y + bar.implicitHeight
} }
PanelBg { PanelBg {
id: sidebarBg id: sidebarBg
bottomLeftRadius: 0 bottomLeftRadius: 0
deformAmount: 0.04 deformAmount: 0.08 * Config.appearance.deform.scale
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg] exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2 implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
panel: panels.sidebar panel: panels.sidebar
@@ -216,10 +200,10 @@ Variants {
PanelBg { PanelBg {
id: osdBg id: osdBg
deformAmount: 0.1 deformAmount: 0.1 * Config.appearance.deform.scale
implicitHeight: panels.osd.height implicitHeight: panels.osd.height
implicitWidth: panels.osd.width implicitWidth: panels.osd.width
panel: panels.osdWrapper panel: panels.osd
radius: 20 radius: 20
x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
@@ -234,7 +218,7 @@ Variants {
PanelBg { PanelBg {
id: utilsBg id: utilsBg
deformAmount: panels.sidebar.visible ? (0.1) : (0.1) deformAmount: panels.sidebar.visible ? (0.1 * Config.appearance.deform.scale) : (0.1 * Config.appearance.deform.scale)
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg] exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
panel: panels.utilities panel: panels.utilities
topLeftRadius: 0 topLeftRadius: 0
@@ -245,11 +229,11 @@ Variants {
property real extraHeight: panels.popouts.isDetached ? 0 : 0.2 property real extraHeight: panels.popouts.isDetached ? 0 : 0.2
deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1 deformAmount: panels.popouts.isDetached ? 0.05 * Config.appearance.deform.scale : panels.popouts.hasCurrent ? 0.15 * Config.appearance.deform.scale : 0.1 * Config.appearance.deform.scale
implicitHeight: panels.popouts.height * (1 + extraHeight) implicitHeight: panels.popouts.height * (1 + extraHeight)
implicitWidth: panels.popouts.width implicitWidth: panels.popouts.width
panel: panels.popoutsWrapper panel: panels.popouts
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : Appearance.rounding.smallest
x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
@@ -262,10 +246,10 @@ Variants {
PanelBg { PanelBg {
id: resourcesBg id: resourcesBg
deformAmount: 0.05 deformAmount: 0.08 * Config.appearance.deform.scale
implicitHeight: panels.resources.height implicitHeight: panels.resources.height
implicitWidth: panels.resources.width implicitWidth: panels.resources.width
panel: panels.resourcesWrapper panel: panels.resources
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
@@ -274,23 +258,17 @@ Variants {
PanelBg { PanelBg {
id: settingsBg id: settingsBg
property real extraHeight: 0.2 deformAmount: 0.08 * Config.appearance.deform.scale
deformAmount: 0.03
implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width
panel: panels.settings panel: panels.settings
radius: Appearance.rounding.large radius: Appearance.rounding.large
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
x: panels.settingsWrapper.x + panels.settings.x + Config.barConfig.border
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
} }
PanelBg { PanelBg {
id: dockBg id: dockBg
deformAmount: 0.08 deformAmount: 0.08 * Config.appearance.deform.scale
panel: panels.dock panel: panels.dock
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
} }
@@ -298,7 +276,7 @@ Variants {
PanelBg { PanelBg {
id: drawingBg id: drawingBg
deformAmount: 0.08 deformAmount: 0.08 * Config.appearance.deform.scale
panel: panels.drawing panel: panels.drawing
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
} }
@@ -393,7 +371,7 @@ Variants {
property real deformAmount: 0.15 property real deformAmount: 0.15
required property Item panel required property Item panel
deformScale: (deformAmount * Config.appearance.deform.scale) / 10000 deformScale: deformAmount / 10000
group: blobGroup group: blobGroup
implicitHeight: panel.height implicitHeight: panel.height
implicitWidth: panel.width implicitWidth: panel.width
+20 -20
View File
@@ -16,14 +16,27 @@ Scope {
property bool launching: false property bool launching: false
property string promptMessage: "" property string promptMessage: ""
readonly property var selectedSession: sessionIndex >= 0 ? sessions[sessionIndex] : null readonly property var selectedSession: sessionIndex >= 0 ? sessions[sessionIndex] : null
readonly property var selectedUser: Users.selectedUser
property int sessionIndex: sessions.length > 0 ? 0 : -1 property int sessionIndex: sessions.length > 0 ? 0 : -1
property var sessions: [] property var sessions: []
readonly property string userFace: selectedUser ? selectedUser.face : ""
readonly property string username: Users.selectedUsername
// User handling - now uses the Users singleton // User handling - now uses the Users singleton
readonly property var users: Users.users readonly property var users: Users.users
readonly property var selectedUser: Users.selectedUser
readonly property string username: Users.selectedUsername
readonly property string userFace: selectedUser ? selectedUser.face : ""
// User selection functions (delegate to Users singleton)
function selectUser(username: string): bool {
return Users.selectUser(username);
}
function selectNextUser(): void {
Users.selectNext();
}
function selectPreviousUser(): void {
Users.selectPrevious();
}
signal flashMsg signal flashMsg
@@ -45,11 +58,11 @@ Scope {
event.accepted = true; event.accepted = true;
return; return;
} else if (event.key === Qt.Key_Escape) { }
buffer = "";
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) { if (event.text && !/[\r\n]/.test(event.text)) {
// No illegal characters (you are insane if you use unicode in your password)
buffer += event.text; buffer += event.text;
event.accepted = true;
} }
} }
@@ -68,19 +81,6 @@ Scope {
Greetd.launch(selectedSession.command, [], true); Greetd.launch(selectedSession.command, [], true);
} }
function selectNextUser(): void {
Users.selectNext();
}
function selectPreviousUser(): void {
Users.selectPrevious();
}
// User selection functions (delegate to Users singleton)
function selectUser(username: string): bool {
return Users.selectUser(username);
}
function submit(): void { function submit(): void {
errorMessage = ""; errorMessage = "";
+1
View File
@@ -346,6 +346,7 @@ Singleton {
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
console.log("this is running");
if (root.gpuType === "GENERIC") { if (root.gpuType === "GENERIC") {
const percs = text.trim().split("\n"); const percs = text.trim().split("\n");
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0); const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
+44 -20
View File
@@ -7,30 +7,23 @@ import QtQuick
Singleton { Singleton {
id: root id: root
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user" // The list of users that can log in graphically
property int selectedIndex: 0 // Each user object has: username, uid, home, shell, gecos (full name), face (avatar path)
readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
property var users: [] property var users: []
function saveDefaultUser(): void { // The currently selected user index
if (selectedUser) { property int selectedIndex: 0
defaultUserStorage.setText(selectedUser.username);
}
}
function selectNext(): void { // The currently selected user object (or null if none)
if (users.length === 0) readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
return;
selectedIndex = (selectedIndex + 1) % users.length;
}
function selectPrevious(): void { // Convenience property for the selected username
if (users.length === 0) readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
return;
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
}
// Path to store the default user preference
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user"
// Select a user by username
function selectUser(username: string): bool { function selectUser(username: string): bool {
for (let i = 0; i < users.length; i++) { for (let i = 0; i < users.length; i++) {
if (users[i].username === username) { if (users[i].username === username) {
@@ -41,6 +34,28 @@ Singleton {
return false; return false;
} }
// Select the next user in the list (wraps around)
function selectNext(): void {
if (users.length === 0)
return;
selectedIndex = (selectedIndex + 1) % users.length;
}
// Select the previous user in the list (wraps around)
function selectPrevious(): void {
if (users.length === 0)
return;
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
}
// Save the current user as the default for next login
function saveDefaultUser(): void {
if (selectedUser) {
defaultUserStorage.setText(selectedUser.username);
}
}
// Process to fetch the list of graphical users
Process { Process {
id: userLister id: userLister
@@ -52,10 +67,13 @@ Singleton {
try { try {
root.users = JSON.parse(text); root.users = JSON.parse(text);
// If we have users and no selection yet, try to select the default user
if (root.users.length > 0) { if (root.users.length > 0) {
// Try to load the default user
if (defaultUserStorage.loaded) { if (defaultUserStorage.loaded) {
const defaultUsername = defaultUserStorage.text().trim(); const defaultUsername = defaultUserStorage.text().trim();
if (defaultUsername && !root.selectUser(defaultUsername)) { if (defaultUsername && !root.selectUser(defaultUsername)) {
// Default user not found, select first user
root.selectedIndex = 0; root.selectedIndex = 0;
} }
} else { } else {
@@ -69,14 +87,15 @@ Singleton {
} }
} }
// FileView for persisting the default user
FileView { FileView {
id: defaultUserStorage id: defaultUserStorage
path: root.defaultUserFile path: root.defaultUserFile
preload: true preload: true
onLoadFailed: {}
onLoaded: { onLoaded: {
// If users are already loaded, try to select the default user
if (root.users.length > 0) { if (root.users.length > 0) {
const defaultUsername = text().trim(); const defaultUsername = text().trim();
if (defaultUsername) { if (defaultUsername) {
@@ -84,5 +103,10 @@ Singleton {
} }
} }
} }
onLoadFailed: {
// File doesn't exist yet, that's fine - we'll create it on first save
console.log("No default user file found, will use first user");
}
} }
} }
-72
View File
@@ -1,72 +0,0 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import ZShell
import qs.Config
Singleton {
id: root
property alias enabled: props.enabled
function setHyprConf(): void {
Hypr.extras.applyOptions({
"animations.enabled": 0,
"decoration.shadow.enabled": 0,
"decoration.blur.enabled": 0,
"general.border_size": 0,
"decoration.rounding": 0
});
}
onEnabledChanged: {
if (enabled) {
setHyprConf();
if (Config.utilities.toasts.gameModeChanged)
Toaster.toast(qsTr("Game mode enabled"), qsTr("Disabled Hyprland animations, blur, shadows and corner radius"), "gamepad");
} else {
Hypr.extras.message("reload");
if (Config.utilities.toasts.gameModeChanged)
Toaster.toast(qsTr("Game mode disabled"), qsTr("Hyprland settings restored"), "gamepad");
}
}
PersistentProperties {
id: props
property bool enabled: Hypr.options["animations:enabled"] === 0
reloadableId: "gamemode"
}
Connections {
function onConfigReloaded(): void {
if (props.enabled)
root.setHyprConf();
}
target: Hypr
}
IpcHandler {
function disable(): void {
props.enabled = false;
}
function enable(): void {
props.enabled = true;
}
function isEnabled(): bool {
return props.enabled;
}
function toggle(): void {
props.enabled = !props.enabled;
}
target: "gameMode"
}
}
+11
View File
@@ -0,0 +1,11 @@
pragma Singleton
import Quickshell
import Quickshell.Networking
Singleton {
id: root
property NetworkDevice activeDevice: devices.find(d => d.connected)
property list<NetworkDevice> devices: Networking.devices.values
}
+1 -2
View File
@@ -66,8 +66,7 @@ MouseArea {
function save(): void { function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--image"] : ["swappy", "-f"]; ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path]));
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path]));
closeAnim.start(); closeAnim.start();
} }
+2 -7
View File
@@ -1,23 +1,18 @@
pragma Singleton pragma Singleton
import Quickshell import Quickshell
import QtQuick
import qs.Config
Singleton { Singleton {
id: root readonly property string amPmStr: timeComponents[2] ?? ""
readonly property date date: clock.date readonly property date date: clock.date
readonly property string dateStr: format(Config.general.dateFormat)
property alias enabled: clock.enabled property alias enabled: clock.enabled
readonly property string hourStr: timeComponents[0] ?? "" readonly property string hourStr: timeComponents[0] ?? ""
readonly property int hours: clock.hours readonly property int hours: clock.hours
readonly property string minuteStr: timeComponents[1] ?? "" readonly property string minuteStr: timeComponents[1] ?? ""
readonly property int minutes: clock.minutes readonly property int minutes: clock.minutes
readonly property string secondStr: timeComponents[2] ?? ""
readonly property int seconds: clock.seconds readonly property int seconds: clock.seconds
readonly property list<string> timeComponents: timeStr.split(":") readonly property list<string> timeComponents: timeStr.split(":")
readonly property string timeStr: format("hh:mm:ss") readonly property string timeStr: format("hh:mm")
function format(fmt: string): string { function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt); return Qt.formatDateTime(clock.date, fmt);
-14
View File
@@ -6,21 +6,7 @@ import Quickshell.Services.UPower
Singleton { Singleton {
id: root id: root
readonly property real batteryPercent: UPower.displayDevice.percentage
readonly property list<UPowerDevice> devices: UPower.devices.values readonly property list<UPowerDevice> devices: UPower.devices.values
readonly property UPowerDevice displayDevice: UPower.displayDevice readonly property UPowerDevice displayDevice: UPower.displayDevice
readonly property bool onBattery: UPower.onBattery readonly property bool onBattery: UPower.onBattery
// property bool toastShown
//
// Connections {
// target: UPower
//
// function onPercentageChanged(): {
// if (root.batteryPercent >= 0.2 && toastShown)
// return;
//
// root.toastShown = true;
// Toaster.toast(qsTr("Battery "))
// }
// }
} }
-79
View File
@@ -11,13 +11,10 @@ Singleton {
id: root id: root
property int availableUpdates: 0 property int availableUpdates: 0
property string cmd: ""
property bool commandReady property bool commandReady
property bool loaded property bool loaded
property double now: Date.now() property double now: Date.now()
property var updates: ({}) property var updates: ({})
property bool updating
property string updatingPackage: ""
function formatUpdateTime(timestamp) { function formatUpdateTime(timestamp) {
const diffMs = root.now - timestamp; const diffMs = root.now - timestamp;
@@ -37,22 +34,6 @@ Singleton {
return Qt.formatDateTime(new Date(timestamp), "dd hh:mm"); return Qt.formatDateTime(new Date(timestamp), "dd hh:mm");
} }
function performPackageUpdate(pkg: string): void {
if (root.cmd === "pacman")
pkgUpdateProc.command = ["pkexec", root.cmd, "--noconfirm", "-Sy", pkg];
else
pkgUpdateProc.command = [root.cmd, "--noconfirm", "--sudo", "pkexec", "-Sy", pkg];
pkgUpdateProc.running = true;
}
function performSystemUpdate(): void {
if (root.cmd === "pacman")
sysUpdateProc.command = ["pkexec", root.cmd, "--noconfirm", "-Syu"];
else
sysUpdateProc.command = [root.cmd, "--noconfirm", "--sudo", "pkexec", "-Syu"];
sysUpdateProc.running = true;
}
onUpdatesChanged: { onUpdatesChanged: {
if (!root.loaded) if (!root.loaded)
return; return;
@@ -111,28 +92,6 @@ Singleton {
} }
} }
Process {
id: updateCmdDetect
command: ["sh", "-c", "command -v yay || command -v paru"]
running: true
stdout: StdioCollector {
onStreamFinished: {
const cmd = this.text.trim();
let helper;
if (cmd.length > 0) {
helper = cmd.split("/").pop();
} else {
helper = "pacman";
}
root.cmd = helper;
}
}
}
Process { Process {
id: updatesProc id: updatesProc
@@ -156,44 +115,6 @@ Singleton {
} }
} }
Process {
id: sysUpdateProc
command: []
running: false
stdout: StdioCollector {
onStreamFinished: {
root.updating = false;
}
}
onRunningChanged: {
if (running)
root.updating = true;
}
}
Process {
id: pkgUpdateProc
command: []
running: false
stdout: StdioCollector {
onStreamFinished: {
root.updating = false;
}
}
onRunningChanged: {
if (running) {
root.updatingPackage = command[command.length - 1];
root.updating = true;
}
}
}
Timer { Timer {
id: saveTimer id: saveTimer
-51
View File
@@ -1,9 +1,7 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import QtQuick
import ZShell.Models import ZShell.Models
import qs.Config import qs.Config
import qs.Modules import qs.Modules
@@ -14,17 +12,10 @@ Searcher {
id: root id: root
property string actualCurrent: WallpaperPath.currentWallpaperPath property string actualCurrent: WallpaperPath.currentWallpaperPath
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent readonly property string current: showPreview ? previewPath : actualCurrent
property alias monitorCrops: monitorCrops
property string previewPath property string previewPath
property bool recentlyChanged
property bool showPreview: false property bool showPreview: false
function getCrop(screen: string): var {
return root.crops[screen];
}
function preview(path: string): void { function preview(path: string): void {
previewPath = path; previewPath = path;
if (Config.general.color.schemeGeneration) if (Config.general.color.schemeGeneration)
@@ -32,35 +23,9 @@ Searcher {
showPreview = true; showPreview = true;
} }
function setCrop(screen: string, rect: rect, scaledRect: rect, zoom: real): void {
let updated = Object.assign({}, root.crops);
if (zoom <= 0)
zoom = 1.0;
else if (zoom > 5.0)
zoom = 5.0;
updated[screen] = {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
scaledX: scaledRect.x,
scaledY: scaledRect.y,
scaledWidth: scaledRect.width,
scaledHeight: scaledRect.height,
zoom: zoom
};
root.crops = updated;
monitorCrops.writeAdapter();
monitorCrops.reload();
}
function setWallpaper(path: string): void { function setWallpaper(path: string): void {
actualCurrent = path; actualCurrent = path;
WallpaperPath.currentWallpaperPath = path; WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 0, 0), Qt.rect(0, 0, 0, 0), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]); Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.general.color.schemeGeneration) if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
@@ -87,22 +52,6 @@ Searcher {
target: "wallpaper" target: "wallpaper"
} }
FileView {
id: monitorCrops
path: `${Paths.state}/wallpaper-crops.json`
watchChanges: true
onAdapterUpdated: writeAdapter()
onFileChanged: reload()
JsonAdapter {
id: adapter
property var monitorCrops: ({})
}
}
FileSystemModel { FileSystemModel {
id: wallpapers id: wallpapers
+1 -1
View File
@@ -3,7 +3,7 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Config import qs.Config
import qs.Modules import qs.Modules
import qs.Helpers import qs.Helpers as Helpers
import qs.Components import qs.Components
CustomRect { CustomRect {
+31
View File
@@ -0,0 +1,31 @@
import Quickshell
import QtQuick
import qs.Config
import qs.Helpers
import qs.Components
CustomRect {
id: root
required property PersistentProperties visibilities
anchors.bottom: parent.bottom
anchors.bottomMargin: 6
anchors.top: parent.top
anchors.topMargin: 6
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: 40
radius: Appearance.rounding.full
StateLayer {
onClicked: {
root.visibilities.dashboard = !root.visibilities.dashboard;
}
}
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onSurface
text: "widgets"
}
}
+74 -22
View File
@@ -12,29 +12,90 @@ Item {
required property Canvas drawing required property Canvas drawing
property bool expanded: true property bool expanded: true
property real offsetScale: shouldBeActive ? 0 : 1
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeActive: visibilities.isDrawing readonly property bool shouldBeActive: visibilities.isDrawing
required property var visibilities required property var visibilities
anchors.leftMargin: (-implicitWidth - 5) * offsetScale
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
implicitWidth: root.expanded ? content.implicitWidth : icon.implicitWidth implicitWidth: 0
opacity: 1 - offsetScale visible: width > 0
visible: offsetScale < 1
Behavior on implicitWidth { states: [
State {
name: "hidden"
when: !root.shouldBeActive
PropertyChanges {
root.implicitWidth: 0
}
PropertyChanges {
icon.opacity: 0
}
PropertyChanges {
content.opacity: 0
}
},
State {
name: "collapsed"
when: root.shouldBeActive && !root.expanded
PropertyChanges {
root.implicitWidth: icon.implicitWidth
}
PropertyChanges {
icon.opacity: 1
}
PropertyChanges {
content.opacity: 0
}
},
State {
name: "visible"
when: root.shouldBeActive && root.expanded
PropertyChanges {
root.implicitWidth: content.implicitWidth
}
PropertyChanges {
icon.opacity: 0
}
PropertyChanges {
content.opacity: 1
}
}
]
transitions: [
Transition {
from: "*"
to: "*"
ParallelAnimation {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial easing.bezierCurve: MaterialEasing.expressiveEffects
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial property: "implicitWidth"
target: root
} }
}
Behavior on offsetScale {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial duration: Appearance.anim.durations.small
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial property: "opacity"
target: icon
}
Anim {
duration: Appearance.anim.durations.small
property: "opacity"
target: content
} }
} }
}
]
onVisibleChanged: { onVisibleChanged: {
if (!visible) if (!visible)
@@ -48,12 +109,8 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
height: content.contentItem.height height: content.contentItem.height
opacity: root.expanded ? 0 : 1 opacity: 1
Behavior on opacity {
Anim {
}
}
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: "arrow_forward_ios" text: "arrow_forward_ios"
@@ -65,12 +122,7 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
opacity: root.expanded ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: Content { sourceComponent: Content {
drawing: root.drawing drawing: root.drawing
visibilities: root.visibilities visibilities: root.visibilities
+1
View File
@@ -13,6 +13,7 @@ Searcher {
function launch(entry: DesktopEntry): void { function launch(entry: DesktopEntry): void {
appDb.incrementFrequency(entry.id); appDb.incrementFrequency(entry.id);
console.log(root.command);
if (entry.runInTerminal) if (entry.runInTerminal)
Quickshell.execDetached({ Quickshell.execDetached({
+1 -6
View File
@@ -8,8 +8,8 @@ import qs.Helpers
Scope { Scope {
id: root id: root
readonly property bool enabled: !Players.list.some(p => p.isPlaying)
required property Lock lock required property Lock lock
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
function handleIdleAction( action: var ): void { function handleIdleAction( action: var ): void {
if ( !action ) if ( !action )
@@ -19,10 +19,6 @@ Scope {
lock.lock.locked = true; lock.lock.locked = true;
else if ( action === "unlock" ) else if ( action === "unlock" )
lock.lock.locked = false; lock.lock.locked = false;
else if (action === "dpms on")
Hypr.dispatch('hl.dsp.dpms({ action = "enable" })');
else if (action === "dpms off")
Hypr.dispatch('hl.dsp.dpms({ action = "disable" })');
else if ( typeof action === "string" ) else if ( typeof action === "string" )
Hypr.dispatch( action ); Hypr.dispatch( action );
else else
@@ -37,7 +33,6 @@ Scope {
enabled: root.enabled && modelData.timeout > 0 ? true : false enabled: root.enabled && modelData.timeout > 0 ? true : false
timeout: modelData.timeout timeout: modelData.timeout
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction ) onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
} }
} }
-3
View File
@@ -58,7 +58,6 @@ CustomRect {
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
height: Config.notifs.sizes.image height: Config.notifs.sizes.image
source: Qt.resolvedUrl(root.image) source: Qt.resolvedUrl(root.image)
visible: Config.lock.showNotifIcon
width: Config.notifs.sizes.image width: Config.notifs.sizes.image
} }
} }
@@ -285,8 +284,6 @@ CustomRect {
Layout.fillWidth: true Layout.fillWidth: true
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: { text: {
if (!Config.lock.showNotifContent)
return "Unlock to view";
const summary = modelData.summary.replace(/\n/g, " "); const summary = modelData.summary.replace(/\n/g, " ");
const body = modelData.body.replace(/\n/g, " "); const body = modelData.body.replace(/\n/g, " ");
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline; const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
-2
View File
@@ -30,8 +30,6 @@ Scope {
} else { } else {
buffer = buffer.slice(0, -1); buffer = buffer.slice(0, -1);
} }
} else if (event.key === Qt.Key_Escape) {
buffer = "";
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) { } else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
// No illegal characters (you are insane if you use unicode in your password) // No illegal characters (you are insane if you use unicode in your password)
buffer += event.text; buffer += event.text;
+9 -3
View File
@@ -10,6 +10,7 @@ Item {
readonly property int padding: 6 readonly property int padding: 6
required property Item panels required property Item panels
required property Item sidebarPanel
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -43,7 +44,7 @@ Item {
return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2); return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
} }
implicitWidth: Config.notifs.sizes.width + padding * 2 implicitWidth: Math.max(sidebarPanel.width * (1 - sidebarPanel.offsetScale), Config.notifs.sizes.width + padding * 2)
Behavior on implicitHeight { Behavior on implicitHeight {
Anim { Anim {
@@ -72,8 +73,9 @@ Item {
required property NotifServer.Notif modelData required property NotifServer.Notif modelData
readonly property alias nonAnimHeight: notif.nonAnimHeight readonly property alias nonAnimHeight: notif.nonAnimHeight
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8) implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
implicitWidth: notif.implicitWidth
ListView.onRemove: removeAnim.start() ListView.onRemove: removeAnim.start()
onIndexChanged: { onIndexChanged: {
@@ -124,16 +126,20 @@ Item {
} }
ClippingRectangle { ClippingRectangle {
// implicitWidth: notif.implicitWidth
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: wrapper.idx === 0 ? 0 : 8 anchors.topMargin: wrapper.idx === 0 ? 0 : 8
color: "transparent" color: "transparent"
implicitHeight: notif.implicitHeight implicitHeight: notif.implicitHeight
implicitWidth: notif.implicitWidth
radius: Appearance.rounding.smallest / 2 radius: Appearance.rounding.smallest / 2
Notification { Notification {
id: notif id: notif
anchors.left: parent.left
anchors.right: parent.right
modelData: wrapper.modelData modelData: wrapper.modelData
} }
} }
+1 -1
View File
@@ -20,9 +20,9 @@ CustomRect {
required property NotifServer.Notif modelData required property NotifServer.Notif modelData
readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2 readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2
// implicitWidth: Config.notifs.sizes.width
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
implicitHeight: inner.implicitHeight implicitHeight: inner.implicitHeight
implicitWidth: Config.notifs.sizes.width
radius: 6 radius: 6
x: Config.notifs.sizes.width x: Config.notifs.sizes.width
@@ -78,10 +78,6 @@ LazyListView {
} }
} }
onDoubleClicked: event => {
if (event.button === Qt.LeftButton)
notifInner.toggleExpand(!notifInner.expanded);
}
onPositionChanged: event => { onPositionChanged: event => {
if (pressed) { if (pressed) {
const diffY = event.y - startY; const diffY = event.y - startY;
@@ -79,10 +79,6 @@ LazyListView {
Component.onCompleted: modelData?.lock(this) Component.onCompleted: modelData?.lock(this)
Component.onDestruction: modelData?.unlock(this) Component.onDestruction: modelData?.unlock(this)
onDoubleClicked: event => {
if (event.button === Qt.LeftButton)
root.requestToggleExpand(!root.expanded);
}
onPositionChanged: event => { onPositionChanged: event => {
if (pressed && !root.expanded) { if (pressed && !root.expanded) {
const diffY = event.y - startY; const diffY = event.y - startY;
@@ -5,7 +5,6 @@ import QtQuick.Layouts
import qs.Components import qs.Components
import qs.Config import qs.Config
import qs.Modules import qs.Modules
import qs.Helpers
import qs.Daemons import qs.Daemons
CustomRect { CustomRect {
@@ -33,7 +32,7 @@ CustomRect {
Toggle { Toggle {
checked: Network.wifiEnabled checked: Network.wifiEnabled
icon: Network.wifiEnabled ? "wifi" : "wifi_off" icon: Network.wifiEnabled ? "wifi" : "wifi_off"
visible: QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi) visible: QSNetwork.Networking.devices.values.length > 0
onClicked: Network.toggleWifi() onClicked: Network.toggleWifi()
} }
@@ -80,13 +79,6 @@ CustomRect {
adapter.enabled = !adapter.enabled; adapter.enabled = !adapter.enabled;
} }
} }
Toggle {
checked: GameMode.enabled
icon: GameMode.enabled ? "videogame_asset" : "videogame_asset_off"
onClicked: GameMode.enabled = !GameMode.enabled
}
} }
} }
+1
View File
@@ -17,6 +17,7 @@ Item {
id: content id: content
panels: root.panels panels: root.panels
sidebarPanel: root.sidebarPanel
visibilities: root.visibilities visibilities: root.visibilities
} }
} }
+1 -1
View File
@@ -17,7 +17,7 @@ CustomRect {
color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2 implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2 implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2
radius: Appearance.rounding.full radius: height / 2
StateLayer { StateLayer {
onClicked: root.visibilities.resources = !root.visibilities.resources onClicked: root.visibilities.resources = !root.visibilities.resources
-18
View File
@@ -104,18 +104,6 @@ Item {
key: "launcher" key: "launcher"
name: "Launcher" name: "Launcher"
} }
ListElement {
icon: "screenshot_region"
key: "screenshot"
name: "Screenshot"
}
ListElement {
icon: "cached"
key: "updates"
name: "Updates"
}
} }
CustomClippingRect { CustomClippingRect {
@@ -183,15 +171,9 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
Layout.preferredWidth: icon.contentWidth Layout.preferredWidth: icon.contentWidth
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
fill: categoryItem.index === clayout.currentIndex ? 1 : 0
font.pointSize: Appearance.font.size.small * 2 font.pointSize: Appearance.font.size.small * 2
text: categoryItem.icon text: categoryItem.icon
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
Behavior on fill {
Anim {
}
}
} }
CustomText { CustomText {
+7 -8
View File
@@ -1,4 +1,3 @@
import Quickshell
import QtQuick.Layouts import QtQuick.Layouts
import qs.Modules.Settings.Controls import qs.Modules.Settings.Controls
import qs.Config import qs.Config
@@ -6,8 +5,6 @@ import qs.Config
SettingsPage { SettingsPage {
id: root id: root
required property ShellScreen screen
SettingsSection { SettingsSection {
sectionId: "Wallpaper" sectionId: "Wallpaper"
@@ -32,11 +29,13 @@ SettingsPage {
step: 50 step: 50
} }
Separator { // Separator {
} // }
//
WallpaperCropper { // WallpaperCropper {
} // Layout.fillWidth: true
// Layout.preferredHeight: 300
// }
} }
SettingsSection { SettingsSection {
+6 -16
View File
@@ -19,8 +19,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Height" name: "Height"
min: 1
object: Config.barConfig object: Config.barConfig
setting: "height" setting: "height"
} }
@@ -29,8 +29,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Rounding" name: "Rounding"
min: 0
object: Config.barConfig object: Config.barConfig
setting: "rounding" setting: "rounding"
} }
@@ -39,21 +39,11 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Border" name: "Border"
min: 0
object: Config.barConfig object: Config.barConfig
setting: "border" setting: "border"
} }
Separator {
}
SettingSpinBox {
min: 0
name: "Smoothing"
object: Config.barConfig
setting: "smoothing"
}
} }
SettingsSection { SettingsSection {
@@ -155,8 +145,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Dock height" name: "Dock height"
min: 1
object: Config.dock object: Config.dock
setting: "height" setting: "height"
} }
@@ -183,8 +173,8 @@ SettingsPage {
} }
SettingStringList { SettingStringList {
addLabel: qsTr("Add pinned app")
name: "Pinned apps" name: "Pinned apps"
addLabel: qsTr("Add pinned app")
object: Config.dock object: Config.dock
setting: "pinnedApps" setting: "pinnedApps"
} }
@@ -193,8 +183,8 @@ SettingsPage {
} }
SettingStringList { SettingStringList {
addLabel: qsTr("Add ignored regex")
name: "Ignored app regexes" name: "Ignored app regexes"
addLabel: qsTr("Add ignored regex")
object: Config.dock object: Config.dock
setting: "ignoredAppRegexes" setting: "ignoredAppRegexes"
} }
-9
View File
@@ -47,15 +47,6 @@ SettingsPage {
object: Config.general object: Config.general
setting: "desktopIcons" setting: "desktopIcons"
} }
Separator {
}
SettingInput {
name: "Date format"
object: Config.general
setting: "dateFormat"
}
} }
SettingsSection { SettingsSection {
+5 -23
View File
@@ -31,8 +31,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Max fingerprint tries" name: "Max fingerprint tries"
min: 1
object: Config.lock object: Config.lock
setting: "maxFprintTries" setting: "maxFprintTries"
step: 1 step: 1
@@ -41,27 +41,9 @@ SettingsPage {
Separator { Separator {
} }
SettingSwitch {
name: "Show notification details"
object: Config.lock
setting: "showNotifContent"
}
Separator {
}
SettingSwitch {
name: "Show notification icon"
object: Config.lock
setting: "showNotifIcon"
}
Separator {
}
SettingSpinBox { SettingSpinBox {
min: 0
name: "Blur amount" name: "Blur amount"
min: 0
object: Config.lock object: Config.lock
setting: "blurAmount" setting: "blurAmount"
step: 1 step: 1
@@ -71,9 +53,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Height multiplier"
max: 2 max: 2
min: 0.1 min: 0.1
name: "Height multiplier"
object: Config.lock.sizes object: Config.lock.sizes
setting: "heightMult" setting: "heightMult"
step: 0.05 step: 0.05
@@ -83,9 +65,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Aspect ratio"
max: 4 max: 4
min: 0.5 min: 0.5
name: "Aspect ratio"
object: Config.lock.sizes object: Config.lock.sizes
setting: "ratio" setting: "ratio"
step: 0.05 step: 0.05
@@ -95,8 +77,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 100
name: "Center width" name: "Center width"
min: 100
object: Config.lock.sizes object: Config.lock.sizes
setting: "centerWidth" setting: "centerWidth"
step: 10 step: 10
@@ -22,13 +22,6 @@ ColumnLayout {
Config.save(); Config.save();
} }
function deleteTimeoutEntry(index) {
let list = [...Config.general.idle.timeouts];
list.splice(index, 1);
Config.general.idle.timeouts = list;
Config.save();
}
function updateTimeoutEntry(i, key, value) { function updateTimeoutEntry(i, key, value) {
const list = [...Config.general.idle.timeouts]; const list = [...Config.general.idle.timeouts];
let entry = list[i]; let entry = list[i];
@@ -56,9 +49,6 @@ ColumnLayout {
onAddActiveActionRequested: { onAddActiveActionRequested: {
root.updateTimeoutEntry(index, "activeAction", ""); root.updateTimeoutEntry(index, "activeAction", "");
} }
onDeleteRequested: function (index) {
root.deleteTimeoutEntry(index);
}
onFieldEdited: function (key, value) { onFieldEdited: function (key, value) {
root.updateTimeoutEntry(index, key, value); root.updateTimeoutEntry(index, key, value);
} }
-130
View File
@@ -1,130 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
import qs.Components
SettingsPage {
SettingsSection {
sectionId: "Screenshot"
SettingsHeader {
name: "Screenshot"
}
SettingSwitch {
name: "Enable effects"
object: Config.screenshot
setting: "enable_pp"
}
Separator {
}
CustomSplitButtonRow {
// active: true
label: qsTr("Effects mode")
menuItems: [
MenuItem {
icon: "build"
text: qsTr("Manual")
value: "manual"
},
MenuItem {
icon: "rotate_auto"
text: qsTr("Auto")
value: "auto"
}
]
onSelected: item => {
Config.screenshot.mode = item.value;
Config.save();
}
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Corner radius"
object: Config.screenshot
setting: "corner_radius"
step: 1
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Enable drop shadow"
object: Config.screenshot
setting: "drop_shadow"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Enable rounded corners"
object: Config.screenshot
setting: "rounded_corners"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Shadow blur radius"
object: Config.screenshot
setting: "shadow_blur_radius"
step: 1
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Shadow color broken atm"
object: Config.Screenshot
setting: "shadow_color"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Shadow offset X"
object: Config.screenshot
setting: "shadow_offset_x"
step: 1
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Shadow offset Y"
object: Config.screenshot
setting: "shadow_offset_y"
step: 1
visible: Config.screenshot.mode === "manual"
}
}
}
@@ -1,197 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick.Layouts
import QtQuick
import qs.Config
import qs.Helpers
import qs.Components
import qs.Modules.Settings.Controls
CustomClippingRect {
id: root
radius: Appearance.rounding.normal - Appearance.padding.smaller
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.margins: Appearance.padding.large
spacing: Appearance.spacing.large
MaterialIcon {
font.pointSize: Appearance.font.size.larger * 4
text: "update"
}
ColumnLayout {
CustomText {
font.pointSize: Appearance.font.size.large * 2
text: "System updates"
}
RowLayout {
id: row
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: `${Updates.availableUpdates} available updates`
}
CustomRect {
Layout.preferredHeight: 40
Layout.preferredWidth: 150
color: Updates.updating ? DynamicColors.layer(DynamicColors.palette.m3outline, 2) : DynamicColors.palette.m3primary
radius: Appearance.rounding.full
RowLayout {
anchors.centerIn: parent
MaterialIcon {
animate: true
color: DynamicColors.palette.m3onPrimary
font.pointSize: Appearance.font.size.large
text: Updates.updating ? "update" : "download"
}
CustomText {
color: Updates.updating ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onPrimary
text: "Update all"
}
}
StateLayer {
color: DynamicColors.palette.m3onPrimary
disabled: Updates.updating
onClicked: Updates.performSystemUpdate()
}
}
}
}
}
CustomListView {
id: view
readonly property int itemHeight: 50 + Appearance.padding.smaller * 2
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
contentHeight: height
spacing: Appearance.spacing.normal
delegate: CustomRect {
id: update
required property var modelData
readonly property list<string> sections: modelData.update.split(" ")
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: view.itemHeight
implicitWidth: parent.width
radius: Appearance.rounding.small - Appearance.padding.small
RowLayout {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.smaller
anchors.rightMargin: Appearance.padding.smaller
MaterialIcon {
font.pointSize: Appearance.font.size.large * 2
text: "package_2"
}
ColumnLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.preferredHeight: 25
elide: Text.ElideRight
font.pointSize: Appearance.font.size.large
text: update.sections[0]
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
text: Updates.formatUpdateTime(update.modelData.timestamp)
}
}
RowLayout {
Layout.fillHeight: true
Layout.preferredWidth: 500
MarqueeText {
id: versionFrom
Layout.fillHeight: true
Layout.preferredWidth: 225
animate: true
color: DynamicColors.palette.m3tertiary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: true
pauseMs: 4000
text: update.sections[1]
width: 225
}
MaterialIcon {
Layout.fillHeight: true
color: DynamicColors.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge
horizontalAlignment: Text.AlignHCenter
text: "arrow_right_alt"
verticalAlignment: Text.AlignVCenter
}
MarqueeText {
id: versionTo
Layout.fillHeight: true
Layout.preferredWidth: 225
animate: true
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: true
pauseMs: 4000
text: update.sections[3]
width: 225
}
}
IconButton {
Layout.preferredHeight: width
icon: "download"
onClicked: {
Updates.performPackageUpdate(update.sections[0]);
}
}
}
}
model: ScriptModel {
id: script
objectProp: "update"
values: Object.entries(Updates.updates).sort((a, b) => b[1] - a[1]).map(([update, timestamp]) => ({
update,
timestamp
}))
}
}
}
}
+1 -21
View File
@@ -1,5 +1,3 @@
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
@@ -22,6 +20,7 @@ Item {
required property PersistentProperties visibilities required property PersistentProperties visibilities
function scrollToSetting(section: string, settingName: string) { function scrollToSetting(section: string, settingName: string) {
// Wait for the StackView transition to complete, then scroll
root.pendingSection = section; root.pendingSection = section;
root.pendingSetting = settingName; root.pendingSetting = settingName;
scrollTimer.restart(); scrollTimer.restart();
@@ -75,10 +74,6 @@ Item {
stack.push(osd); stack.push(osd);
else if (currentCategory === "launcher") else if (currentCategory === "launcher")
stack.push(launcher); stack.push(launcher);
else if (currentCategory === "screenshot")
stack.push(screenshot);
else if (currentCategory === "updates")
stack.push(updates);
} }
target: root target: root
@@ -158,7 +153,6 @@ Item {
id: background id: background
Cat.Background { Cat.Background {
screen: root.screen
} }
} }
@@ -231,18 +225,4 @@ Item {
Cat.Launcher { Cat.Launcher {
} }
} }
Component {
id: screenshot
Cat.Screenshot {
}
}
Component {
id: updates
Cat.SystemUpdates {
}
}
} }
+344 -27
View File
@@ -28,11 +28,72 @@ Item {
property int uidCounter: 0 property int uidCounter: 0
property var visualEntries: [] property var visualEntries: []
function beginVisualDrag(uid, modelData, item) {
const pos = item.mapToItem(root, 0, 0);
root.draggedUid = uid;
root.draggedModelData = modelData;
root.dragHeight = item.height;
root.dragStartX = pos.x;
root.dragStartY = pos.y;
root.dragX = pos.x;
root.dragY = pos.y;
root.dragActive = true;
root.dropAnimating = false;
root.pendingCommitEntries = [];
}
function commitVisualOrder(entries) {
const list = [];
for (let i = 0; i < entries.length; i++)
list.push(entries[i].entry);
root.object[root.setting] = list;
Config.save();
root.rebuildVisualEntries();
}
function endVisualDrag() {
const entries = root.visualEntries.slice();
const finalIndex = root.indexForUid(root.draggedUid);
const finalItem = listView.itemAtIndex(finalIndex);
root.dragActive = false;
if (!finalItem) {
root.pendingCommitEntries = entries;
root.finishVisualDrag();
return;
}
const pos = finalItem.mapToItem(root, 0, 0);
root.pendingCommitEntries = entries;
root.dropAnimating = true;
settleX.to = pos.x;
settleY.to = pos.y;
settleAnim.start();
}
function ensureVisualEntries() { function ensureVisualEntries() {
if (!root.dragActive && !root.dropAnimating) if (!root.dragActive && !root.dropAnimating)
root.rebuildVisualEntries(); root.rebuildVisualEntries();
} }
function finishVisualDrag() {
const entries = root.pendingCommitEntries.slice();
root.dragActive = false;
root.dropAnimating = false;
root.draggedUid = "";
root.draggedModelData = null;
root.pendingCommitEntries = [];
root.dragHeight = 0;
root.commitVisualOrder(entries);
}
function iconForId(id) { function iconForId(id) {
switch (id) { switch (id) {
case "workspaces": case "workspaces":
@@ -92,7 +153,7 @@ Item {
case "spacer": case "spacer":
return qsTr("Spacer"); return qsTr("Spacer");
case "activeWindow": case "activeWindow":
return qsTr("Title"); return qsTr("Active window");
case "tray": case "tray":
return qsTr("Tray"); return qsTr("Tray");
case "upower": case "upower":
@@ -102,14 +163,34 @@ Item {
case "clock": case "clock":
return qsTr("Clock"); return qsTr("Clock");
case "notifBell": case "notifBell":
return qsTr("Notifs"); return qsTr("Notification bell");
case "hyprsunset":
return qsTr("Night light");
default: default:
return id; return id;
} }
} }
function moveArrayItem(list, from, to) {
const next = list.slice();
const [item] = next.splice(from, 1);
next.splice(to, 0, item);
return next;
}
function previewVisualMove(from, hovered, before) {
let to = hovered + (before ? 0 : 1);
if (to > from)
to -= 1;
to = Math.max(0, Math.min(visualModel.items.count - 1, to));
if (from === to)
return;
visualModel.items.move(from, to);
root.visualEntries = root.moveArrayItem(root.visualEntries, from, to);
}
function rebuildVisualEntries() { function rebuildVisualEntries() {
const entries = root.object[root.setting] ?? []; const entries = root.object[root.setting] ?? [];
const next = []; const next = [];
@@ -144,6 +225,7 @@ Item {
list[index] = entry; list[index] = entry;
root.object[root.setting] = list; root.object[root.setting] = list;
Config.save(); Config.save();
root.ensureVisualEntries();
} }
Layout.fillWidth: true Layout.fillWidth: true
@@ -151,7 +233,7 @@ Item {
Component.onCompleted: root.rebuildVisualEntries() Component.onCompleted: root.rebuildVisualEntries()
CustomRect { Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.margins: -Appearance.padding.smaller anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer color: DynamicColors.palette.m3primaryContainer
@@ -166,12 +248,39 @@ Item {
} }
} }
RowLayout { ParallelAnimation {
id: settleAnim
onFinished: root.finishVisualDrag()
Anim {
id: settleX
duration: Appearance.anim.durations.normal
property: "dragX"
target: root
}
Anim {
id: settleY
duration: Appearance.anim.durations.normal
property: "dragY"
target: root
}
}
ColumnLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
spacing: Appearance.spacing.smaller
// spacing: Appearance.spacing.smaller CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
DelegateModel { DelegateModel {
id: visualModel id: visualModel
@@ -184,40 +293,248 @@ Item {
} }
} }
Repeater { ListView {
delegate: entryDelegate id: listView
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
boundsBehavior: Flickable.StopAtBounds
clip: false
implicitHeight: contentHeight
implicitWidth: width
interactive: !(root.dragActive || root.dropAnimating)
model: visualModel model: visualModel
spacing: Appearance.spacing.small
add: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
}
addDisplaced: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
displaced: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
move: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
removeDisplaced: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
}
}
Loader {
active: root.dragActive || root.dropAnimating
asynchronous: false
sourceComponent: Item {
Drag.active: root.dragActive
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
height: proxyRect.implicitHeight
implicitHeight: proxyRect.implicitHeight
implicitWidth: listView.width
visible: root.draggedModelData !== null
width: listView.width
x: root.dragX
y: root.dragY
z: 100
Drag.source: QtObject {
property string uid: root.draggedUid
property int visualIndex: root.indexForUid(root.draggedUid)
}
CustomRect {
id: proxyRect
color: DynamicColors.tPalette.m3surface
implicitHeight: proxyRow.implicitHeight + Appearance.padding.small * 2
implicitWidth: parent.width
opacity: 0.95
radius: Appearance.rounding.normal
width: parent.width
RowLayout {
id: proxyRow
anchors.fill: parent
anchors.margins: Appearance.padding.small
spacing: Appearance.spacing.normal
CustomRect {
color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.12)
implicitHeight: 32
implicitWidth: implicitHeight
radius: Appearance.rounding.small
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onSurfaceVariant
text: "drag_indicator"
}
}
MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
text: root.iconForId(root.draggedModelData?.entry?.id ?? "")
}
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.labelForId(root.draggedModelData?.entry?.id ?? "")
}
CustomSwitch {
checked: root.draggedModelData?.entry?.enabled ?? true
enabled: false
}
}
}
} }
} }
Component { Component {
id: entryDelegate id: entryDelegate
IconButton { DropArea {
required property int index id: slot
readonly property var entryData: modelData.entry
required property var modelData required property var modelData
readonly property string uid: modelData.uid
Layout.fillWidth: true function previewReorder(drag) {
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0) const source = drag.source;
checked: modelData.entry.enabled ?? true if (!source || !source.uid || source.uid === slot.uid)
font: Appearance.font.family.sans return;
// icon: root.iconForId(modelData.entry.id)
icon: root.labelForId(modelData.entry.id)
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
toggle: true
visible: !["spacer", "upower", "dash", "audio"].some(prefix => modelData.entry.id.startsWith(prefix))
Behavior on Layout.preferredWidth { const from = source.visualIndex;
const hovered = slot.DelegateModel.itemsIndex;
if (from < 0 || hovered < 0)
return;
root.previewVisualMove(from, hovered, drag.y < height / 2);
}
height: entryRow.implicitHeight
implicitHeight: entryRow.implicitHeight
implicitWidth: listView.width
width: ListView.view ? ListView.view.width : listView.width
onEntered: drag => previewReorder(drag)
onPositionChanged: drag => previewReorder(drag)
CustomRect {
id: entryRow
anchors.fill: parent
color: DynamicColors.tPalette.m3surface
implicitHeight: entryLayout.implicitHeight + Appearance.padding.small * 2
implicitWidth: parent.width
opacity: root.draggedUid === slot.uid ? 0 : 1
radius: Appearance.rounding.full
Behavior on opacity {
Anim { Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
} }
} }
onClicked: root.updateEntry(index, internalChecked) RowLayout {
id: entryLayout
anchors.fill: parent
anchors.margins: Appearance.padding.small
spacing: Appearance.spacing.normal
CustomRect {
id: handle
color: Qt.alpha(DynamicColors.palette.m3onSurface, handleDrag.active ? 0.12 : handleHover.hovered ? 0.09 : 0.06)
implicitHeight: 32
implicitWidth: implicitHeight
radius: Appearance.rounding.full
Behavior on color {
CAnim {
}
}
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onSurfaceVariant
text: "drag_indicator"
}
HoverHandler {
id: handleHover
cursorShape: handleDrag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
}
DragHandler {
id: handleDrag
enabled: true
grabPermissions: PointerHandler.CanTakeOverFromAnything
target: null
xAxis.enabled: false
yAxis.enabled: true
onActiveChanged: {
if (active) {
root.beginVisualDrag(slot.uid, slot.modelData, entryRow);
} else if (root.draggedUid === slot.uid) {
root.endVisualDrag();
}
}
onActiveTranslationChanged: {
if (!active || root.draggedUid !== slot.uid)
return;
root.dragX = root.dragStartX;
root.dragY = root.dragStartY + activeTranslation.y;
}
}
}
MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
text: root.iconForId(slot.entryData.id)
}
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.labelForId(slot.entryData.id)
}
CustomSwitch {
Layout.rightMargin: Appearance.padding.small
checked: slot.entryData.enabled ?? true
onToggled: root.updateEntry(slot.DelegateModel.itemsIndex, checked)
}
}
}
} }
} }
} }
+8 -31
View File
@@ -10,7 +10,6 @@ Item {
required property var modelData required property var modelData
signal addActiveActionRequested signal addActiveActionRequested
signal deleteRequested(int index)
signal fieldEdited(string key, var value) signal fieldEdited(string key, var value)
Layout.fillWidth: true Layout.fillWidth: true
@@ -66,64 +65,42 @@ Item {
HoverHandler { HoverHandler {
id: nameHover id: nameHover
}
HoverIconButton {
id: editButton
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large
icon: "edit"
shouldBeVisible: nameHover.hovered && !nameCell.editing
onClicked: nameCell.beginEdit()
} }
CustomText { CustomText {
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: nameHover.hovered ? editButton.width + Appearance.spacing.smaller * 2 : 0 anchors.right: editButton.left
anchors.right: deleteButton.left
anchors.rightMargin: Appearance.spacing.small anchors.rightMargin: Appearance.spacing.small
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight // enable if CustomText supports it elide: Text.ElideRight // enable if CustomText supports it
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: root.modelData.name text: root.modelData.name
visible: !nameCell.editing visible: !nameCell.editing
Behavior on anchors.leftMargin {
Anim {
}
}
} }
HoverIconButton { IconButton {
id: deleteButton id: editButton
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large font.pointSize: Appearance.font.size.large
icon: "delete" icon: "edit"
shouldBeVisible: nameHover.hovered && !nameCell.editing visible: nameHover.hovered && !nameCell.editing
onClicked: root.deleteRequested(root.index) onClicked: nameCell.beginEdit()
} }
CustomRect { CustomRect {
anchors.left: parent.left anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.tPalette.m3surface color: DynamicColors.tPalette.m3surface
implicitHeight: nameEditor.implicitHeight + (Appearance.padding.normal * 2)
implicitWidth: Math.min(nameEditor.contentWidth + (Appearance.padding.normal * 2), parent.width - Appearance.padding.normal)
radius: Appearance.rounding.small radius: Appearance.rounding.small
visible: nameCell.editing visible: nameCell.editing
CustomTextField { CustomTextField {
id: nameEditor id: nameEditor
anchors.centerIn: parent anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, nameCell.width - Appearance.padding.normal)
text: nameCell.draftName text: nameCell.draftName
Keys.onEscapePressed: { Keys.onEscapePressed: {
@@ -38,7 +38,7 @@ Item {
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5 Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
Layout.preferredWidth: 500 Layout.preferredWidth: 500
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale radius: 21 + Appearance.padding.normal
CustomRect { CustomRect {
id: searchBox id: searchBox
+71 -230
View File
@@ -1,261 +1,102 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Hyprland
import ZShell.Internal
import qs.Config import qs.Config
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
Item { ColumnLayout {
id: wrapper
property bool changesMade: false
signal requestCrop
Layout.fillWidth: true
Layout.preferredHeight: 400
IconButton {
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: parent.top
icon: "check"
opacity: wrapper.changesMade ? 1 : 0
scale: wrapper.changesMade ? 1 : 0
z: 2
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
onClicked: {
wrapper.requestCrop();
wrapper.changesMade = false;
}
}
RowLayout {
id: root id: root
anchors.fill: parent spacing: 15
spacing: Appearance.spacing.normal width: Math.min(parent ? parent.width : 600, 600)
Repeater { Rectangle {
model: ScriptModel { id: previewContainer
values: [...Quickshell.screens].sort((a, b) => {
return a.x - b.x;
})
}
Item {
id: delegate
required property ShellScreen modelData
function applyCrop(): void {
const croprect = cropRect.mapToItem(scaledImg, 0, 0, cropRect.width, cropRect.height);
const upscaledRect = Qt.rect((croprect.x - cropRect.imageX) / scaledImg.paintedWidth, (croprect.y - cropRect.imageY) / scaledImg.paintedHeight, croprect.width / scaledImg.paintedWidth, croprect.height / scaledImg.paintedHeight);
Wallpapers.setCrop(delegate.modelData.name, upscaledRect, croprect, cropRect.zoom);
}
function zoomClipRect(zoom: real): void {
let oldCenterX = cropRect.x + cropRect.width * 0.5;
let oldCenterY = cropRect.y + cropRect.height * 0.5;
cropRect.zoom = zoom;
cropRect.x = oldCenterX - cropRect.width * 0.5;
cropRect.y = oldCenterY - cropRect.height * 0.5;
cropRect.clampToBounds();
}
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.preferredWidth: height * (Quickshell.screens.length > 0 ? (Quickshell.screens[0].height / Math.max(1, Quickshell.screens[0].width)) : 16 / 9)
clip: true
color: DynamicColors.palette.m3surfaceContainer
radius: Config.appearance.rounding.scale * 10
Connections { Image {
function onRequestCrop(): void { id: img
delegate.applyCrop();
}
target: wrapper anchors.fill: parent
}
RowLayout {
id: sliderLayout
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 30
spacing: Appearance.spacing.large
CustomText {
text: qsTr("Crop scale")
}
CustomSlider {
id: zoomSlider
Layout.fillWidth: true
Layout.preferredHeight: 10
from: 1.0
to: 5.0
value: cropRect.zoom
onMoved: {
delegate.zoomClipRect(value);
wrapper.changesMade = true;
}
}
}
CachingImage {
id: scaledImg
property var displayData
property real monitorScale: 1.0
anchors.bottom: sliderLayout.top
anchors.bottomMargin: Appearance.spacing.normal
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
// retainWhileLoading: true
source: Wallpapers.current source: Wallpapers.current
sourceSize.height: parent.height
sourceSize.width: parent.width
onPaintedWidthChanged: { Rectangle {
if (paintedWidth > 0) {
scaledImg.displayData = Wallpapers.getCrop(delegate.modelData.name);
cropRect.zoom = Wallpapers.getCrop(delegate.modelData.name).zoom;
cropRect.restoreFromData();
}
}
onSourceChanged: cropRect.clampToBounds()
onStatusChanged: if (scaledImg.status == Image.Ready)
cropRect.clampToBounds()
CustomText {
id: monitorId
anchors.centerIn: parent
color: Qt.alpha(DynamicColors.palette.m3surface, 0.85)
font.pointSize: Appearance.font.size.large * 4
style: Text.Outline
styleColor: DynamicColors.palette.m3onSurface
text: delegate.modelData.name
}
CustomRect {
id: cropRect id: cropRect
property real aspectRatio: delegate.modelData.width / delegate.modelData.height property real cropHeight: (imageAspect > screenAspect ? paintedHeight : paintedWidth / screenAspect) / Config.background.zoom
readonly property real baseHeight: baseWidth / aspectRatio property real cropWidth: (imageAspect > screenAspect ? paintedHeight * screenAspect : paintedWidth) / Config.background.zoom
readonly property real baseWidth: { property real imageAspect: Math.max(1, paintedWidth) / Math.max(1, paintedHeight)
let fittedHeight = scaledImg.paintedHeight; property real paintedHeight: img.paintedHeight > 0 ? img.paintedHeight : img.height
let fittedWidth = fittedHeight * aspectRatio; property real paintedWidth: img.paintedWidth > 0 ? img.paintedWidth : img.width
property real paintedX: (img.width - paintedWidth) / 2
if (fittedWidth > scaledImg.paintedWidth) { property real paintedY: (img.height - paintedHeight) / 2
fittedWidth = scaledImg.paintedWidth; property real screenAspect: Quickshell.screens.length > 0 ? (Quickshell.screens[0].width / Math.max(1, Quickshell.screens[0].height)) : 16 / 9
fittedHeight = fittedWidth / aspectRatio;
}
return fittedWidth;
}
readonly property real imageX: (scaledImg.width - scaledImg.paintedWidth) / 2
readonly property real imageY: (scaledImg.height - scaledImg.paintedHeight) / 2
property real imgAspectRatio: scaledImg.paintedWidth / scaledImg.paintedHeight
property real zoom: scaledImg.displayData.zoom
function centerInImage() {
x = imageX + (scaledImg.paintedWidth - width) / 2;
y = imageY + (scaledImg.paintedHeight - height) / 2;
}
function clampToBounds() {
x = Math.max(imageX, Math.min(x, imageX + scaledImg.paintedWidth - width));
y = Math.max(imageY, Math.min(y, imageY + scaledImg.paintedHeight - height));
}
function restoreFromData() {
let data = scaledImg.displayData;
if (data && data.scaledX !== 0 || data.scaledY !== 0 || data.scaledWidth !== 0 || data.scaledHeight !== 0) {
x = data.scaledX;
y = data.scaledY;
clampToBounds();
} else {
zoom = 1.0;
centerInImage();
}
}
border.color: DynamicColors.palette.m3primary border.color: DynamicColors.palette.m3primary
border.width: 2 border.width: 2
height: baseHeight / zoom color: Qt.alpha(DynamicColors.palette.m3primaryContainer, 0.3)
opacity: 1 height: cropHeight
width: baseWidth / zoom width: cropWidth
x: paintedX + (paintedWidth - width) * Config.background.alignX
y: paintedY + (paintedHeight - height) * Config.background.alignY
Behavior on opacity { DragHandler {
Anim { target: null
onActiveTranslationChanged: {
if (active) {
let newX = cropRect.x - cropRect.paintedX + translation.x;
let newY = cropRect.y - cropRect.paintedY + translation.y;
let rangeX = cropRect.paintedWidth - cropRect.width;
let rangeY = cropRect.paintedHeight - cropRect.height;
if (rangeX > 0) {
let valX = newX / rangeX;
Config.background.alignX = Math.max(0.0, Math.min(1.0, valX));
Config.save();
}
if (rangeY > 0) {
let valY = newY / rangeY;
Config.background.alignY = Math.max(0.0, Math.min(1.0, valY));
Config.save();
}
}
} }
} }
Component.onCompleted: clampToBounds() PinchHandler {
onHeightChanged: clampToBounds() maximumScale: 5.0
onWidthChanged: clampToBounds() minimumScale: 1.0
target: null
onActiveScaleChanged: {
if (active) {
let newZoom = Config.background.zoom * (1 / (1 + (activeScale - 1) * 0.1));
Config.background.zoom = Math.max(1.0, Math.min(newZoom, 5.0));
}
}
}
}
}
} }
MouseArea { SettingSpinBox {
id: mouse max: 5.0
min: 1.0
function updateCrop(mouseX, mouseY) { name: "Zoom"
let nx = mouseX - cropRect.width * 0.5; object: Config.background
let ny = mouseY - cropRect.height * 0.5; setting: "zoom"
step: 0.1
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + scaledImg.paintedWidth - cropRect.width));
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + scaledImg.paintedHeight - cropRect.height));
cropRect.x = nx;
cropRect.y = ny;
}
anchors.fill: parent
hoverEnabled: true
preventStealing: true
onPositionChanged: mouse => {
if (pressed) {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
}
onPressed: mouse => {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
onReleased: {
wrapper.changesMade = true;
}
}
}
}
}
} }
} }
+34 -28
View File
@@ -23,13 +23,26 @@ GridView {
delegate: Item { delegate: Item {
required property int index required property int index
readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent
readonly property real itemMargin: Appearance.spacing.normal readonly property real itemMargin: Appearance.spacing.normal / 2
readonly property real itemRadius: Appearance.rounding.small readonly property real itemRadius: Appearance.rounding.normal
required property var modelData required property var modelData
height: root.cellHeight height: root.cellHeight
width: root.cellWidth width: root.cellWidth
StateLayer {
function onClicked(): void {
Wallpapers.setWallpaper(modelData.path);
}
anchors.bottomMargin: itemMargin
anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
radius: itemRadius
}
CustomClippingRect { CustomClippingRect {
id: image id: image
@@ -40,6 +53,8 @@ GridView {
anchors.topMargin: itemMargin anchors.topMargin: itemMargin
antialiasing: true antialiasing: true
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
layer.enabled: true
layer.smooth: true
radius: itemRadius radius: itemRadius
CachingImage { CachingImage {
@@ -85,13 +100,29 @@ GridView {
} }
} }
Timer {
id: fallbackTimer
property bool triggered: false
interval: 800
running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null
onTriggered: triggered = true
}
}
Rectangle { Rectangle {
anchors.bottomMargin: itemMargin
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
antialiasing: true antialiasing: true
border.color: DynamicColors.palette.m3primary border.color: DynamicColors.palette.m3primary
border.width: isCurrent ? 2 : 0 border.width: isCurrent ? 2 : 0
color: "transparent" color: "transparent"
radius: itemRadius + 2 radius: itemRadius - border.width
smooth: true smooth: true
Behavior on border.width { Behavior on border.width {
@@ -111,30 +142,5 @@ GridView {
visible: isCurrent visible: isCurrent
} }
} }
Timer {
id: fallbackTimer
property bool triggered: false
interval: 800
running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null
onTriggered: triggered = true
}
}
StateLayer {
function onClicked(): void {
Wallpapers.setWallpaper(modelData.path);
}
anchors.bottomMargin: itemMargin
anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
radius: itemRadius
}
} }
} }
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -6,7 +6,7 @@ import QtQuick.Controls
import qs.Components import qs.Components
import qs.Config import qs.Config
import "../../scripts/fuzzysort.js" as Fuzzy import "../../scripts/fuzzysort.js" as Fuzzy
import "../../scripts/SettingsIndex.mjs" as SettingsIndex import "./SettingsIndex.mjs" as SettingsIndex
Item { Item {
id: root id: root
@@ -53,10 +53,11 @@ Item {
Shortcut { Shortcut {
sequence: "/" sequence: "/"
onActivated: searchField.forceActiveFocus() onActivated: searchField.forceActiveFocus()
} }
Component.onCompleted: console.log(root.height)
ListModel { ListModel {
id: resultsModel id: resultsModel
} }
+30 -9
View File
@@ -7,24 +7,45 @@ import qs.Helpers
Item { Item {
id: root id: root
property real offsetScale: shouldBeActive ? 0 : 1
required property var panels required property var panels
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeActive: visibilities.settings
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.topMargin: (-implicitHeight - 5) * offsetScale implicitHeight: 0
implicitHeight: content.implicitHeight
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
opacity: 1 - offsetScale visible: height > 0
visible: offsetScale < 1
states: State {
name: "visible"
when: root.visibilities.settings
PropertyChanges {
root.implicitHeight: content.implicitHeight
}
}
transitions: [
Transition {
from: ""
to: "visible"
Behavior on offsetScale {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial easing.bezierCurve: MaterialEasing.expressiveEffects
property: "implicitHeight"
target: root
}
},
Transition {
from: "visible"
to: ""
Anim {
easing.bezierCurve: MaterialEasing.expressiveEffects
property: "implicitHeight"
target: root
} }
} }
]
CustomClippingRect { CustomClippingRect {
anchors.fill: parent anchors.fill: parent
+2 -2
View File
@@ -14,8 +14,8 @@ Scope {
description: "Toggle launcher" description: "Toggle launcher"
name: "toggle-launcher" name: "toggle-launcher"
onPressed: { onPressed: root.launcherInterrupted = false
root.launcherInterrupted = false; onReleased: {
if (!root.launcherInterrupted && !root.hasFullscreen) { if (!root.launcherInterrupted && !root.hasFullscreen) {
const visibilities = Visibilities.getForActive(); const visibilities = Visibilities.getForActive();
visibilities.launcher = !visibilities.launcher; visibilities.launcher = !visibilities.launcher;
+5 -7
View File
@@ -82,14 +82,12 @@ StackView {
CustomRect { CustomRect {
id: item id: item
required property int index
required property QsMenuEntry modelData required property QsMenuEntry modelData
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent" color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
implicitWidth: root.biggestWidth implicitWidth: root.biggestWidth
radius: Appearance.rounding.full radius: Appearance.rounding.smallest / 2
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
Loader { Loader {
id: children id: children
@@ -203,18 +201,18 @@ StackView {
asynchronous: true asynchronous: true
sourceComponent: Item { sourceComponent: Item {
implicitHeight: 30 implicitHeight: back.implicitHeight + 2 / 2
implicitWidth: back.implicitWidth implicitWidth: back.implicitWidth
Item { Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
implicitHeight: 30 implicitHeight: back.implicitHeight
implicitWidth: root.biggestWidth implicitWidth: back.implicitWidth + 10
CustomRect { CustomRect {
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3secondaryContainer color: DynamicColors.palette.m3secondaryContainer
radius: Appearance.rounding.full radius: Appearance.rounding.smallest / 2
StateLayer { StateLayer {
function onClicked(): void { function onClicked(): void {
+1 -1
View File
@@ -31,7 +31,7 @@ Item {
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
// color: "transparent" // color: "transparent"
radius: (20 - Appearance.padding.small) * Appearance.rounding.scale radius: 6
CustomRect { CustomRect {
id: indicator id: indicator
-1
View File
@@ -1,6 +1,5 @@
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick import QtQuick
import QtQuick.VectorImage
import Quickshell import Quickshell
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import qs.Modules import qs.Modules
+30
View File
@@ -0,0 +1,30 @@
pragma Singleton
import Quickshell
import QtQuick
Singleton {
id: root
readonly property date date: clock.date
readonly property string dateStr: format("ddd d MMM - hh:mm:ss")
property alias enabled: clock.enabled
readonly property string hourStr: timeComponents[0] ?? ""
readonly property int hours: clock.hours
readonly property string minuteStr: timeComponents[1] ?? ""
readonly property int minutes: clock.minutes
readonly property string secondStr: timeComponents[2] ?? ""
readonly property int seconds: clock.seconds
readonly property list<string> timeComponents: timeStr.split(":")
readonly property string timeStr: format("hh:mm:ss")
function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt);
}
SystemClock {
id: clock
precision: SystemClock.Seconds
}
}
+2 -5
View File
@@ -54,8 +54,6 @@ CustomClippingRect {
anchors.centerIn: parent anchors.centerIn: parent
contentHeight: childrenRect.height contentHeight: childrenRect.height
contentWidth: 600 contentWidth: 600
displayMarginBeginning: root.itemHeight
displayMarginEnd: root.itemHeight
implicitHeight: Math.min(contentHeight, (root.itemHeight + spacing) * 5 - spacing) implicitHeight: Math.min(contentHeight, (root.itemHeight + spacing) * 5 - spacing)
implicitWidth: contentWidth implicitWidth: contentWidth
spacing: Appearance.spacing.normal spacing: Appearance.spacing.normal
@@ -67,11 +65,10 @@ CustomClippingRect {
required property var modelData required property var modelData
readonly property list<string> sections: modelData.update.split(" ") readonly property list<string> sections: modelData.update.split(" ")
// anchors.left: parent.left anchors.left: parent.left
// anchors.right: parent.right anchors.right: parent.right
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: root.itemHeight implicitHeight: root.itemHeight
implicitWidth: 600
radius: Appearance.rounding.small - Appearance.padding.small radius: Appearance.rounding.small - Appearance.padding.small
RowLayout { RowLayout {
+1 -1
View File
@@ -14,7 +14,7 @@ CustomRect {
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2 implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
implicitWidth: contentRow.implicitWidth + Appearance.spacing.small * 2 implicitWidth: contentRow.implicitWidth + Appearance.spacing.small * 2
radius: Appearance.rounding.full radius: height / 2
RowLayout { RowLayout {
id: contentRow id: contentRow
-80
View File
@@ -1,80 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Components
import qs.Helpers
import qs.Config
Item {
id: root
property Image current: one
property string source: Wallpapers.current
anchors.fill: parent
Component.onCompleted: {
if (source)
Qt.callLater(() => one.update());
}
onSourceChanged: {
if (!source) {
current = null;
} else if (current === one) {
two.update();
} else {
one.update();
}
}
Img {
id: one
}
Img {
id: two
}
component Img: Image {
id: img
function update(): void {
if (source === root.source) {
root.current = this;
} else {
source = root.source;
}
}
anchors.fill: parent
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: 0
retainWhileLoading: true
scale: Wallpapers.showPreview ? 1 : 0.8
sourceClipRect: Qt.rect(Config.background.sourceClipX, Config.background.sourceClipY, Config.background.sourceClipW, Config.background.sourceClipH)
states: State {
name: "visible"
when: root.current === img
PropertyChanges {
img.opacity: 1
img.scale: 1
}
}
transitions: Transition {
Anim {
duration: Config.background.wallFadeDuration
properties: "opacity,scale"
target: img
}
}
onStatusChanged: {
if (status === Image.Ready) {
root.current = this;
}
}
}
}
+58 -57
View File
@@ -1,7 +1,5 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Hyprland
import QtQuick import QtQuick
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
@@ -10,76 +8,79 @@ import qs.Config
Item { Item {
id: root id: root
required property ShellScreen screen property Image current: one
property string source: Wallpapers.current property string source: Wallpapers.current
function refreshData(): void {
Hyprland.refreshMonitors();
const scale = Hyprland.monitorFor(root.screen).scale;
if (scale > 0 && img.resScale !== scale) {
img.resScale = scale;
img.sourceSize.width = root.screen.width * scale;
}
const displayData = Wallpapers.getCrop(root.screen.name);
const displayRect = Qt.rect(img.sourceSize.width * displayData.x, img.implicitHeight * displayData.y, img.sourceSize.width * displayData.width, img.implicitHeight * displayData.height);
img.anchors.fill = null;
img.zoom = displayData.zoom;
img.x = -(displayRect.x * displayData.zoom / img.resScale);
img.y = -(displayRect.y * displayData.zoom / img.resScale);
}
anchors.fill: parent anchors.fill: parent
Image { Component.onCompleted: {
if (source)
Qt.callLater(() => one.update());
}
onSourceChanged: {
if (!source) {
current = null;
} else if (current === one) {
two.update();
} else {
one.update();
}
}
Img {
id: one
}
Img {
id: two
}
component Img: CachingImage {
id: img id: img
property int displayH property real imageRatio: Math.max(1, sourceSize.width) / Math.max(1, sourceSize.height)
property int displayW property bool isValid: sourceSize.width > 0 && sourceSize.height > 0 && root.width > 0 && root.height > 0
property real resScale property real windowRatio: root.width / Math.max(1, root.height)
property real zoom: 1.0
function update(): void {
if (path === root.source) {
root.current = this;
} else {
path = root.source;
}
}
anchors.fill: undefined
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
height: implicitHeight * zoom / resScale height: isValid ? (imageRatio > windowRatio ? root.height : root.width / imageRatio) * Config.background.zoom : root.height
opacity: 1 opacity: 0
retainWhileLoading: true scale: Wallpapers.showPreview ? 1 : 0.8
source: root.source width: isValid ? (imageRatio > windowRatio ? root.height * imageRatio : root.width) * Config.background.zoom : root.width
sourceSize.width: root.screen.width * resScale x: isValid ? (root.width - width) * Config.background.alignX : 0
width: implicitWidth * zoom / resScale y: isValid ? (root.height - height) * Config.background.alignY : 0
Behavior on height { states: State {
Anim { name: "visible"
} when: root.current === img
}
Behavior on width { PropertyChanges {
Anim { img.opacity: 1
} img.scale: 1
} }
Behavior on x { }
Anim { transitions: Transition {
}
}
Behavior on y {
Anim { Anim {
duration: Config.background.wallFadeDuration
properties: "opacity,scale"
target: img
} }
} }
onStatusChanged: { onStatusChanged: {
if (img.status == Image.Ready) { if (status === Image.Ready) {
root.refreshData(); root.current = this;
} }
}
Connections {
function onAdapterUpdated(): void {
root.refreshData();
}
function onLoaded(): void {
root.refreshData();
}
target: Wallpapers.monitorCrops
} }
} }
} }
-1
View File
@@ -30,7 +30,6 @@ Loader {
} }
WallBackground { WallBackground {
screen: root.screen
} }
Loader { Loader {
+1 -2
View File
@@ -79,8 +79,7 @@ Item {
} }
onPressed: { onPressed: {
const ws = button.modelData.name; Hyprland.dispatch(`workspace ${button.modelData.name}`);
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
} }
} }
} }
-1
View File
@@ -12,7 +12,6 @@ qml_module(ZShell-blobs
qt_add_shaders(ZShell-blobs "blob_shaders" qt_add_shaders(ZShell-blobs "blob_shaders"
BATCHABLE OPTIMIZED NOHLSL NOMSL BATCHABLE OPTIMIZED NOHLSL NOMSL
GLSL "300es,330"
PREFIX "/" PREFIX "/"
FILES FILES
shaders/blob.frag shaders/blob.frag
+1 -7
View File
@@ -2,9 +2,6 @@
#include <cstring> #include <cstring>
static_assert(sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
"BlobMaterial packs excludeMask into a float slot via memcpy");
QSGMaterialType* BlobMaterial::type() const { QSGMaterialType* BlobMaterial::type() const {
static QSGMaterialType s_type; static QSGMaterialType s_type;
return &s_type; return &s_type;
@@ -85,11 +82,8 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
const auto& r = mat->m_rects[i]; const auto& r = mat->m_rects[i];
const int base = 160 + i * 80; const int base = 160 + i * 80;
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
float maskAsFloat;
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
const float d0[4] = { r.cx, r.cy, r.hw, r.hh }; const float d0[4] = { r.cx, r.cy, r.hw, r.hh };
const float d1[4] = { maskAsFloat, r.offsetX, r.offsetY, r.minEig }; const float d1[4] = { 0.0f, r.offsetX, r.offsetY, r.minEig };
const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f }; const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
memcpy(buf->data() + base, d0, 16); memcpy(buf->data() + base, d0, 16);
memcpy(buf->data() + base + 16, d1, 16); memcpy(buf->data() + base + 16, d1, 16);
-3
View File
@@ -14,9 +14,6 @@ struct BlobRectData {
float screenHalfX = 0, screenHalfY = 0; float screenHalfX = 0, screenHalfY = 0;
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU // Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
float radius[4] = { 0, 0, 0, 0 }; float radius[4] = { 0, 0, 0, 0 };
// Bitmask of indices in this rect's m_cachedRects that mutually exclude (or are excluded by) this rect.
// Used by the shader to skip smin between excluded pairs.
int excludeMask = 0;
}; };
class BlobMaterial : public QSGMaterial { class BlobMaterial : public QSGMaterial {
+3 -33
View File
@@ -72,17 +72,11 @@ void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeome
// Accumulate sub-pixel drift so slow movements don't desync the shader // Accumulate sub-pixel drift so slow movements don't desync the shader
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x()); m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y()); m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
// Accumulate size delta across multiple frames so incremental size const auto dw = std::abs(newGeometry.width() - oldGeometry.width());
// changes that are each below the threshold still trigger a dirty const auto dh = std::abs(newGeometry.height() - oldGeometry.height());
// mark once their accumulated delta exceeds it. if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f || dw > 0.5 || dh > 0.5) {
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height());
if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f ||
std::abs(m_pendingDw) > 0.5f || std::abs(m_pendingDh) > 0.5f) {
m_pendingDx = 0; m_pendingDx = 0;
m_pendingDy = 0; m_pendingDy = 0;
m_pendingDw = 0;
m_pendingDh = 0;
m_group->markShapeDirty(this); m_group->markShapeDirty(this);
} }
} }
@@ -155,10 +149,6 @@ void BlobShape::updatePolish() {
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY), const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH)); static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
// Track shape pointers parallel to m_cachedRects for pairwise exclusion lookups
QVector<BlobShape*> rectShapes;
rectShapes.reserve(m_group->shapes().size());
for (BlobShape* other : m_group->shapes()) { for (BlobShape* other : m_group->shapes()) {
if (other->isInvertedRect()) if (other->isInvertedRect())
continue; continue;
@@ -220,29 +210,12 @@ void BlobShape::updatePolish() {
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh; r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
m_cachedRects.append(r); m_cachedRects.append(r);
rectShapes.append(other);
} }
} }
if (isInvertedRect()) if (isInvertedRect())
m_cachedMyIndex = -1; m_cachedMyIndex = -1;
// Compute pairwise exclude masks. Bit j in entry i is set iff rect i excludes rect j
// or rect j excludes rect i. The shader uses this to avoid smin between excluded pairs.
const auto cachedCount = m_cachedRects.size();
for (qsizetype i = 0; i < cachedCount; ++i) {
int mask = 0;
BlobShape* si = rectShapes[i];
for (qsizetype j = 0; j < cachedCount; ++j) {
if (j == i)
continue;
BlobShape* sj = rectShapes[j];
if (si->isExcluded(sj) || sj->isExcluded(si))
mask |= (1 << j);
}
m_cachedRects[i].excludeMask = mask;
}
// Cache inverted rect data // Cache inverted rect data
m_cachedHasInverted = false; m_cachedHasInverted = false;
m_cachedInvertedRadius = 0; m_cachedInvertedRadius = 0;
@@ -297,7 +270,6 @@ void BlobShape::updatePolish() {
const auto rectCount = m_cachedRects.size(); const auto rectCount = m_cachedRects.size();
for (qsizetype i = 0; i < rectCount; ++i) { for (qsizetype i = 0; i < rectCount; ++i) {
auto& ri = m_cachedRects[i]; auto& ri = m_cachedRects[i];
const int riExcludeMask = ri.excludeMask;
float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f; float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f;
const float cTrX = ri.cx + ri.hw, cTrY = ri.cy - ri.hh; const float cTrX = ri.cx + ri.hw, cTrY = ri.cy - ri.hh;
@@ -308,8 +280,6 @@ void BlobShape::updatePolish() {
for (qsizetype j = 0; j < rectCount; ++j) { for (qsizetype j = 0; j < rectCount; ++j) {
if (j == i) if (j == i)
continue; continue;
if (riExcludeMask & (1 << j))
continue;
const auto& rj = m_cachedRects[j]; const auto& rj = m_cachedRects[j];
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh))); fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh))); fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
-2
View File
@@ -85,8 +85,6 @@ QVector<BlobRectData> m_cachedRects;
int m_cachedMyIndex = -2; int m_cachedMyIndex = -2;
float m_pendingDx = 0; float m_pendingDx = 0;
float m_pendingDy = 0; float m_pendingDy = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
bool m_cachedHasInverted = false; bool m_cachedHasInverted = false;
float m_cachedInvertedRadius = 0; float m_cachedInvertedRadius = 0;
float m_cachedInvertedOuter[4] = {}; float m_cachedInvertedOuter[4] = {};
+4 -35
View File
@@ -63,17 +63,13 @@ float smaxSharpA(float a, float b, float k) {
void main() { void main() {
vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH); vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH);
// Phase 1: compute per-rect SDF, track owner. We can't smin yet because float mergedSdf = 1e10;
// excluded pairs need to skip the smooth blend, which requires pairwise pass
// below.
float dArr[16];
int owner = -2; int owner = -2;
float minDist = 1e10; float minDist = 1e10;
for (int i = 0; i < rectCount; i++) { for (int i = 0; i < rectCount; i++) {
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
vec4 props = vec4 props = rectData[i * 5 + 1]; // radius, offsetX, offsetY, minEig
rectData[i * 5 + 1]; // excludeMask(int bits), offsetX, offsetY, minEig
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0 vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
vec4 radii = vec4 radii =
@@ -85,10 +81,8 @@ void main() {
// AABB early-out: skip rects far from this pixel // AABB early-out: skip rects far from this pixel
vec2 extent = sh.xy + vec2(smoothFactor * 1.5); vec2 extent = sh.xy + vec2(smoothFactor * 1.5);
if (abs(pixel.x - center.x) > extent.x || if (abs(pixel.x - center.x) > extent.x ||
abs(pixel.y - center.y) > extent.y) { abs(pixel.y - center.y) > extent.y)
dArr[i] = 1e10;
continue; continue;
}
// Apply pre-computed inverse deformation to the evaluation point // Apply pre-computed inverse deformation to the evaluation point
mat2 invDeform = mat2(invDm.xy, invDm.zw); mat2 invDeform = mat2(invDm.xy, invDm.zw);
@@ -144,38 +138,13 @@ void main() {
d *= scale; d *= scale;
} }
dArr[i] = d; mergedSdf = smin(mergedSdf, d, smoothFactor);
if (d < smoothFactor && d < minDist) { if (d < smoothFactor && d < minDist) {
minDist = d; minDist = d;
owner = i; owner = i;
} }
} }
// Phase 2: hard-min baseline over all rects.
float mergedSdf = 1e10;
for (int i = 0; i < rectCount; i++) {
mergedSdf = min(mergedSdf, dArr[i]);
}
// Phase 3: pair-wise smin contributions, skipping excluded pairs. Pair smin
// <= min, so taking the min over all non-excluded pair smins gives the
// smoothly-merged SDF.
for (int i = 0; i < rectCount; i++) {
if (dArr[i] >= 1e9)
continue;
int excludeMask = floatBitsToInt(rectData[i * 5 + 1].x);
for (int j = i + 1; j < rectCount; j++) {
if (dArr[j] >= 1e9)
continue;
if ((excludeMask & (1 << j)) != 0)
continue;
// smin only deviates from min within smoothFactor
if (abs(dArr[i] - dArr[j]) >= smoothFactor)
continue;
mergedSdf = min(mergedSdf, smin(dArr[i], dArr[j], smoothFactor));
}
}
if (hasInverted != 0) { if (hasInverted != 0) {
float dOuter = sdBox(pixel, invertedOuter.xy, invertedOuter.zw) - 1.0; float dOuter = sdBox(pixel, invertedOuter.xy, invertedOuter.zw) - 1.0;
float dInner = float dInner =
+26 -193
View File
@@ -1,170 +1,12 @@
#include "hyprextras.hpp" #include "hyprextras.hpp"
#include "hyprdevices.hpp"
#include <qdir.h> #include <qdir.h>
#include <qjsonarray.h> #include <qjsonarray.h>
#include <qlocalsocket.h> #include <qlocalsocket.h>
#include <qloggingcategory.h>
#include <qmetatype.h>
#include <qregularexpression.h>
#include <qvariant.h> #include <qvariant.h>
Q_LOGGING_CATEGORY(lcHypr, "ZShell.internal.hypr", QtInfoMsg)
namespace ZShell::internal::hypr { namespace ZShell::internal::hypr {
namespace {
static QString luaEscapeString(const QString& s) {
QString out;
out.reserve(s.size() + 2);
out += QLatin1Char('"');
for (const QChar c : s) {
switch (c.unicode()) {
case '\\':
out += QLatin1String(R"(\\)");
break;
case '"':
out += QLatin1String(R"(\")");
break;
case '\n':
out += QLatin1String(R"(\n)");
break;
case '\r':
out += QLatin1String(R"(\r)");
break;
case '\t':
out += QLatin1String(R"(\t)");
break;
default:
out += c;
break;
}
}
out += QLatin1Char('"');
return out;
}
static QString luaValue(const QVariant& v);
static QString luaArray(const QVariantList& list) {
QStringList parts;
parts.reserve(list.size());
for (const auto& item : list) {
parts << luaValue(item);
}
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
}
static QString luaArray(const QStringList& list) {
QStringList parts;
parts.reserve(list.size());
for (const auto& item : list) {
parts << luaEscapeString(item);
}
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
}
static QString luaMapFromHash(const QVariantHash& hash) {
QStringList parts;
parts.reserve(hash.size());
for (auto it = hash.cbegin(); it != hash.cend(); ++it) {
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value());
}
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
}
static QString luaMap(const QVariantMap& map) {
QStringList parts;
parts.reserve(map.size());
for (auto it = map.cbegin(); it != map.cend(); ++it) {
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value());
}
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
}
static QString luaValue(const QVariant& v) {
if (!v.isValid() || v.isNull()) {
return QLatin1String("nil");
}
switch (v.metaType().id()) {
case QMetaType::Bool:
return v.toBool() ? QLatin1String("true") : QLatin1String("false");
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::LongLong:
case QMetaType::ULongLong:
case QMetaType::Double:
return v.toString();
case QMetaType::QString:
return luaEscapeString(v.toString());
case QMetaType::QStringList:
return luaArray(v.toStringList());
case QMetaType::QVariantList:
return luaArray(v.toList());
case QMetaType::QVariantMap:
return luaMap(v.toMap());
case QMetaType::QVariantHash:
return luaMapFromHash(v.toHash());
default:
return luaEscapeString(v.toString());
}
}
static QString normalizeOptionPath(QString key) {
key = key.trimmed();
key.replace(QLatin1Char(':'), QLatin1Char('.'));
return key;
}
static QString buildHlConfigCall(const QString& key, const QVariant& value) {
const auto parts = normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts);
if (parts.isEmpty()) {
return {};
}
QString out;
out.reserve(32 + key.size() + value.toString().size());
out += QLatin1String("hl.config({ ");
for (int i = 0; i < parts.size(); ++i) {
out += parts.at(i);
out += QLatin1String(" = ");
if (i + 1 < parts.size()) {
out += QLatin1String("{ ");
}
}
out += luaValue(value);
for (int i = 0; i + 1 < parts.size(); ++i) {
out += QLatin1String(" }");
}
out += QLatin1String(" })");
return out;
}
} // namespace
HyprExtras::HyprExtras(QObject* parent) HyprExtras::HyprExtras(QObject* parent)
: QObject(parent) : QObject(parent)
, m_requestSocket("") , m_requestSocket("")
@@ -174,22 +16,24 @@ HyprExtras::HyprExtras(QObject* parent)
, m_devices(new HyprDevices(this)) { , m_devices(new HyprDevices(this)) {
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE"); const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
if (his.isEmpty()) { if (his.isEmpty()) {
qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket."; qWarning()
<< "HyprExtras::HyprExtras: $HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket.";
return; return;
} }
auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his); auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
if (!QDir(hyprDir).exists()) { if (!QDir(hyprDir).exists()) {
hyprDir = QStringLiteral("/tmp/hypr/") + his; hyprDir = "/tmp/hypr/" + his;
if (!QDir(hyprDir).exists()) { if (!QDir(hyprDir).exists()) {
qCWarning(lcHypr) << "Hyprland socket directory does not exist. Unable to connect to Hyprland socket."; qWarning() << "HyprExtras::HyprExtras: Hyprland socket directory does not exist. Unable to connect to "
"Hyprland socket.";
return; return;
} }
} }
m_requestSocket = hyprDir + QStringLiteral("/.socket.sock"); m_requestSocket = hyprDir + "/.socket.sock";
m_eventSocket = hyprDir + QStringLiteral("/.socket2.sock"); m_eventSocket = hyprDir + "/.socket2.sock";
refreshOptions(); refreshOptions();
refreshDevices(); refreshDevices();
@@ -218,7 +62,7 @@ void HyprExtras::message(const QString& message) {
makeRequest(message, [](bool success, const QByteArray& res) { makeRequest(message, [](bool success, const QByteArray& res) {
if (!success) { if (!success) {
qCWarning(lcHypr) << "message: request error:" << QString::fromUtf8(res); qWarning() << "HyprExtras::message: request error:" << QString::fromUtf8(res);
} }
}); });
} }
@@ -228,10 +72,9 @@ void HyprExtras::batchMessage(const QStringList& messages) {
return; return;
} }
makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')), makeRequest("[[BATCH]]" + messages.join(";"), [](bool success, const QByteArray& res) {
[](bool success, const QByteArray& res) {
if (!success) { if (!success) {
qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res); qWarning() << "HyprExtras::batchMessage: request error:" << QString::fromUtf8(res);
} }
}); });
} }
@@ -241,25 +84,16 @@ void HyprExtras::applyOptions(const QVariantHash& options) {
return; return;
} }
QStringList calls; QString request = "[[BATCH]]";
calls.reserve(options.size());
for (auto it = options.constBegin(); it != options.constEnd(); ++it) { for (auto it = options.constBegin(); it != options.constEnd(); ++it) {
const auto call = buildHlConfigCall(it.key(), it.value()); request += QString("keyword %1 %2;").arg(it.key(), it.value().toString());
if (!call.isEmpty()) {
calls << call;
}
} }
if (calls.isEmpty()) { makeRequest(request, [this](bool success, const QByteArray& res) {
return;
}
makeRequest(QStringLiteral("eval ") + calls.join(QLatin1String("; ")), [this](bool success, const QByteArray& res) {
if (success) { if (success) {
refreshOptions(); refreshOptions();
} else { } else {
qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res); qWarning() << "HyprExtras::applyOptions: request error" << QString::fromUtf8(res);
} }
}); });
} }
@@ -269,7 +103,7 @@ void HyprExtras::refreshOptions() {
m_optionsRefresh->close(); m_optionsRefresh->close();
} }
m_optionsRefresh = makeRequestJson(QStringLiteral("descriptions"), [this](bool success, const QJsonDocument& response) { m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) {
m_optionsRefresh.reset(); m_optionsRefresh.reset();
if (!success) { if (!success) {
return; return;
@@ -280,9 +114,8 @@ void HyprExtras::refreshOptions() {
for (const auto& o : std::as_const(options)) { for (const auto& o : std::as_const(options)) {
const auto obj = o.toObject(); const auto obj = o.toObject();
const auto key = obj.value(QStringLiteral("value")).toString(); const auto key = obj.value("value").toString();
const auto value = obj.value(QStringLiteral("data")).toObject().value(QStringLiteral("current")).toVariant(); const auto value = obj.value("data").toObject().value("current").toVariant();
if (m_options.value(key) != value) { if (m_options.value(key) != value) {
dirty = true; dirty = true;
m_options.insert(key, value); m_options.insert(key, value);
@@ -300,7 +133,7 @@ void HyprExtras::refreshDevices() {
m_devicesRefresh->close(); m_devicesRefresh->close();
} }
m_devicesRefresh = makeRequestJson(QStringLiteral("devices"), [this](bool success, const QJsonDocument& response) { m_devicesRefresh = makeRequestJson("devices", [this](bool success, const QJsonDocument& response) {
m_devicesRefresh.reset(); m_devicesRefresh.reset();
if (success) { if (success) {
m_devices->updateLastIpcObject(response.object()); m_devices->updateLastIpcObject(response.object());
@@ -310,15 +143,15 @@ void HyprExtras::refreshDevices() {
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const { void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
if (!m_socketValid) { if (!m_socketValid) {
qCWarning(lcHypr) << "socketError: unable to connect to Hyprland event socket:" << error; qWarning() << "HyprExtras::socketError: unable to connect to Hyprland event socket:" << error;
} else { } else {
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error; qWarning() << "HyprExtras::socketError: Hyprland event socket error:" << error;
} }
} }
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) { void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
if (state == QLocalSocket::UnconnectedState && m_socketValid) { if (state == QLocalSocket::UnconnectedState && m_socketValid) {
qCWarning(lcHypr) << "socketStateChanged: Hyprland event socket disconnected."; qWarning() << "HyprExtras::socketStateChanged: Hyprland event socket disconnected.";
} }
m_socketValid = state == QLocalSocket::ConnectedState; m_socketValid = state == QLocalSocket::ConnectedState;
@@ -330,23 +163,23 @@ void HyprExtras::readEvent() {
if (rawEvent.isEmpty()) { if (rawEvent.isEmpty()) {
break; break;
} }
rawEvent.truncate(rawEvent.length() - 1); rawEvent.truncate(rawEvent.length() - 1); // Remove trailing \n
const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>")); const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
handleEvent(QString::fromUtf8(event)); handleEvent(QString::fromUtf8(event));
} }
} }
void HyprExtras::handleEvent(const QString& event) { void HyprExtras::handleEvent(const QString& event) {
if (event == QStringLiteral("configreloaded")) { if (event == "configreloaded") {
refreshOptions(); refreshOptions();
} else if (event == QStringLiteral("activelayout")) { } else if (event == "activelayout") {
refreshDevices(); refreshDevices();
} }
} }
HyprExtras::SocketPtr HyprExtras::makeRequestJson( HyprExtras::SocketPtr HyprExtras::makeRequestJson(
const QString& request, const std::function<void(bool, QJsonDocument)>& callback) { const QString& request, const std::function<void(bool, QJsonDocument)>& callback) {
return makeRequest(QStringLiteral("j/") + request, [callback](bool success, const QByteArray& response) { return makeRequest("j/" + request, [callback](bool success, const QByteArray& response) {
callback(success, QJsonDocument::fromJson(response)); callback(success, QJsonDocument::fromJson(response));
}); });
} }
@@ -371,7 +204,7 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
}); });
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) { QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) {
qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request; qWarning() << "HyprExtras::makeRequest: error making request:" << err << "| request:" << request;
callback(false, {}); callback(false, {});
socket->close(); socket->close();
}); });
+1 -5
View File
@@ -1,19 +1,15 @@
#pragma once #pragma once
#include "hyprdevices.hpp"
#include <qlocalsocket.h> #include <qlocalsocket.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qsharedpointer.h>
#include <qvariant.h>
namespace ZShell::internal::hypr { namespace ZShell::internal::hypr {
class HyprDevices;
class HyprExtras : public QObject { class HyprExtras : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_MOC_INCLUDE("hyprdevices.hpp")
Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged) Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged)
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT) Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
-2
View File
@@ -74,8 +74,6 @@ Defaults from `CMakeLists.txt`:
### Nix Flake ### Nix Flake
_Note that Nix is not actively developed at this point. Things may be broken. Feel free to suggest fixes in a PR_
The flake exposes: The flake exposes:
- `packages.<system>.zshell` - `packages.<system>.zshell`
+1
View File
@@ -9,6 +9,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"typer", "typer",
"pillow", "pillow",
"jinja2",
"materialyoucolor" "materialyoucolor"
] ]
+2 -2
View File
@@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
import typer import typer
from zshell.subcommands import shell, scheme, screenshot, wallpaper from zshell.subcommands import shell, scheme, screenshot, wallpaper, preset
app = typer.Typer() app = typer.Typer()
@@ -8,7 +8,7 @@ app.add_typer(shell.app, name="shell")
app.add_typer(scheme.app, name="scheme") app.add_typer(scheme.app, name="scheme")
app.add_typer(screenshot.app, name="screenshot") app.add_typer(screenshot.app, name="screenshot")
app.add_typer(wallpaper.app, name="wallpaper") app.add_typer(wallpaper.app, name="wallpaper")
# app.add_typer(preset.app, name="preset") app.add_typer(preset.app, name="preset")
def main() -> None: def main() -> None:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,544 @@
{
"id": "catppuccin",
"name": "Catppuccin",
"version": "1.0.0",
"author": "Catppuccin Org",
"description": "Soothing pastel theme for the high-spirited!",
"dark": {},
"light": {},
"variants": {
"type": "multi",
"defaults": {
"dark": { "flavor": "mocha", "accent": "mauve" },
"light": { "flavor": "latte", "accent": "mauve" }
},
"flavors": [
{
"id": "latte",
"name": "Latte",
"light": {
"surface": "#ccd0da",
"surfaceText": "#4c4f69",
"surfaceVariant": "#eff1f5",
"surfaceVariantText": "#6c6f85",
"background": "#eff1f5",
"backgroundText": "#4c4f69",
"outline": "#9ca0b0",
"surfaceContainer": "#eff1f5",
"surfaceContainerHigh": "#e6e9ef",
"surfaceContainerHighest": "#dce0e8",
"error": "#d20f39",
"warning": "#fe640b",
"info": "#1e66f5"
}
},
{
"id": "frappe",
"name": "Frappé",
"dark": {
"surface": "#414559",
"surfaceText": "#c6d0f5",
"surfaceVariant": "#303446",
"surfaceVariantText": "#a5adce",
"background": "#303446",
"backgroundText": "#c6d0f5",
"outline": "#737994",
"surfaceContainer": "#303446",
"surfaceContainerHigh": "#292c3c",
"surfaceContainerHighest": "#232634",
"error": "#e78284",
"warning": "#ef9f76",
"info": "#8caaee"
}
},
{
"id": "macchiato",
"name": "Macchiato",
"dark": {
"surface": "#363a4f",
"surfaceText": "#cad3f5",
"surfaceVariant": "#24273a",
"surfaceVariantText": "#a5adcb",
"background": "#24273a",
"backgroundText": "#cad3f5",
"outline": "#6e738d",
"surfaceContainer": "#24273a",
"surfaceContainerHigh": "#1e2030",
"surfaceContainerHighest": "#181926",
"error": "#ed8796",
"warning": "#f5a97f",
"info": "#8aadf4"
}
},
{
"id": "mocha",
"name": "Mocha",
"dark": {
"surface": "#313244",
"surfaceText": "#cdd6f4",
"surfaceVariant": "#1e1e2e",
"surfaceVariantText": "#a6adc8",
"background": "#1e1e2e",
"backgroundText": "#cdd6f4",
"outline": "#6c7086",
"surfaceContainer": "#1e1e2e",
"surfaceContainerHigh": "#181825",
"surfaceContainerHighest": "#11111b",
"error": "#f38ba8",
"warning": "#fab387",
"info": "#89b4fa"
}
}
],
"accents": [
{
"id": "rosewater",
"name": "Rosewater",
"latte": {
"primary": "#dc8a78",
"primaryText": "#eff1f5",
"primaryContainer": "#e1a99d",
"secondary": "#d8c7c4",
"surfaceTint": "#e1a99d"
},
"frappe": {
"primary": "#f2d5cf",
"primaryText": "#303446",
"primaryContainer": "#b8a5a6",
"secondary": "#a2748b",
"surfaceTint": "#b8a5a6"
},
"macchiato": {
"primary": "#f4dbd6",
"primaryText": "#24273a",
"primaryContainer": "#b6a6a7",
"secondary": "#9f6f8d",
"surfaceTint": "#b6a6a7"
},
"mocha": {
"primary": "#f5e0dc",
"primaryText": "#1e1e2e",
"primaryContainer": "#b5a6a8",
"secondary": "#9d6d87",
"surfaceTint": "#b5a6a8"
}
},
{
"id": "flamingo",
"name": "Flamingo",
"latte": {
"primary": "#dd7878",
"primaryText": "#eff1f5",
"primaryContainer": "#e29c9d",
"secondary": "#d7c3c4",
"surfaceTint": "#e29c9d"
},
"frappe": {
"primary": "#eebebe",
"primaryText": "#303446",
"primaryContainer": "#b5949a",
"secondary": "#9d6b80",
"surfaceTint": "#b5949a"
},
"macchiato": {
"primary": "#f0c6c6",
"primaryText": "#24273a",
"primaryContainer": "#b3979c",
"secondary": "#996780",
"surfaceTint": "#b3979c"
},
"mocha": {
"primary": "#f2cdcd",
"primaryText": "#1e1e2e",
"primaryContainer": "#b3999e",
"secondary": "#98667c",
"surfaceTint": "#b3999e"
}
},
{
"id": "pink",
"name": "Pink",
"latte": {
"primary": "#ea76cb",
"primaryText": "#eff1f5",
"primaryContainer": "#eb9bd7",
"secondary": "#d9c7d5",
"surfaceTint": "#eb9bd7"
},
"frappe": {
"primary": "#f4b8e4",
"primaryText": "#303446",
"primaryContainer": "#b990b5",
"secondary": "#996e9e",
"surfaceTint": "#b990b5"
},
"macchiato": {
"primary": "#f5bde6",
"primaryText": "#24273a",
"primaryContainer": "#b791b2",
"secondary": "#95689a",
"surfaceTint": "#b791b2"
},
"mocha": {
"primary": "#f5c2e7",
"primaryText": "#1e1e2e",
"primaryContainer": "#b591b0",
"secondary": "#966597",
"surfaceTint": "#b591b0"
}
},
{
"id": "mauve",
"name": "Mauve",
"latte": {
"primary": "#8839ef",
"primaryText": "#eff1f5",
"primaryContainer": "#a670f1",
"secondary": "#c2b8d0",
"surfaceTint": "#a670f1"
},
"frappe": {
"primary": "#ca9ee6",
"primaryText": "#303446",
"primaryContainer": "#9c7eb6",
"secondary": "#7d6799",
"surfaceTint": "#9c7eb6"
},
"macchiato": {
"primary": "#c6a0f6",
"primaryText": "#24273a",
"primaryContainer": "#967cbe",
"secondary": "#766597",
"surfaceTint": "#967cbe"
},
"mocha": {
"primary": "#cba6f7",
"primaryText": "#1e1e2e",
"primaryContainer": "#977ebb",
"secondary": "#756294",
"surfaceTint": "#977ebb"
}
},
{
"id": "red",
"name": "Red",
"latte": {
"primary": "#d20f39",
"primaryText": "#eff1f5",
"primaryContainer": "#da5371",
"secondary": "#c0a0a8",
"surfaceTint": "#da5371"
},
"frappe": {
"primary": "#e78284",
"primaryText": "#303446",
"primaryContainer": "#b06a72",
"secondary": "#8b5d66",
"surfaceTint": "#b06a72"
},
"macchiato": {
"primary": "#ed8796",
"primaryText": "#24273a",
"primaryContainer": "#b16b7a",
"secondary": "#865a69",
"surfaceTint": "#b16b7a"
},
"mocha": {
"primary": "#f38ba8",
"primaryText": "#1e1e2e",
"primaryContainer": "#b46b84",
"secondary": "#85596b",
"surfaceTint": "#b46b84"
}
},
{
"id": "maroon",
"name": "Maroon",
"latte": {
"primary": "#e64553",
"primaryText": "#eff1f5",
"primaryContainer": "#e87883",
"secondary": "#cfb7ba",
"surfaceTint": "#e87883"
},
"frappe": {
"primary": "#ea999c",
"primaryText": "#303446",
"primaryContainer": "#b27a83",
"secondary": "#92626f",
"surfaceTint": "#b27a83"
},
"macchiato": {
"primary": "#ee99a0",
"primaryText": "#24273a",
"primaryContainer": "#b27781",
"secondary": "#8c5e6c",
"surfaceTint": "#b27781"
},
"mocha": {
"primary": "#eba0ac",
"primaryText": "#1e1e2e",
"primaryContainer": "#ae7987",
"secondary": "#895b6c",
"surfaceTint": "#ae7987"
}
},
{
"id": "peach",
"name": "Peach",
"latte": {
"primary": "#fe640b",
"primaryText": "#eff1f5",
"primaryContainer": "#f98e51",
"secondary": "#c9b7ad",
"surfaceTint": "#f98e51"
},
"frappe": {
"primary": "#ef9f76",
"primaryText": "#303446",
"primaryContainer": "#b67f68",
"secondary": "#8f6a5f",
"surfaceTint": "#b67f68"
},
"macchiato": {
"primary": "#f5a97f",
"primaryText": "#24273a",
"primaryContainer": "#b7836a",
"secondary": "#8c695e",
"surfaceTint": "#b7836a"
},
"mocha": {
"primary": "#fab387",
"primaryText": "#1e1e2e",
"primaryContainer": "#b8876d",
"secondary": "#8b6a5d",
"surfaceTint": "#b8876d"
}
},
{
"id": "yellow",
"name": "Yellow",
"latte": {
"primary": "#df8e1d",
"primaryText": "#eff1f5",
"primaryContainer": "#e4ac5d",
"secondary": "#c6baaa",
"surfaceTint": "#e4ac5d"
},
"frappe": {
"primary": "#e5c890",
"primaryText": "#303446",
"primaryContainer": "#af9b7a",
"secondary": "#948062",
"surfaceTint": "#af9b7a"
},
"macchiato": {
"primary": "#eed49f",
"primaryText": "#24273a",
"primaryContainer": "#b2a181",
"secondary": "#947e62",
"surfaceTint": "#b2a181"
},
"mocha": {
"primary": "#f9e2af",
"primaryText": "#1e1e2e",
"primaryContainer": "#b8a889",
"secondary": "#978265",
"surfaceTint": "#b8a889"
}
},
{
"id": "green",
"name": "Green",
"latte": {
"primary": "#40a02b",
"primaryText": "#eff1f5",
"primaryContainer": "#74b867",
"secondary": "#9fbd9b",
"surfaceTint": "#74b867"
},
"frappe": {
"primary": "#a6d189",
"primaryText": "#303446",
"primaryContainer": "#83a275",
"secondary": "#648e5e",
"surfaceTint": "#83a275"
},
"macchiato": {
"primary": "#a6da95",
"primaryText": "#24273a",
"primaryContainer": "#80a57a",
"secondary": "#5c8a61",
"surfaceTint": "#80a57a"
},
"mocha": {
"primary": "#a6e3a1",
"primaryText": "#1e1e2e",
"primaryContainer": "#7ea87f",
"secondary": "#5b8964",
"surfaceTint": "#7ea87f"
}
},
{
"id": "teal",
"name": "Teal",
"latte": {
"primary": "#179299",
"primaryText": "#eff1f5",
"primaryContainer": "#57aeb4",
"secondary": "#93b4b7",
"surfaceTint": "#57aeb4"
},
"frappe": {
"primary": "#81c8be",
"primaryText": "#303446",
"primaryContainer": "#699b9a",
"secondary": "#588084",
"surfaceTint": "#699b9a"
},
"macchiato": {
"primary": "#8bd5ca",
"primaryText": "#24273a",
"primaryContainer": "#6da29f",
"secondary": "#577e83",
"surfaceTint": "#6da29f"
},
"mocha": {
"primary": "#94e2d5",
"primaryText": "#1e1e2e",
"primaryContainer": "#71a8a4",
"secondary": "#588284",
"surfaceTint": "#71a8a4"
}
},
{
"id": "sky",
"name": "Sky",
"latte": {
"primary": "#04a5e5",
"primaryText": "#eff1f5",
"primaryContainer": "#4abcea",
"secondary": "#a4b9c2",
"surfaceTint": "#4abcea"
},
"frappe": {
"primary": "#99d1db",
"primaryText": "#303446",
"primaryContainer": "#79a2af",
"secondary": "#628494",
"surfaceTint": "#79a2af"
},
"macchiato": {
"primary": "#91d7e3",
"primaryText": "#24273a",
"primaryContainer": "#71a3b0",
"secondary": "#5e7e8c",
"surfaceTint": "#71a3b0"
},
"mocha": {
"primary": "#89dceb",
"primaryText": "#1e1e2e",
"primaryContainer": "#69a3b3",
"secondary": "#5a7b88",
"surfaceTint": "#69a3b3"
}
},
{
"id": "sapphire",
"name": "Sapphire",
"latte": {
"primary": "#209fb5",
"primaryText": "#eff1f5",
"primaryContainer": "#5db8c8",
"secondary": "#9eb9be",
"surfaceTint": "#5db8c8"
},
"frappe": {
"primary": "#85c1dc",
"primaryText": "#303446",
"primaryContainer": "#6b96af",
"secondary": "#5e7b8e",
"surfaceTint": "#6b96af"
},
"macchiato": {
"primary": "#7dc4e4",
"primaryText": "#24273a",
"primaryContainer": "#6396b1",
"secondary": "#5a7486",
"surfaceTint": "#6396b1"
},
"mocha": {
"primary": "#74c7ec",
"primaryText": "#1e1e2e",
"primaryContainer": "#5a95b4",
"secondary": "#567080",
"surfaceTint": "#5a95b4"
}
},
{
"id": "blue",
"name": "Blue",
"latte": {
"primary": "#1e66f5",
"primaryText": "#eff1f5",
"primaryContainer": "#5c90f5",
"secondary": "#b1bacb",
"surfaceTint": "#5c90f5"
},
"frappe": {
"primary": "#8caaee",
"primaryText": "#303446",
"primaryContainer": "#7086bc",
"secondary": "#637195",
"surfaceTint": "#7086bc"
},
"macchiato": {
"primary": "#8aadf4",
"primaryText": "#24273a",
"primaryContainer": "#6c85bc",
"secondary": "#5f6d8f",
"surfaceTint": "#6c85bc"
},
"mocha": {
"primary": "#89b4fa",
"primaryText": "#1e1e2e",
"primaryContainer": "#6987bd",
"secondary": "#5d6c8b",
"surfaceTint": "#6987bd"
}
},
{
"id": "lavender",
"name": "Lavender",
"latte": {
"primary": "#7287fd",
"primaryText": "#eff1f5",
"primaryContainer": "#97a7fb",
"secondary": "#cdcfdd",
"surfaceTint": "#97a7fb"
},
"frappe": {
"primary": "#babbf1",
"primaryText": "#303446",
"primaryContainer": "#9192be",
"secondary": "#7175a1",
"surfaceTint": "#9192be"
},
"macchiato": {
"primary": "#b7bdf8",
"primaryText": "#24273a",
"primaryContainer": "#8b91bf",
"secondary": "#6b709d",
"surfaceTint": "#8b91bf"
},
"mocha": {
"primary": "#b4befe",
"primaryText": "#1e1e2e",
"primaryContainer": "#878ec0",
"secondary": "#676d99",
"surfaceTint": "#878ec0"
}
}
]
}
}
@@ -1,544 +0,0 @@
_data = {
"id": "catppuccin",
"name": "Catppuccin",
"version": "1.0.0",
"author": "Catppuccin Org",
"description": "Soothing pastel theme for the high-spirited!",
"dark": {},
"light": {},
"variants": {
"type": "multi",
"defaults": {
"dark": {"m3flavor": "mocha", "m3accent": "mauve"},
"light": {"m3flavor": "latte", "m3accent": "mauve"},
},
"flavors": [
{
"id": "latte",
"name": "Latte",
"light": {
"m3surface": "#ccd0da",
"m3surfaceText": "#4c4f69",
"m3surfaceVariant": "#eff1f5",
"m3surfaceVariantText": "#6c6f85",
"m3background": "#eff1f5",
"m3backgroundText": "#4c4f69",
"m3outline": "#9ca0b0",
"m3surfaceContainer": "#eff1f5",
"m3surfaceContainerHigh": "#e6e9ef",
"m3surfaceContainerHighest": "#dce0e8",
"m3error": "#d20f39",
"m3warning": "#fe640b",
"m3info": "#1e66f5",
},
},
{
"id": "frappe",
"name": "Frappé",
"dark": {
"m3surface": "#414559",
"m3surfaceText": "#c6d0f5",
"m3surfaceVariant": "#303446",
"m3surfaceVariantText": "#a5adce",
"m3background": "#303446",
"m3backgroundText": "#c6d0f5",
"m3outline": "#737994",
"m3surfaceContainer": "#303446",
"m3surfaceContainerHigh": "#292c3c",
"m3surfaceContainerHighest": "#232634",
"m3error": "#e78284",
"m3warning": "#ef9f76",
"m3info": "#8caaee",
},
},
{
"id": "macchiato",
"name": "Macchiato",
"dark": {
"m3surface": "#363a4f",
"m3surfaceText": "#cad3f5",
"m3surfaceVariant": "#24273a",
"m3surfaceVariantText": "#a5adcb",
"m3background": "#24273a",
"m3backgroundText": "#cad3f5",
"m3outline": "#6e738d",
"m3surfaceContainer": "#24273a",
"m3surfaceContainerHigh": "#1e2030",
"m3surfaceContainerHighest": "#181926",
"m3error": "#ed8796",
"m3warning": "#f5a97f",
"m3info": "#8aadf4",
},
},
{
"id": "mocha",
"name": "Mocha",
"dark": {
"m3surface": "#313244",
"m3surfaceText": "#cdd6f4",
"m3surfaceVariant": "#1e1e2e",
"m3surfaceVariantText": "#a6adc8",
"m3background": "#1e1e2e",
"m3backgroundText": "#cdd6f4",
"m3outline": "#6c7086",
"m3surfaceContainer": "#1e1e2e",
"m3surfaceContainerHigh": "#181825",
"m3surfaceContainerHighest": "#11111b",
"m3error": "#f38ba8",
"m3warning": "#fab387",
"m3info": "#89b4fa",
},
},
],
"accents": [
{
"id": "rosewater",
"name": "Rosewater",
"latte": {
"m3primary": "#dc8a78",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#e1a99d",
"m3secondary": "#d8c7c4",
"m3surfaceTint": "#e1a99d",
},
"frappe": {
"m3primary": "#f2d5cf",
"m3primaryText": "#303446",
"m3primaryContainer": "#b8a5a6",
"m3secondary": "#a2748b",
"m3surfaceTint": "#b8a5a6",
},
"macchiato": {
"m3primary": "#f4dbd6",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b6a6a7",
"m3secondary": "#9f6f8d",
"m3surfaceTint": "#b6a6a7",
},
"mocha": {
"m3primary": "#f5e0dc",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b5a6a8",
"m3secondary": "#9d6d87",
"m3surfaceTint": "#b5a6a8",
},
},
{
"id": "flamingo",
"name": "Flamingo",
"latte": {
"m3primary": "#dd7878",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#e29c9d",
"m3secondary": "#d7c3c4",
"m3surfaceTint": "#e29c9d",
},
"frappe": {
"m3primary": "#eebebe",
"m3primaryText": "#303446",
"m3primaryContainer": "#b5949a",
"m3secondary": "#9d6b80",
"m3surfaceTint": "#b5949a",
},
"macchiato": {
"m3primary": "#f0c6c6",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b3979c",
"m3secondary": "#996780",
"m3surfaceTint": "#b3979c",
},
"mocha": {
"m3primary": "#f2cdcd",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b3999e",
"m3secondary": "#98667c",
"m3surfaceTint": "#b3999e",
},
},
{
"id": "pink",
"name": "Pink",
"latte": {
"m3primary": "#ea76cb",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#eb9bd7",
"m3secondary": "#d9c7d5",
"m3surfaceTint": "#eb9bd7",
},
"frappe": {
"m3primary": "#f4b8e4",
"m3primaryText": "#303446",
"m3primaryContainer": "#b990b5",
"m3secondary": "#996e9e",
"m3surfaceTint": "#b990b5",
},
"macchiato": {
"m3primary": "#f5bde6",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b791b2",
"m3secondary": "#95689a",
"m3surfaceTint": "#b791b2",
},
"mocha": {
"m3primary": "#f5c2e7",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b591b0",
"m3secondary": "#966597",
"m3surfaceTint": "#b591b0",
},
},
{
"id": "mauve",
"name": "Mauve",
"latte": {
"m3primary": "#8839ef",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#a670f1",
"m3secondary": "#c2b8d0",
"m3surfaceTint": "#a670f1",
},
"frappe": {
"m3primary": "#ca9ee6",
"m3primaryText": "#303446",
"m3primaryContainer": "#9c7eb6",
"m3secondary": "#7d6799",
"m3surfaceTint": "#9c7eb6",
},
"macchiato": {
"m3primary": "#c6a0f6",
"m3primaryText": "#24273a",
"m3primaryContainer": "#967cbe",
"m3secondary": "#766597",
"m3surfaceTint": "#967cbe",
},
"mocha": {
"m3primary": "#cba6f7",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#977ebb",
"m3secondary": "#756294",
"m3surfaceTint": "#977ebb",
},
},
{
"id": "red",
"name": "Red",
"latte": {
"m3primary": "#d20f39",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#da5371",
"m3secondary": "#c0a0a8",
"m3surfaceTint": "#da5371",
},
"frappe": {
"m3primary": "#e78284",
"m3primaryText": "#303446",
"m3primaryContainer": "#b06a72",
"m3secondary": "#8b5d66",
"m3surfaceTint": "#b06a72",
},
"macchiato": {
"m3primary": "#ed8796",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b16b7a",
"m3secondary": "#865a69",
"m3surfaceTint": "#b16b7a",
},
"mocha": {
"m3primary": "#f38ba8",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b46b84",
"m3secondary": "#85596b",
"m3surfaceTint": "#b46b84",
},
},
{
"id": "maroon",
"name": "Maroon",
"latte": {
"m3primary": "#e64553",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#e87883",
"m3secondary": "#cfb7ba",
"m3surfaceTint": "#e87883",
},
"frappe": {
"m3primary": "#ea999c",
"m3primaryText": "#303446",
"m3primaryContainer": "#b27a83",
"m3secondary": "#92626f",
"m3surfaceTint": "#b27a83",
},
"macchiato": {
"m3primary": "#ee99a0",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b27781",
"m3secondary": "#8c5e6c",
"m3surfaceTint": "#b27781",
},
"mocha": {
"m3primary": "#eba0ac",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#ae7987",
"m3secondary": "#895b6c",
"m3surfaceTint": "#ae7987",
},
},
{
"id": "peach",
"name": "Peach",
"latte": {
"m3primary": "#fe640b",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#f98e51",
"m3secondary": "#c9b7ad",
"m3surfaceTint": "#f98e51",
},
"frappe": {
"m3primary": "#ef9f76",
"m3primaryText": "#303446",
"m3primaryContainer": "#b67f68",
"m3secondary": "#8f6a5f",
"m3surfaceTint": "#b67f68",
},
"macchiato": {
"m3primary": "#f5a97f",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b7836a",
"m3secondary": "#8c695e",
"m3surfaceTint": "#b7836a",
},
"mocha": {
"m3primary": "#fab387",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b8876d",
"m3secondary": "#8b6a5d",
"m3surfaceTint": "#b8876d",
},
},
{
"id": "yellow",
"name": "Yellow",
"latte": {
"m3primary": "#df8e1d",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#e4ac5d",
"m3secondary": "#c6baaa",
"m3surfaceTint": "#e4ac5d",
},
"frappe": {
"m3primary": "#e5c890",
"m3primaryText": "#303446",
"m3primaryContainer": "#af9b7a",
"m3secondary": "#948062",
"m3surfaceTint": "#af9b7a",
},
"macchiato": {
"m3primary": "#eed49f",
"m3primaryText": "#24273a",
"m3primaryContainer": "#b2a181",
"m3secondary": "#947e62",
"m3surfaceTint": "#b2a181",
},
"mocha": {
"m3primary": "#f9e2af",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b8a889",
"m3secondary": "#978265",
"m3surfaceTint": "#b8a889",
},
},
{
"id": "green",
"name": "Green",
"latte": {
"m3primary": "#40a02b",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#74b867",
"m3secondary": "#9fbd9b",
"m3surfaceTint": "#74b867",
},
"frappe": {
"m3primary": "#a6d189",
"m3primaryText": "#303446",
"m3primaryContainer": "#83a275",
"m3secondary": "#648e5e",
"m3surfaceTint": "#83a275",
},
"macchiato": {
"m3primary": "#a6da95",
"m3primaryText": "#24273a",
"m3primaryContainer": "#80a57a",
"m3secondary": "#5c8a61",
"m3surfaceTint": "#80a57a",
},
"mocha": {
"m3primary": "#a6e3a1",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#7ea87f",
"m3secondary": "#5b8964",
"m3surfaceTint": "#7ea87f",
},
},
{
"id": "teal",
"name": "Teal",
"latte": {
"m3primary": "#179299",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#57aeb4",
"m3secondary": "#93b4b7",
"m3surfaceTint": "#57aeb4",
},
"frappe": {
"m3primary": "#81c8be",
"m3primaryText": "#303446",
"m3primaryContainer": "#699b9a",
"m3secondary": "#588084",
"m3surfaceTint": "#699b9a",
},
"macchiato": {
"m3primary": "#8bd5ca",
"m3primaryText": "#24273a",
"m3primaryContainer": "#6da29f",
"m3secondary": "#577e83",
"m3surfaceTint": "#6da29f",
},
"mocha": {
"m3primary": "#94e2d5",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#71a8a4",
"m3secondary": "#588284",
"m3surfaceTint": "#71a8a4",
},
},
{
"id": "sky",
"name": "Sky",
"latte": {
"m3primary": "#04a5e5",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#4abcea",
"m3secondary": "#a4b9c2",
"m3surfaceTint": "#4abcea",
},
"frappe": {
"m3primary": "#99d1db",
"m3primaryText": "#303446",
"m3primaryContainer": "#79a2af",
"m3secondary": "#628494",
"m3surfaceTint": "#79a2af",
},
"macchiato": {
"m3primary": "#91d7e3",
"m3primaryText": "#24273a",
"m3primaryContainer": "#71a3b0",
"m3secondary": "#5e7e8c",
"m3surfaceTint": "#71a3b0",
},
"mocha": {
"m3primary": "#89dceb",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#69a3b3",
"m3secondary": "#5a7b88",
"m3surfaceTint": "#69a3b3",
},
},
{
"id": "sapphire",
"name": "Sapphire",
"latte": {
"m3primary": "#209fb5",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#5db8c8",
"m3secondary": "#9eb9be",
"m3surfaceTint": "#5db8c8",
},
"frappe": {
"m3primary": "#85c1dc",
"m3primaryText": "#303446",
"m3primaryContainer": "#6b96af",
"m3secondary": "#5e7b8e",
"m3surfaceTint": "#6b96af",
},
"macchiato": {
"m3primary": "#7dc4e4",
"m3primaryText": "#24273a",
"m3primaryContainer": "#6396b1",
"m3secondary": "#5a7486",
"m3surfaceTint": "#6396b1",
},
"mocha": {
"m3primary": "#74c7ec",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#5a95b4",
"m3secondary": "#567080",
"m3surfaceTint": "#5a95b4",
},
},
{
"id": "blue",
"name": "Blue",
"latte": {
"m3primary": "#1e66f5",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#5c90f5",
"m3secondary": "#b1bacb",
"m3surfaceTint": "#5c90f5",
},
"frappe": {
"m3primary": "#8caaee",
"m3primaryText": "#303446",
"m3primaryContainer": "#7086bc",
"m3secondary": "#637195",
"m3surfaceTint": "#7086bc",
},
"macchiato": {
"m3primary": "#8aadf4",
"m3primaryText": "#24273a",
"m3primaryContainer": "#6c85bc",
"m3secondary": "#5f6d8f",
"m3surfaceTint": "#6c85bc",
},
"mocha": {
"m3primary": "#89b4fa",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#6987bd",
"m3secondary": "#5d6c8b",
"m3surfaceTint": "#6987bd",
},
},
{
"id": "lavender",
"name": "Lavender",
"latte": {
"m3primary": "#7287fd",
"m3primaryText": "#eff1f5",
"m3primaryContainer": "#97a7fb",
"m3secondary": "#cdcfdd",
"m3surfaceTint": "#97a7fb",
},
"frappe": {
"m3primary": "#babbf1",
"m3primaryText": "#303446",
"m3primaryContainer": "#9192be",
"m3secondary": "#7175a1",
"m3surfaceTint": "#9192be",
},
"macchiato": {
"m3primary": "#b7bdf8",
"m3primaryText": "#24273a",
"m3primaryContainer": "#8b91bf",
"m3secondary": "#6b709d",
"m3surfaceTint": "#8b91bf",
},
"mocha": {
"m3primary": "#b4befe",
"m3primaryText": "#1e1e2e",
"m3primaryContainer": "#878ec0",
"m3secondary": "#676d99",
"m3surfaceTint": "#878ec0",
},
},
],
},
}

Some files were not shown because too many files have changed in this diff Show More