Author SHA1 Message Date
AramJonghu 39f99bb336 ci: rework Dockerfile image + buildx dep added
JS/TS / fmt & lint (pull_request) Successful in 13s
Python / test & nuitka build (pull_request) Canceled after 5s
Python / fmt & lint (pull_request) Canceled after 11s
Rust / build, clippy & fmt (pull_request) Canceled after 6s
C++ / fmt (pull_request) Failing after 5s
C++ / build & lint (pull_request) Failing after 2m14s
2026-10-23 04:08:48 +02:00
AramJonghu 068319a4cb refactor(workflows): combined steps where it makes sense 2026-10-23 03:05:40 +02:00
AramJonghu bdb95af7a1 fix(ci): missing dependency wayland-scanner 2026-10-23 03:05:04 +02:00
261 changed files with 3388 additions and 12713 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
run: apk add --no-cache docker-cli docker-cli-buildx
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin
+1 -19
View File
@@ -20,28 +20,10 @@ jobs:
-exec clang-format -i --style=file {} +
git diff --exit-code && echo "clang-format: passed"
build:
build & lint:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- 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
+1 -15
View File
@@ -4,7 +4,7 @@ on:
pull_request:
jobs:
fmt:
fmt & lint:
runs-on: alpine
container: node:26-alpine
@@ -24,20 +24,6 @@ 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:
static:
fmt & lint:
runs-on: debian
container: node:26-trixie-slim
@@ -33,7 +33,7 @@ jobs:
. .venv/bin/activate
ruff check .
verify:
test & nuitka build:
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/
+21 -86
View File
@@ -3,8 +3,9 @@ name: Rust
on:
pull_request:
# jobs can be separated if we integrate tests
jobs:
build:
build, clippy & fmt:
runs-on: alpine
container: node:26-alpine
env:
@@ -34,7 +35,9 @@ jobs:
apk add --no-cache \
git \
cargo \
rust
rust \
rust-clippy \
rustfmt
- name: Cargo check
run: |
@@ -50,90 +53,6 @@ jobs:
else
echo "No Rust project found"
fi
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: |
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
@@ -150,3 +69,19 @@ jobs:
else
echo "No Rust project found"
fi
- name: Format check
run: |
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
-1
View File
@@ -50,7 +50,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(QT_NO_PRIVATE_MODULE_WARNING ON)
set(ENABLE_MODULES "plugin;shell;cli;m3shapes" CACHE STRING "Modules to build/install")
set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir")
+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
+4 -3
View File
@@ -99,7 +99,6 @@ ProgressBar {
}
Line {
id: dot
property real implicitSize
anchors.right: parent.right
@@ -125,7 +124,8 @@ ProgressBar {
anchors.left: parent.left
Behavior on implicitWidth {
Anim {}
Anim {
}
}
Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition)
@@ -222,7 +222,8 @@ ProgressBar {
}
}
Behavior on color {
CAnim {}
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
+6 -6
View File
@@ -2,10 +2,10 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell
import ZShell.Components
import ZShell.Config
import ZShell
import qs.Components
import ZShell.Config
import qs.Services
Slider {
@@ -28,7 +28,7 @@ Slider {
signal interaction(v: real)
implicitHeight: ZUtils.snap(12, root)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
@@ -123,7 +123,7 @@ Slider {
bottomRightRadius: Tokens.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: ZUtils.snap(root.filledWidth, root)
implicitWidth: root.filledWidth
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: ZUtils.snap(root.filledWidth, root)
lineWidth: ZUtils.snap(root.height * 0.7, root)
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
+39 -41
View File
@@ -2,57 +2,55 @@ import QtQuick
import ZShell.Config
import qs.Services
Item {
CustomClippingRect {
id: track
required property var scrollBar
required property var mouseArea
ClipRect {
radius: Tokens.rounding.full
anchors.left: parent.left
anchors.right: parent.right
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight
radius: Tokens.rounding.full
CustomRect {
id: handle
anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight
CustomRect {
id: handle
anchors.bottom: parent.bottom
color: Colors.palette.m3secondary
implicitWidth: track.scrollBar.width * track.scrollBar.effectiveSize
implicitHeight: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
color: Colors.palette.m3secondary
implicitWidth: track.scrollBar.width * track.scrollBar.effectiveSize
implicitHeight: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
}
radius: Tokens.rounding.full
x: track.scrollBar.reversed ? track.scrollBar.width * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.width * track.scrollBar.nonAnimY * track.scrollBar.travelScale
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
x: track.scrollBar.reversed ? track.scrollBar.width * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.width * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitHeight {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
Behavior on implicitHeight {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
+2 -3
View File
@@ -1,5 +1,4 @@
import QtQuick
import ZShell
import ZShell.Config
import qs.Services
@@ -16,9 +15,9 @@ ButtonBase {
const h = label.implicitHeight + padding * 2;
if (h % 2 !== 0)
return h + 1;
return ZUtils.snap(h, root);
return h;
}
implicitWidth: ZUtils.snap(implicitHeight, root)
implicitWidth: implicitHeight
inactiveColor: {
if (!isToggle && type === IconButton.Filled)
return Colors.palette.m3primary;
+1 -3
View File
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -35,11 +34,10 @@ ButtonBase {
}
verticalPadding: Tokens.padding.extraSmall
SnapLayout {
RowLayout {
id: row
anchors.centerIn: parent
spacing: Tokens.spacing.small
MaterialIcon {
+66 -89
View File
@@ -1,8 +1,6 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtCanvas2D
import ZShell
Item {
id: root
@@ -38,62 +36,54 @@ Item {
property real fillAlpha: 0.15
property real lineWidth: 2
property real markerDotRadius: 4
property real labelFontSize: 8
property real labelFontSize: 10
Canvas2D {
Canvas {
id: canvas
anchors.fill: parent
alphaBlending: true
fillColor: "transparent"
renderStrategy: Canvas.Immediate
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);
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);
if (root.curveSmoothing && pts.length > 2) {
const k = 1 / 6;
const n = pts.length;
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));
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);
}
ctx.lineTo(p2.x, p2.y);
}
}
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;
}
// Control points for segment i (pts[i] -> pts[i+1]), matching strokeCurve
function segmentControls(pts, i) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
@@ -119,6 +109,7 @@ 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++) {
@@ -147,16 +138,10 @@ Item {
const t = (tlo + thi) / 2;
const u = 1 - t;
const y = u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
return {
x: targetX,
y
};
}
function cutPoint(pts, targetX) {
return pointAtX(pts, targetX);
return { x: targetX, y };
}
// Part of the curve left/right of a cut x, including the cut point
function cutLeft(pts, targetX) {
if (targetX <= pts[0].x)
return [];
@@ -173,7 +158,7 @@ Item {
const left = pts.slice(0, j + 1);
if (targetX > pts[j].x)
left.push(cutPoint(pts, targetX));
left.push(pointAtX(pts, targetX));
return left;
}
@@ -199,20 +184,17 @@ 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 * 5;
const padRight = 0;
const padTop = 0;
const padBottom = root.labelFontSize + 14;
const padLeft = root.labelFontSize * 3.4;
const padRight = 2;
const padTop = 2;
const padBottom = root.labelFontSize + 8;
const plot = {
x: padLeft,
y: padTop,
@@ -222,6 +204,8 @@ 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);
@@ -295,17 +279,11 @@ 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;
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;
// 1. Grid lines
ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha);
ctx.lineWidth = gridW;
ctx.lineWidth = 1;
if (root.verticalLineStep > 0) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
@@ -314,7 +292,7 @@ Item {
if (i === root.xSteps && !root.lastXLine)
continue;
const x = snapLine(xAt(i), 1);
const x = crisp(xAt(i));
ctx.beginPath();
ctx.moveTo(x, plot.y);
ctx.lineTo(x, H);
@@ -322,11 +300,12 @@ 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 = snapLine(yAt(v), 1);
const y = crisp(yAt(v));
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(plot.x + plot.w, y);
@@ -334,8 +313,9 @@ Item {
}
}
// 2. Labels
ctx.fillStyle = Qt.alpha(root.labelColor, root.labelAlpha);
ctx.font = root.labelFontSize + "pt 'sans-serif'";
ctx.font = root.labelFontSize + "px sans-serif";
ctx.textAlign = "left";
ctx.textBaseline = "top";
@@ -348,7 +328,7 @@ Item {
const text = root.xLabel(i);
if (text)
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 8);
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 4);
}
}
@@ -361,6 +341,7 @@ Item {
}
}
// 3. Curve, split into segments at null gaps
const segments = [];
let current = null;
for (let i = 0; i < series.length; i++) {
@@ -371,7 +352,7 @@ Item {
continue;
}
const pt = {
x: snapV(xAt(i)),
x: xAt(i),
y: yAt(series[i])
};
if (!current)
@@ -388,6 +369,8 @@ 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)
@@ -399,6 +382,7 @@ 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);
@@ -406,6 +390,7 @@ 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) {
@@ -413,32 +398,24 @@ Item {
continue;
ctx.strokeStyle = root.lineColor;
ctx.lineWidth = ZUtils.snap(root.lineWidth, root);
ctx.lineWidth = root.lineWidth;
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 = 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;
}
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(crisp(x), plot.y);
ctx.lineTo(crisp(x), H);
ctx.stroke();
ctx.setLineDash([]);
const i0 = Math.floor(root.markerX);
const v0 = series[i0];
@@ -453,7 +430,7 @@ Item {
if (y != null) {
ctx.fillStyle = root.markerColor;
ctx.beginPath();
ctx.circle(snapV(x), y, root.markerDotRadius);
ctx.arc(x, y, root.markerDotRadius, 0, Math.PI * 2);
ctx.fill();
}
}
+7 -9
View File
@@ -4,7 +4,6 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import ZShell.Components
import qs.Drawers
import qs.Services
@@ -35,7 +34,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;
@@ -102,7 +101,8 @@ MouseArea {
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {}
Anim {
}
}
}
@@ -111,14 +111,12 @@ MouseArea {
color: Colors.palette.m3surfaceContainerLow
radius: parent.radius
SnapLayout {
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.extraSmall
orientation: Qt.Vertical
Repeater {
id: repeater
@@ -139,7 +137,8 @@ MouseArea {
radius: menu.radius - column.anchors.margins
Behavior on radius {
Anim {}
Anim {
}
}
StateLayer {
@@ -154,12 +153,11 @@ MouseArea {
}
}
SnapLayout {
RowLayout {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Tokens.padding.medium
spacing: Tokens.spacing.small
MaterialIcon {
+1 -2
View File
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -54,7 +53,7 @@ Row {
onClicked: root.active?.clicked()
}
SnapLayout {
RowLayout {
id: textRow
anchors.centerIn: parent
+1 -1
View File
@@ -12,7 +12,7 @@ TextField {
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: TextField.QtRendering
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.QtRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
+2 -5
View File
@@ -1,5 +1,4 @@
import ZShell
import ZShell.Components
import QtQuick
import QtQuick.Layouts
import qs.Components
@@ -48,7 +47,7 @@ CustomRect {
z: -1
}
SnapLayout {
RowLayout {
id: layout
anchors.fill: parent
@@ -89,11 +88,9 @@ CustomRect {
}
}
SnapLayout {
ColumnLayout {
Layout.fillWidth: true
spacing: 0
orientation: Qt.Vertical
CustomText {
id: title
+1 -1
View File
@@ -30,7 +30,7 @@ CustomFlickable {
type: Anim.SlowEffects
}
}
layer.textureSize: Qt.size(width * dpr, height * dpr)
// layer.textureSize: Qt.size(width * dpr, height * dpr)
layer.effect: Mask {
maskSource: mask
+39 -41
View File
@@ -2,57 +2,55 @@ import QtQuick
import ZShell.Config
import qs.Services
Item {
CustomClippingRect {
id: track
required property var scrollBar
required property var mouseArea
ClipRect {
anchors.bottom: parent.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full
CustomRect {
id: handle
anchors.right: parent.right
anchors.top: parent.top
radius: Tokens.rounding.full
implicitWidth: handle.implicitWidth
CustomRect {
id: handle
anchors.right: parent.right
color: Colors.palette.m3secondary
implicitHeight: track.scrollBar.height * track.scrollBar.effectiveSize
implicitWidth: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
color: Colors.palette.m3secondary
implicitHeight: track.scrollBar.height * track.scrollBar.effectiveSize
implicitWidth: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
}
radius: Tokens.rounding.full
y: track.scrollBar.reversed ? track.scrollBar.height * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.height * track.scrollBar.nonAnimY * track.scrollBar.travelScale
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: track.scrollBar.reversed ? track.scrollBar.height * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.height * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitWidth {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
Behavior on implicitWidth {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
+9 -1
View File
@@ -43,6 +43,13 @@ Scope {
hasBar: root.geometry.barOnBottom
}
Timer {
interval: 5000
running: true
onTriggered: console.log("top height:", top.exclusiveZone, "left width:", left.exclusiveZone, "right width:", right.exclusiveZone, "bottom height:", bottom.exclusiveZone)
}
component ExclusionZone: CustomWindow {
property bool hasBar
@@ -52,6 +59,7 @@ Scope {
name: "Bar-Exclusion"
screen: root.screen
mask: Region {}
mask: Region {
}
}
}
+11 -16
View File
@@ -1,7 +1,5 @@
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
@@ -16,15 +14,20 @@ 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
required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock
readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
@@ -33,19 +36,14 @@ Item {
readonly property alias popoutsWrapper: popouts
readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar
readonly property alias toasts: toasts
readonly property alias utilities: utilities
required property Item bar
required property real borderThickness
required property var drawingItem
required property EdgeGeometry geometry
required property ShellScreen screen
required property ScreenState screenState
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness)
@@ -75,10 +73,7 @@ Item {
anchors.right: parent.right
anchors.rightMargin: sidebar.width * (1 - sidebar.offsetScale)
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
// anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenter: parent.verticalCenter
clip: sidebar.visible
implicitHeight: osd.implicitHeight
implicitWidth: osd.implicitWidth * (1 - osd.offsetScale)
@@ -86,7 +81,8 @@ Item {
Osd.Wrapper {
id: osd
anchors.fill: parent
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
screen: root.screen
screenState: root.screenState
sidebarOrSessionVisible: sidebar.visible
@@ -132,9 +128,8 @@ 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
+31 -18
View File
@@ -3,12 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Helpers
import qs.Services
SnapLayout {
ColumnLayout {
id: root
readonly property real centerScale: Math.min(1, screenHeight / 1440)
@@ -19,32 +18,36 @@ SnapLayout {
Layout.fillHeight: true
Layout.fillWidth: false
Layout.preferredWidth: centerWidth
orientation: Qt.Vertical
spacing: Tokens.spacing.largeIncreased * 2
SnapLayout {
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Tokens.spacing.small
CustomText {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.hourStr
}
CustomText {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: ":"
}
CustomText {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.minuteStr
}
}
@@ -53,11 +56,13 @@ SnapLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary
font: Tokens.font.title.builders.medium.weight(Font.DemiBold).build()
font.bold: true
font.family: Config.appearance.font.family.mono
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
text: Time.format("dddd, d MMMM yyyy")
}
ClipRect {
CustomClippingRect {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer
@@ -68,7 +73,7 @@ SnapLayout {
MaterialIcon {
anchors.centerIn: parent
color: Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.size(root.centerWidth / 4).build()
font.pointSize: Math.floor(root.centerWidth / 4)
text: "person"
}
@@ -83,7 +88,9 @@ SnapLayout {
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.body.large
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
font.weight: 600
text: root.greeter.username
visible: text.length > 0
}
@@ -119,7 +126,7 @@ SnapLayout {
}
}
SnapLayout {
RowLayout {
id: input
anchors.fill: parent
@@ -148,7 +155,8 @@ SnapLayout {
}
Behavior on opacity {
Anim {}
Anim {
}
}
}
@@ -183,6 +191,7 @@ SnapLayout {
anchors.centerIn: parent
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font.weight: 500
text: root.greeter.launching ? "hourglass_top" : "arrow_forward"
}
}
@@ -195,7 +204,8 @@ SnapLayout {
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
Behavior on implicitHeight {
Anim {}
Anim {
}
}
CustomText {
@@ -235,10 +245,12 @@ SnapLayout {
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Behavior on opacity {
Anim {}
Anim {
}
}
Behavior on scale {
Anim {}
Anim {
}
}
onMsgChanged: {
@@ -280,7 +292,8 @@ SnapLayout {
anchors.left: parent.left
anchors.right: parent.right
color: isError ? Colors.palette.m3error : Colors.palette.m3primary
font: Tokens.font.mono.medium
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
horizontalAlignment: Qt.AlignHCenter
opacity: 0
scale: 0.7
+1 -1
View File
@@ -20,7 +20,7 @@ BusyIndicator {
property int animState
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.body.medium.pointSize * 3
property real implicitSize: Tokens.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8
-41
View File
@@ -1,41 +0,0 @@
import QtQuick
import ZShell.Components
Item {
id: root
property real radius
property real topLeftRadius: radius
property real topRightRadius: radius
property real bottomRightRadius: radius
property real bottomLeftRadius: radius
property color color: "transparent"
default property alias contentData: contentItem.data
Item {
id: contentItem
anchors.fill: parent
}
ShaderEffectSource {
id: source
hideSource: true
sourceItem: contentItem
}
RoundedMask {
anchors.fill: parent
source: source
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
bottomLeftRadius: root.bottomLeftRadius
color: root.color
}
Behavior on color {
CAnim {}
}
}
-23
View File
@@ -1,36 +1,13 @@
import QtQuick
import qs.Services
Flickable {
id: root
property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim {
properties: "x,y"
}
}
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: {
root.doneFakeFlick = false;
root.returnToBounds();
}
}
}
-23
View File
@@ -1,36 +1,13 @@
import QtQuick
import qs.Services
ListView {
id: root
property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
Anim {
properties: "x,y"
type: Anim.DefaultEffects
}
}
// Timer {
// interval: 10
// running: root.doneFakeFlick
//
// onTriggered: {
// root.doneFakeFlick = false;
// }
// }
}
-1
View File
@@ -4,7 +4,6 @@ MouseArea {
property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void {
event.accepted = false;
}
onWheel: event => {
+2 -1
View File
@@ -6,6 +6,7 @@ Rectangle {
color: "transparent"
Behavior on color {
CAnim {}
CAnim {
}
}
}
+7 -9
View File
@@ -14,9 +14,10 @@ Text {
property real animateTo: 1
color: Colors.palette.m3onSurface
font: Tokens.font.body.small
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.normal
linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.QtRendering
renderType: Text.NativeRendering
textFormat: Text.PlainText
Behavior on color {
@@ -27,25 +28,22 @@ Text {
enabled: root.animate
SequentialAnimation {
TAnim {
target: root
Anim {
to: root.animateFrom
type: Anim.FastEffects
}
PropertyAction {
}
TAnim {
target: root
Anim {
to: root.animateTo
type: Anim.DefaultEffects
}
}
}
component TAnim: Anim {
component Anim: NumberAnimation {
duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root
+52 -281
View File
@@ -1,305 +1,76 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Shapes
import QtQuick.Controls
import ZShell.Config
import qs.Services
TextFieldBase {
TextField {
id: root
enum TextFieldType {
Outlined,
Filled
}
background: null
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.smaller
placeholderTextColor: Colors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius)
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real smallFontScale: smallFontSize / font.pointSize
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property int horizontalPadding: Tokens.padding.large
property int verticalPadding: Tokens.padding.large
property int radius: Tokens.rounding.small
property string supportingText
property string trailingIcon
property int smallFontSize: Tokens.font.label.small.pointSize
property int type: CustomTextField.Outlined
property var validate // Regex or function
property bool emptyIsValid: true
property string errorText
property bool isError
property string leadingIcon
bottomPadding: verticalPadding + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: verticalPadding + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
sourceComponent: root.type === CustomTextField.Filled ? filledComp : outlineComp
StateLayer {
id: stateLayer
cursorShape: Qt.IBeamCursor
enabled: !root.activeFocus
manualPressOverride: tapHandler.pressed
radius: root.type === CustomTextField.Outlined ? root.clampedRadius : 0
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
onClicked: root.focus = true
Behavior on color {
CAnim {
}
}
cursorDelegate: CustomRect {
id: cursor
onEditingFinished: {
if (!valid)
isError = true;
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
onTextEdited: {
if (isError)
isError = false;
}
property bool disableBlink
Item {
id: contentWrapper
color: Colors.palette.m3primary
implicitWidth: 2
radius: Tokens.rounding.medium
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
CustomText {
id: placeholder
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.topMargin: Tokens.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : root.text ? Colors.palette.m3outline : root.placeholderTextColor)
font.family: root.font.family
font.pointSize: root.font.pointSize
font.variableAxes: root.font.variableAxes
font.weight: root.font.weight
renderType: Text.QtRendering
text: root.placeholderText
states: [
State {
name: "smallOutlined"
when: root.type === CustomTextField.Outlined && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.verticalCenter: contentWrapper.top
target: placeholder
}
},
State {
name: "smallFilled"
when: root.type === CustomTextField.Filled && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + root.leadingOffset
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.top: contentWrapper.top
anchors.verticalCenter: undefined
target: placeholder
}
}
]
transitions: Transition {
Anim {
properties: "scale,leftMargin"
type: Anim.DefaultEffects
}
AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
}
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.small
}
}
Loader {
id: leadingIconLoader
active: root.leadingIcon
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.leadingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
Loader {
id: trailingIconLoader
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.trailingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
}
Loader {
id: supportingTextLoader
active: root.effectiveSupportingText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: root.horizontalPadding
sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font: Tokens.font.label.small
text: root.effectiveSupportingText
}
}
TapHandler {
id: tapHandler
}
Component {
id: outlineComp
Shape {
id: bg
asynchronous: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: path
readonly property real inset: strokeWidth / 2
readonly property real outlineGap: placeholder.width * root.smallFontScale + Tokens.spacing.small * 2
property real outlineGapScale: root.activeFocus || root.text ? 1 : 0
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: path.inset + root.horizontalPadding - root.clampedRadius + path.outlineGap * (1 - path.outlineGapScale) / 2 + path.outlineGap * path.outlineGapScale
startY: path.inset
strokeColor: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
strokeWidth: root.activeFocus ? 2 : 1
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on strokeColor {
CAnim {}
}
Behavior on strokeWidth {
Anim {}
}
PathLine {
x: bg.width - path.inset - root.clampedRadius
y: path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset
y: path.inset + root.clampedRadius
}
PathLine {
x: bg.width - path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset - root.clampedRadius
y: bg.height - path.inset
}
PathLine {
x: path.inset + root.clampedRadius
y: bg.height - path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathLine {
x: path.inset
y: path.inset + root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset + root.clampedRadius
y: path.inset
}
PathLine {
x: path.inset + root.horizontalPadding - root.clampedRadius / 2 + path.outlineGap * (1 - path.outlineGapScale) / 2
y: path.inset
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 100
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
Component {
id: filledComp
CustomRect {
color: root.activeFocus ? Colors.tPalette.m3surfaceContainerHighest : Colors.tPalette.m3surfaceContainerHigh
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
Anim {}
}
}
Behavior on placeholderTextColor {
CAnim {
}
}
}
+2 -3
View File
@@ -1,16 +1,15 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
TextInput {
renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.colSecondaryContainer
selectionColor: Colors.tPalette.m3onSecondaryContainer
font {
family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting
pixelSize: Config.appearance?.font.size.normal ?? 15
pixelSize: Tokens?.font.size.normal ?? 15
}
}
-79
View File
@@ -1,79 +0,0 @@
import QtQuick
import Quickshell
import ZShell.Config
Image {
id: root
property int fadeInAnim: Anim.DefaultEffects
property int fadeInLargeAnim: Anim.StandardLarge
property int fadeOutAnim: Anim.FastEffects
property bool fadingOut
property bool hadPrevious
property bool preventInit
function maybeStartInAnim(): void {
if (!preventInit && !opacityInAnim.running && status === Image.Ready) {
opacityInAnim.type = hadPrevious ? fadeInAnim : fadeInLargeAnim;
opacityInAnim.start();
}
}
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: 0
retainWhileLoading: true
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Anim on opacity {
id: opacityInAnim
running: false
to: 1
}
Behavior on source {
SequentialAnimation {
ScriptAction {
script: opacityInAnim.stop()
}
PropertyAction {
property: "fadingOut"
target: root
value: true
}
Anim {
property: "opacity"
target: root
to: 0
type: root.fadeOutAnim
}
PropertyAction {
property: "fadingOut"
target: root
value: false
}
PropertyAction {
property: "hadPrevious"
target: root
value: root.source
}
PropertyAction {
}
ScriptAction {
script: root.maybeStartInAnim()
}
}
}
onPreventInitChanged: maybeStartInAnim()
onStatusChanged: maybeStartInAnim()
}
-115
View File
@@ -1,115 +0,0 @@
import QtQuick
import Quickshell
import M3Shapes
import ZShell.Config
import qs.Services
MaterialShape {
id: root
property bool animated: true
property real cRotation
property bool containsIcon
property real dampingRatio: 0.6
property real lRotation
property int morphAnimRotation: 60
property real morphScale: 0.14
property alias rotateAnimDuration: rotateAnim.duration
property int shapeIndex
property list<int> shapes: {
if (containsIcon)
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pentagon, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
}
readonly property real springDuration: {
const wn = Math.sqrt(stiffness);
const r = -dampingRatio * wn;
const c = 1 / Math.sqrt(1 - dampingRatio * dampingRatio);
return Math.log(visibilityThreshold / c) / r;
}
readonly property real springMaxVelocity: {
const wn = Math.sqrt(stiffness);
const factor = Math.exp(-z * Math.acos(z) / Math.sqrt(1 - z * z));
return wn * factor;
}
property bool springSettled: true
property real stiffness: 180
property real thisLRotation
property real visibilityThreshold: 0.075
function spring(t: real): var {
const wn = Math.sqrt(stiffness);
const za = dampingRatio * wn;
const wd = wn * Math.sqrt(1 - dampingRatio * dampingRatio);
const r = za / wd;
const pos = 1 - Math.exp(-za * t) * (Math.cos(wd * t) + r * Math.sin(wd * t));
const vel = Math.exp(-za * t) * (wn * wn / wd) * Math.sin(wd * t);
return [pos, vel];
}
color: Colors.palette.m3primary
implicitSize: 38
toShape: shapes[0]
RotationAnimation on cRotation {
id: rotateAnim
duration: 4666
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.animated
to: 360
}
Behavior on color {
CAnim {
}
}
ElapsedTimer {
id: timer
}
FrameAnimation {
running: root.animated && !root.springSettled
onTriggered: {
const t = timer.elapsed();
if (t >= root.springDuration) {
root.springSettled = true;
} else {
const [pos, vel] = root.spring(t);
root.morphProgress = Math.min(1, pos); // Overshooting the morph looks weird
root.thisLRotation = pos * root.morphAnimRotation;
root.scale = 1 + vel * root.morphScale / root.springMaxVelocity;
}
}
}
Timer {
interval: 650
repeat: true
running: root.animated
triggeredOnStart: true
onTriggered: {
root.beginBatchUpdate();
root.fromShape = root.toShape;
root.shapeIndex = (root.shapeIndex + 1) % root.shapes.length;
root.toShape = root.shapes[root.shapeIndex];
root.morphProgress = 0;
root.rotation = root.rotation;
root.lRotation = (root.lRotation + root.thisLRotation) % 360;
root.thisLRotation = 0;
root.rotation = Qt.binding(() => root.cRotation + root.lRotation + root.thisLRotation);
root.springSettled = false;
timer.restart();
root.endBatchUpdate();
}
}
}
+9 -7
View File
@@ -1,14 +1,16 @@
import QtQuick
import ZShell.Config
import qs.Services
CustomText {
id: root
property real fill: 0
property real fill
property int grade: Colors.light ? 0 : -25
property font fontStyle: Tokens.font.icon.small
renderType: Text.NativeRendering
font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build()
font.family: "Material Symbols Rounded"
font.pointSize: Tokens.font.size.larger
font.variableAxes: ({
FILL: fill.toFixed(1),
GRAD: grade,
opsz: fontInfo.pixelSize,
wght: fontInfo.weight
})
}
+8 -6
View File
@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config
import qs.Services
@@ -11,6 +13,7 @@ MouseArea {
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
property bool disabled
readonly property real endRadius: {
const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
@@ -19,7 +22,6 @@ MouseArea {
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualHoverOverride
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
@@ -27,7 +29,7 @@ MouseArea {
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse || manualHoverOverride ? 0.08 : 0
property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
@@ -50,8 +52,8 @@ MouseArea {
}
anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled
cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: !disabled
hoverEnabled: true
Behavior on stateOpacity {
@@ -98,13 +100,13 @@ MouseArea {
id: base
anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
+41 -19
View File
@@ -1,12 +1,11 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
SnapLayout {
RowLayout {
id: root
required property var greeter
@@ -14,16 +13,34 @@ SnapLayout {
spacing: Tokens.spacing.largeIncreased * 2
CustomRect {
Layout.fillHeight: true
ColumnLayout {
Layout.fillWidth: true
topLeftRadius: Tokens.rounding.largeIncreased
bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
spacing: Tokens.spacing.medium
UserDock {
greeter: root.greeter
CustomRect {
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer
implicitHeight: weather.implicitHeight
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.largeIncreased
WeatherInfo {
id: weather
rootHeight: root.height
}
}
CustomClippingRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
UserDock {
greeter: root.greeter
}
}
}
@@ -32,16 +49,21 @@ SnapLayout {
screenHeight: root.screenHeight
}
CustomRect {
Layout.fillHeight: true
ColumnLayout {
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
spacing: Tokens.spacing.medium
SessionDock {
greeter: root.greeter
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
SessionDock {
greeter: root.greeter
}
}
}
}
+2 -1
View File
@@ -134,7 +134,8 @@ Scope {
id: lockIcon
anchors.centerIn: parent
fontStyle: Tokens.font.icon.builders.extraLarge.size(Tokens.font.icon.extraLarge.pointSize * 3).build()
font.bold: true
font.pointSize: Tokens.font.size.extraLarge * 4
text: "lock"
}
+15 -9
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
@@ -40,7 +39,8 @@ Item {
anchors.centerIn: parent
animate: true
color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline
font: Tokens.font.mono.medium
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
opacity: root.buffer ? 0 : 1
text: {
if (root.greeter.launching)
@@ -51,7 +51,8 @@ Item {
}
Behavior on opacity {
Anim {}
Anim {
}
}
}
@@ -63,13 +64,15 @@ Item {
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurface
elide: Text.ElideLeft
font: Tokens.font.mono.medium
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Qt.AlignHCenter
opacity: root.greeter.echoResponse && root.buffer ? 1 : 0
text: root.buffer
Behavior on opacity {
Anim {}
Anim {
}
}
}
@@ -86,7 +89,7 @@ Item {
anchors.centerIn: parent
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
implicitHeight: Tokens.font.body.medium
implicitHeight: Tokens.font.size.normal
implicitWidth: fullWidth
interactive: false
opacity: root.greeter.echoResponse ? 0 : 1
@@ -104,7 +107,8 @@ Item {
scale: 0
Behavior on opacity {
Anim {}
Anim {
}
}
Behavior on scale {
Anim {
@@ -152,13 +156,15 @@ Item {
Behavior on implicitWidth {
id: imWidthBehavior
Anim {}
Anim {
}
}
model: ScriptModel {
values: root.buffer.split("")
}
Behavior on opacity {
Anim {}
Anim {
}
}
}
}
+21 -21
View File
@@ -3,32 +3,32 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Services
SnapLayout {
ColumnLayout {
id: root
required property var greeter
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.large
orientation: Qt.Vertical
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
font.family: Config.appearance.font.family.mono
font.weight: 500
text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions")
}
Item {
CustomClippingRect {
Layout.fillHeight: true
Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader {
active: opacity > 0
@@ -40,21 +40,22 @@ SnapLayout {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: SnapLayout {
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
orientation: Qt.Vertical
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
fontStyle: Tokens.font.icon.large
font.pointSize: Tokens.font.size.extraLarge * 2
text: "desktop_windows"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build()
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Sessions Found")
}
}
@@ -67,7 +68,6 @@ SnapLayout {
clip: true
currentIndex: root.greeter.sessionIndex
highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.sessions
spacing: Tokens.spacing.small
@@ -80,7 +80,7 @@ SnapLayout {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.large
radius: Tokens.rounding.medium - Tokens.padding.small
StateLayer {
onClicked: {
@@ -88,7 +88,7 @@ SnapLayout {
}
}
SnapLayout {
RowLayout {
id: row
anchors.fill: parent
@@ -97,21 +97,20 @@ SnapLayout {
MaterialIcon {
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.extraLarge
font.pointSize: Tokens.font.size.extraLarge
text: modelData.kind === "x11" ? "tv" : "desktop_windows"
}
SnapLayout {
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.small / 2
orientation: Qt.Vertical
CustomText {
Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight
font: Tokens.font.body.builders.medium.weight(Font.Bold).build()
font.pointSize: Tokens.font.size.normal
font.weight: 600
text: modelData.name
}
@@ -119,7 +118,8 @@ SnapLayout {
Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font: Tokens.font.mono.small
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
text: modelData.kind
}
}
@@ -129,7 +129,7 @@ SnapLayout {
color: Colors.palette.m3primary
implicitHeight: sessions.currentItem?.implicitHeight ?? 0
implicitWidth: sessions.width
radius: Tokens.rounding.large
radius: Tokens.rounding.medium - Tokens.padding.small
y: sessions.currentItem?.y ?? 0
Behavior on y {
+20 -21
View File
@@ -3,33 +3,33 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Helpers
import qs.Services
SnapLayout {
ColumnLayout {
id: root
required property var greeter
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.large
orientation: Qt.Vertical
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
font.family: Config.appearance.font.family.mono
font.weight: 500
text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users")
}
Item {
CustomClippingRect {
Layout.fillHeight: true
Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader {
active: opacity > 0
@@ -41,22 +41,23 @@ SnapLayout {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: SnapLayout {
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
orientation: Qt.Vertical
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
fill: 1
fontStyle: Tokens.font.icon.large
font.pointSize: Tokens.font.size.extraLarge * 2
text: "account_circle"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build()
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Users Found")
}
}
@@ -69,7 +70,6 @@ SnapLayout {
clip: true
currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1
highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.users
spacing: Tokens.spacing.small
@@ -82,7 +82,7 @@ SnapLayout {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.large
radius: Tokens.rounding.medium - Tokens.padding.small
StateLayer {
onClicked: {
@@ -90,14 +90,14 @@ SnapLayout {
}
}
SnapLayout {
RowLayout {
id: row
anchors.fill: parent
anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium
ClipRect {
CustomClippingRect {
Layout.preferredHeight: 50
Layout.preferredWidth: 50
radius: Tokens.rounding.full
@@ -106,7 +106,7 @@ SnapLayout {
anchors.centerIn: parent
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fill: 1
fontStyle: Tokens.font.icon.extraLarge
font.pointSize: Tokens.font.size.extraLarge
text: modelData.kind === "x11" ? "tv" : "account_circle"
}
@@ -116,17 +116,16 @@ SnapLayout {
}
}
SnapLayout {
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.small / 2
orientation: Qt.Vertical
CustomText {
Layout.fillWidth: true
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight
font: Tokens.font.body.builders.medium.weight(Font.Bold).build()
font.pointSize: Tokens.font.size.normal
font.weight: 600
text: modelData.username
}
}
@@ -136,7 +135,7 @@ SnapLayout {
color: Colors.palette.m3primary
implicitHeight: users.currentItem?.implicitHeight ?? 0
implicitWidth: users.width
radius: Tokens.rounding.large
radius: Tokens.rounding.medium - Tokens.padding.small
y: users.currentItem?.y ?? 0
Behavior on y {
+171
View File
@@ -0,0 +1,171 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
ColumnLayout {
id: root
required property int rootHeight
anchors.left: parent.left
anchors.margins: Tokens.padding.large * 2
anchors.right: parent.right
spacing: Tokens.spacing.small
Loader {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: -Tokens.padding.large
Layout.topMargin: Tokens.padding.large * 2
active: root.rootHeight > 610
visible: active
sourceComponent: CustomText {
color: Colors.palette.m3primary
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
text: qsTr("Weather")
}
}
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.largeIncreased
MaterialIcon {
animate: true
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.extraLarge * 2.5
text: Weather.icon
}
ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3secondary
elide: Text.ElideRight
font.pointSize: Tokens.font.size.large
font.weight: 500
text: Weather.description
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
text: qsTr("Humidity: %1%").arg(Weather.humidity)
}
}
Loader {
Layout.rightMargin: Tokens.padding.small
active: root.width > 400
visible: active
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3primary
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
horizontalAlignment: Text.AlignRight
text: Weather.temp
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3outline
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.smaller
horizontalAlignment: Text.AlignRight
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
}
}
}
}
Loader {
id: forecastLoader
Layout.bottomMargin: Tokens.padding.large * 2
Layout.fillWidth: true
Layout.topMargin: Tokens.spacing.small
active: root.rootHeight > 820
visible: active
sourceComponent: RowLayout {
spacing: Tokens.spacing.largeIncreased
Repeater {
model: {
const forecast = Weather.hourlyForecast;
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
if (!forecast)
return Array.from({
length: count
}, () => null);
const start = Math.max(0, Weather.currentHourIndex);
return forecast.slice(start, start + count);
}
ColumnLayout {
id: forecastHour
required property var modelData
Layout.fillWidth: true
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
font.pointSize: Tokens.font.size.larger
horizontalAlignment: Text.AlignHCenter
text: {
const hour = forecastHour.modelData?.hour ?? 0;
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
}
}
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
font.pointSize: Tokens.font.size.extraLarge * 1.5
font.weight: 500
text: forecastHour.modelData?.icon ?? "cloud_alert"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.larger
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
}
}
}
}
}
Timer {
interval: 900000 // 15 minutes
repeat: true
running: true
triggeredOnStart: true
onTriggered: Weather.reload()
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ CustomRect {
radius: Tokens.rounding.full
width: row.implicitWidth + Tokens.padding.small * 2
SnapLayout {
RowLayout {
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();
}
}
}
}
+9 -57
View File
@@ -4,12 +4,11 @@ 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
SnapLayout {
GridLayout {
id: root
required property bool fullscreen
@@ -75,47 +74,9 @@ SnapLayout {
return ch?.entryId ?? "";
}
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
columnSpacing: Tokens.spacing.small
columns: horizontal ? -1 : 1
rowSpacing: Tokens.spacing.small
Repeater {
id: repeater
@@ -249,7 +210,8 @@ SnapLayout {
roleValue: "network"
delegate: EntryWrapper {
NetworkWidget {}
NetworkWidget {
}
}
}
@@ -267,26 +229,16 @@ SnapLayout {
}
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
required property var modelData
default property Item item
required property var modelData
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.spacing + Tokens.spacing.extraSmall : 0
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.rowSpacing + 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.spacing + Tokens.spacing.extraSmall : 0
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.columnSpacing + Tokens.spacing.extraSmall : 0
children: item
implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0
-1
View File
@@ -9,7 +9,6 @@ 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
+49
View File
@@ -0,0 +1,49 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Effects
import ZShell.Config
import qs.Components
import qs.Services
Item {
id: root
required property Item bar
required property ScreenState screenState
anchors.fill: parent
CustomRect {
anchors.fill: parent
anchors.margins: -1
color: Colors.palette.m3surface
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: mask
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: Config.bar.border + 1
anchors.topMargin: root.bar.implicitHeight + 1
radius: Config.bar.border > 0 ? Config.bar.rounding : 0
topLeftRadius: Config.bar.rounding
topRightRadius: Config.bar.rounding
}
}
}
+3 -7
View File
@@ -4,7 +4,6 @@ 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
@@ -44,11 +43,9 @@ WidgetBase {
timeTokens: timeTokens
};
}
required property SnapLayout loader
required property GridLayout 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
@@ -70,8 +67,7 @@ WidgetBase {
}
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
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
+1 -2
View File
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Components.Common
@@ -14,7 +13,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 {
+1 -2
View File
@@ -1,7 +1,6 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import qs.Modules
@@ -12,7 +11,7 @@ Item {
anchors.top: parent.top
implicitWidth: layout.implicitWidth
SnapLayout {
RowLayout {
id: layout
anchors.bottom: parent.bottom
+12 -9
View File
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import qs.Components
import ZShell.Config
@@ -13,6 +12,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
@@ -23,12 +23,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
@@ -54,7 +59,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
@@ -64,20 +69,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
}
}
}
-3
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell.Services
import qs.Services
@@ -34,7 +33,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 {
@@ -53,7 +51,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
+11 -10
View File
@@ -4,7 +4,6 @@ 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
@@ -32,7 +31,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 {
@@ -52,15 +51,17 @@ WidgetBase {
Behavior on implicitHeight {
enabled: !root.horizontal
Anim {}
Anim {
}
}
Behavior on implicitWidth {
enabled: root.horizontal
Anim {}
Anim {
}
}
SnapLayout {
GridLayout {
id: grid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.medium
@@ -68,8 +69,7 @@ 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
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
columns: root.horizontal ? -1 : 1
Repeater {
model: ScriptModel {
@@ -111,7 +111,8 @@ WidgetBase {
delegate: EntryWrapper {
name: "upower"
UPowerWidget {}
UPowerWidget {
}
}
}
}
@@ -134,8 +135,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,13 +1,12 @@
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
@@ -16,8 +15,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,13 +1,12 @@
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
@@ -16,8 +15,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,7 +1,6 @@
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Modules.Bar.Components.Common
import qs.Components
+2 -4
View File
@@ -2,11 +2,10 @@ 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 {
@@ -16,7 +15,7 @@ Item {
readonly property real dpr: Hypr.monitorFor(loader?.screen).scale
required property int ind
required property SystemTrayItem item
required property SnapLayout loader
required property GridLayout loader
required property Wrapper popouts
function resolveIcon(app: string, icon: string): string {
@@ -80,7 +79,6 @@ Item {
anchors.centerIn: parent
antialiasing: true
backer.retainWhileLoading: true
color: root.current && !Config.bar.tray.showOnHover ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
implicitSize: Config.bar.tray.trayIconSize * root.dpr
layer.enabled: Config.bar.tray.recolorIcons
+4 -5
View File
@@ -4,7 +4,6 @@ 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
@@ -16,7 +15,7 @@ WidgetBase {
readonly property alias items: repeater
readonly property alias layout: sysGrid
required property SnapLayout loader
required property GridLayout 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
@@ -29,7 +28,7 @@ WidgetBase {
radius: Tokens.rounding.full
topRightRadius: horizontal ? Tokens.rounding.small / 2 : Tokens.rounding.full
SnapLayout {
GridLayout {
id: sysGrid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
@@ -37,8 +36,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
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
columns: root.horizontal ? -1 : 1
rowSpacing: -2
Repeater {
id: repeater
-1
View File
@@ -1,6 +1,5 @@
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.spacing * (bar.children.length - 1) - bar.vPadding * 2) : (bar.height - otherSize - bar.spacing * (bar.children.length - 1) - bar.vPadding * 4);
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);
}
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
}
}
}
+12 -17
View File
@@ -5,7 +5,6 @@ import Quickshell.Services.Pipewire
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import QtQuick.Controls
import ZShell.Config
import qs.Components
@@ -47,8 +46,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
}
component VolumesTab: SnapLayout {
orientation: Qt.Vertical
component VolumesTab: ColumnLayout {
spacing: 12
CustomRect {
@@ -58,15 +56,14 @@ orientation: Qt.Vertical
color: Colors.tPalette.m3surfaceContainer
radius: root.rounding - vol.anchors.margins
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small
SnapLayout {
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -84,7 +81,7 @@ orientation: Qt.Vertical
}
}
SnapLayout {
RowLayout {
spacing: Tokens.spacing.largeIncreased
CustomMouseArea {
@@ -137,7 +134,7 @@ orientation: Qt.Vertical
node: Audio.source
}
ClipRect {
CustomClippingRect {
anchors.fill: parent
radius: parent.radius
@@ -156,15 +153,14 @@ orientation: Qt.Vertical
}
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small
SnapLayout {
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -182,7 +178,7 @@ orientation: Qt.Vertical
}
}
SnapLayout {
RowLayout {
spacing: Tokens.spacing.largeIncreased
CustomMouseArea {
@@ -253,7 +249,7 @@ orientation: Qt.Vertical
node: appBox.modelData
}
ClipRect {
CustomClippingRect {
anchors.fill: parent
radius: parent.radius
@@ -280,15 +276,14 @@ orientation: Qt.Vertical
text: Audio.getStreamName(appBox.modelData)
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small
SnapLayout {
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@@ -307,7 +302,7 @@ orientation: Qt.Vertical
}
}
SnapLayout {
RowLayout {
spacing: Tokens.spacing.largeIncreased
CustomMouseArea {
+1 -3
View File
@@ -4,7 +4,6 @@ import Quickshell
import Quickshell.Networking
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Modules
@@ -15,8 +14,7 @@ Item {
required property var wrapper
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: layout
spacing: 8
+1 -3
View File
@@ -4,7 +4,6 @@ import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import QtQuick.Controls
import QtQuick.Effects
import qs.Components
@@ -48,8 +47,7 @@ StackView {
duration: 0
}
}
component SubMenu: SnapLayout {
orientation: Qt.Vertical
component SubMenu: ColumnLayout {
id: menu
required property QsMenuHandle handle
+1 -2
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell.Services.UPower
import ZShell.Config
import qs.Modules.Bar.Components.Common
@@ -36,7 +35,7 @@ Column {
Repeater {
model: Battery.allPeripherals
SnapLayout {
RowLayout {
id: layout
required property var modelData
+3 -5
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import ZShell.Config
import qs.Components
@@ -87,7 +86,7 @@ CustomRect {
implicitWidth: 600
radius: root.radius - Tokens.padding.extraSmall
SnapLayout {
RowLayout {
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
@@ -97,8 +96,7 @@ CustomRect {
text: "package_2"
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
Layout.fillWidth: true
CustomText {
@@ -116,7 +114,7 @@ orientation: Qt.Vertical
}
}
SnapLayout {
RowLayout {
Layout.fillHeight: true
Layout.preferredWidth: 300
+5 -8
View File
@@ -4,7 +4,6 @@ import Quickshell
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import qs.Helpers
import ZShell.Config
@@ -26,7 +25,7 @@ Item {
searchField.forceActiveFocus();
}
ClipRect {
CustomClippingRect {
id: search
anchors.left: parent.left
@@ -55,7 +54,7 @@ Item {
}
}
CustomRect {
CustomClippingRect {
id: preview
anchors.bottom: parent.bottom
@@ -65,7 +64,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 {}
@@ -112,7 +110,7 @@ Item {
Repeater {
model: lineRow.modelData.text.split("\t").slice(1)
SnapLayout {
RowLayout {
height: lineText.height
width: lineText.height + Tokens.spacing.extraSmall
@@ -226,16 +224,15 @@ Item {
}
}
SnapLayout {
RowLayout {
anchors.fill: parent
spacing: Tokens.spacing.small
Item {
CustomClippingRect {
id: textRect
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
Item {
id: textWrapper
+4 -5
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell
import qs.Components
@@ -40,8 +39,8 @@ Item {
];
return allTabs.filter(tab => tab.enabled);
}
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)
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
required property ScreenState screenState
implicitHeight: nonAnimHeight
@@ -94,8 +93,8 @@ Item {
}
anchors.fill: parent
implicitHeight: currentItem?.height ?? currentItem?.implicitHeight ?? 0
implicitWidth: currentItem?.width ?? currentItem?.implicitWidth ?? 0
implicitHeight: currentItem?.implicitHeight ?? 0
implicitWidth: currentItem?.implicitWidth ?? 0
Repeater {
id: repeater
+75 -53
View File
@@ -1,82 +1,104 @@
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
SnapLayout {
orientation: Qt.Vertical
GridLayout {
id: root
readonly property bool dashboardVisible: screenState.dashboard
required property int radius
required property ScreenState screenState
spacing: ZUtils.snap(Tokens.spacing.small, root)
columnSpacing: Tokens.spacing.small
rowSpacing: Tokens.spacing.small
height: ZUtils.snap(implicitHeight, root)
width: ZUtils.snap(implicitWidth, root)
ParallelAnimation {
id: openAnim
SnapLayout {
Layout.fillWidth: true
spacing: parent.spacing
Rect {
id: weatherRect
Layout.fillHeight: true
Layout.preferredWidth: ZUtils.snap(Tokens.sizes.dashboard.weatherWidth, root)
radius: root.radius
Weather {}
Anim {
property: "opacity"
target: root
to: 1
}
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
}
Anim {
property: "scale"
target: root
to: 1
}
}
SnapLayout {
Layout.fillWidth: true
spacing: parent.spacing
ParallelAnimation {
id: closeAnim
Rect {
id: calendarRect
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: ZUtils.snap(calendar.implicitHeight, root)
radius: root.radius
Calendar {
id: calendar
screenState: root.screenState
}
Anim {
property: "opacity"
target: root
to: 0
}
Rect {
id: resourcesRect
Layout.fillHeight: true
implicitWidth: ZUtils.snap(resources.implicitWidth, root)
radius: root.radius
Anim {
property: "scale"
target: root
to: 0.9
}
}
Resources {
id: resources
}
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
Layout.fillWidth: true
Layout.preferredHeight: calendar.implicitHeight
Layout.row: 1
radius: root.radius
Calendar {
id: calendar
screenState: root.screenState
}
}
Rect {
Layout.column: 3
Layout.columnSpan: 2
Layout.fillHeight: true
Layout.preferredWidth: resources.implicitWidth
Layout.row: 1
radius: root.radius
Resources {
id: resources
}
}
+2 -4
View File
@@ -4,7 +4,6 @@ import Quickshell
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Services
@@ -118,15 +117,14 @@ CustomMouseArea {
}
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: inner
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraSmall
SnapLayout {
RowLayout {
id: monthNavigationRow
Layout.fillWidth: true
+9 -11
View File
@@ -1,7 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell
import ZShell.Services
import ZShell.Config
import qs.Components
@@ -10,8 +8,9 @@ import qs.Services
Item {
id: root
anchors.fill: parent
implicitWidth: ZUtils.snap(layout.implicitWidth + layout.anchors.margins * 2, root)
anchors.bottom: parent.bottom
anchors.top: parent.top
implicitWidth: layout.implicitWidth + layout.anchors.margins * 4
ServiceRef {
service: Storage
@@ -29,16 +28,13 @@ Item {
service: Gpu
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
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 {
@@ -77,10 +73,12 @@ orientation: Qt.Vertical
required property string icon
implicitSize: ZUtils.snap((root.height - layout.anchors.margins * 2 - layout.spacing * 4) / 5, root)
Layout.fillHeight: true
implicitSize: height
Behavior on clampedVal {
Anim {}
Anim {
}
}
MaterialIcon {
+1 -1
View File
@@ -14,7 +14,7 @@ Row {
padding: 20
spacing: 12
ClipRect {
CustomClippingRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: info.implicitHeight
implicitWidth: info.implicitHeight
+6 -10
View File
@@ -1,8 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import M3Shapes
import ZShell
import ZShell.Config
import qs.Modules.Dashboard.Media
import qs.Components
@@ -15,10 +13,10 @@ Item {
property ScreenState screenState
required property int radius
implicitWidth: ZUtils.snap(1000, root)
implicitHeight: ZUtils.snap(320, root)
implicitWidth: 1000
implicitHeight: 320
ClipRect {
CustomClippingRect {
anchors.fill: parent
radius: root.radius
@@ -27,7 +25,7 @@ Item {
}
}
SnapLayout {
RowLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraLarge
@@ -96,8 +94,7 @@ Item {
active: opacity > 0
opacity: 0
sourceComponent: SnapLayout {
orientation: Qt.Vertical
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.small
MaterialShape {
@@ -143,12 +140,11 @@ orientation: Qt.Vertical
asynchronous: true
active: opacity > 0
sourceComponent: SnapLayout {
sourceComponent: RowLayout {
spacing: Tokens.spacing.extraLarge
Details {
Layout.fillWidth: true
Layout.fillHeight: true
}
LyricsAndSelector {
+154 -178
View File
@@ -1,14 +1,13 @@
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
Item {
ColumnLayout {
id: root
readonly property bool hasUnknownLength: (Players.active?.length ?? 0) > 2147483647
@@ -26,187 +25,46 @@ Item {
return `${mins}:${secs}`;
}
implicitHeight: ZUtils.snap(layout.implicitHeight, root)
implicitWidth: layout.implicitWidth
spacing: Tokens.spacing.extraSmall
SnapLayout {
orientation: Qt.Vertical
id: layout
anchors.left: parent.left
anchors.right: parent.right
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
Timer {
running: Players.active?.isPlaying ?? false
interval: Config.dashboard.mediaUpdateInterval
triggeredOnStart: true
repeat: true
onTriggered: Players.active?.positionChanged()
}
spacing: ZUtils.snap(Tokens.spacing.extraSmall, root)
CustomText {
Layout.fillWidth: true
text: Players.active?.trackTitle ?? ""
font: Tokens.font.title.large
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?.trackArtist || qsTr("Unknown artist")
color: Colors.palette.m3onSurfaceVariant
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
}
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: 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)
}
}
RowLayout {
Layout.topMargin: Tokens.spacing.extraLargeIncreased
Layout.fillWidth: true
spacing: Tokens.spacing.small
TextMetrics {
id: timeMetrics
@@ -214,5 +72,123 @@ orientation: Qt.Vertical
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)
}
}
}
+2 -5
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell.Services
import qs.Components
@@ -158,8 +157,7 @@ Item {
active: opacity > 0
opacity: 0
sourceComponent: SnapLayout {
orientation: Qt.Vertical
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.large
CustomRect {
@@ -200,8 +198,7 @@ orientation: Qt.Vertical
active: opacity > 0
opacity: 0
sourceComponent: SnapLayout {
orientation: Qt.Vertical
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.small
MaterialIcon {
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import Quickshell.Services.Mpris
import ZShell.Config
@@ -9,15 +8,14 @@ import qs.Helpers
import qs.Services
Item {
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: layout
anchors.fill: parent
anchors.leftMargin: Tokens.padding.medium
spacing: Tokens.spacing.medium
SnapLayout {
RowLayout {
Layout.bottomMargin: -Tokens.spacing.medium
spacing: Tokens.spacing.medium
z: 1
+1 -3
View File
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell
import ZShell.Blobs
import ZShell.Config
@@ -142,8 +141,7 @@ Item {
}
]
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: layout
anchors.centerIn: parent
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import ZShell.Config
import ZShell.Internal
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import Quickshell.Widgets
import ZShell.Config
@@ -39,7 +38,7 @@ CustomListView {
id: rowHover
}
SnapLayout {
RowLayout {
id: rowLayout
anchors.left: parent.left
@@ -47,7 +46,7 @@ CustomListView {
anchors.bottom: parent.bottom
spacing: Tokens.spacing.extraSmall / 2
ClipRect {
CustomClippingRect {
id: iconClip
Layout.fillHeight: true
+4 -6
View File
@@ -23,7 +23,7 @@ Item {
service: Memory
}
SnapLayout {
RowLayout {
id: layout
anchors.fill: parent
spacing: Tokens.spacing.medium
@@ -60,14 +60,13 @@ Item {
Layout.fillHeight: true
implicitHeight: mainLayout.implicitHeight + mainLayout.anchors.margins * 2
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: 0
SnapLayout {
RowLayout {
spacing: Tokens.spacing.small
MaterialIcon {
@@ -132,8 +131,7 @@ orientation: Qt.Vertical
}
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: infoColumn
anchors.right: parent.right
anchors.bottom: parent.bottom
+32 -57
View File
@@ -4,8 +4,6 @@ import QtQuick
import QtQuick.Layouts
import QtQuick.Templates
import Quickshell
import ZShell
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
@@ -18,17 +16,9 @@ Item {
required property var tabs
readonly property alias count: bar.count
readonly property int tabIndicatorSpacing: ZUtils.snap(Tokens.spacing.small, root)
readonly property int tabIndicatorSpacing: Tokens.spacing.small
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)
implicitHeight: bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
TabBar {
id: bar
@@ -41,11 +31,9 @@ Item {
currentIndex: root.screenState.dashboardTab
onCurrentIndexChanged: root.screenState.dashboardTab = currentIndex
topPadding: 0
bottomPadding: 0
implicitHeight: implicitContentHeight
implicitHeight: contentHeight
background: null
contentItem: SnapLayout {
contentItem: RowLayout {
spacing: 0
Repeater {
@@ -128,13 +116,10 @@ Item {
readonly property bool current: TabBar.tabBar.currentItem === this
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: 1
Layout.preferredWidth: 1 // Uniform width across all tabs
implicitWidth: implicitContentWidth
implicitHeight: implicitContentHeight
background: null
topPadding: 0
bottomPadding: 0
contentItem: CustomMouseArea {
id: mouse
@@ -146,8 +131,8 @@ Item {
root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0);
}
implicitWidth: Math.max(icon.implicitWidth, label.implicitWidth)
implicitHeight: snapLayout.implicitHeight
implicitWidth: Math.max(icon.width, label.width)
implicitHeight: icon.height + label.height
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
@@ -161,49 +146,39 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.alignWhenCentered: false
implicitHeight: ZUtils.snap(parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2, stateLayer)
implicitHeight: parent.height + root.tabIndicatorSpacing * 2
radius: Tokens.rounding.medium
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurface
onClicked: {
root.screenState.dashboardTab = tab.TabBar.index;
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
}
}
}
SnapLayout {
id: snapLayout
anchors.fill: parent
spacing: 0
orientation: Qt.Vertical
CustomText {
id: label
MaterialIcon {
id: icon
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
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
}
text: tab.text
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
}
}
}
+6 -12
View File
@@ -1,13 +1,11 @@
import QtQuick
import QtQuick.Layouts
import ZShell
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Services
import qs.Helpers
SnapLayout {
RowLayout {
id: root
spacing: Tokens.spacing.largeIncreased
@@ -22,7 +20,7 @@ SnapLayout {
readonly property real markerX: {
const d = root.now;
return ZUtils.snap(d.getHours() + d.getMinutes() / 60, root);
return d.getHours() + d.getMinutes() / 60;
}
function formatHour(h) {
@@ -41,13 +39,11 @@ SnapLayout {
onTriggered: root.now = new Date()
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2
SnapLayout {
RowLayout {
Layout.fillWidth: true
MaterialIcon {
@@ -94,13 +90,11 @@ orientation: Qt.Vertical
}
}
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2
SnapLayout {
RowLayout {
Layout.fillWidth: true
MaterialIcon {
+7 -14
View File
@@ -3,7 +3,6 @@ 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
@@ -19,14 +18,13 @@ Item {
implicitHeight: layout.implicitHeight
Component.onCompleted: Weather.reload()
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: layout
anchors.fill: parent
spacing: Tokens.spacing.medium
SnapLayout {
RowLayout {
Layout.leftMargin: Tokens.padding.large
Layout.rightMargin: Tokens.padding.large
Layout.fillWidth: true
@@ -84,7 +82,7 @@ orientation: Qt.Vertical
}
}
SnapLayout {
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
@@ -123,9 +121,7 @@ orientation: Qt.Vertical
color: Colors.palette.m3onSurface
}
SnapLayout {
id: forecastLayout
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
@@ -141,14 +137,12 @@ orientation: Qt.Vertical
required property var modelData
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
radius: root.radius
color: Colors.tPalette.m3surfaceContainer
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: forecastItemColumn
anchors.centerIn: parent
@@ -202,8 +196,7 @@ orientation: Qt.Vertical
property color colour
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: 60
Layout.preferredHeight: 60
radius: Tokens.rounding.medium
color: Colors.tPalette.m3surfaceContainer
@@ -237,7 +230,7 @@ orientation: Qt.Vertical
}
}
component WeatherStat: SnapLayout {
component WeatherStat: RowLayout {
id: weatherStat
property string icon
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import Quickshell
import Quickshell.Widgets
import Quickshell.Hyprland
@@ -26,7 +25,7 @@ Item {
onClicked: root.close()
}
ClipRect {
CustomClippingRect {
id: popupBackground
readonly property real padding: 4
@@ -44,8 +43,7 @@ Item {
}
opacity: root.visible ? 1 : 0
SnapLayout {
orientation: Qt.Vertical
ColumnLayout {
id: menuLayout
anchors.centerIn: parent
spacing: 0
@@ -55,7 +53,7 @@ orientation: Qt.Vertical
radius: popupBackground.radius - popupBackground.padding
implicitHeight: openTerminalRow.implicitHeight + Tokens.padding.extraSmall * 2
SnapLayout {
RowLayout {
id: openTerminalRow
spacing: 8
anchors.fill: parent
@@ -94,7 +92,7 @@ orientation: Qt.Vertical
radius: popupBackground.radius - popupBackground.padding
implicitHeight: settingsRow.implicitHeight + Tokens.padding.extraSmall * 2
SnapLayout {
RowLayout {
id: settingsRow
spacing: 8
anchors.fill: parent
@@ -134,7 +132,7 @@ orientation: Qt.Vertical
radius: popupBackground.radius - popupBackground.padding
implicitHeight: logoutRow.implicitHeight + Tokens.padding.extraSmall * 2
SnapLayout {
RowLayout {
id: logoutRow
spacing: 8
anchors.fill: parent
@@ -173,7 +171,7 @@ orientation: Qt.Vertical
// radius: popupBackground.radius - popupBackground.padding
// implicitHeight: desktopIconsRow.implicitHeight + Tokens.padding.extraSmall * 2
//
// SnapLayout {
// RowLayout {
// id: desktopIconsRow
// spacing: 8
// anchors.fill: parent
@@ -3,7 +3,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import qs.Components
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -41,7 +40,7 @@ Item {
visible: false
z: 999
ClipRect {
CustomClippingRect {
id: popupBackground
readonly property real padding: Tokens.padding.extraSmall
@@ -58,25 +57,22 @@ Item {
Anim {}
}
SnapLayout {
ColumnLayout {
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
SnapLayout {
RowLayout {
id: openRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -115,12 +111,11 @@ Item {
implicitHeight: openWithRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
SnapLayout {
RowLayout {
id: openWithRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -161,12 +156,11 @@ Item {
implicitHeight: copyPathRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
SnapLayout {
RowLayout {
id: copyPathRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -196,12 +190,11 @@ Item {
radius: popupBackground.radius - popupBackground.padding
visible: contextMenu.targetPaths.length === 1
SnapLayout {
RowLayout {
id: renameRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
@@ -238,12 +231,11 @@ Item {
implicitHeight: deleteRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding
SnapLayout {
RowLayout {
id: deleteRow
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
spacing: 8
MaterialIcon {
+2 -7
View File
@@ -4,7 +4,6 @@ import Quickshell
import Quickshell.Widgets
import qs.Components
import qs.Helpers
import ZShell.Components
import ZShell.Config
import qs.Services
@@ -30,10 +29,7 @@ CustomRect {
active: !isSeparator
anchors.centerIn: parent
sourceComponent: SnapLayout {
spacing: Tokens.spacing.extraSmall
orientation: Qt.Vertical
sourceComponent: ColumnLayout {
IconImage {
id: icon
@@ -42,9 +38,8 @@ CustomRect {
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel?.appId), "image-missing")
}
SnapLayout {
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 3
Repeater {
-1
View File
@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Services
+1 -3
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import ZShell
import ZShell.Config
import qs.Components
import qs.Services
@@ -53,7 +52,7 @@ CustomListView {
highlightFollowsCurrentItem: false
highlightRangeMode: ListView.ApplyRange
implicitHeight: (Tokens.sizes.launcher.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
orientation: Qt.Vertical
preferredHighlightBegin: 0
preferredHighlightEnd: height
@@ -98,7 +97,6 @@ CustomListView {
}
}
highlight: CustomRect {
id: hl
color: Colors.palette.m3onSurface
implicitHeight: root.currentItem?.implicitHeight ?? 0
implicitWidth: root.width
+3 -2
View File
@@ -22,7 +22,7 @@ CarouselView {
delegateProperties: ({
screenState: root.screenState
})
focalWidth: Tokens.sizes.launcher.wallpaperWidth
focalWidth: Config.launcher.sizes.wallpaperWidth
glideDuration: 250
glideEasingType: Easing.OutCubic
implicitHeight: tileHeight + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.small
@@ -41,7 +41,8 @@ CarouselView {
tileHeight: focalWidth / 16 * 9
delegate: Component {
WallpaperTile {}
WallpaperTile {
}
}
Component.onDestruction: Wallpapers.stopPreview()
+2 -3
View File
@@ -2,18 +2,17 @@ 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: ZUtils.snap(Tokens.padding.medium / 2, root)
readonly property int padding: Tokens.padding.largeIncreased / 4
required property var panels
readonly property int rounding: Tokens.rounding.largeIncreased
required property ScreenState screenState
+4 -5
View File
@@ -2,12 +2,11 @@ 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 {
@@ -72,7 +71,7 @@ Item {
PropertyChanges {
appList.active: true
root.implicitHeight: Math.min(root.maxHeight, appList.implicitHeight > 0 ? appList.implicitHeight : empty.implicitHeight)
root.implicitWidth: Tokens.sizes.launcher.itemWidth
root.implicitWidth: Config.launcher.sizes.itemWidth
}
AnchorChanges {
@@ -84,8 +83,8 @@ Item {
name: "wallpapers"
PropertyChanges {
root.implicitHeight: Tokens.sizes.launcher.wallpaperHeight
root.implicitWidth: Math.max(Tokens.sizes.launcher.itemWidth * 1.2, wallpaperList.implicitWidth)
root.implicitHeight: Config.launcher.sizes.wallpaperHeight
root.implicitWidth: Math.max(Config.launcher.sizes.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: Tokens.sizes.launcher.itemHeight
implicitHeight: Config.launcher.sizes.itemHeight
StateLayer {
radius: Tokens.rounding.medium
+18 -42
View File
@@ -1,13 +1,10 @@
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import ZShell
import qs.Modules.Launcher.Services
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Modules
import qs.Services
@@ -19,72 +16,53 @@ Item {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: ZUtils.snap(Tokens.sizes.launcher.itemHeight, root)
implicitHeight: Config.launcher.sizes.itemHeight
StateLayer {
radius: Tokens.rounding.medium
onClicked: {
// Apps.launch(root.modelData);
// root.screenState.launcher = false;
ZUtils.logPlacement(icon, "icon");
ZUtils.logPlacement(name, "name");
ZUtils.logPlacement(favoriteIcon, "favoriteIcon");
ZUtils.logPlacement(comment, "comment");
ZUtils.logPlacement(text, "text");
ZUtils.logPlacement(mainLayout, "main");
Apps.launch(root.modelData);
root.screenState.launcher = false;
}
}
SnapLayout {
id: mainLayout
Item {
anchors.fill: parent
anchors.leftMargin: Tokens.padding.medium
anchors.margins: Tokens.padding.small
anchors.rightMargin: Tokens.padding.medium
spacing: Tokens.spacing.medium
IconImage {
id: icon
implicitSize: parent.height - Tokens.padding.small * 2
anchors.verticalCenter: parent.verticalCenter
implicitSize: parent.height
source: Quickshell.iconPath(root.modelData?.icon, "image-missing")
}
SnapLayout {
id: text
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: Tokens.padding.extraSmall
Layout.bottomMargin: Tokens.padding.extraSmall
orientation: Qt.Vertical
spacing: 0
Item {
anchors.left: icon.right
anchors.leftMargin: Tokens.spacing.medium
anchors.verticalCenter: icon.verticalCenter
implicitHeight: name.implicitHeight + comment.implicitHeight
implicitWidth: parent.width - icon.width
CustomText {
id: name
Layout.fillHeight: true
Layout.fillWidth: true
text: root.modelData?.name ?? ""
verticalAlignment: CustomText.AlignVCenter
horizontalAlignment: CustomText.AlignLeft
}
CustomText {
id: comment
Layout.fillHeight: true
Layout.fillWidth: true
anchors.top: name.bottom
color: Colors.palette.m3outline
elide: Text.ElideRight
font: Tokens.font.body.small
text: (root.modelData?.comment || root.modelData?.genericName || root.modelData?.name) ?? ""
verticalAlignment: CustomText.AlignVCenter
horizontalAlignment: CustomText.AlignLeft
width: root.width - icon.width - Tokens.rounding.medium - Tokens.padding.medium * 2
}
}
@@ -92,16 +70,14 @@ Item {
id: favoriteIcon
active: root.modelData && Strings.testRegexList(Config.launcher.favoriteApps, root.modelData.id)
Layout.fillHeight: true
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: MaterialIcon {
color: Colors.palette.m3primary
fill: 1
text: "favorite"
verticalAlignment: MaterialIcon.AlignVCenter
}
}
}
+1 -2
View File
@@ -2,7 +2,6 @@ import ZShell
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Components
import qs.Modules
import qs.Helpers
@@ -32,7 +31,7 @@ Item {
}
}
SnapLayout {
RowLayout {
anchors.left: parent.left
anchors.margins: Tokens.padding.small
anchors.right: parent.right
+9 -61
View File
@@ -1,5 +1,3 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import ZShell.Config
@@ -11,7 +9,7 @@ Item {
id: root
required property bool isCurrent
required property var modelData
required property var modelData // FileSystemEntry-like {path, relativePath}
required property real tileHeight
required property real tileWidth
required property ScreenState screenState
@@ -21,7 +19,7 @@ Item {
implicitHeight: tileHeight + Tokens.padding.large * 2
implicitWidth: tileWidth
ClipRect {
CustomClippingRect {
id: image
anchors.horizontalCenter: parent.horizontalCenter
@@ -31,75 +29,25 @@ Item {
radius: Tokens.rounding.small
y: Tokens.padding.large
Loader {
active: opacity > 0
MaterialIcon {
anchors.centerIn: parent
opacity: img.status === Image.Ready ? 0 : 1
Behavior on opacity {
Anim {}
}
sourceComponent: CustomRect {
color: Colors.palette.m3primaryContainer
implicitHeight: loadingIndicator.implicitSize + Tokens.padding.large * 2
implicitWidth: loadingIndicator.implicitSize + Tokens.padding.large * 2
radius: Tokens.rounding.full
LoadingIndicator {
id: loadingIndicator
anchors.centerIn: parent
containsIcon: true
implicitSize: Math.min(image.width, image.height) * 0.3
}
}
color: Colors.tPalette.m3outline
fontStyle: Tokens.font.icon.builders.extraLarge.scale(2).weight(Font.DemiBold).build()
text: "image"
}
Image {
id: img
CachingImage {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
asynchronous: true
cache: true
fillMode: Image.PreserveAspectCrop
height: root.tileHeight
source: root.modelData ? root.modelData.thumbnailPath : ""
path: root.modelData ? root.modelData.path : ""
smooth: root.isCurrent
sourceSize.height: root.tileHeight
sourceSize.width: root.tileHeight * (16 / 9)
width: root.tileHeight * (16 / 9)
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
Loader {
opacity: root.modelData.isVideo && img.status === Image.Ready ? 1 : 0
active: opacity > 0
anchors.centerIn: parent
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
sourceComponent: CustomRect {
implicitWidth: videoIcon.implicitWidth + Tokens.padding.extraLarge * 2
implicitHeight: implicitWidth
radius: Tokens.rounding.full
color: Colors.tPalette.m3surface
MaterialIcon {
id: videoIcon
anchors.centerIn: parent
text: "video_library"
fontStyle: Tokens.font.icon.extraLarge
}
}
}
}
StateLayer {
+5 -10
View File
@@ -2,15 +2,14 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Paths
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Modules
import qs.Services
SnapLayout {
ColumnLayout {
id: root
readonly property real centerScale: Math.min(1, (lock.screen?.height ?? 1440) / 1440)
@@ -20,13 +19,10 @@ SnapLayout {
Layout.fillHeight: true
Layout.fillWidth: false
Layout.preferredWidth: centerWidth
orientation: Qt.Vertical
spacing: Tokens.spacing.largeIncreased * 2
SnapLayout {
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Tokens.spacing.small
CustomText {
@@ -59,7 +55,7 @@ SnapLayout {
text: Time.format("dddd, d MMMM yyyy")
}
ClipRect {
CustomClippingRect {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer
@@ -113,12 +109,11 @@ SnapLayout {
}
}
SnapLayout {
RowLayout {
id: input
anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.medium
Item {
+20 -11
View File
@@ -1,31 +1,28 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
Item {
CustomClippingRect {
id: root
required property var lock
implicitHeight: layout.implicitHeight
implicitWidth: layout.implicitWidth
radius: Tokens.rounding.largeIncreased
SnapLayout {
RowLayout {
id: layout
anchors.fill: parent
spacing: Tokens.spacing.largeIncreased * 2
SnapLayout {
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
orientation: Qt.Vertical
WeatherInfo {
id: weather
@@ -53,10 +50,22 @@ Item {
lock: root.lock
}
NotifDock {
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
lock: root.lock
spacing: Tokens.spacing.medium
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
NotifDock {
lock: root.lock
}
}
}
}
}
-1
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import qs.Modules
import qs.Components
import qs.Helpers
+1 -1
View File
@@ -221,9 +221,9 @@ WlSessionLockSurface {
anchors.fill: parent
color: Colors.palette.m3surface
layer.enabled: true
opacity: Colors.transparency.enabled ? Colors.transparency.base : 1
radius: lockContent.radius
layer.enabled: true
layer.effect: MultiEffect {
blurMax: 15

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