Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7469d44003 | |||
| cee0c547db | |||
| 4816912b35 | |||
| ee65cfd9f8 |
@@ -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
|
||||
@@ -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 .
|
||||
@@ -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
@@ -1,4 +1,4 @@
|
||||
**/__pycache__/
|
||||
./__pycache__/
|
||||
./result/
|
||||
.pyre/
|
||||
.cache/
|
||||
@@ -12,4 +12,3 @@ pkg/
|
||||
uv.lock
|
||||
.qtcreator/
|
||||
dist/
|
||||
**/target/
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.venv/
|
||||
scripts/fzf.js
|
||||
scripts/fuzzysort.js
|
||||
@@ -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
@@ -2,7 +2,7 @@ import QtQuick
|
||||
import qs.Config
|
||||
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
duration: MaterialEasing.standardTime
|
||||
easing.bezierCurve: MaterialEasing.standard
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,12 @@ CustomRect {
|
||||
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
|
||||
implicitHeight: label.implicitHeight + padding * 2
|
||||
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 {
|
||||
Anim {
|
||||
id: radiusAnim
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,4 @@ JsonObject {
|
||||
property real alignX: 0.5
|
||||
property real alignY: 0.5
|
||||
property real zoom: 1.0
|
||||
property real sourceClipX: 0
|
||||
property real sourceClipY: 0
|
||||
property real sourceClipW: 0
|
||||
property real sourceClipH: 0
|
||||
}
|
||||
|
||||
+12
-1
@@ -8,6 +8,10 @@ JsonObject {
|
||||
id: "workspaces",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "audio",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "media",
|
||||
enabled: true
|
||||
@@ -20,6 +24,10 @@ JsonObject {
|
||||
id: "updates",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "dash",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "spacer",
|
||||
enabled: true
|
||||
@@ -40,6 +48,10 @@ JsonObject {
|
||||
id: "tray",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "upower",
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
id: "network",
|
||||
enabled: false
|
||||
@@ -58,7 +70,6 @@ JsonObject {
|
||||
property Popouts popouts: Popouts {
|
||||
}
|
||||
property int rounding: 8
|
||||
property int smoothing: 32
|
||||
|
||||
component Popouts: JsonObject {
|
||||
property bool activeWindow: true
|
||||
|
||||
+1
-27
@@ -23,7 +23,6 @@ Singleton {
|
||||
property alias osd: adapter.osd
|
||||
property alias overview: adapter.overview
|
||||
property bool recentlySaved: false
|
||||
property alias screenshot: adapter.screenshot
|
||||
property alias services: adapter.services
|
||||
property alias sidebar: adapter.sidebar
|
||||
property alias utilities: adapter.utilities
|
||||
@@ -83,10 +82,6 @@ Singleton {
|
||||
wallFadeDuration: background.wallFadeDuration,
|
||||
enabled: background.enabled,
|
||||
alignX: background.alignX,
|
||||
sourceClipX: background.sourceClipX,
|
||||
sourceClipY: background.sourceClipY,
|
||||
sourceClipW: background.sourceClipW,
|
||||
sourceClipH: background.sourceClipH,
|
||||
alignY: background.alignY,
|
||||
zoom: background.zoom
|
||||
};
|
||||
@@ -98,7 +93,6 @@ Singleton {
|
||||
hideWhenNotif: barConfig.hideWhenNotif,
|
||||
rounding: barConfig.rounding,
|
||||
border: barConfig.border,
|
||||
smoothing: barConfig.smoothing,
|
||||
height: barConfig.height,
|
||||
popouts: {
|
||||
tray: barConfig.popouts.tray,
|
||||
@@ -134,8 +128,7 @@ Singleton {
|
||||
background: serializeBackground(),
|
||||
launcher: serializeLauncher(),
|
||||
colors: serializeColors(),
|
||||
dock: serializeDock(),
|
||||
screenshot: serializeScreenshot()
|
||||
dock: serializeDock()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -187,7 +180,6 @@ Singleton {
|
||||
logo: general.logo,
|
||||
wallpaperPath: general.wallpaperPath,
|
||||
desktopIcons: general.desktopIcons,
|
||||
dateFormat: general.dateFormat,
|
||||
color: {
|
||||
mode: general.color.mode,
|
||||
smart: general.color.smart,
|
||||
@@ -241,8 +233,6 @@ Singleton {
|
||||
return {
|
||||
recolorLogo: lock.recolorLogo,
|
||||
enableFprint: lock.enableFprint,
|
||||
showNotifContent: lock.showNotifContent,
|
||||
showNotifIcon: lock.showNotifIcon,
|
||||
maxFprintTries: lock.maxFprintTries,
|
||||
blurAmount: lock.blurAmount,
|
||||
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 {
|
||||
return {
|
||||
weatherLocation: services.weatherLocation,
|
||||
@@ -453,8 +429,6 @@ Singleton {
|
||||
}
|
||||
property Overview overview: Overview {
|
||||
}
|
||||
property Screenshot screenshot: Screenshot {
|
||||
}
|
||||
property Services services: Services {
|
||||
}
|
||||
property SidebarConfig sidebar: SidebarConfig {
|
||||
|
||||
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
import ZShell
|
||||
import qs.Helpers
|
||||
@@ -80,32 +79,10 @@ Singleton {
|
||||
}
|
||||
|
||||
function reloadHyprRules(): void {
|
||||
const blur = transparency.enabled ? 1 : 0;
|
||||
const alpha = transparency.base - 0.03;
|
||||
|
||||
const rules = `
|
||||
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}`);
|
||||
const barStr = "keyword layerrule %1 %2, match:namespace ZShell-Bar";
|
||||
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)]);
|
||||
Hypr.extras.batchMessage([authStr.arg("blur").arg(transparency.enabled ? 1 : 0), authStr.arg("ignore_alpha").arg(transparency.base - 0.03)]);
|
||||
}
|
||||
|
||||
function setMode(mode: string): void {
|
||||
@@ -116,14 +93,6 @@ Singleton {
|
||||
|
||||
Component.onCompleted: debounceTimer.triggered()
|
||||
|
||||
Connections {
|
||||
function onUsingLuaChanged(): void {
|
||||
root.reloadHyprRules();
|
||||
}
|
||||
|
||||
target: Hyprland
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onConfigReloaded(): void {
|
||||
root.reloadHyprRules();
|
||||
|
||||
@@ -6,7 +6,6 @@ JsonObject {
|
||||
}
|
||||
property Color color: Color {
|
||||
}
|
||||
property string dateFormat: "ddd d MMM - hh:mm:ss"
|
||||
property bool desktopIcons: false
|
||||
property Idle idle: Idle {
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ JsonObject {
|
||||
property bool enableFprint: true
|
||||
property int maxFprintTries: 3
|
||||
property bool recolorLogo: false
|
||||
property bool showNotifContent: false
|
||||
property bool showNotifIcon: true
|
||||
property Sizes sizes: Sizes {
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -3,33 +3,184 @@ import QtQuick
|
||||
Canvas {
|
||||
id: root
|
||||
|
||||
property rect dirtyRect: Qt.rect(0, 0, 0, 0)
|
||||
property bool frameQueued: false
|
||||
property bool fullRepaintPending: true
|
||||
property point lastPoint: Qt.point(0, 0)
|
||||
property real minPointDistance: 2.0
|
||||
property color penColor: "white"
|
||||
property real penWidth: 4
|
||||
property var points: []
|
||||
property var pendingSegments: []
|
||||
property bool strokeActive: false
|
||||
property var strokes: []
|
||||
|
||||
function clear(): void {
|
||||
var ctx = getContext('2d');
|
||||
root.points = [];
|
||||
ctx.reset();
|
||||
root.requestPaint();
|
||||
}
|
||||
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
onPaint: {
|
||||
if (points.length < 2)
|
||||
function appendPoint(x, y) {
|
||||
if (!strokeActive || strokes.length === 0)
|
||||
return;
|
||||
var ctx = root.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.lineWidth = root.penWidth;
|
||||
ctx.strokeStyle = root.penColor;
|
||||
ctx.lineCap = "round";
|
||||
const dx = x - lastPoint.x;
|
||||
const dy = y - lastPoint.y;
|
||||
|
||||
if ((dx * dx + dy * dy) < (minPointDistance * minPointDistance))
|
||||
return;
|
||||
const x1 = lastPoint.x;
|
||||
const y1 = lastPoint.y;
|
||||
const x2 = x;
|
||||
const y2 = y;
|
||||
|
||||
strokes[strokes.length - 1].push(Qt.point(x2, y2));
|
||||
|
||||
pendingSegments.push({
|
||||
dot: false,
|
||||
x1: x1,
|
||||
y1: y1,
|
||||
x2: x2,
|
||||
y2: y2
|
||||
});
|
||||
|
||||
lastPoint = Qt.point(x2, y2);
|
||||
queueDirty(segmentDirtyRect(x1, y1, x2, y2));
|
||||
}
|
||||
|
||||
function beginStroke(x, y) {
|
||||
const p = Qt.point(x, y);
|
||||
strokes.push([p]);
|
||||
lastPoint = p;
|
||||
strokeActive = true;
|
||||
|
||||
pendingSegments.push({
|
||||
dot: true,
|
||||
x: x,
|
||||
y: y
|
||||
});
|
||||
|
||||
queueDirty(pointDirtyRect(x, y));
|
||||
}
|
||||
|
||||
function clear() {
|
||||
strokes = [];
|
||||
pendingSegments = [];
|
||||
dirtyRect = Qt.rect(0, 0, 0, 0);
|
||||
fullRepaintPending = true;
|
||||
markDirty(Qt.rect(0, 0, width, height));
|
||||
}
|
||||
|
||||
function drawDot(ctx, x, y) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0].x, points[0].y);
|
||||
for (var i = 1; i < points.length; i++)
|
||||
ctx.lineTo(points[i].x, points[i].y);
|
||||
ctx.arc(x, y, penWidth / 2, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function drawSegment(ctx, x1, y1, x2, y2) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function endStroke() {
|
||||
strokeActive = false;
|
||||
}
|
||||
|
||||
function pointDirtyRect(x, y) {
|
||||
const pad = penWidth + 2;
|
||||
return Qt.rect(x - pad, y - pad, pad * 2, pad * 2);
|
||||
}
|
||||
|
||||
function queueDirty(r) {
|
||||
dirtyRect = unionRects(dirtyRect, r);
|
||||
|
||||
if (frameQueued)
|
||||
return;
|
||||
frameQueued = true;
|
||||
|
||||
requestAnimationFrame(function () {
|
||||
frameQueued = false;
|
||||
|
||||
if (dirtyRect.width > 0 && dirtyRect.height > 0) {
|
||||
markDirty(dirtyRect);
|
||||
dirtyRect = Qt.rect(0, 0, 0, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function replayAll(ctx) {
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
for (const stroke of strokes) {
|
||||
if (!stroke || stroke.length === 0)
|
||||
continue;
|
||||
if (stroke.length === 1) {
|
||||
const p = stroke[0];
|
||||
drawDot(ctx, p.x, p.y);
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(stroke[0].x, stroke[0].y);
|
||||
for (let i = 1; i < stroke.length; ++i)
|
||||
ctx.lineTo(stroke[i].x, stroke[i].y);
|
||||
ctx.stroke();
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -30,10 +30,8 @@ CustomMouseArea {
|
||||
const x = event.x;
|
||||
const y = event.y;
|
||||
|
||||
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
|
||||
root.drawing.points.push(Qt.point(x, y));
|
||||
root.drawing.requestPaint();
|
||||
}
|
||||
if (event.buttons & Qt.LeftButton)
|
||||
root.drawing.appendPoint(x, y);
|
||||
|
||||
if (root.inLeftPanel(root.popout, x, y)) {
|
||||
root.z = -2;
|
||||
@@ -46,8 +44,7 @@ CustomMouseArea {
|
||||
|
||||
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
|
||||
root.panels.drawing.expanded = false;
|
||||
root.drawing.points.push(Qt.point(x, y));
|
||||
root.drawing.requestPaint();
|
||||
root.drawing.beginStroke(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,6 +52,7 @@ CustomMouseArea {
|
||||
root.drawing.clear();
|
||||
}
|
||||
onReleased: {
|
||||
root.drawing.points = [];
|
||||
if (root.visibilities.isDrawing)
|
||||
root.drawing.endStroke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,37 +12,22 @@ Scope {
|
||||
required property ShellScreen screen
|
||||
|
||||
ExclusionZone {
|
||||
id: top
|
||||
|
||||
anchors.top: true
|
||||
exclusiveZone: root.bar.exclusiveZone
|
||||
}
|
||||
|
||||
ExclusionZone {
|
||||
id: left
|
||||
|
||||
anchors.left: true
|
||||
}
|
||||
|
||||
ExclusionZone {
|
||||
id: right
|
||||
|
||||
anchors.right: true
|
||||
}
|
||||
|
||||
ExclusionZone {
|
||||
id: bottom
|
||||
|
||||
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 {
|
||||
exclusiveZone: Config.barConfig.border
|
||||
implicitHeight: 1
|
||||
|
||||
@@ -78,7 +78,7 @@ CustomMouseArea {
|
||||
const dragY = y - dragStart.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ Item {
|
||||
readonly property alias resourcesWrapper: resourcesWrapper
|
||||
required property ShellScreen screen
|
||||
readonly property alias settings: settings
|
||||
readonly property alias settingsWrapper: settingsWrapper
|
||||
readonly property alias sidebar: sidebar
|
||||
readonly property alias toasts: toasts
|
||||
readonly property alias utilities: utilities
|
||||
@@ -177,15 +176,6 @@ Item {
|
||||
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 {
|
||||
id: settings
|
||||
|
||||
@@ -196,7 +186,6 @@ Item {
|
||||
screen: root.screen
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Dock.Wrapper {
|
||||
id: dock
|
||||
|
||||
+18
-40
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import ZShell.Blobs
|
||||
@@ -128,14 +127,6 @@ Variants {
|
||||
Component.onCompleted: Visibilities.load(scope.modelData, this)
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function toggleLauncher(fix: string): void {
|
||||
visibilities.launcher = !visibilities.launcher;
|
||||
}
|
||||
|
||||
target: "visibilities"
|
||||
}
|
||||
|
||||
Binding {
|
||||
property: "bar"
|
||||
target: visibilities
|
||||
@@ -158,7 +149,6 @@ Variants {
|
||||
id: blobGroup
|
||||
|
||||
color: DynamicColors.palette.m3surface
|
||||
smoothing: Config.barConfig.smoothing
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
@@ -180,34 +170,28 @@ Variants {
|
||||
PanelBg {
|
||||
id: dashBg
|
||||
|
||||
property real extraHeight: 0.2
|
||||
|
||||
deformAmount: 0.06
|
||||
implicitHeight: panels.dashboard.height * (1 + extraHeight)
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
implicitHeight: panels.dashboard.height
|
||||
implicitWidth: panels.dashboard.width
|
||||
panel: panels.dashboardWrapper
|
||||
panel: panels.dashboard
|
||||
radius: Appearance.rounding.normal
|
||||
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 {
|
||||
id: launcherBg
|
||||
|
||||
property real extraHeight: 0.2
|
||||
|
||||
deformAmount: 0.06
|
||||
implicitHeight: panels.launcher.height * (1 + extraHeight)
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
panel: panels.launcher
|
||||
radius: Appearance.rounding.smallest + 5
|
||||
y: panels.launcher.y + bar.implicitHeight
|
||||
}
|
||||
|
||||
PanelBg {
|
||||
id: sidebarBg
|
||||
|
||||
bottomLeftRadius: 0
|
||||
deformAmount: 0.04
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
|
||||
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
|
||||
panel: panels.sidebar
|
||||
@@ -216,10 +200,10 @@ Variants {
|
||||
PanelBg {
|
||||
id: osdBg
|
||||
|
||||
deformAmount: 0.1
|
||||
deformAmount: 0.1 * Config.appearance.deform.scale
|
||||
implicitHeight: panels.osd.height
|
||||
implicitWidth: panels.osd.width
|
||||
panel: panels.osdWrapper
|
||||
panel: panels.osd
|
||||
radius: 20
|
||||
x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border
|
||||
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
|
||||
@@ -234,7 +218,7 @@ Variants {
|
||||
PanelBg {
|
||||
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]
|
||||
panel: panels.utilities
|
||||
topLeftRadius: 0
|
||||
@@ -245,11 +229,11 @@ Variants {
|
||||
|
||||
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)
|
||||
implicitWidth: panels.popouts.width
|
||||
panel: panels.popoutsWrapper
|
||||
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale
|
||||
panel: panels.popouts
|
||||
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
|
||||
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
|
||||
|
||||
@@ -262,10 +246,10 @@ Variants {
|
||||
PanelBg {
|
||||
id: resourcesBg
|
||||
|
||||
deformAmount: 0.05
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
implicitHeight: panels.resources.height
|
||||
implicitWidth: panels.resources.width
|
||||
panel: panels.resourcesWrapper
|
||||
panel: panels.resources
|
||||
radius: Appearance.rounding.normal
|
||||
x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border
|
||||
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
|
||||
@@ -274,23 +258,17 @@ Variants {
|
||||
PanelBg {
|
||||
id: settingsBg
|
||||
|
||||
property real extraHeight: 0.2
|
||||
|
||||
deformAmount: 0.03
|
||||
implicitHeight: panels.settings.height * (1 + extraHeight)
|
||||
implicitWidth: panels.settings.width
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
panel: panels.settings
|
||||
radius: Appearance.rounding.large
|
||||
topLeftRadius: 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 {
|
||||
id: dockBg
|
||||
|
||||
deformAmount: 0.08
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
panel: panels.dock
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
@@ -298,7 +276,7 @@ Variants {
|
||||
PanelBg {
|
||||
id: drawingBg
|
||||
|
||||
deformAmount: 0.08
|
||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
||||
panel: panels.drawing
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
@@ -393,7 +371,7 @@ Variants {
|
||||
property real deformAmount: 0.15
|
||||
required property Item panel
|
||||
|
||||
deformScale: (deformAmount * Config.appearance.deform.scale) / 10000
|
||||
deformScale: deformAmount / 10000
|
||||
group: blobGroup
|
||||
implicitHeight: panel.height
|
||||
implicitWidth: panel.width
|
||||
|
||||
+20
-20
@@ -16,14 +16,27 @@ Scope {
|
||||
property bool launching: false
|
||||
property string promptMessage: ""
|
||||
readonly property var selectedSession: sessionIndex >= 0 ? sessions[sessionIndex] : null
|
||||
readonly property var selectedUser: Users.selectedUser
|
||||
property int sessionIndex: sessions.length > 0 ? 0 : -1
|
||||
property var sessions: []
|
||||
readonly property string userFace: selectedUser ? selectedUser.face : ""
|
||||
readonly property string username: Users.selectedUsername
|
||||
|
||||
// User handling - now uses the Users singleton
|
||||
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
|
||||
|
||||
@@ -45,11 +58,11 @@ Scope {
|
||||
|
||||
event.accepted = true;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
buffer = "";
|
||||
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
|
||||
// No illegal characters (you are insane if you use unicode in your password)
|
||||
}
|
||||
|
||||
if (event.text && !/[\r\n]/.test(event.text)) {
|
||||
buffer += event.text;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,19 +81,6 @@ Scope {
|
||||
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 {
|
||||
errorMessage = "";
|
||||
|
||||
|
||||
@@ -346,6 +346,7 @@ Singleton {
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
console.log("this is running");
|
||||
if (root.gpuType === "GENERIC") {
|
||||
const percs = text.trim().split("\n");
|
||||
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
|
||||
|
||||
+44
-20
@@ -7,30 +7,23 @@ import QtQuick
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user"
|
||||
property int selectedIndex: 0
|
||||
readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
|
||||
readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
|
||||
// The list of users that can log in graphically
|
||||
// Each user object has: username, uid, home, shell, gecos (full name), face (avatar path)
|
||||
property var users: []
|
||||
|
||||
function saveDefaultUser(): void {
|
||||
if (selectedUser) {
|
||||
defaultUserStorage.setText(selectedUser.username);
|
||||
}
|
||||
}
|
||||
// The currently selected user index
|
||||
property int selectedIndex: 0
|
||||
|
||||
function selectNext(): void {
|
||||
if (users.length === 0)
|
||||
return;
|
||||
selectedIndex = (selectedIndex + 1) % users.length;
|
||||
}
|
||||
// The currently selected user object (or null if none)
|
||||
readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
|
||||
|
||||
function selectPrevious(): void {
|
||||
if (users.length === 0)
|
||||
return;
|
||||
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
|
||||
}
|
||||
// Convenience property for the selected username
|
||||
readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
|
||||
|
||||
// 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 {
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
if (users[i].username === username) {
|
||||
@@ -41,6 +34,28 @@ Singleton {
|
||||
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 {
|
||||
id: userLister
|
||||
|
||||
@@ -52,10 +67,13 @@ Singleton {
|
||||
try {
|
||||
root.users = JSON.parse(text);
|
||||
|
||||
// If we have users and no selection yet, try to select the default user
|
||||
if (root.users.length > 0) {
|
||||
// Try to load the default user
|
||||
if (defaultUserStorage.loaded) {
|
||||
const defaultUsername = defaultUserStorage.text().trim();
|
||||
if (defaultUsername && !root.selectUser(defaultUsername)) {
|
||||
// Default user not found, select first user
|
||||
root.selectedIndex = 0;
|
||||
}
|
||||
} else {
|
||||
@@ -69,14 +87,15 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// FileView for persisting the default user
|
||||
FileView {
|
||||
id: defaultUserStorage
|
||||
|
||||
path: root.defaultUserFile
|
||||
preload: true
|
||||
|
||||
onLoadFailed: {}
|
||||
onLoaded: {
|
||||
// If users are already loaded, try to select the default user
|
||||
if (root.users.length > 0) {
|
||||
const defaultUsername = text().trim();
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -66,8 +66,7 @@ MouseArea {
|
||||
|
||||
function save(): void {
|
||||
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([...cmd, path]));
|
||||
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path]));
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
|
||||
+2
-7
@@ -1,23 +1,18 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property string amPmStr: timeComponents[2] ?? ""
|
||||
readonly property date date: clock.date
|
||||
readonly property string dateStr: format(Config.general.dateFormat)
|
||||
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")
|
||||
readonly property string timeStr: format("hh:mm")
|
||||
|
||||
function format(fmt: string): string {
|
||||
return Qt.formatDateTime(clock.date, fmt);
|
||||
|
||||
@@ -6,21 +6,7 @@ import Quickshell.Services.UPower
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property real batteryPercent: UPower.displayDevice.percentage
|
||||
readonly property list<UPowerDevice> devices: UPower.devices.values
|
||||
readonly property UPowerDevice displayDevice: UPower.displayDevice
|
||||
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 "))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -11,13 +11,10 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property int availableUpdates: 0
|
||||
property string cmd: ""
|
||||
property bool commandReady
|
||||
property bool loaded
|
||||
property double now: Date.now()
|
||||
property var updates: ({})
|
||||
property bool updating
|
||||
property string updatingPackage: ""
|
||||
|
||||
function formatUpdateTime(timestamp) {
|
||||
const diffMs = root.now - timestamp;
|
||||
@@ -37,22 +34,6 @@ Singleton {
|
||||
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: {
|
||||
if (!root.loaded)
|
||||
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 {
|
||||
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 {
|
||||
id: saveTimer
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
import ZShell.Models
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
@@ -14,17 +12,10 @@ Searcher {
|
||||
id: root
|
||||
|
||||
property string actualCurrent: WallpaperPath.currentWallpaperPath
|
||||
property alias crops: adapter.monitorCrops
|
||||
readonly property string current: showPreview ? previewPath : actualCurrent
|
||||
property alias monitorCrops: monitorCrops
|
||||
property string previewPath
|
||||
property bool recentlyChanged
|
||||
property bool showPreview: false
|
||||
|
||||
function getCrop(screen: string): var {
|
||||
return root.crops[screen];
|
||||
}
|
||||
|
||||
function preview(path: string): void {
|
||||
previewPath = path;
|
||||
if (Config.general.color.schemeGeneration)
|
||||
@@ -32,35 +23,9 @@ Searcher {
|
||||
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 {
|
||||
actualCurrent = 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}`]);
|
||||
if (Config.general.color.schemeGeneration)
|
||||
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"
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: monitorCrops
|
||||
|
||||
path: `${Paths.state}/wallpaper-crops.json`
|
||||
watchChanges: true
|
||||
|
||||
onAdapterUpdated: writeAdapter()
|
||||
onFileChanged: reload()
|
||||
|
||||
JsonAdapter {
|
||||
id: adapter
|
||||
|
||||
property var monitorCrops: ({})
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemModel {
|
||||
id: wallpapers
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import qs.Helpers
|
||||
import qs.Helpers as Helpers
|
||||
import qs.Components
|
||||
|
||||
CustomRect {
|
||||
|
||||
@@ -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
@@ -12,29 +12,90 @@ Item {
|
||||
|
||||
required property Canvas drawing
|
||||
property bool expanded: true
|
||||
property real offsetScale: shouldBeActive ? 0 : 1
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeActive: visibilities.isDrawing
|
||||
required property var visibilities
|
||||
|
||||
anchors.leftMargin: (-implicitWidth - 5) * offsetScale
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: root.expanded ? content.implicitWidth : icon.implicitWidth
|
||||
opacity: 1 - offsetScale
|
||||
visible: offsetScale < 1
|
||||
implicitWidth: 0
|
||||
visible: width > 0
|
||||
|
||||
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 {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitWidth"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
Behavior on offsetScale {
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
duration: Appearance.anim.durations.small
|
||||
property: "opacity"
|
||||
target: icon
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
property: "opacity"
|
||||
target: content
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible)
|
||||
@@ -48,12 +109,8 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: content.contentItem.height
|
||||
opacity: root.expanded ? 0 : 1
|
||||
opacity: 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
sourceComponent: MaterialIcon {
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: "arrow_forward_ios"
|
||||
@@ -65,12 +122,7 @@ Item {
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: root.expanded ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
sourceComponent: Content {
|
||||
drawing: root.drawing
|
||||
visibilities: root.visibilities
|
||||
|
||||
@@ -13,6 +13,7 @@ Searcher {
|
||||
|
||||
function launch(entry: DesktopEntry): void {
|
||||
appDb.incrementFrequency(entry.id);
|
||||
console.log(root.command);
|
||||
|
||||
if (entry.runInTerminal)
|
||||
Quickshell.execDetached({
|
||||
|
||||
@@ -8,8 +8,8 @@ import qs.Helpers
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
readonly property bool enabled: !Players.list.some(p => p.isPlaying)
|
||||
required property Lock lock
|
||||
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
|
||||
|
||||
function handleIdleAction( action: var ): void {
|
||||
if ( !action )
|
||||
@@ -19,10 +19,6 @@ Scope {
|
||||
lock.lock.locked = true;
|
||||
else if ( action === "unlock" )
|
||||
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" )
|
||||
Hypr.dispatch( action );
|
||||
else
|
||||
@@ -37,7 +33,6 @@ Scope {
|
||||
|
||||
enabled: root.enabled && modelData.timeout > 0 ? true : false
|
||||
timeout: modelData.timeout
|
||||
|
||||
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ CustomRect {
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
height: Config.notifs.sizes.image
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
visible: Config.lock.showNotifIcon
|
||||
width: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
@@ -285,8 +284,6 @@ CustomRect {
|
||||
Layout.fillWidth: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
text: {
|
||||
if (!Config.lock.showNotifContent)
|
||||
return "Unlock to view";
|
||||
const summary = modelData.summary.replace(/\n/g, " ");
|
||||
const body = modelData.body.replace(/\n/g, " ");
|
||||
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
||||
|
||||
@@ -30,8 +30,6 @@ Scope {
|
||||
} else {
|
||||
buffer = buffer.slice(0, -1);
|
||||
}
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
buffer = "";
|
||||
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
|
||||
// No illegal characters (you are insane if you use unicode in your password)
|
||||
buffer += event.text;
|
||||
|
||||
@@ -10,6 +10,7 @@ Item {
|
||||
|
||||
readonly property int padding: 6
|
||||
required property Item panels
|
||||
required property Item sidebarPanel
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
@@ -43,7 +44,7 @@ Item {
|
||||
|
||||
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 {
|
||||
Anim {
|
||||
@@ -72,8 +73,9 @@ Item {
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property alias nonAnimHeight: notif.nonAnimHeight
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
|
||||
implicitWidth: notif.implicitWidth
|
||||
|
||||
ListView.onRemove: removeAnim.start()
|
||||
onIndexChanged: {
|
||||
@@ -124,16 +126,20 @@ Item {
|
||||
}
|
||||
|
||||
ClippingRectangle {
|
||||
// implicitWidth: notif.implicitWidth
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: wrapper.idx === 0 ? 0 : 8
|
||||
color: "transparent"
|
||||
implicitHeight: notif.implicitHeight
|
||||
implicitWidth: notif.implicitWidth
|
||||
radius: Appearance.rounding.smallest / 2
|
||||
|
||||
Notification {
|
||||
id: notif
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
modelData: wrapper.modelData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ CustomRect {
|
||||
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
|
||||
|
||||
// implicitWidth: Config.notifs.sizes.width
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: inner.implicitHeight
|
||||
implicitWidth: Config.notifs.sizes.width
|
||||
radius: 6
|
||||
x: Config.notifs.sizes.width
|
||||
|
||||
|
||||
@@ -78,10 +78,6 @@ LazyListView {
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: event => {
|
||||
if (event.button === Qt.LeftButton)
|
||||
notifInner.toggleExpand(!notifInner.expanded);
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed) {
|
||||
const diffY = event.y - startY;
|
||||
|
||||
@@ -79,10 +79,6 @@ LazyListView {
|
||||
|
||||
Component.onCompleted: modelData?.lock(this)
|
||||
Component.onDestruction: modelData?.unlock(this)
|
||||
onDoubleClicked: event => {
|
||||
if (event.button === Qt.LeftButton)
|
||||
root.requestToggleExpand(!root.expanded);
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed && !root.expanded) {
|
||||
const diffY = event.y - startY;
|
||||
|
||||
@@ -5,7 +5,6 @@ import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
import qs.Helpers
|
||||
import qs.Daemons
|
||||
|
||||
CustomRect {
|
||||
@@ -33,7 +32,7 @@ CustomRect {
|
||||
Toggle {
|
||||
checked: Network.wifiEnabled
|
||||
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()
|
||||
}
|
||||
@@ -80,13 +79,6 @@ CustomRect {
|
||||
adapter.enabled = !adapter.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
checked: GameMode.enabled
|
||||
icon: GameMode.enabled ? "videogame_asset" : "videogame_asset_off"
|
||||
|
||||
onClicked: GameMode.enabled = !GameMode.enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ Item {
|
||||
id: content
|
||||
|
||||
panels: root.panels
|
||||
sidebarPanel: root.sidebarPanel
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ CustomRect {
|
||||
color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.full
|
||||
radius: height / 2
|
||||
|
||||
StateLayer {
|
||||
onClicked: root.visibilities.resources = !root.visibilities.resources
|
||||
|
||||
@@ -104,18 +104,6 @@ Item {
|
||||
key: "launcher"
|
||||
name: "Launcher"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
icon: "screenshot_region"
|
||||
key: "screenshot"
|
||||
name: "Screenshot"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
icon: "cached"
|
||||
key: "updates"
|
||||
name: "Updates"
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
@@ -183,15 +171,9 @@ Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: icon.contentWidth
|
||||
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
|
||||
text: categoryItem.icon
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Quickshell
|
||||
import QtQuick.Layouts
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
@@ -6,8 +5,6 @@ import qs.Config
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Wallpaper"
|
||||
|
||||
@@ -32,11 +29,13 @@ SettingsPage {
|
||||
step: 50
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
WallpaperCropper {
|
||||
}
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// WallpaperCropper {
|
||||
// Layout.fillWidth: true
|
||||
// Layout.preferredHeight: 300
|
||||
// }
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
|
||||
@@ -19,8 +19,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Height"
|
||||
min: 1
|
||||
object: Config.barConfig
|
||||
setting: "height"
|
||||
}
|
||||
@@ -29,8 +29,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Rounding"
|
||||
min: 0
|
||||
object: Config.barConfig
|
||||
setting: "rounding"
|
||||
}
|
||||
@@ -39,21 +39,11 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Border"
|
||||
min: 0
|
||||
object: Config.barConfig
|
||||
setting: "border"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Smoothing"
|
||||
object: Config.barConfig
|
||||
setting: "smoothing"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
@@ -155,8 +145,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Dock height"
|
||||
min: 1
|
||||
object: Config.dock
|
||||
setting: "height"
|
||||
}
|
||||
@@ -183,8 +173,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add pinned app")
|
||||
name: "Pinned apps"
|
||||
addLabel: qsTr("Add pinned app")
|
||||
object: Config.dock
|
||||
setting: "pinnedApps"
|
||||
}
|
||||
@@ -193,8 +183,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add ignored regex")
|
||||
name: "Ignored app regexes"
|
||||
addLabel: qsTr("Add ignored regex")
|
||||
object: Config.dock
|
||||
setting: "ignoredAppRegexes"
|
||||
}
|
||||
|
||||
@@ -47,15 +47,6 @@ SettingsPage {
|
||||
object: Config.general
|
||||
setting: "desktopIcons"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Date format"
|
||||
object: Config.general
|
||||
setting: "dateFormat"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
|
||||
@@ -31,8 +31,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Max fingerprint tries"
|
||||
min: 1
|
||||
object: Config.lock
|
||||
setting: "maxFprintTries"
|
||||
step: 1
|
||||
@@ -41,27 +41,9 @@ SettingsPage {
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show notification details"
|
||||
object: Config.lock
|
||||
setting: "showNotifContent"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show notification icon"
|
||||
object: Config.lock
|
||||
setting: "showNotifIcon"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Blur amount"
|
||||
min: 0
|
||||
object: Config.lock
|
||||
setting: "blurAmount"
|
||||
step: 1
|
||||
@@ -71,9 +53,9 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Height multiplier"
|
||||
max: 2
|
||||
min: 0.1
|
||||
name: "Height multiplier"
|
||||
object: Config.lock.sizes
|
||||
setting: "heightMult"
|
||||
step: 0.05
|
||||
@@ -83,9 +65,9 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Aspect ratio"
|
||||
max: 4
|
||||
min: 0.5
|
||||
name: "Aspect ratio"
|
||||
object: Config.lock.sizes
|
||||
setting: "ratio"
|
||||
step: 0.05
|
||||
@@ -95,8 +77,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 100
|
||||
name: "Center width"
|
||||
min: 100
|
||||
object: Config.lock.sizes
|
||||
setting: "centerWidth"
|
||||
step: 10
|
||||
|
||||
@@ -22,13 +22,6 @@ ColumnLayout {
|
||||
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) {
|
||||
const list = [...Config.general.idle.timeouts];
|
||||
let entry = list[i];
|
||||
@@ -56,9 +49,6 @@ ColumnLayout {
|
||||
onAddActiveActionRequested: {
|
||||
root.updateTimeoutEntry(index, "activeAction", "");
|
||||
}
|
||||
onDeleteRequested: function (index) {
|
||||
root.deleteTimeoutEntry(index);
|
||||
}
|
||||
onFieldEdited: function (key, value) {
|
||||
root.updateTimeoutEntry(index, key, value);
|
||||
}
|
||||
|
||||
@@ -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,5 +1,3 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
@@ -22,6 +20,7 @@ Item {
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
function scrollToSetting(section: string, settingName: string) {
|
||||
// Wait for the StackView transition to complete, then scroll
|
||||
root.pendingSection = section;
|
||||
root.pendingSetting = settingName;
|
||||
scrollTimer.restart();
|
||||
@@ -75,10 +74,6 @@ Item {
|
||||
stack.push(osd);
|
||||
else if (currentCategory === "launcher")
|
||||
stack.push(launcher);
|
||||
else if (currentCategory === "screenshot")
|
||||
stack.push(screenshot);
|
||||
else if (currentCategory === "updates")
|
||||
stack.push(updates);
|
||||
}
|
||||
|
||||
target: root
|
||||
@@ -158,7 +153,6 @@ Item {
|
||||
id: background
|
||||
|
||||
Cat.Background {
|
||||
screen: root.screen
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,18 +225,4 @@ Item {
|
||||
Cat.Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: screenshot
|
||||
|
||||
Cat.Screenshot {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: updates
|
||||
|
||||
Cat.SystemUpdates {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,72 @@ Item {
|
||||
property int uidCounter: 0
|
||||
property var visualEntries: []
|
||||
|
||||
function beginVisualDrag(uid, modelData, item) {
|
||||
const pos = item.mapToItem(root, 0, 0);
|
||||
|
||||
root.draggedUid = uid;
|
||||
root.draggedModelData = modelData;
|
||||
root.dragHeight = item.height;
|
||||
root.dragStartX = pos.x;
|
||||
root.dragStartY = pos.y;
|
||||
root.dragX = pos.x;
|
||||
root.dragY = pos.y;
|
||||
root.dragActive = true;
|
||||
root.dropAnimating = false;
|
||||
root.pendingCommitEntries = [];
|
||||
}
|
||||
|
||||
function commitVisualOrder(entries) {
|
||||
const list = [];
|
||||
|
||||
for (let i = 0; i < entries.length; i++)
|
||||
list.push(entries[i].entry);
|
||||
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
root.rebuildVisualEntries();
|
||||
}
|
||||
|
||||
function endVisualDrag() {
|
||||
const entries = root.visualEntries.slice();
|
||||
const finalIndex = root.indexForUid(root.draggedUid);
|
||||
const finalItem = listView.itemAtIndex(finalIndex);
|
||||
|
||||
root.dragActive = false;
|
||||
|
||||
if (!finalItem) {
|
||||
root.pendingCommitEntries = entries;
|
||||
root.finishVisualDrag();
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = finalItem.mapToItem(root, 0, 0);
|
||||
|
||||
root.pendingCommitEntries = entries;
|
||||
root.dropAnimating = true;
|
||||
settleX.to = pos.x;
|
||||
settleY.to = pos.y;
|
||||
settleAnim.start();
|
||||
}
|
||||
|
||||
function ensureVisualEntries() {
|
||||
if (!root.dragActive && !root.dropAnimating)
|
||||
root.rebuildVisualEntries();
|
||||
}
|
||||
|
||||
function finishVisualDrag() {
|
||||
const entries = root.pendingCommitEntries.slice();
|
||||
|
||||
root.dragActive = false;
|
||||
root.dropAnimating = false;
|
||||
root.draggedUid = "";
|
||||
root.draggedModelData = null;
|
||||
root.pendingCommitEntries = [];
|
||||
root.dragHeight = 0;
|
||||
|
||||
root.commitVisualOrder(entries);
|
||||
}
|
||||
|
||||
function iconForId(id) {
|
||||
switch (id) {
|
||||
case "workspaces":
|
||||
@@ -92,7 +153,7 @@ Item {
|
||||
case "spacer":
|
||||
return qsTr("Spacer");
|
||||
case "activeWindow":
|
||||
return qsTr("Title");
|
||||
return qsTr("Active window");
|
||||
case "tray":
|
||||
return qsTr("Tray");
|
||||
case "upower":
|
||||
@@ -102,14 +163,34 @@ Item {
|
||||
case "clock":
|
||||
return qsTr("Clock");
|
||||
case "notifBell":
|
||||
return qsTr("Notifs");
|
||||
case "hyprsunset":
|
||||
return qsTr("Night light");
|
||||
return qsTr("Notification bell");
|
||||
default:
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
function moveArrayItem(list, from, to) {
|
||||
const next = list.slice();
|
||||
const [item] = next.splice(from, 1);
|
||||
next.splice(to, 0, item);
|
||||
return next;
|
||||
}
|
||||
|
||||
function previewVisualMove(from, hovered, before) {
|
||||
let to = hovered + (before ? 0 : 1);
|
||||
|
||||
if (to > from)
|
||||
to -= 1;
|
||||
|
||||
to = Math.max(0, Math.min(visualModel.items.count - 1, to));
|
||||
|
||||
if (from === to)
|
||||
return;
|
||||
|
||||
visualModel.items.move(from, to);
|
||||
root.visualEntries = root.moveArrayItem(root.visualEntries, from, to);
|
||||
}
|
||||
|
||||
function rebuildVisualEntries() {
|
||||
const entries = root.object[root.setting] ?? [];
|
||||
const next = [];
|
||||
@@ -144,6 +225,7 @@ Item {
|
||||
list[index] = entry;
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
root.ensureVisualEntries();
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -151,7 +233,7 @@ Item {
|
||||
|
||||
Component.onCompleted: root.rebuildVisualEntries()
|
||||
|
||||
CustomRect {
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
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
|
||||
|
||||
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 {
|
||||
id: visualModel
|
||||
@@ -184,40 +293,248 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
delegate: entryDelegate
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: false
|
||||
implicitHeight: contentHeight
|
||||
implicitWidth: width
|
||||
interactive: !(root.dragActive || root.dropAnimating)
|
||||
model: visualModel
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
addDisplaced: Transition {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
removeDisplaced: Transition {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.dragActive || root.dropAnimating
|
||||
asynchronous: false
|
||||
|
||||
sourceComponent: Item {
|
||||
Drag.active: root.dragActive
|
||||
Drag.hotSpot.x: width / 2
|
||||
Drag.hotSpot.y: height / 2
|
||||
height: proxyRect.implicitHeight
|
||||
implicitHeight: proxyRect.implicitHeight
|
||||
implicitWidth: listView.width
|
||||
visible: root.draggedModelData !== null
|
||||
width: listView.width
|
||||
x: root.dragX
|
||||
y: root.dragY
|
||||
z: 100
|
||||
|
||||
Drag.source: QtObject {
|
||||
property string uid: root.draggedUid
|
||||
property int visualIndex: root.indexForUid(root.draggedUid)
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: proxyRect
|
||||
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
implicitHeight: proxyRow.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: parent.width
|
||||
opacity: 0.95
|
||||
radius: Appearance.rounding.normal
|
||||
width: parent.width
|
||||
|
||||
RowLayout {
|
||||
id: proxyRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomRect {
|
||||
color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.12)
|
||||
implicitHeight: 32
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: "drag_indicator"
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: root.iconForId(root.draggedModelData?.entry?.id ?? "")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.labelForId(root.draggedModelData?.entry?.id ?? "")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
checked: root.draggedModelData?.entry?.enabled ?? true
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: entryDelegate
|
||||
|
||||
IconButton {
|
||||
required property int index
|
||||
DropArea {
|
||||
id: slot
|
||||
|
||||
readonly property var entryData: modelData.entry
|
||||
required property var modelData
|
||||
readonly property string uid: modelData.uid
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
|
||||
checked: modelData.entry.enabled ?? true
|
||||
font: Appearance.font.family.sans
|
||||
// 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))
|
||||
function previewReorder(drag) {
|
||||
const source = drag.source;
|
||||
if (!source || !source.uid || source.uid === slot.uid)
|
||||
return;
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ Item {
|
||||
required property var modelData
|
||||
|
||||
signal addActiveActionRequested
|
||||
signal deleteRequested(int index)
|
||||
signal fieldEdited(string key, var value)
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -66,64 +65,42 @@ Item {
|
||||
|
||||
HoverHandler {
|
||||
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 {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: nameHover.hovered ? editButton.width + Appearance.spacing.smaller * 2 : 0
|
||||
anchors.right: deleteButton.left
|
||||
anchors.right: editButton.left
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight // enable if CustomText supports it
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.modelData.name
|
||||
visible: !nameCell.editing
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HoverIconButton {
|
||||
id: deleteButton
|
||||
IconButton {
|
||||
id: editButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "delete"
|
||||
shouldBeVisible: nameHover.hovered && !nameCell.editing
|
||||
icon: "edit"
|
||||
visible: nameHover.hovered && !nameCell.editing
|
||||
|
||||
onClicked: root.deleteRequested(root.index)
|
||||
onClicked: nameCell.beginEdit()
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.fill: parent
|
||||
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
|
||||
visible: nameCell.editing
|
||||
|
||||
CustomTextField {
|
||||
id: nameEditor
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, nameCell.width - Appearance.padding.normal)
|
||||
anchors.fill: parent
|
||||
text: nameCell.draftName
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
|
||||
@@ -38,7 +38,7 @@ Item {
|
||||
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
|
||||
Layout.preferredWidth: 500
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
|
||||
radius: 21 + Appearance.padding.normal
|
||||
|
||||
CustomRect {
|
||||
id: searchBox
|
||||
|
||||
@@ -1,261 +1,102 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import ZShell.Internal
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
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 {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
spacing: 15
|
||||
width: Math.min(parent ? parent.width : 600, 600)
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
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();
|
||||
}
|
||||
Rectangle {
|
||||
id: previewContainer
|
||||
|
||||
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 {
|
||||
function onRequestCrop(): void {
|
||||
delegate.applyCrop();
|
||||
}
|
||||
Image {
|
||||
id: img
|
||||
|
||||
target: wrapper
|
||||
}
|
||||
|
||||
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
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
// retainWhileLoading: true
|
||||
source: Wallpapers.current
|
||||
sourceSize.height: parent.height
|
||||
sourceSize.width: parent.width
|
||||
|
||||
onPaintedWidthChanged: {
|
||||
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 {
|
||||
Rectangle {
|
||||
id: cropRect
|
||||
|
||||
property real aspectRatio: delegate.modelData.width / delegate.modelData.height
|
||||
readonly property real baseHeight: baseWidth / aspectRatio
|
||||
readonly property real baseWidth: {
|
||||
let fittedHeight = scaledImg.paintedHeight;
|
||||
let fittedWidth = fittedHeight * aspectRatio;
|
||||
|
||||
if (fittedWidth > scaledImg.paintedWidth) {
|
||||
fittedWidth = scaledImg.paintedWidth;
|
||||
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();
|
||||
}
|
||||
}
|
||||
property real cropHeight: (imageAspect > screenAspect ? paintedHeight : paintedWidth / screenAspect) / Config.background.zoom
|
||||
property real cropWidth: (imageAspect > screenAspect ? paintedHeight * screenAspect : paintedWidth) / Config.background.zoom
|
||||
property real imageAspect: Math.max(1, paintedWidth) / Math.max(1, paintedHeight)
|
||||
property real paintedHeight: img.paintedHeight > 0 ? img.paintedHeight : img.height
|
||||
property real paintedWidth: img.paintedWidth > 0 ? img.paintedWidth : img.width
|
||||
property real paintedX: (img.width - paintedWidth) / 2
|
||||
property real paintedY: (img.height - paintedHeight) / 2
|
||||
property real screenAspect: Quickshell.screens.length > 0 ? (Quickshell.screens[0].width / Math.max(1, Quickshell.screens[0].height)) : 16 / 9
|
||||
|
||||
border.color: DynamicColors.palette.m3primary
|
||||
border.width: 2
|
||||
height: baseHeight / zoom
|
||||
opacity: 1
|
||||
width: baseWidth / zoom
|
||||
color: Qt.alpha(DynamicColors.palette.m3primaryContainer, 0.3)
|
||||
height: cropHeight
|
||||
width: cropWidth
|
||||
x: paintedX + (paintedWidth - width) * Config.background.alignX
|
||||
y: paintedY + (paintedHeight - height) * Config.background.alignY
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
DragHandler {
|
||||
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()
|
||||
onHeightChanged: clampToBounds()
|
||||
onWidthChanged: clampToBounds()
|
||||
PinchHandler {
|
||||
maximumScale: 5.0
|
||||
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 {
|
||||
id: mouse
|
||||
|
||||
function updateCrop(mouseX, mouseY) {
|
||||
let nx = mouseX - cropRect.width * 0.5;
|
||||
let ny = mouseY - cropRect.height * 0.5;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingSpinBox {
|
||||
max: 5.0
|
||||
min: 1.0
|
||||
name: "Zoom"
|
||||
object: Config.background
|
||||
setting: "zoom"
|
||||
step: 0.1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,26 @@ GridView {
|
||||
delegate: Item {
|
||||
required property int index
|
||||
readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent
|
||||
readonly property real itemMargin: Appearance.spacing.normal
|
||||
readonly property real itemRadius: Appearance.rounding.small
|
||||
readonly property real itemMargin: Appearance.spacing.normal / 2
|
||||
readonly property real itemRadius: Appearance.rounding.normal
|
||||
required property var modelData
|
||||
|
||||
height: root.cellHeight
|
||||
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 {
|
||||
id: image
|
||||
|
||||
@@ -40,6 +53,8 @@ GridView {
|
||||
anchors.topMargin: itemMargin
|
||||
antialiasing: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
radius: itemRadius
|
||||
|
||||
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 {
|
||||
anchors.bottomMargin: itemMargin
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: itemMargin
|
||||
anchors.rightMargin: itemMargin
|
||||
anchors.topMargin: itemMargin
|
||||
antialiasing: true
|
||||
border.color: DynamicColors.palette.m3primary
|
||||
border.width: isCurrent ? 2 : 0
|
||||
color: "transparent"
|
||||
radius: itemRadius + 2
|
||||
radius: itemRadius - border.width
|
||||
smooth: true
|
||||
|
||||
Behavior on border.width {
|
||||
@@ -111,30 +142,5 @@ GridView {
|
||||
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
@@ -6,7 +6,7 @@ import QtQuick.Controls
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import "../../scripts/fuzzysort.js" as Fuzzy
|
||||
import "../../scripts/SettingsIndex.mjs" as SettingsIndex
|
||||
import "./SettingsIndex.mjs" as SettingsIndex
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -53,10 +53,11 @@ Item {
|
||||
|
||||
Shortcut {
|
||||
sequence: "/"
|
||||
|
||||
onActivated: searchField.forceActiveFocus()
|
||||
}
|
||||
|
||||
Component.onCompleted: console.log(root.height)
|
||||
|
||||
ListModel {
|
||||
id: resultsModel
|
||||
}
|
||||
|
||||
@@ -7,24 +7,45 @@ import qs.Helpers
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real offsetScale: shouldBeActive ? 0 : 1
|
||||
required property var panels
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeActive: visibilities.settings
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.topMargin: (-implicitHeight - 5) * offsetScale
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitHeight: 0
|
||||
implicitWidth: content.implicitWidth
|
||||
opacity: 1 - offsetScale
|
||||
visible: offsetScale < 1
|
||||
visible: height > 0
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.visibilities.settings
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight
|
||||
}
|
||||
}
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
Behavior on offsetScale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
CustomClippingRect {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -14,8 +14,8 @@ Scope {
|
||||
description: "Toggle launcher"
|
||||
name: "toggle-launcher"
|
||||
|
||||
onPressed: {
|
||||
root.launcherInterrupted = false;
|
||||
onPressed: root.launcherInterrupted = false
|
||||
onReleased: {
|
||||
if (!root.launcherInterrupted && !root.hasFullscreen) {
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.launcher = !visibilities.launcher;
|
||||
|
||||
@@ -82,14 +82,12 @@ StackView {
|
||||
CustomRect {
|
||||
id: item
|
||||
|
||||
required property int index
|
||||
required property QsMenuEntry modelData
|
||||
|
||||
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
||||
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
||||
implicitWidth: root.biggestWidth
|
||||
radius: Appearance.rounding.full
|
||||
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
|
||||
radius: Appearance.rounding.smallest / 2
|
||||
|
||||
Loader {
|
||||
id: children
|
||||
@@ -203,18 +201,18 @@ StackView {
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: 30
|
||||
implicitHeight: back.implicitHeight + 2 / 2
|
||||
implicitWidth: back.implicitWidth
|
||||
|
||||
Item {
|
||||
anchors.bottom: parent.bottom
|
||||
implicitHeight: 30
|
||||
implicitWidth: root.biggestWidth
|
||||
implicitHeight: back.implicitHeight
|
||||
implicitWidth: back.implicitWidth + 10
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
radius: Appearance.rounding.smallest / 2
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
|
||||
@@ -31,7 +31,7 @@ Item {
|
||||
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
|
||||
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
|
||||
// color: "transparent"
|
||||
radius: (20 - Appearance.padding.small) * Appearance.rounding.scale
|
||||
radius: 6
|
||||
|
||||
CustomRect {
|
||||
id: indicator
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import QtQuick.Layouts
|
||||
import QtQuick
|
||||
import QtQuick.VectorImage
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.Modules
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,6 @@ CustomClippingRect {
|
||||
anchors.centerIn: parent
|
||||
contentHeight: childrenRect.height
|
||||
contentWidth: 600
|
||||
displayMarginBeginning: root.itemHeight
|
||||
displayMarginEnd: root.itemHeight
|
||||
implicitHeight: Math.min(contentHeight, (root.itemHeight + spacing) * 5 - spacing)
|
||||
implicitWidth: contentWidth
|
||||
spacing: Appearance.spacing.normal
|
||||
@@ -67,11 +65,10 @@ CustomClippingRect {
|
||||
required property var modelData
|
||||
readonly property list<string> sections: modelData.update.split(" ")
|
||||
|
||||
// anchors.left: parent.left
|
||||
// anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: root.itemHeight
|
||||
implicitWidth: 600
|
||||
radius: Appearance.rounding.small - Appearance.padding.small
|
||||
|
||||
RowLayout {
|
||||
|
||||
@@ -14,7 +14,7 @@ CustomRect {
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: contentRow.implicitWidth + Appearance.spacing.small * 2
|
||||
radius: Appearance.rounding.full
|
||||
radius: height / 2
|
||||
|
||||
RowLayout {
|
||||
id: contentRow
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
@@ -10,76 +8,79 @@ import qs.Config
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
property Image current: one
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
property int displayH
|
||||
property int displayW
|
||||
property real resScale
|
||||
property real zoom: 1.0
|
||||
property real imageRatio: Math.max(1, sourceSize.width) / Math.max(1, sourceSize.height)
|
||||
property bool isValid: sourceSize.width > 0 && sourceSize.height > 0 && root.width > 0 && root.height > 0
|
||||
property real windowRatio: root.width / Math.max(1, root.height)
|
||||
|
||||
function update(): void {
|
||||
if (path === root.source) {
|
||||
root.current = this;
|
||||
} else {
|
||||
path = root.source;
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: undefined
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
height: implicitHeight * zoom / resScale
|
||||
opacity: 1
|
||||
retainWhileLoading: true
|
||||
source: root.source
|
||||
sourceSize.width: root.screen.width * resScale
|
||||
width: implicitWidth * zoom / resScale
|
||||
height: isValid ? (imageRatio > windowRatio ? root.height : root.width / imageRatio) * Config.background.zoom : root.height
|
||||
opacity: 0
|
||||
scale: Wallpapers.showPreview ? 1 : 0.8
|
||||
width: isValid ? (imageRatio > windowRatio ? root.height * imageRatio : root.width) * Config.background.zoom : root.width
|
||||
x: isValid ? (root.width - width) * Config.background.alignX : 0
|
||||
y: isValid ? (root.height - height) * Config.background.alignY : 0
|
||||
|
||||
Behavior on height {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
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 (img.status == Image.Ready) {
|
||||
root.refreshData();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onAdapterUpdated(): void {
|
||||
root.refreshData();
|
||||
}
|
||||
|
||||
function onLoaded(): void {
|
||||
root.refreshData();
|
||||
}
|
||||
|
||||
target: Wallpapers.monitorCrops
|
||||
if (status === Image.Ready) {
|
||||
root.current = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ Loader {
|
||||
}
|
||||
|
||||
WallBackground {
|
||||
screen: root.screen
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
||||
@@ -79,8 +79,7 @@ Item {
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
const ws = button.modelData.name;
|
||||
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
|
||||
Hyprland.dispatch(`workspace ${button.modelData.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ qml_module(ZShell-blobs
|
||||
|
||||
qt_add_shaders(ZShell-blobs "blob_shaders"
|
||||
BATCHABLE OPTIMIZED NOHLSL NOMSL
|
||||
GLSL "300es,330"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
shaders/blob.frag
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
static_assert(sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
|
||||
"BlobMaterial packs excludeMask into a float slot via memcpy");
|
||||
|
||||
QSGMaterialType* BlobMaterial::type() const {
|
||||
static QSGMaterialType s_type;
|
||||
return &s_type;
|
||||
@@ -85,11 +82,8 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const auto& r = mat->m_rects[i];
|
||||
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 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 };
|
||||
memcpy(buf->data() + base, d0, 16);
|
||||
memcpy(buf->data() + base + 16, d1, 16);
|
||||
|
||||
@@ -14,9 +14,6 @@ struct BlobRectData {
|
||||
float screenHalfX = 0, screenHalfY = 0;
|
||||
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
|
||||
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 {
|
||||
|
||||
@@ -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
|
||||
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
||||
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
||||
// Accumulate size delta across multiple frames so incremental size
|
||||
// changes that are each below the threshold still trigger a dirty
|
||||
// mark once their accumulated delta exceeds it.
|
||||
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) {
|
||||
const auto dw = std::abs(newGeometry.width() - oldGeometry.width());
|
||||
const auto dh = std::abs(newGeometry.height() - oldGeometry.height());
|
||||
if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f || dw > 0.5 || dh > 0.5) {
|
||||
m_pendingDx = 0;
|
||||
m_pendingDy = 0;
|
||||
m_pendingDw = 0;
|
||||
m_pendingDh = 0;
|
||||
m_group->markShapeDirty(this);
|
||||
}
|
||||
}
|
||||
@@ -155,10 +149,6 @@ void BlobShape::updatePolish() {
|
||||
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
||||
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()) {
|
||||
if (other->isInvertedRect())
|
||||
continue;
|
||||
@@ -220,29 +210,12 @@ void BlobShape::updatePolish() {
|
||||
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
|
||||
|
||||
m_cachedRects.append(r);
|
||||
rectShapes.append(other);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvertedRect())
|
||||
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
|
||||
m_cachedHasInverted = false;
|
||||
m_cachedInvertedRadius = 0;
|
||||
@@ -297,7 +270,6 @@ void BlobShape::updatePolish() {
|
||||
const auto rectCount = m_cachedRects.size();
|
||||
for (qsizetype i = 0; i < rectCount; ++i) {
|
||||
auto& ri = m_cachedRects[i];
|
||||
const int riExcludeMask = ri.excludeMask;
|
||||
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;
|
||||
@@ -308,8 +280,6 @@ void BlobShape::updatePolish() {
|
||||
for (qsizetype j = 0; j < rectCount; ++j) {
|
||||
if (j == i)
|
||||
continue;
|
||||
if (riExcludeMask & (1 << j))
|
||||
continue;
|
||||
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)));
|
||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||
|
||||
@@ -85,8 +85,6 @@ QVector<BlobRectData> m_cachedRects;
|
||||
int m_cachedMyIndex = -2;
|
||||
float m_pendingDx = 0;
|
||||
float m_pendingDy = 0;
|
||||
float m_pendingDw = 0;
|
||||
float m_pendingDh = 0;
|
||||
bool m_cachedHasInverted = false;
|
||||
float m_cachedInvertedRadius = 0;
|
||||
float m_cachedInvertedOuter[4] = {};
|
||||
|
||||
@@ -63,17 +63,13 @@ float smaxSharpA(float a, float b, float k) {
|
||||
void main() {
|
||||
vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH);
|
||||
|
||||
// Phase 1: compute per-rect SDF, track owner. We can't smin yet because
|
||||
// excluded pairs need to skip the smooth blend, which requires pairwise pass
|
||||
// below.
|
||||
float dArr[16];
|
||||
float mergedSdf = 1e10;
|
||||
int owner = -2;
|
||||
float minDist = 1e10;
|
||||
|
||||
for (int i = 0; i < rectCount; i++) {
|
||||
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
|
||||
vec4 props =
|
||||
rectData[i * 5 + 1]; // excludeMask(int bits), offsetX, offsetY, minEig
|
||||
vec4 props = rectData[i * 5 + 1]; // radius, offsetX, offsetY, minEig
|
||||
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
|
||||
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
|
||||
vec4 radii =
|
||||
@@ -85,10 +81,8 @@ void main() {
|
||||
// AABB early-out: skip rects far from this pixel
|
||||
vec2 extent = sh.xy + vec2(smoothFactor * 1.5);
|
||||
if (abs(pixel.x - center.x) > extent.x ||
|
||||
abs(pixel.y - center.y) > extent.y) {
|
||||
dArr[i] = 1e10;
|
||||
abs(pixel.y - center.y) > extent.y)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply pre-computed inverse deformation to the evaluation point
|
||||
mat2 invDeform = mat2(invDm.xy, invDm.zw);
|
||||
@@ -144,38 +138,13 @@ void main() {
|
||||
d *= scale;
|
||||
}
|
||||
|
||||
dArr[i] = d;
|
||||
mergedSdf = smin(mergedSdf, d, smoothFactor);
|
||||
if (d < smoothFactor && d < minDist) {
|
||||
minDist = d;
|
||||
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) {
|
||||
float dOuter = sdBox(pixel, invertedOuter.xy, invertedOuter.zw) - 1.0;
|
||||
float dInner =
|
||||
|
||||
@@ -1,170 +1,12 @@
|
||||
#include "hyprextras.hpp"
|
||||
#include "hyprdevices.hpp"
|
||||
|
||||
#include <qdir.h>
|
||||
#include <qjsonarray.h>
|
||||
#include <qlocalsocket.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qmetatype.h>
|
||||
#include <qregularexpression.h>
|
||||
#include <qvariant.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(lcHypr, "ZShell.internal.hypr", QtInfoMsg)
|
||||
|
||||
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)
|
||||
: QObject(parent)
|
||||
, m_requestSocket("")
|
||||
@@ -174,22 +16,24 @@ HyprExtras::HyprExtras(QObject* parent)
|
||||
, m_devices(new HyprDevices(this)) {
|
||||
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
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;
|
||||
}
|
||||
|
||||
auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
|
||||
if (!QDir(hyprDir).exists()) {
|
||||
hyprDir = QStringLiteral("/tmp/hypr/") + his;
|
||||
hyprDir = "/tmp/hypr/" + his;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
m_requestSocket = hyprDir + QStringLiteral("/.socket.sock");
|
||||
m_eventSocket = hyprDir + QStringLiteral("/.socket2.sock");
|
||||
m_requestSocket = hyprDir + "/.socket.sock";
|
||||
m_eventSocket = hyprDir + "/.socket2.sock";
|
||||
|
||||
refreshOptions();
|
||||
refreshDevices();
|
||||
@@ -218,7 +62,7 @@ void HyprExtras::message(const QString& message) {
|
||||
|
||||
makeRequest(message, [](bool success, const QByteArray& res) {
|
||||
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;
|
||||
}
|
||||
|
||||
makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
|
||||
[](bool success, const QByteArray& res) {
|
||||
makeRequest("[[BATCH]]" + messages.join(";"), [](bool success, const QByteArray& res) {
|
||||
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;
|
||||
}
|
||||
|
||||
QStringList calls;
|
||||
calls.reserve(options.size());
|
||||
|
||||
QString request = "[[BATCH]]";
|
||||
for (auto it = options.constBegin(); it != options.constEnd(); ++it) {
|
||||
const auto call = buildHlConfigCall(it.key(), it.value());
|
||||
if (!call.isEmpty()) {
|
||||
calls << call;
|
||||
}
|
||||
request += QString("keyword %1 %2;").arg(it.key(), it.value().toString());
|
||||
}
|
||||
|
||||
if (calls.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
makeRequest(QStringLiteral("eval ") + calls.join(QLatin1String("; ")), [this](bool success, const QByteArray& res) {
|
||||
makeRequest(request, [this](bool success, const QByteArray& res) {
|
||||
if (success) {
|
||||
refreshOptions();
|
||||
} 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 = makeRequestJson(QStringLiteral("descriptions"), [this](bool success, const QJsonDocument& response) {
|
||||
m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) {
|
||||
m_optionsRefresh.reset();
|
||||
if (!success) {
|
||||
return;
|
||||
@@ -280,9 +114,8 @@ void HyprExtras::refreshOptions() {
|
||||
|
||||
for (const auto& o : std::as_const(options)) {
|
||||
const auto obj = o.toObject();
|
||||
const auto key = obj.value(QStringLiteral("value")).toString();
|
||||
const auto value = obj.value(QStringLiteral("data")).toObject().value(QStringLiteral("current")).toVariant();
|
||||
|
||||
const auto key = obj.value("value").toString();
|
||||
const auto value = obj.value("data").toObject().value("current").toVariant();
|
||||
if (m_options.value(key) != value) {
|
||||
dirty = true;
|
||||
m_options.insert(key, value);
|
||||
@@ -300,7 +133,7 @@ void HyprExtras::refreshDevices() {
|
||||
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();
|
||||
if (success) {
|
||||
m_devices->updateLastIpcObject(response.object());
|
||||
@@ -310,15 +143,15 @@ void HyprExtras::refreshDevices() {
|
||||
|
||||
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
|
||||
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 {
|
||||
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error;
|
||||
qWarning() << "HyprExtras::socketError: Hyprland event socket error:" << error;
|
||||
}
|
||||
}
|
||||
|
||||
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
|
||||
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;
|
||||
@@ -330,23 +163,23 @@ void HyprExtras::readEvent() {
|
||||
if (rawEvent.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
rawEvent.truncate(rawEvent.length() - 1);
|
||||
rawEvent.truncate(rawEvent.length() - 1); // Remove trailing \n
|
||||
const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
|
||||
handleEvent(QString::fromUtf8(event));
|
||||
}
|
||||
}
|
||||
|
||||
void HyprExtras::handleEvent(const QString& event) {
|
||||
if (event == QStringLiteral("configreloaded")) {
|
||||
if (event == "configreloaded") {
|
||||
refreshOptions();
|
||||
} else if (event == QStringLiteral("activelayout")) {
|
||||
} else if (event == "activelayout") {
|
||||
refreshDevices();
|
||||
}
|
||||
}
|
||||
|
||||
HyprExtras::SocketPtr HyprExtras::makeRequestJson(
|
||||
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));
|
||||
});
|
||||
}
|
||||
@@ -371,7 +204,7 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
|
||||
});
|
||||
|
||||
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, {});
|
||||
socket->close();
|
||||
});
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "hyprdevices.hpp"
|
||||
#include <qlocalsocket.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
#include <qsharedpointer.h>
|
||||
#include <qvariant.h>
|
||||
|
||||
namespace ZShell::internal::hypr {
|
||||
|
||||
class HyprDevices;
|
||||
|
||||
class HyprExtras : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
Q_MOC_INCLUDE("hyprdevices.hpp")
|
||||
|
||||
Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged)
|
||||
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
|
||||
|
||||
@@ -74,8 +74,6 @@ Defaults from `CMakeLists.txt`:
|
||||
|
||||
### 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:
|
||||
|
||||
- `packages.<system>.zshell`
|
||||
|
||||
@@ -9,6 +9,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"typer",
|
||||
"pillow",
|
||||
"jinja2",
|
||||
"materialyoucolor"
|
||||
]
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
import typer
|
||||
from zshell.subcommands import shell, scheme, screenshot, wallpaper
|
||||
from zshell.subcommands import shell, scheme, screenshot, wallpaper, preset
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
@@ -8,7 +8,7 @@ app.add_typer(shell.app, name="shell")
|
||||
app.add_typer(scheme.app, name="scheme")
|
||||
app.add_typer(screenshot.app, name="screenshot")
|
||||
app.add_typer(wallpaper.app, name="wallpaper")
|
||||
# app.add_typer(preset.app, name="preset")
|
||||
app.add_typer(preset.app, name="preset")
|
||||
|
||||
|
||||
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",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user