Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33746fca04 | |||
| 853b683962 | |||
| b1bfcb3ed0 | |||
| 68662120ba | |||
| b8af60008d | |||
| b8524ff621 | |||
| ffde4063a0 | |||
| 96bf5f3365 | |||
| 053efb4aaf | |||
| c88aef2164 | |||
| 01b54ec5e1 | |||
| 7276ee28dc | |||
| a14ebe2016 | |||
| d3f6765819 | |||
| a3d0ee18cb | |||
| c9d6b95ca5 | |||
| 794a26a3fe | |||
| ca3a288eab | |||
| 902863e5ba | |||
| dd49198cf7 | |||
| ceca949535 | |||
| 24d5584b98 | |||
| 62ec1b9f33 | |||
| 9c6a1ce1a4 | |||
| b6ad180b6a | |||
| b20767c702 | |||
| 362b7bb8c2 | |||
| 405825518a | |||
| db7a822caf | |||
| 3bd9444e2f | |||
| d0e696c681 | |||
| 550630feaa | |||
| 8c22855dd8 | |||
| 015ee61885 | |||
| 8fba953f52 | |||
| 3d2fc0a3b1 | |||
| c060be79e8 | |||
| cb1df5078b | |||
| 5eb32fc30c | |||
| 0a84c822d5 | |||
| f89236f51e | |||
| 4b1316e887 | |||
| 63f4694322 | |||
| 51a8f1d5e1 | |||
| d57010a501 | |||
| d506d5ad27 | |||
| f6119072f7 | |||
| 00063309cd | |||
| c30891de83 | |||
| 55e9c0f267 | |||
| 3424df53e2 | |||
| 583c50f994 | |||
| ca53152630 | |||
| 0cd7df243b | |||
| 6a8ad4dbf2 | |||
| f57577fefd | |||
| 3a05cd339d | |||
| c67a498f8d | |||
| e7e772ebc6 | |||
| fb2c9c6a21 | |||
| 383671344f | |||
| beb1d96750 | |||
| 6f8af9028b | |||
| e874c19ee2 | |||
| af04e5d227 | |||
| 4ab19a8e37 | |||
| 783d05f815 | |||
| 17fb9c0fef | |||
| 39cbfa2c93 | |||
| 17fef78672 | |||
| c120dcae41 | |||
| 64e65ca9df | |||
| 22a7993c07 | |||
| 24526ca2d1 | |||
| c5ee27bf62 |
@@ -0,0 +1,42 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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 .
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.venv/
|
||||||
|
scripts/fzf.js
|
||||||
|
scripts/fuzzysort.js
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"printWidth": 80,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"bracketSameLine": false,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"proseWrap": "preserve"
|
||||||
|
}
|
||||||
@@ -41,12 +41,11 @@ CustomRect {
|
|||||||
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
|
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
|
||||||
implicitHeight: label.implicitHeight + padding * 2
|
implicitHeight: label.implicitHeight + padding * 2
|
||||||
implicitWidth: implicitHeight
|
implicitWidth: implicitHeight
|
||||||
radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
|
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale
|
||||||
|
|
||||||
Behavior on radius {
|
Behavior on radius {
|
||||||
Anim {
|
Anim {
|
||||||
id: radiusAnim
|
id: radiusAnim
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,8 @@ JsonObject {
|
|||||||
property real alignX: 0.5
|
property real alignX: 0.5
|
||||||
property real alignY: 0.5
|
property real alignY: 0.5
|
||||||
property real zoom: 1.0
|
property real zoom: 1.0
|
||||||
|
property real sourceClipX: 0
|
||||||
|
property real sourceClipY: 0
|
||||||
|
property real sourceClipW: 0
|
||||||
|
property real sourceClipH: 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ JsonObject {
|
|||||||
property Popouts popouts: Popouts {
|
property Popouts popouts: Popouts {
|
||||||
}
|
}
|
||||||
property int rounding: 8
|
property int rounding: 8
|
||||||
|
property int smoothing: 32
|
||||||
|
|
||||||
component Popouts: JsonObject {
|
component Popouts: JsonObject {
|
||||||
property bool activeWindow: true
|
property bool activeWindow: true
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ Singleton {
|
|||||||
wallFadeDuration: background.wallFadeDuration,
|
wallFadeDuration: background.wallFadeDuration,
|
||||||
enabled: background.enabled,
|
enabled: background.enabled,
|
||||||
alignX: background.alignX,
|
alignX: background.alignX,
|
||||||
|
sourceClipX: background.sourceClipX,
|
||||||
|
sourceClipY: background.sourceClipY,
|
||||||
|
sourceClipW: background.sourceClipW,
|
||||||
|
sourceClipH: background.sourceClipH,
|
||||||
alignY: background.alignY,
|
alignY: background.alignY,
|
||||||
zoom: background.zoom
|
zoom: background.zoom
|
||||||
};
|
};
|
||||||
@@ -94,6 +98,7 @@ Singleton {
|
|||||||
hideWhenNotif: barConfig.hideWhenNotif,
|
hideWhenNotif: barConfig.hideWhenNotif,
|
||||||
rounding: barConfig.rounding,
|
rounding: barConfig.rounding,
|
||||||
border: barConfig.border,
|
border: barConfig.border,
|
||||||
|
smoothing: barConfig.smoothing,
|
||||||
height: barConfig.height,
|
height: barConfig.height,
|
||||||
popouts: {
|
popouts: {
|
||||||
tray: barConfig.popouts.tray,
|
tray: barConfig.popouts.tray,
|
||||||
@@ -236,6 +241,7 @@ Singleton {
|
|||||||
return {
|
return {
|
||||||
recolorLogo: lock.recolorLogo,
|
recolorLogo: lock.recolorLogo,
|
||||||
enableFprint: lock.enableFprint,
|
enableFprint: lock.enableFprint,
|
||||||
|
showNotifContent: lock.showNotifContent,
|
||||||
maxFprintTries: lock.maxFprintTries,
|
maxFprintTries: lock.maxFprintTries,
|
||||||
blurAmount: lock.blurAmount,
|
blurAmount: lock.blurAmount,
|
||||||
sizes: {
|
sizes: {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Hyprland
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import ZShell
|
import ZShell
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
@@ -79,10 +80,32 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reloadHyprRules(): void {
|
function reloadHyprRules(): void {
|
||||||
const barStr = "keyword layerrule %1 %2, match:namespace ZShell-Bar";
|
const blur = transparency.enabled ? 1 : 0;
|
||||||
const authStr = "keyword layerrule %1 %2, match:namespace ZShell-Auth";
|
const alpha = transparency.base - 0.03;
|
||||||
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)]);
|
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}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMode(mode: string): void {
|
function setMode(mode: string): void {
|
||||||
@@ -93,6 +116,14 @@ Singleton {
|
|||||||
|
|
||||||
Component.onCompleted: debounceTimer.triggered()
|
Component.onCompleted: debounceTimer.triggered()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onUsingLuaChanged(): void {
|
||||||
|
root.reloadHyprRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: Hyprland
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onConfigReloaded(): void {
|
function onConfigReloaded(): void {
|
||||||
root.reloadHyprRules();
|
root.reloadHyprRules();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ JsonObject {
|
|||||||
property bool enableFprint: true
|
property bool enableFprint: true
|
||||||
property int maxFprintTries: 3
|
property int maxFprintTries: 3
|
||||||
property bool recolorLogo: false
|
property bool recolorLogo: false
|
||||||
|
property bool showNotifContent: false
|
||||||
property Sizes sizes: Sizes {
|
property Sizes sizes: Sizes {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-174
@@ -3,184 +3,33 @@ import QtQuick
|
|||||||
Canvas {
|
Canvas {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property rect dirtyRect: Qt.rect(0, 0, 0, 0)
|
|
||||||
property bool frameQueued: false
|
|
||||||
property bool fullRepaintPending: true
|
|
||||||
property point lastPoint: Qt.point(0, 0)
|
|
||||||
property real minPointDistance: 2.0
|
|
||||||
property color penColor: "white"
|
property color penColor: "white"
|
||||||
property real penWidth: 4
|
property real penWidth: 4
|
||||||
property var pendingSegments: []
|
property var points: []
|
||||||
property bool strokeActive: false
|
|
||||||
property var strokes: []
|
|
||||||
|
|
||||||
function appendPoint(x, y) {
|
function clear(): void {
|
||||||
if (!strokeActive || strokes.length === 0)
|
var ctx = getContext('2d');
|
||||||
|
root.points = [];
|
||||||
|
ctx.reset();
|
||||||
|
root.requestPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
if (points.length < 2)
|
||||||
return;
|
return;
|
||||||
const dx = x - lastPoint.x;
|
var ctx = root.getContext('2d');
|
||||||
const dy = y - lastPoint.y;
|
ctx.save();
|
||||||
|
ctx.lineWidth = root.penWidth;
|
||||||
if ((dx * dx + dy * dy) < (minPointDistance * minPointDistance))
|
ctx.strokeStyle = root.penColor;
|
||||||
return;
|
|
||||||
const x1 = lastPoint.x;
|
|
||||||
const y1 = lastPoint.y;
|
|
||||||
const x2 = x;
|
|
||||||
const y2 = y;
|
|
||||||
|
|
||||||
strokes[strokes.length - 1].push(Qt.point(x2, y2));
|
|
||||||
|
|
||||||
pendingSegments.push({
|
|
||||||
dot: false,
|
|
||||||
x1: x1,
|
|
||||||
y1: y1,
|
|
||||||
x2: x2,
|
|
||||||
y2: y2
|
|
||||||
});
|
|
||||||
|
|
||||||
lastPoint = Qt.point(x2, y2);
|
|
||||||
queueDirty(segmentDirtyRect(x1, y1, x2, y2));
|
|
||||||
}
|
|
||||||
|
|
||||||
function beginStroke(x, y) {
|
|
||||||
const p = Qt.point(x, y);
|
|
||||||
strokes.push([p]);
|
|
||||||
lastPoint = p;
|
|
||||||
strokeActive = true;
|
|
||||||
|
|
||||||
pendingSegments.push({
|
|
||||||
dot: true,
|
|
||||||
x: x,
|
|
||||||
y: y
|
|
||||||
});
|
|
||||||
|
|
||||||
queueDirty(pointDirtyRect(x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
strokes = [];
|
|
||||||
pendingSegments = [];
|
|
||||||
dirtyRect = Qt.rect(0, 0, 0, 0);
|
|
||||||
fullRepaintPending = true;
|
|
||||||
markDirty(Qt.rect(0, 0, width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawDot(ctx, x, y) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x, y, penWidth / 2, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSegment(ctx, x1, y1, x2, y2) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x1, y1);
|
|
||||||
ctx.lineTo(x2, y2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function endStroke() {
|
|
||||||
strokeActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pointDirtyRect(x, y) {
|
|
||||||
const pad = penWidth + 2;
|
|
||||||
return Qt.rect(x - pad, y - pad, pad * 2, pad * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function queueDirty(r) {
|
|
||||||
dirtyRect = unionRects(dirtyRect, r);
|
|
||||||
|
|
||||||
if (frameQueued)
|
|
||||||
return;
|
|
||||||
frameQueued = true;
|
|
||||||
|
|
||||||
requestAnimationFrame(function () {
|
|
||||||
frameQueued = false;
|
|
||||||
|
|
||||||
if (dirtyRect.width > 0 && dirtyRect.height > 0) {
|
|
||||||
markDirty(dirtyRect);
|
|
||||||
dirtyRect = Qt.rect(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function replayAll(ctx) {
|
|
||||||
ctx.clearRect(0, 0, width, height);
|
|
||||||
|
|
||||||
for (const stroke of strokes) {
|
|
||||||
if (!stroke || stroke.length === 0)
|
|
||||||
continue;
|
|
||||||
if (stroke.length === 1) {
|
|
||||||
const p = stroke[0];
|
|
||||||
drawDot(ctx, p.x, p.y);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(stroke[0].x, stroke[0].y);
|
|
||||||
for (let i = 1; i < stroke.length; ++i)
|
|
||||||
ctx.lineTo(stroke[i].x, stroke[i].y);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestFullRepaint() {
|
|
||||||
fullRepaintPending = true;
|
|
||||||
markDirty(Qt.rect(0, 0, width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
function segmentDirtyRect(x1, y1, x2, y2) {
|
|
||||||
const pad = penWidth + 2;
|
|
||||||
const left = Math.min(x1, x2) - pad;
|
|
||||||
const top = Math.min(y1, y2) - pad;
|
|
||||||
const right = Math.max(x1, x2) + pad;
|
|
||||||
const bottom = Math.max(y1, y2) + pad;
|
|
||||||
return Qt.rect(left, top, right - left, bottom - top);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unionRects(a, b) {
|
|
||||||
if (a.width <= 0 || a.height <= 0)
|
|
||||||
return b;
|
|
||||||
if (b.width <= 0 || b.height <= 0)
|
|
||||||
return a;
|
|
||||||
|
|
||||||
const left = Math.min(a.x, b.x);
|
|
||||||
const top = Math.min(a.y, b.y);
|
|
||||||
const right = Math.max(a.x + a.width, b.x + b.width);
|
|
||||||
const bottom = Math.max(a.y + a.height, b.y + b.height);
|
|
||||||
|
|
||||||
return Qt.rect(left, top, right - left, bottom - top);
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
contextType: "2d"
|
|
||||||
renderStrategy: Canvas.Threaded
|
|
||||||
renderTarget: Canvas.Image
|
|
||||||
|
|
||||||
onHeightChanged: requestFullRepaint()
|
|
||||||
onPaint: region => {
|
|
||||||
const ctx = getContext("2d");
|
|
||||||
|
|
||||||
ctx.lineCap = "round";
|
ctx.lineCap = "round";
|
||||||
ctx.lineJoin = "round";
|
ctx.beginPath();
|
||||||
ctx.lineWidth = penWidth;
|
ctx.moveTo(points[0].x, points[0].y);
|
||||||
ctx.strokeStyle = penColor;
|
for (var i = 1; i < points.length; i++)
|
||||||
ctx.fillStyle = penColor;
|
ctx.lineTo(points[i].x, points[i].y);
|
||||||
|
ctx.stroke();
|
||||||
if (fullRepaintPending) {
|
points = points.slice(points.length - 2);
|
||||||
fullRepaintPending = false;
|
ctx.restore();
|
||||||
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,8 +30,10 @@ CustomMouseArea {
|
|||||||
const x = event.x;
|
const x = event.x;
|
||||||
const y = event.y;
|
const y = event.y;
|
||||||
|
|
||||||
if (event.buttons & Qt.LeftButton)
|
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
|
||||||
root.drawing.appendPoint(x, y);
|
root.drawing.points.push(Qt.point(x, y));
|
||||||
|
root.drawing.requestPaint();
|
||||||
|
}
|
||||||
|
|
||||||
if (root.inLeftPanel(root.popout, x, y)) {
|
if (root.inLeftPanel(root.popout, x, y)) {
|
||||||
root.z = -2;
|
root.z = -2;
|
||||||
@@ -44,7 +46,8 @@ CustomMouseArea {
|
|||||||
|
|
||||||
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
|
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
|
||||||
root.panels.drawing.expanded = false;
|
root.panels.drawing.expanded = false;
|
||||||
root.drawing.beginStroke(x, y);
|
root.drawing.points.push(Qt.point(x, y));
|
||||||
|
root.drawing.requestPaint();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +55,6 @@ CustomMouseArea {
|
|||||||
root.drawing.clear();
|
root.drawing.clear();
|
||||||
}
|
}
|
||||||
onReleased: {
|
onReleased: {
|
||||||
if (root.visibilities.isDrawing)
|
root.drawing.points = [];
|
||||||
root.drawing.endStroke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,37 @@ Scope {
|
|||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
|
|
||||||
ExclusionZone {
|
ExclusionZone {
|
||||||
|
id: top
|
||||||
|
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
exclusiveZone: root.bar.exclusiveZone
|
exclusiveZone: root.bar.exclusiveZone
|
||||||
}
|
}
|
||||||
|
|
||||||
ExclusionZone {
|
ExclusionZone {
|
||||||
|
id: left
|
||||||
|
|
||||||
anchors.left: true
|
anchors.left: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ExclusionZone {
|
ExclusionZone {
|
||||||
|
id: right
|
||||||
|
|
||||||
anchors.right: true
|
anchors.right: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ExclusionZone {
|
ExclusionZone {
|
||||||
|
id: bottom
|
||||||
|
|
||||||
anchors.bottom: true
|
anchors.bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 5000
|
||||||
|
running: true
|
||||||
|
|
||||||
|
onTriggered: console.log("top height:", top.exclusiveZone, "left width:", left.exclusiveZone, "right width:", right.exclusiveZone, "bottom height:", bottom.exclusiveZone)
|
||||||
|
}
|
||||||
|
|
||||||
component ExclusionZone: CustomWindow {
|
component ExclusionZone: CustomWindow {
|
||||||
exclusiveZone: Config.barConfig.border
|
exclusiveZone: Config.barConfig.border
|
||||||
implicitHeight: 1
|
implicitHeight: 1
|
||||||
|
|||||||
+18
-18
@@ -78,7 +78,7 @@ CustomMouseArea {
|
|||||||
const dragY = y - dragStart.y;
|
const dragY = y - dragStart.y;
|
||||||
|
|
||||||
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
|
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
|
||||||
root.input.z = 2;
|
// root.input.z = 2;
|
||||||
root.panels.drawing.expanded = false;
|
root.panels.drawing.expanded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,25 +96,25 @@ CustomMouseArea {
|
|||||||
if (dragY < -10)
|
if (dragY < -10)
|
||||||
visibilities.dock = true;
|
visibilities.dock = true;
|
||||||
|
|
||||||
if (panels.sidebar.width === 0) {
|
if (panels.sidebar.width === 0) {
|
||||||
const showOsd = inRightPanel(panels.osdWrapper, x, y);
|
const showOsd = inRightPanel(panels.osdWrapper, x, y);
|
||||||
|
|
||||||
if (showOsd) {
|
if (showOsd) {
|
||||||
osdShortcutActive = false;
|
osdShortcutActive = false;
|
||||||
root.panels.osd.hovered = true;
|
root.panels.osd.hovered = true;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const outOfSidebar = x < width - panels.sidebar.width;
|
|
||||||
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
|
|
||||||
|
|
||||||
if (!osdShortcutActive) {
|
|
||||||
visibilities.osd = showOsd;
|
|
||||||
root.panels.osd.hovered = showOsd;
|
|
||||||
} else if (showOsd) {
|
|
||||||
osdShortcutActive = false;
|
|
||||||
root.panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const outOfSidebar = x < width - panels.sidebar.width;
|
||||||
|
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
|
||||||
|
|
||||||
|
if (!osdShortcutActive) {
|
||||||
|
visibilities.osd = showOsd;
|
||||||
|
root.panels.osd.hovered = showOsd;
|
||||||
|
} else if (showOsd) {
|
||||||
|
osdShortcutActive = false;
|
||||||
|
root.panels.osd.hovered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Config.dock.enable && !Config.dock.hoverToReveal && !visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y))
|
if (Config.dock.enable && !Config.dock.hoverToReveal && !visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y))
|
||||||
visibilities.dock = true;
|
visibilities.dock = true;
|
||||||
|
|||||||
+92
-6
@@ -13,6 +13,7 @@ import qs.Modules.Resources as Resources
|
|||||||
import qs.Modules.Settings as Settings
|
import qs.Modules.Settings as Settings
|
||||||
import qs.Modules.Drawing as Drawing
|
import qs.Modules.Drawing as Drawing
|
||||||
import qs.Modules.Dock as Dock
|
import qs.Modules.Dock as Dock
|
||||||
|
import qs.Modules.SysTray.Popouts as SysPopouts
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -34,8 +35,10 @@ Item {
|
|||||||
readonly property alias resourcesWrapper: resourcesWrapper
|
readonly property alias resourcesWrapper: resourcesWrapper
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
readonly property alias settings: settings
|
readonly property alias settings: settings
|
||||||
|
readonly property alias settingsWrapper: settingsWrapper
|
||||||
readonly property alias sidebar: sidebar
|
readonly property alias sidebar: sidebar
|
||||||
readonly property alias toasts: toasts
|
readonly property alias toasts: toasts
|
||||||
|
readonly property alias traySubmenus: traySubmenus
|
||||||
readonly property alias utilities: utilities
|
readonly property alias utilities: utilities
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
@@ -92,6 +95,79 @@ Item {
|
|||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: traySubmenus
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: popouts.content.state.submenus
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
id: subMenuWrapper
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
property real targetX: 0
|
||||||
|
property real targetY: 0
|
||||||
|
|
||||||
|
function updatePosition() {
|
||||||
|
let sourceItem = modelData.sourceItem;
|
||||||
|
if (!sourceItem || !sourceItem.parent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let mapped = sourceItem.mapToItem(root, 0, -Appearance.padding.small);
|
||||||
|
|
||||||
|
let rightX = mapped.x + modelData.sourceWidth + Config.barConfig.border;
|
||||||
|
let leftX = mapped.x - implicitWidth - Config.barConfig.border;
|
||||||
|
|
||||||
|
if (rightX + implicitWidth > root.width) {
|
||||||
|
targetX = leftX;
|
||||||
|
} else {
|
||||||
|
targetX = rightX;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetY = mapped.y;
|
||||||
|
|
||||||
|
if (targetY + implicitHeight > root.height) {
|
||||||
|
targetY = root.height - implicitHeight;
|
||||||
|
}
|
||||||
|
if (targetY < 0)
|
||||||
|
targetY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: subMenuContent.implicitHeight + Appearance.padding.small * 2
|
||||||
|
implicitWidth: subMenuContent.implicitWidth + Appearance.padding.small * 2
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
x: targetX
|
||||||
|
y: targetY
|
||||||
|
|
||||||
|
Behavior on implicitHeight {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
updatePosition();
|
||||||
|
}
|
||||||
|
onImplicitHeightChanged: updatePosition()
|
||||||
|
onImplicitWidthChanged: updatePosition()
|
||||||
|
|
||||||
|
SysPopouts.SubMenu {
|
||||||
|
id: subMenuContent
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
handle: subMenuWrapper.modelData.handle
|
||||||
|
level: subMenuWrapper.index + 1
|
||||||
|
popouts: root.popouts.state
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Modules.ClipWrapper {
|
Modules.ClipWrapper {
|
||||||
id: popouts
|
id: popouts
|
||||||
|
|
||||||
@@ -176,15 +252,25 @@ Item {
|
|||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings.Wrapper {
|
Item {
|
||||||
id: settings
|
id: settingsWrapper
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
// anchors.centerIn: parent
|
clip: true
|
||||||
panels: root
|
implicitHeight: settings.implicitHeight * (1 - settings.offsetScale)
|
||||||
screen: root.screen
|
implicitWidth: settings.implicitWidth
|
||||||
visibilities: root.visibilities
|
|
||||||
|
Settings.Wrapper {
|
||||||
|
id: settings
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
// anchors.centerIn: parent
|
||||||
|
panels: root
|
||||||
|
screen: root.screen
|
||||||
|
visibilities: root.visibilities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dock.Wrapper {
|
Dock.Wrapper {
|
||||||
|
|||||||
+60
-19
@@ -64,7 +64,7 @@ Variants {
|
|||||||
|
|
||||||
height: win.height - bar.implicitHeight - Config.barConfig.border
|
height: win.height - bar.implicitHeight - Config.barConfig.border
|
||||||
intersection: Intersection.Xor
|
intersection: Intersection.Xor
|
||||||
regions: popoutRegions.instances
|
regions: [...popoutRegions.instances, ...subMenuRegions.instances]
|
||||||
width: win.width - Config.barConfig.border * 2
|
width: win.width - Config.barConfig.border * 2
|
||||||
x: Config.barConfig.border
|
x: Config.barConfig.border
|
||||||
y: bar.implicitHeight
|
y: bar.implicitHeight
|
||||||
@@ -93,6 +93,22 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
id: subMenuRegions
|
||||||
|
|
||||||
|
model: panels.traySubmenus.children
|
||||||
|
|
||||||
|
Region {
|
||||||
|
required property Item modelData
|
||||||
|
|
||||||
|
height: modelData.height
|
||||||
|
intersection: Intersection.Subtract
|
||||||
|
width: modelData.width
|
||||||
|
x: modelData.x + panels.traySubmenus.x + Config.barConfig.border
|
||||||
|
y: modelData.y + panels.traySubmenus.y + bar.implicitHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
|
|
||||||
@@ -158,6 +174,7 @@ Variants {
|
|||||||
id: blobGroup
|
id: blobGroup
|
||||||
|
|
||||||
color: DynamicColors.palette.m3surface
|
color: DynamicColors.palette.m3surface
|
||||||
|
smoothing: Config.barConfig.smoothing
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
CAnim {
|
CAnim {
|
||||||
@@ -179,28 +196,34 @@ Variants {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: dashBg
|
id: dashBg
|
||||||
|
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
property real extraHeight: 0.2
|
||||||
implicitHeight: panels.dashboard.height
|
|
||||||
|
deformAmount: 0.06
|
||||||
|
implicitHeight: panels.dashboard.height * (1 + extraHeight)
|
||||||
implicitWidth: panels.dashboard.width
|
implicitWidth: panels.dashboard.width
|
||||||
panel: panels.dashboard
|
panel: panels.dashboardWrapper
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border
|
x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border
|
||||||
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight
|
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelBg {
|
PanelBg {
|
||||||
id: launcherBg
|
id: launcherBg
|
||||||
|
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
property real extraHeight: 0.2
|
||||||
|
|
||||||
|
deformAmount: 0.06
|
||||||
|
implicitHeight: panels.launcher.height * (1 + extraHeight)
|
||||||
panel: panels.launcher
|
panel: panels.launcher
|
||||||
radius: Appearance.rounding.smallest + 5
|
radius: Appearance.rounding.smallest + 5
|
||||||
|
y: panels.launcher.y + bar.implicitHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelBg {
|
PanelBg {
|
||||||
id: sidebarBg
|
id: sidebarBg
|
||||||
|
|
||||||
bottomLeftRadius: 0
|
bottomLeftRadius: 0
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
deformAmount: 0.04
|
||||||
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
|
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
|
||||||
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
|
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
|
||||||
panel: panels.sidebar
|
panel: panels.sidebar
|
||||||
@@ -209,10 +232,10 @@ Variants {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: osdBg
|
id: osdBg
|
||||||
|
|
||||||
deformAmount: 0.1 * Config.appearance.deform.scale
|
deformAmount: 0.1
|
||||||
implicitHeight: panels.osd.height
|
implicitHeight: panels.osd.height
|
||||||
implicitWidth: panels.osd.width
|
implicitWidth: panels.osd.width
|
||||||
panel: panels.osd
|
panel: panels.osdWrapper
|
||||||
radius: 20
|
radius: 20
|
||||||
x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border
|
x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border
|
||||||
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
|
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
|
||||||
@@ -227,7 +250,7 @@ Variants {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: utilsBg
|
id: utilsBg
|
||||||
|
|
||||||
deformAmount: panels.sidebar.visible ? (0.1 * Config.appearance.deform.scale) : (0.1 * Config.appearance.deform.scale)
|
deformAmount: panels.sidebar.visible ? (0.1) : (0.1)
|
||||||
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
|
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
|
||||||
panel: panels.utilities
|
panel: panels.utilities
|
||||||
topLeftRadius: 0
|
topLeftRadius: 0
|
||||||
@@ -238,11 +261,11 @@ Variants {
|
|||||||
|
|
||||||
property real extraHeight: panels.popouts.isDetached ? 0 : 0.2
|
property real extraHeight: panels.popouts.isDetached ? 0 : 0.2
|
||||||
|
|
||||||
deformAmount: panels.popouts.isDetached ? 0.05 * Config.appearance.deform.scale : panels.popouts.hasCurrent ? 0.15 * Config.appearance.deform.scale : 0.1 * Config.appearance.deform.scale
|
deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1
|
||||||
implicitHeight: panels.popouts.height * (1 + extraHeight)
|
implicitHeight: panels.popouts.height * (1 + extraHeight)
|
||||||
implicitWidth: panels.popouts.width
|
implicitWidth: panels.popouts.width
|
||||||
panel: panels.popouts
|
panel: panels.popoutsWrapper
|
||||||
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : Appearance.rounding.smallest
|
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale
|
||||||
x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border
|
x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border
|
||||||
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
|
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
|
||||||
|
|
||||||
@@ -255,10 +278,10 @@ Variants {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: resourcesBg
|
id: resourcesBg
|
||||||
|
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
deformAmount: 0.05
|
||||||
implicitHeight: panels.resources.height
|
implicitHeight: panels.resources.height
|
||||||
implicitWidth: panels.resources.width
|
implicitWidth: panels.resources.width
|
||||||
panel: panels.resources
|
panel: panels.resourcesWrapper
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border
|
x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border
|
||||||
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
|
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
|
||||||
@@ -267,17 +290,23 @@ Variants {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: settingsBg
|
id: settingsBg
|
||||||
|
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
property real extraHeight: 0.2
|
||||||
|
|
||||||
|
deformAmount: 0.03
|
||||||
|
implicitHeight: panels.settings.height * (1 + extraHeight)
|
||||||
|
implicitWidth: panels.settings.width
|
||||||
panel: panels.settings
|
panel: panels.settings
|
||||||
radius: Appearance.rounding.large
|
radius: Appearance.rounding.large
|
||||||
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
|
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
|
||||||
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
|
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
|
||||||
|
x: panels.settingsWrapper.x + panels.settings.x + Config.barConfig.border
|
||||||
|
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelBg {
|
PanelBg {
|
||||||
id: dockBg
|
id: dockBg
|
||||||
|
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
deformAmount: 0.08
|
||||||
panel: panels.dock
|
panel: panels.dock
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
}
|
}
|
||||||
@@ -285,10 +314,22 @@ Variants {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: drawingBg
|
id: drawingBg
|
||||||
|
|
||||||
deformAmount: 0.08 * Config.appearance.deform.scale
|
deformAmount: 0.08
|
||||||
panel: panels.drawing
|
panel: panels.drawing
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: panels.traySubmenus.children
|
||||||
|
|
||||||
|
PanelBg {
|
||||||
|
required property Item modelData
|
||||||
|
|
||||||
|
deformAmount: 0.1
|
||||||
|
panel: modelData
|
||||||
|
radius: 20 * Appearance.rounding.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Drawing {
|
Drawing {
|
||||||
@@ -380,7 +421,7 @@ Variants {
|
|||||||
property real deformAmount: 0.15
|
property real deformAmount: 0.15
|
||||||
required property Item panel
|
required property Item panel
|
||||||
|
|
||||||
deformScale: deformAmount / 10000
|
deformScale: (deformAmount * Config.appearance.deform.scale) / 10000
|
||||||
group: blobGroup
|
group: blobGroup
|
||||||
implicitHeight: panel.height
|
implicitHeight: panel.height
|
||||||
implicitWidth: panel.width
|
implicitWidth: panel.width
|
||||||
|
|||||||
@@ -346,7 +346,6 @@ Singleton {
|
|||||||
|
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
console.log("this is running");
|
|
||||||
if (root.gpuType === "GENERIC") {
|
if (root.gpuType === "GENERIC") {
|
||||||
const percs = text.trim().split("\n");
|
const percs = text.trim().split("\n");
|
||||||
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
|
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
|
||||||
|
|||||||
+20
-44
@@ -7,23 +7,30 @@ import QtQuick
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// The list of users that can log in graphically
|
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user"
|
||||||
// Each user object has: username, uid, home, shell, gecos (full name), face (avatar path)
|
property int selectedIndex: 0
|
||||||
|
readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
|
||||||
|
readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
|
||||||
property var users: []
|
property var users: []
|
||||||
|
|
||||||
// The currently selected user index
|
function saveDefaultUser(): void {
|
||||||
property int selectedIndex: 0
|
if (selectedUser) {
|
||||||
|
defaultUserStorage.setText(selectedUser.username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The currently selected user object (or null if none)
|
function selectNext(): void {
|
||||||
readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
|
if (users.length === 0)
|
||||||
|
return;
|
||||||
|
selectedIndex = (selectedIndex + 1) % users.length;
|
||||||
|
}
|
||||||
|
|
||||||
// Convenience property for the selected username
|
function selectPrevious(): void {
|
||||||
readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
|
if (users.length === 0)
|
||||||
|
return;
|
||||||
|
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
|
||||||
|
}
|
||||||
|
|
||||||
// Path to store the default user preference
|
|
||||||
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user"
|
|
||||||
|
|
||||||
// Select a user by username
|
|
||||||
function selectUser(username: string): bool {
|
function selectUser(username: string): bool {
|
||||||
for (let i = 0; i < users.length; i++) {
|
for (let i = 0; i < users.length; i++) {
|
||||||
if (users[i].username === username) {
|
if (users[i].username === username) {
|
||||||
@@ -34,28 +41,6 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the next user in the list (wraps around)
|
|
||||||
function selectNext(): void {
|
|
||||||
if (users.length === 0)
|
|
||||||
return;
|
|
||||||
selectedIndex = (selectedIndex + 1) % users.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the previous user in the list (wraps around)
|
|
||||||
function selectPrevious(): void {
|
|
||||||
if (users.length === 0)
|
|
||||||
return;
|
|
||||||
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the current user as the default for next login
|
|
||||||
function saveDefaultUser(): void {
|
|
||||||
if (selectedUser) {
|
|
||||||
defaultUserStorage.setText(selectedUser.username);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process to fetch the list of graphical users
|
|
||||||
Process {
|
Process {
|
||||||
id: userLister
|
id: userLister
|
||||||
|
|
||||||
@@ -67,13 +52,10 @@ Singleton {
|
|||||||
try {
|
try {
|
||||||
root.users = JSON.parse(text);
|
root.users = JSON.parse(text);
|
||||||
|
|
||||||
// If we have users and no selection yet, try to select the default user
|
|
||||||
if (root.users.length > 0) {
|
if (root.users.length > 0) {
|
||||||
// Try to load the default user
|
|
||||||
if (defaultUserStorage.loaded) {
|
if (defaultUserStorage.loaded) {
|
||||||
const defaultUsername = defaultUserStorage.text().trim();
|
const defaultUsername = defaultUserStorage.text().trim();
|
||||||
if (defaultUsername && !root.selectUser(defaultUsername)) {
|
if (defaultUsername && !root.selectUser(defaultUsername)) {
|
||||||
// Default user not found, select first user
|
|
||||||
root.selectedIndex = 0;
|
root.selectedIndex = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -87,15 +69,14 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileView for persisting the default user
|
|
||||||
FileView {
|
FileView {
|
||||||
id: defaultUserStorage
|
id: defaultUserStorage
|
||||||
|
|
||||||
path: root.defaultUserFile
|
path: root.defaultUserFile
|
||||||
preload: true
|
preload: true
|
||||||
|
|
||||||
|
onLoadFailed: {}
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
// If users are already loaded, try to select the default user
|
|
||||||
if (root.users.length > 0) {
|
if (root.users.length > 0) {
|
||||||
const defaultUsername = text().trim();
|
const defaultUsername = text().trim();
|
||||||
if (defaultUsername) {
|
if (defaultUsername) {
|
||||||
@@ -103,10 +84,5 @@ 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ Singleton {
|
|||||||
|
|
||||||
function setHyprConf(): void {
|
function setHyprConf(): void {
|
||||||
Hypr.extras.applyOptions({
|
Hypr.extras.applyOptions({
|
||||||
"animations:enabled": 0,
|
"animations.enabled": 0,
|
||||||
"decoration:shadow:enabled": 0,
|
"decoration.shadow.enabled": 0,
|
||||||
"decoration:blur:enabled": 0,
|
"decoration.blur.enabled": 0,
|
||||||
"general:border_size": 0,
|
"general.border_size": 0,
|
||||||
"decoration:rounding": 0
|
"decoration.rounding": 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Searcher {
|
|||||||
property string actualCurrent: WallpaperPath.currentWallpaperPath
|
property string actualCurrent: WallpaperPath.currentWallpaperPath
|
||||||
readonly property string current: showPreview ? previewPath : actualCurrent
|
readonly property string current: showPreview ? previewPath : actualCurrent
|
||||||
property string previewPath
|
property string previewPath
|
||||||
|
property bool recentlyChanged
|
||||||
property bool showPreview: false
|
property bool showPreview: false
|
||||||
|
|
||||||
function preview(path: string): void {
|
function preview(path: string): void {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Item {
|
|||||||
readonly property Item current: currentPopout?.item ?? null
|
readonly property Item current: currentPopout?.item ?? null
|
||||||
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
|
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
|
||||||
required property PopoutState popouts
|
required property PopoutState popouts
|
||||||
|
required property ShellScreen screen
|
||||||
|
|
||||||
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2
|
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2
|
||||||
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2
|
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2
|
||||||
@@ -63,6 +64,7 @@ Item {
|
|||||||
|
|
||||||
TrayMenuPopout {
|
TrayMenuPopout {
|
||||||
popouts: root.popouts
|
popouts: root.popouts
|
||||||
|
screen: root.screen
|
||||||
trayItem: trayMenu.modelData.menu
|
trayItem: trayMenu.modelData.menu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+25
-77
@@ -12,90 +12,29 @@ Item {
|
|||||||
|
|
||||||
required property Canvas drawing
|
required property Canvas drawing
|
||||||
property bool expanded: true
|
property bool expanded: true
|
||||||
|
property real offsetScale: shouldBeActive ? 0 : 1
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
readonly property bool shouldBeActive: visibilities.isDrawing
|
readonly property bool shouldBeActive: visibilities.isDrawing
|
||||||
required property var visibilities
|
required property var visibilities
|
||||||
|
|
||||||
|
anchors.leftMargin: (-implicitWidth - 5) * offsetScale
|
||||||
implicitHeight: content.implicitHeight
|
implicitHeight: content.implicitHeight
|
||||||
implicitWidth: 0
|
implicitWidth: root.expanded ? content.implicitWidth : icon.implicitWidth
|
||||||
visible: width > 0
|
opacity: 1 - offsetScale
|
||||||
|
visible: offsetScale < 1
|
||||||
|
|
||||||
states: [
|
Behavior on implicitWidth {
|
||||||
State {
|
Anim {
|
||||||
name: "hidden"
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
when: !root.shouldBeActive
|
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||||
|
|
||||||
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: [
|
Behavior on offsetScale {
|
||||||
Transition {
|
Anim {
|
||||||
from: "*"
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
to: "*"
|
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
Anim {
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
property: "implicitWidth"
|
|
||||||
target: root
|
|
||||||
}
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.small
|
|
||||||
property: "opacity"
|
|
||||||
target: icon
|
|
||||||
}
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.small
|
|
||||||
property: "opacity"
|
|
||||||
target: content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (!visible)
|
if (!visible)
|
||||||
@@ -109,8 +48,12 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: content.contentItem.height
|
height: content.contentItem.height
|
||||||
opacity: 1
|
opacity: root.expanded ? 0 : 1
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
sourceComponent: MaterialIcon {
|
sourceComponent: MaterialIcon {
|
||||||
font.pointSize: Appearance.font.size.larger
|
font.pointSize: Appearance.font.size.larger
|
||||||
text: "arrow_forward_ios"
|
text: "arrow_forward_ios"
|
||||||
@@ -122,7 +65,12 @@ Item {
|
|||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
opacity: root.expanded ? 1 : 0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
sourceComponent: Content {
|
sourceComponent: Content {
|
||||||
drawing: root.drawing
|
drawing: root.drawing
|
||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ Searcher {
|
|||||||
|
|
||||||
function launch(entry: DesktopEntry): void {
|
function launch(entry: DesktopEntry): void {
|
||||||
appDb.incrementFrequency(entry.id);
|
appDb.incrementFrequency(entry.id);
|
||||||
console.log(root.command);
|
|
||||||
|
|
||||||
if (entry.runInTerminal)
|
if (entry.runInTerminal)
|
||||||
Quickshell.execDetached({
|
Quickshell.execDetached({
|
||||||
|
|||||||
@@ -284,6 +284,8 @@ CustomRect {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||||
text: {
|
text: {
|
||||||
|
if (!Config.lock.showNotifContent)
|
||||||
|
return "Unlock to view";
|
||||||
const summary = modelData.summary.replace(/\n/g, " ");
|
const summary = modelData.summary.replace(/\n/g, " ");
|
||||||
const body = modelData.body.replace(/\n/g, " ");
|
const body = modelData.body.replace(/\n/g, " ");
|
||||||
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
||||||
|
|||||||
@@ -1,8 +1,36 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
property string currentName
|
property string currentName
|
||||||
property bool hasCurrent
|
property bool hasCurrent
|
||||||
|
property var submenus: []
|
||||||
|
|
||||||
signal detachRequested(mode: string)
|
signal detachRequested(mode: string)
|
||||||
|
|
||||||
|
function clearSubmenus(): void {
|
||||||
|
submenus = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSubmenus(level: int): void {
|
||||||
|
submenus = submenus.slice(0, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushSubmenu(level: int, handle: var, sourceItem: var, sourceWidth: int): void {
|
||||||
|
let newSubmenus = submenus.slice(0, level);
|
||||||
|
newSubmenus.push({
|
||||||
|
"handle": handle,
|
||||||
|
"sourceItem": sourceItem,
|
||||||
|
"sourceWidth": sourceWidth
|
||||||
|
});
|
||||||
|
submenus = newSubmenus;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentNameChanged: {
|
||||||
|
root.clearSubmenus();
|
||||||
|
}
|
||||||
|
onHasCurrentChanged: {
|
||||||
|
root.clearSubmenus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ CustomRect {
|
|||||||
color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2
|
implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2
|
||||||
radius: height / 2
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
onClicked: root.visibilities.resources = !root.visibilities.resources
|
onClicked: root.visibilities.resources = !root.visibilities.resources
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ SettingsPage {
|
|||||||
step: 50
|
step: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separator {
|
Separator {
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// WallpaperCropper {
|
WallpaperCropper {
|
||||||
// Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
// Layout.preferredHeight: 300
|
Layout.preferredHeight: 600
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsSection {
|
SettingsSection {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Height"
|
|
||||||
min: 1
|
min: 1
|
||||||
|
name: "Height"
|
||||||
object: Config.barConfig
|
object: Config.barConfig
|
||||||
setting: "height"
|
setting: "height"
|
||||||
}
|
}
|
||||||
@@ -29,8 +29,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Rounding"
|
|
||||||
min: 0
|
min: 0
|
||||||
|
name: "Rounding"
|
||||||
object: Config.barConfig
|
object: Config.barConfig
|
||||||
setting: "rounding"
|
setting: "rounding"
|
||||||
}
|
}
|
||||||
@@ -39,11 +39,21 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Border"
|
|
||||||
min: 0
|
min: 0
|
||||||
|
name: "Border"
|
||||||
object: Config.barConfig
|
object: Config.barConfig
|
||||||
setting: "border"
|
setting: "border"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingSpinBox {
|
||||||
|
min: 0
|
||||||
|
name: "Smoothing"
|
||||||
|
object: Config.barConfig
|
||||||
|
setting: "smoothing"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsSection {
|
SettingsSection {
|
||||||
@@ -145,8 +155,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Dock height"
|
|
||||||
min: 1
|
min: 1
|
||||||
|
name: "Dock height"
|
||||||
object: Config.dock
|
object: Config.dock
|
||||||
setting: "height"
|
setting: "height"
|
||||||
}
|
}
|
||||||
@@ -173,8 +183,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingStringList {
|
SettingStringList {
|
||||||
name: "Pinned apps"
|
|
||||||
addLabel: qsTr("Add pinned app")
|
addLabel: qsTr("Add pinned app")
|
||||||
|
name: "Pinned apps"
|
||||||
object: Config.dock
|
object: Config.dock
|
||||||
setting: "pinnedApps"
|
setting: "pinnedApps"
|
||||||
}
|
}
|
||||||
@@ -183,8 +193,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingStringList {
|
SettingStringList {
|
||||||
name: "Ignored app regexes"
|
|
||||||
addLabel: qsTr("Add ignored regex")
|
addLabel: qsTr("Add ignored regex")
|
||||||
|
name: "Ignored app regexes"
|
||||||
object: Config.dock
|
object: Config.dock
|
||||||
setting: "ignoredAppRegexes"
|
setting: "ignoredAppRegexes"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Max fingerprint tries"
|
|
||||||
min: 1
|
min: 1
|
||||||
|
name: "Max fingerprint tries"
|
||||||
object: Config.lock
|
object: Config.lock
|
||||||
setting: "maxFprintTries"
|
setting: "maxFprintTries"
|
||||||
step: 1
|
step: 1
|
||||||
@@ -41,9 +41,18 @@ SettingsPage {
|
|||||||
Separator {
|
Separator {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingSwitch {
|
||||||
|
name: "Show notification details"
|
||||||
|
object: Config.lock
|
||||||
|
setting: "showNotifContent"
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Blur amount"
|
|
||||||
min: 0
|
min: 0
|
||||||
|
name: "Blur amount"
|
||||||
object: Config.lock
|
object: Config.lock
|
||||||
setting: "blurAmount"
|
setting: "blurAmount"
|
||||||
step: 1
|
step: 1
|
||||||
@@ -53,9 +62,9 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Height multiplier"
|
|
||||||
max: 2
|
max: 2
|
||||||
min: 0.1
|
min: 0.1
|
||||||
|
name: "Height multiplier"
|
||||||
object: Config.lock.sizes
|
object: Config.lock.sizes
|
||||||
setting: "heightMult"
|
setting: "heightMult"
|
||||||
step: 0.05
|
step: 0.05
|
||||||
@@ -65,9 +74,9 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Aspect ratio"
|
|
||||||
max: 4
|
max: 4
|
||||||
min: 0.5
|
min: 0.5
|
||||||
|
name: "Aspect ratio"
|
||||||
object: Config.lock.sizes
|
object: Config.lock.sizes
|
||||||
setting: "ratio"
|
setting: "ratio"
|
||||||
step: 0.05
|
step: 0.05
|
||||||
@@ -77,8 +86,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Center width"
|
|
||||||
min: 100
|
min: 100
|
||||||
|
name: "Center width"
|
||||||
object: Config.lock.sizes
|
object: Config.lock.sizes
|
||||||
setting: "centerWidth"
|
setting: "centerWidth"
|
||||||
step: 10
|
step: 10
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Item {
|
|||||||
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
|
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
|
||||||
Layout.preferredWidth: 500
|
Layout.preferredWidth: 500
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
radius: 21 + Appearance.padding.normal
|
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: searchBox
|
id: searchBox
|
||||||
|
|||||||
@@ -6,97 +6,109 @@ import qs.Config
|
|||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
|
|
||||||
ColumnLayout {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
spacing: 15
|
Image {
|
||||||
width: Math.min(parent ? parent.width : 600, 600)
|
id: imageView
|
||||||
|
|
||||||
Rectangle {
|
property real displayH: paintedHeight
|
||||||
id: previewContainer
|
property real displayW: paintedWidth
|
||||||
|
property real displayX: (width - paintedWidth) * 0.5
|
||||||
|
property real displayY: (height - paintedHeight) * 0.5
|
||||||
|
property real scaleX: sourceW / displayW
|
||||||
|
property real scaleY: sourceH / displayH
|
||||||
|
property real sourceH: Quickshell.screens[0].height
|
||||||
|
property real sourceW: Quickshell.screens[0].width
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
smooth: true
|
||||||
|
source: Wallpapers.current
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: overlay
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height * (Quickshell.screens.length > 0 ? (Quickshell.screens[0].height / Math.max(1, Quickshell.screens[0].width)) : 16 / 9)
|
|
||||||
clip: true
|
clip: true
|
||||||
color: DynamicColors.palette.m3surfaceContainer
|
height: imageView.displayH
|
||||||
radius: Config.appearance.rounding.scale * 10
|
width: imageView.displayW
|
||||||
|
x: imageView.displayX
|
||||||
|
y: imageView.displayY
|
||||||
|
|
||||||
Image {
|
CustomRect {
|
||||||
id: img
|
id: cropRect
|
||||||
|
|
||||||
|
property real aspectRatio: Quickshell.screens[0].width / Quickshell.screens[0].height
|
||||||
|
readonly property rect sourceRect: Qt.rect(x * imageView.scaleX, y * imageView.scaleY, width * imageView.scaleX, height * imageView.scaleY)
|
||||||
|
property real zoom: Config.background.zoom
|
||||||
|
|
||||||
|
function clampToBounds() {
|
||||||
|
x = Math.max(0, Math.min(x, overlay.width - width));
|
||||||
|
|
||||||
|
y = Math.max(0, Math.min(y, overlay.height - height));
|
||||||
|
}
|
||||||
|
|
||||||
|
border.color: DynamicColors.palette.m3primary
|
||||||
|
border.width: 2
|
||||||
|
color: DynamicColors.tPalette.m3primary
|
||||||
|
height: width / aspectRatio
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
visible: imageView.status === Image.Ready
|
||||||
|
width: Math.min(overlay.width / zoom, overlay.height * aspectRatio / zoom)
|
||||||
|
x: Config.background.sourceClipX / imageView.scaleX
|
||||||
|
y: Config.background.sourceClipY / imageView.scaleY
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
function updateCrop(mouseX, mouseY) {
|
||||||
|
let nx = mouseX - cropRect.width * 0.5;
|
||||||
|
let ny = mouseY - cropRect.height * 0.5;
|
||||||
|
|
||||||
|
nx = Math.max(0, Math.min(nx, overlay.width - cropRect.width));
|
||||||
|
|
||||||
|
ny = Math.max(0, Math.min(ny, overlay.height - cropRect.height));
|
||||||
|
|
||||||
|
cropRect.x = nx;
|
||||||
|
cropRect.y = ny;
|
||||||
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
asynchronous: true
|
hoverEnabled: true
|
||||||
fillMode: Image.PreserveAspectFit
|
preventStealing: true
|
||||||
source: Wallpapers.current
|
|
||||||
|
|
||||||
Rectangle {
|
onPositionChanged: mouse => {
|
||||||
id: cropRect
|
if (pressed)
|
||||||
|
updateCrop(mouse.x, mouse.y);
|
||||||
|
}
|
||||||
|
onPressed: mouse => {
|
||||||
|
updateCrop(mouse.x, mouse.y);
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
Wallpapers.recentlyChanged = false;
|
||||||
|
Config.background.sourceClipX = cropRect.sourceRect.x;
|
||||||
|
Config.background.sourceClipY = cropRect.sourceRect.y;
|
||||||
|
Config.background.sourceClipW = cropRect.sourceRect.width;
|
||||||
|
Config.background.sourceClipH = cropRect.sourceRect.height;
|
||||||
|
Config.save();
|
||||||
|
}
|
||||||
|
onWheel: wheel => {
|
||||||
|
let oldCenterX = cropRect.x + cropRect.width * 0.5;
|
||||||
|
let oldCenterY = cropRect.y + cropRect.height * 0.5;
|
||||||
|
|
||||||
property real cropHeight: (imageAspect > screenAspect ? paintedHeight : paintedWidth / screenAspect) / Config.background.zoom
|
if (wheel.angleDelta.y > 0)
|
||||||
property real cropWidth: (imageAspect > screenAspect ? paintedHeight * screenAspect : paintedWidth) / Config.background.zoom
|
cropRect.zoom *= 1.1;
|
||||||
property real imageAspect: Math.max(1, paintedWidth) / Math.max(1, paintedHeight)
|
else
|
||||||
property real paintedHeight: img.paintedHeight > 0 ? img.paintedHeight : img.height
|
cropRect.zoom /= 1.1;
|
||||||
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
|
cropRect.zoom = Math.max(1.0, Math.min(cropRect.zoom, 10.0));
|
||||||
border.width: 2
|
Config.background.zoom = cropRect.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
|
|
||||||
|
|
||||||
DragHandler {
|
cropRect.x = oldCenterX - cropRect.width * 0.5;
|
||||||
target: null
|
cropRect.y = oldCenterY - cropRect.height * 0.5;
|
||||||
|
|
||||||
onActiveTranslationChanged: {
|
cropRect.clampToBounds();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
|
||||||
max: 5.0
|
|
||||||
min: 1.0
|
|
||||||
name: "Zoom"
|
|
||||||
object: Config.background
|
|
||||||
setting: "zoom"
|
|
||||||
step: 0.1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ import QtQuick.Controls
|
|||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import "../../scripts/fuzzysort.js" as Fuzzy
|
import "../../scripts/fuzzysort.js" as Fuzzy
|
||||||
import "./SettingsIndex.mjs" as SettingsIndex
|
import "../../scripts/SettingsIndex.mjs" as SettingsIndex
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -53,11 +53,10 @@ Item {
|
|||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "/"
|
sequence: "/"
|
||||||
|
|
||||||
onActivated: searchField.forceActiveFocus()
|
onActivated: searchField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: console.log(root.height)
|
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: resultsModel
|
id: resultsModel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,45 +7,24 @@ import qs.Helpers
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property real offsetScale: shouldBeActive ? 0 : 1
|
||||||
required property var panels
|
required property var panels
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
|
readonly property bool shouldBeActive: visibilities.settings
|
||||||
required property PersistentProperties visibilities
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
implicitHeight: 0
|
anchors.topMargin: (-implicitHeight - 5) * offsetScale
|
||||||
|
implicitHeight: content.implicitHeight
|
||||||
implicitWidth: content.implicitWidth
|
implicitWidth: content.implicitWidth
|
||||||
visible: height > 0
|
opacity: 1 - offsetScale
|
||||||
|
visible: offsetScale < 1
|
||||||
|
|
||||||
states: State {
|
Behavior on offsetScale {
|
||||||
name: "visible"
|
Anim {
|
||||||
when: root.visibilities.settings
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||||
PropertyChanges {
|
|
||||||
root.implicitHeight: content.implicitHeight
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transitions: [
|
|
||||||
Transition {
|
|
||||||
from: ""
|
|
||||||
to: "visible"
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
duration: MaterialEasing.expressiveEffectsTime
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
property: "implicitHeight"
|
|
||||||
target: root
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Transition {
|
|
||||||
from: "visible"
|
|
||||||
to: ""
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
property: "implicitHeight"
|
|
||||||
target: root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
CustomClippingRect {
|
CustomClippingRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ Scope {
|
|||||||
if (!root.launcherInterrupted && !root.hasFullscreen) {
|
if (!root.launcherInterrupted && !root.hasFullscreen) {
|
||||||
const visibilities = Visibilities.getForActive();
|
const visibilities = Visibilities.getForActive();
|
||||||
visibilities.launcher = !visibilities.launcher;
|
visibilities.launcher = !visibilities.launcher;
|
||||||
console.log(root.launcherInterrupted);
|
|
||||||
}
|
}
|
||||||
root.launcherInterrupted = false;
|
root.launcherInterrupted = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Components
|
||||||
|
import qs.Modules
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: menu
|
||||||
|
|
||||||
|
property int biggestWidth: 0
|
||||||
|
required property QsMenuHandle handle
|
||||||
|
required property int level
|
||||||
|
required property PopoutState popouts
|
||||||
|
required property ShellScreen screen
|
||||||
|
property bool shown: true
|
||||||
|
|
||||||
|
height: childrenRect.height
|
||||||
|
opacity: shown ? 1 : 0
|
||||||
|
padding: 0
|
||||||
|
scale: shown ? 1 : 0.8
|
||||||
|
spacing: 4
|
||||||
|
width: biggestWidth
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on scale {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
|
||||||
|
menu: menu.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: menuOpener.children
|
||||||
|
|
||||||
|
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: menu.biggestWidth
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: children
|
||||||
|
|
||||||
|
active: !item.modelData.isSeparator
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: Item {
|
||||||
|
implicitHeight: 30
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
function onClicked(): void {
|
||||||
|
const entry = item.modelData;
|
||||||
|
if (entry.hasChildren) {
|
||||||
|
menu.popouts.pushSubmenu(menu.level, entry, item, menu.biggestWidth);
|
||||||
|
} else {
|
||||||
|
entry.triggered();
|
||||||
|
menu.popouts.hasCurrent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disabled: !item.modelData.enabled
|
||||||
|
radius: item.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
active: item.modelData.icon !== ""
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: Item {
|
||||||
|
implicitHeight: label.implicitHeight
|
||||||
|
implicitWidth: label.implicitHeight
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: iconImage
|
||||||
|
|
||||||
|
implicitSize: parent.implicitHeight
|
||||||
|
source: item.modelData.icon
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: iconImage
|
||||||
|
colorization: 1.0
|
||||||
|
colorizationColor: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||||
|
source: iconImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||||
|
text: labelMetrics.elidedText
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: labelMetrics
|
||||||
|
|
||||||
|
font.family: label.font.family
|
||||||
|
font.pointSize: label.font.pointSize
|
||||||
|
text: item.modelData.text
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
var biggestWidth = menu.biggestWidth;
|
||||||
|
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
|
||||||
|
if (currentWidth > biggestWidth) {
|
||||||
|
menu.biggestWidth = currentWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: expand
|
||||||
|
|
||||||
|
active: item.modelData.hasChildren
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: MaterialIcon {
|
||||||
|
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||||
|
text: "chevron_right"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,249 +1,16 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Effects
|
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
StackView {
|
SubMenu {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int biggestWidth: 0
|
handle: trayItem
|
||||||
required property PopoutState popouts
|
level: 0
|
||||||
property int rootWidth: 0
|
|
||||||
required property QsMenuHandle trayItem
|
required property QsMenuHandle trayItem
|
||||||
|
}
|
||||||
implicitHeight: currentItem.implicitHeight
|
|
||||||
implicitWidth: currentItem.implicitWidth
|
|
||||||
|
|
||||||
initialItem: SubMenu {
|
|
||||||
handle: root.trayItem
|
|
||||||
}
|
|
||||||
popEnter: NoAnim {
|
|
||||||
}
|
|
||||||
popExit: NoAnim {
|
|
||||||
}
|
|
||||||
pushEnter: NoAnim {
|
|
||||||
}
|
|
||||||
pushExit: NoAnim {
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: subMenuComp
|
|
||||||
|
|
||||||
SubMenu {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component NoAnim: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
component SubMenu: Column {
|
|
||||||
id: menu
|
|
||||||
|
|
||||||
required property QsMenuHandle handle
|
|
||||||
property bool isSubMenu
|
|
||||||
property bool shown
|
|
||||||
|
|
||||||
opacity: shown ? 1 : 0
|
|
||||||
padding: 0
|
|
||||||
scale: shown ? 1 : 0.8
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on scale {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: shown = true
|
|
||||||
StackView.onActivating: shown = true
|
|
||||||
StackView.onDeactivating: shown = false
|
|
||||||
StackView.onRemoved: destroy()
|
|
||||||
|
|
||||||
QsMenuOpener {
|
|
||||||
id: menuOpener
|
|
||||||
|
|
||||||
menu: menu.handle
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: menuOpener.children
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: item
|
|
||||||
|
|
||||||
required property QsMenuEntry modelData
|
|
||||||
|
|
||||||
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
|
||||||
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
|
||||||
implicitWidth: root.biggestWidth
|
|
||||||
radius: Appearance.rounding.smallest / 2
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: children
|
|
||||||
|
|
||||||
active: !item.modelData.isSeparator
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: Item {
|
|
||||||
implicitHeight: 30
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
function onClicked(): void {
|
|
||||||
const entry = item.modelData;
|
|
||||||
if (entry.hasChildren) {
|
|
||||||
root.rootWidth = root.biggestWidth;
|
|
||||||
root.biggestWidth = 0;
|
|
||||||
root.push(subMenuComp.createObject(null, {
|
|
||||||
handle: entry,
|
|
||||||
isSubMenu: true
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
item.modelData.triggered();
|
|
||||||
root.popouts.hasCurrent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
disabled: !item.modelData.enabled
|
|
||||||
radius: item.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: icon
|
|
||||||
|
|
||||||
active: item.modelData.icon !== ""
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 10
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: Item {
|
|
||||||
implicitHeight: label.implicitHeight
|
|
||||||
implicitWidth: label.implicitHeight
|
|
||||||
|
|
||||||
IconImage {
|
|
||||||
id: iconImage
|
|
||||||
|
|
||||||
implicitSize: parent.implicitHeight
|
|
||||||
source: item.modelData.icon
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiEffect {
|
|
||||||
anchors.fill: iconImage
|
|
||||||
colorization: 1.0
|
|
||||||
colorizationColor: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
|
||||||
source: iconImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: label
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 10
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
|
||||||
text: labelMetrics.elidedText
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: labelMetrics
|
|
||||||
|
|
||||||
font.family: label.font.family
|
|
||||||
font.pointSize: label.font.pointSize
|
|
||||||
text: item.modelData.text
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
var biggestWidth = root.biggestWidth;
|
|
||||||
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
|
|
||||||
if (currentWidth > biggestWidth) {
|
|
||||||
root.biggestWidth = currentWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: expand
|
|
||||||
|
|
||||||
active: item.modelData.hasChildren
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: MaterialIcon {
|
|
||||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
|
||||||
text: "chevron_right"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: loader
|
|
||||||
|
|
||||||
active: menu.isSubMenu
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: Item {
|
|
||||||
implicitHeight: back.implicitHeight + 2 / 2
|
|
||||||
implicitWidth: back.implicitWidth
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
implicitHeight: back.implicitHeight
|
|
||||||
implicitWidth: back.implicitWidth + 10
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: DynamicColors.palette.m3secondaryContainer
|
|
||||||
radius: Appearance.rounding.smallest / 2
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
function onClicked(): void {
|
|
||||||
root.pop();
|
|
||||||
root.biggestWidth = root.rootWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
color: DynamicColors.palette.m3onSecondaryContainer
|
|
||||||
radius: parent.radius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: back
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: DynamicColors.palette.m3onSecondaryContainer
|
|
||||||
text: "chevron_left"
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: DynamicColors.palette.m3onSecondaryContainer
|
|
||||||
text: qsTr("Back")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,7 @@ Item {
|
|||||||
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
|
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
|
||||||
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
|
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
|
||||||
// color: "transparent"
|
// color: "transparent"
|
||||||
radius: 6
|
radius: (20 - Appearance.padding.small) * Appearance.rounding.scale
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: indicator
|
id: indicator
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ CustomRect {
|
|||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||||
implicitWidth: contentRow.implicitWidth + Appearance.spacing.small * 2
|
implicitWidth: contentRow.implicitWidth + Appearance.spacing.small * 2
|
||||||
radius: height / 2
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: contentRow
|
id: contentRow
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
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,5 +1,6 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
@@ -8,79 +9,34 @@ import qs.Config
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property Image current: one
|
required property ShellScreen screen
|
||||||
property string source: Wallpapers.current
|
property string source: Wallpapers.current
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Component.onCompleted: {
|
Image {
|
||||||
if (source)
|
|
||||||
Qt.callLater(() => one.update());
|
|
||||||
}
|
|
||||||
onSourceChanged: {
|
|
||||||
if (!source) {
|
|
||||||
current = null;
|
|
||||||
} else if (current === one) {
|
|
||||||
two.update();
|
|
||||||
} else {
|
|
||||||
one.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Img {
|
|
||||||
id: one
|
|
||||||
}
|
|
||||||
|
|
||||||
Img {
|
|
||||||
id: two
|
|
||||||
}
|
|
||||||
|
|
||||||
component Img: CachingImage {
|
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
property real imageRatio: Math.max(1, sourceSize.width) / Math.max(1, sourceSize.height)
|
anchors.fill: parent
|
||||||
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
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
height: isValid ? (imageRatio > windowRatio ? root.height : root.width / imageRatio) * Config.background.zoom : root.height
|
opacity: 1
|
||||||
opacity: 0
|
retainWhileLoading: true
|
||||||
scale: Wallpapers.showPreview ? 1 : 0.8
|
source: root.source
|
||||||
width: isValid ? (imageRatio > windowRatio ? root.height * imageRatio : root.width) * Config.background.zoom : root.width
|
sourceClipRect: Wallpapers.recentlyChanged ? null : Qt.rect(Config.background.sourceClipX, Config.background.sourceClipY, Config.background.sourceClipW, Config.background.sourceClipH)
|
||||||
x: isValid ? (root.width - width) * Config.background.alignX : 0
|
sourceSize.height: root.screen.height
|
||||||
y: isValid ? (root.height - height) * Config.background.alignY : 0
|
sourceSize.width: root.screen.width
|
||||||
|
|
||||||
states: State {
|
onSourceChanged: {
|
||||||
name: "visible"
|
if (Wallpapers.recentlyChanged) {
|
||||||
when: root.current === img
|
Config.background.sourceClipH = 0;
|
||||||
|
Config.background.sourceClipW = 0;
|
||||||
PropertyChanges {
|
Config.background.sourceClipY = 0;
|
||||||
img.opacity: 1
|
Config.background.sourceClipX = 0;
|
||||||
img.scale: 1
|
Config.background.zoom = 1.0;
|
||||||
}
|
Config.save();
|
||||||
}
|
|
||||||
transitions: Transition {
|
|
||||||
Anim {
|
|
||||||
duration: Config.background.wallFadeDuration
|
|
||||||
properties: "opacity,scale"
|
|
||||||
target: img
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onStatusChanged: {
|
|
||||||
if (status === Image.Ready) {
|
|
||||||
root.current = this;
|
|
||||||
}
|
}
|
||||||
|
Wallpapers.recentlyChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Loader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WallBackground {
|
WallBackground {
|
||||||
|
screen: root.screen
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
Hyprland.dispatch(`workspace ${button.modelData.name}`);
|
const ws = button.modelData.name;
|
||||||
|
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -15,13 +15,14 @@ Item {
|
|||||||
property real currentCenter
|
property real currentCenter
|
||||||
property alias currentName: popoutState.currentName
|
property alias currentName: popoutState.currentName
|
||||||
property string detachedMode
|
property string detachedMode
|
||||||
readonly property bool isDetached: detachedMode.length > 0
|
|
||||||
property alias hasCurrent: popoutState.hasCurrent
|
property alias hasCurrent: popoutState.hasCurrent
|
||||||
|
readonly property bool isDetached: detachedMode.length > 0
|
||||||
readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight
|
readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight
|
||||||
readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
|
readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
|
||||||
required property real offsetScale
|
required property real offsetScale
|
||||||
property string queuedMode
|
property string queuedMode
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
|
property alias state: popoutState
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
hasCurrent = false;
|
hasCurrent = false;
|
||||||
@@ -79,6 +80,7 @@ Item {
|
|||||||
|
|
||||||
sourceComponent: Content {
|
sourceComponent: Content {
|
||||||
popouts: popoutState
|
popouts: popoutState
|
||||||
|
screen: root.screen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ qml_module(ZShell-blobs
|
|||||||
|
|
||||||
qt_add_shaders(ZShell-blobs "blob_shaders"
|
qt_add_shaders(ZShell-blobs "blob_shaders"
|
||||||
BATCHABLE OPTIMIZED NOHLSL NOMSL
|
BATCHABLE OPTIMIZED NOHLSL NOMSL
|
||||||
|
GLSL "300es,330"
|
||||||
PREFIX "/"
|
PREFIX "/"
|
||||||
FILES
|
FILES
|
||||||
shaders/blob.frag
|
shaders/blob.frag
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
static_assert(sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
|
||||||
|
"BlobMaterial packs excludeMask into a float slot via memcpy");
|
||||||
|
|
||||||
QSGMaterialType* BlobMaterial::type() const {
|
QSGMaterialType* BlobMaterial::type() const {
|
||||||
static QSGMaterialType s_type;
|
static QSGMaterialType s_type;
|
||||||
return &s_type;
|
return &s_type;
|
||||||
@@ -82,8 +85,11 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
|
|||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const auto& r = mat->m_rects[i];
|
const auto& r = mat->m_rects[i];
|
||||||
const int base = 160 + i * 80;
|
const int base = 160 + i * 80;
|
||||||
|
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
|
||||||
|
float maskAsFloat;
|
||||||
|
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
|
||||||
const float d0[4] = { r.cx, r.cy, r.hw, r.hh };
|
const float d0[4] = { r.cx, r.cy, r.hw, r.hh };
|
||||||
const float d1[4] = { 0.0f, r.offsetX, r.offsetY, r.minEig };
|
const float d1[4] = { maskAsFloat, r.offsetX, r.offsetY, r.minEig };
|
||||||
const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
|
const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
|
||||||
memcpy(buf->data() + base, d0, 16);
|
memcpy(buf->data() + base, d0, 16);
|
||||||
memcpy(buf->data() + base + 16, d1, 16);
|
memcpy(buf->data() + base + 16, d1, 16);
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ struct BlobRectData {
|
|||||||
float screenHalfX = 0, screenHalfY = 0;
|
float screenHalfX = 0, screenHalfY = 0;
|
||||||
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
|
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
|
||||||
float radius[4] = { 0, 0, 0, 0 };
|
float radius[4] = { 0, 0, 0, 0 };
|
||||||
|
// Bitmask of indices in this rect's m_cachedRects that mutually exclude (or are excluded by) this rect.
|
||||||
|
// Used by the shader to skip smin between excluded pairs.
|
||||||
|
int excludeMask = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlobMaterial : public QSGMaterial {
|
class BlobMaterial : public QSGMaterial {
|
||||||
|
|||||||
@@ -72,11 +72,17 @@ void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeome
|
|||||||
// Accumulate sub-pixel drift so slow movements don't desync the shader
|
// Accumulate sub-pixel drift so slow movements don't desync the shader
|
||||||
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
||||||
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
||||||
const auto dw = std::abs(newGeometry.width() - oldGeometry.width());
|
// Accumulate size delta across multiple frames so incremental size
|
||||||
const auto dh = std::abs(newGeometry.height() - oldGeometry.height());
|
// changes that are each below the threshold still trigger a dirty
|
||||||
if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f || dw > 0.5 || dh > 0.5) {
|
// 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) {
|
||||||
m_pendingDx = 0;
|
m_pendingDx = 0;
|
||||||
m_pendingDy = 0;
|
m_pendingDy = 0;
|
||||||
|
m_pendingDw = 0;
|
||||||
|
m_pendingDh = 0;
|
||||||
m_group->markShapeDirty(this);
|
m_group->markShapeDirty(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,6 +155,10 @@ void BlobShape::updatePolish() {
|
|||||||
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
||||||
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
||||||
|
|
||||||
|
// Track shape pointers parallel to m_cachedRects for pairwise exclusion lookups
|
||||||
|
QVector<BlobShape*> rectShapes;
|
||||||
|
rectShapes.reserve(m_group->shapes().size());
|
||||||
|
|
||||||
for (BlobShape* other : m_group->shapes()) {
|
for (BlobShape* other : m_group->shapes()) {
|
||||||
if (other->isInvertedRect())
|
if (other->isInvertedRect())
|
||||||
continue;
|
continue;
|
||||||
@@ -210,12 +220,29 @@ void BlobShape::updatePolish() {
|
|||||||
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
|
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
|
||||||
|
|
||||||
m_cachedRects.append(r);
|
m_cachedRects.append(r);
|
||||||
|
rectShapes.append(other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInvertedRect())
|
if (isInvertedRect())
|
||||||
m_cachedMyIndex = -1;
|
m_cachedMyIndex = -1;
|
||||||
|
|
||||||
|
// Compute pairwise exclude masks. Bit j in entry i is set iff rect i excludes rect j
|
||||||
|
// or rect j excludes rect i. The shader uses this to avoid smin between excluded pairs.
|
||||||
|
const auto cachedCount = m_cachedRects.size();
|
||||||
|
for (qsizetype i = 0; i < cachedCount; ++i) {
|
||||||
|
int mask = 0;
|
||||||
|
BlobShape* si = rectShapes[i];
|
||||||
|
for (qsizetype j = 0; j < cachedCount; ++j) {
|
||||||
|
if (j == i)
|
||||||
|
continue;
|
||||||
|
BlobShape* sj = rectShapes[j];
|
||||||
|
if (si->isExcluded(sj) || sj->isExcluded(si))
|
||||||
|
mask |= (1 << j);
|
||||||
|
}
|
||||||
|
m_cachedRects[i].excludeMask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
// Cache inverted rect data
|
// Cache inverted rect data
|
||||||
m_cachedHasInverted = false;
|
m_cachedHasInverted = false;
|
||||||
m_cachedInvertedRadius = 0;
|
m_cachedInvertedRadius = 0;
|
||||||
@@ -270,6 +297,7 @@ void BlobShape::updatePolish() {
|
|||||||
const auto rectCount = m_cachedRects.size();
|
const auto rectCount = m_cachedRects.size();
|
||||||
for (qsizetype i = 0; i < rectCount; ++i) {
|
for (qsizetype i = 0; i < rectCount; ++i) {
|
||||||
auto& ri = m_cachedRects[i];
|
auto& ri = m_cachedRects[i];
|
||||||
|
const int riExcludeMask = ri.excludeMask;
|
||||||
float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f;
|
float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f;
|
||||||
|
|
||||||
const float cTrX = ri.cx + ri.hw, cTrY = ri.cy - ri.hh;
|
const float cTrX = ri.cx + ri.hw, cTrY = ri.cy - ri.hh;
|
||||||
@@ -280,6 +308,8 @@ void BlobShape::updatePolish() {
|
|||||||
for (qsizetype j = 0; j < rectCount; ++j) {
|
for (qsizetype j = 0; j < rectCount; ++j) {
|
||||||
if (j == i)
|
if (j == i)
|
||||||
continue;
|
continue;
|
||||||
|
if (riExcludeMask & (1 << j))
|
||||||
|
continue;
|
||||||
const auto& rj = m_cachedRects[j];
|
const auto& rj = m_cachedRects[j];
|
||||||
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
|
|||||||
@@ -84,8 +84,10 @@ QRectF m_localPaddedRect;
|
|||||||
QVector<BlobRectData> m_cachedRects;
|
QVector<BlobRectData> m_cachedRects;
|
||||||
int m_cachedMyIndex = -2;
|
int m_cachedMyIndex = -2;
|
||||||
float m_pendingDx = 0;
|
float m_pendingDx = 0;
|
||||||
float m_pendingDy = 0;
|
float m_pendingDy = 0;
|
||||||
bool m_cachedHasInverted = false;
|
float m_pendingDw = 0;
|
||||||
|
float m_pendingDh = 0;
|
||||||
|
bool m_cachedHasInverted = false;
|
||||||
float m_cachedInvertedRadius = 0;
|
float m_cachedInvertedRadius = 0;
|
||||||
float m_cachedInvertedOuter[4] = {};
|
float m_cachedInvertedOuter[4] = {};
|
||||||
float m_cachedInvertedInner[4] = {};
|
float m_cachedInvertedInner[4] = {};
|
||||||
|
|||||||
@@ -63,13 +63,17 @@ float smaxSharpA(float a, float b, float k) {
|
|||||||
void main() {
|
void main() {
|
||||||
vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH);
|
vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH);
|
||||||
|
|
||||||
float mergedSdf = 1e10;
|
// 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];
|
||||||
int owner = -2;
|
int owner = -2;
|
||||||
float minDist = 1e10;
|
float minDist = 1e10;
|
||||||
|
|
||||||
for (int i = 0; i < rectCount; i++) {
|
for (int i = 0; i < rectCount; i++) {
|
||||||
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
|
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
|
||||||
vec4 props = rectData[i * 5 + 1]; // radius, offsetX, offsetY, minEig
|
vec4 props =
|
||||||
|
rectData[i * 5 + 1]; // excludeMask(int bits), offsetX, offsetY, minEig
|
||||||
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
|
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
|
||||||
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
|
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
|
||||||
vec4 radii =
|
vec4 radii =
|
||||||
@@ -81,8 +85,10 @@ void main() {
|
|||||||
// AABB early-out: skip rects far from this pixel
|
// AABB early-out: skip rects far from this pixel
|
||||||
vec2 extent = sh.xy + vec2(smoothFactor * 1.5);
|
vec2 extent = sh.xy + vec2(smoothFactor * 1.5);
|
||||||
if (abs(pixel.x - center.x) > extent.x ||
|
if (abs(pixel.x - center.x) > extent.x ||
|
||||||
abs(pixel.y - center.y) > extent.y)
|
abs(pixel.y - center.y) > extent.y) {
|
||||||
|
dArr[i] = 1e10;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply pre-computed inverse deformation to the evaluation point
|
// Apply pre-computed inverse deformation to the evaluation point
|
||||||
mat2 invDeform = mat2(invDm.xy, invDm.zw);
|
mat2 invDeform = mat2(invDm.xy, invDm.zw);
|
||||||
@@ -138,13 +144,38 @@ void main() {
|
|||||||
d *= scale;
|
d *= scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedSdf = smin(mergedSdf, d, smoothFactor);
|
dArr[i] = d;
|
||||||
if (d < smoothFactor && d < minDist) {
|
if (d < smoothFactor && d < minDist) {
|
||||||
minDist = d;
|
minDist = d;
|
||||||
owner = i;
|
owner = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 2: hard-min baseline over all rects.
|
||||||
|
float mergedSdf = 1e10;
|
||||||
|
for (int i = 0; i < rectCount; i++) {
|
||||||
|
mergedSdf = min(mergedSdf, dArr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 3: pair-wise smin contributions, skipping excluded pairs. Pair smin
|
||||||
|
// <= min, so taking the min over all non-excluded pair smins gives the
|
||||||
|
// smoothly-merged SDF.
|
||||||
|
for (int i = 0; i < rectCount; i++) {
|
||||||
|
if (dArr[i] >= 1e9)
|
||||||
|
continue;
|
||||||
|
int excludeMask = floatBitsToInt(rectData[i * 5 + 1].x);
|
||||||
|
for (int j = i + 1; j < rectCount; j++) {
|
||||||
|
if (dArr[j] >= 1e9)
|
||||||
|
continue;
|
||||||
|
if ((excludeMask & (1 << j)) != 0)
|
||||||
|
continue;
|
||||||
|
// smin only deviates from min within smoothFactor
|
||||||
|
if (abs(dArr[i] - dArr[j]) >= smoothFactor)
|
||||||
|
continue;
|
||||||
|
mergedSdf = min(mergedSdf, smin(dArr[i], dArr[j], smoothFactor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasInverted != 0) {
|
if (hasInverted != 0) {
|
||||||
float dOuter = sdBox(pixel, invertedOuter.xy, invertedOuter.zw) - 1.0;
|
float dOuter = sdBox(pixel, invertedOuter.xy, invertedOuter.zw) - 1.0;
|
||||||
float dInner =
|
float dInner =
|
||||||
|
|||||||
@@ -1,217 +1,384 @@
|
|||||||
#include "hyprextras.hpp"
|
#include "hyprextras.hpp"
|
||||||
|
#include "hyprdevices.hpp"
|
||||||
|
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qjsonarray.h>
|
#include <qjsonarray.h>
|
||||||
#include <qlocalsocket.h>
|
#include <qlocalsocket.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qmetatype.h>
|
||||||
|
#include <qregularexpression.h>
|
||||||
#include <qvariant.h>
|
#include <qvariant.h>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(lcHypr, "ZShell.internal.hypr", QtInfoMsg)
|
||||||
|
|
||||||
namespace ZShell::internal::hypr {
|
namespace ZShell::internal::hypr {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static QString luaEscapeString(const QString& s) {
|
||||||
|
QString out;
|
||||||
|
out.reserve(s.size() + 2);
|
||||||
|
out += QLatin1Char('"');
|
||||||
|
|
||||||
|
for (const QChar c : s) {
|
||||||
|
switch (c.unicode()) {
|
||||||
|
case '\\':
|
||||||
|
out += QLatin1String(R"(\\)");
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
out += QLatin1String(R"(\")");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
out += QLatin1String(R"(\n)");
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
out += QLatin1String(R"(\r)");
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
out += QLatin1String(R"(\t)");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out += QLatin1Char('"');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString luaValue(const QVariant& v);
|
||||||
|
|
||||||
|
static QString luaArray(const QVariantList& list) {
|
||||||
|
QStringList parts;
|
||||||
|
parts.reserve(list.size());
|
||||||
|
|
||||||
|
for (const auto& item : list) {
|
||||||
|
parts << luaValue(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString luaArray(const QStringList& list) {
|
||||||
|
QStringList parts;
|
||||||
|
parts.reserve(list.size());
|
||||||
|
|
||||||
|
for (const auto& item : list) {
|
||||||
|
parts << luaEscapeString(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString luaMapFromHash(const QVariantHash& hash) {
|
||||||
|
QStringList parts;
|
||||||
|
parts.reserve(hash.size());
|
||||||
|
|
||||||
|
for (auto it = hash.cbegin(); it != hash.cend(); ++it) {
|
||||||
|
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString luaMap(const QVariantMap& map) {
|
||||||
|
QStringList parts;
|
||||||
|
parts.reserve(map.size());
|
||||||
|
|
||||||
|
for (auto it = map.cbegin(); it != map.cend(); ++it) {
|
||||||
|
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString luaValue(const QVariant& v) {
|
||||||
|
if (!v.isValid() || v.isNull()) {
|
||||||
|
return QLatin1String("nil");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (v.metaType().id()) {
|
||||||
|
case QMetaType::Bool:
|
||||||
|
return v.toBool() ? QLatin1String("true") : QLatin1String("false");
|
||||||
|
|
||||||
|
case QMetaType::Int:
|
||||||
|
case QMetaType::UInt:
|
||||||
|
case QMetaType::LongLong:
|
||||||
|
case QMetaType::ULongLong:
|
||||||
|
case QMetaType::Double:
|
||||||
|
return v.toString();
|
||||||
|
|
||||||
|
case QMetaType::QString:
|
||||||
|
return luaEscapeString(v.toString());
|
||||||
|
|
||||||
|
case QMetaType::QStringList:
|
||||||
|
return luaArray(v.toStringList());
|
||||||
|
|
||||||
|
case QMetaType::QVariantList:
|
||||||
|
return luaArray(v.toList());
|
||||||
|
|
||||||
|
case QMetaType::QVariantMap:
|
||||||
|
return luaMap(v.toMap());
|
||||||
|
|
||||||
|
case QMetaType::QVariantHash:
|
||||||
|
return luaMapFromHash(v.toHash());
|
||||||
|
|
||||||
|
default:
|
||||||
|
return luaEscapeString(v.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString normalizeOptionPath(QString key) {
|
||||||
|
key = key.trimmed();
|
||||||
|
key.replace(QLatin1Char(':'), QLatin1Char('.'));
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString buildHlConfigCall(const QString& key, const QVariant& value) {
|
||||||
|
const auto parts = normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts);
|
||||||
|
if (parts.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString out;
|
||||||
|
out.reserve(32 + key.size() + value.toString().size());
|
||||||
|
out += QLatin1String("hl.config({ ");
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.size(); ++i) {
|
||||||
|
out += parts.at(i);
|
||||||
|
out += QLatin1String(" = ");
|
||||||
|
if (i + 1 < parts.size()) {
|
||||||
|
out += QLatin1String("{ ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out += luaValue(value);
|
||||||
|
|
||||||
|
for (int i = 0; i + 1 < parts.size(); ++i) {
|
||||||
|
out += QLatin1String(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
out += QLatin1String(" })");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
HyprExtras::HyprExtras(QObject* parent)
|
HyprExtras::HyprExtras(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_requestSocket("")
|
, m_requestSocket("")
|
||||||
, m_eventSocket("")
|
, m_eventSocket("")
|
||||||
, m_socket(nullptr)
|
, m_socket(nullptr)
|
||||||
, m_socketValid(false)
|
, m_socketValid(false)
|
||||||
, m_devices(new HyprDevices(this)) {
|
, m_devices(new HyprDevices(this)) {
|
||||||
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
|
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
if (his.isEmpty()) {
|
if (his.isEmpty()) {
|
||||||
qWarning()
|
qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket.";
|
||||||
<< "HyprExtras::HyprExtras: $HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket.";
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
|
auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
|
||||||
if (!QDir(hyprDir).exists()) {
|
if (!QDir(hyprDir).exists()) {
|
||||||
hyprDir = "/tmp/hypr/" + his;
|
hyprDir = QStringLiteral("/tmp/hypr/") + his;
|
||||||
|
|
||||||
if (!QDir(hyprDir).exists()) {
|
if (!QDir(hyprDir).exists()) {
|
||||||
qWarning() << "HyprExtras::HyprExtras: Hyprland socket directory does not exist. Unable to connect to "
|
qCWarning(lcHypr) << "Hyprland socket directory does not exist. Unable to connect to Hyprland socket.";
|
||||||
"Hyprland socket.";
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_requestSocket = hyprDir + "/.socket.sock";
|
m_requestSocket = hyprDir + QStringLiteral("/.socket.sock");
|
||||||
m_eventSocket = hyprDir + "/.socket2.sock";
|
m_eventSocket = hyprDir + QStringLiteral("/.socket2.sock");
|
||||||
|
|
||||||
refreshOptions();
|
refreshOptions();
|
||||||
refreshDevices();
|
refreshDevices();
|
||||||
|
|
||||||
m_socket = new QLocalSocket(this);
|
m_socket = new QLocalSocket(this);
|
||||||
|
|
||||||
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
|
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
|
||||||
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged);
|
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged);
|
||||||
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
|
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
|
||||||
|
|
||||||
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
|
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantHash HyprExtras::options() const {
|
QVariantHash HyprExtras::options() const {
|
||||||
return m_options;
|
return m_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprDevices* HyprExtras::devices() const {
|
HyprDevices* HyprExtras::devices() const {
|
||||||
return m_devices;
|
return m_devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::message(const QString& message) {
|
void HyprExtras::message(const QString& message) {
|
||||||
if (message.isEmpty()) {
|
if (message.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeRequest(message, [](bool success, const QByteArray& res) {
|
makeRequest(message, [](bool success, const QByteArray& res) {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qWarning() << "HyprExtras::message: request error:" << QString::fromUtf8(res);
|
qCWarning(lcHypr) << "message: request error:" << QString::fromUtf8(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::batchMessage(const QStringList& messages) {
|
void HyprExtras::batchMessage(const QStringList& messages) {
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeRequest("[[BATCH]]" + messages.join(";"), [](bool success, const QByteArray& res) {
|
makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
|
||||||
if (!success) {
|
[](bool success, const QByteArray& res) {
|
||||||
qWarning() << "HyprExtras::batchMessage: request error:" << QString::fromUtf8(res);
|
if (!success) {
|
||||||
}
|
qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::applyOptions(const QVariantHash& options) {
|
void HyprExtras::applyOptions(const QVariantHash& options) {
|
||||||
if (options.isEmpty()) {
|
if (options.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString request = "[[BATCH]]";
|
QStringList calls;
|
||||||
for (auto it = options.constBegin(); it != options.constEnd(); ++it) {
|
calls.reserve(options.size());
|
||||||
request += QString("keyword %1 %2;").arg(it.key(), it.value().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
makeRequest(request, [this](bool success, const QByteArray& res) {
|
for (auto it = options.constBegin(); it != options.constEnd(); ++it) {
|
||||||
if (success) {
|
const auto call = buildHlConfigCall(it.key(), it.value());
|
||||||
refreshOptions();
|
if (!call.isEmpty()) {
|
||||||
} else {
|
calls << call;
|
||||||
qWarning() << "HyprExtras::applyOptions: request error" << QString::fromUtf8(res);
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (calls.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeRequest(QStringLiteral("eval ") + calls.join(QLatin1String("; ")), [this](bool success, const QByteArray& res) {
|
||||||
|
if (success) {
|
||||||
|
refreshOptions();
|
||||||
|
} else {
|
||||||
|
qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::refreshOptions() {
|
void HyprExtras::refreshOptions() {
|
||||||
if (!m_optionsRefresh.isNull()) {
|
if (!m_optionsRefresh.isNull()) {
|
||||||
m_optionsRefresh->close();
|
m_optionsRefresh->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_optionsRefresh = makeRequestJson("descriptions", [this](bool success, const QJsonDocument& response) {
|
m_optionsRefresh = makeRequestJson(QStringLiteral("descriptions"), [this](bool success, const QJsonDocument& response) {
|
||||||
m_optionsRefresh.reset();
|
m_optionsRefresh.reset();
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto options = response.array();
|
const auto options = response.array();
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
|
|
||||||
for (const auto& o : std::as_const(options)) {
|
for (const auto& o : std::as_const(options)) {
|
||||||
const auto obj = o.toObject();
|
const auto obj = o.toObject();
|
||||||
const auto key = obj.value("value").toString();
|
const auto key = obj.value(QStringLiteral("value")).toString();
|
||||||
const auto value = obj.value("data").toObject().value("current").toVariant();
|
const auto value = obj.value(QStringLiteral("data")).toObject().value(QStringLiteral("current")).toVariant();
|
||||||
if (m_options.value(key) != value) {
|
|
||||||
dirty = true;
|
|
||||||
m_options.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirty) {
|
if (m_options.value(key) != value) {
|
||||||
emit optionsChanged();
|
dirty = true;
|
||||||
}
|
m_options.insert(key, value);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
emit optionsChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::refreshDevices() {
|
void HyprExtras::refreshDevices() {
|
||||||
if (!m_devicesRefresh.isNull()) {
|
if (!m_devicesRefresh.isNull()) {
|
||||||
m_devicesRefresh->close();
|
m_devicesRefresh->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_devicesRefresh = makeRequestJson("devices", [this](bool success, const QJsonDocument& response) {
|
m_devicesRefresh = makeRequestJson(QStringLiteral("devices"), [this](bool success, const QJsonDocument& response) {
|
||||||
m_devicesRefresh.reset();
|
m_devicesRefresh.reset();
|
||||||
if (success) {
|
if (success) {
|
||||||
m_devices->updateLastIpcObject(response.object());
|
m_devices->updateLastIpcObject(response.object());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
|
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
|
||||||
if (!m_socketValid) {
|
if (!m_socketValid) {
|
||||||
qWarning() << "HyprExtras::socketError: unable to connect to Hyprland event socket:" << error;
|
qCWarning(lcHypr) << "socketError: unable to connect to Hyprland event socket:" << error;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "HyprExtras::socketError: Hyprland event socket error:" << error;
|
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
|
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
|
||||||
if (state == QLocalSocket::UnconnectedState && m_socketValid) {
|
if (state == QLocalSocket::UnconnectedState && m_socketValid) {
|
||||||
qWarning() << "HyprExtras::socketStateChanged: Hyprland event socket disconnected.";
|
qCWarning(lcHypr) << "socketStateChanged: Hyprland event socket disconnected.";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_socketValid = state == QLocalSocket::ConnectedState;
|
m_socketValid = state == QLocalSocket::ConnectedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::readEvent() {
|
void HyprExtras::readEvent() {
|
||||||
while (true) {
|
while (true) {
|
||||||
auto rawEvent = m_socket->readLine();
|
auto rawEvent = m_socket->readLine();
|
||||||
if (rawEvent.isEmpty()) {
|
if (rawEvent.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rawEvent.truncate(rawEvent.length() - 1); // Remove trailing \n
|
rawEvent.truncate(rawEvent.length() - 1);
|
||||||
const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
|
const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
|
||||||
handleEvent(QString::fromUtf8(event));
|
handleEvent(QString::fromUtf8(event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::handleEvent(const QString& event) {
|
void HyprExtras::handleEvent(const QString& event) {
|
||||||
if (event == "configreloaded") {
|
if (event == QStringLiteral("configreloaded")) {
|
||||||
refreshOptions();
|
refreshOptions();
|
||||||
} else if (event == "activelayout") {
|
} else if (event == QStringLiteral("activelayout")) {
|
||||||
refreshDevices();
|
refreshDevices();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprExtras::SocketPtr HyprExtras::makeRequestJson(
|
HyprExtras::SocketPtr HyprExtras::makeRequestJson(
|
||||||
const QString& request, const std::function<void(bool, QJsonDocument)>& callback) {
|
const QString& request, const std::function<void(bool, QJsonDocument)>& callback) {
|
||||||
return makeRequest("j/" + request, [callback](bool success, const QByteArray& response) {
|
return makeRequest(QStringLiteral("j/") + request, [callback](bool success, const QByteArray& response) {
|
||||||
callback(success, QJsonDocument::fromJson(response));
|
callback(success, QJsonDocument::fromJson(response));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprExtras::SocketPtr HyprExtras::makeRequest(
|
HyprExtras::SocketPtr HyprExtras::makeRequest(
|
||||||
const QString& request, const std::function<void(bool, QByteArray)>& callback) {
|
const QString& request, const std::function<void(bool, QByteArray)>& callback) {
|
||||||
if (m_requestSocket.isEmpty()) {
|
if (m_requestSocket.isEmpty()) {
|
||||||
return SocketPtr();
|
return SocketPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto socket = SocketPtr::create(this);
|
auto socket = SocketPtr::create(this);
|
||||||
|
|
||||||
QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() {
|
QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() {
|
||||||
QObject::connect(socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() {
|
QObject::connect(socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() {
|
||||||
const auto response = socket->readAll();
|
const auto response = socket->readAll();
|
||||||
callback(true, std::move(response));
|
callback(true, std::move(response));
|
||||||
socket->close();
|
socket->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
socket->write(request.toUtf8());
|
socket->write(request.toUtf8());
|
||||||
socket->flush();
|
socket->flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) {
|
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) {
|
||||||
qWarning() << "HyprExtras::makeRequest: error making request:" << err << "| request:" << request;
|
qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request;
|
||||||
callback(false, {});
|
callback(false, {});
|
||||||
socket->close();
|
socket->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
socket->connectToServer(m_requestSocket);
|
socket->connectToServer(m_requestSocket);
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZShell::internal::hypr
|
} // namespace ZShell::internal::hypr
|
||||||
|
|||||||
@@ -1,56 +1,60 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "hyprdevices.hpp"
|
|
||||||
#include <qlocalsocket.h>
|
#include <qlocalsocket.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
|
#include <qsharedpointer.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
namespace ZShell::internal::hypr {
|
namespace ZShell::internal::hypr {
|
||||||
|
|
||||||
class HyprExtras : public QObject {
|
class HyprDevices;
|
||||||
Q_OBJECT
|
|
||||||
QML_ELEMENT
|
|
||||||
|
|
||||||
Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged)
|
class HyprExtras : public QObject {
|
||||||
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
|
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)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HyprExtras(QObject* parent = nullptr);
|
explicit HyprExtras(QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] QVariantHash options() const;
|
[[nodiscard]] QVariantHash options() const;
|
||||||
[[nodiscard]] HyprDevices* devices() const;
|
[[nodiscard]] HyprDevices* devices() const;
|
||||||
|
|
||||||
Q_INVOKABLE void message(const QString& message);
|
Q_INVOKABLE void message(const QString& message);
|
||||||
Q_INVOKABLE void batchMessage(const QStringList& messages);
|
Q_INVOKABLE void batchMessage(const QStringList& messages);
|
||||||
Q_INVOKABLE void applyOptions(const QVariantHash& options);
|
Q_INVOKABLE void applyOptions(const QVariantHash& options);
|
||||||
|
|
||||||
Q_INVOKABLE void refreshOptions();
|
Q_INVOKABLE void refreshOptions();
|
||||||
Q_INVOKABLE void refreshDevices();
|
Q_INVOKABLE void refreshDevices();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void optionsChanged();
|
void optionsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SocketPtr = QSharedPointer<QLocalSocket>;
|
using SocketPtr = QSharedPointer<QLocalSocket>;
|
||||||
|
|
||||||
QString m_requestSocket;
|
QString m_requestSocket;
|
||||||
QString m_eventSocket;
|
QString m_eventSocket;
|
||||||
QLocalSocket* m_socket;
|
QLocalSocket* m_socket;
|
||||||
bool m_socketValid;
|
bool m_socketValid;
|
||||||
|
|
||||||
QVariantHash m_options;
|
QVariantHash m_options;
|
||||||
HyprDevices* const m_devices;
|
HyprDevices* const m_devices;
|
||||||
|
|
||||||
SocketPtr m_optionsRefresh;
|
SocketPtr m_optionsRefresh;
|
||||||
SocketPtr m_devicesRefresh;
|
SocketPtr m_devicesRefresh;
|
||||||
|
|
||||||
void socketError(QLocalSocket::LocalSocketError error) const;
|
void socketError(QLocalSocket::LocalSocketError error) const;
|
||||||
void socketStateChanged(QLocalSocket::LocalSocketState state);
|
void socketStateChanged(QLocalSocket::LocalSocketState state);
|
||||||
void readEvent();
|
void readEvent();
|
||||||
void handleEvent(const QString& event);
|
void handleEvent(const QString& event);
|
||||||
|
|
||||||
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback);
|
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback);
|
||||||
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback);
|
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::internal::hypr
|
} // namespace ZShell::internal::hypr
|
||||||
|
|||||||
@@ -9,14 +9,8 @@ _data = {
|
|||||||
"variants": {
|
"variants": {
|
||||||
"type": "multi",
|
"type": "multi",
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"dark": {
|
"dark": {"m3flavor": "mocha", "m3accent": "mauve"},
|
||||||
"m3flavor": "mocha",
|
"light": {"m3flavor": "latte", "m3accent": "mauve"},
|
||||||
"m3accent": "mauve"
|
|
||||||
},
|
|
||||||
"light": {
|
|
||||||
"m3flavor": "latte",
|
|
||||||
"m3accent": "mauve"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"flavors": [
|
"flavors": [
|
||||||
{
|
{
|
||||||
@@ -35,8 +29,8 @@ _data = {
|
|||||||
"m3surfaceContainerHighest": "#dce0e8",
|
"m3surfaceContainerHighest": "#dce0e8",
|
||||||
"m3error": "#d20f39",
|
"m3error": "#d20f39",
|
||||||
"m3warning": "#fe640b",
|
"m3warning": "#fe640b",
|
||||||
"m3info": "#1e66f5"
|
"m3info": "#1e66f5",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "frappe",
|
"id": "frappe",
|
||||||
@@ -54,8 +48,8 @@ _data = {
|
|||||||
"m3surfaceContainerHighest": "#232634",
|
"m3surfaceContainerHighest": "#232634",
|
||||||
"m3error": "#e78284",
|
"m3error": "#e78284",
|
||||||
"m3warning": "#ef9f76",
|
"m3warning": "#ef9f76",
|
||||||
"m3info": "#8caaee"
|
"m3info": "#8caaee",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "macchiato",
|
"id": "macchiato",
|
||||||
@@ -73,8 +67,8 @@ _data = {
|
|||||||
"m3surfaceContainerHighest": "#181926",
|
"m3surfaceContainerHighest": "#181926",
|
||||||
"m3error": "#ed8796",
|
"m3error": "#ed8796",
|
||||||
"m3warning": "#f5a97f",
|
"m3warning": "#f5a97f",
|
||||||
"m3info": "#8aadf4"
|
"m3info": "#8aadf4",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mocha",
|
"id": "mocha",
|
||||||
@@ -92,9 +86,9 @@ _data = {
|
|||||||
"m3surfaceContainerHighest": "#11111b",
|
"m3surfaceContainerHighest": "#11111b",
|
||||||
"m3error": "#f38ba8",
|
"m3error": "#f38ba8",
|
||||||
"m3warning": "#fab387",
|
"m3warning": "#fab387",
|
||||||
"m3info": "#89b4fa"
|
"m3info": "#89b4fa",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"accents": [
|
"accents": [
|
||||||
{
|
{
|
||||||
@@ -105,29 +99,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#e1a99d",
|
"m3primaryContainer": "#e1a99d",
|
||||||
"m3secondary": "#d8c7c4",
|
"m3secondary": "#d8c7c4",
|
||||||
"m3surfaceTint": "#e1a99d"
|
"m3surfaceTint": "#e1a99d",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#f2d5cf",
|
"m3primary": "#f2d5cf",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#b8a5a6",
|
"m3primaryContainer": "#b8a5a6",
|
||||||
"m3secondary": "#a2748b",
|
"m3secondary": "#a2748b",
|
||||||
"m3surfaceTint": "#b8a5a6"
|
"m3surfaceTint": "#b8a5a6",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#f4dbd6",
|
"m3primary": "#f4dbd6",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b6a6a7",
|
"m3primaryContainer": "#b6a6a7",
|
||||||
"m3secondary": "#9f6f8d",
|
"m3secondary": "#9f6f8d",
|
||||||
"m3surfaceTint": "#b6a6a7"
|
"m3surfaceTint": "#b6a6a7",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#f5e0dc",
|
"m3primary": "#f5e0dc",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#b5a6a8",
|
"m3primaryContainer": "#b5a6a8",
|
||||||
"m3secondary": "#9d6d87",
|
"m3secondary": "#9d6d87",
|
||||||
"m3surfaceTint": "#b5a6a8"
|
"m3surfaceTint": "#b5a6a8",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "flamingo",
|
"id": "flamingo",
|
||||||
@@ -137,29 +131,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#e29c9d",
|
"m3primaryContainer": "#e29c9d",
|
||||||
"m3secondary": "#d7c3c4",
|
"m3secondary": "#d7c3c4",
|
||||||
"m3surfaceTint": "#e29c9d"
|
"m3surfaceTint": "#e29c9d",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#eebebe",
|
"m3primary": "#eebebe",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#b5949a",
|
"m3primaryContainer": "#b5949a",
|
||||||
"m3secondary": "#9d6b80",
|
"m3secondary": "#9d6b80",
|
||||||
"m3surfaceTint": "#b5949a"
|
"m3surfaceTint": "#b5949a",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#f0c6c6",
|
"m3primary": "#f0c6c6",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b3979c",
|
"m3primaryContainer": "#b3979c",
|
||||||
"m3secondary": "#996780",
|
"m3secondary": "#996780",
|
||||||
"m3surfaceTint": "#b3979c"
|
"m3surfaceTint": "#b3979c",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#f2cdcd",
|
"m3primary": "#f2cdcd",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#b3999e",
|
"m3primaryContainer": "#b3999e",
|
||||||
"m3secondary": "#98667c",
|
"m3secondary": "#98667c",
|
||||||
"m3surfaceTint": "#b3999e"
|
"m3surfaceTint": "#b3999e",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pink",
|
"id": "pink",
|
||||||
@@ -169,29 +163,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#eb9bd7",
|
"m3primaryContainer": "#eb9bd7",
|
||||||
"m3secondary": "#d9c7d5",
|
"m3secondary": "#d9c7d5",
|
||||||
"m3surfaceTint": "#eb9bd7"
|
"m3surfaceTint": "#eb9bd7",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#f4b8e4",
|
"m3primary": "#f4b8e4",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#b990b5",
|
"m3primaryContainer": "#b990b5",
|
||||||
"m3secondary": "#996e9e",
|
"m3secondary": "#996e9e",
|
||||||
"m3surfaceTint": "#b990b5"
|
"m3surfaceTint": "#b990b5",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#f5bde6",
|
"m3primary": "#f5bde6",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b791b2",
|
"m3primaryContainer": "#b791b2",
|
||||||
"m3secondary": "#95689a",
|
"m3secondary": "#95689a",
|
||||||
"m3surfaceTint": "#b791b2"
|
"m3surfaceTint": "#b791b2",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#f5c2e7",
|
"m3primary": "#f5c2e7",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#b591b0",
|
"m3primaryContainer": "#b591b0",
|
||||||
"m3secondary": "#966597",
|
"m3secondary": "#966597",
|
||||||
"m3surfaceTint": "#b591b0"
|
"m3surfaceTint": "#b591b0",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mauve",
|
"id": "mauve",
|
||||||
@@ -201,29 +195,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#a670f1",
|
"m3primaryContainer": "#a670f1",
|
||||||
"m3secondary": "#c2b8d0",
|
"m3secondary": "#c2b8d0",
|
||||||
"m3surfaceTint": "#a670f1"
|
"m3surfaceTint": "#a670f1",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#ca9ee6",
|
"m3primary": "#ca9ee6",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#9c7eb6",
|
"m3primaryContainer": "#9c7eb6",
|
||||||
"m3secondary": "#7d6799",
|
"m3secondary": "#7d6799",
|
||||||
"m3surfaceTint": "#9c7eb6"
|
"m3surfaceTint": "#9c7eb6",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#c6a0f6",
|
"m3primary": "#c6a0f6",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#967cbe",
|
"m3primaryContainer": "#967cbe",
|
||||||
"m3secondary": "#766597",
|
"m3secondary": "#766597",
|
||||||
"m3surfaceTint": "#967cbe"
|
"m3surfaceTint": "#967cbe",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#cba6f7",
|
"m3primary": "#cba6f7",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#977ebb",
|
"m3primaryContainer": "#977ebb",
|
||||||
"m3secondary": "#756294",
|
"m3secondary": "#756294",
|
||||||
"m3surfaceTint": "#977ebb"
|
"m3surfaceTint": "#977ebb",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "red",
|
"id": "red",
|
||||||
@@ -233,29 +227,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#da5371",
|
"m3primaryContainer": "#da5371",
|
||||||
"m3secondary": "#c0a0a8",
|
"m3secondary": "#c0a0a8",
|
||||||
"m3surfaceTint": "#da5371"
|
"m3surfaceTint": "#da5371",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#e78284",
|
"m3primary": "#e78284",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#b06a72",
|
"m3primaryContainer": "#b06a72",
|
||||||
"m3secondary": "#8b5d66",
|
"m3secondary": "#8b5d66",
|
||||||
"m3surfaceTint": "#b06a72"
|
"m3surfaceTint": "#b06a72",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#ed8796",
|
"m3primary": "#ed8796",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b16b7a",
|
"m3primaryContainer": "#b16b7a",
|
||||||
"m3secondary": "#865a69",
|
"m3secondary": "#865a69",
|
||||||
"m3surfaceTint": "#b16b7a"
|
"m3surfaceTint": "#b16b7a",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#f38ba8",
|
"m3primary": "#f38ba8",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#b46b84",
|
"m3primaryContainer": "#b46b84",
|
||||||
"m3secondary": "#85596b",
|
"m3secondary": "#85596b",
|
||||||
"m3surfaceTint": "#b46b84"
|
"m3surfaceTint": "#b46b84",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "maroon",
|
"id": "maroon",
|
||||||
@@ -265,29 +259,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#e87883",
|
"m3primaryContainer": "#e87883",
|
||||||
"m3secondary": "#cfb7ba",
|
"m3secondary": "#cfb7ba",
|
||||||
"m3surfaceTint": "#e87883"
|
"m3surfaceTint": "#e87883",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#ea999c",
|
"m3primary": "#ea999c",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#b27a83",
|
"m3primaryContainer": "#b27a83",
|
||||||
"m3secondary": "#92626f",
|
"m3secondary": "#92626f",
|
||||||
"m3surfaceTint": "#b27a83"
|
"m3surfaceTint": "#b27a83",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#ee99a0",
|
"m3primary": "#ee99a0",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b27781",
|
"m3primaryContainer": "#b27781",
|
||||||
"m3secondary": "#8c5e6c",
|
"m3secondary": "#8c5e6c",
|
||||||
"m3surfaceTint": "#b27781"
|
"m3surfaceTint": "#b27781",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#eba0ac",
|
"m3primary": "#eba0ac",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#ae7987",
|
"m3primaryContainer": "#ae7987",
|
||||||
"m3secondary": "#895b6c",
|
"m3secondary": "#895b6c",
|
||||||
"m3surfaceTint": "#ae7987"
|
"m3surfaceTint": "#ae7987",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "peach",
|
"id": "peach",
|
||||||
@@ -297,29 +291,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#f98e51",
|
"m3primaryContainer": "#f98e51",
|
||||||
"m3secondary": "#c9b7ad",
|
"m3secondary": "#c9b7ad",
|
||||||
"m3surfaceTint": "#f98e51"
|
"m3surfaceTint": "#f98e51",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#ef9f76",
|
"m3primary": "#ef9f76",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#b67f68",
|
"m3primaryContainer": "#b67f68",
|
||||||
"m3secondary": "#8f6a5f",
|
"m3secondary": "#8f6a5f",
|
||||||
"m3surfaceTint": "#b67f68"
|
"m3surfaceTint": "#b67f68",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#f5a97f",
|
"m3primary": "#f5a97f",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b7836a",
|
"m3primaryContainer": "#b7836a",
|
||||||
"m3secondary": "#8c695e",
|
"m3secondary": "#8c695e",
|
||||||
"m3surfaceTint": "#b7836a"
|
"m3surfaceTint": "#b7836a",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#fab387",
|
"m3primary": "#fab387",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#b8876d",
|
"m3primaryContainer": "#b8876d",
|
||||||
"m3secondary": "#8b6a5d",
|
"m3secondary": "#8b6a5d",
|
||||||
"m3surfaceTint": "#b8876d"
|
"m3surfaceTint": "#b8876d",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "yellow",
|
"id": "yellow",
|
||||||
@@ -329,29 +323,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#e4ac5d",
|
"m3primaryContainer": "#e4ac5d",
|
||||||
"m3secondary": "#c6baaa",
|
"m3secondary": "#c6baaa",
|
||||||
"m3surfaceTint": "#e4ac5d"
|
"m3surfaceTint": "#e4ac5d",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#e5c890",
|
"m3primary": "#e5c890",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#af9b7a",
|
"m3primaryContainer": "#af9b7a",
|
||||||
"m3secondary": "#948062",
|
"m3secondary": "#948062",
|
||||||
"m3surfaceTint": "#af9b7a"
|
"m3surfaceTint": "#af9b7a",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#eed49f",
|
"m3primary": "#eed49f",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#b2a181",
|
"m3primaryContainer": "#b2a181",
|
||||||
"m3secondary": "#947e62",
|
"m3secondary": "#947e62",
|
||||||
"m3surfaceTint": "#b2a181"
|
"m3surfaceTint": "#b2a181",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#f9e2af",
|
"m3primary": "#f9e2af",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#b8a889",
|
"m3primaryContainer": "#b8a889",
|
||||||
"m3secondary": "#978265",
|
"m3secondary": "#978265",
|
||||||
"m3surfaceTint": "#b8a889"
|
"m3surfaceTint": "#b8a889",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "green",
|
"id": "green",
|
||||||
@@ -361,29 +355,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#74b867",
|
"m3primaryContainer": "#74b867",
|
||||||
"m3secondary": "#9fbd9b",
|
"m3secondary": "#9fbd9b",
|
||||||
"m3surfaceTint": "#74b867"
|
"m3surfaceTint": "#74b867",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#a6d189",
|
"m3primary": "#a6d189",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#83a275",
|
"m3primaryContainer": "#83a275",
|
||||||
"m3secondary": "#648e5e",
|
"m3secondary": "#648e5e",
|
||||||
"m3surfaceTint": "#83a275"
|
"m3surfaceTint": "#83a275",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#a6da95",
|
"m3primary": "#a6da95",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#80a57a",
|
"m3primaryContainer": "#80a57a",
|
||||||
"m3secondary": "#5c8a61",
|
"m3secondary": "#5c8a61",
|
||||||
"m3surfaceTint": "#80a57a"
|
"m3surfaceTint": "#80a57a",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#a6e3a1",
|
"m3primary": "#a6e3a1",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#7ea87f",
|
"m3primaryContainer": "#7ea87f",
|
||||||
"m3secondary": "#5b8964",
|
"m3secondary": "#5b8964",
|
||||||
"m3surfaceTint": "#7ea87f"
|
"m3surfaceTint": "#7ea87f",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "teal",
|
"id": "teal",
|
||||||
@@ -393,29 +387,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#57aeb4",
|
"m3primaryContainer": "#57aeb4",
|
||||||
"m3secondary": "#93b4b7",
|
"m3secondary": "#93b4b7",
|
||||||
"m3surfaceTint": "#57aeb4"
|
"m3surfaceTint": "#57aeb4",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#81c8be",
|
"m3primary": "#81c8be",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#699b9a",
|
"m3primaryContainer": "#699b9a",
|
||||||
"m3secondary": "#588084",
|
"m3secondary": "#588084",
|
||||||
"m3surfaceTint": "#699b9a"
|
"m3surfaceTint": "#699b9a",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#8bd5ca",
|
"m3primary": "#8bd5ca",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#6da29f",
|
"m3primaryContainer": "#6da29f",
|
||||||
"m3secondary": "#577e83",
|
"m3secondary": "#577e83",
|
||||||
"m3surfaceTint": "#6da29f"
|
"m3surfaceTint": "#6da29f",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#94e2d5",
|
"m3primary": "#94e2d5",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#71a8a4",
|
"m3primaryContainer": "#71a8a4",
|
||||||
"m3secondary": "#588284",
|
"m3secondary": "#588284",
|
||||||
"m3surfaceTint": "#71a8a4"
|
"m3surfaceTint": "#71a8a4",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sky",
|
"id": "sky",
|
||||||
@@ -425,29 +419,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#4abcea",
|
"m3primaryContainer": "#4abcea",
|
||||||
"m3secondary": "#a4b9c2",
|
"m3secondary": "#a4b9c2",
|
||||||
"m3surfaceTint": "#4abcea"
|
"m3surfaceTint": "#4abcea",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#99d1db",
|
"m3primary": "#99d1db",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#79a2af",
|
"m3primaryContainer": "#79a2af",
|
||||||
"m3secondary": "#628494",
|
"m3secondary": "#628494",
|
||||||
"m3surfaceTint": "#79a2af"
|
"m3surfaceTint": "#79a2af",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#91d7e3",
|
"m3primary": "#91d7e3",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#71a3b0",
|
"m3primaryContainer": "#71a3b0",
|
||||||
"m3secondary": "#5e7e8c",
|
"m3secondary": "#5e7e8c",
|
||||||
"m3surfaceTint": "#71a3b0"
|
"m3surfaceTint": "#71a3b0",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#89dceb",
|
"m3primary": "#89dceb",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#69a3b3",
|
"m3primaryContainer": "#69a3b3",
|
||||||
"m3secondary": "#5a7b88",
|
"m3secondary": "#5a7b88",
|
||||||
"m3surfaceTint": "#69a3b3"
|
"m3surfaceTint": "#69a3b3",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sapphire",
|
"id": "sapphire",
|
||||||
@@ -457,29 +451,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#5db8c8",
|
"m3primaryContainer": "#5db8c8",
|
||||||
"m3secondary": "#9eb9be",
|
"m3secondary": "#9eb9be",
|
||||||
"m3surfaceTint": "#5db8c8"
|
"m3surfaceTint": "#5db8c8",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#85c1dc",
|
"m3primary": "#85c1dc",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#6b96af",
|
"m3primaryContainer": "#6b96af",
|
||||||
"m3secondary": "#5e7b8e",
|
"m3secondary": "#5e7b8e",
|
||||||
"m3surfaceTint": "#6b96af"
|
"m3surfaceTint": "#6b96af",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#7dc4e4",
|
"m3primary": "#7dc4e4",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#6396b1",
|
"m3primaryContainer": "#6396b1",
|
||||||
"m3secondary": "#5a7486",
|
"m3secondary": "#5a7486",
|
||||||
"m3surfaceTint": "#6396b1"
|
"m3surfaceTint": "#6396b1",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#74c7ec",
|
"m3primary": "#74c7ec",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#5a95b4",
|
"m3primaryContainer": "#5a95b4",
|
||||||
"m3secondary": "#567080",
|
"m3secondary": "#567080",
|
||||||
"m3surfaceTint": "#5a95b4"
|
"m3surfaceTint": "#5a95b4",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "blue",
|
"id": "blue",
|
||||||
@@ -489,29 +483,29 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#5c90f5",
|
"m3primaryContainer": "#5c90f5",
|
||||||
"m3secondary": "#b1bacb",
|
"m3secondary": "#b1bacb",
|
||||||
"m3surfaceTint": "#5c90f5"
|
"m3surfaceTint": "#5c90f5",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#8caaee",
|
"m3primary": "#8caaee",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#7086bc",
|
"m3primaryContainer": "#7086bc",
|
||||||
"m3secondary": "#637195",
|
"m3secondary": "#637195",
|
||||||
"m3surfaceTint": "#7086bc"
|
"m3surfaceTint": "#7086bc",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#8aadf4",
|
"m3primary": "#8aadf4",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#6c85bc",
|
"m3primaryContainer": "#6c85bc",
|
||||||
"m3secondary": "#5f6d8f",
|
"m3secondary": "#5f6d8f",
|
||||||
"m3surfaceTint": "#6c85bc"
|
"m3surfaceTint": "#6c85bc",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#89b4fa",
|
"m3primary": "#89b4fa",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#6987bd",
|
"m3primaryContainer": "#6987bd",
|
||||||
"m3secondary": "#5d6c8b",
|
"m3secondary": "#5d6c8b",
|
||||||
"m3surfaceTint": "#6987bd"
|
"m3surfaceTint": "#6987bd",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "lavender",
|
"id": "lavender",
|
||||||
@@ -521,30 +515,30 @@ _data = {
|
|||||||
"m3primaryText": "#eff1f5",
|
"m3primaryText": "#eff1f5",
|
||||||
"m3primaryContainer": "#97a7fb",
|
"m3primaryContainer": "#97a7fb",
|
||||||
"m3secondary": "#cdcfdd",
|
"m3secondary": "#cdcfdd",
|
||||||
"m3surfaceTint": "#97a7fb"
|
"m3surfaceTint": "#97a7fb",
|
||||||
},
|
},
|
||||||
"frappe": {
|
"frappe": {
|
||||||
"m3primary": "#babbf1",
|
"m3primary": "#babbf1",
|
||||||
"m3primaryText": "#303446",
|
"m3primaryText": "#303446",
|
||||||
"m3primaryContainer": "#9192be",
|
"m3primaryContainer": "#9192be",
|
||||||
"m3secondary": "#7175a1",
|
"m3secondary": "#7175a1",
|
||||||
"m3surfaceTint": "#9192be"
|
"m3surfaceTint": "#9192be",
|
||||||
},
|
},
|
||||||
"macchiato": {
|
"macchiato": {
|
||||||
"m3primary": "#b7bdf8",
|
"m3primary": "#b7bdf8",
|
||||||
"m3primaryText": "#24273a",
|
"m3primaryText": "#24273a",
|
||||||
"m3primaryContainer": "#8b91bf",
|
"m3primaryContainer": "#8b91bf",
|
||||||
"m3secondary": "#6b709d",
|
"m3secondary": "#6b709d",
|
||||||
"m3surfaceTint": "#8b91bf"
|
"m3surfaceTint": "#8b91bf",
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"m3primary": "#b4befe",
|
"m3primary": "#b4befe",
|
||||||
"m3primaryText": "#1e1e2e",
|
"m3primaryText": "#1e1e2e",
|
||||||
"m3primaryContainer": "#878ec0",
|
"m3primaryContainer": "#878ec0",
|
||||||
"m3secondary": "#676d99",
|
"m3secondary": "#676d99",
|
||||||
"m3surfaceTint": "#878ec0"
|
"m3surfaceTint": "#878ec0",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import json
|
# import json
|
||||||
import typer
|
# import typer
|
||||||
from zshell.assets.schemes.catppuccin import catppuccin
|
# from zshell.assets.schemes.catppuccin import catppuccin
|
||||||
|
#
|
||||||
|
# app = typer.Typer()
|
||||||
|
#
|
||||||
|
# SCHEMES = catppuccin.variants.flavors
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @app.command()
|
||||||
|
# def set():
|
||||||
|
|
||||||
app = typer.Typer()
|
# TODO: Currently unsused
|
||||||
|
|
||||||
SCHEMES = catppuccin.variants.flavors
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def set():
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import typer
|
# import typer
|
||||||
import subprocess
|
# import subprocess
|
||||||
|
#
|
||||||
|
# from typing import Optional
|
||||||
|
#
|
||||||
|
# app = typer.Typer()
|
||||||
|
#
|
||||||
|
# RECORDER = "gpu-screen-recorder"
|
||||||
|
# HOME = str(os.getenv("HOME"))
|
||||||
|
# CONFIG = Path(HOME + "/.config/zshell/config.json")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @app.command()
|
||||||
|
# def start():
|
||||||
|
|
||||||
from typing import Optional
|
# TODO: Currently unused
|
||||||
|
|
||||||
app = typer.Typer()
|
|
||||||
|
|
||||||
RECORDER = "gpu-screen-recorder"
|
|
||||||
HOME = str(os.getenv("HOME"))
|
|
||||||
CONFIG = Path(HOME + "/.config/zshell/config.json")
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def start():
|
|
||||||
|
|||||||
@@ -23,64 +23,71 @@ app = typer.Typer()
|
|||||||
@app.command()
|
@app.command()
|
||||||
def generate(
|
def generate(
|
||||||
# image inputs (optional - used for image mode)
|
# image inputs (optional - used for image mode)
|
||||||
image_path: Optional[Path] = typer.Option(
|
image_path: Optional[Path] = typer.Option(None, help="Path to source image. Required for image mode."),
|
||||||
None, help="Path to source image. Required for image mode."),
|
|
||||||
scheme: Optional[str] = typer.Option(
|
scheme: Optional[str] = typer.Option(
|
||||||
None, help="Color scheme algorithm to use for image mode. Ignored in preset mode."),
|
None, help="Color scheme algorithm to use for image mode. Ignored in preset mode."
|
||||||
|
),
|
||||||
# preset inputs (optional - used for preset mode)
|
# preset inputs (optional - used for preset mode)
|
||||||
preset: Optional[str] = typer.Option(
|
preset: Optional[str] = typer.Option(
|
||||||
None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"),
|
None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"
|
||||||
mode: Optional[str] = typer.Option(
|
),
|
||||||
None, help="Mode of the preset scheme (dark or light)."),
|
mode: Optional[str] = typer.Option(None, help="Mode of the preset scheme (dark or light)."),
|
||||||
):
|
):
|
||||||
|
|
||||||
HOME = str(os.getenv("HOME"))
|
HOME = str(os.getenv("HOME"))
|
||||||
OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json")
|
OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json")
|
||||||
SEQ_STATE = Path(HOME + "/.local/state/zshell/sequences.txt")
|
SEQ_STATE = Path(HOME + "/.local/state/zshell/sequences.txt")
|
||||||
THUMB_PATH = Path(HOME +
|
THUMB_PATH = Path(HOME + "/.cache/zshell/imagecache/thumbnail.jpg")
|
||||||
"/.cache/zshell/imagecache/thumbnail.jpg")
|
WALL_DIR_PATH = Path(HOME + "/.local/state/zshell/wallpaper_path.json")
|
||||||
WALL_DIR_PATH = Path(HOME +
|
|
||||||
"/.local/state/zshell/wallpaper_path.json")
|
|
||||||
|
|
||||||
TEMPLATE_DIR = Path(HOME + "/.config/zshell/templates")
|
TEMPLATE_DIR = Path(HOME + "/.config/zshell/templates")
|
||||||
WALL_PATH = Path()
|
WALL_PATH = Path()
|
||||||
CONFIG = Path(HOME + "/.config/zshell/config.json")
|
CONFIG = Path(HOME + "/.config/zshell/config.json")
|
||||||
|
|
||||||
if preset is not None and image_path is not None:
|
if preset is not None and image_path is not None:
|
||||||
raise typer.BadParameter(
|
raise typer.BadParameter("Use either --image-path or --preset, not both.")
|
||||||
"Use either --image-path or --preset, not both.")
|
|
||||||
|
|
||||||
def get_scheme_class(scheme_name: str):
|
def get_scheme_class(scheme_name: str):
|
||||||
match scheme_name:
|
match scheme_name:
|
||||||
case "fruit-salad":
|
case "fruit-salad":
|
||||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
||||||
|
|
||||||
return SchemeFruitSalad
|
return SchemeFruitSalad
|
||||||
case "expressive":
|
case "expressive":
|
||||||
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
|
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
|
||||||
|
|
||||||
return SchemeExpressive
|
return SchemeExpressive
|
||||||
case "monochrome":
|
case "monochrome":
|
||||||
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
|
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
|
||||||
|
|
||||||
return SchemeMonochrome
|
return SchemeMonochrome
|
||||||
case "rainbow":
|
case "rainbow":
|
||||||
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
|
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
|
||||||
|
|
||||||
return SchemeRainbow
|
return SchemeRainbow
|
||||||
case "tonal-spot":
|
case "tonal-spot":
|
||||||
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
||||||
|
|
||||||
return SchemeTonalSpot
|
return SchemeTonalSpot
|
||||||
case "neutral":
|
case "neutral":
|
||||||
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
|
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
|
||||||
|
|
||||||
return SchemeNeutral
|
return SchemeNeutral
|
||||||
case "fidelity":
|
case "fidelity":
|
||||||
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity
|
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity
|
||||||
|
|
||||||
return SchemeFidelity
|
return SchemeFidelity
|
||||||
case "content":
|
case "content":
|
||||||
from materialyoucolor.scheme.scheme_content import SchemeContent
|
from materialyoucolor.scheme.scheme_content import SchemeContent
|
||||||
|
|
||||||
return SchemeContent
|
return SchemeContent
|
||||||
case "vibrant":
|
case "vibrant":
|
||||||
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant
|
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant
|
||||||
|
|
||||||
return SchemeVibrant
|
return SchemeVibrant
|
||||||
case _:
|
case _:
|
||||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
||||||
|
|
||||||
return SchemeFruitSalad
|
return SchemeFruitSalad
|
||||||
|
|
||||||
def hex_to_hct(hex_color: str) -> Hct:
|
def hex_to_hct(hex_color: str) -> Hct:
|
||||||
@@ -163,16 +170,11 @@ def generate(
|
|||||||
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
|
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
|
||||||
diff = difference_degrees(from_hct.hue, to_hct.hue)
|
diff = difference_degrees(from_hct.hue, to_hct.hue)
|
||||||
rotation = min(diff * 0.8, 100)
|
rotation = min(diff * 0.8, 100)
|
||||||
output_hue = sanitize_degrees_double(
|
output_hue = sanitize_degrees_double(from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue))
|
||||||
from_hct.hue
|
|
||||||
+ rotation * rotation_direction(from_hct.hue, to_hct.hue)
|
|
||||||
)
|
|
||||||
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
|
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
|
||||||
return Hct.from_hct(output_hue, from_hct.chroma, tone)
|
return Hct.from_hct(output_hue, from_hct.chroma, tone)
|
||||||
|
|
||||||
def terminal_palette(
|
def terminal_palette(colors: dict[str, str], mode: str, variant: str) -> dict[str, str]:
|
||||||
colors: dict[str, str], mode: str, variant: str
|
|
||||||
) -> dict[str, str]:
|
|
||||||
light = mode.lower() == "light"
|
light = mode.lower() == "light"
|
||||||
|
|
||||||
key_hex = (
|
key_hex = (
|
||||||
@@ -307,7 +309,7 @@ def generate(
|
|||||||
"seed": seed.to_int(),
|
"seed": seed.to_int(),
|
||||||
"flavor": flavor,
|
"flavor": flavor,
|
||||||
"variant": variant,
|
"variant": variant,
|
||||||
"colors": colors
|
"colors": colors,
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v in colors.items():
|
for k, v in colors.items():
|
||||||
@@ -349,7 +351,7 @@ def generate(
|
|||||||
|
|
||||||
def tmux_wrap_sequences(seq: str) -> str:
|
def tmux_wrap_sequences(seq: str) -> str:
|
||||||
ESC = "\x1b"
|
ESC = "\x1b"
|
||||||
return f"{ESC}Ptmux;{seq.replace(ESC, ESC+ESC)}{ESC}\\"
|
return f"{ESC}Ptmux;{seq.replace(ESC, ESC + ESC)}{ESC}\\"
|
||||||
|
|
||||||
def parse_output_directive(first_line: str) -> Optional[Path]:
|
def parse_output_directive(first_line: str) -> Optional[Path]:
|
||||||
s = first_line.strip()
|
s = first_line.strip()
|
||||||
@@ -406,8 +408,7 @@ def generate(
|
|||||||
template = env.from_string(body)
|
template = env.from_string(body)
|
||||||
text = template.render(**context)
|
text = template.render(**context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(
|
raise RuntimeError(f"Template render failed for '{rel}': {e}") from e
|
||||||
f"Template render failed for '{rel}': {e}") from e
|
|
||||||
|
|
||||||
out_path.write_text(text, encoding="utf-8")
|
out_path.write_text(text, encoding="utf-8")
|
||||||
|
|
||||||
@@ -435,18 +436,13 @@ def generate(
|
|||||||
try:
|
try:
|
||||||
return PRESETS[name].primary
|
return PRESETS[name].primary
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise typer.BadParameter(
|
raise typer.BadParameter(f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}")
|
||||||
f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}")
|
|
||||||
|
|
||||||
def generate_color_scheme(seed: Hct, mode: str, scheme_class) -> dict[str, str]:
|
def generate_color_scheme(seed: Hct, mode: str, scheme_class) -> dict[str, str]:
|
||||||
|
|
||||||
is_dark = mode.lower() == "dark"
|
is_dark = mode.lower() == "dark"
|
||||||
|
|
||||||
scheme = scheme_class(
|
scheme = scheme_class(seed, is_dark, 0.0)
|
||||||
seed,
|
|
||||||
is_dark,
|
|
||||||
0.0
|
|
||||||
)
|
|
||||||
|
|
||||||
color_dict = {}
|
color_dict = {}
|
||||||
for color in vars(MaterialDynamicColors).keys():
|
for color in vars(MaterialDynamicColors).keys():
|
||||||
@@ -499,7 +495,7 @@ def generate(
|
|||||||
"mode": effective_mode,
|
"mode": effective_mode,
|
||||||
"variant": scheme,
|
"variant": scheme,
|
||||||
"colors": colors,
|
"colors": colors,
|
||||||
"seed": seed.to_int()
|
"seed": seed.to_int(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if TEMPLATE_DIR is not None:
|
if TEMPLATE_DIR is not None:
|
||||||
@@ -511,7 +507,7 @@ def generate(
|
|||||||
wallpaper_path=wp,
|
wallpaper_path=wp,
|
||||||
name=name,
|
name=name,
|
||||||
flavor=flavor,
|
flavor=flavor,
|
||||||
variant=scheme
|
variant=scheme,
|
||||||
)
|
)
|
||||||
|
|
||||||
rendered = render_all_templates(
|
rendered = render_all_templates(
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ app = typer.Typer()
|
|||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def start():
|
def start():
|
||||||
subprocess.run(args + ["ipc"] + ["call"] +
|
subprocess.run(args + ["ipc"] + ["call"] + ["picker"] + ["open"], check=True)
|
||||||
["picker"] + ["open"], check=True)
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def start_freeze():
|
def start_freeze():
|
||||||
subprocess.run(args + ["ipc"] + ["call"] +
|
subprocess.run(args + ["ipc"] + ["call"] + ["picker"] + ["openFreeze"], check=True)
|
||||||
["picker"] + ["openFreeze"], check=True)
|
|
||||||
|
|||||||
@@ -33,5 +33,4 @@ def lock():
|
|||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def call(target: str, method: str, method_args: list[str] = typer.Argument(None)):
|
def call(target: str, method: str, method_args: list[str] = typer.Argument(None)):
|
||||||
subprocess.run(args + ["ipc"] + ["call"] + [target] +
|
subprocess.run(args + ["ipc"] + ["call"] + [target] + [method] + method_args, check=True)
|
||||||
[method] + method_args, check=True)
|
|
||||||
|
|||||||
@@ -12,29 +12,28 @@ app = typer.Typer()
|
|||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def set(wallpaper: Path):
|
def set(wallpaper: Path):
|
||||||
subprocess.run(args + ["ipc"] + ["call"] +
|
subprocess.run(args + ["ipc"] + ["call"] + ["wallpaper"] + ["set"] + [wallpaper], check=True)
|
||||||
["wallpaper"] + ["set"] + [wallpaper], check=True)
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def lockscreen(
|
def lockscreen(
|
||||||
input_image: Annotated[
|
input_image: Annotated[
|
||||||
Path,
|
Path,
|
||||||
typer.Option(),
|
typer.Option(),
|
||||||
],
|
],
|
||||||
output_path: Annotated[
|
output_path: Annotated[
|
||||||
Path,
|
Path,
|
||||||
typer.Option(),
|
typer.Option(),
|
||||||
],
|
],
|
||||||
blur_amount: int = 20
|
blur_amount: int = 20,
|
||||||
):
|
):
|
||||||
img = Image.open(input_image)
|
img = Image.open(input_image)
|
||||||
size = img.size
|
size = img.size
|
||||||
if (blur_amount == 0):
|
if blur_amount == 0:
|
||||||
img.save(output_path, "PNG")
|
img.save(output_path, "PNG")
|
||||||
return
|
return
|
||||||
|
|
||||||
if (size[0] < 3840 or size[1] < 2160):
|
if size[0] < 3840 or size[1] < 2160:
|
||||||
img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST)
|
img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST)
|
||||||
else:
|
else:
|
||||||
img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST)
|
img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST)
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ["scripts/fzf.js", "scripts/fuzzysort.js"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.{js,jsx,ts,tsx,mjs,cjs}"],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2021,
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
linterOptions: {
|
||||||
|
reportUnusedDisableDirectives: true,
|
||||||
|
},
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
File diff suppressed because it is too large
Load Diff
+95
-95
@@ -3,141 +3,141 @@
|
|||||||
// Translated to Js from Cython with an LLM and reviewed
|
// Translated to Js from Cython with an LLM and reviewed
|
||||||
|
|
||||||
function min3(a, b, c) {
|
function min3(a, b, c) {
|
||||||
return a < b && a < c ? a : b < c ? b : c;
|
return a < b && a < c ? a : b < c ? b : c;
|
||||||
}
|
}
|
||||||
|
|
||||||
function max3(a, b, c) {
|
function max3(a, b, c) {
|
||||||
return a > b && a > c ? a : b > c ? b : c;
|
return a > b && a > c ? a : b > c ? b : c;
|
||||||
}
|
}
|
||||||
|
|
||||||
function min2(a, b) {
|
function min2(a, b) {
|
||||||
return a < b ? a : b;
|
return a < b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function max2(a, b) {
|
function max2(a, b) {
|
||||||
return a > b ? a : b;
|
return a > b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function levenshteinDistance(s1, s2) {
|
function levenshteinDistance(s1, s2) {
|
||||||
let len1 = s1.length;
|
let len1 = s1.length;
|
||||||
let len2 = s2.length;
|
let len2 = s2.length;
|
||||||
|
|
||||||
if (len1 === 0) return len2;
|
if (len1 === 0) return len2;
|
||||||
if (len2 === 0) return len1;
|
if (len2 === 0) return len1;
|
||||||
|
|
||||||
if (len2 > len1) {
|
if (len2 > len1) {
|
||||||
[s1, s2] = [s2, s1];
|
[s1, s2] = [s2, s1];
|
||||||
[len1, len2] = [len2, len1];
|
[len1, len2] = [len2, len1];
|
||||||
}
|
|
||||||
|
|
||||||
let prev = new Array(len2 + 1);
|
|
||||||
let curr = new Array(len2 + 1);
|
|
||||||
|
|
||||||
for (let j = 0; j <= len2; j++) {
|
|
||||||
prev[j] = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 1; i <= len1; i++) {
|
|
||||||
curr[0] = i;
|
|
||||||
for (let j = 1; j <= len2; j++) {
|
|
||||||
let cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
|
||||||
curr[j] = min3(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
||||||
}
|
}
|
||||||
[prev, curr] = [curr, prev];
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev[len2];
|
let prev = new Array(len2 + 1);
|
||||||
|
let curr = new Array(len2 + 1);
|
||||||
|
|
||||||
|
for (let j = 0; j <= len2; j++) {
|
||||||
|
prev[j] = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i <= len1; i++) {
|
||||||
|
curr[0] = i;
|
||||||
|
for (let j = 1; j <= len2; j++) {
|
||||||
|
let cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
||||||
|
curr[j] = min3(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
||||||
|
}
|
||||||
|
[prev, curr] = [curr, prev];
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev[len2];
|
||||||
}
|
}
|
||||||
|
|
||||||
function partialRatio(shortS, longS) {
|
function partialRatio(shortS, longS) {
|
||||||
let lenS = shortS.length;
|
let lenS = shortS.length;
|
||||||
let lenL = longS.length;
|
let lenL = longS.length;
|
||||||
let best = 0.0;
|
let best = 0.0;
|
||||||
|
|
||||||
if (lenS === 0) return 1.0;
|
if (lenS === 0) return 1.0;
|
||||||
|
|
||||||
for (let i = 0; i <= lenL - lenS; i++) {
|
for (let i = 0; i <= lenL - lenS; i++) {
|
||||||
let sub = longS.slice(i, i + lenS);
|
let sub = longS.slice(i, i + lenS);
|
||||||
let dist = levenshteinDistance(shortS, sub);
|
let dist = levenshteinDistance(shortS, sub);
|
||||||
let score = 1.0 - dist / lenS;
|
let score = 1.0 - dist / lenS;
|
||||||
if (score > best) best = score;
|
if (score > best) best = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeScore(s1, s2) {
|
function computeScore(s1, s2) {
|
||||||
if (s1 === s2) return 1.0;
|
if (s1 === s2) return 1.0;
|
||||||
|
|
||||||
let dist = levenshteinDistance(s1, s2);
|
let dist = levenshteinDistance(s1, s2);
|
||||||
let maxLen = max2(s1.length, s2.length);
|
let maxLen = max2(s1.length, s2.length);
|
||||||
if (maxLen === 0) return 1.0;
|
if (maxLen === 0) return 1.0;
|
||||||
|
|
||||||
let full = 1.0 - dist / maxLen;
|
let full = 1.0 - dist / maxLen;
|
||||||
let part =
|
let part =
|
||||||
s1.length < s2.length ? partialRatio(s1, s2) : partialRatio(s2, s1);
|
s1.length < s2.length ? partialRatio(s1, s2) : partialRatio(s2, s1);
|
||||||
|
|
||||||
let score = 0.85 * full + 0.15 * part;
|
let score = 0.85 * full + 0.15 * part;
|
||||||
|
|
||||||
if (s1 && s2 && s1[0] !== s2[0]) {
|
if (s1 && s2 && s1[0] !== s2[0]) {
|
||||||
score -= 0.05;
|
score -= 0.05;
|
||||||
}
|
|
||||||
|
|
||||||
let lenDiff = Math.abs(s1.length - s2.length);
|
|
||||||
if (lenDiff >= 3) {
|
|
||||||
score -= (0.05 * lenDiff) / maxLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
let commonPrefixLen = 0;
|
|
||||||
let minLen = min2(s1.length, s2.length);
|
|
||||||
for (let i = 0; i < minLen; i++) {
|
|
||||||
if (s1[i] === s2[i]) {
|
|
||||||
commonPrefixLen++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
score += 0.02 * commonPrefixLen;
|
|
||||||
|
|
||||||
if (s1.includes(s2) || s2.includes(s1)) {
|
let lenDiff = Math.abs(s1.length - s2.length);
|
||||||
score += 0.06;
|
if (lenDiff >= 3) {
|
||||||
}
|
score -= (0.05 * lenDiff) / maxLen;
|
||||||
|
}
|
||||||
|
|
||||||
return Math.max(0.0, Math.min(1.0, score));
|
let commonPrefixLen = 0;
|
||||||
|
let minLen = min2(s1.length, s2.length);
|
||||||
|
for (let i = 0; i < minLen; i++) {
|
||||||
|
if (s1[i] === s2[i]) {
|
||||||
|
commonPrefixLen++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
score += 0.02 * commonPrefixLen;
|
||||||
|
|
||||||
|
if (s1.includes(s2) || s2.includes(s1)) {
|
||||||
|
score += 0.06;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(0.0, Math.min(1.0, score));
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeTextMatchScore(s1, s2) {
|
function computeTextMatchScore(s1, s2) {
|
||||||
if (s1 === s2) return 1.0;
|
if (s1 === s2) return 1.0;
|
||||||
|
|
||||||
let dist = levenshteinDistance(s1, s2);
|
let dist = levenshteinDistance(s1, s2);
|
||||||
let maxLen = max2(s1.length, s2.length);
|
let maxLen = max2(s1.length, s2.length);
|
||||||
if (maxLen === 0) return 1.0;
|
if (maxLen === 0) return 1.0;
|
||||||
|
|
||||||
let full = 1.0 - dist / maxLen;
|
let full = 1.0 - dist / maxLen;
|
||||||
let part =
|
let part =
|
||||||
s1.length < s2.length ? partialRatio(s1, s2) : partialRatio(s2, s1);
|
s1.length < s2.length ? partialRatio(s1, s2) : partialRatio(s2, s1);
|
||||||
|
|
||||||
let score = 0.4 * full + 0.6 * part;
|
let score = 0.4 * full + 0.6 * part;
|
||||||
|
|
||||||
let lenDiff = Math.abs(s1.length - s2.length);
|
let lenDiff = Math.abs(s1.length - s2.length);
|
||||||
if (lenDiff >= 10) {
|
if (lenDiff >= 10) {
|
||||||
score -= (0.02 * lenDiff) / maxLen;
|
score -= (0.02 * lenDiff) / maxLen;
|
||||||
}
|
|
||||||
|
|
||||||
let commonPrefixLen = 0;
|
|
||||||
let minLen = min2(s1.length, s2.length);
|
|
||||||
for (let i = 0; i < minLen; i++) {
|
|
||||||
if (s1[i] === s2[i]) {
|
|
||||||
commonPrefixLen++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
score += 0.01 * commonPrefixLen;
|
|
||||||
|
|
||||||
if (s1.includes(s2) || s2.includes(s1)) {
|
let commonPrefixLen = 0;
|
||||||
score += 0.2;
|
let minLen = min2(s1.length, s2.length);
|
||||||
}
|
for (let i = 0; i < minLen; i++) {
|
||||||
|
if (s1[i] === s2[i]) {
|
||||||
|
commonPrefixLen++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
score += 0.01 * commonPrefixLen;
|
||||||
|
|
||||||
return Math.max(0.0, Math.min(1.0, score));
|
if (s1.includes(s2) || s2.includes(s1)) {
|
||||||
|
score += 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(0.0, Math.min(1.0, score));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut pixmap = rgba_image_to_pixmap(&img);
|
let mut pixmap = rgba_image_to_pixmap(&img);
|
||||||
let mut dst_paint = PixmapPaint::default();
|
let dst_paint = PixmapPaint {
|
||||||
dst_paint.blend_mode = BlendMode::DestinationIn;
|
blend_mode: BlendMode::DestinationIn,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
pixmap.draw_pixmap(0, 0, mask.as_ref(), &dst_paint, Transform::identity(), None);
|
pixmap.draw_pixmap(0, 0, mask.as_ref(), &dst_paint, Transform::identity(), None);
|
||||||
pixmap_to_rgba_image(pixmap)
|
pixmap_to_rgba_image(pixmap)
|
||||||
}
|
}
|
||||||
@@ -69,8 +71,10 @@ pub fn apply_drop_shadow(
|
|||||||
let shadow_x = (extra_left as f32 + offset_x) as i32;
|
let shadow_x = (extra_left as f32 + offset_x) as i32;
|
||||||
let shadow_y = (extra_top as f32 + offset_y) as i32;
|
let shadow_y = (extra_top as f32 + offset_y) as i32;
|
||||||
|
|
||||||
let mut sp = PixmapPaint::default();
|
let sp = PixmapPaint {
|
||||||
sp.blend_mode = BlendMode::Source;
|
blend_mode: BlendMode::Source,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
shadow_pixmap.draw_pixmap(
|
shadow_pixmap.draw_pixmap(
|
||||||
shadow_x,
|
shadow_x,
|
||||||
shadow_y,
|
shadow_y,
|
||||||
@@ -87,8 +91,10 @@ pub fn apply_drop_shadow(
|
|||||||
let blurred_pixmap = rgba_image_to_pixmap(&blurred);
|
let blurred_pixmap = rgba_image_to_pixmap(&blurred);
|
||||||
|
|
||||||
let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap");
|
let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap");
|
||||||
let mut p = PixmapPaint::default();
|
let p = PixmapPaint {
|
||||||
p.blend_mode = BlendMode::Source;
|
blend_mode: BlendMode::Source,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
canvas.draw_pixmap(
|
canvas.draw_pixmap(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -98,8 +104,10 @@ pub fn apply_drop_shadow(
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut p2 = PixmapPaint::default();
|
let p2 = PixmapPaint {
|
||||||
p2.blend_mode = BlendMode::SourceOver;
|
blend_mode: BlendMode::SourceOver,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
canvas.draw_pixmap(
|
canvas.draw_pixmap(
|
||||||
extra_left as i32,
|
extra_left as i32,
|
||||||
extra_top as i32,
|
extra_top as i32,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
mod config;
|
mod config;
|
||||||
mod effects;
|
mod effects;
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{Context, Result, bail};
|
||||||
use std::io::Write as _;
|
use std::io::Write as _;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user