Author SHA1 Message Date
zach 9cbb90f9e1 feat: initial commit for pixel-snapping components and helpers 2026-10-29 17:34:57 +01:00
264 changed files with 4243 additions and 9340 deletions
-3
View File
@@ -1,3 +0,0 @@
#omz revision: d2b7f557cceaddb16ba89bbf8200d8a8df35c4d8
#omz fpath: /home/zach/.oh-my-zsh/plugins/colored-man-pages /home/zach/.oh-my-zsh/plugins/universalarchive /home/zach/.oh-my-zsh/plugins/tldr /home/zach/.oh-my-zsh/plugins/extract /home/zach/.oh-my-zsh/plugins/copypath /home/zach/.oh-my-zsh/plugins/cp /home/zach/.oh-my-zsh/plugins/conda-env /home/zach/.oh-my-zsh/plugins/command-not-found /home/zach/.oh-my-zsh/plugins/dirhistory /home/zach/.oh-my-zsh/plugins/copybuffer /home/zach/.oh-my-zsh/plugins/copyfile /home/zach/.oh-my-zsh/custom/plugins/fast-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-autosuggestions /home/zach/.oh-my-zsh/plugins/archlinux /home/zach/.oh-my-zsh/plugins/web-search /home/zach/.oh-my-zsh/plugins/sudo /home/zach/.oh-my-zsh/plugins/git /home/zach/.oh-my-zsh/functions /home/zach/.oh-my-zsh/completions /home/zach/.oh-my-zsh/custom/functions /home/zach/.oh-my-zsh/custom/completions /home/zach/tools/ripgrep/complete /home/linuxbrew/.linuxbrew/share/zsh/site-functions /home/zach/.oh-my-zsh/plugins/colored-man-pages /home/zach/.oh-my-zsh/plugins/universalarchive /home/zach/.oh-my-zsh/plugins/tldr /home/zach/.oh-my-zsh/plugins/extract /home/zach/.oh-my-zsh/plugins/copypath /home/zach/.oh-my-zsh/plugins/cp /home/zach/.oh-my-zsh/plugins/conda-env /home/zach/.oh-my-zsh/plugins/command-not-found /home/zach/.oh-my-zsh/plugins/dirhistory /home/zach/.oh-my-zsh/plugins/copybuffer /home/zach/.oh-my-zsh/plugins/copyfile /home/zach/.oh-my-zsh/custom/plugins/fast-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-autosuggestions /home/zach/.oh-my-zsh/plugins/archlinux /home/zach/.oh-my-zsh/plugins/web-search /home/zach/.oh-my-zsh/plugins/sudo /home/zach/.oh-my-zsh/plugins/git /home/zach/.oh-my-zsh/functions /home/zach/.oh-my-zsh/completions /home/zach/.oh-my-zsh/custom/functions /home/zach/.oh-my-zsh/custom/completions /home/zach/.oh-my-zsh/cache/completions /home/zach/tools/ripgrep/complete /home/linuxbrew/.linuxbrew/share/zsh/site-functions /usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/functions/Calendar /usr/share/zsh/functions/Chpwd /usr/share/zsh/functions/Completion /usr/share/zsh/functions/Completion/Base /usr/share/zsh/functions/Completion/Linux /usr/share/zsh/functions/Completion/Unix /usr/share/zsh/functions/Completion/X /usr/share/zsh/functions/Completion/Zsh /usr/share/zsh/functions/Exceptions /usr/share/zsh/functions/MIME /usr/share/zsh/functions/Math /usr/share/zsh/functions/Misc /usr/share/zsh/functions/Newuser /usr/share/zsh/functions/Prompts /usr/share/zsh/functions/TCP /usr/share/zsh/functions/VCS_Info /usr/share/zsh/functions/VCS_Info/Backends /usr/share/zsh/functions/Zftp /usr/share/zsh/functions/Zle /home/zach/.zfunc
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v4
- name: Install Docker CLI
run: apk add --no-cache docker-cli docker-cli-buildx
run: apk add --no-cache docker-cli
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin
+20 -21
View File
@@ -17,47 +17,46 @@ jobs:
run: |
find . \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
-not -path './build/*' \
-exec clang-format --dry-run -Werror --style=file {} +
-exec clang-format -i --style=file {} +
git diff --exit-code && echo "clang-format: passed"
build & lint:
build:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache CMake build
uses: actions/cache@v4
with:
path: build
key: cpp-build-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt') }}
restore-keys: |
cpp-build-
- name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Cache ccache
uses: actions/cache@v4
- name: Build
run: ninja -C build
clang-tidy:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: ~/.ccache
key: ccache-${{ runner.os }}-${{ github.sha }}
restore-keys: |
ccache-${{ runner.os }}-
fetch-depth: 0
- name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Build
run: ninja -C build
env:
CMAKE_CXX_COMPILER_LAUNCHER: ccache
- name: clang-tidy
run: |
sed -i 's/-mno-direct-extern-access //g' build/compile_commands.json
set -o pipefail
run-clang-tidy -p build -j "$(nproc)" \
-source-filter="^$(pwd)/Plugins/" \
-header-filter=".*Plugins/.*" \
-removed-arg=-mno-direct-extern-access
run-clang-tidy -p build -j $(nproc) -header-filter="Plugins/.*" 2>&1 | \
grep -v 'Suppressed\|non-user\|non-system\|unknown argument.*mno-direct\|Found compiler error\|warnings generated\.\|^Use -header-filter'
+15 -1
View File
@@ -4,7 +4,7 @@ on:
pull_request:
jobs:
fmt & lint:
fmt:
runs-on: alpine
container: node:26-alpine
@@ -24,6 +24,20 @@ jobs:
else
echo "No JS/TS files found"
fi
lint:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git
- 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
+3 -3
View File
@@ -7,7 +7,7 @@ on:
pull_request:
jobs:
fmt & lint:
static:
runs-on: debian
container: node:26-trixie-slim
@@ -33,7 +33,7 @@ jobs:
. .venv/bin/activate
ruff check .
test & nuitka build:
verify:
if: always()
runs-on: debian
container: node:26-trixie-slim
@@ -64,4 +64,4 @@ jobs:
- name: Nuitka module check
run: |
. .venv/bin/activate
nuitka --module --include-package=zshell cli/src/zshell/
nuitka --module --include-package=zshell cli/src/zshell/
+121 -49
View File
@@ -4,77 +4,149 @@ on:
pull_request:
jobs:
clippy & fmt:
build:
runs-on: alpine
container: rust:alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Install node for actions
run: |
apk add --no-cache nodejs git
- name: Install rust components
run: |
rustup component add clippy rustfmt
cargo clippy --version
rustfmt --version
- 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-build-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
key: rust-build-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-cargo-
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Clippy (with compile check)
- name: Install tools
run: |
manifests=$(find . \( -name .cargo -o -name .git -o -name target \) -prune -o -name Cargo.toml -print)
if [ -z "$manifests" ]; then
if [ -n "$(find . \( -name .cargo -o -name target \) -prune -o -name '*.rs' -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
fi
apk add --no-cache \
git \
cargo \
rust
- name: Cargo check
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
for manifest in $(find . -name "Cargo.toml"); do
cargo check --manifest-path "$manifest" && \
echo "$manifest: check passed" || \
{ echo "$manifest: check failed"; exit 1; }
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"
exit 0
fi
status=0
for manifest in $manifests; do
if (cd "$(dirname "$manifest")" && cargo clippy --all-targets --locked -- -D warnings); then
echo "$manifest: Clippy passed"
else
echo "$manifest: Clippy failed"
status=1
fi
done
exit $status
fmt:
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-fmt-${{ runner.os }}-${{ 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
- name: Format check
run: |
manifests=$(find . \( -name .cargo -o -name .git -o -name target \) -prune -o -name Cargo.toml -print)
if [ -z "$manifests" ]; then
if [ -n "$(find . \( -name .cargo -o -name target \) -prune -o -name '*.rs' -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
fi
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
{ echo "$manifest: needs formatting"; 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
clippy:
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-clippy-${{ runner.os }}-${{ 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 \
rust-clippy
- 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"
exit 0
fi
status=0
for manifest in $manifests; do
if (cd "$(dirname "$manifest")" && cargo fmt --check); then
echo "$manifest: formatting OK"
else
echo "$manifest: needs formatting"
status=1
fi
done
exit $status
+10 -25
View File
@@ -1,38 +1,23 @@
**/__pycache__/
./result/
.pyre/
.cache/
.venv/
.qmlls.ini
build/
compile_commands.json
testpython
pkg/
*.tar.*
uv.lock
.qtcreator/
dist/
**/target/
**/test-plugins/
**/Charts/
network-dev/
**/zshell.dist/
# Qml
.qtcreator/
.qmlls.ini
# CMake compile artifacts
.cache/
compile_commands.json
build/
# Cargo home (CI sets CARGO_HOME inside the workspace)
.cargo/
# Python
uv.lock
testpython
.venv/
**/__pycache__/
# Nuitka compile
**/zshell.pyi
**/zshell.cpython-*
**/zshell.build/
# Agents stuff
**/zshell.dist/
.opencode/
run-agent.sh
AGENTS.md
+4 -2
View File
@@ -46,7 +46,8 @@ Item {
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
Behavior on strokeColor {
CAnim {}
CAnim {
}
}
PathAngleArc {
@@ -83,7 +84,8 @@ Item {
}
}
Behavior on color {
CAnim {}
CAnim {
}
}
Anim on waveProgress {
id: waveProgAnim
+2 -3
View File
@@ -1,5 +1,4 @@
import QtQuick
import Quickshell
import ZShell.Components
Item {
@@ -20,7 +19,7 @@ Item {
}
ShaderEffectSource {
id: src
id: source
hideSource: true
sourceItem: contentItem
@@ -28,7 +27,7 @@ Item {
RoundedMask {
anchors.fill: parent
source: src
source: source
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
+2 -1
View File
@@ -160,7 +160,8 @@ Item {
Behavior on color {
enabled: false
CAnim {}
CAnim {
}
}
}
+2 -1
View File
@@ -8,6 +8,7 @@ MultiEffect {
colorization: 1
Behavior on colorizationColor {
CAnim {}
CAnim {
}
}
}
+2 -1
View File
@@ -7,6 +7,7 @@ ClippingRectangle {
color: "transparent"
Behavior on color {
CAnim {}
CAnim {
}
}
}
+2 -1
View File
@@ -7,6 +7,7 @@ ClippingWrapperRectangle {
color: "transparent"
Behavior on color {
CAnim {}
CAnim {
}
}
}
+1
View File
@@ -99,6 +99,7 @@ ProgressBar {
}
Line {
id: dot
property real implicitSize
anchors.right: parent.right
+6 -6
View File
@@ -2,10 +2,10 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
Slider {
@@ -28,7 +28,7 @@ Slider {
signal interaction(v: real)
implicitHeight: 12
implicitHeight: ZUtils.snap(12, root)
implicitWidth: 200
contentItem: Item {
@@ -123,7 +123,7 @@ Slider {
bottomRightRadius: Tokens.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
implicitWidth: ZUtils.snap(root.filledWidth, root)
radius: root.radius
topRightRadius: Tokens.rounding.extraSmall / 2
}
@@ -137,8 +137,8 @@ Slider {
frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
implicitWidth: ZUtils.snap(root.filledWidth, root)
lineWidth: ZUtils.snap(root.height * 0.7, root)
startX: x
Behavior on color {
+10 -5
View File
@@ -105,16 +105,20 @@ Switch {
width: height
Behavior on end1 {
PropAnim {}
PropAnim {
}
}
Behavior on end2 {
PropAnim {}
PropAnim {
}
}
Behavior on start1 {
PropAnim {}
PropAnim {
}
}
Behavior on start2 {
PropAnim {}
PropAnim {
}
}
ShapePath {
@@ -130,7 +134,8 @@ Switch {
strokeWidth: 16 * 0.15
Behavior on strokeColor {
CAnim {}
CAnim {
}
}
PathLine {
+4 -2
View File
@@ -20,7 +20,8 @@ Text {
textFormat: Text.PlainText
Behavior on color {
CAnim {}
CAnim {
}
}
Behavior on text {
enabled: root.animate
@@ -32,7 +33,8 @@ Text {
type: Anim.FastEffects
}
PropertyAction {}
PropertyAction {
}
TAnim {
target: root
+2 -1
View File
@@ -65,7 +65,8 @@ Image {
value: root.source
}
PropertyAction {}
PropertyAction {
}
ScriptAction {
script: root.maybeStartInAnim()
+3 -2
View File
@@ -1,4 +1,5 @@
import QtQuick
import ZShell
import ZShell.Config
import qs.Services
@@ -15,9 +16,9 @@ ButtonBase {
const h = label.implicitHeight + padding * 2;
if (h % 2 !== 0)
return h + 1;
return h;
return ZUtils.snap(h, root);
}
implicitWidth: implicitHeight
implicitWidth: ZUtils.snap(implicitHeight, root)
inactiveColor: {
if (!isToggle && type === IconButton.Filled)
return Colors.palette.m3primary;
+3 -1
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -34,10 +35,11 @@ ButtonBase {
}
verticalPadding: Tokens.padding.extraSmall
RowLayout {
SnapLayout {
id: row
anchors.centerIn: parent
spacing: Tokens.spacing.small
MaterialIcon {
+85 -65
View File
@@ -1,6 +1,8 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtCanvas2D
import ZShell
Item {
id: root
@@ -36,54 +38,62 @@ Item {
property real fillAlpha: 0.15
property real lineWidth: 2
property real markerDotRadius: 4
property real labelFontSize: 10
property real labelFontSize: 8
Canvas {
Canvas2D {
id: canvas
anchors.fill: parent
renderStrategy: Canvas.Immediate
alphaBlending: true
fillColor: "transparent"
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
function snapV(v) {
return ZUtils.snap(v, canvas);
}
function strokeCurve(ctx, pts) {
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
if (root.curveSmoothing && pts.length > 2) {
const k = 1 / 6;
const n = pts.length;
const dpr = ZUtils.dprFor(canvas);
for (let i = 0; i < pts.length - 1; i++) {
const p1 = pts[i];
const p2 = pts[i + 1];
const [c1x, c1y, c2x, c2y] = segmentControls(pts, i);
for (let i = 0; i < n - 1; i++) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[Math.min(i + 2, n - 1)];
let c1x = p1.x + (p2.x - p0.x) * k;
let c1y = p1.y + (p2.y - p0.y) * k;
let c2x = p2.x - (p3.x - p1.x) * k;
let c2y = p2.y - (p3.y - p1.y) * k;
const minY = Math.min(p1.y, p2.y);
const maxY = Math.max(p1.y, p2.y);
c1y = Math.max(minY, Math.min(maxY, c1y));
c2y = Math.max(minY, Math.min(maxY, c2y));
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, p2.x, p2.y);
}
} else {
for (let i = 0; i < pts.length - 1; i++) {
const p0 = pts[i];
const p1 = pts[i + 1];
const cx = (p0.x + p1.x) * 0.5;
ctx.bezierCurveTo(cx, p0.y, cx, p1.y, p1.x, p1.y);
const firstCol = Math.ceil(p1.x * dpr) + 1;
const lastCol = Math.floor(p2.x * dpr) - 1;
for (let col = firstCol; col <= lastCol; ++col) {
const x = col / dpr;
ctx.lineTo(x, bezierY(p1, c1x, c1y, c2x, c2y, p2, x));
}
ctx.lineTo(p2.x, p2.y);
}
}
// Control points for segment i (pts[i] -> pts[i+1]), matching strokeCurve
function bezierY(p1, c1x, c1y, c2x, c2y, p2, targetX) {
let tlo = 0;
let thi = 1;
for (let it = 0; it < 16; it++) {
const tm = (tlo + thi) / 2;
const u = 1 - tm;
const x = u * u * u * p1.x + 3 * u * u * tm * c1x + 3 * u * tm * tm * c2x + tm * tm * tm * p2.x;
if (x < targetX)
tlo = tm;
else
thi = tm;
}
const t = (tlo + thi) / 2;
const u = 1 - t;
return u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
}
function segmentControls(pts, i) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
@@ -109,7 +119,6 @@ Item {
return [cx, p1.y, cx, p2.y];
}
// Point on the curve at a given x (x is monotonic per segment)
function pointAtX(pts, targetX) {
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
@@ -144,7 +153,10 @@ Item {
};
}
// Part of the curve left/right of a cut x, including the cut point
function cutPoint(pts, targetX) {
return pointAtX(pts, targetX);
}
function cutLeft(pts, targetX) {
if (targetX <= pts[0].x)
return [];
@@ -161,7 +173,7 @@ Item {
const left = pts.slice(0, j + 1);
if (targetX > pts[j].x)
left.push(pointAtX(pts, targetX));
left.push(cutPoint(pts, targetX));
return left;
}
@@ -187,17 +199,20 @@ Item {
onPaint: {
const ctx = getContext("2d");
if (!ctx)
return;
ctx.reset();
ctx.clearRect(0, 0, width, height);
const W = width;
const H = height;
if (W < 2 || H < 2)
return;
const padLeft = root.labelFontSize * 3.4;
const padRight = 2;
const padTop = 2;
const padBottom = root.labelFontSize + 8;
const padLeft = root.labelFontSize * 5;
const padRight = 0;
const padTop = 0;
const padBottom = root.labelFontSize + 14;
const plot = {
x: padLeft,
y: padTop,
@@ -207,8 +222,6 @@ Item {
if (plot.w < 2 || plot.h < 2)
return;
// Auto-fit: NaN yMin/yMax derive from the data, snapped to the
// horizontal step grid, honoring minHorizontalLines
let lo = root.yMin;
let hi = root.yMax;
const loAuto = Number.isNaN(lo);
@@ -282,11 +295,17 @@ Item {
const xAt = i => plot.x + (i / root.xSteps) * plot.w;
const yAt = v => plot.y + (1 - (v - scaleBottom) / range) * plot.h;
const crisp = v => Math.floor(v) + 0.5;
// 1. Grid lines
const dpr = ZUtils.dprFor(canvas);
const devW = w => Math.max(1, Math.round(w * dpr));
const snapLine = (v, w) => {
const dw = devW(w);
return (Math.round(v * dpr - dw / 2) + dw / 2) / dpr;
};
const gridW = devW(1) / dpr;
ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha);
ctx.lineWidth = 1;
ctx.lineWidth = gridW;
if (root.verticalLineStep > 0) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
@@ -295,7 +314,7 @@ Item {
if (i === root.xSteps && !root.lastXLine)
continue;
const x = crisp(xAt(i));
const x = snapLine(xAt(i), 1);
ctx.beginPath();
ctx.moveTo(x, plot.y);
ctx.lineTo(x, H);
@@ -303,12 +322,11 @@ Item {
}
}
// Lines extend down into yPaddingBottom, snapped to the grid
const vStart = lo - (root.yPaddingBottom > 0 ? Math.floor(root.yPaddingBottom / root.horizontalLineStep) * root.horizontalLineStep : 0);
if (root.horizontalLineStep > 0) {
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) {
const y = crisp(yAt(v));
const y = snapLine(yAt(v), 1);
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(plot.x + plot.w, y);
@@ -316,9 +334,8 @@ Item {
}
}
// 2. Labels
ctx.fillStyle = Qt.alpha(root.labelColor, root.labelAlpha);
ctx.font = root.labelFontSize + "px sans-serif";
ctx.font = root.labelFontSize + "pt 'sans-serif'";
ctx.textAlign = "left";
ctx.textBaseline = "top";
@@ -331,7 +348,7 @@ Item {
const text = root.xLabel(i);
if (text)
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 4);
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 8);
}
}
@@ -344,7 +361,6 @@ Item {
}
}
// 3. Curve, split into segments at null gaps
const segments = [];
let current = null;
for (let i = 0; i < series.length; i++) {
@@ -355,7 +371,7 @@ Item {
continue;
}
const pt = {
x: xAt(i),
x: snapV(xAt(i)),
y: yAt(series[i])
};
if (!current)
@@ -372,8 +388,6 @@ Item {
const markerPx = hasMarker ? xAt(root.markerX) : 0;
let gapPx = 0;
if (hasMarker) {
// markerGap is measured from the dot edge, perpendicular to the
// curve; project onto x via the local curve slope
let slope = 0;
const seg = segments.find(s => s[0].x <= markerPx && markerPx <= s[s.length - 1].x);
if (seg && seg.length > 2 && markerPx - 1 >= seg[0].x && markerPx + 1 <= seg[seg.length - 1].x)
@@ -385,7 +399,6 @@ Item {
if (seg.length < 2)
continue;
// Fill stays continuous under the marker
ctx.fillStyle = Qt.alpha(root.lineColor, root.fillAlpha);
strokeCurve(ctx, seg);
ctx.lineTo(seg[seg.length - 1].x, plot.y + plot.h);
@@ -393,7 +406,6 @@ Item {
ctx.closePath();
ctx.fill();
// Stroke has a gap around the marker
const pieces = hasMarker ? [cutLeft(seg, markerPx - gapPx), cutRight(seg, markerPx + gapPx)] : [seg];
for (const pts of pieces) {
@@ -401,24 +413,32 @@ Item {
continue;
ctx.strokeStyle = root.lineColor;
ctx.lineWidth = root.lineWidth;
ctx.lineWidth = ZUtils.snap(root.lineWidth, root);
strokeCurve(ctx, pts);
ctx.stroke();
}
}
// 4. Marker: dashed vertical + dot
if (root.markerX >= 0 && root.markerX <= root.xSteps) {
const x = markerPx;
ctx.setLineDash([4, 4]);
ctx.strokeStyle = Qt.alpha(root.markerColor, 0.6);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(crisp(x), plot.y);
ctx.lineTo(crisp(x), H);
ctx.stroke();
ctx.setLineDash([]);
ctx.lineWidth = gridW;
const lineX = snapLine(x, 1);
let dashY = plot.y;
let dashOn = true;
while (dashY < H) {
const dashEnd = Math.min(dashY + 4, H);
if (dashOn) {
ctx.beginPath();
ctx.moveTo(lineX, snapV(dashY));
ctx.lineTo(lineX, snapV(dashEnd));
ctx.stroke();
}
dashY = dashEnd;
dashOn = !dashOn;
}
const i0 = Math.floor(root.markerX);
const v0 = series[i0];
@@ -433,7 +453,7 @@ Item {
if (y != null) {
ctx.fillStyle = root.markerColor;
ctx.beginPath();
ctx.arc(x, y, root.markerDotRadius, 0, Math.PI * 2);
ctx.circle(snapV(x), y, root.markerDotRadius);
ctx.fill();
}
}
+2 -1
View File
@@ -64,7 +64,8 @@ MaterialShape {
to: 360
}
Behavior on color {
CAnim {}
CAnim {
}
}
ElapsedTimer {
+4 -2
View File
@@ -47,10 +47,12 @@ Item {
implicitWidth: vertical ? elideText.implicitHeight : elideText.implicitWidth
Behavior on leftFadeStrength {
Anim {}
Anim {
}
}
Behavior on rightFadeStrength {
Anim {}
Anim {
}
}
onHeightChanged: resetMarquee()
+11 -5
View File
@@ -4,6 +4,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import ZShell.Components
import qs.Drawers
import qs.Services
@@ -34,7 +35,7 @@ MouseArea {
cursorShape: expanded ? Qt.ArrowCursor : undefined
enabled: expanded
hoverEnabled: expanded
layer.enabled: false
layer.enabled: opacity < 1
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
@@ -101,7 +102,8 @@ MouseArea {
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {}
Anim {
}
}
}
@@ -110,12 +112,14 @@ MouseArea {
color: Colors.palette.m3surfaceContainerLow
radius: parent.radius
ColumnLayout {
SnapLayout {
id: column
anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.extraSmall
orientation: Qt.Vertical
Repeater {
id: repeater
@@ -136,7 +140,8 @@ MouseArea {
radius: menu.radius - column.anchors.margins
Behavior on radius {
Anim {}
Anim {
}
}
StateLayer {
@@ -151,11 +156,12 @@ MouseArea {
}
}
RowLayout {
SnapLayout {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Tokens.padding.medium
spacing: Tokens.spacing.small
MaterialIcon {
+8 -4
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -53,7 +54,7 @@ Row {
onClicked: root.active?.clicked()
}
RowLayout {
SnapLayout {
id: textRow
anchors.centerIn: parent
@@ -102,7 +103,8 @@ Row {
topLeftRadius: rad
Behavior on rad {
Anim {}
Anim {
}
}
StateLayer {
@@ -126,10 +128,12 @@ Row {
text: "expand_more"
Behavior on anchors.horizontalCenterOffset {
Anim {}
Anim {
}
}
Behavior on rotation {
Anim {}
Anim {
}
}
}
}
+5 -2
View File
@@ -1,4 +1,5 @@
import ZShell
import ZShell.Components
import QtQuick
import QtQuick.Layouts
import qs.Components
@@ -47,7 +48,7 @@ CustomRect {
z: -1
}
RowLayout {
SnapLayout {
id: layout
anchors.fill: parent
@@ -88,9 +89,11 @@ CustomRect {
}
}
ColumnLayout {
SnapLayout {
Layout.fillWidth: true
spacing: 0
orientation: Qt.Vertical
CustomText {
id: title
+6 -3
View File
@@ -44,7 +44,8 @@ Item {
onValuesChanged: root.flagChanged()
}
ToastWrapper {}
ToastWrapper {
}
}
component ToastWrapper: MouseArea {
@@ -85,10 +86,12 @@ Item {
}
}
Behavior on opacity {
Anim {}
Anim {
}
}
Behavior on scale {
Anim {}
Anim {
}
}
Component.onCompleted: modelData.lock(this)
+2 -1
View File
@@ -20,7 +20,8 @@ Shape {
strokeWidth: 0
Behavior on fillColor {
CAnim {}
CAnim {
}
}
PathSvg {
+1
View File
@@ -141,6 +141,7 @@ Singleton {
BeatTracker {
id: beatTracker
}
PwObjectTracker {
+2 -1
View File
@@ -295,7 +295,8 @@ Singleton {
Component {
id: apComp
AccessPoint {}
AccessPoint {
}
}
Process {
+5 -2
View File
@@ -1081,13 +1081,15 @@ Singleton {
Component {
id: commandProc
CommandProcess {}
CommandProcess {
}
}
Component {
id: apComp
AccessPoint {}
AccessPoint {
}
}
Timer {
@@ -1308,6 +1310,7 @@ Singleton {
}
stdout: StdioCollector {
id: stdoutCollector
}
onExited: code => {
+2 -1
View File
@@ -164,7 +164,8 @@ Singleton {
Component {
id: notifComp
Notif {}
Notif {
}
}
component Notif: QtObject {
+9 -5
View File
@@ -1,5 +1,7 @@
import Quickshell
import QtQuick
import ZShell.Config
import ZShell
import qs.Components
import qs.Modules.Bar.Popouts as Popouts
import qs.Modules.Notifications as Notifications
@@ -14,7 +16,6 @@ import qs.Modules.Settings as Settings
import qs.Modules.Drawing as Drawing
import qs.Modules.Dock as Dock
import qs.Modules.Clipboard as Clipboard
import ZShell.Config
Item {
id: root
@@ -74,7 +75,10 @@ Item {
anchors.right: parent.right
anchors.rightMargin: sidebar.width * (1 - sidebar.offsetScale)
anchors.verticalCenter: parent.verticalCenter
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
// anchors.verticalCenter: parent.verticalCenter
clip: sidebar.visible
implicitHeight: osd.implicitHeight
implicitWidth: osd.implicitWidth * (1 - osd.offsetScale)
@@ -82,8 +86,7 @@ Item {
Osd.Wrapper {
id: osd
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.fill: parent
screen: root.screen
screenState: root.screenState
sidebarOrSessionVisible: sidebar.visible
@@ -129,8 +132,9 @@ Item {
Launcher.Wrapper {
id: launcher
x: ZUtils.snap((parent.width - implicitWidth) / 2, root)
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
panels: root
screen: root.screen
screenState: root.screenState
+6 -3
View File
@@ -3,11 +3,12 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Helpers
import qs.Services
ColumnLayout {
SnapLayout {
id: root
readonly property real centerScale: Math.min(1, screenHeight / 1440)
@@ -18,9 +19,11 @@ ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: false
Layout.preferredWidth: centerWidth
orientation: Qt.Vertical
spacing: Tokens.spacing.largeIncreased * 2
RowLayout {
SnapLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Tokens.spacing.small
@@ -116,7 +119,7 @@ ColumnLayout {
}
}
RowLayout {
SnapLayout {
id: input
anchors.fill: parent
+4 -2
View File
@@ -46,7 +46,8 @@ Item {
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
Behavior on strokeColor {
CAnim {}
CAnim {
}
}
PathAngleArc {
@@ -83,7 +84,8 @@ Item {
}
}
Behavior on color {
CAnim {}
CAnim {
}
}
Anim on waveProgress {
id: waveProgAnim
+2 -1
View File
@@ -7,6 +7,7 @@ ClippingRectangle {
color: "transparent"
Behavior on color {
CAnim {}
CAnim {
}
}
}
+4 -2
View File
@@ -20,7 +20,8 @@ Text {
textFormat: Text.PlainText
Behavior on color {
CAnim {}
CAnim {
}
}
Behavior on text {
enabled: root.animate
@@ -32,7 +33,8 @@ Text {
type: Anim.FastEffects
}
PropertyAction {}
PropertyAction {
}
TAnim {
target: root
+2 -1
View File
@@ -65,7 +65,8 @@ Image {
value: root.source
}
PropertyAction {}
PropertyAction {
}
ScriptAction {
script: root.maybeStartInAnim()
+2 -1
View File
@@ -64,7 +64,8 @@ MaterialShape {
to: 360
}
Behavior on color {
CAnim {}
CAnim {
}
}
ElapsedTimer {
+3 -2
View File
@@ -1,11 +1,12 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
RowLayout {
SnapLayout {
id: root
required property var greeter
+1
View File
@@ -200,5 +200,6 @@ Singleton {
ElapsedTimer {
id: timer
}
}
+1
View File
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
+10 -4
View File
@@ -3,17 +3,20 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Services
ColumnLayout {
SnapLayout {
id: root
required property var greeter
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.large
orientation: Qt.Vertical
CustomText {
Layout.fillWidth: true
@@ -37,8 +40,9 @@ ColumnLayout {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: ColumnLayout {
sourceComponent: SnapLayout {
spacing: Tokens.spacing.largeIncreased
orientation: Qt.Vertical
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
@@ -84,7 +88,7 @@ ColumnLayout {
}
}
RowLayout {
SnapLayout {
id: row
anchors.fill: parent
@@ -97,9 +101,11 @@ ColumnLayout {
text: modelData.kind === "x11" ? "tv" : "desktop_windows"
}
ColumnLayout {
SnapLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.small / 2
orientation: Qt.Vertical
CustomText {
Layout.fillWidth: true
+10 -4
View File
@@ -3,18 +3,21 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Helpers
import qs.Services
ColumnLayout {
SnapLayout {
id: root
required property var greeter
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.large
orientation: Qt.Vertical
CustomText {
Layout.fillWidth: true
@@ -38,8 +41,9 @@ ColumnLayout {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: ColumnLayout {
sourceComponent: SnapLayout {
spacing: Tokens.spacing.largeIncreased
orientation: Qt.Vertical
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
@@ -86,7 +90,7 @@ ColumnLayout {
}
}
RowLayout {
SnapLayout {
id: row
anchors.fill: parent
@@ -112,9 +116,11 @@ ColumnLayout {
}
}
ColumnLayout {
SnapLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.small / 2
orientation: Qt.Vertical
CustomText {
Layout.fillWidth: true
+1
View File
@@ -16,6 +16,7 @@ ShellRoot {
GreeterState {
id: greeter
}
GreeterSurface {
+2 -1
View File
@@ -69,7 +69,8 @@ Singleton {
model: Quickshell.screens
Monitor {}
Monitor {
}
}
Process {
+2 -1
View File
@@ -33,7 +33,8 @@ Singleton {
implicitHeight: 0
implicitWidth: 0
mask: Region {}
mask: Region {
}
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ CustomRect {
radius: Tokens.rounding.full
width: row.implicitWidth + Tokens.padding.small * 2
RowLayout {
SnapLayout {
id: row
anchors.centerIn: parent
+43 -105
View File
@@ -5,7 +5,7 @@ import Quickshell
import Quickshell.Io
import QtQuick
import ZShell.Models
import ZShell.Wallpaper
import ZShell.Internal
import ZShell.Config
import qs.Components
import qs.Services
@@ -16,48 +16,24 @@ Searcher {
id: root
property string actualCurrent: WallpaperPath.currentWallpaperPath
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent
readonly property string currentThumbnail: thumbnailFor(current)
readonly property bool currentIsVideo: isVideoPath(current)
property alias monitorCrops: monitorCrops
property bool pendingPreviewClear
property bool previewColorLock
property string previewPath
property bool recentlyChanged
property bool showPreview: false
property int cropVersion: 0
function getCategoryFor(w: WallpaperEntry): string {
function getCategoryFor(w: FileSystemEntry): string {
let category = w.parentDir.slice(Paths.wallsdir.length + 1);
if (category.includes("/"))
category = category.slice(0, category.indexOf("/"));
return category;
}
readonly property var videoExts: ["mp4", "mkv", "webm", "avi", "mov", "wmv", "flv", "m4v", "mpg", "mpeg", "3gp", "ogv", "ts", "vob"]
function isVideoPath(path: string): bool {
if (!path)
return false;
const dot = path.lastIndexOf(".");
if (dot < 0)
return false;
return videoExts.includes(path.slice(dot + 1).toLowerCase());
}
function thumbnailFor(path: string): string {
if (!path)
return "";
for (const entry of root.list) {
if (entry.path === path)
return entry.thumbnailPath || path;
}
return path;
}
function getCrop(screen: string): var {
return wallpapers.crop(root.current, screen);
return root.crops[screen];
}
function preview(path: string): void {
@@ -74,18 +50,19 @@ Searcher {
else if (zoom > 5.0)
zoom = 5.0;
wallpapers.setCrop(root.current, screen, {
root.crops[screen] = {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
zoom: zoom
});
};
}
function setWallpaper(path: string): void {
actualCurrent = path;
WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 1, 1), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.colors.schemeGen)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
@@ -119,13 +96,38 @@ Searcher {
target: "wallpaper"
}
WallpaperDb {
FileView {
id: monitorCrops
path: `${Paths.state}/wallpaper-crops.json`
watchChanges: true
onAdapterUpdated: cropWriteDelay.restart()
onFileChanged: reload()
JsonAdapter {
id: adapter
property var monitorCrops: ({})
}
}
Timer {
id: cropWriteDelay
interval: 100
repeat: false
running: false
onTriggered: monitorCrops.writeAdapter()
}
FileSystemModel {
id: wallpapers
dbPath: `${Paths.state}/wallpapers.sqlite`
cacheSize: wallpaper.largestBufferSize
requireHardwareCodec: true
filter: FileSystemModel.Images
path: Config.general.wallpaperPath
recursive: true
}
Process {
@@ -144,100 +146,36 @@ Searcher {
WallpaperService {
id: wallpaper
dbPath: wallpapers.dbPath
path: root.current
}
Connections {
target: wallpapers
function onCacheStored(path: string): void {
if (path === root.current)
wallpaper.invalidateCache();
}
function onCropsChanged(path: string): void {
if (path === root.current)
root.cropVersion++;
}
}
Variants {
id: screenVariants
model: Quickshell.screens
WallpaperObject {
id: obj
readonly property var crops: root.getCrop(modelData.name)
required property var modelData
// Crop animation only for edits, never for wallpaper swaps.
property bool animateCrop: false
cropHeight: crops?.height ?? 1.0
cropWidth: crops?.width ?? 1.0
cropX: crops?.x ?? 0.0
cropY: crops?.y ?? 0.0
screen: modelData.name
service: wallpaper
// Apply the wallpaper's own crop. Driven off the service's pathChanged
// (which fires after applyPath has snapshotted the outgoing frame with
// the outgoing crop) and off crop edits, never as a reactive binding on
// the wallpaper path — so a swap applies the incoming crop atomically
// after the base is captured.
function seedCrop(): void {
// Load the current wallpaper's crop without pushing: the service
// already read it from the database. This only seeds the values so
// a later edit animates from the right starting point.
obj.syncCrop(root.getCrop(modelData.name));
}
// Apply an edited crop: assign via the setters so the Behaviors animate
// the numbers and push each intermediate value to the service.
function editCrop(): void {
const c = root.getCrop(modelData.name);
obj.cropX = c?.x ?? 0.0;
obj.cropY = c?.y ?? 0.0;
obj.cropWidth = c?.width ?? 1.0;
obj.cropHeight = c?.height ?? 1.0;
obj.zoom = c?.zoom ?? 1.0;
}
Component.onCompleted: seedCrop()
onScreenChanged: seedCrop()
zoom: crops?.zoom ?? 1.0
Behavior on cropHeight {
enabled: obj.animateCrop
Anim {}
}
Behavior on cropWidth {
enabled: obj.animateCrop
Anim {}
}
Behavior on cropX {
enabled: obj.animateCrop
Anim {}
}
Behavior on cropY {
enabled: obj.animateCrop
Anim {}
}
}
}
Connections {
target: wallpaper
function onPathChanged(): void {
for (const obj of screenVariants.instances) {
obj.animateCrop = false;
obj.seedCrop();
}
}
}
Connections {
target: root
function onCropVersionChanged(): void {
for (const obj of screenVariants.instances) {
obj.animateCrop = true;
obj.editCrop();
}
}
}
}
+58 -8
View File
@@ -4,11 +4,12 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Modules.Bar.Components
import qs.Modules.Bar.Popouts
GridLayout {
SnapLayout {
id: root
required property bool fullscreen
@@ -74,9 +75,47 @@ GridLayout {
return ch?.entryId ?? "";
}
columnSpacing: Tokens.spacing.small
columns: horizontal ? -1 : 1
rowSpacing: Tokens.spacing.small
readonly property var sectionRects: {
const count = repeater.count; // dependency: re-run when items are added/removed
const names = ["start", "middle", "end"]; // constant ids by position
const bounds = [null];
let spacers = 0;
for (let i = 0; i < count; i++) {
const it = repeater.itemAt(i) as EntryWrapper;
if (!it)
continue;
if (it.entryId === "spacer") {
bounds.push(null);
spacers++;
continue;
}
const l = it.x, t = it.y, r = it.x + it.width, b = it.y + it.height;
const cur = bounds[spacers];
bounds[spacers] = cur ? {
l: Math.min(cur.l, l),
t: Math.min(cur.t, t),
r: Math.max(cur.r, r),
b: Math.max(cur.b, b)
} : {
l,
t,
r,
b
};
}
return bounds.map((c, n) => ({
id: names[n] ?? `section${n}`,
rect: c ? Qt.rect(c.l, c.t, c.r - c.l, c.b - c.t) : Qt.rect(0, 0, 0, 0)
}));
}
spacing: Tokens.spacing.small
orientation: horizontal ? Qt.Horizontal : Qt.Vertical
Repeater {
id: repeater
@@ -210,7 +249,8 @@ GridLayout {
roleValue: "network"
delegate: EntryWrapper {
NetworkWidget {}
NetworkWidget {
}
}
}
@@ -228,16 +268,26 @@ GridLayout {
}
component EntryWrapper: Item {
readonly property int section: {
let n = 0;
for (let i = 0; i < index; i++) {
if (repeater.model.values[i].id === "spacer")
n++;
}
return n;
}
readonly property string entryId: modelData.id
required property int index
default property Item item
required property var modelData
default property Item item
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
Layout.bottomMargin: !root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.rowSpacing + Tokens.spacing.extraSmall : 0
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.spacing + Tokens.spacing.extraSmall : 0
Layout.rightMargin: root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.columnSpacing + Tokens.spacing.extraSmall : 0
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.spacing + Tokens.spacing.extraSmall : 0
children: item
implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0
+1
View File
@@ -9,6 +9,7 @@ import qs.Components
Item {
id: root
readonly property var sections: (content.item as Bar)?.sectionRects ?? []
readonly property int clampedExtent: Math.max(Config.bar.border, extent)
readonly property int contentThickness: Math.floor(Math.max(Config.bar.height, Tokens.sizes.bar.innerSize * (horizontal ? 1 : 1.5))) + padding * 2
readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentThickness
+5 -5
View File
@@ -4,6 +4,7 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Modules.Bar.Popouts
import qs.Modules.Bar.Components.Common
import qs.Helpers
@@ -43,11 +44,9 @@ WidgetBase {
timeTokens: timeTokens
};
}
required property GridLayout loader
required property SnapLayout loader
required property Wrapper popouts
readonly property int roundedWidth: Math.round(timeText.implicitWidth)
readonly property int evenWidth: roundedWidth % 2 === 0 ? roundedWidth : roundedWidth + 1
readonly property real size: horizontal ? evenWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
required property ScreenState screenState
implicitHeight: horizontal ? baseSize : size
@@ -69,7 +68,8 @@ WidgetBase {
}
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: verticalColumn
anchors.centerIn: parent
@@ -24,7 +24,7 @@ Row {
color: Battery.getColors(root.percentage, root.devState).bg
height: root.batHeight
radius: root.radius
width: root.batWidth - 1
width: root.batWidth
CustomText {
color: Battery.getColors(root.percentage, root.devState).fg
+2 -1
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Components.Common
@@ -13,7 +14,7 @@ WidgetBase {
readonly property int textWidth: Math.min(metrics.width, 200)
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.small * 2
implicitWidth: horizontal ? (layout.implicitWidth % 2 === 0 ? layout.implicitWidth : layout.implicitWidth + 1) + Tokens.padding.small * 2 : baseSize
implicitWidth: horizontal ? layout.implicitWidth + Tokens.padding.small * 2 : baseSize
radius: Tokens.rounding.full
Behavior on implicitHeight {
+2 -1
View File
@@ -1,6 +1,7 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import qs.Modules
@@ -11,7 +12,7 @@ Item {
anchors.top: parent.top
implicitWidth: layout.implicitWidth
RowLayout {
SnapLayout {
id: layout
anchors.bottom: parent.bottom
+13 -8
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import qs.Components
import ZShell.Config
@@ -12,6 +13,7 @@ GridLayout {
property real animatedPercentage: 0
readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI
readonly property real dpr: (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1
required property bool horizontal
property string icon
required property color iconColor
@@ -22,12 +24,17 @@ GridLayout {
property bool warning: percentage * 100 >= warningThreshold
property int warningThreshold: 80
function snap(v) {
return Math.round(v * dpr) / dpr;
}
columnSpacing: Tokens.spacing.medium
columns: horizontal ? -1 : 1
percentage: 0
Behavior on animatedPercentage {
Anim {}
Anim {
}
}
Component.onCompleted: animatedPercentage = percentage
@@ -53,7 +60,7 @@ GridLayout {
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4
Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
radius: Tokens.rounding.full
radius: Math.min(width, height) / 2
Item {
id: clipper
@@ -63,20 +70,18 @@ GridLayout {
anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
clip: true
implicitHeight: root.horizontal ? root.percentage * track.height : track.height * 2
implicitWidth: root.horizontal ? track.width * 2 : root.percentage * track.width
height: root.horizontal ? root.snap(root.percentage * track.height) : track.height * 2
width: root.horizontal ? track.width * 2 : root.snap(root.percentage * track.width)
CustomRect {
anchors.bottom: root.horizontal ? parent.bottom : undefined
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
color: root.mainColor
height: track.height
radius: track.radius
implicitHeight: track.height
implicitWidth: track.width
width: track.width
}
}
}
+1 -2
View File
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell.Services
import qs.Services
@@ -33,7 +34,6 @@ WidgetBase {
anchors.horizontalCenterOffset: root.horizontal ? 0 : 1
anchors.verticalCenterOffset: root.horizontal ? 0 : -3
columnSpacing: Tokens.spacing.small
rowSpacing: 0
columns: root.horizontal ? -1 : 1
ServiceRef {
@@ -52,7 +52,6 @@ WidgetBase {
Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: root.horizontal
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "memory"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
+7 -7
View File
@@ -4,6 +4,7 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Helpers
import qs.Modules.Bar.Components.StatusIcons
import qs.Modules.Bar.Components.Common
@@ -31,7 +32,7 @@ WidgetBase {
return -1;
}
readonly property real size: horizontal ? grid.implicitWidth + grid.anchors.leftMargin + grid.anchors.rightMargin : grid.implicitHeight + grid.anchors.bottomMargin + grid.anchors.topMargin
readonly property int spacing: Tokens.spacing.extraSmall
readonly property int spacing: Tokens.spacing.medium / 2
// Entries that can shrink to nothing, spacing included
function collapsed(entry: var): bool {
@@ -59,7 +60,7 @@ WidgetBase {
Anim {}
}
GridLayout {
SnapLayout {
id: grid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.medium
@@ -67,9 +68,8 @@ WidgetBase {
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
columns: root.horizontal ? -1 : 1
columnSpacing: root.spacing
rowSpacing: root.spacing
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
Repeater {
model: ScriptModel {
@@ -134,8 +134,8 @@ WidgetBase {
Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0
Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap)
children: item
implicitHeight: item?.height ?? item?.implicitHeight ?? 0
implicitWidth: item?.width ?? item?.implicitWidth ?? 0
implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0
Behavior on leftGap {
Anim {
@@ -1,12 +1,13 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Daemons
import qs.Components
import qs.Services
MaterialIcon {
id: root
id: speaker
required property bool horizontal
@@ -15,8 +16,6 @@ MaterialIcon {
fill: 1
fontStyle: Tokens.font.icon.small
text: Audio.muted ? "volume_off" : "volume_up"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth {
Anim {}
@@ -1,12 +1,13 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Daemons
import qs.Components
import qs.Services
MaterialIcon {
id: root
id: mic
required property bool horizontal
@@ -15,8 +16,6 @@ MaterialIcon {
fill: 1
fontStyle: Tokens.font.icon.small
text: Audio.sourceMuted ? "mic_off" : "mic"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth {
Anim {}
@@ -1,6 +1,7 @@
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Modules.Bar.Components.Common
import qs.Components
+3 -2
View File
@@ -2,10 +2,11 @@ import QtQuick.Layouts
import QtQuick
import Quickshell
import Quickshell.Services.SystemTray
import ZShell.Config
import ZShell.Components
import qs.Helpers
import qs.Modules.Bar.Popouts
import qs.Components
import ZShell.Config
import qs.Services
Item {
@@ -15,7 +16,7 @@ Item {
readonly property real dpr: Hypr.monitorFor(loader?.screen).scale
required property int ind
required property SystemTrayItem item
required property GridLayout loader
required property SnapLayout loader
required property Wrapper popouts
function resolveIcon(app: string, icon: string): string {
+5 -5
View File
@@ -4,6 +4,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell.Services.SystemTray
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Modules.Bar.Components.Common
import qs.Modules.Bar.Components.Tray
@@ -15,7 +16,7 @@ WidgetBase {
readonly property alias items: repeater
readonly property alias layout: sysGrid
required property GridLayout loader
required property SnapLayout loader
readonly property int padding: Tokens.padding.extraSmall
required property Wrapper popouts
readonly property real size: horizontal ? sysGrid.implicitWidth + sysGrid.anchors.leftMargin + sysGrid.anchors.rightMargin : sysGrid.implicitHeight + sysGrid.anchors.topMargin + sysGrid.anchors.bottomMargin
@@ -28,7 +29,7 @@ WidgetBase {
radius: Tokens.rounding.full
topRightRadius: horizontal ? Tokens.rounding.small / 2 : Tokens.rounding.full
GridLayout {
SnapLayout {
id: sysGrid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
@@ -36,9 +37,8 @@ WidgetBase {
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
columns: root.horizontal ? -1 : 1
rowSpacing: -2
columnSpacing: Tokens.spacing.extraSmall
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
Repeater {
id: repeater
+1
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Components.Common
+1 -1
View File
@@ -17,7 +17,7 @@ Item {
readonly property int maxSize: {
const otherModules = bar.children.filter(c => c.enabled && c.entryId && c.item !== this && c.entryId !== "spacer");
const otherSize = otherModules.reduce((acc, curr) => acc + (horizontal ? (curr.item?.nonAnimWidth ?? curr.width ?? 0) : (curr.item.nonAnimHeight ?? curr.height ?? 0)), 0);
return horizontal ? (bar.width - otherSize - bar.columnSpacing * (bar.children.length - 1) - bar.vPadding * 2) : (bar.height - otherSize - bar.rowSpacing * (bar.children.length - 1) - bar.vPadding * 4);
return horizontal ? (bar.width - otherSize - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2) : (bar.height - otherSize - bar.spacing * (bar.children.length - 1) - bar.vPadding * 4);
}
clip: true
+27 -25
View File
@@ -12,14 +12,13 @@ import qs.Services
Item {
id: root
property real activeWorkspaceMargin: Tokens.padding.extraSmall / 2
readonly property int spacing: Tokens.spacing.extraSmall / 2
property real activeWorkspaceMargin: Math.ceil(Tokens.padding.medium / 4)
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
required property bool horizontal
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
required property ShellScreen screen
readonly property real shortSize: Math.max(Config.bar.height, Tokens.sizes.bar.innerSize)
readonly property real size: (workspaceButtonWidth * workspacesShown) + (spacing * Math.max(0, workspacesShown - 1)) + activeWorkspaceMargin * 2
readonly property real size: (workspaceButtonWidth * workspacesShown) + activeWorkspaceMargin * 2
property int workspaceButtonWidth: (horizontal ? bgRect.implicitHeight : bgRect.implicitWidth) - root.activeWorkspaceMargin * 2
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)
@@ -60,17 +59,15 @@ Item {
anchors.fill: parent
anchors.margins: root.activeWorkspaceMargin
columnSpacing: root.spacing
columnSpacing: 0
columns: root.horizontal ? root.workspacesShown : 1
rowSpacing: root.spacing
rowSpacing: 0
rows: root.horizontal ? 1 : root.workspacesShown
layer.enabled: true
layer.mipmap: true
Repeater {
model: root.workspaces
CustomRect {
Button {
id: button
required property int index
@@ -78,19 +75,27 @@ Item {
implicitHeight: indicator.indicatorThickness
implicitWidth: indicator.indicatorThickness
radius: Tokens.rounding.full
width: root.workspaceButtonWidth
CustomText {
anchors.centerIn: parent
color: Colors.palette.m3onSecondaryContainer
text: button.modelData?.name ?? ""
background: Item {
id: workspaceButtonBackground
implicitHeight: root.workspaceButtonWidth
implicitWidth: root.workspaceButtonWidth
CustomText {
anchors.centerIn: parent
color: Colors.palette.m3onSecondaryContainer
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.modelData?.name ?? ""
verticalAlignment: Text.AlignVCenter
}
}
StateLayer {
onPressed: {
const ws = button.modelData.name;
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
}
onPressed: {
const ws = button.modelData.name;
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
}
}
}
@@ -99,10 +104,8 @@ Item {
CustomRect {
id: indicator
readonly property real step: root.workspaceButtonWidth + root.spacing
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth + Math.abs(idxPair.idx1 - idxPair.idx2) * root.spacing
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * step + root.activeWorkspaceMargin
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
property real indicatorThickness: root.workspaceButtonWidth
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter
@@ -126,10 +129,9 @@ Item {
implicitHeight: grid.height
implicitWidth: grid.width
source: grid
autoPaddingEnabled: false
sourceColor: Colors.palette.m3onSurface
x: root.horizontal ? -indicator.x + root.activeWorkspaceMargin : 0
y: root.horizontal ? 0 : -indicator.y + root.activeWorkspaceMargin
x: root.horizontal ? -indicator.x + 3 : 0
y: root.horizontal ? 0 : -indicator.y + 3
}
}
}
+15 -10
View File
@@ -5,6 +5,7 @@ import Quickshell.Services.Pipewire
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import QtQuick.Controls
import ZShell.Config
import qs.Components
@@ -46,7 +47,8 @@ Item {
anchors.verticalCenter: parent.verticalCenter
}
component VolumesTab: ColumnLayout {
component VolumesTab: SnapLayout {
orientation: Qt.Vertical
spacing: 12
CustomRect {
@@ -56,14 +58,15 @@ Item {
color: Colors.tPalette.m3surfaceContainer
radius: root.rounding - vol.anchors.margins
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small
RowLayout {
SnapLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -81,7 +84,7 @@ Item {
}
}
RowLayout {
SnapLayout {
spacing: Tokens.spacing.largeIncreased
CustomMouseArea {
@@ -153,14 +156,15 @@ Item {
}
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small
RowLayout {
SnapLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -178,7 +182,7 @@ Item {
}
}
RowLayout {
SnapLayout {
spacing: Tokens.spacing.largeIncreased
CustomMouseArea {
@@ -276,14 +280,15 @@ Item {
text: Audio.getStreamName(appBox.modelData)
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small
RowLayout {
SnapLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -302,7 +307,7 @@ Item {
}
}
RowLayout {
SnapLayout {
spacing: Tokens.spacing.largeIncreased
CustomMouseArea {
+2 -1
View File
@@ -50,7 +50,8 @@ Item {
}
Behavior on offsetScale {
Anim {}
Anim {
}
}
Behavior on x {
enabled: !root.horizontal || root.offsetScale < 1
+3 -1
View File
@@ -4,6 +4,7 @@ import Quickshell
import Quickshell.Networking
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Modules
@@ -14,7 +15,8 @@ Item {
required property var wrapper
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: layout
spacing: 8
+17 -8
View File
@@ -4,6 +4,7 @@ import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import QtQuick.Controls
import QtQuick.Effects
import qs.Components
@@ -26,15 +27,20 @@ StackView {
initialItem: SubMenu {
handle: root.trayItem
}
popEnter: NoAnim {}
popExit: NoAnim {}
pushEnter: NoAnim {}
pushExit: NoAnim {}
popEnter: NoAnim {
}
popExit: NoAnim {
}
pushEnter: NoAnim {
}
pushExit: NoAnim {
}
Component {
id: subMenuComp
SubMenu {}
SubMenu {
}
}
component NoAnim: Transition {
@@ -42,7 +48,8 @@ StackView {
duration: 0
}
}
component SubMenu: ColumnLayout {
component SubMenu: SnapLayout {
orientation: Qt.Vertical
id: menu
required property QsMenuHandle handle
@@ -54,10 +61,12 @@ StackView {
spacing: Tokens.spacing.extraSmall
Behavior on opacity {
Anim {}
Anim {
}
}
Behavior on scale {
Anim {}
Anim {
}
}
Component.onCompleted: shown = true
+2 -1
View File
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell.Services.UPower
import ZShell.Config
import qs.Modules.Bar.Components.Common
@@ -35,7 +36,7 @@ Column {
Repeater {
model: Battery.allPeripherals
RowLayout {
SnapLayout {
id: layout
required property var modelData
+5 -3
View File
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import ZShell.Config
import qs.Components
@@ -86,7 +87,7 @@ CustomRect {
implicitWidth: 600
radius: root.radius - Tokens.padding.extraSmall
RowLayout {
SnapLayout {
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
@@ -96,7 +97,8 @@ CustomRect {
text: "package_2"
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
Layout.fillWidth: true
CustomText {
@@ -114,7 +116,7 @@ CustomRect {
}
}
RowLayout {
SnapLayout {
Layout.fillHeight: true
Layout.preferredWidth: 300
+5 -6
View File
@@ -4,6 +4,7 @@ import Quickshell
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import qs.Helpers
import ZShell.Config
@@ -54,7 +55,7 @@ Item {
}
}
CustomRect {
ClipRect {
id: preview
anchors.bottom: parent.bottom
@@ -64,7 +65,6 @@ Item {
color: Colors.tPalette.m3surfaceContainer
implicitWidth: ClipHistory.previewIsImage ? Math.max(Math.min(imagePreview.sourceSize.width + Tokens.padding.large * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth) : Math.max(Math.min(textPreviewColumn.width + textPreviewColumn.anchors.margins * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth)
radius: 25
clip: true
Behavior on implicitWidth {
Anim {}
@@ -111,7 +111,7 @@ Item {
Repeater {
model: lineRow.modelData.text.split("\t").slice(1)
RowLayout {
SnapLayout {
height: lineText.height
width: lineText.height + Tokens.spacing.extraSmall
@@ -225,16 +225,15 @@ Item {
}
}
RowLayout {
SnapLayout {
anchors.fill: parent
spacing: Tokens.spacing.small
Item {
ClipRect {
id: textRect
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
Item {
id: textWrapper
+5 -4
View File
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell
import qs.Components
@@ -39,8 +40,8 @@ Item {
];
return allTabs.filter(tab => tab.enabled);
}
readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin
readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
readonly property real nonAnimHeight: ZUtils.snap(tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin, root)
readonly property real nonAnimWidth: ZUtils.snap(view.implicitWidth + viewWrapper.anchors.margins * 2, root)
required property ScreenState screenState
implicitHeight: nonAnimHeight
@@ -93,8 +94,8 @@ Item {
}
anchors.fill: parent
implicitHeight: currentItem?.implicitHeight ?? 0
implicitWidth: currentItem?.implicitWidth ?? 0
implicitHeight: currentItem?.height ?? currentItem?.implicitHeight ?? 0
implicitWidth: currentItem?.width ?? currentItem?.implicitWidth ?? 0
Repeater {
id: repeater
+55 -77
View File
@@ -1,104 +1,82 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell
import ZShell.Config
import ZShell.Components
import qs.Helpers
import qs.Components
import qs.Modules
import ZShell.Config
import qs.Modules.Dashboard.Dash
import qs.Services
GridLayout {
SnapLayout {
orientation: Qt.Vertical
id: root
readonly property bool dashboardVisible: screenState.dashboard
required property int radius
required property ScreenState screenState
columnSpacing: Tokens.spacing.small
rowSpacing: Tokens.spacing.small
spacing: ZUtils.snap(Tokens.spacing.small, root)
ParallelAnimation {
id: openAnim
height: ZUtils.snap(implicitHeight, root)
width: ZUtils.snap(implicitWidth, root)
Anim {
property: "opacity"
target: root
to: 1
}
Anim {
property: "scale"
target: root
to: 1
}
}
ParallelAnimation {
id: closeAnim
Anim {
property: "opacity"
target: root
to: 0
}
Anim {
property: "scale"
target: root
to: 0.9
}
}
Rect {
Layout.column: 2
Layout.columnSpan: 3
Layout.preferredHeight: user.implicitHeight
Layout.preferredWidth: user.implicitWidth
radius: root.radius
User {
id: user
screenState: root.screenState
}
}
Rect {
Layout.columnSpan: 2
Layout.fillHeight: true
Layout.preferredWidth: Tokens.sizes.dashboard.weatherWidth
Layout.row: 0
radius: root.radius
Weather {}
}
Rect {
Layout.column: 0
Layout.columnSpan: 3
SnapLayout {
Layout.fillWidth: true
Layout.preferredHeight: calendar.implicitHeight
Layout.row: 1
radius: root.radius
spacing: parent.spacing
Calendar {
id: calendar
Rect {
id: weatherRect
Layout.fillHeight: true
Layout.preferredWidth: ZUtils.snap(Tokens.sizes.dashboard.weatherWidth, root)
radius: root.radius
screenState: root.screenState
Weather {}
}
Rect {
id: userRect
Layout.preferredHeight: ZUtils.snap(user.implicitHeight, root)
Layout.preferredWidth: ZUtils.snap(user.implicitWidth, root)
radius: root.radius
User {
id: user
screenState: root.screenState
}
}
}
Rect {
Layout.column: 3
Layout.columnSpan: 2
Layout.fillHeight: true
Layout.preferredWidth: resources.implicitWidth
Layout.row: 1
radius: root.radius
SnapLayout {
Layout.fillWidth: true
spacing: parent.spacing
Resources {
id: resources
Rect {
id: calendarRect
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: ZUtils.snap(calendar.implicitHeight, root)
radius: root.radius
Calendar {
id: calendar
screenState: root.screenState
}
}
Rect {
id: resourcesRect
Layout.fillHeight: true
implicitWidth: ZUtils.snap(resources.implicitWidth, root)
radius: root.radius
Resources {
id: resources
}
}
}
+8 -4
View File
@@ -4,6 +4,7 @@ import Quickshell
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Services
@@ -117,14 +118,15 @@ CustomMouseArea {
}
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: inner
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraSmall
RowLayout {
SnapLayout {
id: monthNavigationRow
Layout.fillWidth: true
@@ -307,10 +309,12 @@ CustomMouseArea {
}
}
Behavior on x {
Anim {}
Anim {
}
}
Behavior on y {
Anim {}
Anim {
}
}
onTodayItemChanged: {
+10 -7
View File
@@ -1,5 +1,7 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell
import ZShell.Services
import ZShell.Config
import qs.Components
@@ -8,9 +10,8 @@ import qs.Services
Item {
id: root
anchors.bottom: parent.bottom
anchors.top: parent.top
implicitWidth: layout.implicitWidth + layout.anchors.margins * 4
anchors.fill: parent
implicitWidth: ZUtils.snap(layout.implicitWidth + layout.anchors.margins * 2, root)
ServiceRef {
service: Storage
@@ -28,13 +29,16 @@ Item {
service: Gpu
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: layout
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: Tokens.padding.large
anchors.top: parent.top
x: ZUtils.snap((root.width - implicitWidth) / 2, root)
spacing: Tokens.spacing.medium
Resource {
@@ -73,8 +77,7 @@ Item {
required property string icon
Layout.fillHeight: true
implicitSize: height
implicitSize: ZUtils.snap((root.height - layout.anchors.margins * 2 - layout.spacing * 4) / 5, root)
Behavior on clampedVal {
Anim {}
+9 -5
View File
@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import M3Shapes
import ZShell
import ZShell.Config
import qs.Modules.Dashboard.Media
import qs.Components
@@ -13,8 +15,8 @@ Item {
property ScreenState screenState
required property int radius
implicitWidth: 1000
implicitHeight: 320
implicitWidth: ZUtils.snap(1000, root)
implicitHeight: ZUtils.snap(320, root)
ClipRect {
anchors.fill: parent
@@ -25,7 +27,7 @@ Item {
}
}
RowLayout {
SnapLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraLarge
@@ -94,7 +96,8 @@ Item {
active: opacity > 0
opacity: 0
sourceComponent: ColumnLayout {
sourceComponent: SnapLayout {
orientation: Qt.Vertical
spacing: Tokens.spacing.small
MaterialShape {
@@ -140,11 +143,12 @@ Item {
asynchronous: true
active: opacity > 0
sourceComponent: RowLayout {
sourceComponent: SnapLayout {
spacing: Tokens.spacing.extraLarge
Details {
Layout.fillWidth: true
Layout.fillHeight: true
}
LyricsAndSelector {
+4 -2
View File
@@ -33,7 +33,8 @@ Item {
Repeater {
id: shapes
DriftingShape {}
DriftingShape {
}
}
FrameAnimation {
@@ -79,7 +80,8 @@ Item {
shape: root.shapePool[Math.floor(Math.random() * root.shapePool.length)]
Behavior on color {
CAnim {}
CAnim {
}
}
Behavior on opacity {
Anim {
+2 -1
View File
@@ -18,7 +18,8 @@ Item {
layer.enabled: true
Behavior on fallbackColor {
CAnim {}
CAnim {
}
}
layer.effect: MultiEffect {
blurMax: 1
+178 -154
View File
@@ -1,13 +1,14 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.Mpris
import ZShell
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
import qs.Helpers
ColumnLayout {
Item {
id: root
readonly property bool hasUnknownLength: (Players.active?.length ?? 0) > 2147483647
@@ -25,46 +26,187 @@ ColumnLayout {
return `${mins}:${secs}`;
}
spacing: Tokens.spacing.extraSmall
implicitHeight: ZUtils.snap(layout.implicitHeight, root)
implicitWidth: layout.implicitWidth
Timer {
running: Players.active?.isPlaying ?? false
interval: Config.dashboard.mediaUpdateInterval
triggeredOnStart: true
repeat: true
onTriggered: Players.active?.positionChanged()
}
SnapLayout {
orientation: Qt.Vertical
id: layout
anchors.left: parent.left
anchors.right: parent.right
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
CustomText {
Layout.fillWidth: true
text: Players.active?.trackTitle ?? ""
font: Tokens.font.title.large
elide: Text.ElideRight
animate: true
}
spacing: ZUtils.snap(Tokens.spacing.extraSmall, root)
CustomText {
Layout.fillWidth: true
text: Players.active?.trackArtist || qsTr("Unknown artist")
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.title.medium
elide: Text.ElideRight
animate: true
}
Timer {
running: Players.active?.isPlaying ?? false
interval: Config.dashboard.mediaUpdateInterval
triggeredOnStart: true
repeat: true
onTriggered: Players.active?.positionChanged()
}
CustomText {
Layout.fillWidth: true
text: Players.active?.trackAlbum || qsTr("Unknown album")
color: Colors.palette.m3secondary
font: Tokens.font.title.medium
elide: Text.ElideRight
animate: true
}
CustomText {
id: title
Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root)
text: Players.active?.trackTitle ?? ""
font: Tokens.font.title.large
elide: Text.ElideRight
animate: true
}
RowLayout {
Layout.topMargin: Tokens.spacing.extraLargeIncreased
Layout.fillWidth: true
spacing: Tokens.spacing.small
CustomText {
id: artist
Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root)
text: Players.active?.trackArtist || qsTr("Unknown artist")
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.title.medium
elide: Text.ElideRight
animate: true
}
CustomText {
id: album
Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root)
text: Players.active?.trackAlbum || qsTr("Unknown album")
color: Colors.palette.m3secondary
font: Tokens.font.title.medium
elide: Text.ElideRight
animate: true
}
SnapLayout {
id: sliderLayout
Layout.topMargin: Tokens.spacing.extraLargeIncreased
Layout.fillWidth: true
spacing: ZUtils.snap(Tokens.spacing.small, root)
CustomText {
id: positionLabel
Layout.preferredWidth: ZUtils.snapCeil(timeMetrics.width, root)
Layout.preferredHeight: positionSlider.implicitHeight
text: root.lengthStr(Players.active?.position ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
CustomSlider {
id: positionSlider
Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snap(implicitHeight, root)
value: Players.active ? Players.active.position / (Players.active.length || 1) : 0
enabled: (Players.active?.canSeek ?? false) && !root.hasUnknownLength
wavy: true
animateWave: Players.active?.isPlaying ?? false
waveFrequency: 5
waveDuration: 2000
interactionOnMove: false
onInteraction: value => {
const active = Players.active;
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
}
Binding {
target: positionLabel
property: "text"
value: root.lengthStr(positionSlider.pos * (Players.active?.length ?? 0))
when: positionSlider.dragging
}
}
CustomText {
Layout.preferredWidth: ZUtils.snapCeil(timeMetrics.width, root)
Layout.preferredHeight: positionSlider.implicitHeight
text: root.hasUnknownLength ? "--:--" : root.lengthStr(Players.active?.length ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
ButtonRow {
Layout.topMargin: Tokens.spacing.largeIncreased
Layout.fillWidth: true
spacing: Tokens.spacing.extraSmall
IconButton {
type: IconButton.Tonal
icon: "shuffle"
isRound: true
shapeMorph: true
checked: Players.active?.shuffle ?? false
font: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
enabled: Players.active?.shuffleSupported
onClicked: Players.active.shuffle = !Players.active?.shuffle
implicitWidth: Math.round(implicitHeight * 0.9)
}
IconButton {
id: previousBtn
type: IconButton.Tonal
icon: "skip_previous"
isRound: true
shapeMorph: true
font: Tokens.font.icon.large
enabled: Players.active?.canGoPrevious
onClicked: Players.active?.previous()
}
IconButton {
id: playPauseBtn
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
isRound: true
shapeMorph: true
fillWidth: true
checked: Players.active?.isPlaying ?? false
font: Tokens.font.icon.large
enabled: Players.active?.canTogglePlaying
onClicked: Players.active?.togglePlaying()
}
IconButton {
id: nextBtn
type: IconButton.Tonal
icon: "skip_next"
isRound: true
shapeMorph: true
font: Tokens.font.icon.large
enabled: Players.active?.canGoNext
onClicked: Players.active?.next()
}
IconButton {
type: IconButton.Tonal
icon: Players.active?.loopState === MprisLoopState.Track ? "repeat_one" : "repeat"
isRound: true
shapeMorph: true
checked: Players.active?.loopState === MprisLoopState.Track || Players.active?.loopState === MprisLoopState.Playlist
font: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
enabled: Players.active?.loopSupported
onClicked: {
const state = Players.active.loopState;
if (state === MprisLoopState.None)
Players.active.loopState = MprisLoopState.Track;
else if (state === MprisLoopState.Track)
Players.active.loopState = MprisLoopState.Playlist;
else
Players.active.loopState = MprisLoopState.None;
}
implicitWidth: Math.round(implicitHeight * 0.9)
}
}
TextMetrics {
id: timeMetrics
@@ -72,123 +214,5 @@ ColumnLayout {
text: Players.active ? root.lengthStr(Math.max(Players.active.position, root.hasUnknownLength ? 0 : Players.active.length)).replace(/[1-9]/g, "0") : "00:00"
font: Tokens.font.label.medium
}
CustomText {
id: positionLabel
Layout.preferredWidth: timeMetrics.width
text: root.lengthStr(Players.active?.position ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
}
CustomSlider {
id: positionSlider
Layout.fillWidth: true
value: Players.active ? Players.active.position / (Players.active.length || 1) : 0
enabled: (Players.active?.canSeek ?? false) && !root.hasUnknownLength
wavy: true
animateWave: Players.active?.isPlaying ?? false
waveFrequency: 5
waveDuration: 2000
interactionOnMove: false
onInteraction: value => {
const active = Players.active;
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
}
Binding {
target: positionLabel
property: "text"
value: root.lengthStr(positionSlider.pos * (Players.active?.length ?? 0))
when: positionSlider.dragging
}
}
CustomText {
Layout.preferredWidth: timeMetrics.width
text: root.hasUnknownLength ? "--:--" : root.lengthStr(Players.active?.length ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
}
}
ButtonRow {
Layout.topMargin: Tokens.spacing.largeIncreased
Layout.fillWidth: true
spacing: Tokens.spacing.extraSmall
IconButton {
type: IconButton.Tonal
icon: "shuffle"
isRound: true
shapeMorph: true
checked: Players.active?.shuffle ?? false
font: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
enabled: Players.active?.shuffleSupported
onClicked: Players.active.shuffle = !Players.active?.shuffle
implicitWidth: Math.round(implicitHeight * 0.9)
}
IconButton {
id: previousBtn
type: IconButton.Tonal
icon: "skip_previous"
isRound: true
shapeMorph: true
font: Tokens.font.icon.large
enabled: Players.active?.canGoPrevious
onClicked: Players.active?.previous()
}
IconButton {
id: playPauseBtn
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
isRound: true
shapeMorph: true
fillWidth: true
checked: Players.active?.isPlaying ?? false
font: Tokens.font.icon.large
enabled: Players.active?.canTogglePlaying
onClicked: Players.active?.togglePlaying()
}
IconButton {
id: nextBtn
type: IconButton.Tonal
icon: "skip_next"
isRound: true
shapeMorph: true
font: Tokens.font.icon.large
enabled: Players.active?.canGoNext
onClicked: Players.active?.next()
}
IconButton {
type: IconButton.Tonal
icon: Players.active?.loopState === MprisLoopState.Track ? "repeat_one" : "repeat"
isRound: true
shapeMorph: true
checked: Players.active?.loopState === MprisLoopState.Track || Players.active?.loopState === MprisLoopState.Playlist
font: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
enabled: Players.active?.loopSupported
onClicked: {
const state = Players.active.loopState;
if (state === MprisLoopState.None)
Players.active.loopState = MprisLoopState.Track;
else if (state === MprisLoopState.Track)
Players.active.loopState = MprisLoopState.Playlist;
else
Players.active.loopState = MprisLoopState.None;
}
implicitWidth: Math.round(implicitHeight * 0.9)
}
}
}
+5 -2
View File
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell.Services
import qs.Components
@@ -157,7 +158,8 @@ Item {
active: opacity > 0
opacity: 0
sourceComponent: ColumnLayout {
sourceComponent: SnapLayout {
orientation: Qt.Vertical
spacing: Tokens.spacing.large
CustomRect {
@@ -198,7 +200,8 @@ Item {
active: opacity > 0
opacity: 0
sourceComponent: ColumnLayout {
sourceComponent: SnapLayout {
orientation: Qt.Vertical
spacing: Tokens.spacing.small
MaterialIcon {
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import Quickshell.Services.Mpris
import ZShell.Config
@@ -8,14 +9,15 @@ import qs.Helpers
import qs.Services
Item {
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: layout
anchors.fill: parent
anchors.leftMargin: Tokens.padding.medium
spacing: Tokens.spacing.medium
RowLayout {
SnapLayout {
Layout.bottomMargin: -Tokens.spacing.medium
spacing: Tokens.spacing.medium
z: 1
+3 -1
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell
import ZShell.Blobs
import ZShell.Config
@@ -141,7 +142,8 @@ Item {
}
]
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: layout
anchors.centerIn: parent
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import ZShell.Config
import ZShell.Internal
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import Quickshell.Widgets
import ZShell.Config
@@ -38,7 +39,7 @@ CustomListView {
id: rowHover
}
RowLayout {
SnapLayout {
id: rowLayout
anchors.left: parent.left
+6 -4
View File
@@ -23,7 +23,7 @@ Item {
service: Memory
}
RowLayout {
SnapLayout {
id: layout
anchors.fill: parent
spacing: Tokens.spacing.medium
@@ -60,13 +60,14 @@ Item {
Layout.fillHeight: true
implicitHeight: mainLayout.implicitHeight + mainLayout.anchors.margins * 2
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: mainLayout
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: 0
RowLayout {
SnapLayout {
spacing: Tokens.spacing.small
MaterialIcon {
@@ -131,7 +132,8 @@ Item {
}
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: infoColumn
anchors.right: parent.right
anchors.bottom: parent.bottom
+57 -32
View File
@@ -4,6 +4,8 @@ import QtQuick
import QtQuick.Layouts
import QtQuick.Templates
import Quickshell
import ZShell
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
@@ -16,9 +18,17 @@ Item {
required property var tabs
readonly property alias count: bar.count
readonly property int tabIndicatorSpacing: Tokens.spacing.small
readonly property int tabIndicatorSpacing: ZUtils.snap(Tokens.spacing.small, root)
implicitHeight: bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
function snapLayoutHeight(v) {
const dpr = ZUtils.dprFor(root);
let h = Math.ceil(v);
while (Math.abs(h * dpr - Math.round(h * dpr)) > 1e-6)
++h;
return h;
}
implicitHeight: ZUtils.snap(bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight, root)
TabBar {
id: bar
@@ -31,9 +41,11 @@ Item {
currentIndex: root.screenState.dashboardTab
onCurrentIndexChanged: root.screenState.dashboardTab = currentIndex
implicitHeight: contentHeight
topPadding: 0
bottomPadding: 0
implicitHeight: implicitContentHeight
background: null
contentItem: RowLayout {
contentItem: SnapLayout {
spacing: 0
Repeater {
@@ -116,10 +128,13 @@ Item {
readonly property bool current: TabBar.tabBar.currentItem === this
Layout.fillWidth: true
Layout.preferredWidth: 1 // Uniform width across all tabs
Layout.fillHeight: true
Layout.preferredWidth: 1
implicitWidth: implicitContentWidth
implicitHeight: implicitContentHeight
background: null
topPadding: 0
bottomPadding: 0
contentItem: CustomMouseArea {
id: mouse
@@ -131,8 +146,8 @@ Item {
root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0);
}
implicitWidth: Math.max(icon.width, label.width)
implicitHeight: icon.height + label.height
implicitWidth: Math.max(icon.implicitWidth, label.implicitWidth)
implicitHeight: snapLayout.implicitHeight
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
@@ -146,39 +161,49 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: parent.height + root.tabIndicatorSpacing * 2
anchors.alignWhenCentered: false
implicitHeight: ZUtils.snap(parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2, stateLayer)
radius: Tokens.rounding.medium
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurface
onClicked: root.screenState.dashboardTab = tab.TabBar.index
}
MaterialIcon {
id: icon
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: label.top
text: tab.iconName
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
fill: tab.current ? 1 : 0
fontStyle: Tokens.font.icon.medium
Behavior on fill {
Anim {
type: Anim.DefaultEffects
}
onClicked: {
root.screenState.dashboardTab = tab.TabBar.index;
}
}
CustomText {
id: label
SnapLayout {
id: snapLayout
anchors.fill: parent
spacing: 0
orientation: Qt.Vertical
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
MaterialIcon {
id: icon
text: tab.text
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
text: tab.iconName
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
fill: tab.current ? 1 : 0
fontStyle: Tokens.font.icon.medium
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
Behavior on fill {
Anim {
type: Anim.DefaultEffects
}
}
}
CustomText {
id: label
text: tab.text
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
}
}
}
}
+12 -6
View File
@@ -1,11 +1,13 @@
import QtQuick
import QtQuick.Layouts
import ZShell
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
import qs.Helpers
RowLayout {
SnapLayout {
id: root
spacing: Tokens.spacing.largeIncreased
@@ -20,7 +22,7 @@ RowLayout {
readonly property real markerX: {
const d = root.now;
return d.getHours() + d.getMinutes() / 60;
return ZUtils.snap(d.getHours() + d.getMinutes() / 60, root);
}
function formatHour(h) {
@@ -39,11 +41,13 @@ RowLayout {
onTriggered: root.now = new Date()
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2
RowLayout {
SnapLayout {
Layout.fillWidth: true
MaterialIcon {
@@ -90,11 +94,13 @@ RowLayout {
}
}
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2
RowLayout {
SnapLayout {
Layout.fillWidth: true
MaterialIcon {
+14 -7
View File
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Modules.Dashboard.Weather
import qs.Services
@@ -18,13 +19,14 @@ Item {
implicitHeight: layout.implicitHeight
Component.onCompleted: Weather.reload()
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: layout
anchors.fill: parent
spacing: Tokens.spacing.medium
RowLayout {
SnapLayout {
Layout.leftMargin: Tokens.padding.large
Layout.rightMargin: Tokens.padding.large
Layout.fillWidth: true
@@ -82,7 +84,7 @@ Item {
}
}
RowLayout {
SnapLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
@@ -121,7 +123,9 @@ Item {
color: Colors.palette.m3onSurface
}
RowLayout {
SnapLayout {
id: forecastLayout
Layout.fillWidth: true
spacing: Tokens.spacing.medium
@@ -137,12 +141,14 @@ Item {
required property var modelData
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
radius: root.radius
color: Colors.tPalette.m3surfaceContainer
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: forecastItemColumn
anchors.centerIn: parent
@@ -196,7 +202,8 @@ Item {
property color colour
Layout.fillWidth: true
Layout.preferredHeight: 60
Layout.fillHeight: true
implicitHeight: 60
radius: Tokens.rounding.medium
color: Colors.tPalette.m3surfaceContainer
@@ -230,7 +237,7 @@ Item {
}
}
component WeatherStat: RowLayout {
component WeatherStat: SnapLayout {
id: weatherStat
property string icon
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import Quickshell.Widgets
import Quickshell.Hyprland
@@ -43,7 +44,8 @@ Item {
}
opacity: root.visible ? 1 : 0
ColumnLayout {
SnapLayout {
orientation: Qt.Vertical
id: menuLayout
anchors.centerIn: parent
spacing: 0
@@ -53,7 +55,7 @@ Item {
radius: popupBackground.radius - popupBackground.padding
implicitHeight: openTerminalRow.implicitHeight + Tokens.padding.extraSmall * 2
RowLayout {
SnapLayout {
id: openTerminalRow
spacing: 8
anchors.fill: parent
@@ -92,7 +94,7 @@ Item {
radius: popupBackground.radius - popupBackground.padding
implicitHeight: settingsRow.implicitHeight + Tokens.padding.extraSmall * 2
RowLayout {
SnapLayout {
id: settingsRow
spacing: 8
anchors.fill: parent
@@ -132,7 +134,7 @@ Item {
radius: popupBackground.radius - popupBackground.padding
implicitHeight: logoutRow.implicitHeight + Tokens.padding.extraSmall * 2
RowLayout {
SnapLayout {
id: logoutRow
spacing: 8
anchors.fill: parent
@@ -171,7 +173,7 @@ Item {
// radius: popupBackground.radius - popupBackground.padding
// implicitHeight: desktopIconsRow.implicitHeight + Tokens.padding.extraSmall * 2
//
// RowLayout {
// SnapLayout {
// id: desktopIconsRow
// spacing: 8
// anchors.fill: parent
@@ -3,6 +3,7 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import qs.Components
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -57,22 +58,25 @@ Item {
Anim {}
}
ColumnLayout {
SnapLayout {
id: menuLayout
anchors.centerIn: parent
spacing: 0
orientation: Qt.Vertical
CustomRect {
Layout.preferredWidth: 160
implicitHeight: openRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
RowLayout {
SnapLayout {
id: openRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -111,11 +115,12 @@ Item {
implicitHeight: openWithRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
RowLayout {
SnapLayout {
id: openWithRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -156,11 +161,12 @@ Item {
implicitHeight: copyPathRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
RowLayout {
SnapLayout {
id: copyPathRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -190,11 +196,12 @@ Item {
radius: popupBackground.radius - popupBackground.padding
visible: contextMenu.targetPaths.length === 1
RowLayout {
SnapLayout {
id: renameRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -231,11 +238,12 @@ Item {
implicitHeight: deleteRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
RowLayout {
SnapLayout {
id: deleteRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
+10 -5
View File
@@ -54,12 +54,14 @@ Item {
Behavior on x {
enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath)
Anim {}
Anim {
}
}
Behavior on y {
enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath)
Anim {}
Anim {
}
}
Item {
@@ -83,7 +85,8 @@ Item {
y: (root.iconsRoot.selectedIcons.includes(root.filePath) && root.iconsRoot.dragLeader !== "" && root.iconsRoot.dragLeader !== root.filePath) ? root.iconsRoot.groupDragY : 0
}
transitions: Transition {
Anim {}
Anim {
}
}
onXChanged: {
@@ -205,7 +208,8 @@ Item {
radius: Tokens.rounding.small
Behavior on opacity {
Anim {}
Anim {
}
}
}
@@ -264,7 +268,8 @@ Item {
radius: Tokens.rounding.small
Behavior on opacity {
Anim {}
Anim {
}
}
}
}
+9 -3
View File
@@ -4,6 +4,7 @@ import Quickshell
import Quickshell.Widgets
import qs.Components
import qs.Helpers
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -29,7 +30,10 @@ CustomRect {
active: !isSeparator
anchors.centerIn: parent
sourceComponent: ColumnLayout {
sourceComponent: SnapLayout {
spacing: Tokens.spacing.extraSmall
orientation: Qt.Vertical
IconImage {
id: icon
@@ -38,8 +42,9 @@ CustomRect {
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel?.appId), "image-missing")
}
RowLayout {
SnapLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 3
Repeater {
@@ -82,7 +87,8 @@ CustomRect {
Loader {
active: isSeparator
sourceComponent: DockSeparator {}
sourceComponent: DockSeparator {
}
anchors {
fill: parent
+1
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Services
+3 -1
View File
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import ZShell
import ZShell.Config
import qs.Components
import qs.Services
@@ -52,7 +53,7 @@ CustomListView {
highlightFollowsCurrentItem: false
highlightRangeMode: ListView.ApplyRange
implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
implicitHeight: (Tokens.sizes.launcher.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
orientation: Qt.Vertical
preferredHighlightBegin: 0
preferredHighlightEnd: height
@@ -97,6 +98,7 @@ CustomListView {
}
}
highlight: CustomRect {
id: hl
color: Colors.palette.m3onSurface
implicitHeight: root.currentItem?.implicitHeight ?? 0
implicitWidth: root.width
+1 -1
View File
@@ -22,7 +22,7 @@ CarouselView {
delegateProperties: ({
screenState: root.screenState
})
focalWidth: Config.launcher.sizes.wallpaperWidth
focalWidth: Tokens.sizes.launcher.wallpaperWidth
glideDuration: 250
glideEasingType: Easing.OutCubic
implicitHeight: tileHeight + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.small
+3 -2
View File
@@ -2,17 +2,18 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import ZShell
import ZShell.Config
import qs.Modules.Launcher.Services
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
Item {
id: root
required property real maxHeight
readonly property int padding: Tokens.padding.largeIncreased / 4
readonly property int padding: ZUtils.snap(Tokens.padding.medium / 2, root)
required property var panels
readonly property int rounding: Tokens.rounding.largeIncreased
required property ScreenState screenState
+5 -4
View File
@@ -2,11 +2,12 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import ZShell
import ZShell.Config
import qs.Paths
import qs.Modules
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
Item {
@@ -71,7 +72,7 @@ Item {
PropertyChanges {
appList.active: true
root.implicitHeight: Math.min(root.maxHeight, appList.implicitHeight > 0 ? appList.implicitHeight : empty.implicitHeight)
root.implicitWidth: Config.launcher.sizes.itemWidth
root.implicitWidth: Tokens.sizes.launcher.itemWidth
}
AnchorChanges {
@@ -83,8 +84,8 @@ Item {
name: "wallpapers"
PropertyChanges {
root.implicitHeight: Config.launcher.sizes.wallpaperHeight
root.implicitWidth: Math.max(Config.launcher.sizes.itemWidth * 1.2, wallpaperList.implicitWidth)
root.implicitHeight: Tokens.sizes.launcher.wallpaperHeight
root.implicitWidth: Math.max(Tokens.sizes.launcher.itemWidth * 1.2, wallpaperList.implicitWidth)
wallpaperList.active: true
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ Item {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: Config.launcher.sizes.itemHeight
implicitHeight: Tokens.sizes.launcher.itemHeight
StateLayer {
radius: Tokens.rounding.medium

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