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
293 changed files with 4307 additions and 13919 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 uses: actions/checkout@v4
- name: Install Docker CLI - 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 - name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin 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 {} + -exec clang-format -i --style=file {} +
git diff --exit-code && echo "clang-format: passed" git diff --exit-code && echo "clang-format: passed"
build: build & lint:
runs-on: alpine runs-on: alpine
container: container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest 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: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
+1 -15
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
jobs: jobs:
fmt: fmt & lint:
runs-on: alpine runs-on: alpine
container: node:26-alpine container: node:26-alpine
@@ -24,20 +24,6 @@ jobs:
else else
echo "No JS/TS files found" echo "No JS/TS files found"
fi 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 - name: ESLint
run: | run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then if [ -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: pull_request:
jobs: jobs:
static: fmt & lint:
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: node:26-trixie-slim
@@ -33,7 +33,7 @@ jobs:
. .venv/bin/activate . .venv/bin/activate
ruff check . ruff check .
verify: test & nuitka build:
if: always() if: always()
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: node:26-trixie-slim
@@ -64,4 +64,4 @@ jobs:
- name: Nuitka module check - name: Nuitka module check
run: | run: |
. .venv/bin/activate . .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: on:
pull_request: pull_request:
# jobs can be separated if we integrate tests
jobs: jobs:
build: build, clippy & fmt:
runs-on: alpine runs-on: alpine
container: node:26-alpine container: node:26-alpine
env: env:
@@ -34,7 +35,9 @@ jobs:
apk add --no-cache \ apk add --no-cache \
git \ git \
cargo \ cargo \
rust rust \
rust-clippy \
rustfmt
- name: Cargo check - name: Cargo check
run: | run: |
@@ -50,90 +53,6 @@ jobs:
else else
echo "No Rust project found" echo "No Rust project found"
fi 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 - name: Clippy
run: | run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
@@ -150,3 +69,19 @@ jobs:
else else
echo "No Rust project found" echo "No Rust project found"
fi 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_CXX_EXTENSIONS OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 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(ENABLE_MODULES "plugin;shell;cli;m3shapes" CACHE STRING "Modules to build/install")
set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir") set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir")
+2 -3
View File
@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import Quickshell
import ZShell.Components import ZShell.Components
Item { Item {
@@ -20,7 +19,7 @@ Item {
} }
ShaderEffectSource { ShaderEffectSource {
id: src id: source
hideSource: true hideSource: true
sourceItem: contentItem sourceItem: contentItem
@@ -28,7 +27,7 @@ Item {
RoundedMask { RoundedMask {
anchors.fill: parent anchors.fill: parent
source: src source: source
topLeftRadius: root.topLeftRadius topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius bottomRightRadius: root.bottomRightRadius
+4 -3
View File
@@ -99,7 +99,6 @@ ProgressBar {
} }
Line { Line {
id: dot
property real implicitSize property real implicitSize
anchors.right: parent.right anchors.right: parent.right
@@ -125,7 +124,8 @@ ProgressBar {
anchors.left: parent.left anchors.left: parent.left
Behavior on implicitWidth { Behavior on implicitWidth {
Anim {} Anim {
}
} }
Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition) Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition)
@@ -222,7 +222,8 @@ ProgressBar {
} }
} }
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
Anim on waveProgress { Anim on waveProgress {
duration: root.waveDuration duration: root.waveDuration
+6 -6
View File
@@ -2,10 +2,10 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Templates
import ZShell
import ZShell.Components import ZShell.Components
import ZShell.Config import ZShell
import qs.Components import qs.Components
import ZShell.Config
import qs.Services import qs.Services
Slider { Slider {
@@ -28,7 +28,7 @@ Slider {
signal interaction(v: real) signal interaction(v: real)
implicitHeight: ZUtils.snap(12, root) implicitHeight: 12
implicitWidth: 200 implicitWidth: 200
contentItem: Item { contentItem: Item {
@@ -123,7 +123,7 @@ Slider {
bottomRightRadius: Tokens.rounding.extraSmall / 2 bottomRightRadius: Tokens.rounding.extraSmall / 2
color: root.fgColor color: root.fgColor
implicitHeight: root.height implicitHeight: root.height
implicitWidth: ZUtils.snap(root.filledWidth, root) implicitWidth: root.filledWidth
radius: root.radius radius: root.radius
topRightRadius: Tokens.rounding.extraSmall / 2 topRightRadius: Tokens.rounding.extraSmall / 2
} }
@@ -137,8 +137,8 @@ Slider {
frequency: root.waveFrequency frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: ZUtils.snap(root.filledWidth, root) implicitWidth: root.filledWidth
lineWidth: ZUtils.snap(root.height * 0.7, root) lineWidth: root.height * 0.7
startX: x startX: x
Behavior on color { Behavior on color {
+39 -41
View File
@@ -2,57 +2,55 @@ import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
Item { CustomClippingRect {
id: track id: track
required property var scrollBar required property var scrollBar
required property var mouseArea required property var mouseArea
ClipRect { anchors.left: parent.left
radius: Tokens.rounding.full anchors.right: parent.right
anchors.left: parent.left anchors.bottom: parent.bottom
anchors.right: parent.right implicitHeight: handle.implicitHeight
radius: Tokens.rounding.full
CustomRect {
id: handle
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight color: Colors.palette.m3secondary
implicitWidth: track.scrollBar.width * track.scrollBar.effectiveSize
CustomRect { implicitHeight: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
id: handle opacity: {
if (!track.scrollBar.enabled)
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;
return 0; return 0;
} if (track.scrollBar.size === 1)
radius: Tokens.rounding.full return 0;
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.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 { Behavior on implicitHeight {
Anim {} Anim {}
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects type: Anim.DefaultEffects
}
} }
}
MouseArea { MouseArea {
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
}
} }
} }
} }
+2 -3
View File
@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import ZShell
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -16,9 +15,9 @@ ButtonBase {
const h = label.implicitHeight + padding * 2; const h = label.implicitHeight + padding * 2;
if (h % 2 !== 0) if (h % 2 !== 0)
return h + 1; return h + 1;
return ZUtils.snap(h, root); return h;
} }
implicitWidth: ZUtils.snap(implicitHeight, root) implicitWidth: implicitHeight
inactiveColor: { inactiveColor: {
if (!isToggle && type === IconButton.Filled) if (!isToggle && type === IconButton.Filled)
return Colors.palette.m3primary; return Colors.palette.m3primary;
+1 -3
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -35,11 +34,10 @@ ButtonBase {
} }
verticalPadding: Tokens.padding.extraSmall verticalPadding: Tokens.padding.extraSmall
SnapLayout { RowLayout {
id: row id: row
anchors.centerIn: parent anchors.centerIn: parent
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
MaterialIcon { MaterialIcon {
+66 -89
View File
@@ -1,8 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtCanvas2D
import ZShell
Item { Item {
id: root id: root
@@ -38,62 +36,54 @@ Item {
property real fillAlpha: 0.15 property real fillAlpha: 0.15
property real lineWidth: 2 property real lineWidth: 2
property real markerDotRadius: 4 property real markerDotRadius: 4
property real labelFontSize: 8 property real labelFontSize: 10
Canvas2D { Canvas {
id: canvas id: canvas
anchors.fill: parent anchors.fill: parent
renderStrategy: Canvas.Immediate
alphaBlending: true
fillColor: "transparent"
onWidthChanged: requestPaint() onWidthChanged: requestPaint()
onHeightChanged: requestPaint() onHeightChanged: requestPaint()
function snapV(v) {
return ZUtils.snap(v, canvas);
}
function strokeCurve(ctx, pts) { function strokeCurve(ctx, pts) {
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y); ctx.moveTo(pts[0].x, pts[0].y);
const dpr = ZUtils.dprFor(canvas); if (root.curveSmoothing && pts.length > 2) {
for (let i = 0; i < pts.length - 1; i++) { const k = 1 / 6;
const p1 = pts[i]; const n = pts.length;
const p2 = pts[i + 1];
const [c1x, c1y, c2x, c2y] = segmentControls(pts, i);
const firstCol = Math.ceil(p1.x * dpr) + 1; for (let i = 0; i < n - 1; i++) {
const lastCol = Math.floor(p2.x * dpr) - 1; const p0 = pts[Math.max(i - 1, 0)];
for (let col = firstCol; col <= lastCol; ++col) { const p1 = pts[i];
const x = col / dpr; const p2 = pts[i + 1];
ctx.lineTo(x, bezierY(p1, c1x, c1y, c2x, c2y, p2, x)); 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) { // Control points for segment i (pts[i] -> pts[i+1]), matching strokeCurve
let tlo = 0;
let thi = 1;
for (let it = 0; it < 16; it++) {
const tm = (tlo + thi) / 2;
const u = 1 - tm;
const x = u * u * u * p1.x + 3 * u * u * tm * c1x + 3 * u * tm * tm * c2x + tm * tm * tm * p2.x;
if (x < targetX)
tlo = tm;
else
thi = tm;
}
const t = (tlo + thi) / 2;
const u = 1 - t;
return u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
}
function segmentControls(pts, i) { function segmentControls(pts, i) {
const p0 = pts[Math.max(i - 1, 0)]; const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i]; const p1 = pts[i];
@@ -119,6 +109,7 @@ Item {
return [cx, p1.y, cx, p2.y]; return [cx, p1.y, cx, p2.y];
} }
// Point on the curve at a given x (x is monotonic per segment)
function pointAtX(pts, targetX) { function pointAtX(pts, targetX) {
let j = pts.length - 2; let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) { for (let i = 0; i < pts.length - 1; i++) {
@@ -147,16 +138,10 @@ Item {
const t = (tlo + thi) / 2; const t = (tlo + thi) / 2;
const u = 1 - t; 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; const y = u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
return { return { x: targetX, y };
x: targetX,
y
};
}
function cutPoint(pts, targetX) {
return pointAtX(pts, targetX);
} }
// Part of the curve left/right of a cut x, including the cut point
function cutLeft(pts, targetX) { function cutLeft(pts, targetX) {
if (targetX <= pts[0].x) if (targetX <= pts[0].x)
return []; return [];
@@ -173,7 +158,7 @@ Item {
const left = pts.slice(0, j + 1); const left = pts.slice(0, j + 1);
if (targetX > pts[j].x) if (targetX > pts[j].x)
left.push(cutPoint(pts, targetX)); left.push(pointAtX(pts, targetX));
return left; return left;
} }
@@ -199,20 +184,17 @@ Item {
onPaint: { onPaint: {
const ctx = getContext("2d"); const ctx = getContext("2d");
if (!ctx)
return;
ctx.reset(); ctx.reset();
ctx.clearRect(0, 0, width, height);
const W = width; const W = width;
const H = height; const H = height;
if (W < 2 || H < 2) if (W < 2 || H < 2)
return; return;
const padLeft = root.labelFontSize * 5; const padLeft = root.labelFontSize * 3.4;
const padRight = 0; const padRight = 2;
const padTop = 0; const padTop = 2;
const padBottom = root.labelFontSize + 14; const padBottom = root.labelFontSize + 8;
const plot = { const plot = {
x: padLeft, x: padLeft,
y: padTop, y: padTop,
@@ -222,6 +204,8 @@ Item {
if (plot.w < 2 || plot.h < 2) if (plot.w < 2 || plot.h < 2)
return; return;
// Auto-fit: NaN yMin/yMax derive from the data, snapped to the
// horizontal step grid, honoring minHorizontalLines
let lo = root.yMin; let lo = root.yMin;
let hi = root.yMax; let hi = root.yMax;
const loAuto = Number.isNaN(lo); const loAuto = Number.isNaN(lo);
@@ -295,17 +279,11 @@ Item {
const xAt = i => plot.x + (i / root.xSteps) * plot.w; const xAt = i => plot.x + (i / root.xSteps) * plot.w;
const yAt = v => plot.y + (1 - (v - scaleBottom) / range) * plot.h; const yAt = v => plot.y + (1 - (v - scaleBottom) / range) * plot.h;
const crisp = v => Math.floor(v) + 0.5;
const dpr = ZUtils.dprFor(canvas); // 1. Grid lines
const devW = w => Math.max(1, Math.round(w * dpr));
const snapLine = (v, w) => {
const dw = devW(w);
return (Math.round(v * dpr - dw / 2) + dw / 2) / dpr;
};
const gridW = devW(1) / dpr;
ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha); ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha);
ctx.lineWidth = gridW; ctx.lineWidth = 1;
if (root.verticalLineStep > 0) { if (root.verticalLineStep > 0) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) { for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
@@ -314,7 +292,7 @@ Item {
if (i === root.xSteps && !root.lastXLine) if (i === root.xSteps && !root.lastXLine)
continue; continue;
const x = snapLine(xAt(i), 1); const x = crisp(xAt(i));
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(x, plot.y); ctx.moveTo(x, plot.y);
ctx.lineTo(x, H); 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); const vStart = lo - (root.yPaddingBottom > 0 ? Math.floor(root.yPaddingBottom / root.horizontalLineStep) * root.horizontalLineStep : 0);
if (root.horizontalLineStep > 0) { if (root.horizontalLineStep > 0) {
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) { 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.beginPath();
ctx.moveTo(0, y); ctx.moveTo(0, y);
ctx.lineTo(plot.x + plot.w, y); ctx.lineTo(plot.x + plot.w, y);
@@ -334,8 +313,9 @@ Item {
} }
} }
// 2. Labels
ctx.fillStyle = Qt.alpha(root.labelColor, root.labelAlpha); 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.textAlign = "left";
ctx.textBaseline = "top"; ctx.textBaseline = "top";
@@ -348,7 +328,7 @@ Item {
const text = root.xLabel(i); const text = root.xLabel(i);
if (text) 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 = []; const segments = [];
let current = null; let current = null;
for (let i = 0; i < series.length; i++) { for (let i = 0; i < series.length; i++) {
@@ -371,7 +352,7 @@ Item {
continue; continue;
} }
const pt = { const pt = {
x: snapV(xAt(i)), x: xAt(i),
y: yAt(series[i]) y: yAt(series[i])
}; };
if (!current) if (!current)
@@ -388,6 +369,8 @@ Item {
const markerPx = hasMarker ? xAt(root.markerX) : 0; const markerPx = hasMarker ? xAt(root.markerX) : 0;
let gapPx = 0; let gapPx = 0;
if (hasMarker) { if (hasMarker) {
// markerGap is measured from the dot edge, perpendicular to the
// curve; project onto x via the local curve slope
let slope = 0; let slope = 0;
const seg = segments.find(s => s[0].x <= markerPx && markerPx <= s[s.length - 1].x); 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) 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) if (seg.length < 2)
continue; continue;
// Fill stays continuous under the marker
ctx.fillStyle = Qt.alpha(root.lineColor, root.fillAlpha); ctx.fillStyle = Qt.alpha(root.lineColor, root.fillAlpha);
strokeCurve(ctx, seg); strokeCurve(ctx, seg);
ctx.lineTo(seg[seg.length - 1].x, plot.y + plot.h); ctx.lineTo(seg[seg.length - 1].x, plot.y + plot.h);
@@ -406,6 +390,7 @@ Item {
ctx.closePath(); ctx.closePath();
ctx.fill(); ctx.fill();
// Stroke has a gap around the marker
const pieces = hasMarker ? [cutLeft(seg, markerPx - gapPx), cutRight(seg, markerPx + gapPx)] : [seg]; const pieces = hasMarker ? [cutLeft(seg, markerPx - gapPx), cutRight(seg, markerPx + gapPx)] : [seg];
for (const pts of pieces) { for (const pts of pieces) {
@@ -413,32 +398,24 @@ Item {
continue; continue;
ctx.strokeStyle = root.lineColor; ctx.strokeStyle = root.lineColor;
ctx.lineWidth = ZUtils.snap(root.lineWidth, root); ctx.lineWidth = root.lineWidth;
strokeCurve(ctx, pts); strokeCurve(ctx, pts);
ctx.stroke(); ctx.stroke();
} }
} }
// 4. Marker: dashed vertical + dot
if (root.markerX >= 0 && root.markerX <= root.xSteps) { if (root.markerX >= 0 && root.markerX <= root.xSteps) {
const x = markerPx; const x = markerPx;
ctx.setLineDash([4, 4]);
ctx.strokeStyle = Qt.alpha(root.markerColor, 0.6); ctx.strokeStyle = Qt.alpha(root.markerColor, 0.6);
ctx.lineWidth = gridW; ctx.lineWidth = 1;
const lineX = snapLine(x, 1); ctx.beginPath();
let dashY = plot.y; ctx.moveTo(crisp(x), plot.y);
let dashOn = true; ctx.lineTo(crisp(x), H);
ctx.stroke();
while (dashY < H) { ctx.setLineDash([]);
const dashEnd = Math.min(dashY + 4, H);
if (dashOn) {
ctx.beginPath();
ctx.moveTo(lineX, snapV(dashY));
ctx.lineTo(lineX, snapV(dashEnd));
ctx.stroke();
}
dashY = dashEnd;
dashOn = !dashOn;
}
const i0 = Math.floor(root.markerX); const i0 = Math.floor(root.markerX);
const v0 = series[i0]; const v0 = series[i0];
@@ -453,7 +430,7 @@ Item {
if (y != null) { if (y != null) {
ctx.fillStyle = root.markerColor; ctx.fillStyle = root.markerColor;
ctx.beginPath(); ctx.beginPath();
ctx.circle(snapV(x), y, root.markerDotRadius); ctx.arc(x, y, root.markerDotRadius, 0, Math.PI * 2);
ctx.fill(); ctx.fill();
} }
} }
+7 -9
View File
@@ -4,7 +4,6 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Drawers import qs.Drawers
import qs.Services import qs.Services
@@ -35,7 +34,7 @@ MouseArea {
cursorShape: expanded ? Qt.ArrowCursor : undefined cursorShape: expanded ? Qt.ArrowCursor : undefined
enabled: expanded enabled: expanded
hoverEnabled: expanded hoverEnabled: expanded
layer.enabled: false layer.enabled: opacity < 1
opacity: expanded ? 1 : 0 opacity: expanded ? 1 : 0
parent: { parent: {
const win = QsWindow.window; const win = QsWindow.window;
@@ -102,7 +101,8 @@ MouseArea {
yScale: root.expanded ? 1 : 0.1 yScale: root.expanded ? 1 : 0.1
Behavior on yScale { Behavior on yScale {
Anim {} Anim {
}
} }
} }
@@ -111,14 +111,12 @@ MouseArea {
color: Colors.palette.m3surfaceContainerLow color: Colors.palette.m3surfaceContainerLow
radius: parent.radius radius: parent.radius
SnapLayout { ColumnLayout {
id: column id: column
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.extraSmall
orientation: Qt.Vertical
Repeater { Repeater {
id: repeater id: repeater
@@ -139,7 +137,8 @@ MouseArea {
radius: menu.radius - column.anchors.margins radius: menu.radius - column.anchors.margins
Behavior on radius { Behavior on radius {
Anim {} Anim {
}
} }
StateLayer { StateLayer {
@@ -154,12 +153,11 @@ MouseArea {
} }
} }
SnapLayout { RowLayout {
id: menuOptionRow id: menuOptionRow
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.medium anchors.margins: Tokens.padding.medium
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
MaterialIcon { MaterialIcon {
+1 -2
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -54,7 +53,7 @@ Row {
onClicked: root.active?.clicked() onClicked: root.active?.clicked()
} }
SnapLayout { RowLayout {
id: textRow id: textRow
anchors.centerIn: parent anchors.centerIn: parent
+1 -1
View File
@@ -12,7 +12,7 @@ TextField {
implicitHeight: contentHeight + topPadding + bottomPadding implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: TextField.QtRendering renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.QtRendering
selectedTextColor: color selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4) selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
+2 -5
View File
@@ -1,5 +1,4 @@
import ZShell import ZShell
import ZShell.Components
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Components import qs.Components
@@ -48,7 +47,7 @@ CustomRect {
z: -1 z: -1
} }
SnapLayout { RowLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
@@ -89,11 +88,9 @@ CustomRect {
} }
} }
SnapLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: 0 spacing: 0
orientation: Qt.Vertical
CustomText { CustomText {
id: title id: title
+1 -1
View File
@@ -30,7 +30,7 @@ CustomFlickable {
type: Anim.SlowEffects type: Anim.SlowEffects
} }
} }
layer.textureSize: Qt.size(width * dpr, height * dpr) // layer.textureSize: Qt.size(width * dpr, height * dpr)
layer.effect: Mask { layer.effect: Mask {
maskSource: mask maskSource: mask
+39 -41
View File
@@ -2,57 +2,55 @@ import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
Item { CustomClippingRect {
id: track id: track
required property var scrollBar required property var scrollBar
required property var mouseArea 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.right: parent.right
anchors.top: parent.top color: Colors.palette.m3secondary
radius: Tokens.rounding.full implicitHeight: track.scrollBar.height * track.scrollBar.effectiveSize
implicitWidth: handle.implicitWidth implicitWidth: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
CustomRect { if (!track.scrollBar.enabled)
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;
return 0; return 0;
} if (track.scrollBar.size === 1)
radius: Tokens.rounding.full return 0;
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.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 { Behavior on implicitWidth {
Anim {} Anim {}
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects type: Anim.DefaultEffects
}
} }
}
MouseArea { MouseArea {
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
}
} }
} }
} }
-7
View File
@@ -4,16 +4,12 @@ import Quickshell
import QtQuick import QtQuick
import ZShell.Internal import ZShell.Internal
import qs.Components import qs.Components
import qs.Services
Item { Item {
id: root id: root
readonly property alias content: contentLoader.item readonly property alias content: contentLoader.item
required property ScreenState screenState required property ScreenState screenState
property rect holeRect
property real holeRadius: 0
Loader { Loader {
id: contentLoader id: contentLoader
@@ -22,9 +18,6 @@ Item {
anchors.fill: parent anchors.fill: parent
sourceComponent: StrokeCanvas { sourceComponent: StrokeCanvas {
anchors.fill: parent
hole: root.holeRect
holeRadius: root.holeRadius
penColor: root.screenState.penColor penColor: root.screenState.penColor
penWidth: root.screenState.penWidth penWidth: root.screenState.penWidth
} }
-95
View File
@@ -1,95 +0,0 @@
import Quickshell
import QtQuick
import ZShell.Config
import qs.Components
CustomMouseArea {
id: root
required property real borderThickness
required property Drawing drawing
required property Panels panels
required property ScreenState screenState
required property EdgeGeometry geometry
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness);
return y >= panelY && y <= panelY + panel.height;
}
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.BlankCursor
enabled: z > 0
hoverEnabled: true
focus: root.screenState.isDrawing
z: 2
Binding {
target: root.drawing.content
property: "hoverPoint"
value: Qt.point(root.mouseX, root.mouseY)
}
Keys.onPressed: e => {
const mod = e.modifiers;
const key = e.key;
if ((key == Qt.Key_Z) && (mod & Qt.ControlModifier) && (mod & Qt.ShiftModifier)) {
root.drawing.content.redo();
e.accepted = true;
} else if ((key == Qt.Key_Z) && (mod & Qt.ControlModifier)) {
root.drawing.content.undoLastGroup();
e.accepted = true;
}
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
if (!drawing.content.hoverVisible)
drawing.content.showHover();
if (event.buttons & Qt.LeftButton) {
drawing.content.appendPoint(x, y);
return;
}
if (!(event.buttons & Qt.LeftButton) && inLeftPanel(panels.drawing, x, y)) {
z = -2;
panels.drawing.expand();
drawing.content.hideHover();
}
}
onPressed: event => {
const x = event.x;
const y = event.y;
panels.drawing.collapse();
if (event.buttons & Qt.LeftButton) {
panels.drawing.collapse();
drawing.content.beginStroke(x, y);
return;
}
if (event.button & Qt.RightButton) {
drawing.content.clear();
return;
}
}
onReleased: event => {
const x = event.x;
const y = event.y;
if (event.button & Qt.LeftButton) {
drawing.content.endStroke();
drawing.content.showHover();
}
}
}
+9 -1
View File
@@ -43,6 +43,13 @@ Scope {
hasBar: root.geometry.barOnBottom 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 { component ExclusionZone: CustomWindow {
property bool hasBar property bool hasBar
@@ -52,6 +59,7 @@ Scope {
name: "Bar-Exclusion" name: "Bar-Exclusion"
screen: root.screen screen: root.screen
mask: Region {} mask: Region {
}
} }
} }
+198 -121
View File
@@ -1,33 +1,25 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Config import ZShell.Config
import ZShell.Internal
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Popouts import qs.Modules.Bar.Popouts
CustomMouseArea { Item {
id: root id: root
required property Drawing drawing
required property DrawingInput input
required property Item bar required property Item bar
required property real borderThickness required property real borderThickness
property bool dashboardShortcutActive
required property Drawing drawing
required property EdgeGeometry geometry required property EdgeGeometry geometry
property bool osdShortcutActive
required property Panels panels required property Panels panels
required property Wrapper popouts required property Wrapper popouts
required property ShellScreen screen required property ShellScreen screen
required property ScreenState screenState
property bool dashboardShortcutActive
property point dragStart
property bool osdShortcutActive
property bool setInitialPoint: false
property bool singleGestureTriggered: false property bool singleGestureTriggered: false
property bool utilitiesShortcutActive property bool utilitiesShortcutActive
required property ScreenState screenState
function inBottomPanel(panel: Item, x: real, y: real): bool { function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
@@ -64,116 +56,124 @@ CustomMouseArea {
return x >= panelX && x <= panelX + panel.width; return x >= panelX && x <= panelX + panel.width;
} }
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
anchors.fill: parent anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: { DragHandler {
if (!containsMouse) { id: singleHandler
if (!osdShortcutActive) {
screenState.osd = false; cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
panels.osd.hovered = false; dragThreshold: 0
enabled: !root.screenState.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1
minimumPointCount: 1
target: null
onActiveChanged: {
if (!active)
root.singleGestureTriggered = false;
}
onCentroidChanged: {
const x = centroid.position.x;
const y = centroid.position.y;
const pressX = centroid.pressPosition.x;
const pressY = centroid.pressPosition.y;
const dragX = x - pressX;
const dragY = y - pressY;
if (pressY >= root.screen.height - Config.bar.border && pressX > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold)
root.screenState.launcher = true;
if (root.singleGestureTriggered)
return;
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, pressX, pressY);
if (inDashboard && root.withinPanelWidth(root.panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold) {
root.screenState.dashboard = true;
root.singleGestureTriggered = true;
} else if (dragY < -Config.dashboard.dragThreshold) {
root.screenState.dashboard = false;
root.singleGestureTriggered = true;
}
} }
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) { if (root.geometry.insetTop(root.borderThickness) > pressY && (Config.dashboard.showOnHover || !inDashboard)) {
popouts.hasCurrent = false; if (dragY > 20) {
root.screenState.settings = true;
root.singleGestureTriggered = true;
} else if (dragY < -20) {
root.screenState.settings = false;
root.singleGestureTriggered = true;
}
} }
if (Config.bar.autoHide || bar.fullscreen) if (pressY > root.screen.height - root.geometry.insetBottom(root.borderThickness) && pressX < root.screen.width / 5 && dragY < -50)
bar.isHovered = false; root.screenState.clipboard = true;
if (screenState.dashboard && Config.dashboard.showOnHover) if (!Config.dock.hoverToReveal && pressY > root.screen.height - root.geometry.insetTop(root.borderThickness) && pressX > root.screen.width / 5 && !root.screenState.launcher)
screenState.dashboard = false; if (dragY < -10) {
root.screenState.dock = true;
root.singleGestureTriggered = true;
}
if (pressX > root.screen.width - Config.bar.border && pressY < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
root.screenState.sidebar = true;
root.singleGestureTriggered = true;
}
if (pressX >= root.screen.width - Config.bar.border && pressY > (root.screen.height / 2) && dragX < -20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
if (pressX <= Config.bar.border && dragX > 20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
} }
} }
onPositionChanged: event => {
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (screenState.isDrawing && !inLeftPanel(panels.drawing, x, y)) { PointHandler {
input.z = 2; id: drawingHandler
panels.drawing.collapse();
}
if (root.bar.fullscreen && !root.bar.isHovered) property bool setInitialPoint: false
unhideTimer.start();
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold) acceptedButtons: Qt.LeftButton | Qt.RightButton
screenState.launcher = true; enabled: root.screenState.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
const entryAt = bar.entryAt(geometry.axisPos(x, y)); onActiveChanged: {
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y) && (!entryAt || entryAt === "activeWindow"); if (!active) {
setInitialPoint = false;
if (!dashboardShortcutActive) { root.drawing.content.endStroke();
screenState.dashboard = showDashboard; } else {
} else if (showDashboard) { root.panels.drawing.collapse();
dashboardShortcutActive = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold)
screenState.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold)
screenState.dashboard = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y)) {
if (dragY > 20) {
screenState.settings = true;
} else if (dragY < -20) {
screenState.settings = false;
} }
} }
onPointChanged: {
if (!active)
return;
const x = point.position.x;
const y = point.position.y;
const origX = point.pressPosition.x;
const origY = point.pressPosition.y;
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50) if (point.pressedButtons & Qt.RightButton) {
screenState.clipboard = true; root.drawing.content.clear();
return;
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
if (dragY < -10)
screenState.dock = true;
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
screenState.sidebar = true;
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
bar.isHovered = true;
if (panels.sidebar.offsetScale === 1) {
const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
} }
} else { if (x === 0 && y === 0 && origX === 0 && origY === 0)
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale); return;
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!osdShortcutActive) { if (!setInitialPoint) {
screenState.osd = showOsd; setInitialPoint = true;
panels.osd.hovered = showOsd; root.drawing.content.beginStroke(origX, origY);
} else if (showOsd) { return;
osdShortcutActive = false;
panels.osd.hovered = true;
} }
root.drawing.content.appendPoint(x, y);
} }
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
screenState.dock = true;
if (geometry.barContains(x, y))
bar.checkPopout(geometry.axisPos(x, y));
}
onPressed: event => {
dragStart = Qt.point(event.x, event.y);
} }
Timer { Timer {
@@ -184,20 +184,98 @@ CustomMouseArea {
running: false running: false
onTriggered: { onTriggered: {
if (root.containsMouse) if (hoverHandler.hovered)
root.bar.isHovered = true; root.bar.isHovered = true;
} }
} }
HoverHandler {
id: hoverHandler
cursorShape: root.screenState.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
onHoveredChanged: {
if (!hovered) {
if (!root.osdShortcutActive) {
root.screenState.osd = false;
root.panels.osd.hovered = false;
}
if (!root.popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
root.popouts.hasCurrent = false;
}
if (Config.bar.autoHide || root.bar.fullscreen)
root.bar.isHovered = false;
if (root.screenState.dashboard && Config.dashboard.showOnHover)
root.screenState.dashboard = false;
}
}
onPointChanged: {
const x = point.position.x;
const y = point.position.y;
if (root.screenState.isDrawing) {
if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) {
root.panels.drawing.expand();
root.drawing.content.hideHover();
return;
}
root.drawing.content.showHover(x, y);
return;
}
if (!root.screenState.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true))
root.bar.isHovered = true;
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (root.panels.sidebar.offsetScale === 1) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
if (showOsd) {
root.osdShortcutActive = false;
root.panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < root.width - root.panels.sidebar.width * (1 - root.panels.sidebar.offsetScale);
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!root.osdShortcutActive) {
root.screenState.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
root.osdShortcutActive = false;
root.panels.osd.hovered = true;
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !root.screenState.dock && !root.screenState.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.screenState.dock = true;
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, x, y);
if (Config.dashboard.showOnHover)
root.screenState.dashboard = inDashboard;
if (root.geometry.barContains(x, y))
root.bar.checkPopout(root.geometry.axisPos(x, y));
}
}
Connections { Connections {
function onDashboardChanged(): void { function onDashboardChanged() {
if (root.screenState.dashboard) { if (root.screenState.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY); const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
if (!inDashboardArea) { if (!inDashboardArea) {
root.dashboardShortcutActive = true; root.dashboardShortcutActive = true;
} }
if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x)
root.screenState.launcher = false;
root.screenState.launcher = false;
root.screenState.settings = false; root.screenState.settings = false;
root.screenState.sidebar = false; root.screenState.sidebar = false;
root.screenState.resources = false; root.screenState.resources = false;
@@ -207,15 +285,12 @@ CustomMouseArea {
} }
} }
function onIsDrawingChanged(): void { function onIsDrawingChanged() {
if (root.screenState.isDrawing) { if (!root.screenState.isDrawing)
root.drawing.content.showHover(); root.drawing.clear();
} else {
root.drawing.content.clear();
}
} }
function onLauncherChanged(): void { function onLauncherChanged() {
if (!root.screenState.launcher) { if (!root.screenState.launcher) {
root.dashboardShortcutActive = false; root.dashboardShortcutActive = false;
root.osdShortcutActive = false; root.osdShortcutActive = false;
@@ -230,13 +305,16 @@ CustomMouseArea {
} }
if (root.screenState.launcher) { if (root.screenState.launcher) {
root.screenState.dashboard = false; if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
root.screenState.dashboard = false;
}
root.screenState.dock = false; root.screenState.dock = false;
root.screenState.settings = false; root.screenState.settings = false;
} }
} }
function onOsdChanged(): void { function onOsdChanged() {
if (root.screenState.osd) { if (root.screenState.osd) {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY); const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) { if (!inOsdArea) {
@@ -247,7 +325,7 @@ CustomMouseArea {
} }
} }
function onResourcesChanged(): void { function onResourcesChanged() {
if (root.screenState.resources) { if (root.screenState.resources) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates")) if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
root.popouts.hasCurrent = false; root.popouts.hasCurrent = false;
@@ -257,7 +335,7 @@ CustomMouseArea {
} }
} }
function onSettingsChanged(): void { function onSettingsChanged() {
if (root.screenState.settings) { if (root.screenState.settings) {
root.screenState.resources = false; root.screenState.resources = false;
root.screenState.dashboard = false; root.screenState.dashboard = false;
@@ -267,10 +345,9 @@ CustomMouseArea {
} }
} }
function onSidebarChanged(): void { function onSidebarChanged() {
if (root.screenState.sidebar) { if (root.screenState.sidebar) {
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x) root.screenState.dashboard = false;
root.screenState.dashboard = false;
root.screenState.settings = false; root.screenState.settings = false;
root.popouts.hasCurrent = false; root.popouts.hasCurrent = false;
} }
+315
View File
@@ -0,0 +1,315 @@
import Quickshell
import QtQuick
import ZShell.Config
import ZShell.Internal
import qs.Components
import qs.Helpers
import qs.Modules.Bar.Popouts
CustomMouseArea {
id: root
required property Item bar
required property real borderThickness
property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing
required property EdgeGeometry geometry
property bool osdShortcutActive
required property Panels panels
required property Wrapper popouts
required property ShellScreen screen
required property ScreenState screenState
property bool setInitialPoint: false
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
}
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inPopoutArea(x: real, y: real): bool {
const panel = panels.popoutsWrapper;
if (!geometry.horizontal)
return inLeftPanel(panel, x, y);
return withinPanelWidth(panel, x, y) && withinPanelHeight(panel, x, y);
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inTopPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness);
return y >= panelY && y <= panelY + panel.height;
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + geometry.insetLeft(borderThickness);
return x >= panelX && x <= panelX + panel.width;
}
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if (!containsMouse) {
if (!osdShortcutActive) {
screenState.osd = false;
panels.osd.hovered = false;
}
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
popouts.hasCurrent = false;
}
if (Config.bar.autoHide || bar.fullscreen)
bar.isHovered = false;
if (screenState.dashboard && Config.dashboard.showOnHover)
screenState.dashboard = false;
}
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (screenState.isDrawing) {
if (pressed) {
drawing.content.appendPoint(x, y);
} else {
if (inLeftPanel(panels.drawing, x, y)) {
if (!(pressedButtons & Qt.LeftButton)) {
panels.drawing.expand();
drawing.content.hideHover();
Cursor.clearOverride();
}
} else {
Cursor.setOverride(Qt.BlankCursor);
drawing.content.showHover(x, y);
}
}
if (drawing.content.hoverVisible)
drawing.content.showHover(x, y);
return;
}
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
screenState.launcher = true;
const entryAt = bar.entryAt(geometry.axisPos(x, y));
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y) && (!entryAt || entryAt === "activeWindow");
if (!dashboardShortcutActive) {
screenState.dashboard = showDashboard;
} else if (showDashboard) {
dashboardShortcutActive = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold)
screenState.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold)
screenState.dashboard = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y)) {
if (dragY > 20) {
screenState.settings = true;
} else if (dragY < -20) {
screenState.settings = false;
}
}
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
screenState.clipboard = true;
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
if (dragY < -10)
screenState.dock = true;
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
screenState.sidebar = true;
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
bar.isHovered = true;
if (panels.sidebar.offsetScale === 1) {
const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!osdShortcutActive) {
screenState.osd = showOsd;
panels.osd.hovered = showOsd;
} else if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
screenState.dock = true;
if (geometry.barContains(x, y))
bar.checkPopout(geometry.axisPos(x, y));
}
onPressed: event => {
dragStart = Qt.point(event.x, event.y);
if (screenState.isDrawing) {
if (event.button & Qt.RightButton) {
drawing.content.clear();
return;
}
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.beginStroke(event.x, event.y);
}
}
onPressedChanged: {
if (!pressed) {
if (screenState.isDrawing) {
setInitialPoint = false;
drawing.content.endStroke();
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.showHover(mouseX, mouseY);
}
} else {
if (screenState.isDrawing) {
panels.drawing.collapse();
}
}
}
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (root.containsMouse)
root.bar.isHovered = true;
}
}
Connections {
function onDashboardChanged(): void {
if (root.screenState.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
root.screenState.launcher = false;
root.screenState.settings = false;
root.screenState.sidebar = false;
root.screenState.resources = false;
root.popouts.hasCurrent = false;
} else {
root.dashboardShortcutActive = false;
}
}
function onIsDrawingChanged(): void {
if (root.screenState.isDrawing) {
Cursor.setOverride(Qt.BlankCursor);
} else {
Cursor.clearOverride();
}
}
function onLauncherChanged(): void {
if (!root.screenState.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.screenState.osd = false;
root.panels.osd.hovered = false;
}
}
if (root.screenState.launcher) {
root.screenState.dashboard = false;
root.screenState.dock = false;
root.screenState.settings = false;
}
}
function onOsdChanged(): void {
if (root.screenState.osd) {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.osdShortcutActive = true;
}
} else {
root.osdShortcutActive = false;
}
}
function onResourcesChanged(): void {
if (root.screenState.resources) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
root.popouts.hasCurrent = false;
root.screenState.settings = false;
root.screenState.dashboard = false;
}
}
function onSettingsChanged(): void {
if (root.screenState.settings) {
root.screenState.resources = false;
root.screenState.dashboard = false;
root.screenState.sidebar = false;
root.panels.popouts.hasCurrent = false;
root.screenState.launcher = false;
}
}
function onSidebarChanged(): void {
if (root.screenState.sidebar) {
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x)
root.screenState.dashboard = false;
root.screenState.settings = false;
root.popouts.hasCurrent = false;
}
}
target: root.screenState
}
}
+14 -24
View File
@@ -1,7 +1,5 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Config
import ZShell
import qs.Components import qs.Components
import qs.Modules.Bar.Popouts as Popouts import qs.Modules.Bar.Popouts as Popouts
import qs.Modules.Notifications as Notifications 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.Drawing as Drawing
import qs.Modules.Dock as Dock import qs.Modules.Dock as Dock
import qs.Modules.Clipboard as Clipboard import qs.Modules.Clipboard as Clipboard
import ZShell.Config
Item { Item {
id: root id: root
required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock readonly property alias dock: dock
readonly property alias drawing: drawing readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher readonly property alias launcher: launcher
readonly property alias notifications: notifications readonly property alias notifications: notifications
readonly property alias osd: osd readonly property alias osd: osd
@@ -33,19 +36,14 @@ Item {
readonly property alias popoutsWrapper: popouts readonly property alias popoutsWrapper: popouts
readonly property alias resources: resources readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property alias settings: settings readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar readonly property alias sidebar: sidebar
readonly property alias toasts: toasts readonly property alias toasts: toasts
readonly property alias utilities: utilities readonly property alias utilities: utilities
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.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness) anchors.leftMargin: geometry.insetLeft(borderThickness)
@@ -75,10 +73,7 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: sidebar.width * (1 - sidebar.offsetScale) anchors.rightMargin: sidebar.width * (1 - sidebar.offsetScale)
anchors.verticalCenter: parent.verticalCenter
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
// anchors.verticalCenter: parent.verticalCenter
clip: sidebar.visible clip: sidebar.visible
implicitHeight: osd.implicitHeight implicitHeight: osd.implicitHeight
implicitWidth: osd.implicitWidth * (1 - osd.offsetScale) implicitWidth: osd.implicitWidth * (1 - osd.offsetScale)
@@ -86,7 +81,8 @@ Item {
Osd.Wrapper { Osd.Wrapper {
id: osd id: osd
anchors.fill: parent anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
screen: root.screen screen: root.screen
screenState: root.screenState screenState: root.screenState
sidebarOrSessionVisible: sidebar.visible sidebarOrSessionVisible: sidebar.visible
@@ -132,9 +128,8 @@ Item {
Launcher.Wrapper { Launcher.Wrapper {
id: launcher id: launcher
x: ZUtils.snap((parent.width - implicitWidth) / 2, root)
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
panels: root panels: root
screen: root.screen screen: root.screen
screenState: root.screenState screenState: root.screenState
@@ -154,10 +149,8 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1 property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
x: ZUtils.snap((parent.width - implicitWidth) / 2, dashboardWrapper)
clip: offsetScale > 0 clip: offsetScale > 0
implicitHeight: dashboard.implicitHeight * (1 - offsetScale) implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
implicitWidth: dashboard.implicitWidth implicitWidth: dashboard.implicitWidth
@@ -196,11 +189,8 @@ Item {
Settings.Wrapper { Settings.Wrapper {
id: settings id: settings
x: ZUtils.snap((parent.width - implicitWidth) / 2, settings) anchors.centerIn: parent
y: ZUtils.snap((parent.height - implicitHeight) / 2 - (implicitHeight + 5 + (parent.height - implicitHeight) / 2) * offsetScale, settings) anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
// anchors.centerIn: parent
// anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
panels: root panels: root
screen: root.screen screen: root.screen
screenState: root.screenState screenState: root.screenState
+2 -24
View File
@@ -56,7 +56,7 @@ CustomWindow {
property color surfaceColor: Colors.tPalette.m3surface property color surfaceColor: Colors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar || screenState.isDrawing ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent" color: "transparent"
mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions) mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
@@ -347,31 +347,12 @@ CustomWindow {
Drawing { Drawing {
id: drawing id: drawing
readonly property int extraWidth: drawingBg.radius * 2
anchors.fill: parent anchors.fill: parent
holeRect: Qt.rect(root.borderThickness + panels.drawing.x - extraWidth, panels.drawing.y + bar.implicitHeight, panels.drawing.implicitWidth + extraWidth, panels.drawing.implicitHeight)
holeRadius: drawingBg.radius
screenState: root.screenState screenState: root.screenState
z: 2 z: 2
} }
LazyLoader { Interactions2 {
id: lazyLoader
activeAsync: root.screenState.isDrawing
component: DrawingInput {
borderThickness: root.borderLayoutThickness
drawing: drawing
panels: panels
screenState: root.screenState
geometry: geometry
parent: root.contentItem
}
}
Interactions {
id: interactions id: interactions
anchors.fill: parent anchors.fill: parent
@@ -384,8 +365,6 @@ CustomWindow {
popouts: panels.popouts popouts: panels.popouts
screen: root.screen screen: root.screen
screenState: root.screenState screenState: root.screenState
input: lazyLoader.item
z: 1
Panels { Panels {
id: panels id: panels
@@ -463,7 +442,6 @@ CustomWindow {
parent: root.contentItem parent: root.contentItem
screen: root.screen screen: root.screen
screenState: root.screenState screenState: root.screenState
z: 4
} }
} }
+31 -18
View File
@@ -3,12 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import qs.Services import qs.Services
SnapLayout { ColumnLayout {
id: root id: root
readonly property real centerScale: Math.min(1, screenHeight / 1440) readonly property real centerScale: Math.min(1, screenHeight / 1440)
@@ -19,32 +18,36 @@ SnapLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: false Layout.fillWidth: false
Layout.preferredWidth: centerWidth Layout.preferredWidth: centerWidth
orientation: Qt.Vertical
spacing: Tokens.spacing.largeIncreased * 2 spacing: Tokens.spacing.largeIncreased * 2
SnapLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary 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 text: Time.hourStr
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary 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: ":" text: ":"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary 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 text: Time.minuteStr
} }
} }
@@ -53,11 +56,13 @@ SnapLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -Tokens.padding.large * 2 Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary 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") text: Time.format("dddd, d MMMM yyyy")
} }
ClipRect { CustomClippingRect {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2 Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
@@ -68,7 +73,7 @@ SnapLayout {
MaterialIcon { MaterialIcon {
anchors.centerIn: parent anchors.centerIn: parent
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.size(root.centerWidth / 4).build() font.pointSize: Math.floor(root.centerWidth / 4)
text: "person" text: "person"
} }
@@ -83,7 +88,9 @@ SnapLayout {
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant 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 text: root.greeter.username
visible: text.length > 0 visible: text.length > 0
} }
@@ -119,7 +126,7 @@ SnapLayout {
} }
} }
SnapLayout { RowLayout {
id: input id: input
anchors.fill: parent anchors.fill: parent
@@ -148,7 +155,8 @@ SnapLayout {
} }
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
@@ -183,6 +191,7 @@ SnapLayout {
anchors.centerIn: parent anchors.centerIn: parent
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font.weight: 500
text: root.greeter.launching ? "hourglass_top" : "arrow_forward" text: root.greeter.launching ? "hourglass_top" : "arrow_forward"
} }
} }
@@ -195,7 +204,8 @@ SnapLayout {
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight) implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
Behavior on implicitHeight { Behavior on implicitHeight {
Anim {} Anim {
}
} }
CustomText { CustomText {
@@ -235,10 +245,12 @@ SnapLayout {
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
Behavior on scale { Behavior on scale {
Anim {} Anim {
}
} }
onMsgChanged: { onMsgChanged: {
@@ -280,7 +292,8 @@ SnapLayout {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: isError ? Colors.palette.m3error : Colors.palette.m3primary 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 horizontalAlignment: Qt.AlignHCenter
opacity: 0 opacity: 0
scale: 0.7 scale: 0.7
+1 -1
View File
@@ -20,7 +20,7 @@ BusyIndicator {
property int animState property int animState
property color bgColor: Colors.palette.m3secondaryContainer property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary 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 property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8 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 QtQuick
import qs.Services
Flickable { Flickable {
id: root id: root
property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim { Anim {
properties: "x,y" 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 QtQuick
import qs.Services
ListView { ListView {
id: root id: root
property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
Anim { Anim {
properties: "x,y" 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 property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void { function onWheel(event: WheelEvent): void {
event.accepted = false;
} }
onWheel: event => { onWheel: event => {
+2 -1
View File
@@ -6,6 +6,7 @@ Rectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
} }
+7 -9
View File
@@ -14,9 +14,10 @@ Text {
property real animateTo: 1 property real animateTo: 1
color: Colors.palette.m3onSurface 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 linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.QtRendering renderType: Text.NativeRendering
textFormat: Text.PlainText textFormat: Text.PlainText
Behavior on color { Behavior on color {
@@ -27,25 +28,22 @@ Text {
enabled: root.animate enabled: root.animate
SequentialAnimation { SequentialAnimation {
TAnim { Anim {
target: root
to: root.animateFrom to: root.animateFrom
type: Anim.FastEffects
} }
PropertyAction { PropertyAction {
} }
TAnim { Anim {
target: root
to: root.animateTo to: root.animateTo
type: Anim.DefaultEffects
} }
} }
} }
component TAnim: Anim { component Anim: NumberAnimation {
duration: root.animateDuration / 2 duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : "" properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : "" property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root target: root
+52 -281
View File
@@ -1,305 +1,76 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
TextFieldBase { TextField {
id: root id: root
enum TextFieldType { background: null
Outlined, color: Colors.palette.m3onSurface
Filled 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) Behavior on color {
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText CAnim {
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
} }
} }
cursorDelegate: CustomRect {
id: cursor
onEditingFinished: { property bool disableBlink
if (!valid)
isError = true;
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
onTextEdited: {
if (isError)
isError = false;
}
Item { color: Colors.palette.m3primary
id: contentWrapper implicitWidth: 2
radius: Tokens.rounding.medium
anchors.bottomMargin: root.supportingTextOffset Behavior on opacity {
anchors.fill: parent Anim {
duration: Tokens.anim.durations.small
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
}
} }
} }
Loader { Connections {
id: leadingIconLoader function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
active: root.leadingIcon cursor.opacity = 1;
anchors.left: parent.left cursor.disableBlink = true;
anchors.leftMargin: Tokens.padding.small enableBlink.restart();
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
} }
} }
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
} }
} }
Behavior on placeholderTextColor {
Component { CAnim {
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 {}
}
}
} }
} }
} }
+2 -3
View File
@@ -1,16 +1,15 @@
import QtQuick import QtQuick
import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
TextInput { TextInput {
renderType: Text.NativeRendering renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.colSecondaryContainer selectionColor: Colors.tPalette.m3onSecondaryContainer
font { font {
family: Config.appearance?.font.family.sans ?? "sans-serif" family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting 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 ZShell.Config
import qs.Services import qs.Services
CustomText { CustomText {
id: root property real fill
property real fill: 0
property int grade: Colors.light ? 0 : -25 property int grade: Colors.light ? 0 : -25
property font fontStyle: Tokens.font.icon.small
renderType: Text.NativeRendering font.family: "Material Symbols Rounded"
font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build() 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
import QtQuick.Shapes import QtQuick.Shapes
import ZShell import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -11,6 +13,7 @@ MouseArea {
property alias bottomRightRadius: base.bottomRightRadius property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius property real circleRadius
property alias color: base.color property alias color: base.color
property bool disabled
readonly property real endRadius: { readonly property real endRadius: {
const d1 = distSq(0, 0); const d1 = distSq(0, 0);
const d2 = distSq(width, 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; return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
} }
property real endRadiusAtPress property real endRadiusAtPress
property bool manualHoverOverride
property bool manualPressOverride property bool manualPressOverride
property real pressX: width / 2 property real pressX: width / 2
property real pressY: height / 2 property real pressY: height / 2
@@ -27,7 +29,7 @@ MouseArea {
readonly property alias rect: base readonly property alias rect: base
property bool shapeMorph property bool shapeMorph
property bool showHoverBackground: true 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 topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius property alias topRightRadius: base.topRightRadius
@@ -50,8 +52,8 @@ MouseArea {
} }
anchors.fill: parent anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled enabled: !disabled
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity { Behavior on stateOpacity {
@@ -98,13 +100,13 @@ MouseArea {
id: base id: base
anchors.fill: parent anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
opacity: root.stateOpacity opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props) // Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property // qmllint disable missing-property
radius: root.parent?.radius ?? 0 radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0 topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0 topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property // qmllint enable missing-property
+41 -19
View File
@@ -1,12 +1,11 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import ZShell.Config
import qs.Services import qs.Services
SnapLayout { RowLayout {
id: root id: root
required property var greeter required property var greeter
@@ -14,16 +13,34 @@ SnapLayout {
spacing: Tokens.spacing.largeIncreased * 2 spacing: Tokens.spacing.largeIncreased * 2
CustomRect { ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
topLeftRadius: Tokens.rounding.largeIncreased spacing: Tokens.spacing.medium
bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
UserDock { CustomRect {
greeter: root.greeter 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 screenHeight: root.screenHeight
} }
CustomRect { ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased spacing: Tokens.spacing.medium
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
SessionDock { CustomRect {
greeter: root.greeter 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 id: lockIcon
anchors.centerIn: parent 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" text: "lock"
} }
+15 -9
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -40,7 +39,8 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline 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 opacity: root.buffer ? 0 : 1
text: { text: {
if (root.greeter.launching) if (root.greeter.launching)
@@ -51,7 +51,8 @@ Item {
} }
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
@@ -63,13 +64,15 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
elide: Text.ElideLeft elide: Text.ElideLeft
font: Tokens.font.mono.medium font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
opacity: root.greeter.echoResponse && root.buffer ? 1 : 0 opacity: root.greeter.echoResponse && root.buffer ? 1 : 0
text: root.buffer text: root.buffer
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
@@ -86,7 +89,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0 anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
implicitHeight: Tokens.font.body.medium implicitHeight: Tokens.font.size.normal
implicitWidth: fullWidth implicitWidth: fullWidth
interactive: false interactive: false
opacity: root.greeter.echoResponse ? 0 : 1 opacity: root.greeter.echoResponse ? 0 : 1
@@ -104,7 +107,8 @@ Item {
scale: 0 scale: 0
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {
@@ -152,13 +156,15 @@ Item {
Behavior on implicitWidth { Behavior on implicitWidth {
id: imWidthBehavior id: imWidthBehavior
Anim {} Anim {
}
} }
model: ScriptModel { model: ScriptModel {
values: root.buffer.split("") values: root.buffer.split("")
} }
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
} }
+21 -21
View File
@@ -3,32 +3,32 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Services import qs.Services
SnapLayout { ColumnLayout {
id: root id: root
required property var greeter required property var greeter
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
spacing: Tokens.spacing.large
orientation: Qt.Vertical
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight 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") 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.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader { Loader {
active: opacity > 0 active: opacity > 0
@@ -40,21 +40,22 @@ SnapLayout {
duration: Tokens.anim.durations.extraLarge duration: Tokens.anim.durations.extraLarge
} }
} }
sourceComponent: SnapLayout { sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.largeIncreased
orientation: Qt.Vertical
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
fontStyle: Tokens.font.icon.large font.pointSize: Tokens.font.size.extraLarge * 2
text: "desktop_windows" text: "desktop_windows"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant 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") text: qsTr("No Sessions Found")
} }
} }
@@ -67,7 +68,6 @@ SnapLayout {
clip: true clip: true
currentIndex: root.greeter.sessionIndex currentIndex: root.greeter.sessionIndex
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.sessions model: root.greeter.sessions
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
@@ -80,7 +80,7 @@ SnapLayout {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2 implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.large radius: Tokens.rounding.medium - Tokens.padding.small
StateLayer { StateLayer {
onClicked: { onClicked: {
@@ -88,7 +88,7 @@ SnapLayout {
} }
} }
SnapLayout { RowLayout {
id: row id: row
anchors.fill: parent anchors.fill: parent
@@ -97,21 +97,20 @@ SnapLayout {
MaterialIcon { MaterialIcon {
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant 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" text: modelData.kind === "x11" ? "tv" : "desktop_windows"
} }
SnapLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Tokens.spacing.small / 2 spacing: Tokens.spacing.small / 2
orientation: Qt.Vertical
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight 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 text: modelData.name
} }
@@ -119,7 +118,8 @@ SnapLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.mono.small font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
text: modelData.kind text: modelData.kind
} }
} }
@@ -129,7 +129,7 @@ SnapLayout {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: sessions.currentItem?.implicitHeight ?? 0 implicitHeight: sessions.currentItem?.implicitHeight ?? 0
implicitWidth: sessions.width implicitWidth: sessions.width
radius: Tokens.rounding.large radius: Tokens.rounding.medium - Tokens.padding.small
y: sessions.currentItem?.y ?? 0 y: sessions.currentItem?.y ?? 0
Behavior on y { Behavior on y {
+20 -21
View File
@@ -3,33 +3,33 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import qs.Services import qs.Services
SnapLayout { ColumnLayout {
id: root id: root
required property var greeter required property var greeter
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
spacing: Tokens.spacing.large
orientation: Qt.Vertical
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight 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") 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.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader { Loader {
active: opacity > 0 active: opacity > 0
@@ -41,22 +41,23 @@ SnapLayout {
duration: Tokens.anim.durations.extraLarge duration: Tokens.anim.durations.extraLarge
} }
} }
sourceComponent: SnapLayout { sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.largeIncreased
orientation: Qt.Vertical
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
fill: 1 fill: 1
fontStyle: Tokens.font.icon.large font.pointSize: Tokens.font.size.extraLarge * 2
text: "account_circle" text: "account_circle"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant 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") text: qsTr("No Users Found")
} }
} }
@@ -69,7 +70,6 @@ SnapLayout {
clip: true clip: true
currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1 currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.users model: root.greeter.users
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
@@ -82,7 +82,7 @@ SnapLayout {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2 implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.large radius: Tokens.rounding.medium - Tokens.padding.small
StateLayer { StateLayer {
onClicked: { onClicked: {
@@ -90,14 +90,14 @@ SnapLayout {
} }
} }
SnapLayout { RowLayout {
id: row id: row
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
ClipRect { CustomClippingRect {
Layout.preferredHeight: 50 Layout.preferredHeight: 50
Layout.preferredWidth: 50 Layout.preferredWidth: 50
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -106,7 +106,7 @@ SnapLayout {
anchors.centerIn: parent anchors.centerIn: parent
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fill: 1 fill: 1
fontStyle: Tokens.font.icon.extraLarge font.pointSize: Tokens.font.size.extraLarge
text: modelData.kind === "x11" ? "tv" : "account_circle" text: modelData.kind === "x11" ? "tv" : "account_circle"
} }
@@ -116,17 +116,16 @@ SnapLayout {
} }
} }
SnapLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Tokens.spacing.small / 2 spacing: Tokens.spacing.small / 2
orientation: Qt.Vertical
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight 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 text: modelData.username
} }
} }
@@ -136,7 +135,7 @@ SnapLayout {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: users.currentItem?.implicitHeight ?? 0 implicitHeight: users.currentItem?.implicitHeight ?? 0
implicitWidth: users.width implicitWidth: users.width
radius: Tokens.rounding.large radius: Tokens.rounding.medium - Tokens.padding.small
y: users.currentItem?.y ?? 0 y: users.currentItem?.y ?? 0
Behavior on y { 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 radius: Tokens.rounding.full
width: row.implicitWidth + Tokens.padding.small * 2 width: row.implicitWidth + Tokens.padding.small * 2
SnapLayout { RowLayout {
id: row id: row
anchors.centerIn: parent anchors.centerIn: parent
+43 -105
View File
@@ -5,7 +5,7 @@ import Quickshell
import Quickshell.Io import Quickshell.Io
import QtQuick import QtQuick
import ZShell.Models import ZShell.Models
import ZShell.Wallpaper import ZShell.Internal
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -16,48 +16,24 @@ Searcher {
id: root id: root
property string actualCurrent: WallpaperPath.currentWallpaperPath property string actualCurrent: WallpaperPath.currentWallpaperPath
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent readonly property string current: showPreview ? previewPath : actualCurrent
readonly property string currentThumbnail: thumbnailFor(current) property alias monitorCrops: monitorCrops
readonly property bool currentIsVideo: isVideoPath(current)
property bool pendingPreviewClear property bool pendingPreviewClear
property bool previewColorLock property bool previewColorLock
property string previewPath property string previewPath
property bool recentlyChanged property bool recentlyChanged
property bool showPreview: false 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); let category = w.parentDir.slice(Paths.wallsdir.length + 1);
if (category.includes("/")) if (category.includes("/"))
category = category.slice(0, category.indexOf("/")); category = category.slice(0, category.indexOf("/"));
return category; 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 { function getCrop(screen: string): var {
return wallpapers.crop(root.current, screen); return root.crops[screen];
} }
function preview(path: string): void { function preview(path: string): void {
@@ -74,18 +50,19 @@ Searcher {
else if (zoom > 5.0) else if (zoom > 5.0)
zoom = 5.0; zoom = 5.0;
wallpapers.setCrop(root.current, screen, { root.crops[screen] = {
x: rect.x, x: rect.x,
y: rect.y, y: rect.y,
width: rect.width, width: rect.width,
height: rect.height, height: rect.height,
zoom: zoom zoom: zoom
}); };
} }
function setWallpaper(path: string): void { function setWallpaper(path: string): void {
actualCurrent = path; actualCurrent = path;
WallpaperPath.currentWallpaperPath = path; WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 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}`]); 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) if (Config.colors.schemeGen)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]); 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" 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 id: wallpapers
dbPath: `${Paths.state}/wallpapers.sqlite` filter: FileSystemModel.Images
cacheSize: wallpaper.largestBufferSize
requireHardwareCodec: true
path: Config.general.wallpaperPath path: Config.general.wallpaperPath
recursive: true
} }
Process { Process {
@@ -144,100 +146,36 @@ Searcher {
WallpaperService { WallpaperService {
id: wallpaper id: wallpaper
dbPath: wallpapers.dbPath
path: root.current 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 { Variants {
id: screenVariants
model: Quickshell.screens model: Quickshell.screens
WallpaperObject { WallpaperObject {
id: obj readonly property var crops: root.getCrop(modelData.name)
required property var modelData required property var modelData
// Crop animation only for edits, never for wallpaper swaps. cropHeight: crops?.height ?? 1.0
property bool animateCrop: false cropWidth: crops?.width ?? 1.0
cropX: crops?.x ?? 0.0
cropY: crops?.y ?? 0.0
screen: modelData.name screen: modelData.name
service: wallpaper service: wallpaper
zoom: crops?.zoom ?? 1.0
// 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()
Behavior on cropHeight { Behavior on cropHeight {
enabled: obj.animateCrop
Anim {} Anim {}
} }
Behavior on cropWidth { Behavior on cropWidth {
enabled: obj.animateCrop
Anim {} Anim {}
} }
Behavior on cropX { Behavior on cropX {
enabled: obj.animateCrop
Anim {} Anim {}
} }
Behavior on cropY { Behavior on cropY {
enabled: obj.animateCrop
Anim {} 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();
}
}
}
} }
+10 -59
View File
@@ -4,13 +4,11 @@ import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Modules.Bar.Components import qs.Modules.Bar.Components
import qs.Modules.Bar.Popouts import qs.Modules.Bar.Popouts
import ZShell
SnapLayout { GridLayout {
id: root id: root
required property bool fullscreen required property bool fullscreen
@@ -24,7 +22,7 @@ SnapLayout {
function axisCenterOf(item: Item): real { function axisCenterOf(item: Item): real {
const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2); const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2);
return horizontal ? ZUtils.snap(c.x, item) : ZUtils.snap(c.y, item); return horizontal ? Math.floor(c.x) : Math.floor(c.y);
} }
function checkPopout(pos: real): void { function checkPopout(pos: real): void {
@@ -76,47 +74,9 @@ SnapLayout {
return ch?.entryId ?? ""; return ch?.entryId ?? "";
} }
readonly property var sectionRects: { columnSpacing: Tokens.spacing.small
const count = repeater.count; // dependency: re-run when items are added/removed columns: horizontal ? -1 : 1
const names = ["start", "middle", "end"]; // constant ids by position rowSpacing: Tokens.spacing.small
const bounds = [null];
let spacers = 0;
for (let i = 0; i < count; i++) {
const it = repeater.itemAt(i) as EntryWrapper;
if (!it)
continue;
if (it.entryId === "spacer") {
bounds.push(null);
spacers++;
continue;
}
const l = it.x, t = it.y, r = it.x + it.width, b = it.y + it.height;
const cur = bounds[spacers];
bounds[spacers] = cur ? {
l: Math.min(cur.l, l),
t: Math.min(cur.t, t),
r: Math.max(cur.r, r),
b: Math.max(cur.b, b)
} : {
l,
t,
r,
b
};
}
return bounds.map((c, n) => ({
id: names[n] ?? `section${n}`,
rect: c ? Qt.rect(c.l, c.t, c.r - c.l, c.b - c.t) : Qt.rect(0, 0, 0, 0)
}));
}
spacing: Tokens.spacing.small
orientation: horizontal ? Qt.Horizontal : Qt.Vertical
Repeater { Repeater {
id: repeater id: repeater
@@ -250,7 +210,8 @@ SnapLayout {
roleValue: "network" roleValue: "network"
delegate: EntryWrapper { delegate: EntryWrapper {
NetworkWidget {} NetworkWidget {
}
} }
} }
@@ -268,26 +229,16 @@ SnapLayout {
} }
component EntryWrapper: Item { 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 readonly property string entryId: modelData.id
required property int index required property int index
required property var modelData
default property Item item default property Item item
required property var modelData
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
Layout.bottomMargin: !root.horizontal && index === repeater.count - 1 ? root.vPadding : 0 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.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 children: item
implicitHeight: item?.implicitHeight ?? 0 implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0 implicitWidth: item?.implicitWidth ?? 0
-1
View File
@@ -9,7 +9,6 @@ import qs.Components
Item { Item {
id: root id: root
readonly property var sections: (content.item as Bar)?.sectionRects ?? []
readonly property int clampedExtent: Math.max(Config.bar.border, extent) 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 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 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
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Modules.Bar.Popouts import qs.Modules.Bar.Popouts
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
import qs.Helpers import qs.Helpers
@@ -44,11 +43,9 @@ WidgetBase {
timeTokens: timeTokens timeTokens: timeTokens
}; };
} }
required property SnapLayout loader required property GridLayout loader
required property Wrapper popouts required property Wrapper popouts
readonly property int roundedWidth: Math.round(timeText.implicitWidth) readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
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
required property ScreenState screenState required property ScreenState screenState
implicitHeight: horizontal ? baseSize : size implicitHeight: horizontal ? baseSize : size
@@ -70,8 +67,7 @@ WidgetBase {
} }
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: verticalColumn id: verticalColumn
anchors.centerIn: parent anchors.centerIn: parent
@@ -24,7 +24,7 @@ Row {
color: Battery.getColors(root.percentage, root.devState).bg color: Battery.getColors(root.percentage, root.devState).bg
height: root.batHeight height: root.batHeight
radius: root.radius radius: root.radius
width: root.batWidth - 1 width: root.batWidth
CustomText { CustomText {
color: Battery.getColors(root.percentage, root.devState).fg color: Battery.getColors(root.percentage, root.devState).fg
+1 -2
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
@@ -14,7 +13,7 @@ WidgetBase {
readonly property int textWidth: Math.min(metrics.width, 200) readonly property int textWidth: Math.min(metrics.width, 200)
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.small * 2 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 radius: Tokens.rounding.full
Behavior on implicitHeight { Behavior on implicitHeight {
+1 -2
View File
@@ -1,7 +1,6 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import qs.Components import qs.Components
import qs.Modules import qs.Modules
@@ -12,7 +11,7 @@ Item {
anchors.top: parent.top anchors.top: parent.top
implicitWidth: layout.implicitWidth implicitWidth: layout.implicitWidth
SnapLayout { RowLayout {
id: layout id: layout
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
+12 -9
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell import Quickshell
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
@@ -13,6 +12,7 @@ GridLayout {
property real animatedPercentage: 0 property real animatedPercentage: 0
readonly property real arcStartAngle: 0.75 * Math.PI readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI readonly property real arcSweep: 1.5 * Math.PI
readonly property real dpr: (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1
required property bool horizontal required property bool horizontal
property string icon property string icon
required property color iconColor required property color iconColor
@@ -23,12 +23,17 @@ GridLayout {
property bool warning: percentage * 100 >= warningThreshold property bool warning: percentage * 100 >= warningThreshold
property int warningThreshold: 80 property int warningThreshold: 80
function snap(v) {
return Math.round(v * dpr) / dpr;
}
columnSpacing: Tokens.spacing.medium columnSpacing: Tokens.spacing.medium
columns: horizontal ? -1 : 1 columns: horizontal ? -1 : 1
percentage: 0 percentage: 0
Behavior on animatedPercentage { Behavior on animatedPercentage {
Anim {} Anim {
}
} }
Component.onCompleted: animatedPercentage = percentage Component.onCompleted: animatedPercentage = percentage
@@ -54,7 +59,7 @@ GridLayout {
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4 Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4
Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2) color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
radius: Tokens.rounding.full radius: Math.min(width, height) / 2
Item { Item {
id: clipper id: clipper
@@ -64,20 +69,18 @@ GridLayout {
anchors.left: root.horizontal ? undefined : parent.left anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
clip: true clip: true
implicitHeight: root.horizontal ? root.percentage * track.height : track.height * 2 height: root.horizontal ? root.snap(root.percentage * track.height) : track.height * 2
implicitWidth: root.horizontal ? track.width * 2 : root.percentage * track.width width: root.horizontal ? track.width * 2 : root.snap(root.percentage * track.width)
CustomRect { CustomRect {
anchors.bottom: root.horizontal ? parent.bottom : undefined anchors.bottom: root.horizontal ? parent.bottom : undefined
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
color: root.mainColor color: root.mainColor
height: track.height
radius: track.radius radius: track.radius
width: track.width
implicitHeight: track.height
implicitWidth: track.width
} }
} }
} }
-3
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import ZShell.Services import ZShell.Services
import qs.Services import qs.Services
@@ -34,7 +33,6 @@ WidgetBase {
anchors.horizontalCenterOffset: root.horizontal ? 0 : 1 anchors.horizontalCenterOffset: root.horizontal ? 0 : 1
anchors.verticalCenterOffset: root.horizontal ? 0 : -3 anchors.verticalCenterOffset: root.horizontal ? 0 : -3
columnSpacing: Tokens.spacing.small columnSpacing: Tokens.spacing.small
rowSpacing: 0
columns: root.horizontal ? -1 : 1 columns: root.horizontal ? -1 : 1
ServiceRef { ServiceRef {
@@ -53,7 +51,6 @@ WidgetBase {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: root.horizontal Layout.fillHeight: root.horizontal
Layout.fillWidth: !root.horizontal Layout.fillWidth: !root.horizontal
horizontal: root.horizontal horizontal: root.horizontal
icon: "memory" icon: "memory"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
+11 -10
View File
@@ -4,7 +4,6 @@ import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Components.StatusIcons import qs.Modules.Bar.Components.StatusIcons
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
@@ -32,7 +31,7 @@ WidgetBase {
return -1; 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 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 // Entries that can shrink to nothing, spacing included
function collapsed(entry: var): bool { function collapsed(entry: var): bool {
@@ -52,15 +51,17 @@ WidgetBase {
Behavior on implicitHeight { Behavior on implicitHeight {
enabled: !root.horizontal enabled: !root.horizontal
Anim {} Anim {
}
} }
Behavior on implicitWidth { Behavior on implicitWidth {
enabled: root.horizontal enabled: root.horizontal
Anim {} Anim {
}
} }
SnapLayout { GridLayout {
id: grid id: grid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.medium anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.medium
@@ -68,8 +69,7 @@ WidgetBase {
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0 anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0 anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical columns: root.horizontal ? -1 : 1
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
@@ -111,7 +111,8 @@ WidgetBase {
delegate: EntryWrapper { delegate: EntryWrapper {
name: "upower" name: "upower"
UPowerWidget {} UPowerWidget {
}
} }
} }
} }
@@ -134,8 +135,8 @@ WidgetBase {
Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0 Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0
Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap) Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap)
children: item children: item
implicitHeight: item?.height ?? item?.implicitHeight ?? 0 implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.width ?? item?.implicitWidth ?? 0 implicitWidth: item?.implicitWidth ?? 0
Behavior on leftGap { Behavior on leftGap {
Anim { Anim {
@@ -1,13 +1,12 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Services import qs.Services
MaterialIcon { MaterialIcon {
id: root id: speaker
required property bool horizontal required property bool horizontal
@@ -16,8 +15,6 @@ MaterialIcon {
fill: 1 fill: 1
fontStyle: Tokens.font.icon.small fontStyle: Tokens.font.icon.small
text: Audio.muted ? "volume_off" : "volume_up" text: Audio.muted ? "volume_off" : "volume_up"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth { Behavior on Layout.maximumWidth {
Anim {} Anim {}
@@ -1,13 +1,12 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Services import qs.Services
MaterialIcon { MaterialIcon {
id: root id: mic
required property bool horizontal required property bool horizontal
@@ -16,8 +15,6 @@ MaterialIcon {
fill: 1 fill: 1
fontStyle: Tokens.font.icon.small fontStyle: Tokens.font.icon.small
text: Audio.sourceMuted ? "mic_off" : "mic" text: Audio.sourceMuted ? "mic_off" : "mic"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth { Behavior on Layout.maximumWidth {
Anim {} Anim {}
@@ -1,7 +1,6 @@
import Quickshell.Services.UPower import Quickshell.Services.UPower
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
import qs.Components import qs.Components
+2 -4
View File
@@ -2,11 +2,10 @@ import QtQuick.Layouts
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import ZShell.Config
import ZShell.Components
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Popouts import qs.Modules.Bar.Popouts
import qs.Components import qs.Components
import ZShell.Config
import qs.Services import qs.Services
Item { Item {
@@ -16,7 +15,7 @@ Item {
readonly property real dpr: Hypr.monitorFor(loader?.screen).scale readonly property real dpr: Hypr.monitorFor(loader?.screen).scale
required property int ind required property int ind
required property SystemTrayItem item required property SystemTrayItem item
required property SnapLayout loader required property GridLayout loader
required property Wrapper popouts required property Wrapper popouts
function resolveIcon(app: string, icon: string): string { function resolveIcon(app: string, icon: string): string {
@@ -80,7 +79,6 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
antialiasing: true antialiasing: true
backer.retainWhileLoading: true
color: root.current && !Config.bar.tray.showOnHover ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.current && !Config.bar.tray.showOnHover ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
implicitSize: Config.bar.tray.trayIconSize * root.dpr implicitSize: Config.bar.tray.trayIconSize * root.dpr
layer.enabled: Config.bar.tray.recolorIcons layer.enabled: Config.bar.tray.recolorIcons
+4 -5
View File
@@ -4,7 +4,6 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
import qs.Modules.Bar.Components.Tray import qs.Modules.Bar.Components.Tray
@@ -16,7 +15,7 @@ WidgetBase {
readonly property alias items: repeater readonly property alias items: repeater
readonly property alias layout: sysGrid readonly property alias layout: sysGrid
required property SnapLayout loader required property GridLayout loader
readonly property int padding: Tokens.padding.extraSmall readonly property int padding: Tokens.padding.extraSmall
required property Wrapper popouts 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 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 radius: Tokens.rounding.full
topRightRadius: horizontal ? Tokens.rounding.small / 2 : Tokens.rounding.full topRightRadius: horizontal ? Tokens.rounding.small / 2 : Tokens.rounding.full
SnapLayout { GridLayout {
id: sysGrid id: sysGrid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.extraSmall anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
@@ -37,8 +36,8 @@ WidgetBase {
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0 anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0 anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical columns: root.horizontal ? -1 : 1
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2 rowSpacing: -2
Repeater { Repeater {
id: repeater id: repeater
-1
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
+1 -1
View File
@@ -17,7 +17,7 @@ Item {
readonly property int maxSize: { readonly property int maxSize: {
const otherModules = bar.children.filter(c => c.enabled && c.entryId && c.item !== this && c.entryId !== "spacer"); 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); 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 clip: true
+27 -25
View File
@@ -12,14 +12,13 @@ import qs.Services
Item { Item {
id: root id: root
property real activeWorkspaceMargin: Tokens.padding.extraSmall / 2 property real activeWorkspaceMargin: Math.ceil(Tokens.padding.medium / 4)
readonly property int spacing: Tokens.spacing.extraSmall / 2
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1 readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
required property bool horizontal required property bool horizontal
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
required property ShellScreen screen required property ShellScreen screen
readonly property real shortSize: Math.max(Config.bar.height, Tokens.sizes.bar.innerSize) 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 workspaceButtonWidth: (horizontal ? bgRect.implicitHeight : bgRect.implicitWidth) - root.activeWorkspaceMargin * 2
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor) readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)
@@ -60,17 +59,15 @@ Item {
anchors.fill: parent anchors.fill: parent
anchors.margins: root.activeWorkspaceMargin anchors.margins: root.activeWorkspaceMargin
columnSpacing: root.spacing columnSpacing: 0
columns: root.horizontal ? root.workspacesShown : 1 columns: root.horizontal ? root.workspacesShown : 1
rowSpacing: root.spacing rowSpacing: 0
rows: root.horizontal ? 1 : root.workspacesShown rows: root.horizontal ? 1 : root.workspacesShown
layer.enabled: true
layer.mipmap: true
Repeater { Repeater {
model: root.workspaces model: root.workspaces
CustomRect { Button {
id: button id: button
required property int index required property int index
@@ -78,19 +75,27 @@ Item {
implicitHeight: indicator.indicatorThickness implicitHeight: indicator.indicatorThickness
implicitWidth: indicator.indicatorThickness implicitWidth: indicator.indicatorThickness
radius: Tokens.rounding.full width: root.workspaceButtonWidth
CustomText { background: Item {
anchors.centerIn: parent id: workspaceButtonBackground
color: Colors.palette.m3onSecondaryContainer
text: button.modelData?.name ?? "" 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: {
onPressed: { const ws = button.modelData.name;
const ws = button.modelData.name; Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
}
} }
} }
} }
@@ -99,10 +104,8 @@ Item {
CustomRect { CustomRect {
id: indicator id: indicator
readonly property real step: root.workspaceButtonWidth + root.spacing 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 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 indicatorThickness: root.workspaceButtonWidth property real indicatorThickness: root.workspaceButtonWidth
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter
@@ -126,10 +129,9 @@ Item {
implicitHeight: grid.height implicitHeight: grid.height
implicitWidth: grid.width implicitWidth: grid.width
source: grid source: grid
autoPaddingEnabled: false
sourceColor: Colors.palette.m3onSurface sourceColor: Colors.palette.m3onSurface
x: root.horizontal ? -indicator.x + root.activeWorkspaceMargin : 0 x: root.horizontal ? -indicator.x + 3 : 0
y: root.horizontal ? 0 : -indicator.y + root.activeWorkspaceMargin y: root.horizontal ? 0 : -indicator.y + 3
} }
} }
} }
+12 -17
View File
@@ -5,7 +5,6 @@ import Quickshell.Services.Pipewire
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import QtQuick.Controls import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
@@ -47,8 +46,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
component VolumesTab: SnapLayout { component VolumesTab: ColumnLayout {
orientation: Qt.Vertical
spacing: 12 spacing: 12
CustomRect { CustomRect {
@@ -58,15 +56,14 @@ orientation: Qt.Vertical
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
radius: root.rounding - vol.anchors.margins radius: root.rounding - vol.anchors.margins
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
anchors.bottomMargin: Tokens.padding.small anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small anchors.topMargin: Tokens.padding.small
SnapLayout { RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -84,7 +81,7 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { RowLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.largeIncreased
CustomMouseArea { CustomMouseArea {
@@ -137,7 +134,7 @@ orientation: Qt.Vertical
node: Audio.source node: Audio.source
} }
ClipRect { CustomClippingRect {
anchors.fill: parent anchors.fill: parent
radius: parent.radius radius: parent.radius
@@ -156,15 +153,14 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
anchors.bottomMargin: Tokens.padding.small anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small anchors.topMargin: Tokens.padding.small
SnapLayout { RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -182,7 +178,7 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { RowLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.largeIncreased
CustomMouseArea { CustomMouseArea {
@@ -253,7 +249,7 @@ orientation: Qt.Vertical
node: appBox.modelData node: appBox.modelData
} }
ClipRect { CustomClippingRect {
anchors.fill: parent anchors.fill: parent
radius: parent.radius radius: parent.radius
@@ -280,15 +276,14 @@ orientation: Qt.Vertical
text: Audio.getStreamName(appBox.modelData) text: Audio.getStreamName(appBox.modelData)
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
anchors.bottomMargin: Tokens.padding.small anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.small
anchors.topMargin: Tokens.padding.small anchors.topMargin: Tokens.padding.small
SnapLayout { RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -307,7 +302,7 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { RowLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.largeIncreased
CustomMouseArea { CustomMouseArea {
+4 -4
View File
@@ -4,7 +4,6 @@ import QtQuick
import Quickshell import Quickshell
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import ZShell
Item { Item {
id: root id: root
@@ -37,7 +36,7 @@ Item {
const diff = parent.width - Math.floor(off + content.nonAnimWidth); const diff = parent.width - Math.floor(off + content.nonAnimWidth);
if (diff < 0) if (diff < 0)
return off + diff; return off + diff;
return ZUtils.snap(Math.max(off, 0), root); return Math.floor(Math.max(off, 0));
} }
y: { y: {
if (horizontal) if (horizontal)
@@ -47,11 +46,12 @@ Item {
const diff = parent.height - Math.floor(off + content.nonAnimHeight); const diff = parent.height - Math.floor(off + content.nonAnimHeight);
if (diff < 0) if (diff < 0)
return off + diff; return off + diff;
return ZUtils.snap(Math.max(off, 0), root); return Math.max(off, 0);
} }
Behavior on offsetScale { Behavior on offsetScale {
Anim {} Anim {
}
} }
Behavior on x { Behavior on x {
enabled: !root.horizontal || root.offsetScale < 1 enabled: !root.horizontal || root.offsetScale < 1
+1 -3
View File
@@ -4,7 +4,6 @@ import Quickshell
import Quickshell.Networking import Quickshell.Networking
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import qs.Modules import qs.Modules
@@ -15,8 +14,7 @@ Item {
required property var wrapper required property var wrapper
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: layout id: layout
spacing: 8 spacing: 8
+1 -3
View File
@@ -4,7 +4,6 @@ import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects import QtQuick.Effects
import qs.Components import qs.Components
@@ -48,8 +47,7 @@ StackView {
duration: 0 duration: 0
} }
} }
component SubMenu: SnapLayout { component SubMenu: ColumnLayout {
orientation: Qt.Vertical
id: menu id: menu
required property QsMenuHandle handle required property QsMenuHandle handle
+1 -2
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell.Services.UPower import Quickshell.Services.UPower
import ZShell.Config import ZShell.Config
import qs.Modules.Bar.Components.Common import qs.Modules.Bar.Components.Common
@@ -36,7 +35,7 @@ Column {
Repeater { Repeater {
model: Battery.allPeripherals model: Battery.allPeripherals
SnapLayout { RowLayout {
id: layout id: layout
required property var modelData required property var modelData
+3 -5
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell import Quickshell
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
@@ -87,7 +86,7 @@ CustomRect {
implicitWidth: 600 implicitWidth: 600
radius: root.radius - Tokens.padding.extraSmall radius: root.radius - Tokens.padding.extraSmall
SnapLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.small
@@ -97,8 +96,7 @@ CustomRect {
text: "package_2" text: "package_2"
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
Layout.fillWidth: true Layout.fillWidth: true
CustomText { CustomText {
@@ -116,7 +114,7 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.preferredWidth: 300 Layout.preferredWidth: 300
+2 -3
View File
@@ -2,7 +2,6 @@ import Quickshell
import QtQuick import QtQuick
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import ZShell
Item { Item {
id: root id: root
@@ -27,8 +26,8 @@ Item {
detachedMode = ""; detachedMode = "";
} }
implicitHeight: ZUtils.snap(nonAnimHeight, root) implicitHeight: nonAnimHeight
implicitWidth: ZUtils.snap(nonAnimWidth, root) implicitWidth: nonAnimWidth
Behavior on implicitHeight { Behavior on implicitHeight {
enabled: root.offsetScale < 1 enabled: root.offsetScale < 1
+5 -8
View File
@@ -4,7 +4,6 @@ import Quickshell
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import ZShell.Config import ZShell.Config
@@ -26,7 +25,7 @@ Item {
searchField.forceActiveFocus(); searchField.forceActiveFocus();
} }
ClipRect { CustomClippingRect {
id: search id: search
anchors.left: parent.left anchors.left: parent.left
@@ -55,7 +54,7 @@ Item {
} }
} }
CustomRect { CustomClippingRect {
id: preview id: preview
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -65,7 +64,6 @@ Item {
color: Colors.tPalette.m3surfaceContainer 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) 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 radius: 25
clip: true
Behavior on implicitWidth { Behavior on implicitWidth {
Anim {} Anim {}
@@ -112,7 +110,7 @@ Item {
Repeater { Repeater {
model: lineRow.modelData.text.split("\t").slice(1) model: lineRow.modelData.text.split("\t").slice(1)
SnapLayout { RowLayout {
height: lineText.height height: lineText.height
width: lineText.height + Tokens.spacing.extraSmall width: lineText.height + Tokens.spacing.extraSmall
@@ -226,16 +224,15 @@ Item {
} }
} }
SnapLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
Item { CustomClippingRect {
id: textRect id: textRect
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
clip: true
Item { Item {
id: textWrapper id: textWrapper
+4 -5
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import ZShell import ZShell
import qs.Components import qs.Components
@@ -40,8 +39,8 @@ Item {
]; ];
return allTabs.filter(tab => tab.enabled); 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 nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin
readonly property real nonAnimWidth: ZUtils.snap(view.implicitWidth + viewWrapper.anchors.margins * 2, root) readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
required property ScreenState screenState required property ScreenState screenState
implicitHeight: nonAnimHeight implicitHeight: nonAnimHeight
@@ -94,8 +93,8 @@ Item {
} }
anchors.fill: parent anchors.fill: parent
implicitHeight: currentItem?.height ?? currentItem?.implicitHeight ?? 0 implicitHeight: currentItem?.implicitHeight ?? 0
implicitWidth: currentItem?.width ?? currentItem?.implicitWidth ?? 0 implicitWidth: currentItem?.implicitWidth ?? 0
Repeater { Repeater {
id: repeater id: repeater
+75 -53
View File
@@ -1,82 +1,104 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell
import ZShell.Config
import ZShell.Components
import qs.Helpers import qs.Helpers
import qs.Components import qs.Components
import qs.Modules import qs.Modules
import ZShell.Config
import qs.Modules.Dashboard.Dash import qs.Modules.Dashboard.Dash
import qs.Services import qs.Services
SnapLayout { GridLayout {
orientation: Qt.Vertical
id: root id: root
readonly property bool dashboardVisible: screenState.dashboard readonly property bool dashboardVisible: screenState.dashboard
required property int radius required property int radius
required property ScreenState screenState required property ScreenState screenState
spacing: ZUtils.snap(Tokens.spacing.small, root) columnSpacing: Tokens.spacing.small
rowSpacing: Tokens.spacing.small
height: ZUtils.snap(implicitHeight, root) ParallelAnimation {
width: ZUtils.snap(implicitWidth, root) id: openAnim
SnapLayout { Anim {
Layout.fillWidth: true property: "opacity"
spacing: parent.spacing target: root
to: 1
Rect {
id: weatherRect
Layout.fillHeight: true
Layout.preferredWidth: ZUtils.snap(Tokens.sizes.dashboard.weatherWidth, root)
radius: root.radius
Weather {}
} }
Rect { Anim {
id: userRect property: "scale"
Layout.preferredHeight: ZUtils.snap(user.implicitHeight, root) target: root
Layout.preferredWidth: ZUtils.snap(user.implicitWidth, root) to: 1
radius: root.radius
User {
id: user
screenState: root.screenState
}
} }
} }
SnapLayout { ParallelAnimation {
Layout.fillWidth: true id: closeAnim
spacing: parent.spacing
Rect { Anim {
id: calendarRect property: "opacity"
Layout.fillWidth: true target: root
Layout.fillHeight: true to: 0
implicitHeight: ZUtils.snap(calendar.implicitHeight, root)
radius: root.radius
Calendar {
id: calendar
screenState: root.screenState
}
} }
Rect { Anim {
id: resourcesRect property: "scale"
Layout.fillHeight: true target: root
implicitWidth: ZUtils.snap(resources.implicitWidth, root) to: 0.9
radius: root.radius }
}
Resources { Rect {
id: resources 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
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -118,15 +117,14 @@ CustomMouseArea {
} }
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: inner id: inner
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.extraSmall
SnapLayout { RowLayout {
id: monthNavigationRow id: monthNavigationRow
Layout.fillWidth: true Layout.fillWidth: true
+9 -11
View File
@@ -1,7 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell
import ZShell.Services import ZShell.Services
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
@@ -10,8 +8,9 @@ import qs.Services
Item { Item {
id: root id: root
anchors.fill: parent anchors.bottom: parent.bottom
implicitWidth: ZUtils.snap(layout.implicitWidth + layout.anchors.margins * 2, root) anchors.top: parent.top
implicitWidth: layout.implicitWidth + layout.anchors.margins * 4
ServiceRef { ServiceRef {
service: Storage service: Storage
@@ -29,16 +28,13 @@ Item {
service: Gpu service: Gpu
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: layout id: layout
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
anchors.top: parent.top anchors.top: parent.top
x: ZUtils.snap((root.width - implicitWidth) / 2, root)
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
Resource { Resource {
@@ -77,10 +73,12 @@ orientation: Qt.Vertical
required property string icon 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 { Behavior on clampedVal {
Anim {} Anim {
}
} }
MaterialIcon { MaterialIcon {
+1 -1
View File
@@ -14,7 +14,7 @@ Row {
padding: 20 padding: 20
spacing: 12 spacing: 12
ClipRect { CustomClippingRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2) color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: info.implicitHeight implicitHeight: info.implicitHeight
implicitWidth: info.implicitHeight implicitWidth: info.implicitHeight
+6 -10
View File
@@ -1,8 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import M3Shapes import M3Shapes
import ZShell
import ZShell.Config import ZShell.Config
import qs.Modules.Dashboard.Media import qs.Modules.Dashboard.Media
import qs.Components import qs.Components
@@ -15,10 +13,10 @@ Item {
property ScreenState screenState property ScreenState screenState
required property int radius required property int radius
implicitWidth: ZUtils.snap(1000, root) implicitWidth: 1000
implicitHeight: ZUtils.snap(320, root) implicitHeight: 320
ClipRect { CustomClippingRect {
anchors.fill: parent anchors.fill: parent
radius: root.radius radius: root.radius
@@ -27,7 +25,7 @@ Item {
} }
} }
SnapLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraLarge spacing: Tokens.spacing.extraLarge
@@ -96,8 +94,7 @@ Item {
active: opacity > 0 active: opacity > 0
opacity: 0 opacity: 0
sourceComponent: SnapLayout { sourceComponent: ColumnLayout {
orientation: Qt.Vertical
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
MaterialShape { MaterialShape {
@@ -143,12 +140,11 @@ orientation: Qt.Vertical
asynchronous: true asynchronous: true
active: opacity > 0 active: opacity > 0
sourceComponent: SnapLayout { sourceComponent: RowLayout {
spacing: Tokens.spacing.extraLarge spacing: Tokens.spacing.extraLarge
Details { Details {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true
} }
LyricsAndSelector { LyricsAndSelector {
+154 -178
View File
@@ -1,14 +1,13 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.Mpris import Quickshell.Services.Mpris
import ZShell
import ZShell.Components import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
import qs.Helpers import qs.Helpers
Item { ColumnLayout {
id: root id: root
readonly property bool hasUnknownLength: (Players.active?.length ?? 0) > 2147483647 readonly property bool hasUnknownLength: (Players.active?.length ?? 0) > 2147483647
@@ -26,187 +25,46 @@ Item {
return `${mins}:${secs}`; return `${mins}:${secs}`;
} }
implicitHeight: ZUtils.snap(layout.implicitHeight, root) spacing: Tokens.spacing.extraSmall
implicitWidth: layout.implicitWidth
SnapLayout { Timer {
orientation: Qt.Vertical running: Players.active?.isPlaying ?? false
id: layout interval: Config.dashboard.mediaUpdateInterval
anchors.left: parent.left triggeredOnStart: true
anchors.right: parent.right repeat: true
y: ZUtils.snap((parent.height - implicitHeight) / 2, root) 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 { CustomText {
running: Players.active?.isPlaying ?? false Layout.fillWidth: true
interval: Config.dashboard.mediaUpdateInterval text: Players.active?.trackArtist || qsTr("Unknown artist")
triggeredOnStart: true color: Colors.palette.m3onSurfaceVariant
repeat: true font: Tokens.font.title.medium
onTriggered: Players.active?.positionChanged() elide: Text.ElideRight
} animate: true
}
CustomText { CustomText {
id: title Layout.fillWidth: true
Layout.fillWidth: true text: Players.active?.trackAlbum || qsTr("Unknown album")
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root) color: Colors.palette.m3secondary
text: Players.active?.trackTitle ?? "" font: Tokens.font.title.medium
font: Tokens.font.title.large elide: Text.ElideRight
elide: Text.ElideRight animate: true
animate: true }
}
CustomText { RowLayout {
id: artist Layout.topMargin: Tokens.spacing.extraLargeIncreased
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root) spacing: Tokens.spacing.small
text: Players.active?.trackArtist || qsTr("Unknown artist")
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.title.medium
elide: Text.ElideRight
animate: true
}
CustomText {
id: album
Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root)
text: Players.active?.trackAlbum || qsTr("Unknown album")
color: Colors.palette.m3secondary
font: Tokens.font.title.medium
elide: Text.ElideRight
animate: true
}
SnapLayout {
id: sliderLayout
Layout.topMargin: Tokens.spacing.extraLargeIncreased
Layout.fillWidth: true
spacing: ZUtils.snap(Tokens.spacing.small, root)
CustomText {
id: positionLabel
Layout.preferredWidth: ZUtils.snapCeil(timeMetrics.width, root)
Layout.preferredHeight: positionSlider.implicitHeight
text: root.lengthStr(Players.active?.position ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
CustomSlider {
id: positionSlider
Layout.fillWidth: true
Layout.preferredHeight: ZUtils.snap(implicitHeight, root)
value: Players.active ? Players.active.position / (Players.active.length || 1) : 0
enabled: (Players.active?.canSeek ?? false) && !root.hasUnknownLength
wavy: true
animateWave: Players.active?.isPlaying ?? false
waveFrequency: 5
waveDuration: 2000
interactionOnMove: false
onInteraction: value => {
const active = Players.active;
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
}
Binding {
target: positionLabel
property: "text"
value: root.lengthStr(positionSlider.pos * (Players.active?.length ?? 0))
when: positionSlider.dragging
}
}
CustomText {
Layout.preferredWidth: ZUtils.snapCeil(timeMetrics.width, root)
Layout.preferredHeight: positionSlider.implicitHeight
text: root.hasUnknownLength ? "--:--" : root.lengthStr(Players.active?.length ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
ButtonRow {
Layout.topMargin: Tokens.spacing.largeIncreased
Layout.fillWidth: true
spacing: Tokens.spacing.extraSmall
IconButton {
type: IconButton.Tonal
icon: "shuffle"
isRound: true
shapeMorph: true
checked: Players.active?.shuffle ?? false
font: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
enabled: Players.active?.shuffleSupported
onClicked: Players.active.shuffle = !Players.active?.shuffle
implicitWidth: Math.round(implicitHeight * 0.9)
}
IconButton {
id: previousBtn
type: IconButton.Tonal
icon: "skip_previous"
isRound: true
shapeMorph: true
font: Tokens.font.icon.large
enabled: Players.active?.canGoPrevious
onClicked: Players.active?.previous()
}
IconButton {
id: playPauseBtn
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
isRound: true
shapeMorph: true
fillWidth: true
checked: Players.active?.isPlaying ?? false
font: Tokens.font.icon.large
enabled: Players.active?.canTogglePlaying
onClicked: Players.active?.togglePlaying()
}
IconButton {
id: nextBtn
type: IconButton.Tonal
icon: "skip_next"
isRound: true
shapeMorph: true
font: Tokens.font.icon.large
enabled: Players.active?.canGoNext
onClicked: Players.active?.next()
}
IconButton {
type: IconButton.Tonal
icon: Players.active?.loopState === MprisLoopState.Track ? "repeat_one" : "repeat"
isRound: true
shapeMorph: true
checked: Players.active?.loopState === MprisLoopState.Track || Players.active?.loopState === MprisLoopState.Playlist
font: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
enabled: Players.active?.loopSupported
onClicked: {
const state = Players.active.loopState;
if (state === MprisLoopState.None)
Players.active.loopState = MprisLoopState.Track;
else if (state === MprisLoopState.Track)
Players.active.loopState = MprisLoopState.Playlist;
else
Players.active.loopState = MprisLoopState.None;
}
implicitWidth: Math.round(implicitHeight * 0.9)
}
}
TextMetrics { TextMetrics {
id: timeMetrics 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" 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 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
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell.Config import ZShell.Config
import ZShell.Services import ZShell.Services
import qs.Components import qs.Components
@@ -158,8 +157,7 @@ Item {
active: opacity > 0 active: opacity > 0
opacity: 0 opacity: 0
sourceComponent: SnapLayout { sourceComponent: ColumnLayout {
orientation: Qt.Vertical
spacing: Tokens.spacing.large spacing: Tokens.spacing.large
CustomRect { CustomRect {
@@ -200,8 +198,7 @@ orientation: Qt.Vertical
active: opacity > 0 active: opacity > 0
opacity: 0 opacity: 0
sourceComponent: SnapLayout { sourceComponent: ColumnLayout {
orientation: Qt.Vertical
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
MaterialIcon { MaterialIcon {
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell import Quickshell
import Quickshell.Services.Mpris import Quickshell.Services.Mpris
import ZShell.Config import ZShell.Config
@@ -9,15 +8,14 @@ import qs.Helpers
import qs.Services import qs.Services
Item { Item {
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: layout id: layout
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.medium anchors.leftMargin: Tokens.padding.medium
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
SnapLayout { RowLayout {
Layout.bottomMargin: -Tokens.spacing.medium Layout.bottomMargin: -Tokens.spacing.medium
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
z: 1 z: 1
+4 -6
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import ZShell import ZShell
import ZShell.Blobs import ZShell.Blobs
import ZShell.Config import ZShell.Config
@@ -17,7 +16,7 @@ Item {
implicitWidth: btn.implicitWidth * 0.9 implicitWidth: btn.implicitWidth * 0.9
implicitHeight: btn.implicitHeight * 0.9 implicitHeight: btn.implicitHeight * 0.9
BlobGroup2 { BlobGroup {
id: blobGroup id: blobGroup
color: Colors.palette.m3surfaceContainerHighest color: Colors.palette.m3surfaceContainerHighest
@@ -29,7 +28,7 @@ Item {
} }
} }
BlobRect2 { BlobRect {
id: btnRect id: btnRect
anchors.fill: parent anchors.fill: parent
@@ -42,7 +41,7 @@ Item {
} }
} }
BlobRect2 { BlobRect {
id: rect id: rect
anchors.right: parent.right anchors.right: parent.right
@@ -142,9 +141,8 @@ Item {
} }
] ]
SnapLayout { ColumnLayout {
id: layout id: layout
orientation: Qt.Vertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.extraSmall
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell import Quickshell
import ZShell.Config import ZShell.Config
import ZShell.Internal import ZShell.Internal
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import ZShell.Config import ZShell.Config
@@ -39,7 +38,7 @@ CustomListView {
id: rowHover id: rowHover
} }
SnapLayout { RowLayout {
id: rowLayout id: rowLayout
anchors.left: parent.left anchors.left: parent.left
@@ -47,7 +46,7 @@ CustomListView {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
spacing: Tokens.spacing.extraSmall / 2 spacing: Tokens.spacing.extraSmall / 2
ClipRect { CustomClippingRect {
id: iconClip id: iconClip
Layout.fillHeight: true Layout.fillHeight: true
+4 -6
View File
@@ -23,7 +23,7 @@ Item {
service: Memory service: Memory
} }
SnapLayout { RowLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
@@ -60,14 +60,13 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
implicitHeight: mainLayout.implicitHeight + mainLayout.anchors.margins * 2 implicitHeight: mainLayout.implicitHeight + mainLayout.anchors.margins * 2
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: mainLayout id: mainLayout
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: 0 spacing: 0
SnapLayout { RowLayout {
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
MaterialIcon { MaterialIcon {
@@ -132,8 +131,7 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: infoColumn id: infoColumn
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
+32 -57
View File
@@ -4,8 +4,6 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Templates import QtQuick.Templates
import Quickshell import Quickshell
import ZShell
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -18,17 +16,9 @@ Item {
required property var tabs required property var tabs
readonly property alias count: bar.count 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) { implicitHeight: bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
const dpr = ZUtils.dprFor(root);
let h = Math.ceil(v);
while (Math.abs(h * dpr - Math.round(h * dpr)) > 1e-6)
++h;
return h;
}
implicitHeight: ZUtils.snap(bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight, root)
TabBar { TabBar {
id: bar id: bar
@@ -41,11 +31,9 @@ Item {
currentIndex: root.screenState.dashboardTab currentIndex: root.screenState.dashboardTab
onCurrentIndexChanged: root.screenState.dashboardTab = currentIndex onCurrentIndexChanged: root.screenState.dashboardTab = currentIndex
topPadding: 0 implicitHeight: contentHeight
bottomPadding: 0
implicitHeight: implicitContentHeight
background: null background: null
contentItem: SnapLayout { contentItem: RowLayout {
spacing: 0 spacing: 0
Repeater { Repeater {
@@ -128,13 +116,10 @@ Item {
readonly property bool current: TabBar.tabBar.currentItem === this readonly property bool current: TabBar.tabBar.currentItem === this
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.preferredWidth: 1 // Uniform width across all tabs
Layout.preferredWidth: 1
implicitWidth: implicitContentWidth implicitWidth: implicitContentWidth
implicitHeight: implicitContentHeight implicitHeight: implicitContentHeight
background: null background: null
topPadding: 0
bottomPadding: 0
contentItem: CustomMouseArea { contentItem: CustomMouseArea {
id: mouse id: mouse
@@ -146,8 +131,8 @@ Item {
root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0); root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0);
} }
implicitWidth: Math.max(icon.implicitWidth, label.implicitWidth) implicitWidth: Math.max(icon.width, label.width)
implicitHeight: snapLayout.implicitHeight implicitHeight: icon.height + label.height
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@@ -161,49 +146,39 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.alignWhenCentered: false implicitHeight: parent.height + root.tabIndicatorSpacing * 2
implicitHeight: ZUtils.snap(parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2, stateLayer)
radius: Tokens.rounding.medium radius: Tokens.rounding.medium
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurface color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurface
onClicked: { onClicked: root.screenState.dashboardTab = tab.TabBar.index
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 { CustomText {
id: snapLayout id: label
anchors.fill: parent
spacing: 0
orientation: Qt.Vertical
MaterialIcon { anchors.horizontalCenter: parent.horizontalCenter
id: icon anchors.bottom: parent.bottom
text: tab.iconName text: tab.text
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant 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
}
} }
} }
} }
+6 -12
View File
@@ -1,13 +1,11 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
import qs.Helpers import qs.Helpers
SnapLayout { RowLayout {
id: root id: root
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.largeIncreased
@@ -22,7 +20,7 @@ SnapLayout {
readonly property real markerX: { readonly property real markerX: {
const d = root.now; const d = root.now;
return ZUtils.snap(d.getHours() + d.getMinutes() / 60, root); return d.getHours() + d.getMinutes() / 60;
} }
function formatHour(h) { function formatHour(h) {
@@ -41,13 +39,11 @@ SnapLayout {
onTriggered: root.now = new Date() onTriggered: root.now = new Date()
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2 Layout.preferredWidth: root.width / 2
SnapLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
MaterialIcon { MaterialIcon {
@@ -94,13 +90,11 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2 Layout.preferredWidth: root.width / 2
SnapLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
MaterialIcon { MaterialIcon {
+7 -14
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Components
import qs.Components import qs.Components
import qs.Modules.Dashboard.Weather import qs.Modules.Dashboard.Weather
import qs.Services import qs.Services
@@ -19,14 +18,13 @@ Item {
implicitHeight: layout.implicitHeight implicitHeight: layout.implicitHeight
Component.onCompleted: Weather.reload() Component.onCompleted: Weather.reload()
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: layout id: layout
anchors.fill: parent anchors.fill: parent
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
SnapLayout { RowLayout {
Layout.leftMargin: Tokens.padding.large Layout.leftMargin: Tokens.padding.large
Layout.rightMargin: Tokens.padding.large Layout.rightMargin: Tokens.padding.large
Layout.fillWidth: true Layout.fillWidth: true
@@ -84,7 +82,7 @@ orientation: Qt.Vertical
} }
} }
SnapLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
@@ -123,9 +121,7 @@ orientation: Qt.Vertical
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
} }
SnapLayout { RowLayout {
id: forecastLayout
Layout.fillWidth: true Layout.fillWidth: true
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
@@ -141,14 +137,12 @@ orientation: Qt.Vertical
required property var modelData required property var modelData
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2 implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
radius: root.radius radius: root.radius
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: forecastItemColumn id: forecastItemColumn
anchors.centerIn: parent anchors.centerIn: parent
@@ -202,8 +196,7 @@ orientation: Qt.Vertical
property color colour property color colour
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.preferredHeight: 60
implicitHeight: 60
radius: Tokens.rounding.medium radius: Tokens.rounding.medium
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
@@ -237,7 +230,7 @@ orientation: Qt.Vertical
} }
} }
component WeatherStat: SnapLayout { component WeatherStat: RowLayout {
id: weatherStat id: weatherStat
property string icon property string icon
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Hyprland import Quickshell.Hyprland
@@ -26,7 +25,7 @@ Item {
onClicked: root.close() onClicked: root.close()
} }
ClipRect { CustomClippingRect {
id: popupBackground id: popupBackground
readonly property real padding: 4 readonly property real padding: 4
@@ -44,8 +43,7 @@ Item {
} }
opacity: root.visible ? 1 : 0 opacity: root.visible ? 1 : 0
SnapLayout { ColumnLayout {
orientation: Qt.Vertical
id: menuLayout id: menuLayout
anchors.centerIn: parent anchors.centerIn: parent
spacing: 0 spacing: 0
@@ -55,7 +53,7 @@ orientation: Qt.Vertical
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
implicitHeight: openTerminalRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: openTerminalRow.implicitHeight + Tokens.padding.extraSmall * 2
SnapLayout { RowLayout {
id: openTerminalRow id: openTerminalRow
spacing: 8 spacing: 8
anchors.fill: parent anchors.fill: parent
@@ -94,7 +92,7 @@ orientation: Qt.Vertical
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
implicitHeight: settingsRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: settingsRow.implicitHeight + Tokens.padding.extraSmall * 2
SnapLayout { RowLayout {
id: settingsRow id: settingsRow
spacing: 8 spacing: 8
anchors.fill: parent anchors.fill: parent
@@ -134,7 +132,7 @@ orientation: Qt.Vertical
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
implicitHeight: logoutRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: logoutRow.implicitHeight + Tokens.padding.extraSmall * 2
SnapLayout { RowLayout {
id: logoutRow id: logoutRow
spacing: 8 spacing: 8
anchors.fill: parent anchors.fill: parent
@@ -173,7 +171,7 @@ orientation: Qt.Vertical
// radius: popupBackground.radius - popupBackground.padding // radius: popupBackground.radius - popupBackground.padding
// implicitHeight: desktopIconsRow.implicitHeight + Tokens.padding.extraSmall * 2 // implicitHeight: desktopIconsRow.implicitHeight + Tokens.padding.extraSmall * 2
// //
// SnapLayout { // RowLayout {
// id: desktopIconsRow // id: desktopIconsRow
// spacing: 8 // spacing: 8
// anchors.fill: parent // anchors.fill: parent
@@ -3,7 +3,6 @@ import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import qs.Components import qs.Components
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -41,7 +40,7 @@ Item {
visible: false visible: false
z: 999 z: 999
ClipRect { CustomClippingRect {
id: popupBackground id: popupBackground
readonly property real padding: Tokens.padding.extraSmall readonly property real padding: Tokens.padding.extraSmall
@@ -58,25 +57,22 @@ Item {
Anim {} Anim {}
} }
SnapLayout { ColumnLayout {
id: menuLayout id: menuLayout
anchors.centerIn: parent anchors.centerIn: parent
spacing: 0 spacing: 0
orientation: Qt.Vertical
CustomRect { CustomRect {
Layout.preferredWidth: 160 Layout.preferredWidth: 160
implicitHeight: openRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: openRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
SnapLayout { RowLayout {
id: openRow id: openRow
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
spacing: 8 spacing: 8
MaterialIcon { MaterialIcon {
@@ -115,12 +111,11 @@ Item {
implicitHeight: openWithRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: openWithRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
SnapLayout { RowLayout {
id: openWithRow id: openWithRow
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
spacing: 8 spacing: 8
MaterialIcon { MaterialIcon {
@@ -161,12 +156,11 @@ Item {
implicitHeight: copyPathRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: copyPathRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
SnapLayout { RowLayout {
id: copyPathRow id: copyPathRow
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
spacing: 8 spacing: 8
MaterialIcon { MaterialIcon {
@@ -196,12 +190,11 @@ Item {
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
visible: contextMenu.targetPaths.length === 1 visible: contextMenu.targetPaths.length === 1
SnapLayout { RowLayout {
id: renameRow id: renameRow
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
spacing: 8 spacing: 8
MaterialIcon { MaterialIcon {
@@ -238,12 +231,11 @@ Item {
implicitHeight: deleteRow.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: deleteRow.implicitHeight + Tokens.padding.extraSmall * 2
radius: popupBackground.radius - popupBackground.padding radius: popupBackground.radius - popupBackground.padding
SnapLayout { RowLayout {
id: deleteRow id: deleteRow
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.small
spacing: 8 spacing: 8
MaterialIcon { MaterialIcon {
+2 -7
View File
@@ -4,7 +4,6 @@ import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import ZShell.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -30,10 +29,7 @@ CustomRect {
active: !isSeparator active: !isSeparator
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: SnapLayout { sourceComponent: ColumnLayout {
spacing: Tokens.spacing.extraSmall
orientation: Qt.Vertical
IconImage { IconImage {
id: icon id: icon
@@ -42,9 +38,8 @@ CustomRect {
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel?.appId), "image-missing") source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel?.appId), "image-missing")
} }
SnapLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
spacing: 3 spacing: 3
Repeater { Repeater {
-1
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Components
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
+1 -3
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import ZShell
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -53,7 +52,7 @@ CustomListView {
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
highlightRangeMode: ListView.ApplyRange 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 orientation: Qt.Vertical
preferredHighlightBegin: 0 preferredHighlightBegin: 0
preferredHighlightEnd: height preferredHighlightEnd: height
@@ -98,7 +97,6 @@ CustomListView {
} }
} }
highlight: CustomRect { highlight: CustomRect {
id: hl
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
implicitHeight: root.currentItem?.implicitHeight ?? 0 implicitHeight: root.currentItem?.implicitHeight ?? 0
implicitWidth: root.width implicitWidth: root.width
+3 -2
View File
@@ -22,7 +22,7 @@ CarouselView {
delegateProperties: ({ delegateProperties: ({
screenState: root.screenState screenState: root.screenState
}) })
focalWidth: Tokens.sizes.launcher.wallpaperWidth focalWidth: Config.launcher.sizes.wallpaperWidth
glideDuration: 250 glideDuration: 250
glideEasingType: Easing.OutCubic glideEasingType: Easing.OutCubic
implicitHeight: tileHeight + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.small implicitHeight: tileHeight + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.small
@@ -41,7 +41,8 @@ CarouselView {
tileHeight: focalWidth / 16 * 9 tileHeight: focalWidth / 16 * 9
delegate: Component { delegate: Component {
WallpaperTile {} WallpaperTile {
}
} }
Component.onDestruction: Wallpapers.stopPreview() Component.onDestruction: Wallpapers.stopPreview()
+2 -3
View File
@@ -2,18 +2,17 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell
import ZShell.Config
import qs.Modules.Launcher.Services import qs.Modules.Launcher.Services
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import ZShell.Config
import qs.Services import qs.Services
Item { Item {
id: root id: root
required property real maxHeight 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 required property var panels
readonly property int rounding: Tokens.rounding.largeIncreased readonly property int rounding: Tokens.rounding.largeIncreased
required property ScreenState screenState required property ScreenState screenState
+4 -5
View File
@@ -2,12 +2,11 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell
import ZShell.Config
import qs.Paths import qs.Paths
import qs.Modules import qs.Modules
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import ZShell.Config
import qs.Services import qs.Services
Item { Item {
@@ -72,7 +71,7 @@ Item {
PropertyChanges { PropertyChanges {
appList.active: true appList.active: true
root.implicitHeight: Math.min(root.maxHeight, appList.implicitHeight > 0 ? appList.implicitHeight : empty.implicitHeight) 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 { AnchorChanges {
@@ -84,8 +83,8 @@ Item {
name: "wallpapers" name: "wallpapers"
PropertyChanges { PropertyChanges {
root.implicitHeight: Tokens.sizes.launcher.wallpaperHeight root.implicitHeight: Config.launcher.sizes.wallpaperHeight
root.implicitWidth: Math.max(Tokens.sizes.launcher.itemWidth * 1.2, wallpaperList.implicitWidth) root.implicitWidth: Math.max(Config.launcher.sizes.itemWidth * 1.2, wallpaperList.implicitWidth)
wallpaperList.active: true wallpaperList.active: true
} }
} }
+1 -1
View File
@@ -13,7 +13,7 @@ Item {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: Tokens.sizes.launcher.itemHeight implicitHeight: Config.launcher.sizes.itemHeight
StateLayer { StateLayer {
radius: Tokens.rounding.medium radius: Tokens.rounding.medium

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