Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
017d7073d2 | ||
|
|
8271e5d013 | ||
|
|
566b71d386 | ||
|
|
c5608dc9b9 | ||
|
|
78a9577209 | ||
|
|
067291f478 | ||
|
|
395d1dad3d | ||
|
|
fe0ce991da | ||
|
|
858e4e5b12 | ||
|
|
ed7e977eb5 | ||
|
|
4252812e82 | ||
|
|
a893e6cbe6 | ||
|
|
3792e9d73f | ||
|
|
906b15b092 | ||
|
|
811ad6b7f4 | ||
|
|
76f5a23c2a | ||
|
|
c0744b29b6 | ||
|
|
5896256372 | ||
|
|
9cbb90f9e1 |
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Docker CLI
|
||||
run: apk add --no-cache docker-cli docker-cli-buildx
|
||||
run: apk add --no-cache docker-cli
|
||||
|
||||
- name: Login to registry
|
||||
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin
|
||||
|
||||
+20
-21
@@ -17,47 +17,46 @@ jobs:
|
||||
run: |
|
||||
find . \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
|
||||
-not -path './build/*' \
|
||||
-exec clang-format --dry-run -Werror --style=file {} +
|
||||
-exec clang-format -i --style=file {} +
|
||||
git diff --exit-code && echo "clang-format: passed"
|
||||
|
||||
build & lint:
|
||||
build:
|
||||
runs-on: alpine
|
||||
container:
|
||||
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache CMake build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: build
|
||||
key: cpp-build-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt') }}
|
||||
restore-keys: |
|
||||
cpp-build-
|
||||
- name: Configure
|
||||
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Cache ccache
|
||||
uses: actions/cache@v4
|
||||
- name: Build
|
||||
run: ninja -C build
|
||||
|
||||
clang-tidy:
|
||||
runs-on: alpine
|
||||
container:
|
||||
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-${{ runner.os }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-${{ runner.os }}-
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure
|
||||
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
run: ninja -C build
|
||||
env:
|
||||
CMAKE_CXX_COMPILER_LAUNCHER: ccache
|
||||
|
||||
- name: clang-tidy
|
||||
run: |
|
||||
sed -i 's/-mno-direct-extern-access //g' build/compile_commands.json
|
||||
set -o pipefail
|
||||
run-clang-tidy -p build -j "$(nproc)" \
|
||||
-source-filter="^$(pwd)/Plugins/" \
|
||||
-header-filter=".*Plugins/.*" \
|
||||
-removed-arg=-mno-direct-extern-access
|
||||
run-clang-tidy -p build -j $(nproc) -header-filter="Plugins/.*" 2>&1 | \
|
||||
grep -v 'Suppressed\|non-user\|non-system\|unknown argument.*mno-direct\|Found compiler error\|warnings generated\.\|^Use -header-filter'
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
fmt & lint:
|
||||
fmt:
|
||||
runs-on: alpine
|
||||
container: node:26-alpine
|
||||
|
||||
@@ -24,6 +24,20 @@ jobs:
|
||||
else
|
||||
echo "No JS/TS files found"
|
||||
fi
|
||||
|
||||
lint:
|
||||
runs-on: alpine
|
||||
container: node:26-alpine
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
apk add --no-cache \
|
||||
git
|
||||
|
||||
- name: ESLint
|
||||
run: |
|
||||
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
fmt & lint:
|
||||
static:
|
||||
runs-on: debian
|
||||
container: node:26-trixie-slim
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
. .venv/bin/activate
|
||||
ruff check .
|
||||
|
||||
test & nuitka build:
|
||||
verify:
|
||||
if: always()
|
||||
runs-on: debian
|
||||
container: node:26-trixie-slim
|
||||
|
||||
+99
-11
@@ -4,9 +4,9 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
clippy & fmt:
|
||||
build:
|
||||
runs-on: alpine
|
||||
container: node:26-alpine # keep node image for checkout to work
|
||||
container: node:26-alpine
|
||||
env:
|
||||
CARGO_HOME: ${{ github.workspace }}/.cargo
|
||||
|
||||
@@ -16,29 +16,34 @@ jobs:
|
||||
|
||||
- name: Cache cargo packages
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-cargo-packages
|
||||
with:
|
||||
path: |
|
||||
.cargo/registry
|
||||
.cargo/git
|
||||
target
|
||||
key: rust-build-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
key: rust-build-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
rust-${{ runner.os }}-cargo-
|
||||
rust-${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
rust-${{ runner.os }}-build-
|
||||
rust-
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
apk add --no-cache git cargo rust rust-clippy rustfmt
|
||||
apk add --no-cache \
|
||||
git \
|
||||
cargo \
|
||||
rust
|
||||
|
||||
- name: Clippy (with compile check)
|
||||
- name: Cargo check
|
||||
run: |
|
||||
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
|
||||
status=0
|
||||
for manifest in $(find . -name "Cargo.toml"); do
|
||||
cargo clippy --manifest-path "$manifest" -- -D warnings && \
|
||||
echo "$manifest: Clippy passed" || \
|
||||
{ echo "$manifest: Clippy failed"; status=1; }
|
||||
cargo check --manifest-path "$manifest" && \
|
||||
echo "$manifest: check passed" || \
|
||||
{ echo "$manifest: check failed"; exit 1; }
|
||||
done
|
||||
exit $status
|
||||
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
|
||||
echo "Rust files found but no Cargo.toml"
|
||||
exit 1
|
||||
@@ -46,6 +51,39 @@ jobs:
|
||||
echo "No Rust project found"
|
||||
fi
|
||||
|
||||
fmt:
|
||||
runs-on: alpine
|
||||
container: node:26-alpine
|
||||
env:
|
||||
CARGO_HOME: ${{ github.workspace }}/.cargo
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache cargo packages
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-cargo-packages
|
||||
with:
|
||||
path: |
|
||||
.cargo/registry
|
||||
.cargo/git
|
||||
target
|
||||
key: rust-fmt-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
rust-${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
rust-${{ runner.os }}-build-
|
||||
rust-
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
apk add --no-cache \
|
||||
git \
|
||||
cargo \
|
||||
rust \
|
||||
rustfmt
|
||||
|
||||
- name: Format check
|
||||
run: |
|
||||
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
|
||||
@@ -62,3 +100,53 @@ jobs:
|
||||
else
|
||||
echo "No Rust project found"
|
||||
fi
|
||||
|
||||
clippy:
|
||||
runs-on: alpine
|
||||
container: node:26-alpine
|
||||
env:
|
||||
CARGO_HOME: ${{ github.workspace }}/.cargo
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache cargo packages
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-cargo-packages
|
||||
with:
|
||||
path: |
|
||||
.cargo/registry
|
||||
.cargo/git
|
||||
target
|
||||
key: rust-clippy-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
rust-${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
rust-${{ runner.os }}-build-
|
||||
rust-
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
apk add --no-cache \
|
||||
git \
|
||||
cargo \
|
||||
rust \
|
||||
rust-clippy
|
||||
|
||||
- name: Clippy
|
||||
run: |
|
||||
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
|
||||
status=0
|
||||
for manifest in $(find . -name "Cargo.toml"); do
|
||||
cargo clippy --manifest-path "$manifest" --all-targets --all-features -- -D warnings && \
|
||||
echo "$manifest: Clippy passed" || \
|
||||
{ echo "$manifest: Clippy failed"; status=1; }
|
||||
done
|
||||
exit $status
|
||||
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
|
||||
echo "Rust files found but no Cargo.toml"
|
||||
exit 1
|
||||
else
|
||||
echo "No Rust project found"
|
||||
fi
|
||||
|
||||
+10
-22
@@ -1,35 +1,23 @@
|
||||
**/__pycache__/
|
||||
./result/
|
||||
.pyre/
|
||||
.cache/
|
||||
.venv/
|
||||
.qmlls.ini
|
||||
build/
|
||||
compile_commands.json
|
||||
testpython
|
||||
pkg/
|
||||
*.tar.*
|
||||
uv.lock
|
||||
.qtcreator/
|
||||
dist/
|
||||
**/target/
|
||||
**/test-plugins/
|
||||
**/Charts/
|
||||
network-dev/
|
||||
**/zshell.dist/
|
||||
|
||||
# Qml
|
||||
.qtcreator/
|
||||
.qmlls.ini
|
||||
|
||||
# CMake compile artifacts
|
||||
.cache/
|
||||
compile_commands.json
|
||||
build/
|
||||
|
||||
# Python
|
||||
uv.lock
|
||||
testpython
|
||||
.venv/
|
||||
**/__pycache__/
|
||||
|
||||
# Nuitka compile
|
||||
**/zshell.pyi
|
||||
**/zshell.cpython-*
|
||||
**/zshell.build/
|
||||
|
||||
# Agents stuff
|
||||
**/zshell.dist/
|
||||
.opencode/
|
||||
run-agent.sh
|
||||
AGENTS.md
|
||||
|
||||
@@ -46,7 +46,8 @@ Item {
|
||||
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
@@ -83,7 +84,8 @@ Item {
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Anim on waveProgress {
|
||||
id: waveProgAnim
|
||||
|
||||
@@ -160,7 +160,8 @@ Item {
|
||||
Behavior on color {
|
||||
enabled: false
|
||||
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ MultiEffect {
|
||||
colorization: 1
|
||||
|
||||
Behavior on colorizationColor {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ ClippingRectangle {
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ ClippingWrapperRectangle {
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ ProgressBar {
|
||||
}
|
||||
|
||||
Line {
|
||||
id: dot
|
||||
property real implicitSize
|
||||
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -2,10 +2,10 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates
|
||||
import ZShell.Components
|
||||
import ZShell
|
||||
import qs.Components
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Slider {
|
||||
@@ -28,7 +28,7 @@ Slider {
|
||||
|
||||
signal interaction(v: real)
|
||||
|
||||
implicitHeight: 12
|
||||
implicitHeight: ZUtils.snap(12, root)
|
||||
implicitWidth: 200
|
||||
|
||||
contentItem: Item {
|
||||
@@ -123,7 +123,7 @@ Slider {
|
||||
bottomRightRadius: Tokens.rounding.extraSmall / 2
|
||||
color: root.fgColor
|
||||
implicitHeight: root.height
|
||||
implicitWidth: root.filledWidth
|
||||
implicitWidth: ZUtils.snap(root.filledWidth, root)
|
||||
radius: root.radius
|
||||
topRightRadius: Tokens.rounding.extraSmall / 2
|
||||
}
|
||||
@@ -137,8 +137,8 @@ Slider {
|
||||
frequency: root.waveFrequency
|
||||
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
|
||||
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
|
||||
implicitWidth: root.filledWidth
|
||||
lineWidth: root.height * 0.7
|
||||
implicitWidth: ZUtils.snap(root.filledWidth, root)
|
||||
lineWidth: ZUtils.snap(root.height * 0.7, root)
|
||||
startX: x
|
||||
|
||||
Behavior on color {
|
||||
|
||||
@@ -105,16 +105,20 @@ Switch {
|
||||
width: height
|
||||
|
||||
Behavior on end1 {
|
||||
PropAnim {}
|
||||
PropAnim {
|
||||
}
|
||||
}
|
||||
Behavior on end2 {
|
||||
PropAnim {}
|
||||
PropAnim {
|
||||
}
|
||||
}
|
||||
Behavior on start1 {
|
||||
PropAnim {}
|
||||
PropAnim {
|
||||
}
|
||||
}
|
||||
Behavior on start2 {
|
||||
PropAnim {}
|
||||
PropAnim {
|
||||
}
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
@@ -130,7 +134,8 @@ Switch {
|
||||
strokeWidth: 16 * 0.15
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathLine {
|
||||
|
||||
@@ -20,7 +20,8 @@ Text {
|
||||
textFormat: Text.PlainText
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Behavior on text {
|
||||
enabled: root.animate
|
||||
@@ -32,7 +33,8 @@ Text {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
|
||||
PropertyAction {}
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
TAnim {
|
||||
target: root
|
||||
|
||||
@@ -65,7 +65,8 @@ Image {
|
||||
value: root.source
|
||||
}
|
||||
|
||||
PropertyAction {}
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
ScriptAction {
|
||||
script: root.maybeStartInAnim()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import ZShell
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
@@ -15,9 +16,9 @@ ButtonBase {
|
||||
const h = label.implicitHeight + padding * 2;
|
||||
if (h % 2 !== 0)
|
||||
return h + 1;
|
||||
return h;
|
||||
return ZUtils.snap(h, root);
|
||||
}
|
||||
implicitWidth: implicitHeight
|
||||
implicitWidth: ZUtils.snap(implicitHeight, root)
|
||||
inactiveColor: {
|
||||
if (!isToggle && type === IconButton.Filled)
|
||||
return Colors.palette.m3primary;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
@@ -34,10 +35,11 @@ ButtonBase {
|
||||
}
|
||||
verticalPadding: Tokens.padding.extraSmall
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import QtQuick
|
||||
import qs.Services
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
|
||||
interactive: !ShellState.forActive().isDrawing
|
||||
|
||||
rebound: Transition {
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
|
||||
readonly property KineticWheelScroller _scroller: KineticWheelScroller {
|
||||
id: wheelScroller
|
||||
anchors.fill: parent
|
||||
parent: root
|
||||
z: 12
|
||||
flickable: root
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
import QtQuick
|
||||
|
||||
// Taken from hyprfm: https://github.com/soyeb-jim285/hyprfm/blob/main/src/qml/views/KineticWheelScroller.qml
|
||||
// Adapted to fit this project where needed.
|
||||
|
||||
// Deliberately not a MouseArea: a MouseArea claims the arrow cursor for its
|
||||
// whole area, which silently killed every cursorShape in the views this
|
||||
// overlays (column resize grips, pointer hands). A WheelHandler on a plain
|
||||
// Item takes wheel events without touching the cursor.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var flickable
|
||||
|
||||
property real wheelStep: 56
|
||||
property real mouseWheelMultiplier: 1.0
|
||||
property real touchpadMultiplier: 1.35
|
||||
property real minVelocity: 480
|
||||
property real maxVelocity: 5200
|
||||
property real kineticGain: 1.35
|
||||
property real smoothing: 0.72
|
||||
property real edgeEpsilon: 1.0
|
||||
// Browser-style overscroll: larger visible rubber-band, softer
|
||||
// resistance, and a springy settle on release.
|
||||
property real overshootLimit: 140
|
||||
property real overshootResistance: 0.38
|
||||
property real maxOvershootInput: 520
|
||||
// Bounce magnitude per unit of impact velocity. Keep this deliberately
|
||||
// small: slow edge hits should settle, not fire a visibly faster bounce.
|
||||
property real kineticBounceGain: 0.018
|
||||
property real maxKineticBounceInput: 90
|
||||
// Minimum impact velocity that actually produces a visible bounce.
|
||||
// Very slow landings skip bounce entirely — matches what browsers do
|
||||
// when a fling decays to near-zero before reaching the edge.
|
||||
property real minBounceVelocity: 260
|
||||
|
||||
property real velocity: 0
|
||||
property double lastWheelTimestamp: 0
|
||||
property bool kineticCandidate: false
|
||||
property real overshootInput: 0
|
||||
property real overshootReleaseVelocity: 0
|
||||
property real pendingKineticBounceVelocity: 0
|
||||
// Set by runKineticGlide when the glide will clamp against an edge.
|
||||
// It is the glide velocity at the instant of impact, which drives the
|
||||
// elastic bounce amount and duration — browser-style: slow impact
|
||||
// makes a small gentle bounce; fast impact makes a big springy one.
|
||||
property real pendingImpactVelocity: 0
|
||||
|
||||
// Browser-style exponential-feeling glide replaces the linear
|
||||
// flickDeceleration decay. Tail uses OutQuart — soft landing.
|
||||
property real kineticTau: 0.42
|
||||
|
||||
property real mouseGlideTime: 0.09 // decay time constant (s); higher = longer coast
|
||||
property real mouseAccelWindow: 140 // ms between notches that counts as "continuous"
|
||||
property real mouseAccelStep: 0.15 // extra impulse multiplier per consecutive notch
|
||||
property real mouseMaxAccel: 2.5
|
||||
property real mouseSnap: 0.2 // fraction of each notch applied instantly (0 = fully glided, 1 = no smoothing)
|
||||
|
||||
property real mouseVelocity: 0
|
||||
property int mouseDir: 0
|
||||
property int mouseStreak: 0
|
||||
property double mouseLastTime: 0
|
||||
|
||||
// Escape hatch if auto-detection misjudges a device
|
||||
property bool forceMouseWheel: false
|
||||
|
||||
readonly property real visualOvershoot: overshootInput < 0 ? rubberDistance(-overshootInput) : -rubberDistance(overshootInput)
|
||||
|
||||
signal scrollStarted
|
||||
|
||||
WheelHandler {
|
||||
id: wheelHandler
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
// `blocking` decides whether the event continues to the Flickable
|
||||
// underneath; handleWheel() flips it per event (the old MouseArea
|
||||
// code toggled wheel.accepted for the same purpose).
|
||||
onWheel: wheel => root.handleWheel(wheel)
|
||||
}
|
||||
|
||||
function isMouseWheel(wheel) {
|
||||
if (forceMouseWheel)
|
||||
return true;
|
||||
var ay = wheel.angleDelta.y;
|
||||
if (ay === 0)
|
||||
return false;
|
||||
return Math.abs(ay) % 120 === 0;
|
||||
}
|
||||
|
||||
function minimumContentY() {
|
||||
if (!flickable)
|
||||
return 0;
|
||||
|
||||
return flickable.originY;
|
||||
}
|
||||
|
||||
function maximumContentY() {
|
||||
if (!flickable)
|
||||
return 0;
|
||||
|
||||
return Math.max(minimumContentY(), minimumContentY() + flickable.contentHeight - flickable.height);
|
||||
}
|
||||
|
||||
function boundedContentY(value) {
|
||||
return Math.max(minimumContentY(), Math.min(maximumContentY(), value));
|
||||
}
|
||||
|
||||
function rubberDistance(distance) {
|
||||
if (distance <= 0)
|
||||
return 0;
|
||||
|
||||
var scaled = (distance * overshootResistance) / overshootLimit;
|
||||
return overshootLimit * (1 - 1 / (1 + scaled));
|
||||
}
|
||||
|
||||
function setOvershootInput(value) {
|
||||
overshootInput = Math.max(-maxOvershootInput, Math.min(maxOvershootInput, value));
|
||||
}
|
||||
|
||||
function hasOvershoot() {
|
||||
return Math.abs(overshootInput) > edgeEpsilon;
|
||||
}
|
||||
|
||||
function absorbOvershoot(delta) {
|
||||
if (!hasOvershoot())
|
||||
return delta;
|
||||
|
||||
var sameDirection = (overshootInput < 0 && delta < 0) || (overshootInput > 0 && delta > 0);
|
||||
|
||||
if (sameDirection) {
|
||||
setOvershootInput(overshootInput + delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var combined = overshootInput + delta;
|
||||
if ((overshootInput < 0 && combined < 0) || (overshootInput > 0 && combined > 0)) {
|
||||
setOvershootInput(combined);
|
||||
return 0;
|
||||
}
|
||||
|
||||
setOvershootInput(0);
|
||||
return combined;
|
||||
}
|
||||
|
||||
function deltaFor(wheel) {
|
||||
var rawDelta;
|
||||
if (isMouseWheel(wheel))
|
||||
rawDelta = (wheel.angleDelta.y / 120.0) * wheelStep * mouseWheelMultiplier;
|
||||
else
|
||||
rawDelta = wheel.pixelDelta.y !== 0 ? wheel.pixelDelta.y * touchpadMultiplier : (wheel.angleDelta.y / 120.0) * wheelStep * mouseWheelMultiplier;
|
||||
|
||||
return wheel.inverted ? rawDelta : -rawDelta;
|
||||
}
|
||||
|
||||
function shouldUseKinetic(wheel, gap) {
|
||||
return wheel.pixelDelta.y !== 0 || (wheel.phase !== Qt.NoScrollPhase && wheel.angleDelta.y === 0);
|
||||
}
|
||||
|
||||
function scrollBy(delta) {
|
||||
if (!flickable || Math.abs(delta) < 0.01)
|
||||
return;
|
||||
kineticBounceSequence.stop();
|
||||
bounceBackAnimation.stop();
|
||||
|
||||
var remainingDelta = absorbOvershoot(delta);
|
||||
if (Math.abs(remainingDelta) < 0.01)
|
||||
return;
|
||||
var minY = minimumContentY();
|
||||
var maxY = maximumContentY();
|
||||
var next = flickable.contentY + remainingDelta;
|
||||
|
||||
if (next < minY) {
|
||||
flickable.contentY = minY;
|
||||
setOvershootInput(overshootInput + next - minY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (next > maxY) {
|
||||
flickable.contentY = maxY;
|
||||
setOvershootInput(overshootInput + next - maxY);
|
||||
return;
|
||||
}
|
||||
|
||||
flickable.contentY = next;
|
||||
}
|
||||
|
||||
function remainingDistanceFor(velocityValue) {
|
||||
if (!flickable)
|
||||
return 0;
|
||||
|
||||
if (velocityValue > 0)
|
||||
return Math.max(0, maximumContentY() - flickable.contentY);
|
||||
|
||||
if (velocityValue < 0)
|
||||
return Math.max(0, flickable.contentY - minimumContentY());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function clampedContentVelocity(velocityValue) {
|
||||
if (!flickable || velocityValue === 0)
|
||||
return 0;
|
||||
|
||||
var remaining = remainingDistanceFor(velocityValue);
|
||||
if (remaining <= edgeEpsilon)
|
||||
return 0;
|
||||
|
||||
var maxAllowed = Math.sqrt(2 * flickable.flickDeceleration * remaining);
|
||||
var magnitude = Math.min(Math.abs(velocityValue), maxAllowed);
|
||||
return velocityValue < 0 ? -magnitude : magnitude;
|
||||
}
|
||||
|
||||
function resetState() {
|
||||
velocity = 0;
|
||||
lastWheelTimestamp = 0;
|
||||
kineticCandidate = false;
|
||||
pendingKineticBounceVelocity = 0;
|
||||
pendingImpactVelocity = 0;
|
||||
overshootReleaseVelocity = 0;
|
||||
finishTimer.stop();
|
||||
kineticAnim.stop();
|
||||
kineticBounceSequence.stop();
|
||||
bounceBackAnimation.stop();
|
||||
stopMouseGlide();
|
||||
}
|
||||
|
||||
function bounceBack() {
|
||||
if (!hasOvershoot()) {
|
||||
setOvershootInput(0);
|
||||
overshootReleaseVelocity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var mag = Math.min(1.0, Math.abs(overshootInput) / maxOvershootInput);
|
||||
var speed = Math.abs(overshootReleaseVelocity);
|
||||
var duration = speed >= minBounceVelocity ? Math.round(Math.max(280, Math.min(620, Math.abs(overshootInput) / speed * 1600))) : Math.round(380 + mag * 260);
|
||||
|
||||
kineticBounceSequence.stop();
|
||||
bounceBackAnimation.stop();
|
||||
bounceBackAnimation.duration = duration;
|
||||
bounceBackAnimation.from = overshootInput;
|
||||
bounceBackAnimation.to = 0;
|
||||
overshootReleaseVelocity = 0;
|
||||
bounceBackAnimation.start();
|
||||
}
|
||||
|
||||
function triggerKineticBounce(contentVelocity) {
|
||||
if (!flickable || contentVelocity === 0)
|
||||
return;
|
||||
var atTop = flickable.contentY <= minimumContentY() + edgeEpsilon;
|
||||
var atBottom = flickable.contentY >= maximumContentY() - edgeEpsilon;
|
||||
if ((contentVelocity < 0 && !atTop) || (contentVelocity > 0 && !atBottom))
|
||||
return;
|
||||
var speed = Math.abs(contentVelocity);
|
||||
var effectiveVelocity = contentVelocity;
|
||||
if (speed < minBounceVelocity)
|
||||
effectiveVelocity = (contentVelocity < 0 ? -1 : 1) * Math.max(90, speed * 0.6);
|
||||
|
||||
var target = Math.max(-maxKineticBounceInput, Math.min(maxKineticBounceInput, effectiveVelocity * kineticBounceGain));
|
||||
if (Math.abs(target) < 2.5)
|
||||
target = contentVelocity < 0 ? -2.5 : 2.5;
|
||||
|
||||
var mag = Math.min(1.0, Math.abs(target) / maxKineticBounceInput);
|
||||
var inDur = Math.round(150 + mag * 120);
|
||||
var outDur = Math.round(320 + mag * 300);
|
||||
|
||||
kineticBounceSequence.stop();
|
||||
bounceBackAnimation.stop();
|
||||
overshootReleaseVelocity = 0;
|
||||
overshootInput = 0;
|
||||
kineticBounceIn.from = 0;
|
||||
kineticBounceIn.to = target;
|
||||
kineticBounceIn.duration = inDur;
|
||||
kineticBounceOut.from = target;
|
||||
kineticBounceOut.to = 0;
|
||||
kineticBounceOut.duration = outDur;
|
||||
kineticBounceSequence.start();
|
||||
}
|
||||
|
||||
function stopAndSettle() {
|
||||
kineticAnim.stop();
|
||||
if (flickable && flickable.flicking)
|
||||
flickable.cancelFlick();
|
||||
|
||||
resetState();
|
||||
bounceBack();
|
||||
}
|
||||
|
||||
function runKineticGlide(contentVelocity) {
|
||||
// contentVelocity > 0 means the content should scroll downward
|
||||
// (contentY increases), matching deltaFor()'s sign convention.
|
||||
if (!flickable || contentVelocity === 0) {
|
||||
pendingImpactVelocity = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
var current = flickable.contentY;
|
||||
var unclampedTarget = current + contentVelocity * kineticTau;
|
||||
var target = boundedContentY(unclampedTarget);
|
||||
var distUnclamped = Math.abs(unclampedTarget - current);
|
||||
var distActual = Math.abs(target - current);
|
||||
if (distActual < 4) {
|
||||
pendingImpactVelocity = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the glide is clipped by an edge, compute the velocity at the
|
||||
// moment of impact using the OutQuart velocity profile. That is
|
||||
// the velocity the bounce should feed on — gentle impacts get
|
||||
// gentle bounces, hard impacts get big springy ones.
|
||||
var hitsEdge = distActual + 0.5 < distUnclamped;
|
||||
if (hitsEdge && distUnclamped > 0) {
|
||||
var f = distActual / distUnclamped; // fraction covered
|
||||
// OutQuart: y = 1 - (1-x)^4, so velocity fraction at distance
|
||||
// fraction f equals (1-f)^(3/4) (times the constant peak).
|
||||
var velFraction = Math.pow(Math.max(0, 1 - f), 0.75);
|
||||
pendingImpactVelocity = contentVelocity * velFraction;
|
||||
} else {
|
||||
pendingImpactVelocity = 0;
|
||||
}
|
||||
|
||||
kineticAnim.stop();
|
||||
kineticAnim.from = current;
|
||||
kineticAnim.to = target;
|
||||
// Duration from distance and velocity; clamped so short flicks
|
||||
// stay snappy and long flicks don't run forever.
|
||||
kineticAnim.duration = Math.round(Math.min(1400, Math.max(180, distActual / Math.abs(contentVelocity) * 1800)));
|
||||
kineticAnim.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
function atEdgeFor(contentVelocity) {
|
||||
if (!flickable)
|
||||
return false;
|
||||
if (contentVelocity > 0)
|
||||
return flickable.contentY >= maximumContentY() - edgeEpsilon;
|
||||
if (contentVelocity < 0)
|
||||
return flickable.contentY <= minimumContentY() + edgeEpsilon;
|
||||
return false;
|
||||
}
|
||||
|
||||
function finishScroll() {
|
||||
if (!flickable)
|
||||
return;
|
||||
finishTimer.stop();
|
||||
|
||||
if (hasOvershoot()) {
|
||||
velocity = 0;
|
||||
lastWheelTimestamp = 0;
|
||||
kineticCandidate = false;
|
||||
pendingKineticBounceVelocity = 0;
|
||||
pendingImpactVelocity = 0;
|
||||
kineticAnim.stop();
|
||||
bounceBack();
|
||||
return;
|
||||
}
|
||||
|
||||
var rawContentVelocity = Math.max(-maxVelocity, Math.min(maxVelocity, velocity * kineticGain));
|
||||
var contentVelocity = clampedContentVelocity(rawContentVelocity);
|
||||
var useKinetic = kineticCandidate && Math.abs(rawContentVelocity) >= minVelocity;
|
||||
|
||||
velocity = 0;
|
||||
lastWheelTimestamp = 0;
|
||||
kineticCandidate = false;
|
||||
|
||||
if (useKinetic) {
|
||||
if (atEdgeFor(rawContentVelocity)) {
|
||||
// Already at the edge — fling bounces immediately.
|
||||
pendingKineticBounceVelocity = 0;
|
||||
triggerKineticBounce(rawContentVelocity);
|
||||
} else if (Math.abs(contentVelocity) >= minVelocity && runKineticGlide(contentVelocity)) {
|
||||
// Glide started — bounce fires on kineticAnim.onFinished
|
||||
// if it lands at an edge.
|
||||
pendingKineticBounceVelocity = contentVelocity;
|
||||
} else {
|
||||
pendingKineticBounceVelocity = 0;
|
||||
flickable.returnToBounds();
|
||||
}
|
||||
} else {
|
||||
pendingKineticBounceVelocity = 0;
|
||||
flickable.returnToBounds();
|
||||
}
|
||||
}
|
||||
|
||||
function handleWheel(wheel) {
|
||||
wheelHandler.blocking = true;
|
||||
if (!enabled || !visible || !flickable || !flickable.visible || !flickable.interactive) {
|
||||
wheelHandler.blocking = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (wheel.modifiers !== Qt.NoModifier) {
|
||||
wheelHandler.blocking = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (maximumContentY() <= minimumContentY()) {
|
||||
wheelHandler.blocking = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var delta = deltaFor(wheel);
|
||||
if (delta === 0) {
|
||||
wheelHandler.blocking = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel any in-flight glide — user input always wins, position
|
||||
// stays where it is, then new delta is applied from there.
|
||||
if (kineticAnim.running) {
|
||||
pendingKineticBounceVelocity = 0;
|
||||
pendingImpactVelocity = 0;
|
||||
kineticAnim.stop();
|
||||
}
|
||||
kineticBounceSequence.stop();
|
||||
bounceBackAnimation.stop();
|
||||
if (flickable.flicking) {
|
||||
pendingKineticBounceVelocity = 0;
|
||||
pendingImpactVelocity = 0;
|
||||
flickable.cancelFlick();
|
||||
}
|
||||
|
||||
var now = Date.now();
|
||||
var isTouchpad = !isMouseWheel(wheel) && (wheel.pixelDelta.y !== 0 || (wheel.phase !== Qt.NoScrollPhase && wheel.angleDelta.y === 0));
|
||||
console.log("wheel", wheel.angleDelta.y, wheel.pixelDelta.y, wheel.phase);
|
||||
if (!isTouchpad) {
|
||||
velocity = 0;
|
||||
lastWheelTimestamp = 0;
|
||||
kineticCandidate = false;
|
||||
scrollStarted();
|
||||
handleMouseDelta(delta, now);
|
||||
return;
|
||||
}
|
||||
|
||||
// Touchpad path (unchanged below), but cancel any mouse glide first
|
||||
stopMouseGlide();
|
||||
|
||||
var gap = lastWheelTimestamp > 0 ? now - lastWheelTimestamp : 16;
|
||||
var dt = Math.max(8, Math.min(24, gap));
|
||||
var instantVelocity = (delta / dt) * 1000;
|
||||
|
||||
kineticCandidate = kineticCandidate || shouldUseKinetic(wheel, gap);
|
||||
scrollStarted();
|
||||
scrollBy(delta);
|
||||
|
||||
if (hasOvershoot()) {
|
||||
overshootReleaseVelocity = Math.max(-maxVelocity, Math.min(maxVelocity, instantVelocity));
|
||||
velocity = 0;
|
||||
kineticCandidate = false;
|
||||
lastWheelTimestamp = now;
|
||||
finishTimer.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
velocity = lastWheelTimestamp <= 0 ? instantVelocity : velocity * (1.0 - smoothing) + instantVelocity * smoothing;
|
||||
lastWheelTimestamp = now;
|
||||
finishTimer.restart();
|
||||
|
||||
if (wheel.phase === Qt.ScrollEnd)
|
||||
finishScroll();
|
||||
}
|
||||
|
||||
function stopMouseGlide() {
|
||||
mouseGlide.stop();
|
||||
mouseVelocity = 0;
|
||||
mouseDir = 0;
|
||||
mouseStreak = 0;
|
||||
mouseLastTime = 0;
|
||||
}
|
||||
|
||||
function handleMouseDelta(delta, now) {
|
||||
var dir = delta > 0 ? 1 : -1;
|
||||
|
||||
// Edge or active overshoot: existing rubber-band path
|
||||
if (hasOvershoot() || atEdgeFor(dir)) {
|
||||
stopMouseGlide();
|
||||
scrollBy(delta);
|
||||
overshootReleaseVelocity = 0;
|
||||
finishTimer.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
var continuous = dir === mouseDir && (now - mouseLastTime) <= mouseAccelWindow;
|
||||
if (!continuous)
|
||||
mouseVelocity = 0; // new gesture or reversal: start clean
|
||||
mouseStreak = continuous ? mouseStreak + 1 : 0;
|
||||
|
||||
// Compute the gap BEFORE overwriting mouseLastTime
|
||||
var gap = mouseLastTime > 0 ? Math.max(12, Math.min(mouseAccelWindow, now - mouseLastTime)) : 0;
|
||||
|
||||
mouseDir = dir;
|
||||
mouseLastTime = now;
|
||||
|
||||
var accel = Math.min(mouseMaxAccel, 1 + mouseStreak * mouseAccelStep);
|
||||
|
||||
// --- velocity update (this is the part that changed) ---
|
||||
|
||||
// Part of the notch lands immediately
|
||||
var instant = delta * mouseSnap;
|
||||
flickable.contentY = boundedContentY(flickable.contentY + instant);
|
||||
|
||||
// The rest becomes glide velocity, stacked onto the current velocity
|
||||
var glide = delta * (1 - mouseSnap) * accel;
|
||||
var v = mouseVelocity + glide / mouseGlideTime;
|
||||
|
||||
// While notches keep arriving, don't fall below the rate they imply
|
||||
if (continuous && gap > 0) {
|
||||
var rate = glide / (gap / 1000);
|
||||
if (Math.abs(rate) > Math.abs(v))
|
||||
v = rate;
|
||||
}
|
||||
mouseVelocity = Math.max(-maxVelocity, Math.min(maxVelocity, v));
|
||||
|
||||
// --- end velocity update ---
|
||||
|
||||
if (!mouseGlide.running)
|
||||
mouseGlide.start();
|
||||
}
|
||||
|
||||
function stepMouseGlide(dt) {
|
||||
if (!flickable || flickable.dragging) {
|
||||
stopMouseGlide();
|
||||
return;
|
||||
}
|
||||
|
||||
dt = Math.min(dt, 0.05);
|
||||
var decay = Math.exp(-dt / mouseGlideTime);
|
||||
// Exact integral of v0 * e^(-t/tau) over dt, so it's frame-rate independent
|
||||
var step = mouseVelocity * mouseGlideTime * (1 - decay);
|
||||
var next = flickable.contentY + step;
|
||||
var minY = minimumContentY();
|
||||
var maxY = maximumContentY();
|
||||
|
||||
if (next <= minY || next >= maxY) {
|
||||
var impact = mouseVelocity;
|
||||
flickable.contentY = next <= minY ? minY : maxY;
|
||||
mouseGlide.stop();
|
||||
mouseVelocity = 0;
|
||||
mouseDir = 0;
|
||||
mouseStreak = 0;
|
||||
triggerKineticBounce(impact); // bounce scales with the real impact speed
|
||||
return;
|
||||
}
|
||||
|
||||
flickable.contentY = next;
|
||||
mouseVelocity *= decay;
|
||||
|
||||
if (Math.abs(mouseVelocity) < 40) // was 8
|
||||
stopMouseGlide();
|
||||
}
|
||||
|
||||
FrameAnimation {
|
||||
id: mouseGlide
|
||||
onTriggered: root.stepMouseGlide(frameTime)
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: finishTimer
|
||||
interval: 45
|
||||
repeat: false
|
||||
onTriggered: root.finishScroll()
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: kineticAnim
|
||||
target: root.flickable
|
||||
property: "contentY"
|
||||
easing.type: Easing.OutQuart
|
||||
onFinished: {
|
||||
// Prefer the impact-time velocity (computed in runKineticGlide)
|
||||
// so the bounce matches the residual energy at the moment the
|
||||
// glide hit the edge — slow landings → soft bounce, fast
|
||||
// landings → big springy bounce.
|
||||
var bv = Math.abs(root.pendingImpactVelocity) > root.edgeEpsilon ? root.pendingImpactVelocity : root.pendingKineticBounceVelocity;
|
||||
root.pendingImpactVelocity = 0;
|
||||
root.pendingKineticBounceVelocity = 0;
|
||||
if (Math.abs(bv) > root.edgeEpsilon)
|
||||
root.triggerKineticBounce(bv);
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: bounceBackAnimation
|
||||
target: root
|
||||
property: "overshootInput"
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: kineticBounceSequence
|
||||
|
||||
NumberAnimation {
|
||||
id: kineticBounceIn
|
||||
target: root
|
||||
property: "overshootInput"
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: kineticBounceOut
|
||||
target: root
|
||||
property: "overshootInput"
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Translate {
|
||||
id: overshootTranslate
|
||||
y: root.visualOvershoot
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.flickable
|
||||
|
||||
function onInteractiveChanged() {
|
||||
if (!root.flickable.interactive)
|
||||
root.stopAndSettle();
|
||||
}
|
||||
|
||||
function onContentHeightChanged() {
|
||||
if (!root.flickable.moving) {
|
||||
root.flickable.contentY = root.boundedContentY(root.flickable.contentY);
|
||||
root.bounceBack();
|
||||
}
|
||||
}
|
||||
|
||||
function onHeightChanged() {
|
||||
if (!root.flickable.moving) {
|
||||
root.flickable.contentY = root.boundedContentY(root.flickable.contentY);
|
||||
root.bounceBack();
|
||||
}
|
||||
}
|
||||
|
||||
function onVisibleChanged() {
|
||||
if (!root.flickable.visible)
|
||||
root.stopAndSettle();
|
||||
}
|
||||
|
||||
function onMovementEnded() {
|
||||
if (Math.abs(root.pendingKineticBounceVelocity) > root.edgeEpsilon) {
|
||||
var bounceVelocity = root.pendingKineticBounceVelocity;
|
||||
root.pendingKineticBounceVelocity = 0;
|
||||
root.triggerKineticBounce(bounceVelocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!flickable)
|
||||
return;
|
||||
flickable.flickDeceleration = 2800;
|
||||
flickable.maximumFlickVelocity = Math.max(flickable.maximumFlickVelocity, maxVelocity);
|
||||
flickable.contentItem.transform = [overshootTranslate];
|
||||
}
|
||||
}
|
||||
+81
-61
@@ -1,6 +1,8 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtCanvas2D
|
||||
import ZShell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -36,54 +38,62 @@ Item {
|
||||
property real fillAlpha: 0.15
|
||||
property real lineWidth: 2
|
||||
property real markerDotRadius: 4
|
||||
property real labelFontSize: 10
|
||||
property real labelFontSize: 8
|
||||
|
||||
Canvas {
|
||||
Canvas2D {
|
||||
id: canvas
|
||||
|
||||
anchors.fill: parent
|
||||
renderStrategy: Canvas.Immediate
|
||||
|
||||
alphaBlending: true
|
||||
fillColor: "transparent"
|
||||
|
||||
onWidthChanged: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
|
||||
function snapV(v) {
|
||||
return ZUtils.snap(v, canvas);
|
||||
}
|
||||
|
||||
function strokeCurve(ctx, pts) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pts[0].x, pts[0].y);
|
||||
|
||||
if (root.curveSmoothing && pts.length > 2) {
|
||||
const k = 1 / 6;
|
||||
const n = pts.length;
|
||||
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
const p0 = pts[Math.max(i - 1, 0)];
|
||||
const dpr = ZUtils.dprFor(canvas);
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
const p1 = pts[i];
|
||||
const p2 = pts[i + 1];
|
||||
const p3 = pts[Math.min(i + 2, n - 1)];
|
||||
const [c1x, c1y, c2x, c2y] = segmentControls(pts, i);
|
||||
|
||||
let c1x = p1.x + (p2.x - p0.x) * k;
|
||||
let c1y = p1.y + (p2.y - p0.y) * k;
|
||||
let c2x = p2.x - (p3.x - p1.x) * k;
|
||||
let c2y = p2.y - (p3.y - p1.y) * k;
|
||||
|
||||
const minY = Math.min(p1.y, p2.y);
|
||||
const maxY = Math.max(p1.y, p2.y);
|
||||
c1y = Math.max(minY, Math.min(maxY, c1y));
|
||||
c2y = Math.max(minY, Math.min(maxY, c2y));
|
||||
|
||||
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, p2.x, p2.y);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
const p0 = pts[i];
|
||||
const p1 = pts[i + 1];
|
||||
const cx = (p0.x + p1.x) * 0.5;
|
||||
ctx.bezierCurveTo(cx, p0.y, cx, p1.y, p1.x, p1.y);
|
||||
const firstCol = Math.ceil(p1.x * dpr) + 1;
|
||||
const lastCol = Math.floor(p2.x * dpr) - 1;
|
||||
for (let col = firstCol; col <= lastCol; ++col) {
|
||||
const x = col / dpr;
|
||||
ctx.lineTo(x, bezierY(p1, c1x, c1y, c2x, c2y, p2, x));
|
||||
}
|
||||
|
||||
ctx.lineTo(p2.x, p2.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Control points for segment i (pts[i] -> pts[i+1]), matching strokeCurve
|
||||
function bezierY(p1, c1x, c1y, c2x, c2y, p2, targetX) {
|
||||
let tlo = 0;
|
||||
let thi = 1;
|
||||
for (let it = 0; it < 16; it++) {
|
||||
const tm = (tlo + thi) / 2;
|
||||
const u = 1 - tm;
|
||||
const x = u * u * u * p1.x + 3 * u * u * tm * c1x + 3 * u * tm * tm * c2x + tm * tm * tm * p2.x;
|
||||
if (x < targetX)
|
||||
tlo = tm;
|
||||
else
|
||||
thi = tm;
|
||||
}
|
||||
|
||||
const t = (tlo + thi) / 2;
|
||||
const u = 1 - t;
|
||||
return u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
|
||||
}
|
||||
|
||||
function segmentControls(pts, i) {
|
||||
const p0 = pts[Math.max(i - 1, 0)];
|
||||
const p1 = pts[i];
|
||||
@@ -109,7 +119,6 @@ Item {
|
||||
return [cx, p1.y, cx, p2.y];
|
||||
}
|
||||
|
||||
// Point on the curve at a given x (x is monotonic per segment)
|
||||
function pointAtX(pts, targetX) {
|
||||
let j = pts.length - 2;
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
@@ -144,7 +153,10 @@ Item {
|
||||
};
|
||||
}
|
||||
|
||||
// Part of the curve left/right of a cut x, including the cut point
|
||||
function cutPoint(pts, targetX) {
|
||||
return pointAtX(pts, targetX);
|
||||
}
|
||||
|
||||
function cutLeft(pts, targetX) {
|
||||
if (targetX <= pts[0].x)
|
||||
return [];
|
||||
@@ -161,7 +173,7 @@ Item {
|
||||
|
||||
const left = pts.slice(0, j + 1);
|
||||
if (targetX > pts[j].x)
|
||||
left.push(pointAtX(pts, targetX));
|
||||
left.push(cutPoint(pts, targetX));
|
||||
return left;
|
||||
}
|
||||
|
||||
@@ -187,17 +199,20 @@ Item {
|
||||
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
if (!ctx)
|
||||
return;
|
||||
ctx.reset();
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
const W = width;
|
||||
const H = height;
|
||||
if (W < 2 || H < 2)
|
||||
return;
|
||||
|
||||
const padLeft = root.labelFontSize * 3.4;
|
||||
const padRight = 2;
|
||||
const padTop = 2;
|
||||
const padBottom = root.labelFontSize + 8;
|
||||
const padLeft = root.labelFontSize * 5;
|
||||
const padRight = 0;
|
||||
const padTop = 0;
|
||||
const padBottom = root.labelFontSize + 14;
|
||||
const plot = {
|
||||
x: padLeft,
|
||||
y: padTop,
|
||||
@@ -207,8 +222,6 @@ Item {
|
||||
if (plot.w < 2 || plot.h < 2)
|
||||
return;
|
||||
|
||||
// Auto-fit: NaN yMin/yMax derive from the data, snapped to the
|
||||
// horizontal step grid, honoring minHorizontalLines
|
||||
let lo = root.yMin;
|
||||
let hi = root.yMax;
|
||||
const loAuto = Number.isNaN(lo);
|
||||
@@ -282,11 +295,17 @@ Item {
|
||||
|
||||
const xAt = i => plot.x + (i / root.xSteps) * plot.w;
|
||||
const yAt = v => plot.y + (1 - (v - scaleBottom) / range) * plot.h;
|
||||
const crisp = v => Math.floor(v) + 0.5;
|
||||
|
||||
// 1. Grid lines
|
||||
const dpr = ZUtils.dprFor(canvas);
|
||||
const devW = w => Math.max(1, Math.round(w * dpr));
|
||||
const snapLine = (v, w) => {
|
||||
const dw = devW(w);
|
||||
return (Math.round(v * dpr - dw / 2) + dw / 2) / dpr;
|
||||
};
|
||||
const gridW = devW(1) / dpr;
|
||||
|
||||
ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.lineWidth = gridW;
|
||||
|
||||
if (root.verticalLineStep > 0) {
|
||||
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
|
||||
@@ -295,7 +314,7 @@ Item {
|
||||
if (i === root.xSteps && !root.lastXLine)
|
||||
continue;
|
||||
|
||||
const x = crisp(xAt(i));
|
||||
const x = snapLine(xAt(i), 1);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, plot.y);
|
||||
ctx.lineTo(x, H);
|
||||
@@ -303,12 +322,11 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Lines extend down into yPaddingBottom, snapped to the grid
|
||||
const vStart = lo - (root.yPaddingBottom > 0 ? Math.floor(root.yPaddingBottom / root.horizontalLineStep) * root.horizontalLineStep : 0);
|
||||
|
||||
if (root.horizontalLineStep > 0) {
|
||||
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) {
|
||||
const y = crisp(yAt(v));
|
||||
const y = snapLine(yAt(v), 1);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, y);
|
||||
ctx.lineTo(plot.x + plot.w, y);
|
||||
@@ -316,9 +334,8 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Labels
|
||||
ctx.fillStyle = Qt.alpha(root.labelColor, root.labelAlpha);
|
||||
ctx.font = root.labelFontSize + "px sans-serif";
|
||||
ctx.font = root.labelFontSize + "pt 'sans-serif'";
|
||||
|
||||
ctx.textAlign = "left";
|
||||
ctx.textBaseline = "top";
|
||||
@@ -331,7 +348,7 @@ Item {
|
||||
|
||||
const text = root.xLabel(i);
|
||||
if (text)
|
||||
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 4);
|
||||
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +361,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Curve, split into segments at null gaps
|
||||
const segments = [];
|
||||
let current = null;
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
@@ -355,7 +371,7 @@ Item {
|
||||
continue;
|
||||
}
|
||||
const pt = {
|
||||
x: xAt(i),
|
||||
x: snapV(xAt(i)),
|
||||
y: yAt(series[i])
|
||||
};
|
||||
if (!current)
|
||||
@@ -372,8 +388,6 @@ Item {
|
||||
const markerPx = hasMarker ? xAt(root.markerX) : 0;
|
||||
let gapPx = 0;
|
||||
if (hasMarker) {
|
||||
// markerGap is measured from the dot edge, perpendicular to the
|
||||
// curve; project onto x via the local curve slope
|
||||
let slope = 0;
|
||||
const seg = segments.find(s => s[0].x <= markerPx && markerPx <= s[s.length - 1].x);
|
||||
if (seg && seg.length > 2 && markerPx - 1 >= seg[0].x && markerPx + 1 <= seg[seg.length - 1].x)
|
||||
@@ -385,7 +399,6 @@ Item {
|
||||
if (seg.length < 2)
|
||||
continue;
|
||||
|
||||
// Fill stays continuous under the marker
|
||||
ctx.fillStyle = Qt.alpha(root.lineColor, root.fillAlpha);
|
||||
strokeCurve(ctx, seg);
|
||||
ctx.lineTo(seg[seg.length - 1].x, plot.y + plot.h);
|
||||
@@ -393,7 +406,6 @@ Item {
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Stroke has a gap around the marker
|
||||
const pieces = hasMarker ? [cutLeft(seg, markerPx - gapPx), cutRight(seg, markerPx + gapPx)] : [seg];
|
||||
|
||||
for (const pts of pieces) {
|
||||
@@ -401,24 +413,32 @@ Item {
|
||||
continue;
|
||||
|
||||
ctx.strokeStyle = root.lineColor;
|
||||
ctx.lineWidth = root.lineWidth;
|
||||
ctx.lineWidth = ZUtils.snap(root.lineWidth, root);
|
||||
strokeCurve(ctx, pts);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Marker: dashed vertical + dot
|
||||
if (root.markerX >= 0 && root.markerX <= root.xSteps) {
|
||||
const x = markerPx;
|
||||
|
||||
ctx.setLineDash([4, 4]);
|
||||
ctx.strokeStyle = Qt.alpha(root.markerColor, 0.6);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.lineWidth = gridW;
|
||||
const lineX = snapLine(x, 1);
|
||||
let dashY = plot.y;
|
||||
let dashOn = true;
|
||||
|
||||
while (dashY < H) {
|
||||
const dashEnd = Math.min(dashY + 4, H);
|
||||
if (dashOn) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(crisp(x), plot.y);
|
||||
ctx.lineTo(crisp(x), H);
|
||||
ctx.moveTo(lineX, snapV(dashY));
|
||||
ctx.lineTo(lineX, snapV(dashEnd));
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
dashY = dashEnd;
|
||||
dashOn = !dashOn;
|
||||
}
|
||||
|
||||
const i0 = Math.floor(root.markerX);
|
||||
const v0 = series[i0];
|
||||
@@ -433,7 +453,7 @@ Item {
|
||||
if (y != null) {
|
||||
ctx.fillStyle = root.markerColor;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, root.markerDotRadius, 0, Math.PI * 2);
|
||||
ctx.circle(snapV(x), y, root.markerDotRadius);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,8 @@ MaterialShape {
|
||||
to: 360
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
ElapsedTimer {
|
||||
|
||||
@@ -47,10 +47,12 @@ Item {
|
||||
implicitWidth: vertical ? elideText.implicitHeight : elideText.implicitWidth
|
||||
|
||||
Behavior on leftFadeStrength {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on rightFadeStrength {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: resetMarquee()
|
||||
|
||||
+6
-2
@@ -4,6 +4,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Drawers
|
||||
import qs.Services
|
||||
|
||||
@@ -110,12 +111,14 @@ MouseArea {
|
||||
color: Colors.palette.m3surfaceContainerLow
|
||||
radius: parent.radius
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
id: column
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.extraSmall
|
||||
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
orientation: Qt.Vertical
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
@@ -151,11 +154,12 @@ MouseArea {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: menuOptionRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.medium
|
||||
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
@@ -53,7 +54,7 @@ Row {
|
||||
onClicked: root.active?.clicked()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: textRow
|
||||
|
||||
anchors.centerIn: parent
|
||||
@@ -102,7 +103,8 @@ Row {
|
||||
topLeftRadius: rad
|
||||
|
||||
Behavior on rad {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
@@ -126,10 +128,12 @@ Row {
|
||||
text: "expand_more"
|
||||
|
||||
Behavior on anchors.horizontalCenterOffset {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import ZShell
|
||||
import ZShell.Components
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
@@ -47,7 +48,7 @@ CustomRect {
|
||||
z: -1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
@@ -88,9 +89,11 @@ CustomRect {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
orientation: Qt.Vertical
|
||||
|
||||
CustomText {
|
||||
id: title
|
||||
|
||||
@@ -44,7 +44,8 @@ Item {
|
||||
onValuesChanged: root.flagChanged()
|
||||
}
|
||||
|
||||
ToastWrapper {}
|
||||
ToastWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
component ToastWrapper: MouseArea {
|
||||
@@ -85,10 +86,12 @@ Item {
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
|
||||
@@ -20,7 +20,8 @@ Shape {
|
||||
strokeWidth: 0
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathSvg {
|
||||
|
||||
@@ -141,6 +141,7 @@ Singleton {
|
||||
|
||||
BeatTracker {
|
||||
id: beatTracker
|
||||
|
||||
}
|
||||
|
||||
PwObjectTracker {
|
||||
|
||||
+2
-1
@@ -295,7 +295,8 @@ Singleton {
|
||||
Component {
|
||||
id: apComp
|
||||
|
||||
AccessPoint {}
|
||||
AccessPoint {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
|
||||
+5
-2
@@ -1081,13 +1081,15 @@ Singleton {
|
||||
Component {
|
||||
id: commandProc
|
||||
|
||||
CommandProcess {}
|
||||
CommandProcess {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: apComp
|
||||
|
||||
AccessPoint {}
|
||||
AccessPoint {
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -1308,6 +1310,7 @@ Singleton {
|
||||
}
|
||||
stdout: StdioCollector {
|
||||
id: stdoutCollector
|
||||
|
||||
}
|
||||
|
||||
onExited: code => {
|
||||
|
||||
@@ -164,7 +164,8 @@ Singleton {
|
||||
Component {
|
||||
id: notifComp
|
||||
|
||||
Notif {}
|
||||
Notif {
|
||||
}
|
||||
}
|
||||
|
||||
component Notif: QtObject {
|
||||
|
||||
@@ -4,12 +4,16 @@ import Quickshell
|
||||
import QtQuick
|
||||
import ZShell.Internal
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property alias content: contentLoader.item
|
||||
|
||||
required property ScreenState screenState
|
||||
property rect holeRect
|
||||
property real holeRadius: 0
|
||||
|
||||
Loader {
|
||||
id: contentLoader
|
||||
@@ -18,6 +22,9 @@ Item {
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: StrokeCanvas {
|
||||
anchors.fill: parent
|
||||
hole: root.holeRect
|
||||
holeRadius: root.holeRadius
|
||||
penColor: root.screenState.penColor
|
||||
penWidth: root.screenState.penWidth
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
-194
@@ -1,25 +1,33 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import ZShell.Config
|
||||
import ZShell.Internal
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Modules.Bar.Popouts
|
||||
|
||||
Item {
|
||||
CustomMouseArea {
|
||||
id: root
|
||||
|
||||
required property Drawing drawing
|
||||
required property DrawingInput input
|
||||
|
||||
required property Item bar
|
||||
required property real borderThickness
|
||||
property bool dashboardShortcutActive
|
||||
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 dashboardShortcutActive
|
||||
property point dragStart
|
||||
property bool osdShortcutActive
|
||||
property bool setInitialPoint: false
|
||||
property bool singleGestureTriggered: false
|
||||
property bool utilitiesShortcutActive
|
||||
required property ScreenState screenState
|
||||
|
||||
function inBottomPanel(panel: Item, x: real, y: real): bool {
|
||||
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
|
||||
@@ -56,124 +64,116 @@ Item {
|
||||
return x >= panelX && x <= panelX + panel.width;
|
||||
}
|
||||
|
||||
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
DragHandler {
|
||||
id: singleHandler
|
||||
|
||||
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
|
||||
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;
|
||||
}
|
||||
onContainsMouseChanged: {
|
||||
if (!containsMouse) {
|
||||
if (!osdShortcutActive) {
|
||||
screenState.osd = false;
|
||||
panels.osd.hovered = false;
|
||||
}
|
||||
|
||||
if (root.geometry.insetTop(root.borderThickness) > pressY && (Config.dashboard.showOnHover || !inDashboard)) {
|
||||
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 && !inLeftPanel(panels.drawing, x, y)) {
|
||||
input.z = 2;
|
||||
panels.drawing.collapse();
|
||||
}
|
||||
|
||||
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) {
|
||||
root.screenState.settings = true;
|
||||
root.singleGestureTriggered = true;
|
||||
screenState.settings = true;
|
||||
} else if (dragY < -20) {
|
||||
root.screenState.settings = false;
|
||||
root.singleGestureTriggered = true;
|
||||
screenState.settings = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (pressY > root.screen.height - root.geometry.insetBottom(root.borderThickness) && pressX < root.screen.width / 5 && dragY < -50)
|
||||
root.screenState.clipboard = true;
|
||||
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
|
||||
screenState.clipboard = true;
|
||||
|
||||
if (!Config.dock.hoverToReveal && pressY > root.screen.height - root.geometry.insetTop(root.borderThickness) && pressX > root.screen.width / 5 && !root.screenState.launcher)
|
||||
if (dragY < -10) {
|
||||
root.screenState.dock = true;
|
||||
root.singleGestureTriggered = 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 (pressX > root.screen.width - Config.bar.border && pressY < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
|
||||
root.screenState.sidebar = true;
|
||||
root.singleGestureTriggered = true;
|
||||
}
|
||||
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
|
||||
screenState.sidebar = true;
|
||||
|
||||
if (pressX >= root.screen.width - Config.bar.border && pressY > (root.screen.height / 2) && dragX < -20) {
|
||||
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 })`);
|
||||
root.singleGestureTriggered = true;
|
||||
}
|
||||
|
||||
if (pressX <= Config.bar.border && dragX > 20) {
|
||||
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
|
||||
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
|
||||
root.singleGestureTriggered = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PointHandler {
|
||||
id: drawingHandler
|
||||
|
||||
property bool setInitialPoint: false
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
enabled: root.screenState.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
|
||||
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
setInitialPoint = false;
|
||||
root.drawing.content.endStroke();
|
||||
} else {
|
||||
root.panels.drawing.collapse();
|
||||
}
|
||||
}
|
||||
onPointChanged: {
|
||||
if (!active)
|
||||
return;
|
||||
const x = point.position.x;
|
||||
const y = point.position.y;
|
||||
const origX = point.pressPosition.x;
|
||||
const origY = point.pressPosition.y;
|
||||
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
|
||||
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
|
||||
|
||||
if (point.pressedButtons & Qt.RightButton) {
|
||||
root.drawing.content.clear();
|
||||
return;
|
||||
if (!osdShortcutActive) {
|
||||
screenState.osd = showOsd;
|
||||
panels.osd.hovered = showOsd;
|
||||
} else if (showOsd) {
|
||||
osdShortcutActive = false;
|
||||
panels.osd.hovered = true;
|
||||
}
|
||||
if (x === 0 && y === 0 && origX === 0 && origY === 0)
|
||||
return;
|
||||
|
||||
if (!setInitialPoint) {
|
||||
setInitialPoint = true;
|
||||
root.drawing.content.beginStroke(origX, origY);
|
||||
return;
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -184,98 +184,20 @@ Item {
|
||||
running: false
|
||||
|
||||
onTriggered: {
|
||||
if (hoverHandler.hovered)
|
||||
if (root.containsMouse)
|
||||
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 {
|
||||
function onDashboardChanged() {
|
||||
function onDashboardChanged(): void {
|
||||
if (root.screenState.dashboard) {
|
||||
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
|
||||
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
|
||||
if (!inDashboardArea) {
|
||||
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.sidebar = false;
|
||||
root.screenState.resources = false;
|
||||
@@ -285,12 +207,15 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function onIsDrawingChanged() {
|
||||
if (!root.screenState.isDrawing)
|
||||
root.drawing.clear();
|
||||
function onIsDrawingChanged(): void {
|
||||
if (root.screenState.isDrawing) {
|
||||
root.drawing.content.showHover();
|
||||
} else {
|
||||
root.drawing.content.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function onLauncherChanged() {
|
||||
function onLauncherChanged(): void {
|
||||
if (!root.screenState.launcher) {
|
||||
root.dashboardShortcutActive = false;
|
||||
root.osdShortcutActive = false;
|
||||
@@ -305,16 +230,13 @@ Item {
|
||||
}
|
||||
|
||||
if (root.screenState.launcher) {
|
||||
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
|
||||
root.screenState.dashboard = false;
|
||||
}
|
||||
|
||||
root.screenState.dock = false;
|
||||
root.screenState.settings = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onOsdChanged() {
|
||||
function onOsdChanged(): void {
|
||||
if (root.screenState.osd) {
|
||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
||||
if (!inOsdArea) {
|
||||
@@ -325,7 +247,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function onResourcesChanged() {
|
||||
function onResourcesChanged(): void {
|
||||
if (root.screenState.resources) {
|
||||
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
|
||||
root.popouts.hasCurrent = false;
|
||||
@@ -335,7 +257,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function onSettingsChanged() {
|
||||
function onSettingsChanged(): void {
|
||||
if (root.screenState.settings) {
|
||||
root.screenState.resources = false;
|
||||
root.screenState.dashboard = false;
|
||||
@@ -345,8 +267,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function onSidebarChanged() {
|
||||
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;
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
+17
-8
@@ -1,5 +1,7 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import ZShell.Config
|
||||
import ZShell
|
||||
import qs.Components
|
||||
import qs.Modules.Bar.Popouts as Popouts
|
||||
import qs.Modules.Notifications as Notifications
|
||||
@@ -14,7 +16,6 @@ import qs.Modules.Settings as Settings
|
||||
import qs.Modules.Drawing as Drawing
|
||||
import qs.Modules.Dock as Dock
|
||||
import qs.Modules.Clipboard as Clipboard
|
||||
import ZShell.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -74,7 +75,10 @@ Item {
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: sidebar.width * (1 - sidebar.offsetScale)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
|
||||
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
clip: sidebar.visible
|
||||
implicitHeight: osd.implicitHeight
|
||||
implicitWidth: osd.implicitWidth * (1 - osd.offsetScale)
|
||||
@@ -82,8 +86,7 @@ Item {
|
||||
Osd.Wrapper {
|
||||
id: osd
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.fill: parent
|
||||
screen: root.screen
|
||||
screenState: root.screenState
|
||||
sidebarOrSessionVisible: sidebar.visible
|
||||
@@ -129,8 +132,9 @@ Item {
|
||||
Launcher.Wrapper {
|
||||
id: launcher
|
||||
|
||||
x: ZUtils.snap((parent.width - implicitWidth) / 2, root)
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
panels: root
|
||||
screen: root.screen
|
||||
screenState: root.screenState
|
||||
@@ -150,8 +154,10 @@ Item {
|
||||
|
||||
property real offsetScale: dashboard.shouldBeActive ? 0 : 1
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
|
||||
x: ZUtils.snap((parent.width - implicitWidth) / 2, dashboardWrapper)
|
||||
|
||||
clip: offsetScale > 0
|
||||
implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
|
||||
implicitWidth: dashboard.implicitWidth
|
||||
@@ -190,8 +196,11 @@ Item {
|
||||
Settings.Wrapper {
|
||||
id: settings
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
|
||||
x: ZUtils.snap((parent.width - implicitWidth) / 2, settings)
|
||||
y: ZUtils.snap((parent.height - implicitHeight) / 2 - (implicitHeight + 5 + (parent.height - implicitHeight) / 2) * offsetScale, settings)
|
||||
|
||||
// anchors.centerIn: parent
|
||||
// anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
|
||||
panels: root
|
||||
screen: root.screen
|
||||
screenState: root.screenState
|
||||
|
||||
+24
-2
@@ -56,7 +56,7 @@ CustomWindow {
|
||||
property color surfaceColor: Colors.tPalette.m3surface
|
||||
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar || screenState.isDrawing ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
|
||||
color: "transparent"
|
||||
mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
|
||||
@@ -347,12 +347,31 @@ CustomWindow {
|
||||
Drawing {
|
||||
id: drawing
|
||||
|
||||
readonly property int extraWidth: drawingBg.radius * 2
|
||||
|
||||
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
|
||||
z: 2
|
||||
}
|
||||
|
||||
Interactions2 {
|
||||
LazyLoader {
|
||||
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
|
||||
|
||||
anchors.fill: parent
|
||||
@@ -365,6 +384,8 @@ CustomWindow {
|
||||
popouts: panels.popouts
|
||||
screen: root.screen
|
||||
screenState: root.screenState
|
||||
input: lazyLoader.item
|
||||
z: 1
|
||||
|
||||
Panels {
|
||||
id: panels
|
||||
@@ -442,6 +463,7 @@ CustomWindow {
|
||||
parent: root.contentItem
|
||||
screen: root.screen
|
||||
screenState: root.screenState
|
||||
z: 4
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -3,11 +3,12 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Services
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
id: root
|
||||
|
||||
readonly property real centerScale: Math.min(1, screenHeight / 1440)
|
||||
@@ -18,9 +19,11 @@ ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: centerWidth
|
||||
|
||||
orientation: Qt.Vertical
|
||||
spacing: Tokens.spacing.largeIncreased * 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
@@ -116,7 +119,7 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: input
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -46,7 +46,8 @@ Item {
|
||||
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
@@ -83,7 +84,8 @@ Item {
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Anim on waveProgress {
|
||||
id: waveProgAnim
|
||||
|
||||
@@ -7,6 +7,7 @@ ClippingRectangle {
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ Text {
|
||||
textFormat: Text.PlainText
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Behavior on text {
|
||||
enabled: root.animate
|
||||
@@ -32,7 +33,8 @@ Text {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
|
||||
PropertyAction {}
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
TAnim {
|
||||
target: root
|
||||
|
||||
@@ -65,7 +65,8 @@ Image {
|
||||
value: root.source
|
||||
}
|
||||
|
||||
PropertyAction {}
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
ScriptAction {
|
||||
script: root.maybeStartInAnim()
|
||||
|
||||
@@ -64,7 +64,8 @@ MaterialShape {
|
||||
to: 360
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
ElapsedTimer {
|
||||
|
||||
+3
-2
@@ -1,11 +1,12 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: root
|
||||
|
||||
required property var greeter
|
||||
|
||||
@@ -200,5 +200,6 @@ Singleton {
|
||||
|
||||
ElapsedTimer {
|
||||
id: timer
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
+10
-4
@@ -3,17 +3,20 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
id: root
|
||||
|
||||
required property var greeter
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.large
|
||||
|
||||
spacing: Tokens.spacing.large
|
||||
orientation: Qt.Vertical
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
@@ -37,8 +40,9 @@ ColumnLayout {
|
||||
duration: Tokens.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
orientation: Qt.Vertical
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
@@ -84,7 +88,7 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: row
|
||||
|
||||
anchors.fill: parent
|
||||
@@ -97,9 +101,11 @@ ColumnLayout {
|
||||
text: modelData.kind === "x11" ? "tv" : "desktop_windows"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: Tokens.spacing.small / 2
|
||||
orientation: Qt.Vertical
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
|
||||
+10
-4
@@ -3,18 +3,21 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Services
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
id: root
|
||||
|
||||
required property var greeter
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.large
|
||||
|
||||
spacing: Tokens.spacing.large
|
||||
orientation: Qt.Vertical
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
@@ -38,8 +41,9 @@ ColumnLayout {
|
||||
duration: Tokens.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
orientation: Qt.Vertical
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
@@ -86,7 +90,7 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: row
|
||||
|
||||
anchors.fill: parent
|
||||
@@ -112,9 +116,11 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: Tokens.spacing.small / 2
|
||||
orientation: Qt.Vertical
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -16,6 +16,7 @@ ShellRoot {
|
||||
|
||||
GreeterState {
|
||||
id: greeter
|
||||
|
||||
}
|
||||
|
||||
GreeterSurface {
|
||||
|
||||
@@ -69,7 +69,8 @@ Singleton {
|
||||
|
||||
model: Quickshell.screens
|
||||
|
||||
Monitor {}
|
||||
Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
|
||||
@@ -33,7 +33,8 @@ Singleton {
|
||||
implicitHeight: 0
|
||||
implicitWidth: 0
|
||||
|
||||
mask: Region {}
|
||||
mask: Region {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ CustomRect {
|
||||
radius: Tokens.rounding.full
|
||||
width: row.implicitWidth + Tokens.padding.small * 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
+58
-8
@@ -4,11 +4,13 @@ import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Modules.Bar.Components
|
||||
import qs.Modules.Bar.Popouts
|
||||
import ZShell
|
||||
|
||||
GridLayout {
|
||||
SnapLayout {
|
||||
id: root
|
||||
|
||||
required property bool fullscreen
|
||||
@@ -22,7 +24,7 @@ GridLayout {
|
||||
|
||||
function axisCenterOf(item: Item): real {
|
||||
const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2);
|
||||
return horizontal ? Math.floor(c.x) : Math.floor(c.y);
|
||||
return horizontal ? ZUtils.snap(c.x, item) : ZUtils.snap(c.y, item);
|
||||
}
|
||||
|
||||
function checkPopout(pos: real): void {
|
||||
@@ -74,9 +76,47 @@ GridLayout {
|
||||
return ch?.entryId ?? "";
|
||||
}
|
||||
|
||||
columnSpacing: Tokens.spacing.small
|
||||
columns: horizontal ? -1 : 1
|
||||
rowSpacing: Tokens.spacing.small
|
||||
readonly property var sectionRects: {
|
||||
const count = repeater.count; // dependency: re-run when items are added/removed
|
||||
const names = ["start", "middle", "end"]; // constant ids by position
|
||||
const bounds = [null];
|
||||
let spacers = 0;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const it = repeater.itemAt(i) as EntryWrapper;
|
||||
if (!it)
|
||||
continue;
|
||||
|
||||
if (it.entryId === "spacer") {
|
||||
bounds.push(null);
|
||||
spacers++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const l = it.x, t = it.y, r = it.x + it.width, b = it.y + it.height;
|
||||
const cur = bounds[spacers];
|
||||
|
||||
bounds[spacers] = cur ? {
|
||||
l: Math.min(cur.l, l),
|
||||
t: Math.min(cur.t, t),
|
||||
r: Math.max(cur.r, r),
|
||||
b: Math.max(cur.b, b)
|
||||
} : {
|
||||
l,
|
||||
t,
|
||||
r,
|
||||
b
|
||||
};
|
||||
}
|
||||
|
||||
return bounds.map((c, n) => ({
|
||||
id: names[n] ?? `section${n}`,
|
||||
rect: c ? Qt.rect(c.l, c.t, c.r - c.l, c.b - c.t) : Qt.rect(0, 0, 0, 0)
|
||||
}));
|
||||
}
|
||||
|
||||
spacing: Tokens.spacing.small
|
||||
orientation: horizontal ? Qt.Horizontal : Qt.Vertical
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
@@ -228,16 +268,26 @@ GridLayout {
|
||||
}
|
||||
|
||||
component EntryWrapper: Item {
|
||||
readonly property int section: {
|
||||
let n = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (repeater.model.values[i].id === "spacer")
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
readonly property string entryId: modelData.id
|
||||
required property int index
|
||||
default property Item item
|
||||
required property var modelData
|
||||
|
||||
default property Item item
|
||||
|
||||
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
|
||||
Layout.bottomMargin: !root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
|
||||
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.rowSpacing + Tokens.spacing.extraSmall : 0
|
||||
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.spacing + Tokens.spacing.extraSmall : 0
|
||||
Layout.rightMargin: root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
|
||||
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.columnSpacing + Tokens.spacing.extraSmall : 0
|
||||
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.spacing + Tokens.spacing.extraSmall : 0
|
||||
children: item
|
||||
implicitHeight: item?.implicitHeight ?? 0
|
||||
implicitWidth: item?.implicitWidth ?? 0
|
||||
|
||||
@@ -9,6 +9,7 @@ import qs.Components
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property var sections: (content.item as Bar)?.sectionRects ?? []
|
||||
readonly property int clampedExtent: Math.max(Config.bar.border, extent)
|
||||
readonly property int contentThickness: Math.floor(Math.max(Config.bar.height, Tokens.sizes.bar.innerSize * (horizontal ? 1 : 1.5))) + padding * 2
|
||||
readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentThickness
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Modules.Bar.Popouts
|
||||
import qs.Modules.Bar.Components.Common
|
||||
import qs.Helpers
|
||||
@@ -43,7 +44,7 @@ WidgetBase {
|
||||
timeTokens: timeTokens
|
||||
};
|
||||
}
|
||||
required property GridLayout loader
|
||||
required property SnapLayout loader
|
||||
required property Wrapper popouts
|
||||
readonly property int roundedWidth: Math.round(timeText.implicitWidth)
|
||||
readonly property int evenWidth: roundedWidth % 2 === 0 ? roundedWidth : roundedWidth + 1
|
||||
@@ -69,7 +70,8 @@ WidgetBase {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: verticalColumn
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Bar.Components.Common
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Modules
|
||||
|
||||
@@ -11,7 +12,7 @@ Item {
|
||||
anchors.top: parent.top
|
||||
implicitWidth: layout.implicitWidth
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: layout
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import ZShell.Services
|
||||
import qs.Services
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Helpers
|
||||
import qs.Modules.Bar.Components.StatusIcons
|
||||
import qs.Modules.Bar.Components.Common
|
||||
@@ -59,7 +60,7 @@ WidgetBase {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
SnapLayout {
|
||||
id: grid
|
||||
|
||||
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.medium
|
||||
@@ -67,9 +68,8 @@ WidgetBase {
|
||||
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
|
||||
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
|
||||
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
|
||||
columns: root.horizontal ? -1 : 1
|
||||
columnSpacing: root.spacing
|
||||
rowSpacing: root.spacing
|
||||
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical
|
||||
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Daemons
|
||||
import qs.Components
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Daemons
|
||||
import qs.Components
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Quickshell.Services.UPower
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Modules.Bar.Components.Common
|
||||
import qs.Components
|
||||
|
||||
@@ -2,10 +2,11 @@ import QtQuick.Layouts
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Helpers
|
||||
import qs.Modules.Bar.Popouts
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
@@ -15,7 +16,7 @@ Item {
|
||||
readonly property real dpr: Hypr.monitorFor(loader?.screen).scale
|
||||
required property int ind
|
||||
required property SystemTrayItem item
|
||||
required property GridLayout loader
|
||||
required property SnapLayout loader
|
||||
required property Wrapper popouts
|
||||
|
||||
function resolveIcon(app: string, icon: string): string {
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.SystemTray
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Modules.Bar.Components.Common
|
||||
import qs.Modules.Bar.Components.Tray
|
||||
@@ -15,7 +16,7 @@ WidgetBase {
|
||||
|
||||
readonly property alias items: repeater
|
||||
readonly property alias layout: sysGrid
|
||||
required property GridLayout loader
|
||||
required property SnapLayout loader
|
||||
readonly property int padding: Tokens.padding.extraSmall
|
||||
required property Wrapper popouts
|
||||
readonly property real size: horizontal ? sysGrid.implicitWidth + sysGrid.anchors.leftMargin + sysGrid.anchors.rightMargin : sysGrid.implicitHeight + sysGrid.anchors.topMargin + sysGrid.anchors.bottomMargin
|
||||
@@ -28,7 +29,7 @@ WidgetBase {
|
||||
radius: Tokens.rounding.full
|
||||
topRightRadius: horizontal ? Tokens.rounding.small / 2 : Tokens.rounding.full
|
||||
|
||||
GridLayout {
|
||||
SnapLayout {
|
||||
id: sysGrid
|
||||
|
||||
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
|
||||
@@ -36,9 +37,8 @@ WidgetBase {
|
||||
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
|
||||
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
|
||||
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
|
||||
columns: root.horizontal ? -1 : 1
|
||||
rowSpacing: -2
|
||||
columnSpacing: Tokens.spacing.extraSmall
|
||||
orientation: root.horizontal ? Qt.Horizontal : Qt.Vertical
|
||||
spacing: root.horizontal ? Tokens.spacing.extraSmall : -2
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Bar.Components.Common
|
||||
|
||||
@@ -17,7 +17,7 @@ Item {
|
||||
readonly property int maxSize: {
|
||||
const otherModules = bar.children.filter(c => c.enabled && c.entryId && c.item !== this && c.entryId !== "spacer");
|
||||
const otherSize = otherModules.reduce((acc, curr) => acc + (horizontal ? (curr.item?.nonAnimWidth ?? curr.width ?? 0) : (curr.item.nonAnimHeight ?? curr.height ?? 0)), 0);
|
||||
return horizontal ? (bar.width - otherSize - bar.columnSpacing * (bar.children.length - 1) - bar.vPadding * 2) : (bar.height - otherSize - bar.rowSpacing * (bar.children.length - 1) - bar.vPadding * 4);
|
||||
return horizontal ? (bar.width - otherSize - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2) : (bar.height - otherSize - bar.spacing * (bar.children.length - 1) - bar.vPadding * 4);
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
@@ -5,6 +5,7 @@ import Quickshell.Services.Pipewire
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import QtQuick.Controls
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
@@ -46,7 +47,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
component VolumesTab: ColumnLayout {
|
||||
component VolumesTab: SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
spacing: 12
|
||||
|
||||
CustomRect {
|
||||
@@ -56,14 +58,15 @@ Item {
|
||||
color: Colors.tPalette.m3surfaceContainer
|
||||
radius: root.rounding - vol.anchors.margins
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
anchors.bottomMargin: Tokens.padding.small
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
anchors.rightMargin: Tokens.padding.small
|
||||
anchors.topMargin: Tokens.padding.small
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -81,7 +84,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
|
||||
CustomMouseArea {
|
||||
@@ -153,14 +156,15 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
anchors.bottomMargin: Tokens.padding.small
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
anchors.rightMargin: Tokens.padding.small
|
||||
anchors.topMargin: Tokens.padding.small
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -178,7 +182,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
|
||||
CustomMouseArea {
|
||||
@@ -276,14 +280,15 @@ Item {
|
||||
text: Audio.getStreamName(appBox.modelData)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
anchors.bottomMargin: Tokens.padding.small
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
anchors.rightMargin: Tokens.padding.small
|
||||
anchors.topMargin: Tokens.padding.small
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -302,7 +307,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
|
||||
CustomMouseArea {
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import ZShell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -36,7 +37,7 @@ Item {
|
||||
const diff = parent.width - Math.floor(off + content.nonAnimWidth);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
return Math.floor(Math.max(off, 0));
|
||||
return ZUtils.snap(Math.max(off, 0), root);
|
||||
}
|
||||
y: {
|
||||
if (horizontal)
|
||||
@@ -46,7 +47,7 @@ Item {
|
||||
const diff = parent.height - Math.floor(off + content.nonAnimHeight);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
return Math.max(off, 0);
|
||||
return ZUtils.snap(Math.max(off, 0), root);
|
||||
}
|
||||
|
||||
Behavior on offsetScale {
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import Quickshell.Networking
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import qs.Modules
|
||||
@@ -14,7 +15,8 @@ Item {
|
||||
|
||||
required property var wrapper
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: layout
|
||||
|
||||
spacing: 8
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import qs.Components
|
||||
@@ -26,15 +27,20 @@ StackView {
|
||||
initialItem: SubMenu {
|
||||
handle: root.trayItem
|
||||
}
|
||||
popEnter: NoAnim {}
|
||||
popExit: NoAnim {}
|
||||
pushEnter: NoAnim {}
|
||||
pushExit: NoAnim {}
|
||||
popEnter: NoAnim {
|
||||
}
|
||||
popExit: NoAnim {
|
||||
}
|
||||
pushEnter: NoAnim {
|
||||
}
|
||||
pushExit: NoAnim {
|
||||
}
|
||||
|
||||
Component {
|
||||
id: subMenuComp
|
||||
|
||||
SubMenu {}
|
||||
SubMenu {
|
||||
}
|
||||
}
|
||||
|
||||
component NoAnim: Transition {
|
||||
@@ -42,7 +48,8 @@ StackView {
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
component SubMenu: ColumnLayout {
|
||||
component SubMenu: SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: menu
|
||||
|
||||
required property QsMenuHandle handle
|
||||
@@ -54,10 +61,12 @@ StackView {
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: shown = true
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell.Services.UPower
|
||||
import ZShell.Config
|
||||
import qs.Modules.Bar.Components.Common
|
||||
@@ -35,7 +36,7 @@ Column {
|
||||
Repeater {
|
||||
model: Battery.allPeripherals
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: layout
|
||||
|
||||
required property var modelData
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
@@ -86,7 +87,7 @@ CustomRect {
|
||||
implicitWidth: 600
|
||||
radius: root.radius - Tokens.padding.extraSmall
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
anchors.rightMargin: Tokens.padding.small
|
||||
@@ -96,7 +97,8 @@ CustomRect {
|
||||
text: "package_2"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
@@ -114,7 +116,7 @@ CustomRect {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 300
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import Quickshell
|
||||
import QtQuick
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import ZShell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -26,8 +27,8 @@ Item {
|
||||
detachedMode = "";
|
||||
}
|
||||
|
||||
implicitHeight: nonAnimHeight
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitHeight: ZUtils.snap(nonAnimHeight, root)
|
||||
implicitWidth: ZUtils.snap(nonAnimWidth, root)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: root.offsetScale < 1
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import ZShell.Config
|
||||
@@ -111,7 +112,7 @@ Item {
|
||||
Repeater {
|
||||
model: lineRow.modelData.text.split("\t").slice(1)
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
height: lineText.height
|
||||
width: lineText.height + Tokens.spacing.extraSmall
|
||||
|
||||
@@ -225,7 +226,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import ZShell
|
||||
import qs.Components
|
||||
@@ -39,8 +40,8 @@ Item {
|
||||
];
|
||||
return allTabs.filter(tab => tab.enabled);
|
||||
}
|
||||
readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin
|
||||
readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
|
||||
readonly property real nonAnimHeight: ZUtils.snap(tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin, root)
|
||||
readonly property real nonAnimWidth: ZUtils.snap(view.implicitWidth + viewWrapper.anchors.margins * 2, root)
|
||||
required property ScreenState screenState
|
||||
|
||||
implicitHeight: nonAnimHeight
|
||||
@@ -93,8 +94,8 @@ Item {
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
implicitHeight: currentItem?.implicitHeight ?? 0
|
||||
implicitWidth: currentItem?.implicitWidth ?? 0
|
||||
implicitHeight: currentItem?.height ?? currentItem?.implicitHeight ?? 0
|
||||
implicitWidth: currentItem?.width ?? currentItem?.implicitWidth ?? 0
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
+31
-53
@@ -1,60 +1,45 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
import qs.Modules
|
||||
import ZShell.Config
|
||||
import qs.Modules.Dashboard.Dash
|
||||
import qs.Services
|
||||
|
||||
GridLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: root
|
||||
|
||||
readonly property bool dashboardVisible: screenState.dashboard
|
||||
required property int radius
|
||||
required property ScreenState screenState
|
||||
|
||||
columnSpacing: Tokens.spacing.small
|
||||
rowSpacing: Tokens.spacing.small
|
||||
spacing: ZUtils.snap(Tokens.spacing.small, root)
|
||||
|
||||
ParallelAnimation {
|
||||
id: openAnim
|
||||
height: ZUtils.snap(implicitHeight, root)
|
||||
width: ZUtils.snap(implicitWidth, root)
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: parent.spacing
|
||||
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
Rect {
|
||||
id: weatherRect
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: ZUtils.snap(Tokens.sizes.dashboard.weatherWidth, root)
|
||||
radius: root.radius
|
||||
|
||||
ParallelAnimation {
|
||||
id: closeAnim
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: root
|
||||
to: 0.9
|
||||
}
|
||||
Weather {}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.column: 2
|
||||
Layout.columnSpan: 3
|
||||
Layout.preferredHeight: user.implicitHeight
|
||||
Layout.preferredWidth: user.implicitWidth
|
||||
id: userRect
|
||||
Layout.preferredHeight: ZUtils.snap(user.implicitHeight, root)
|
||||
Layout.preferredWidth: ZUtils.snap(user.implicitWidth, root)
|
||||
radius: root.radius
|
||||
|
||||
User {
|
||||
@@ -63,23 +48,17 @@ GridLayout {
|
||||
screenState: root.screenState
|
||||
}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: Tokens.sizes.dashboard.weatherWidth
|
||||
Layout.row: 0
|
||||
radius: root.radius
|
||||
|
||||
Weather {}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 3
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: calendar.implicitHeight
|
||||
Layout.row: 1
|
||||
spacing: parent.spacing
|
||||
|
||||
Rect {
|
||||
id: calendarRect
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: ZUtils.snap(calendar.implicitHeight, root)
|
||||
radius: root.radius
|
||||
|
||||
Calendar {
|
||||
@@ -90,17 +69,16 @@ GridLayout {
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.column: 3
|
||||
Layout.columnSpan: 2
|
||||
id: resourcesRect
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: resources.implicitWidth
|
||||
Layout.row: 1
|
||||
implicitWidth: ZUtils.snap(resources.implicitWidth, root)
|
||||
radius: root.radius
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Rect: CustomRect {
|
||||
color: Colors.tPalette.m3surfaceContainer
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
@@ -117,14 +118,15 @@ CustomMouseArea {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: inner
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.large
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: monthNavigationRow
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -307,10 +309,12 @@ CustomMouseArea {
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onTodayItemChanged: {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell
|
||||
import ZShell.Services
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
@@ -8,9 +10,8 @@ import qs.Services
|
||||
Item {
|
||||
id: root
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: layout.implicitWidth + layout.anchors.margins * 4
|
||||
anchors.fill: parent
|
||||
implicitWidth: ZUtils.snap(layout.implicitWidth + layout.anchors.margins * 2, root)
|
||||
|
||||
ServiceRef {
|
||||
service: Storage
|
||||
@@ -28,13 +29,16 @@ Item {
|
||||
service: Gpu
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: layout
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.margins: Tokens.padding.large
|
||||
anchors.top: parent.top
|
||||
|
||||
x: ZUtils.snap((root.width - implicitWidth) / 2, root)
|
||||
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
Resource {
|
||||
@@ -73,8 +77,7 @@ Item {
|
||||
|
||||
required property string icon
|
||||
|
||||
Layout.fillHeight: true
|
||||
implicitSize: height
|
||||
implicitSize: ZUtils.snap((root.height - layout.anchors.margins * 2 - layout.spacing * 4) / 5, root)
|
||||
|
||||
Behavior on clampedVal {
|
||||
Anim {}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import M3Shapes
|
||||
import ZShell
|
||||
import ZShell.Config
|
||||
import qs.Modules.Dashboard.Media
|
||||
import qs.Components
|
||||
@@ -13,8 +15,8 @@ Item {
|
||||
property ScreenState screenState
|
||||
required property int radius
|
||||
|
||||
implicitWidth: 1000
|
||||
implicitHeight: 320
|
||||
implicitWidth: ZUtils.snap(1000, root)
|
||||
implicitHeight: ZUtils.snap(320, root)
|
||||
|
||||
ClipRect {
|
||||
anchors.fill: parent
|
||||
@@ -25,7 +27,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.large
|
||||
spacing: Tokens.spacing.extraLarge
|
||||
@@ -94,7 +96,8 @@ Item {
|
||||
active: opacity > 0
|
||||
opacity: 0
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
MaterialShape {
|
||||
@@ -140,11 +143,12 @@ Item {
|
||||
asynchronous: true
|
||||
active: opacity > 0
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
spacing: Tokens.spacing.extraLarge
|
||||
|
||||
Details {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
LyricsAndSelector {
|
||||
|
||||
@@ -33,7 +33,8 @@ Item {
|
||||
Repeater {
|
||||
id: shapes
|
||||
|
||||
DriftingShape {}
|
||||
DriftingShape {
|
||||
}
|
||||
}
|
||||
|
||||
FrameAnimation {
|
||||
@@ -79,7 +80,8 @@ Item {
|
||||
shape: root.shapePool[Math.floor(Math.random() * root.shapePool.length)]
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
|
||||
@@ -18,7 +18,8 @@ Item {
|
||||
layer.enabled: true
|
||||
|
||||
Behavior on fallbackColor {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
layer.effect: MultiEffect {
|
||||
blurMax: 1
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.Mpris
|
||||
import ZShell
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
import qs.Helpers
|
||||
|
||||
ColumnLayout {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool hasUnknownLength: (Players.active?.length ?? 0) > 2147483647
|
||||
@@ -25,7 +26,17 @@ ColumnLayout {
|
||||
return `${mins}:${secs}`;
|
||||
}
|
||||
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
implicitHeight: ZUtils.snap(layout.implicitHeight, root)
|
||||
implicitWidth: layout.implicitWidth
|
||||
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: layout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
y: ZUtils.snap((parent.height - implicitHeight) / 2, root)
|
||||
|
||||
spacing: ZUtils.snap(Tokens.spacing.extraSmall, root)
|
||||
|
||||
Timer {
|
||||
running: Players.active?.isPlaying ?? false
|
||||
@@ -36,7 +47,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: title
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root)
|
||||
text: Players.active?.trackTitle ?? ""
|
||||
font: Tokens.font.title.large
|
||||
elide: Text.ElideRight
|
||||
@@ -44,7 +57,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: artist
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: ZUtils.snapCeil(implicitHeight, root)
|
||||
text: Players.active?.trackArtist || qsTr("Unknown artist")
|
||||
color: Colors.palette.m3onSurfaceVariant
|
||||
font: Tokens.font.title.medium
|
||||
@@ -53,7 +68,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -61,32 +78,29 @@ ColumnLayout {
|
||||
animate: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: sliderLayout
|
||||
Layout.topMargin: Tokens.spacing.extraLargeIncreased
|
||||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
TextMetrics {
|
||||
id: timeMetrics
|
||||
|
||||
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
|
||||
}
|
||||
spacing: ZUtils.snap(Tokens.spacing.small, root)
|
||||
|
||||
CustomText {
|
||||
id: positionLabel
|
||||
|
||||
Layout.preferredWidth: timeMetrics.width
|
||||
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
|
||||
@@ -109,11 +123,13 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: timeMetrics.width
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,4 +207,12 @@ ColumnLayout {
|
||||
implicitWidth: Math.round(implicitHeight * 0.9)
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: timeMetrics
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import ZShell.Services
|
||||
import qs.Components
|
||||
@@ -157,7 +158,8 @@ Item {
|
||||
active: opacity > 0
|
||||
opacity: 0
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
spacing: Tokens.spacing.large
|
||||
|
||||
CustomRect {
|
||||
@@ -198,7 +200,8 @@ Item {
|
||||
active: opacity > 0
|
||||
opacity: 0
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell
|
||||
import Quickshell.Services.Mpris
|
||||
import ZShell.Config
|
||||
@@ -8,14 +9,15 @@ import qs.Helpers
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.medium
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.bottomMargin: -Tokens.spacing.medium
|
||||
spacing: Tokens.spacing.medium
|
||||
z: 1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import ZShell
|
||||
import ZShell.Blobs
|
||||
import ZShell.Config
|
||||
@@ -16,7 +17,7 @@ Item {
|
||||
implicitWidth: btn.implicitWidth * 0.9
|
||||
implicitHeight: btn.implicitHeight * 0.9
|
||||
|
||||
BlobGroup {
|
||||
BlobGroup2 {
|
||||
id: blobGroup
|
||||
|
||||
color: Colors.palette.m3surfaceContainerHighest
|
||||
@@ -28,7 +29,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
BlobRect {
|
||||
BlobRect2 {
|
||||
id: btnRect
|
||||
|
||||
anchors.fill: parent
|
||||
@@ -41,7 +42,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
BlobRect {
|
||||
BlobRect2 {
|
||||
id: rect
|
||||
|
||||
anchors.right: parent.right
|
||||
@@ -141,8 +142,9 @@ Item {
|
||||
}
|
||||
]
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
id: layout
|
||||
orientation: Qt.Vertical
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell
|
||||
import ZShell.Config
|
||||
import ZShell.Internal
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import ZShell.Config
|
||||
@@ -38,7 +39,7 @@ CustomListView {
|
||||
id: rowHover
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -23,7 +23,7 @@ Item {
|
||||
service: Memory
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
spacing: Tokens.spacing.medium
|
||||
@@ -60,13 +60,14 @@ Item {
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: mainLayout.implicitHeight + mainLayout.anchors.margins * 2
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: mainLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.large
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
@@ -131,7 +132,8 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: infoColumn
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
+40
-15
@@ -4,6 +4,8 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Templates
|
||||
import Quickshell
|
||||
import ZShell
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
@@ -16,9 +18,17 @@ Item {
|
||||
required property var tabs
|
||||
|
||||
readonly property alias count: bar.count
|
||||
readonly property int tabIndicatorSpacing: Tokens.spacing.small
|
||||
readonly property int tabIndicatorSpacing: ZUtils.snap(Tokens.spacing.small, root)
|
||||
|
||||
implicitHeight: bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
|
||||
function snapLayoutHeight(v) {
|
||||
const dpr = ZUtils.dprFor(root);
|
||||
let h = Math.ceil(v);
|
||||
while (Math.abs(h * dpr - Math.round(h * dpr)) > 1e-6)
|
||||
++h;
|
||||
return h;
|
||||
}
|
||||
|
||||
implicitHeight: ZUtils.snap(bar.implicitHeight + bar.anchors.topMargin + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight, root)
|
||||
|
||||
TabBar {
|
||||
id: bar
|
||||
@@ -31,9 +41,11 @@ Item {
|
||||
currentIndex: root.screenState.dashboardTab
|
||||
onCurrentIndexChanged: root.screenState.dashboardTab = currentIndex
|
||||
|
||||
implicitHeight: contentHeight
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
implicitHeight: implicitContentHeight
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
contentItem: SnapLayout {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
@@ -116,10 +128,13 @@ Item {
|
||||
readonly property bool current: TabBar.tabBar.currentItem === this
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1 // Uniform width across all tabs
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 1
|
||||
implicitWidth: implicitContentWidth
|
||||
implicitHeight: implicitContentHeight
|
||||
background: null
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
contentItem: CustomMouseArea {
|
||||
id: mouse
|
||||
@@ -131,8 +146,8 @@ Item {
|
||||
root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0);
|
||||
}
|
||||
|
||||
implicitWidth: Math.max(icon.width, label.width)
|
||||
implicitHeight: icon.height + label.height
|
||||
implicitWidth: Math.max(icon.implicitWidth, label.implicitWidth)
|
||||
implicitHeight: snapLayout.implicitHeight
|
||||
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@@ -146,24 +161,33 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitHeight: parent.height + root.tabIndicatorSpacing * 2
|
||||
anchors.alignWhenCentered: false
|
||||
implicitHeight: ZUtils.snap(parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2, stateLayer)
|
||||
|
||||
radius: Tokens.rounding.medium
|
||||
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurface
|
||||
onClicked: root.screenState.dashboardTab = tab.TabBar.index
|
||||
onClicked: {
|
||||
root.screenState.dashboardTab = tab.TabBar.index;
|
||||
}
|
||||
}
|
||||
|
||||
SnapLayout {
|
||||
id: snapLayout
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
orientation: Qt.Vertical
|
||||
|
||||
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
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
@@ -174,11 +198,12 @@ Item {
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
text: tab.text
|
||||
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
import qs.Helpers
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: root
|
||||
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
@@ -20,7 +22,7 @@ RowLayout {
|
||||
|
||||
readonly property real markerX: {
|
||||
const d = root.now;
|
||||
return d.getHours() + d.getMinutes() / 60;
|
||||
return ZUtils.snap(d.getHours() + d.getMinutes() / 60, root);
|
||||
}
|
||||
|
||||
function formatHour(h) {
|
||||
@@ -39,11 +41,13 @@ RowLayout {
|
||||
onTriggered: root.now = new Date()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: root.width / 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
MaterialIcon {
|
||||
@@ -90,11 +94,13 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: root.width / 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
MaterialIcon {
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import qs.Modules.Dashboard.Weather
|
||||
import qs.Services
|
||||
@@ -18,13 +19,14 @@ Item {
|
||||
implicitHeight: layout.implicitHeight
|
||||
Component.onCompleted: Weather.reload()
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.leftMargin: Tokens.padding.large
|
||||
Layout.rightMargin: Tokens.padding.large
|
||||
Layout.fillWidth: true
|
||||
@@ -82,7 +84,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
@@ -121,7 +123,9 @@ Item {
|
||||
color: Colors.palette.m3onSurface
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: forecastLayout
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
@@ -137,12 +141,14 @@ Item {
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
|
||||
|
||||
radius: root.radius
|
||||
color: Colors.tPalette.m3surfaceContainer
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: forecastItemColumn
|
||||
|
||||
anchors.centerIn: parent
|
||||
@@ -196,7 +202,8 @@ Item {
|
||||
property color colour
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 60
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: 60
|
||||
radius: Tokens.rounding.medium
|
||||
color: Colors.tPalette.m3surfaceContainer
|
||||
|
||||
@@ -230,7 +237,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
component WeatherStat: RowLayout {
|
||||
component WeatherStat: SnapLayout {
|
||||
id: weatherStat
|
||||
|
||||
property string icon
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
@@ -43,7 +44,8 @@ Item {
|
||||
}
|
||||
opacity: root.visible ? 1 : 0
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
orientation: Qt.Vertical
|
||||
id: menuLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
@@ -53,7 +55,7 @@ Item {
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
implicitHeight: openTerminalRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: openTerminalRow
|
||||
spacing: 8
|
||||
anchors.fill: parent
|
||||
@@ -92,7 +94,7 @@ Item {
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
implicitHeight: settingsRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: settingsRow
|
||||
spacing: 8
|
||||
anchors.fill: parent
|
||||
@@ -132,7 +134,7 @@ Item {
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
implicitHeight: logoutRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: logoutRow
|
||||
spacing: 8
|
||||
anchors.fill: parent
|
||||
@@ -171,7 +173,7 @@ Item {
|
||||
// radius: popupBackground.radius - popupBackground.padding
|
||||
// implicitHeight: desktopIconsRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
//
|
||||
// RowLayout {
|
||||
// SnapLayout {
|
||||
// id: desktopIconsRow
|
||||
// spacing: 8
|
||||
// anchors.fill: parent
|
||||
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Components
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
@@ -57,22 +58,25 @@ Item {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
SnapLayout {
|
||||
id: menuLayout
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
spacing: 0
|
||||
orientation: Qt.Vertical
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredWidth: 160
|
||||
implicitHeight: openRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: openRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
|
||||
spacing: 8
|
||||
|
||||
MaterialIcon {
|
||||
@@ -111,11 +115,12 @@ Item {
|
||||
implicitHeight: openWithRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: openWithRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
|
||||
spacing: 8
|
||||
|
||||
MaterialIcon {
|
||||
@@ -156,11 +161,12 @@ Item {
|
||||
implicitHeight: copyPathRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: copyPathRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
|
||||
spacing: 8
|
||||
|
||||
MaterialIcon {
|
||||
@@ -190,11 +196,12 @@ Item {
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
visible: contextMenu.targetPaths.length === 1
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: renameRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
|
||||
spacing: 8
|
||||
|
||||
MaterialIcon {
|
||||
@@ -231,11 +238,12 @@ Item {
|
||||
implicitHeight: deleteRow.implicitHeight + Tokens.padding.extraSmall * 2
|
||||
radius: popupBackground.radius - popupBackground.padding
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
id: deleteRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.small
|
||||
|
||||
spacing: 8
|
||||
|
||||
MaterialIcon {
|
||||
|
||||
@@ -54,12 +54,14 @@ Item {
|
||||
Behavior on x {
|
||||
enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath)
|
||||
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath)
|
||||
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -83,7 +85,8 @@ Item {
|
||||
y: (root.iconsRoot.selectedIcons.includes(root.filePath) && root.iconsRoot.dragLeader !== "" && root.iconsRoot.dragLeader !== root.filePath) ? root.iconsRoot.groupDragY : 0
|
||||
}
|
||||
transitions: Transition {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onXChanged: {
|
||||
@@ -205,7 +208,8 @@ Item {
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +268,8 @@ Item {
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import ZShell.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
@@ -29,7 +30,10 @@ CustomRect {
|
||||
active: !isSeparator
|
||||
anchors.centerIn: parent
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
sourceComponent: SnapLayout {
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
orientation: Qt.Vertical
|
||||
|
||||
IconImage {
|
||||
id: icon
|
||||
|
||||
@@ -38,8 +42,9 @@ CustomRect {
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel?.appId), "image-missing")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
SnapLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
spacing: 3
|
||||
|
||||
Repeater {
|
||||
@@ -82,7 +87,8 @@ CustomRect {
|
||||
Loader {
|
||||
active: isSeparator
|
||||
|
||||
sourceComponent: DockSeparator {}
|
||||
sourceComponent: DockSeparator {
|
||||
}
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import ZShell
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
@@ -52,7 +53,7 @@ CustomListView {
|
||||
|
||||
highlightFollowsCurrentItem: false
|
||||
highlightRangeMode: ListView.ApplyRange
|
||||
implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
|
||||
implicitHeight: (Tokens.sizes.launcher.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
|
||||
orientation: Qt.Vertical
|
||||
preferredHighlightBegin: 0
|
||||
preferredHighlightEnd: height
|
||||
@@ -97,6 +98,7 @@ CustomListView {
|
||||
}
|
||||
}
|
||||
highlight: CustomRect {
|
||||
id: hl
|
||||
color: Colors.palette.m3onSurface
|
||||
implicitHeight: root.currentItem?.implicitHeight ?? 0
|
||||
implicitWidth: root.width
|
||||
|
||||
Regular → Executable
+1
-1
@@ -22,7 +22,7 @@ CarouselView {
|
||||
delegateProperties: ({
|
||||
screenState: root.screenState
|
||||
})
|
||||
focalWidth: Config.launcher.sizes.wallpaperWidth
|
||||
focalWidth: Tokens.sizes.launcher.wallpaperWidth
|
||||
glideDuration: 250
|
||||
glideEasingType: Easing.OutCubic
|
||||
implicitHeight: tileHeight + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.small
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user