8 Commits
Author SHA1 Message Date
zach 656bd0a54d Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m37s
C++ / build (pull_request) Successful in 2m22s
2026-06-30 22:42:51 +02:00
zach f01001dcfa merge main into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m42s
C++ / build (pull_request) Successful in 2m28s
2026-06-30 18:15:43 +02:00
zach bc6b0d50ab Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 34s
Python / lint-format (pull_request) Successful in 35s
Python / test (pull_request) Successful in 1m11s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m58s
C++ / build (pull_request) Successful in 4m31s
2026-06-29 22:10:45 +02:00
zach f27c9afc25 more methods for wifi and scanning + layout
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-26 15:15:03 +02:00
Inorishio 5962fc8354 Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 27s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
2026-06-24 18:58:36 +02:00
Inorishio 1adcb9841c Added network properties, nicNames, netDevices, networkName < reminder I should use layouts.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m52s
2026-06-22 23:34:58 +02:00
Inorishio 0e0f8555ed fixing singleton in helpers for network + getting output from systray 2026-06-15 20:48:01 +02:00
Inorishio 1d63e67554 removed network module and moved it to tray, have a popout window without content! everything still in wi.i.p. 2026-06-13 18:22:03 +02:00
475 changed files with 16343 additions and 16394 deletions
-108
View File
@@ -1,108 +0,0 @@
name: Bug Report
about: Something broke
title: "bug: "
labels:
- bug
body:
- type: textarea
id: description
attributes:
label: What happened
placeholder: Describe the bug
validations:
required: true
- type: textarea
id: expected
attributes:
label: What should happen
placeholder: Expected behavior
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1.
2.
3.
render: shell
validations:
required: true
- type: dropdown
id: os
attributes:
label: OS
options:
- NixOS
- Arch
- Debian / Ubuntu
- Fedora
- Alpine
- Artix
- Gentoo
- Other
validations:
required: false
- type: input
id: other-os
attributes:
label: Other OS (if "Other" above)
placeholder: e.g. Void, Gentoo, FreeBSD
validations:
required: false
- type: dropdown
id: init
attributes:
label: Init system
description: Relevant for UWSM configuration
options:
- systemd
- non-systemd (openrc / runit / s6 / ...)
validations:
required: false
- type: input
id: other-init
attributes:
label: Other init system (if "non-systemd" above)
placeholder: e.g. openrc, runit, s6, dinit
validations:
required: false
- type: input
id: qt-version
attributes:
label: Qt version
placeholder: 6.11.1
validations:
required: false
- type: input
id: commit
attributes:
label: Branch / commit
placeholder: main / abc1234
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs / backtrace / screenshots
render: shell
validations:
required: false
- type: textarea
id: workaround
attributes:
label: Found a workaround?
validations:
required: false
-51
View File
@@ -1,51 +0,0 @@
name: Feature Request
about: Idea or improvement
title: "feat: "
labels:
- enhancement
body:
- type: textarea
id: problem
attributes:
label: What's missing / annoying
placeholder: The use case or pain point
validations:
required: true
- type: textarea
id: solution
attributes:
label: How should it work
placeholder: Describe the desired behavior
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Other approaches considered
validations:
required: false
- type: dropdown
id: scope
attributes:
label: Area
options:
- Core / Daemon
- UI / Components
- Plugin system
- CMake / Build
- CI / Tooling
- Documentation
- Other
validations:
required: false
- type: textarea
id: context
attributes:
label: Mockups / references / notes
validations:
required: false
-59
View File
@@ -1,59 +0,0 @@
name: Pull Request
about: Submit changes
title: ""
labels: []
body:
- type: textarea
id: description
attributes:
label: What & why
placeholder: Describe the change and motivation
validations:
required: true
- type: input
id: issue
attributes:
label: Related issue
placeholder: "#123"
validations:
required: false
- type: dropdown
id: type
attributes:
label: Type
options:
- Bug fix
- New feature
- Refactor / style
- CI / build / tooling
- Documentation
validations:
required: true
- type: checkboxes
id: tested
attributes:
label: Tested
options:
- label: Build succeeds
- label: Existing tests pass
- label: Manually verified
- type: textarea
id: breaking
attributes:
label: Breaking changes?
placeholder: API/ABI breakage? Migration path?
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: CI passes (format, lint, typecheck)
- label: Clean commits
- label: No new warnings
- label: Docs updated if needed
+3 -3
View File
@@ -2,7 +2,7 @@ name: Rebuild CI Image
on:
schedule:
- cron: "0 6 * * 1"
- cron: '0 6 * * 1'
workflow_dispatch:
jobs:
@@ -11,7 +11,7 @@ jobs:
container:
image: node:26-alpine
env:
IMAGE: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
IMAGE: git.aramjonghu.nl/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
@@ -21,7 +21,7 @@ jobs:
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
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.nl --username aramjonghu --password-stdin
- name: Build image
run: docker build -t "$IMAGE" -f ci/Dockerfile .
+1 -34
View File
@@ -4,43 +4,10 @@ on:
pull_request:
jobs:
fmt:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Format check
run: |
find . \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
-not -path './build/*' \
-exec clang-format -i --style=file {} +
git diff --exit-code && echo "clang-format: passed"
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: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Build
run: ninja -C build
clang-tidy:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
image: git.aramjonghu.nl/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
@@ -1,10 +1,10 @@
name: JS/TS
name: Lint & Format (JS/TS)
on:
pull_request:
jobs:
fmt:
lint-format:
runs-on: alpine
container: node:26-alpine
@@ -18,6 +18,7 @@ jobs:
git
- name: Prettier
continue-on-error: true
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
npx --yes prettier --check "**/*.{js,jsx,ts,tsx,mjs,cjs}" --ignore-path .prettierignore
@@ -25,19 +26,6 @@ jobs:
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
+65
View File
@@ -0,0 +1,65 @@
name: Python
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git \
python3 \
py3-pip
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir ruff
- name: Format check
continue-on-error: true
run: |
. .venv/bin/activate
ruff format --check .
- name: Lint
run: |
. .venv/bin/activate
ruff check .
test:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git \
python3 \
py3-pip \
py3-pillow \
build-base
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir \
typer \
pillow \
materialyoucolor \
jinja2 \
pytest
- name: Test
run: |
. .venv/bin/activate
cd cli
python -m pytest tests/ -v
+85
View File
@@ -0,0 +1,85 @@
name: Lint & Format (Rust)
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rustfmt \
rust-clippy
- id: format-check
name: Format check
continue-on-error: true
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
{ echo "$manifest: needs formatting"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
- id: 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
- name: Check results
if: always()
run: |
if [ "${{ steps.format-check.outcome }}" = "failure" ] || [ "${{ steps.clippy.outcome }}" = "failure" ]; then
echo "One or more checks failed"
exit 1
fi
echo "All checks passed"
-67
View File
@@ -1,67 +0,0 @@
name: Python
env:
APT_DEPS: git python3 python3-pip python3-venv python3-gi python3-cairo python3-dbus python3-pyudev python3-psutil python3-evdev python3-yaml python3-xlib python3-pillow gir1.2-gtk-3.0
on:
pull_request:
jobs:
static:
runs-on: debian
container: node:26-trixie-slim
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apt-get update
apt-get install -y --no-install-recommends git python3 python3-pip python3-venv
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir ruff
- name: Format check
run: |
. .venv/bin/activate
ruff format --check .
- name: Lint
run: |
. .venv/bin/activate
ruff check .
verify:
if: always()
runs-on: debian
container: node:26-trixie-slim
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apt-get update
apt-get install -y --no-install-recommends $APT_DEPS build-essential python3-dev
python3 -m venv --system-site-packages .venv
. .venv/bin/activate
pip install --no-cache-dir basedpyright nuitka .
- name: Type check
run: |
. .venv/bin/activate
basedpyright
- name: Test
run: |
. .venv/bin/activate
cd cli
python -m pytest tests/ -v
- name: Nuitka module check
run: |
. .venv/bin/activate
nuitka --module --include-package=zshell cli/src/zshell/
-152
View File
@@ -1,152 +0,0 @@
name: Rust
on:
pull_request:
jobs:
build:
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-build-${{ 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
- name: Cargo check
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
for manifest in $(find . -name "Cargo.toml"); do
cargo check --manifest-path "$manifest" && \
echo "$manifest: check passed" || \
{ echo "$manifest: check failed"; exit 1; }
done
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
fmt:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-fmt-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rustfmt
- name: Format check
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
{ echo "$manifest: needs formatting"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
clippy:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-clippy-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rust-clippy
- name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
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
+1 -3
View File
@@ -15,6 +15,4 @@ dist/
**/target/
**/test-plugins/
**/Charts/
network-dev/
**/zshell.build/
**/zshell.dist/
**/network-dev/
+3 -107
View File
@@ -36,15 +36,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(ENABLE_MODULES "plugin;shell;m3shapes" CACHE STRING "Modules to build/install")
set(ENABLE_MODULES "plugin;shell" CACHE STRING "Modules to build/install")
set(INSTALL_LIBDIR "usr/lib/ZShell" CACHE STRING "Library install dir")
set(INSTALL_QMLDIR "usr/lib/qt6/qml" CACHE STRING "QML install dir")
set(INSTALL_QSCONFDIR "etc/xdg/quickshell/zshell" CACHE STRING "Quickshell config install dir")
set(INSTALL_GREETERCONFDIR "etc/xdg/quickshell/zshell-greeter" CACHE STRING "Quickshell greeter install dir")
set(CMAKE_INSTALL_MESSAGE NEVER)
add_compile_options(
-Wall -Wextra -Wpedantic -Wshadow -Wconversion
-Wold-style-cast -Wnull-dereference -Wdouble-promotion
@@ -53,9 +51,7 @@ add_compile_options(
-Wunreachable-code
)
if("shell" IN_LIST ENABLE_MODULES)
# Build settings index
find_package(Python3 COMPONENTS Interpreter REQUIRED)
set(SETTINGS_INDEX_JSON "${CMAKE_BINARY_DIR}/settings-index.json")
execute_process(
@@ -68,96 +64,15 @@ if("shell" IN_LIST ENABLE_MODULES)
if(NOT SETTINGS_INDEX_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to build settings search index")
endif()
# Nuitka compilation
set(ZSHELL_CLI_BUILD_DIR "${CMAKE_BINARY_DIR}/zshell-cli")
set(ZSHELL_CLI_DIST "${ZSHELL_CLI_BUILD_DIR}/zshell.dist")
set(ZSHELL_CLI_SRC "${CMAKE_SOURCE_DIR}/cli/src/zshell")
find_program(NUITKA_EXECUTABLE nuitka)
if(NUITKA_EXECUTABLE)
file(GLOB_RECURSE ZSHELL_CLI_SOURCES CONFIGURE_DEPENDS
"${ZSHELL_CLI_SRC}/*.py"
)
file(GLOB_RECURSE ZSHELL_CLI_ASSETS CONFIGURE_DEPENDS
"${ZSHELL_CLI_SRC}/assets/*"
)
add_custom_command(
OUTPUT "${ZSHELL_CLI_DIST}/zshell-cli"
COMMAND ${CMAKE_COMMAND} -E make_directory "${ZSHELL_CLI_BUILD_DIR}"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${ZSHELL_CLI_DIST}"
COMMAND
${NUITKA_EXECUTABLE}
--standalone
--include-data-dir=${CMAKE_SOURCE_DIR}/cli/src/zshell/assets=zshell/assets
--include-package=gi.overrides
--include-package-data=solaar
--output-dir=${ZSHELL_CLI_BUILD_DIR}
--output-filename=zshell-cli
${CMAKE_SOURCE_DIR}/cli/src/zshell/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/cli
DEPENDS ${ZSHELL_CLI_SOURCES} ${ZSHELL_CLI_ASSETS}
)
add_custom_target(zshell-cli ALL DEPENDS "${ZSHELL_CLI_DIST}/zshell-cli")
install(PROGRAMS "${ZSHELL_CLI_DIST}/zshell-cli" DESTINATION "${INSTALL_LIBDIR}/zshell-cli")
install(DIRECTORY "${ZSHELL_CLI_DIST}/" DESTINATION "${INSTALL_LIBDIR}/zshell-cli" PATTERN "zshell-cli" EXCLUDE)
configure_file(
"${CMAKE_SOURCE_DIR}/Plugins/cmake/zshell-cli.cmake.in"
"${CMAKE_BINARY_DIR}/Plugins/cmake/zshell-cli.cmake"
@ONLY
)
install(SCRIPT "${CMAKE_BINARY_DIR}/Plugins/cmake/zshell-cli.cmake")
else()
message(STATUS "Nuitka not found, building zshell-cli as a Python wheel instead")
find_package(Python3 COMPONENTS Interpreter REQUIRED)
set(ZSHELL_CLI_DIR "${CMAKE_SOURCE_DIR}")
set(ZSHELL_CLI_DIST_DIR "${ZSHELL_CLI_DIR}/dist")
set(ZSHELL_CLI_WHEEL_STAMP "${CMAKE_BINARY_DIR}/zshell-cli/wheel.stamp")
file(GLOB_RECURSE ZSHELL_CLI_SOURCES CONFIGURE_DEPENDS
"${ZSHELL_CLI_DIR}/src/zshell/*.py"
)
file(GLOB_RECURSE ZSHELL_CLI_ASSETS CONFIGURE_DEPENDS
"${ZSHELL_CLI_DIR}/src/zshell/assets/*"
)
add_custom_command(
OUTPUT "${ZSHELL_CLI_WHEEL_STAMP}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/zshell-cli"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${ZSHELL_CLI_DIST_DIR}"
COMMAND ${Python3_EXECUTABLE} -m build --wheel --no-isolation
COMMAND ${CMAKE_COMMAND} -E touch "${ZSHELL_CLI_WHEEL_STAMP}"
WORKING_DIRECTORY "${ZSHELL_CLI_DIR}"
DEPENDS ${ZSHELL_CLI_SOURCES} ${ZSHELL_CLI_ASSETS}
COMMENT "Building zshell-cli python wheel"
)
add_custom_target(zshell-cli ALL DEPENDS "${ZSHELL_CLI_WHEEL_STAMP}")
configure_file(
"${CMAKE_SOURCE_DIR}/Plugins/cmake/zshell-cli-python.cmake.in"
"${CMAKE_BINARY_DIR}/Plugins/cmake/zshell-cli-python.cmake"
@ONLY
)
install(SCRIPT "${CMAKE_BINARY_DIR}/Plugins/cmake/zshell-cli-python.cmake")
endif()
endif()
if("plugin" IN_LIST ENABLE_MODULES)
add_subdirectory(Plugins)
endif()
if("shell" IN_LIST ENABLE_MODULES)
foreach(dir assets scripts Components Services Modules Daemons Drawers Effects Helpers Paths)
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
endforeach()
@@ -170,22 +85,3 @@ if("shell" IN_LIST ENABLE_MODULES)
# Greeter
install(DIRECTORY Greeter/ DESTINATION "${INSTALL_GREETERCONFDIR}")
endif()
if("m3shapes" IN_LIST ENABLE_MODULES)
message(STATUS "Fetching M3Shapes module")
include(FetchContent)
set(M3SHAPES_REV bdc327b29f95394a732baf3c9b19658ba23755b6)
FetchContent_Declare(
m3shapes_external
GIT_REPOSITORY https://github.com/soramanew/m3shapes.git
GIT_TAG ${M3SHAPES_REV}
SOURCE_DIR "${CMAKE_BINARY_DIR}/_deps/m3shapes-${M3SHAPES_REV}"
)
FetchContent_MakeAvailable(m3shapes_external)
message(STATUS "Done fetching M3Shapes module")
# Fix m3shapes wrong rpath
if(TARGET m3shapesplugin)
set_target_properties(m3shapesplugin PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
endif()
+14 -14
View File
@@ -1,5 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Config
AnchorAnimation {
enum Type {
@@ -20,32 +20,32 @@ AnchorAnimation {
duration: {
if (type < AnchorAnim.StandardSmall || type > AnchorAnim.SlowSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.FastSpatial)
return Tokens.anim.durations.expressiveFastSpatial;
return Appearance.anim.durations.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial;
return Appearance.anim.durations.large;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
return Tokens.anim.durations[types[idx]];
const idx = type % 4;
return Appearance.anim.durations[types[idx]];
}
easing: {
easing.bezierCurve: {
if (type == AnchorAnim.FastSpatial)
return Tokens.anim.expressiveFastSpatial;
return Appearance.anim.curves.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial;
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial;
return Appearance.anim.curves.expressiveSlowSpatial;
if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge)
return Tokens.anim.standard;
return Appearance.anim.curves.standard;
if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge)
return Tokens.anim.emphasized;
return Appearance.anim.curves.emphasized;
return Tokens.anim.expressiveDefaultSpatial;
return Appearance.anim.curves.expressiveDefaultSpatial;
}
}
+19 -19
View File
@@ -1,5 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Config
NumberAnimation {
enum Type {
@@ -23,41 +23,41 @@ NumberAnimation {
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Tokens.anim.durations.normal;
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Tokens.anim.durations.expressiveFastSpatial;
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial;
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Tokens.anim.durations.expressiveDefaultEffects;
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
return Tokens.anim.durations[types[idx]];
const idx = type % 4;
return Appearance.anim.durations[types[idx]];
}
easing: {
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Tokens.anim.expressiveFastSpatial;
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial;
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial;
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Tokens.anim.expressiveFastEffects;
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Tokens.anim.expressiveDefaultEffects;
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Tokens.anim.expressiveSlowEffects;
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.emphasized;
return Tokens.anim.standard;
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
}
+14 -15
View File
@@ -1,12 +1,11 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
import qs.Config
Slider {
id: root
property color color: Colors.palette.m3secondary
property color color: DynamicColors.palette.m3secondary
required property string icon
property bool initialized: false
readonly property bool isHorizontal: orientation === Qt.Horizontal
@@ -20,9 +19,9 @@ Slider {
background: CustomRect {
id: groove
color: Colors.layer(Colors.palette.m3surfaceContainer, 2)
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
height: root.availableHeight
radius: Tokens.rounding.full
radius: Appearance.rounding.full
width: root.availableWidth
x: root.leftPadding
y: root.topPadding
@@ -63,8 +62,8 @@ Slider {
id: rect
anchors.fill: parent
color: Colors.palette.m3inverseSurface
radius: Tokens.rounding.full
color: DynamicColors.palette.m3inverseSurface
radius: Appearance.rounding.full
MouseArea {
id: handleInteraction
@@ -83,12 +82,12 @@ Slider {
function update(): void {
animate = !moving;
binding.when = moving;
font.pointSize = moving ? Tokens.font.size.small : Tokens.font.size.larger;
font.family = moving ? Config.appearance.font.family.sans : Config.appearance.font.family.material;
font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger;
font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material;
}
anchors.centerIn: parent
color: Colors.palette.m3inverseOnSurface
color: DynamicColors.palette.m3inverseOnSurface
text: root.icon
onMovingChanged: anim.restart()
@@ -106,8 +105,8 @@ Slider {
id: anim
Anim {
duration: Tokens.anim.durations.normal / 2
easing: Tokens.anim.standardAccel
duration: Appearance.anim.durations.normal / 2
easing.bezierCurve: Appearance.anim.curves.standardAccel
property: "scale"
target: icon
to: 0
@@ -118,8 +117,8 @@ Slider {
}
Anim {
duration: Tokens.anim.durations.normal / 2
easing: Tokens.anim.standardDecel
duration: Appearance.anim.durations.normal / 2
easing.bezierCurve: Appearance.anim.curves.standardDecel
property: "scale"
target: icon
to: 1
@@ -130,7 +129,7 @@ Slider {
}
Behavior on value {
Anim {
duration: Tokens.anim.durations.large
duration: Appearance.anim.durations.large
}
}
+9 -11
View File
@@ -1,7 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Components
import qs.Services
import qs.Config
CustomRect {
id: root
@@ -15,14 +13,14 @@ CustomRect {
property color activeColor
property color activeOnColor
property bool checked
property real checkedRadius: Tokens.rounding.medium
property real defaultRadius: Tokens.rounding.large
property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(Colors.palette.m3onSurface, 0.38)
property real checkedRadius: Appearance.rounding.medium
property real defaultRadius: Appearance.rounding.large
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property bool fillWidth
property font font: ({
family: Config.appearance.font.family.sans,
pointSize: Tokens.font.size.larger,
family: Appearance.font.family.sans,
pointSize: Appearance.font.size.larger,
bold: false
})
property real horizontalPadding: padding
@@ -37,7 +35,7 @@ CustomRect {
readonly property color onColor: !enabled ? disabledOnColor : internalChecked ? activeOnColor : inactiveOnColor
property real padding
readonly property alias pressed: stateLayer.pressed
property real pressedRadius: Tokens.rounding.small
property real pressedRadius: Appearance.rounding.small
readonly property alias radiusAnim: radiusAnim
property bool radiusMorph: true
property alias shapeMorph: stateLayer.shapeMorph
@@ -55,7 +53,7 @@ CustomRect {
if (internalChecked)
return checkedRadius;
if (isRound)
return (height || implicitHeight) / 2 * Math.min(1, Tokens.rounding.scale);
return (height || implicitHeight) / 2 * Math.min(1, Appearance.rounding.scale);
return defaultRadius;
}
+4 -3
View File
@@ -1,7 +1,8 @@
import QtQuick
import ZShell.Config
import qs.Config
ColorAnimation {
duration: Tokens.anim.durations.expressiveSlowEffects
easing: Tokens.anim.expressiveSlowEffects
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
easing.type: Easing.BezierSpline
}
+8 -9
View File
@@ -1,8 +1,7 @@
import ZShell.Config
import qs.Config
import ZShell.Internal
import QtQuick
import QtQuick.Templates
import qs.Services
BusyIndicator {
id: root
@@ -18,12 +17,12 @@ BusyIndicator {
}
property int animState
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.size.normal * 3
property color bgColor: DynamicColors.palette.m3secondaryContainer
property color fgColor: DynamicColors.palette.m3primary
property real implicitSize: Appearance.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8
property real strokeWidth: Appearance.padding.small * 0.8
property alias type: manager.indeterminateAnimationType
implicitHeight: implicitSize
@@ -51,7 +50,7 @@ BusyIndicator {
}
transitions: Transition {
Anim {
duration: manager.completeEndDuration * Tokens.anim.durations.scale
duration: manager.completeEndDuration * Appearance.anim.durations.scale
properties: "opacity,internalStrokeWidth"
}
}
@@ -77,7 +76,7 @@ BusyIndicator {
}
NumberAnimation {
duration: manager.duration * Tokens.anim.durations.scale
duration: manager.duration * Appearance.anim.durations.scale
from: 0
loops: Animation.Infinite
property: "progress"
@@ -87,7 +86,7 @@ BusyIndicator {
}
NumberAnimation {
duration: manager.completeEndDuration * Tokens.anim.durations.scale
duration: manager.completeEndDuration * Appearance.anim.durations.scale
from: 0
property: "completeEndProgress"
running: root.animState === CircularIndicator.Completing
+6 -7
View File
@@ -3,25 +3,24 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Shapes
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Config
Item {
id: root
readonly property real arcRadius: (size - padding - strokeWidth * (1 + waveAmplitude * 2)) / 2
property color bgColor: Colors.palette.m3secondaryContainer
property color bgColor: DynamicColors.palette.m3secondaryContainer
property real clampedVal: Math.max(1 / 360, Math.min(1, isNaN(value) ? 0 : value))
readonly property real dotAngleRad: (startAngle + sweepAngle - gapAngle * (sweepAngle < 360 ? 0 : 1)) * Math.PI / 180
property color fgColor: Colors.palette.m3primary
property color fgColor: DynamicColors.palette.m3primary
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
property alias hasEndIndicator: dot.active
property real implicitSize
property int padding: 0
readonly property real size: Math.min(width, height)
property int spacing: Tokens.spacing.small
property int spacing: Appearance.spacing.small
property int startAngle: -90
property int strokeWidth: Tokens.padding.extraSmall
property int strokeWidth: Appearance.padding.small
property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value
@@ -111,7 +110,7 @@ Item {
implicitHeight: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
implicitWidth: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
opacity: Math.min(1, remainingArc.sweepAngle)
radius: Tokens.rounding.full
radius: Appearance.rounding.full
}
}
}
+135
View File
@@ -0,0 +1,135 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
ColumnLayout {
id: root
default property alias content: contentColumn.data
property string description: ""
property bool expanded: false
property bool nested: false
property bool showBackground: false
required property string title
signal toggleRequested
Layout.fillWidth: true
spacing: Appearance.spacing.small
Item {
id: sectionHeaderItem
Layout.fillWidth: true
Layout.preferredHeight: Math.max(titleRow.implicitHeight + Appearance.padding.normal * 2, 48)
RowLayout {
id: titleRow
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.normal
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.normal
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.normal
CustomText {
font.pointSize: Appearance.font.size.larger
font.weight: 500
text: root.title
}
Item {
Layout.fillWidth: true
}
MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.normal
rotation: root.expanded ? 180 : 0
text: "expand_more"
Behavior on rotation {
Anim {
duration: Appearance.anim.durations.small
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
}
StateLayer {
anchors.fill: parent
color: DynamicColors.palette.m3onSurface
radius: Appearance.rounding.normal
showHoverBackground: false
onClicked: {
root.toggleRequested();
root.expanded = !root.expanded;
}
}
}
Item {
id: contentWrapper
Layout.fillWidth: true
Layout.preferredHeight: root.expanded ? (contentColumn.implicitHeight + Appearance.spacing.small * 2) : 0
clip: true
Behavior on Layout.preferredHeight {
Anim {
easing.bezierCurve: Appearance.anim.curves.standard
}
}
CustomRect {
id: backgroundRect
anchors.fill: parent
color: DynamicColors.transparency.enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, root.nested ? 3 : 2) : (root.nested ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3surfaceContainer)
opacity: root.showBackground && root.expanded ? 1.0 : 0.0
radius: Appearance.rounding.normal
visible: root.showBackground
Behavior on opacity {
Anim {
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
ColumnLayout {
id: contentColumn
anchors.bottomMargin: Appearance.spacing.small
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.normal
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.normal
opacity: root.expanded ? 1.0 : 0.0
spacing: Appearance.spacing.small
y: Appearance.spacing.small
Behavior on opacity {
Anim {
easing.bezierCurve: Appearance.anim.curves.standard
}
}
CustomText {
id: descriptionText
Layout.bottomMargin: root.description !== "" ? Appearance.spacing.small : 0
Layout.fillWidth: true
Layout.topMargin: root.description !== "" ? Appearance.spacing.smaller : 0
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
text: root.description
visible: root.description !== ""
wrapMode: Text.Wrap
}
}
}
}
+6 -7
View File
@@ -1,8 +1,7 @@
pragma ComponentBehavior: Bound
import QtQuick
import ZShell.Config
import qs.Services
import qs.Config
Item {
id: root
@@ -19,9 +18,9 @@ Item {
property real lastChromaticHue: 0
readonly property real radius: (Math.min(width, height) - handleSize) / 2
readonly property int segmentCount: 240
readonly property color thumbColor: Colors.palette.m3inverseSurface
readonly property color thumbContentColor: Colors.palette.m3inverseOnSurface
readonly property color trackColor: Colors.layer(Colors.palette.m3surfaceContainer, 2)
readonly property color thumbColor: DynamicColors.palette.m3inverseSurface
readonly property color thumbContentColor: DynamicColors.palette.m3inverseOnSurface
readonly property color trackColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
function hueToAngle(hue) {
return arcStartAngle + arcSweep * hue;
@@ -154,8 +153,8 @@ Item {
anchors.centerIn: parent
color: root.drawing?.drawingState.penColor
implicitHeight: implicitWidth
implicitWidth: canvas.height - root.handleSize - Tokens.padding.extraLarge * 2
radius: Tokens.rounding.full
implicitWidth: canvas.height - root.handleSize - Appearance.padding.extraLarge * 2
radius: Appearance.rounding.full
Behavior on color {
enabled: false
+70
View File
@@ -0,0 +1,70 @@
import QtQuick
import QtQuick.Templates
import qs.Config
Slider {
id: root
property color nonPeakColor: DynamicColors.tPalette.m3primary
required property real peak
property color peakColor: DynamicColors.palette.m3primary
background: Item {
CustomRect {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 3
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 3
bottomRightRadius: root.implicitHeight / 15
color: root.nonPeakColor
implicitWidth: root.handle.x - root.implicitHeight
radius: Appearance.rounding.full
topRightRadius: root.implicitHeight / 15
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 15
color: root.peakColor
implicitWidth: parent.width * root.peak
radius: Appearance.rounding.full
topRightRadius: root.implicitHeight / 15
Behavior on implicitWidth {
Anim {
duration: 50
}
}
}
}
CustomRect {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 3
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 3
bottomLeftRadius: root.implicitHeight / 15
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: root.implicitWidth - root.handle.x - root.handle.implicitWidth - root.implicitHeight
radius: Appearance.rounding.full
topLeftRadius: root.implicitHeight / 15
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
implicitHeight: 15
implicitWidth: 5
radius: Appearance.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}
}
+69
View File
@@ -0,0 +1,69 @@
import QtQuick
import QtQuick.Controls.Basic
BusyIndicator {
id: control
property int busySize: 64
property color color: delegate.color
contentItem: Item {
implicitHeight: control.busySize
implicitWidth: control.busySize
Item {
id: item
height: control.busySize
opacity: control.running ? 1 : 0
width: control.busySize
x: parent.width / 2 - (control.busySize / 2)
y: parent.height / 2 - (control.busySize / 2)
Behavior on opacity {
OpacityAnimator {
duration: 250
}
}
RotationAnimator {
duration: 1250
from: 0
loops: Animation.Infinite
running: control.visible && control.running
target: item
to: 360
}
Repeater {
id: repeater
model: 6
CustomRect {
id: delegate
required property int index
color: control.color
implicitHeight: 10
implicitWidth: 10
radius: 5
x: item.width / 2 - width / 2
y: item.height / 2 - height / 2
transform: [
Translate {
y: -Math.min(item.width, item.height) * 0.5 + 5
},
Rotation {
angle: delegate.index / repeater.count * 360
origin.x: 5
origin.y: 5
}
]
}
}
}
}
}
+32
View File
@@ -0,0 +1,32 @@
import QtQuick
import QtQuick.Controls
import qs.Config
Button {
id: control
property color bgColor: DynamicColors.palette.m3primary
property int radius: Appearance.rounding.smallest / 2
property color textColor: DynamicColors.palette.m3onPrimary
background: CustomRect {
color: control.bgColor
opacity: control.enabled ? 1.0 : 0.5
radius: control.radius
}
contentItem: CustomText {
color: control.textColor
horizontalAlignment: Text.AlignHCenter
opacity: control.enabled ? 1.0 : 0.5
text: control.text
verticalAlignment: Text.AlignVCenter
}
StateLayer {
radius: control.radius
onClicked: {
control.clicked();
}
}
}
+5 -6
View File
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
import qs.Config
CheckBox {
id: control
@@ -19,14 +18,14 @@ CheckBox {
indicator: CustomRect {
// x: control.leftPadding
// y: parent.implicitHeight / 2 - implicitHeight / 2
border.color: control.checked ? Colors.palette.m3primary : "transparent"
color: Colors.palette.m3surfaceVariant
border.color: control.checked ? DynamicColors.palette.m3primary : "transparent"
color: DynamicColors.palette.m3surfaceVariant
implicitHeight: control.checkHeight
implicitWidth: control.checkWidth
radius: Tokens.rounding.small / 2
radius: Appearance.rounding.smallest / 2
CustomRect {
color: Colors.palette.m3primary
color: DynamicColors.palette.m3primary
implicitHeight: control.checkHeight - (y * 2)
implicitWidth: control.checkWidth - (x * 2)
radius: 3
+169
View File
@@ -0,0 +1,169 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import qs.Config
ComboBox {
id: root
property int cornerRadius: Appearance.rounding.normal
property int fieldHeight: 42
property bool filled: true
property real focusRingOpacity: 0.70
property int hPadding: 16
property int menuCornerRadius: 16
property int menuRowHeight: 46
property int menuVisibleRows: 7
property bool preferPopupWindow: false
hoverEnabled: true
implicitHeight: fieldHeight
implicitWidth: 240
spacing: 8
// ---------- Field background (filled/outlined + state layers + focus ring) ----------
background: Item {
anchors.fill: parent
CustomRect {
id: container
anchors.fill: parent
color: DynamicColors.palette.m3surfaceVariant
radius: root.cornerRadius
StateLayer {
}
}
}
// ---------- Content ----------
contentItem: RowLayout {
anchors.fill: parent
anchors.leftMargin: root.hPadding
anchors.rightMargin: root.hPadding
spacing: 12
// Display text
CustomText {
Layout.fillWidth: true
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pixelSize: 16
font.weight: Font.Medium
text: root.currentText
verticalAlignment: Text.AlignVCenter
}
// Indicator chevron (simple, replace with your icon system)
CustomText {
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurfaceVariant
rotation: root.popup.visible ? 180 : 0
text: "▾"
transformOrigin: Item.Center
verticalAlignment: Text.AlignVCenter
Behavior on rotation {
NumberAnimation {
duration: 140
easing.type: Easing.OutCubic
}
}
}
}
popup: Popup {
id: p
implicitHeight: list.contentItem.height + Appearance.padding.small * 2
implicitWidth: root.width
modal: true
popupType: root.preferPopupWindow ? Popup.Window : Popup.Item
y: -list.currentIndex * (root.menuRowHeight + Appearance.spacing.small) - Appearance.padding.small
background: CustomRect {
color: DynamicColors.palette.m3surface
radius: root.menuCornerRadius
}
contentItem: ListView {
id: list
anchors.bottomMargin: Appearance.padding.small
anchors.fill: parent
anchors.topMargin: Appearance.padding.small
clip: true
currentIndex: root.currentIndex
model: root.delegateModel
spacing: Appearance.spacing.small
delegate: CustomRect {
required property int index
required property var modelData
anchors.horizontalCenter: parent.horizontalCenter
color: (index === root.currentIndex) ? DynamicColors.palette.m3primary : "transparent"
implicitHeight: root.menuRowHeight
implicitWidth: p.implicitWidth - Appearance.padding.small * 2
radius: Appearance.rounding.normal - Appearance.padding.small
RowLayout {
anchors.fill: parent
spacing: 10
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurface
elide: Text.ElideRight
font.pixelSize: 15
text: modelData
verticalAlignment: Text.AlignVCenter
}
CustomText {
color: DynamicColors.palette.m3onSurfaceVariant
text: "✓"
verticalAlignment: Text.AlignVCenter
visible: index === root.currentIndex
}
}
StateLayer {
onClicked: {
root.currentIndex = index;
p.close();
}
}
}
}
// Expressive-ish open/close motion: subtle scale+fade (tune to taste). :contentReference[oaicite:5]{index=5}
enter: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
Anim {
from: 0.98
property: "scale"
to: 1.0
}
}
exit: Transition {
Anim {
from: 1
property: "opacity"
to: 0
}
}
Elevation {
anchors.fill: parent
level: 2
radius: root.menuCornerRadius
z: -1
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ ListView {
property bool doneFakeFlick
interactive: !Visibilities.getForActive()?.isDrawing
interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
+7 -8
View File
@@ -5,8 +5,7 @@ import QtQuick.Templates
import ZShell
import ZShell.Components
import ZShell.Internal
import ZShell.Config
import qs.Services
import qs.Config
ProgressBar {
id: root
@@ -17,8 +16,8 @@ ProgressBar {
Stopped
}
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property color bgColor: DynamicColors.palette.m3secondaryContainer
property color fgColor: DynamicColors.palette.m3primary
property int indeterminateAnimState: CustomProgressBar.Stopped
property real waveAmplitude: 0.5
property int waveDuration: 1000
@@ -65,7 +64,7 @@ ProgressBar {
LinearIndicatorManager {
id: manager
gap: Tokens.spacing.extraSmall
gap: Appearance.spacing.extraSmall
Anim on completeEndProgress {
duration: manager.completeEndDuration
@@ -95,7 +94,7 @@ ProgressBar {
id: remaining
anchors.right: parent.right
implicitWidth: parent.width - wave.implicitWidth - Tokens.spacing.extraSmall
implicitWidth: parent.width - wave.implicitWidth - Appearance.spacing.extraSmall
}
Line {
@@ -107,7 +106,7 @@ ProgressBar {
color: root.fgColor
implicitHeight: implicitSize
implicitWidth: implicitSize
radius: Tokens.rounding.full
radius: Appearance.rounding.full
Behavior on implicitSize {
Anim {
@@ -201,7 +200,7 @@ ProgressBar {
color: root.bgColor
implicitHeight: parent.height
implicitWidth: bounds.y - bounds.x
radius: Tokens.rounding.full
radius: Appearance.rounding.full
x: bounds.x
}
component Wave: WavyLine {
+7 -8
View File
@@ -1,12 +1,11 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
import qs.Config
RadioButton {
id: root
font.pointSize: Tokens.font.size.normal
font.pointSize: Appearance.font.size.normal
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
@@ -21,12 +20,12 @@ RadioButton {
id: outerCircle
anchors.verticalCenter: parent.verticalCenter
border.color: root.checked ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
border.color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
border.width: 2
color: "transparent"
implicitHeight: 16
implicitWidth: 16
radius: Tokens.rounding.full
radius: Appearance.rounding.full
Behavior on border.color {
CAnim {
@@ -35,7 +34,7 @@ RadioButton {
StateLayer {
anchors.margins: -7
color: root.checked ? Colors.palette.m3onSurface : Colors.palette.m3primary
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1
onClicked: {
@@ -45,10 +44,10 @@ RadioButton {
CustomRect {
anchors.centerIn: parent
color: Qt.alpha(Colors.palette.m3primary, root.checked ? 1 : 0)
color: Qt.alpha(DynamicColors.palette.m3primary, root.checked ? 1 : 0)
implicitHeight: 8
implicitWidth: 8
radius: Tokens.rounding.full
radius: Appearance.rounding.full
}
}
}
+6 -7
View File
@@ -1,8 +1,7 @@
import QtQuick
import QtQuick.Templates
import qs.Helpers
import ZShell.Config
import qs.Services
import qs.Config
ScrollBar {
id: root
@@ -18,7 +17,7 @@ ScrollBar {
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
enabled: !Visibilities.getForActive().isDrawing
implicitWidth: Tokens.padding.extraSmall * 2
implicitWidth: Appearance.padding.extraSmall * 2
contentItem: Item {
}
@@ -52,15 +51,15 @@ ScrollBar {
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full
radius: Appearance.rounding.full
CustomRect {
id: handle
anchors.right: parent.right
color: Colors.palette.m3secondary
color: DynamicColors.palette.m3secondary
implicitHeight: root.height * root.effectiveSize
implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Appearance.padding.extraSmall * 2 : Appearance.padding.extraSmall
opacity: {
if (!root.enabled)
return 0;
@@ -74,7 +73,7 @@ ScrollBar {
return 0.6;
return 0;
}
radius: Tokens.rounding.full
radius: Appearance.rounding.full
y: root.reversed ? root.height * (1 + root.nonAnimY) * root.travelScale : root.height * root.nonAnimY * root.travelScale
Behavior on implicitWidth {
+17 -21
View File
@@ -5,23 +5,20 @@ import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import ZShell.Config
import qs.Services
import qs.Config
Slider {
id: root
property bool animateWave
property color bgColor: enabled ? Colors.palette.m3secondaryContainer : Qt.alpha(Colors.palette.m3onSurface, 0.1)
readonly property bool dragging: mouse.pressed
property color fgColor: enabled ? Colors.palette.m3primary : Qt.alpha(Colors.palette.m3onSurface, 0.38)
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property alias inset: inset
property color insetColor: enabled ? (inset.attached ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onPrimary) : Colors.palette.m3onSurface
property color insetColor: enabled ? (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) : DynamicColors.palette.m3onSurface
property string insetIcon: ""
property bool interactionOnMove: true
property real pos: visualPosition
property int radius: Tokens.rounding.small
property int radius: Appearance.rounding.small
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
@@ -38,15 +35,15 @@ Slider {
id: remaining
anchors.left: handle.right
anchors.leftMargin: Tokens.spacing.extraSmall
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
bottomLeftRadius: Tokens.rounding.extraSmall / 2
bottomLeftRadius: Appearance.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: root.radius
topLeftRadius: Tokens.rounding.extraSmall / 2
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
@@ -57,14 +54,14 @@ Slider {
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Tokens.rounding.full
radius: Appearance.rounding.full
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Tokens.spacing.extraSmall
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
@@ -73,7 +70,7 @@ Slider {
return parent.height * (mouse.pressed ? lerp(3.5, 1.5) : lerp(3, 1.2));
}
implicitWidth: 4
radius: Tokens.rounding.full
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
@@ -94,17 +91,17 @@ Slider {
MaterialIcon {
id: inset
readonly property bool attached: root.width ? (root.width - handle.implicitWidth - handle.anchors.leftMargin) * root.pos < inset.paintedWidth + Tokens.spacing.extraSmall * 2 : false
readonly property bool attached: root.width ? (root.width - handle.implicitWidth - handle.anchors.leftMargin) * root.pos < inset.paintedWidth + Appearance.spacing.extraSmall * 2 : false
property real dockT: attached ? 1 : 0
anchors.bottom: parent.bottom
anchors.top: parent.top
color: root.insetColor
font.pixelSize: Tokens.font.size.smaller * 2
font.pixelSize: Appearance.font.size.smaller * 2
text: root.insetIcon
verticalAlignment: Text.AlignVCenter
visible: !root.wavy && root.insetIcon !== ""
x: Tokens.spacing.extraSmall + dockT * (handle.x + Tokens.spacing.small)
x: Appearance.spacing.extraSmall + dockT * (handle.x + Appearance.spacing.small)
Behavior on dockT {
Anim {
@@ -116,12 +113,12 @@ Slider {
id: lineComp
CustomRect {
bottomRightRadius: Tokens.rounding.extraSmall / 2
bottomRightRadius: Appearance.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
radius: root.radius
topRightRadius: Tokens.rounding.extraSmall / 2
topRightRadius: Appearance.rounding.extraSmall / 2
}
}
@@ -188,8 +185,7 @@ Slider {
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
if (root.interactionOnMove)
root.interaction(posBinding.value);
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
+18 -19
View File
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
import qs.Config
DoubleSpinBox {
id: root
@@ -30,36 +29,36 @@ DoubleSpinBox {
editable: true
implicitHeight: Math.max(up.indicator.implicitHeight, down.indicator.implicitHeight, contentItem.implicitHeight) + topPadding + bottomPadding
implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
leftPadding: up.indicator.implicitWidth + Tokens.spacing.extraSmall / 2
rightPadding: down.indicator.implicitWidth + Tokens.spacing.extraSmall / 2
spacing: Tokens.spacing.small
leftPadding: up.indicator.implicitWidth + Appearance.spacing.extraSmall / 2
rightPadding: down.indicator.implicitWidth + Appearance.spacing.extraSmall / 2
spacing: Appearance.spacing.small
contentItem: TextFieldBase {
horizontalAlignment: TextField.AlignHCenter
implicitWidth: 65
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Tokens.padding.small
leftPadding: Appearance.padding.larger
readOnly: !root.editable
rightPadding: Tokens.padding.small
rightPadding: Appearance.padding.larger
text: root.textFromValue(root.value, root.locale)
validator: root.validator
background: CustomRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer)
radius: Tokens.rounding.extraSmall
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
radius: Appearance.rounding.extraSmall
}
}
down.indicator: IconButton {
id: downButton
bottomRightRadius: pressed ? Tokens.rounding.small : Tokens.rounding.extraSmall
color: enabled ? Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor
disabledColor: Qt.alpha(Colors.palette.m3surfaceContainerHighest, 0.4)
bottomRightRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
color: enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor
disabledColor: Qt.alpha(DynamicColors.palette.m3surfaceContainerHighest, 0.4)
icon: "remove"
isRound: true
label.anchors.horizontalCenterOffset: pressed ? 0 : 2
padding: Tokens.padding.extraSmall
topRightRadius: pressed ? Tokens.rounding.small : Tokens.rounding.extraSmall
padding: Appearance.padding.extraSmall
topRightRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
type: IconButton.Text
Behavior on bottomRightRadius {
@@ -82,14 +81,14 @@ DoubleSpinBox {
id: upButton
anchors.right: parent.right
bottomLeftRadius: pressed ? Tokens.rounding.small : Tokens.rounding.extraSmall
color: enabled ? Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor
disabledColor: Qt.alpha(Colors.palette.m3surfaceContainerHighest, 0.4)
bottomLeftRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
color: enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor
disabledColor: Qt.alpha(DynamicColors.palette.m3surfaceContainerHighest, 0.4)
icon: "add"
isRound: true
label.anchors.horizontalCenterOffset: pressed ? 0 : -2
padding: Tokens.padding.extraSmall
topLeftRadius: pressed ? Tokens.rounding.small : Tokens.rounding.extraSmall
padding: Appearance.padding.extraSmall
topLeftRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
type: IconButton.Text
Behavior on bottomLeftRadius {
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Config
Row {
id: root
@@ -12,14 +11,14 @@ Row {
}
property alias active: menu.active
property color color: type == SplitButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondaryContainer
property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(Colors.palette.m3onSurface, 0.38)
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded
property string fallbackIcon
property string fallbackText
property real horizontalPadding: Tokens.padding.medium
property real horizontalPadding: Appearance.padding.larger
readonly property alias iconLabel: iconLabel
readonly property alias label: label
readonly property alias menu: menu
@@ -27,20 +26,20 @@ Row {
property bool menuOnTop
property real minLeftWidth
readonly property alias stateLayer: stateLayer
property color textColor: type == SplitButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondaryContainer
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: SplitButton.Filled
property real verticalPadding: Tokens.padding.small
property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.small
spacing: Math.floor(Tokens.spacing.extraSmall / 2)
spacing: Math.floor(Appearance.spacing.extraSmall)
CustomRect {
bottomRightRadius: Tokens.rounding.medium / 2
bottomRightRadius: Appearance.rounding.small / 2
color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandBtn.implicitHeight
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Tokens.rounding.scale)
topRightRadius: Tokens.rounding.medium / 2
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2
StateLayer {
id: stateLayer
@@ -58,7 +57,7 @@ Row {
anchors.centerIn: parent
anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4)
spacing: Tokens.spacing.small
spacing: Appearance.spacing.small
MaterialIcon {
id: iconLabel
@@ -92,13 +91,13 @@ Row {
CustomRect {
id: expandBtn
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) : Tokens.rounding.medium / 2
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad
color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Tokens.rounding.scale)
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topLeftRadius: rad
Behavior on rad {
@@ -142,7 +141,7 @@ Row {
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom
attachTo: expandBtn
marginY: Tokens.spacing.small * (root.menuOnTop ? -1 : 1)
marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1)
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top
}
}
+69
View File
@@ -0,0 +1,69 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Config
Item {
id: root
property alias active: splitButton.active
property alias buttonAlias: splitButton
property alias expanded: splitButton.expanded
property int expandedZ: 100
required property string label
property alias menuItems: splitButton.menuItems
property bool shouldBeActive: true
property alias type: splitButton.type
signal selected(item: MenuItem)
anchors.left: parent.left
anchors.right: parent.right
clip: false
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.normal
CustomText {
Layout.fillWidth: true
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger
text: root.label
}
CustomSplitButton {
id: splitButton
enabled: root.enabled
type: CustomSplitButton.Filled
z: 2
menu.onItemSelected: item => {
root.selected(item);
}
stateLayer.onClicked: {
splitButton.expanded = !splitButton.expanded;
}
}
}
}
+15 -28
View File
@@ -1,8 +1,7 @@
import QtQuick
import QtQuick.Shapes
import QtQuick.Templates
import ZShell.Config
import qs.Services
import qs.Config
Switch {
id: root
@@ -13,28 +12,20 @@ Switch {
implicitWidth: implicitIndicatorWidth
indicator: CustomRect {
color: {
if (!root.enabled)
return root.checked ? Qt.alpha(Colors.palette.m3onSurface, 0.12) : Qt.alpha(Colors.palette.m3surfaceContainerHighest, 0.38);
return root.checked ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer);
}
implicitHeight: 14 + Tokens.padding.small * 2
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2
implicitWidth: implicitHeight * 1.7
radius: Tokens.rounding.full
radius: Appearance.rounding.full
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter
color: {
if (!root.enabled)
return root.checked ? Qt.alpha(Colors.palette.m3surface, 0.7) : Qt.alpha(Colors.palette.m3onSurface, 0.12);
return root.checked ? Colors.palette.m3onPrimary : Colors.layer(Colors.palette.m3outline, root.cLayer + 1);
}
implicitHeight: parent.implicitHeight - Tokens.padding.extraSmall
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall
implicitWidth: nonAnimWidth
radius: Tokens.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - Tokens.padding.extraSmall / 2 : Tokens.padding.extraSmall / 2
radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2
Behavior on implicitWidth {
Anim {
@@ -49,7 +40,7 @@ Switch {
CustomRect {
anchors.fill: parent
color: root.checked && root.enabled ? Colors.palette.m3primary : Colors.palette.m3onSurface
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius
@@ -100,7 +91,7 @@ Switch {
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Tokens.padding.medium
height: parent.implicitHeight - Appearance.padding.larger
preferredRendererType: Shape.CurveRenderer
width: height
@@ -122,16 +113,12 @@ Switch {
}
ShapePath {
capStyle: Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: icon.start1.x
startY: icon.start1.y
strokeColor: {
if (!root.enabled)
return root.checked ? Colors.palette.m3outline : Colors.palette.m3surfaceContainer;
return root.checked ? Colors.palette.m3primary : Colors.palette.m3surfaceContainerHighest;
}
strokeWidth: 16 * 0.15
strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor {
CAnim {
@@ -164,7 +151,7 @@ Switch {
}
component PropAnim: PropertyAnimation {
duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial
duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
}
}
+5 -6
View File
@@ -1,8 +1,7 @@
pragma ComponentBehavior: Bound
import QtQuick
import ZShell.Config
import qs.Services
import qs.Config
Text {
id: root
@@ -13,10 +12,10 @@ Text {
property string animateProp: "scale"
property real animateTo: 1
color: Colors.palette.m3onSurface
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.normal
linkColor: Colors.palette.m3onPrimaryFixedVariant
color: DynamicColors.palette.m3onSurface
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.normal
linkColor: DynamicColors.palette.m3onPrimaryFixedVariant
renderType: Text.NativeRendering
textFormat: Text.PlainText
+48 -54
View File
@@ -2,8 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Shapes
import ZShell.Config
import qs.Services
import qs.Config
TextFieldBase {
id: root
@@ -17,26 +16,28 @@ TextFieldBase {
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
property bool emptyIsValid: true
property string errorText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int horizontalPadding: Tokens.padding.large
readonly property int filledOffset: type === CustomTextField.Filled ? Appearance.spacing.small : 0
readonly property int horizontalPadding: Appearance.padding.large
property bool isError
property string leadingIcon
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
property int radius: Tokens.rounding.small
readonly property real outlineGap: placeholder.width * root.smallFontScale + Appearance.spacing.extraSmall * 2
property real outlineGapScale: activeFocus || text ? 1 : 0
property int radius: Appearance.rounding.small
readonly property real smallFontScale: smallFontSize / font.pointSize
property int smallFontSize: Tokens.font.size.small
property int smallFontSize: Appearance.font.size.small
property string supportingText
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Appearance.spacing.extraSmall : 0
property string trailingIcon
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
property int type: CustomTextField.Outlined
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property var validate // Regex or function
bottomPadding: Tokens.padding.large + supportingTextOffset - filledOffset
bottomPadding: Appearance.padding.large + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: Tokens.padding.large + filledOffset
topPadding: Appearance.padding.large + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
@@ -56,6 +57,11 @@ TextFieldBase {
onClicked: root.focus = true
}
}
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
onEditingFinished: {
if (!valid)
@@ -81,9 +87,9 @@ TextFieldBase {
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.topMargin: Tokens.padding.extraSmall
anchors.topMargin: Appearance.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : root.text ? Colors.palette.m3outline : root.placeholderTextColor)
color: root.isError ? DynamicColors.palette.m3error : (root.activeFocus ? DynamicColors.palette.m3primary : root.text ? DynamicColors.palette.m3outline : root.placeholderTextColor)
font.family: root.font.family
font.pointSize: root.font.pointSize
font.variableAxes: root.font.variableAxes
@@ -97,7 +103,7 @@ TextFieldBase {
when: root.type === CustomTextField.Outlined && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + -Appearance.spacing.extraSmall
placeholder.scale: root.smallFontScale
}
@@ -129,8 +135,8 @@ TextFieldBase {
}
AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
duration: Appearance.anim.durations.expressiveEffects
easing: Appearance.anim.curves.expressiveDefaultEffects
}
}
}
@@ -140,11 +146,12 @@ TextFieldBase {
active: root.leadingIcon
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small
anchors.leftMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: root.leadingIcon
}
}
@@ -154,11 +161,12 @@ TextFieldBase {
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.rightMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: root.trailingIcon
}
}
@@ -173,8 +181,8 @@ TextFieldBase {
anchors.leftMargin: root.horizontalPadding
sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.small
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
text: root.effectiveSupportingText
}
}
@@ -193,24 +201,12 @@ TextFieldBase {
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: path
readonly property real inset: strokeWidth / 2
readonly property real outlineGap: placeholder.width * root.smallFontScale + Tokens.spacing.small * 2
property real outlineGapScale: root.activeFocus || root.text ? 1 : 0
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: path.inset + root.horizontalPadding - root.clampedRadius + path.outlineGap * (1 - path.outlineGapScale) / 2 + path.outlineGap * path.outlineGapScale
startY: path.inset
strokeColor: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
startX: root.horizontalPadding - root.clampedRadius + root.outlineGap * (1 - root.outlineGapScale) / 2 + root.outlineGap * root.outlineGapScale
strokeColor: root.isError ? DynamicColors.palette.m3error : (root.activeFocus ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline)
strokeWidth: root.activeFocus ? 2 : 1
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on strokeColor {
CAnim {
}
@@ -221,56 +217,54 @@ TextFieldBase {
}
PathLine {
x: bg.width - path.inset - root.clampedRadius
y: path.inset
x: bg.width - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset
y: path.inset + root.clampedRadius
x: bg.width
y: root.clampedRadius
}
PathLine {
x: bg.width - path.inset
y: bg.height - path.inset - root.clampedRadius
x: bg.width
y: bg.height - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset - root.clampedRadius
y: bg.height - path.inset
x: bg.width - root.clampedRadius
y: bg.height
}
PathLine {
x: path.inset + root.clampedRadius
y: bg.height - path.inset
x: root.clampedRadius
y: bg.height
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset
y: bg.height - path.inset - root.clampedRadius
x: 0
y: bg.height - root.clampedRadius
}
PathLine {
x: path.inset
y: path.inset + root.clampedRadius
x: 0
y: root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset + root.clampedRadius
y: path.inset
x: root.clampedRadius
y: 0
}
PathLine {
x: path.inset + root.horizontalPadding - root.clampedRadius / 2 + path.outlineGap * (1 - path.outlineGapScale) / 2
y: path.inset
x: root.horizontalPadding - root.clampedRadius + root.outlineGap * (1 - root.outlineGapScale) / 2
}
}
}
@@ -280,7 +274,7 @@ TextFieldBase {
id: filledComp
CustomRect {
color: root.activeFocus ? Colors.tPalette.m3surfaceContainerHighest : Colors.tPalette.m3surfaceContainerHigh
color: root.activeFocus ? DynamicColors.tPalette.m3surfaceContainerHighest : DynamicColors.tPalette.m3surfaceContainerHigh
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
@@ -288,7 +282,7 @@ TextFieldBase {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
color: root.isError ? DynamicColors.palette.m3error : (root.activeFocus ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline)
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
+5 -6
View File
@@ -1,16 +1,15 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
import qs.Config
TextInput {
renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.colSecondaryContainer
selectedTextColor: DynamicColors.palette.m3onSecondaryContainer
selectionColor: DynamicColors.tPalette.colSecondaryContainer
font {
family: Config.appearance?.font.family.sans ?? "sans-serif"
family: Appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting
pixelSize: Config.appearance?.font.size.normal ?? 15
pixelSize: Appearance?.font.size.normal ?? 15
}
}
+25
View File
@@ -0,0 +1,25 @@
import QtQuick
import QtQuick.Controls
import qs.Components
ToolTip {
id: root
property bool alternativeVisibleCondition: false
property bool extraVisibleCondition: true
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
background: null
horizontalPadding: 10
verticalPadding: 5
visible: internalVisibleCondition
contentItem: CustomTooltipContent {
id: contentItem
horizontalPadding: root.horizontalPadding
shown: root.internalVisibleCondition
text: root.text
verticalPadding: root.verticalPadding
}
}
+54
View File
@@ -0,0 +1,54 @@
import QtQuick
import qs.Components
import qs.Config
Item {
id: root
property real horizontalPadding: 10
property bool isVisible: backgroundRectangle.implicitHeight > 0
property bool shown: false
required property string text
property real verticalPadding: 5
implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding
implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding
Rectangle {
id: backgroundRectangle
clip: true
color: DynamicColors.tPalette.m3inverseSurface ?? "#3C4043"
implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0
implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0
opacity: shown ? 1 : 0
radius: Appearance.rounding.smallest
Behavior on implicitHeight {
Anim {
}
}
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity {
Anim {
}
}
anchors {
bottom: root.bottom
horizontalCenter: root.horizontalCenter
}
CustomText {
id: tooltipTextObject
anchors.centerIn: parent
color: DynamicColors.palette.m3inverseOnSurface ?? "#FFFFFF"
text: root.text
wrapMode: Text.Wrap
}
}
}
+2 -3
View File
@@ -1,14 +1,13 @@
import QtQuick
import QtQuick.Effects
import ZShell.Config
import qs.Services
import qs.Config
RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level]
property int level
blur: (dp * 5) ** 0.7
color: Qt.alpha(Colors.palette.m3shadow, 0.7)
color: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
offset.y: dp / 2
spread: -dp * 0.3 + (dp * 0.1) ** 2
+44
View File
@@ -0,0 +1,44 @@
import qs.Config
import QtQuick
CustomRect {
required property int extra
anchors.margins: 8
anchors.right: parent.right
color: DynamicColors.palette.m3tertiary
implicitHeight: count.implicitHeight + 4 * 2
implicitWidth: count.implicitWidth + 8 * 2
opacity: extra > 0 ? 1 : 0
radius: Appearance.rounding.smallest
scale: extra > 0 ? 1 : 0.5
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
Behavior on scale {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Elevation {
anchors.fill: parent
level: 2
opacity: parent.opacity
radius: parent.radius
z: -1
}
CustomText {
id: count
anchors.centerIn: parent
animate: parent.opacity > 0
color: DynamicColors.palette.m3onTertiary
text: qsTr("+%1").arg(parent.extra)
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import QtQuick
import Quickshell
import ZShell.Config
import qs.Config
Image {
id: root
+1 -1
View File
@@ -1,5 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Config
BaseStyledSlider {
id: root
+1 -1
View File
@@ -1,5 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Config
BaseStyledSlider {
id: root
+33
View File
@@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Controls
import qs.Config
IconButton {
id: root
required property bool shouldBeVisible
opacity: 0
scale: 0
visible: root.scale > 0
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
Behavior on scale {
Anim {
}
}
onShouldBeVisibleChanged: {
if (root.shouldBeVisible) {
root.opacity = 1;
root.scale = 1;
} else {
root.opacity = 0;
root.scale = 0;
}
}
}
+8 -9
View File
@@ -1,6 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Config
ButtonBase {
id: root
@@ -8,8 +7,8 @@ ButtonBase {
property alias icon: label.text
readonly property alias label: label
activeColor: type === IconButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondary
activeOnColor: type === IconButton.Filled ? Colors.palette.m3onPrimary : type === IconButton.Tonal ? Colors.palette.m3onSecondary : Colors.palette.m3primary
activeColor: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
implicitHeight: {
const h = label.implicitHeight + padding * 2;
if (h % 2 !== 0)
@@ -19,15 +18,15 @@ ButtonBase {
implicitWidth: implicitHeight
inactiveColor: {
if (!isToggle && type === IconButton.Filled)
return Colors.palette.m3primary;
return type === IconButton.Filled ? Colors.tPalette.m3surfaceContainer : Colors.palette.m3secondaryContainer;
return DynamicColors.palette.m3primary;
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === IconButton.Filled)
return Colors.palette.m3onPrimary;
return type === IconButton.Tonal ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onSurfaceVariant;
return DynamicColors.palette.m3onPrimary;
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
}
padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.extraSmall
padding: type === IconButton.Text ? Appearance.padding.extraSmall / 2 : Appearance.padding.small
MaterialIcon {
id: label
+12 -13
View File
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Config
ButtonBase {
id: root
@@ -11,34 +10,34 @@ ButtonBase {
readonly property alias label: label
property alias text: label.text
activeColor: type === TextButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondary
activeColor: type === TextButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: {
if (type === TextButton.Text)
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary;
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondary;
}
horizontalPadding: Tokens.padding.small
horizontalPadding: Appearance.padding.larger
implicitHeight: row.implicitHeight + verticalPadding * 2
implicitWidth: row.implicitWidth + horizontalPadding * 2
inactiveColor: {
if (!isToggle && type === TextButton.Filled)
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.tPalette.m3surfaceContainer : Colors.palette.m3secondaryContainer;
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === TextButton.Filled)
return Colors.palette.m3onPrimary;
return DynamicColors.palette.m3onPrimary;
if (type === TextButton.Text)
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer;
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer;
}
verticalPadding: Tokens.padding.extraSmall
verticalPadding: Appearance.padding.small
RowLayout {
id: row
anchors.centerIn: parent
spacing: Tokens.spacing.small
spacing: Appearance.spacing.small
MaterialIcon {
id: iconLabel
-115
View File
@@ -1,115 +0,0 @@
import QtQuick
import Quickshell
import M3Shapes
import ZShell.Config
import qs.Services
MaterialShape {
id: root
property bool animated: true
property real cRotation
property bool containsIcon
property real dampingRatio: 0.6
property real lRotation
property int morphAnimRotation: 60
property real morphScale: 0.14
property alias rotateAnimDuration: rotateAnim.duration
property int shapeIndex
property list<int> shapes: {
if (containsIcon)
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pentagon, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
}
readonly property real springDuration: {
const wn = Math.sqrt(stiffness);
const r = -dampingRatio * wn;
const c = 1 / Math.sqrt(1 - dampingRatio * dampingRatio);
return Math.log(visibilityThreshold / c) / r;
}
readonly property real springMaxVelocity: {
const wn = Math.sqrt(stiffness);
const factor = Math.exp(-z * Math.acos(z) / Math.sqrt(1 - z * z));
return wn * factor;
}
property bool springSettled: true
property real stiffness: 180
property real thisLRotation
property real visibilityThreshold: 0.075
function spring(t: real): var {
const wn = Math.sqrt(stiffness);
const za = dampingRatio * wn;
const wd = wn * Math.sqrt(1 - dampingRatio * dampingRatio);
const r = za / wd;
const pos = 1 - Math.exp(-za * t) * (Math.cos(wd * t) + r * Math.sin(wd * t));
const vel = Math.exp(-za * t) * (wn * wn / wd) * Math.sin(wd * t);
return [pos, vel];
}
color: Colors.palette.m3primary
implicitSize: 38
toShape: shapes[0]
RotationAnimation on cRotation {
id: rotateAnim
duration: 4666
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.animated
to: 360
}
Behavior on color {
CAnim {
}
}
ElapsedTimer {
id: timer
}
FrameAnimation {
running: root.animated && !root.springSettled
onTriggered: {
const t = timer.elapsed();
if (t >= root.springDuration) {
root.springSettled = true;
} else {
const [pos, vel] = root.spring(t);
root.morphProgress = Math.min(1, pos); // Overshooting the morph looks weird
root.thisLRotation = pos * root.morphAnimRotation;
root.scale = 1 + vel * root.morphScale / root.springMaxVelocity;
}
}
}
Timer {
interval: 650
repeat: true
running: root.animated
triggeredOnStart: true
onTriggered: {
root.beginBatchUpdate();
root.fromShape = root.toShape;
root.shapeIndex = (root.shapeIndex + 1) % root.shapes.length;
root.toShape = root.shapes[root.shapeIndex];
root.morphProgress = 0;
root.rotation = root.rotation;
root.lRotation = (root.lRotation + root.thisLRotation) % 360;
root.thisLRotation = 0;
root.rotation = Qt.binding(() => root.cRotation + root.lRotation + root.thisLRotation);
root.springSettled = false;
timer.restart();
root.endBatchUpdate();
}
}
}
+309
View File
@@ -0,0 +1,309 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Config
Item {
id: root
enum Variant {
Outlined,
Filled
}
readonly property color accent: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline))
readonly property real disabledOpacity: 0.38
readonly property string effectiveTrailingIcon: root.trailingIcon.length > 0 ? root.trailingIcon : (root.password ? (field.echoMode === TextInput.Password ? "visibility" : "visibility_off") : (root.isError ? "error" : ""))
property string errorText: ""
readonly property alias field: field
readonly property bool floating: focused || hasContent || prefixText.length > 0
readonly property bool focused: field.activeFocus
readonly property bool hasContent: field.text.length > 0
readonly property color iconColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant))
property int inputMethodHints: Qt.ImhNone
property bool isError: false
property string label: ""
readonly property color labelColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant))
property string leadingIcon: ""
property int maxLength: 0
property color notchColor: DynamicColors.palette.m3surface
readonly property bool outlined: variant === M3TextField.Variant.Outlined
property bool password: false
property string placeholder: ""
property string prefixText: ""
property string suffixText: ""
readonly property color supportColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurfaceVariant)
property string supportingText: ""
property alias text: field.text
property string trailingIcon: ""
readonly property bool trailingInteractive: root.password && root.trailingIcon.length === 0
property var validator: null
property int variant: M3TextField.Variant.Outlined
signal accepted
function forceFieldFocus(): void {
field.forceActiveFocus();
}
implicitHeight: 56 + (supportRow.visible ? supportRow.implicitHeight + Appearance.spacing.extraSmall : 0)
implicitWidth: 240
CustomRect {
id: filledBg
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
color: root.enabled ? (root.focused ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) : DynamicColors.palette.m3surfaceContainerHigh) : DynamicColors.palette.m3onSurface
implicitHeight: 56
opacity: root.enabled ? 1 : 0.04 // M3 filled disabled container @ 4%
topLeftRadius: Appearance.rounding.small
topRightRadius: Appearance.rounding.small
visible: !root.outlined
Behavior on color {
CAnim {
}
}
StateLayer {
anchors.fill: parent
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
enabled: root.enabled
topLeftRadius: Appearance.rounding.small
topRightRadius: Appearance.rounding.small
onClicked: field.forceActiveFocus()
}
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.accent
implicitHeight: root.focused || root.isError ? 2 : 1
opacity: root.enabled ? 1 : root.disabledOpacity
Behavior on color {
CAnim {
}
}
Behavior on implicitHeight {
Anim {
type: Anim.StandardSmall
}
}
}
}
CustomRect {
id: outline
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
border.color: root.accent
border.width: root.focused || root.isError ? 2 : 1
color: "transparent"
implicitHeight: 56
opacity: root.enabled ? 1 : root.disabledOpacity
radius: Appearance.rounding.small
visible: root.outlined
Behavior on border.color {
CAnim {
}
}
Behavior on border.width {
Anim {
type: Anim.StandardSmall
}
}
StateLayer {
anchors.fill: parent
anchors.margins: outline.border.width
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
enabled: root.enabled
radius: Appearance.rounding.small
onClicked: field.forceActiveFocus()
}
}
Rectangle {
color: root.notchColor
height: outline.border.width
opacity: root.enabled ? 1 : root.disabledOpacity
visible: root.outlined && root.floating && root.label.length > 0
width: labelText.width + Appearance.spacing.extraSmall
x: labelText.x - Appearance.spacing.extraSmall / 2
y: 0
}
CustomText {
id: labelText
color: root.labelColor
font.pointSize: root.floating ? Appearance.font.size.small : Appearance.font.size.medium
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.label
visible: root.label.length > 0
x: root.floating ? (Appearance.padding.larger + Appearance.spacing.extraSmall / 2) : (leading.visible ? leading.x + leading.width + Appearance.spacing.small : Appearance.padding.larger)
y: root.floating ? (root.outlined ? -height / 2 : Appearance.padding.extraSmall) : (56 - height) / 2
Behavior on color {
CAnim {
}
}
Behavior on x {
Anim {
type: Anim.StandardSmall
}
}
Behavior on y {
Anim {
type: Anim.StandardSmall
}
}
}
MaterialIcon {
id: leading
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: (56 - height) / 2
color: root.iconColor
font.pointSize: Appearance.font.size.medium
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.leadingIcon
visible: root.leadingIcon.length > 0
Behavior on color {
CAnim {
}
}
}
MaterialIcon {
id: trailing
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: (56 - height) / 2
color: root.isError ? DynamicColors.palette.m3error : (root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface)
font.pointSize: Appearance.font.size.medium
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.effectiveTrailingIcon
visible: root.effectiveTrailingIcon.length > 0
MouseArea {
anchors.fill: parent
anchors.margins: -Appearance.padding.small
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: root.trailingInteractive && root.enabled
onClicked: field.echoMode = field.echoMode === TextInput.Password ? TextInput.Normal : TextInput.Password
}
}
CustomText {
id: prefix
anchors.left: leading.visible ? leading.right : parent.left
anchors.leftMargin: leading.visible ? Appearance.spacing.small : Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface
font.pointSize: Appearance.font.size.medium
height: 56
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.prefixText
verticalAlignment: Text.AlignVCenter
visible: root.prefixText.length > 0 && root.floating
}
CustomText {
id: suffix
anchors.right: trailing.visible ? trailing.left : parent.right
anchors.rightMargin: trailing.visible ? Appearance.spacing.small : Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface
font.pointSize: Appearance.font.size.medium
height: 56
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.suffixText
verticalAlignment: Text.AlignVCenter
visible: root.suffixText.length > 0 && root.floating
}
CustomTextField {
id: field
anchors.left: prefix.visible ? prefix.right : (leading.visible ? leading.right : parent.left)
anchors.leftMargin: prefix.visible ? Appearance.spacing.extraSmall : (leading.visible ? Appearance.spacing.small : Appearance.padding.larger)
anchors.right: suffix.visible ? suffix.left : (trailing.visible ? trailing.left : parent.right)
anchors.rightMargin: suffix.visible ? Appearance.spacing.extraSmall : (trailing.visible ? Appearance.spacing.small : Appearance.padding.larger)
anchors.top: parent.top
color: DynamicColors.palette.m3onSurface
echoMode: root.password ? TextInput.Password : TextInput.Normal
enabled: root.enabled
font.pointSize: Appearance.font.size.medium
height: 56
inputMethodHints: root.inputMethodHints
maximumLength: root.maxLength > 0 ? root.maxLength : 32767
opacity: root.enabled ? 1 : root.disabledOpacity
placeholderText: root.focused ? root.placeholder : ""
topPadding: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
validator: root.validator
verticalAlignment: TextInput.AlignVCenter
onAccepted: root.accepted()
onTextEdited: if (root.isError)
root.isError = false
}
Item {
id: supportRow
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 56 + Appearance.spacing.extraSmall
implicitHeight: Math.max(supportText.implicitHeight, counter.implicitHeight)
visible: (root.isError && root.errorText.length > 0) || root.supportingText.length > 0 || root.maxLength > 0
CustomText {
id: supportText
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.right: counter.visible ? counter.left : parent.right
anchors.rightMargin: Appearance.spacing.small
color: root.supportColor
font.pointSize: Appearance.font.size.small
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.isError && root.errorText.length > 0 ? root.errorText : root.supportingText
wrapMode: Text.WordWrap
}
CustomText {
id: counter
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
color: root.supportColor
font.pointSize: Appearance.font.size.small
opacity: root.enabled ? 1 : root.disabledOpacity
text: `${root.text.length}/${root.maxLength}`
visible: root.maxLength > 0
}
}
}
+132 -151
View File
@@ -1,13 +1,12 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Config
Item {
id: root
property alias anim: marqueeAnim
property bool animate: false
property color color: Colors.palette.m3onSurface
property color color: DynamicColors.palette.m3onSurface
property int fadeStrengthAnimMs: 180
property real fadeStrengthIdle: 0.0
property real fadeStrengthMoving: 1.0
@@ -18,14 +17,13 @@ Item {
property real leftFadeStrength: overflowing && leftFadeEnabled ? fadeStrengthMoving : fadeStrengthIdle
property int leftFadeWidth: 28
property bool marqueeEnabled: true
readonly property bool overflowing: metrics.width > content.width
readonly property bool overflowing: metrics.width > root.width
property int pauseMs: 1200
property real pixelsPerSecond: 40
property real rightFadeStrength: overflowing ? fadeStrengthMoving : fadeStrengthIdle
property int rightFadeWidth: 28
property bool sliding: false
property alias text: elideText.text
property bool vertical: false
function durationForDistance(px): int {
return Math.max(1, Math.round(Math.abs(px) / root.pixelsPerSecond * 1000));
@@ -42,9 +40,8 @@ Item {
}
}
clip: false
implicitHeight: vertical ? elideText.implicitWidth : elideText.implicitHeight
implicitWidth: vertical ? elideText.implicitHeight : elideText.implicitWidth
clip: true
implicitHeight: elideText.implicitHeight
Behavior on leftFadeStrength {
Anim {
@@ -55,187 +52,171 @@ Item {
}
}
onHeightChanged: resetMarquee()
onTextChanged: resetMarquee()
onVerticalChanged: resetMarquee()
onVisibleChanged: if (!visible)
resetMarquee()
onWidthChanged: resetMarquee()
TextMetrics {
id: metrics
font: elideText.font
text: elideText.text
}
CustomText {
id: elideText
anchors.verticalCenter: parent.verticalCenter
animate: root.animate
animateProp: "scale,opacity"
color: root.color
elide: Text.ElideNone
visible: !root.overflowing
width: root.width
}
Item {
id: content
id: marqueeViewport
anchors.centerIn: parent
height: root.vertical ? root.width : root.height
width: root.vertical ? root.height : root.width
anchors.fill: parent
clip: true
layer.enabled: true
visible: root.overflowing
transform: Rotation {
angle: root.vertical ? -90 : 0
origin.x: content.width / 2
origin.y: content.height / 2
}
TextMetrics {
id: metrics
font: elideText.font
text: elideText.text
}
CustomText {
id: elideText
anchors.verticalCenter: parent.verticalCenter
animate: root.animate
animateProp: "scale,opacity"
color: root.color
elide: Text.ElideNone
visible: !root.overflowing
width: content.width
layer.effect: OpacityMask {
maskSource: rightFadeMask
}
Item {
id: marqueeViewport
id: strip
anchors.fill: parent
clip: false
layer.enabled: true
visible: root.overflowing
anchors.verticalCenter: parent.verticalCenter
height: t1.implicitHeight
width: t1.width + root.gap + t2.width
x: 0
layer.effect: OpacityMask {
maskSource: rightFadeMask
CustomText {
id: t1
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text
}
Item {
id: strip
CustomText {
id: t2
anchors.verticalCenter: parent.verticalCenter
height: t1.implicitHeight
width: t1.width + root.gap + t2.width
x: 0
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: t1.text
x: t1.width + root.gap
}
}
CustomText {
id: t1
SequentialAnimation {
id: marqueeAnim
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text
}
running: false
CustomText {
id: t2
onFinished: pauseTimer.restart()
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: t1.text
x: t1.width + root.gap
ScriptAction {
script: {
root.sliding = true;
root.leftFadeEnabled = true;
}
}
SequentialAnimation {
id: marqueeAnim
Anim {
duration: root.durationForDistance(t1.width)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: 0
property: "x"
target: strip
to: -t1.width
}
running: false
onFinished: pauseTimer.restart()
ScriptAction {
script: {
root.sliding = true;
root.leftFadeEnabled = true;
}
}
Anim {
duration: root.durationForDistance(t1.width)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: 0
property: "x"
target: strip
to: -t1.width
}
ScriptAction {
script: {
root.leftFadeEnabled = false;
}
}
Anim {
duration: root.durationForDistance(root.gap)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: -t1.width
property: "x"
target: strip
to: -(t1.width + root.gap)
}
ScriptAction {
script: {
root.sliding = false;
strip.x = 0;
}
ScriptAction {
script: {
root.leftFadeEnabled = false;
}
}
Timer {
id: pauseTimer
Anim {
duration: root.durationForDistance(root.gap)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: -t1.width
property: "x"
target: strip
to: -(t1.width + root.gap)
}
interval: root.pauseMs
repeat: false
running: true
onTriggered: {
if (root.marqueeEnabled)
marqueeAnim.start();
ScriptAction {
script: {
root.sliding = false;
strip.x = 0;
}
}
}
Rectangle {
id: rightFadeMask
Timer {
id: pauseTimer
readonly property real fadeStartPos: {
const w = Math.max(1, width);
return Math.max(0, Math.min(1, (w - root.rightFadeWidth) / w));
interval: root.pauseMs
repeat: false
running: true
onTriggered: {
if (root.marqueeEnabled)
marqueeAnim.start();
}
readonly property real leftFadeEndPos: {
const w = Math.max(1, width);
return Math.max(0, Math.min(1, root.leftFadeWidth / w));
}
}
Rectangle {
id: rightFadeMask
readonly property real fadeStartPos: {
const w = Math.max(1, width);
return Math.max(0, Math.min(1, (w - root.rightFadeWidth) / w));
}
readonly property real leftFadeEndPos: {
const w = Math.max(1, width);
return Math.max(0, Math.min(1, root.leftFadeWidth / w));
}
anchors.fill: marqueeViewport
layer.enabled: true
visible: false
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.leftFadeStrength)
position: 0.0
}
anchors.fill: marqueeViewport
layer.enabled: true
visible: false
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.leftFadeEndPos
}
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.fadeStartPos
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.leftFadeStrength)
position: 0.0
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.leftFadeEndPos
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.fadeStartPos
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.rightFadeStrength)
position: 1.0
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.rightFadeStrength)
position: 1.0
}
}
}
+3 -4
View File
@@ -1,12 +1,11 @@
import ZShell.Config
import qs.Services
import qs.Config
CustomText {
id: root
readonly property list<int> allowed: [20, 24, 40, 48]
property real fill: 0
property int grade: Colors.light ? 0 : -25
property int grade: DynamicColors.light ? 0 : -25
readonly property int opsz: clampOpsz(optical)
property int optical: fontInfo.pixelSize
@@ -17,7 +16,7 @@ CustomText {
}
font.family: "Material Symbols Rounded"
font.pointSize: Tokens.font.size.larger
font.pointSize: Appearance.font.size.larger
font.variableAxes: ({
FILL: fill,
GRAD: grade,
+19 -20
View File
@@ -3,9 +3,8 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import qs.Config
import qs.Drawers
import qs.Services
MouseArea {
id: root
@@ -78,7 +77,7 @@ MouseArea {
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Tokens.rounding.large
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
@@ -108,15 +107,15 @@ MouseArea {
CustomRect {
anchors.fill: parent
color: Colors.palette.m3surfaceContainerLow
color: DynamicColors.palette.m3surfaceContainerLow
radius: parent.radius
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.extraSmall
anchors.margins: Appearance.padding.extraSmall
spacing: Appearance.spacing.extraSmall
Repeater {
id: repeater
@@ -126,15 +125,15 @@ MouseArea {
CustomRect {
id: item
readonly property bool active: modelData === root?.active
readonly property bool active: modelData === root.active
required property int index
required property MenuItem modelData
Layout.fillWidth: true
color: Qt.alpha(Colors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Tokens.padding.medium * 2
implicitWidth: menuOptionRow.implicitWidth + Tokens.padding.medium * 2
radius: menu.radius - column.anchors.margins
color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
Behavior on radius {
Anim {
@@ -142,7 +141,7 @@ MouseArea {
}
StateLayer {
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
enabled: root.expanded
onClicked: {
@@ -157,30 +156,30 @@ MouseArea {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Tokens.padding.medium
spacing: Tokens.spacing.small
anchors.margins: Appearance.padding.larger
spacing: Appearance.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: item.modelData?.icon ?? ""
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
text: item.modelData?.text ?? ""
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text
}
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData?.trailingIcon.length > 0
active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active
sourceComponent: MaterialIcon {
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon
}
}
+76
View File
@@ -0,0 +1,76 @@
import QtQuick
Path {
id: root
required property real viewHeight
required property real viewWidth
startX: root.viewWidth / 2
startY: 0
PathAttribute {
name: "itemOpacity"
value: 0.25
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (1 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.45
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (2 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.70
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (3 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 1.00
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (4 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.70
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (5 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.45
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight
}
PathAttribute {
name: "itemOpacity"
value: 0.25
}
}
+180
View File
@@ -0,0 +1,180 @@
import QtQuick
import QtQuick.Effects
import qs.Config
Elevation {
id: root
required property int currentIndex
property bool expanded
required property int from
property color insideTextColor: DynamicColors.palette.m3onPrimary
property int itemHeight
property int listHeight: 200
property color outsideTextColor: DynamicColors.palette.m3onSurfaceVariant
readonly property var spinnerModel: root.range(root.from, root.to)
required property int to
property Item triggerItem
signal itemSelected(item: int)
function range(first, last) {
let out = [];
for (let i = first; i <= last; ++i)
out.push(i);
return out;
}
implicitHeight: root.expanded ? view.implicitHeight : 0
level: root.expanded ? 2 : 0
radius: itemHeight / 2
visible: implicitHeight > 0
z: root.expanded ? 100 : 0
Behavior on implicitHeight {
Anim {
}
}
onExpandedChanged: {
if (!root.expanded)
root.itemSelected(view.currentIndex + 1);
}
Component {
id: spinnerDelegate
Item {
id: wrapper
readonly property color delegateTextColor: wrapper.PathView.view ? wrapper.PathView.view.delegateTextColor : "white"
required property var modelData
height: root.itemHeight
opacity: wrapper.PathView.itemOpacity
visible: wrapper.PathView.onPath
width: wrapper.PathView.view ? wrapper.PathView.view.width : 0
z: wrapper.PathView.isCurrentItem ? 100 : Math.round(wrapper.PathView.itemScale * 100)
CustomText {
anchors.centerIn: parent
color: wrapper.delegateTextColor
font.pointSize: Appearance.font.size.large
text: wrapper.modelData
}
}
}
CustomClippingRect {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius
z: root.z
// Main visible spinner: normal/outside text color
PathView {
id: view
property color delegateTextColor: root.outsideTextColor
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
currentIndex: root.currentIndex - 1
delegate: spinnerDelegate
dragMargin: width
highlightRangeMode: PathView.StrictlyEnforceRange
implicitHeight: root.listHeight
model: root.spinnerModel
pathItemCount: 7
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
snapMode: PathView.SnapToItem
path: PathMenu {
viewHeight: view.height
viewWidth: view.width
}
}
// The selection rectangle itself
CustomRect {
id: selectionRect
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
height: root.itemHeight
radius: root.itemHeight / 2
width: parent.width
z: 2
}
// Hidden source: same PathView, but with the "inside selection" text color
Item {
id: selectedTextSource
anchors.fill: parent
layer.enabled: true
visible: false
PathView {
id: selectedTextView
property color delegateTextColor: root.insideTextColor
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
currentIndex: view.currentIndex
delegate: spinnerDelegate
dragMargin: view.dragMargin
highlightRangeMode: view.highlightRangeMode
implicitHeight: root.listHeight
interactive: false
model: view.model
// Keep this PathView visually locked to the real one
offset: view.offset
pathItemCount: view.pathItemCount
preferredHighlightBegin: view.preferredHighlightBegin
preferredHighlightEnd: view.preferredHighlightEnd
snapMode: view.snapMode
path: PathMenu {
viewHeight: selectedTextView.height
viewWidth: selectedTextView.width
}
}
}
// Mask matching the selection rectangle
Item {
id: selectionMask
anchors.fill: parent
layer.enabled: true
visible: false
CustomRect {
color: "white"
height: selectionRect.height
radius: selectionRect.radius
width: selectionRect.width
x: selectionRect.x
y: selectionRect.y
}
}
// Only show the "inside selection" text where the mask exists
MultiEffect {
anchors.fill: selectedTextSource
maskEnabled: true
maskInverted: false
maskSource: selectionMask
source: selectedTextSource
z: 3
}
}
}
+12 -13
View File
@@ -1,6 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Config
TextFieldBase {
id: root
@@ -9,17 +8,17 @@ TextFieldBase {
readonly property alias clearIcon: clearIcon
readonly property alias searchIcon: searchIcon
bottomPadding: Tokens.padding.large
leftPadding: searchIcon.width + searchIcon.anchors.leftMargin + Tokens.spacing.small
rightPadding: clearIcon.width + clearIcon.anchors.rightMargin + Tokens.spacing.small
topPadding: Tokens.padding.large
bottomPadding: Appearance.padding.large
leftPadding: searchIcon.width + searchIcon.anchors.leftMargin + Appearance.spacing.small
rightPadding: clearIcon.width + clearIcon.anchors.rightMargin + Appearance.spacing.small
topPadding: Appearance.padding.large
background: CustomRect {
id: bg
anchors.fill: parent
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.full
color: DynamicColors.tPalette.m3surfaceContainer
radius: Appearance.rounding.full
StateLayer {
id: stateLayer
@@ -59,10 +58,10 @@ TextFieldBase {
id: searchIcon
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.large
anchors.leftMargin: Appearance.padding.large
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.large
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: "search"
}
@@ -70,12 +69,12 @@ TextFieldBase {
id: clearIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.rightMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
enabled: root.text
icon: "clear"
opacity: root.text ? 1 : 0
radius: Tokens.rounding.full
radius: Appearance.rounding.full
radiusMorph: false
stateLayer.hoverEnabled: enabled
type: IconButton.Text
+50
View File
@@ -0,0 +1,50 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
CustomRect {
id: root
required property string label
required property real max
required property real min
property var onValueModified: function (value) {}
property real step: 1
required property real value
Layout.fillWidth: true
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
implicitHeight: row.implicitHeight + Appearance.padding.large * 2
radius: Appearance.rounding.normal
Behavior on implicitHeight {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.large
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.normal
CustomText {
Layout.fillWidth: true
text: root.label
}
CustomSpinBox {
max: root.max
min: root.min
step: root.step
value: root.value
onValueModified: value => {
root.onValueModified(value);
}
}
}
}
+6 -6
View File
@@ -1,9 +1,9 @@
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Config
import ZShell.Components
import qs.Helpers
import qs.Services
import qs.Config
MouseArea {
id: root
@@ -52,7 +52,7 @@ MouseArea {
anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled && !Visibilities.getForActive()?.isDrawing
enabled: parent.enabled && !Visibilities.getForActive().isDrawing
hoverEnabled: true
Behavior on stateOpacity {
@@ -79,8 +79,8 @@ MouseArea {
id: rippleAnim
alwaysRunToEnd: true
duration: Tokens.anim.durations.expressiveSlowEffects * 2
easing: Tokens.anim.standard
duration: Appearance.anim.durations.expressiveSlowEffects * 2
easing.bezierCurve: Appearance.anim.curves.standard
property: "circleRadius"
target: root
to: root.endRadius
@@ -101,7 +101,7 @@ MouseArea {
anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
+11 -12
View File
@@ -1,6 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Config
ButtonBase {
id: root
@@ -8,28 +7,28 @@ ButtonBase {
readonly property alias label: label
property alias text: label.text
activeColor: type === TextButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondary
activeColor: type === TextButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: {
if (type === TextButton.Text)
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary;
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondary;
}
horizontalPadding: Tokens.padding.small
horizontalPadding: Appearance.padding.normal
implicitHeight: label.implicitHeight + verticalPadding * 2
implicitWidth: label.implicitWidth + horizontalPadding * 2
inactiveColor: {
if (!isToggle && type === TextButton.Filled)
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.tPalette.m3surfaceContainer : Colors.palette.m3secondaryContainer;
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === TextButton.Filled)
return Colors.palette.m3onPrimary;
return DynamicColors.palette.m3onPrimary;
if (type === TextButton.Text)
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer;
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer;
}
verticalPadding: Tokens.padding.extraSmall
verticalPadding: Appearance.padding.small
CustomText {
id: label
+8 -9
View File
@@ -1,20 +1,19 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
import qs.Config
TextField {
id: root
color: Colors.palette.m3onSurface
color: DynamicColors.palette.m3onSurface
cursorVisible: !readOnly
font.pointSize: Tokens.font.size.small
font.pointSize: Appearance.font.size.small
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
placeholderTextColor: DynamicColors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
selectionColor: Qt.alpha(DynamicColors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
@@ -33,10 +32,10 @@ TextField {
property bool disableBlink
color: Colors.palette.m3primary
color: DynamicColors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
radius: Appearance.rounding.large
x: root.cursorRectangle.x
y: root.cursorRectangle.y
@@ -47,7 +46,7 @@ TextField {
}
Behavior on x {
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
duration: Appearance.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
+35 -36
View File
@@ -2,8 +2,7 @@ import ZShell
import QtQuick
import QtQuick.Layouts
import qs.Components
import ZShell.Config
import qs.Services
import qs.Config
CustomRect {
id: root
@@ -13,27 +12,27 @@ CustomRect {
anchors.left: parent.left
anchors.right: parent.right
border.color: {
let colour = Colors.palette.m3outlineVariant;
let colour = DynamicColors.palette.m3outlineVariant;
if (root.modelData.type === Toast.Success)
colour = Colors.palette.m3success;
colour = DynamicColors.palette.m3success;
if (root.modelData.type === Toast.Warning)
colour = Colors.palette.m3secondaryContainer;
colour = DynamicColors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
colour = Colors.palette.m3error;
colour = DynamicColors.palette.m3error;
return Qt.alpha(colour, 0.3);
}
border.width: 1
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3successContainer;
return DynamicColors.palette.m3successContainer;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3secondary;
return DynamicColors.palette.m3secondary;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3errorContainer;
return Colors.palette.m3surface;
return DynamicColors.palette.m3errorContainer;
return DynamicColors.palette.m3surface;
}
implicitHeight: layout.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium
implicitHeight: layout.implicitHeight + Appearance.padding.smaller * 2
radius: Appearance.rounding.normal
Behavior on border.color {
CAnim {
@@ -52,24 +51,24 @@ CustomRect {
id: layout
anchors.fill: parent
anchors.leftMargin: Tokens.padding.small
anchors.margins: Tokens.padding.small
anchors.rightMargin: Tokens.padding.small
spacing: Tokens.spacing.medium
anchors.leftMargin: Appearance.padding.normal
anchors.margins: Appearance.padding.smaller
anchors.rightMargin: Appearance.padding.normal
spacing: Appearance.spacing.normal
CustomRect {
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3success;
return DynamicColors.palette.m3success;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3secondaryContainer;
return DynamicColors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3error;
return Colors.palette.m3surfaceContainerHigh;
return DynamicColors.palette.m3error;
return DynamicColors.palette.m3surfaceContainerHigh;
}
implicitHeight: icon.implicitHeight + Tokens.padding.small * 2
implicitHeight: icon.implicitHeight + Appearance.padding.smaller * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.medium
radius: Appearance.rounding.normal
MaterialIcon {
id: icon
@@ -77,14 +76,14 @@ CustomRect {
anchors.centerIn: parent
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3onSuccess;
return DynamicColors.palette.m3onSuccess;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3onSecondaryContainer;
return DynamicColors.palette.m3onSecondaryContainer;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3onError;
return Colors.palette.m3onSurfaceVariant;
return DynamicColors.palette.m3onError;
return DynamicColors.palette.m3onSurfaceVariant;
}
font.pointSize: Math.round(Tokens.font.size.large * 1.2)
font.pointSize: Math.round(Appearance.font.size.large * 1.2)
text: root.modelData.icon
}
}
@@ -99,15 +98,15 @@ CustomRect {
Layout.fillWidth: true
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3onSuccessContainer;
return DynamicColors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3onSecondary;
return DynamicColors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3onErrorContainer;
return Colors.palette.m3onSurface;
return DynamicColors.palette.m3onErrorContainer;
return DynamicColors.palette.m3onSurface;
}
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
font.pointSize: Appearance.font.size.normal
text: root.modelData.title
}
@@ -115,12 +114,12 @@ CustomRect {
Layout.fillWidth: true
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3onSuccessContainer;
return DynamicColors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3onSecondary;
return DynamicColors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3onErrorContainer;
return Colors.palette.m3onSurface;
return DynamicColors.palette.m3onErrorContainer;
return DynamicColors.palette.m3onSurface;
}
elide: Text.ElideRight
opacity: 0.8
+7 -7
View File
@@ -4,13 +4,13 @@ import ZShell
import Quickshell
import QtQuick
import qs.Components
import ZShell.Config
import qs.Config
Item {
id: root
property bool flag
readonly property int spacing: Tokens.spacing.small
readonly property int spacing: Appearance.spacing.small
implicitHeight: {
let h = -spacing;
@@ -21,7 +21,7 @@ Item {
}
return h;
}
implicitWidth: Config.utilities.sizes.toastWidth - Tokens.padding.small * 2
implicitWidth: Config.utilities.sizes.toastWidth - Appearance.padding.normal * 2
Repeater {
id: repeater
@@ -81,8 +81,8 @@ Item {
Behavior on anchors.bottomMargin {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity {
@@ -104,8 +104,8 @@ Item {
Anim {
id: initAnim
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
from: 0
properties: "opacity,scale"
target: toast
+13 -43
View File
@@ -6,55 +6,25 @@ import qs.Effects
CustomListView {
id: root
property real endFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property real fadeAmount: 0.1
readonly property bool horizontal: orientation === ListView.Horizontal
property real startFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
function contentSize(): real {
return horizontal ? contentWidth : contentHeight;
}
property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
function fadeShouldBeActive(isStart: bool): bool {
// When content is smaller than flickable size, hide fade when rebound starts.
if (contentSize() + marginStart() + marginEnd() < viewportSize() && rebound.running && ((isStart ? overshootStart() > 0 : overshootStart() < 0))) {
// When content is smaller than flickable size, hide fade when rebound starts
if (contentHeight + topMargin + bottomMargin < height && rebound.running && ((isStart ? verticalOvershoot > 0 : verticalOvershoot < 0)))
return false;
}
if (isStart)
return visibleStart() > 0;
return visibleStart() + visibleRatio() < 1;
return visibleArea.yPosition > 0;
return visibleArea.yPosition + visibleArea.heightRatio < 1;
}
function marginEnd(): real {
return horizontal ? rightMargin : bottomMargin;
}
function marginStart(): real {
return horizontal ? leftMargin : topMargin;
}
function overshootStart(): real {
return horizontal ? horizontalOvershoot : verticalOvershoot;
}
function viewportSize(): real {
return horizontal ? width : height;
}
function visibleRatio(): real {
return horizontal ? visibleArea.widthRatio : visibleArea.heightRatio;
}
function visibleStart(): real {
return horizontal ? visibleArea.xPosition : visibleArea.yPosition;
}
flickableDirection: horizontal ? Flickable.HorizontalFlick : Flickable.VerticalFlick
flickableDirection: Flickable.VerticalFlick
layer.enabled: true
orientation: ListView.Vertical
Behavior on endFadeOpacity {
Behavior on bottomFadeOpacity {
Anim {
type: Anim.SlowEffects
}
@@ -70,10 +40,10 @@ CustomListView {
visible: false
gradient: Gradient {
orientation: root.horizontal ? Gradient.Horizontal : Gradient.Vertical
orientation: Gradient.Vertical
GradientStop {
color: Qt.rgba(0, 0, 0, root.startFadeOpacity)
color: Qt.rgba(0, 0, 0, root.topFadeOpacity)
position: 0
}
@@ -88,13 +58,13 @@ CustomListView {
}
GradientStop {
color: Qt.rgba(0, 0, 0, root.endFadeOpacity)
color: Qt.rgba(0, 0, 0, root.bottomFadeOpacity)
position: 1
}
}
}
}
Behavior on startFadeOpacity {
Behavior on topFadeOpacity {
Anim {
type: Anim.SlowEffects
}
-47
View File
@@ -1,47 +0,0 @@
import QtQuick
import QtQuick.Shapes
import ZShell.Config
import qs.Services
Shape {
id: root
property real amplitude: 3
property color color: Colors.palette.m3surfaceContainer
readonly property real waveHeight: amplitude * 2
property int waves: 4
asynchronous: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: root.color
strokeColor: "transparent"
strokeWidth: 0
Behavior on fillColor {
CAnim {
}
}
PathSvg {
path: {
const w = root.width;
const h = root.height;
const a = root.amplitude;
const n = Math.max(1, root.waves);
const wl = w / n;
const half = wl / 2;
let d = `M 0,${a} `;
for (let i = 0; i < n; ++i) {
const x = i * wl;
d += `Q ${x + half / 2},${-a} ${x + half},${a} `;
d += `Q ${x + half + half / 2},${3 * a} ${x + wl},${a} `;
}
d += `L ${w},${h} L 0,${h} Z`;
return d;
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
import Quickshell.Io
JsonObject {
property Accents accents: Accents {
}
component Accents: JsonObject {
property string primary: "#4080ff"
property string primaryAlt: "#60a0ff"
property string warning: "#ff6b6b"
property string warningAlt: "#ff8787"
}
}
+13
View File
@@ -0,0 +1,13 @@
pragma Singleton
import Quickshell
Singleton {
readonly property AppearanceConf.Anim anim: Config.appearance.anim
readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
}
+116
View File
@@ -0,0 +1,116 @@
import Quickshell.Io
JsonObject {
property Anim anim: Anim {
}
property Deform deform: Deform {
}
property FontStuff font: FontStuff {
}
property Padding padding: Padding {
}
property Rounding rounding: Rounding {
}
property Spacing spacing: Spacing {
}
property Transparency transparency: Transparency {
}
component Anim: JsonObject {
property AnimCurves curves: AnimCurves {
}
property AnimDurations durations: AnimDurations {
}
property real mediaGifSpeedAdjustment: 300
property real sessionGifSpeed: 0.7
}
component AnimCurves: JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
}
component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale
property int large: 600 * scale
property int normal: 400 * scale
property real scale: 1
property int small: 200 * scale
}
component Deform: JsonObject {
property real scale: 1
}
component FontFamily: JsonObject {
property string clock: "Rubik"
property string material: "Material Symbols Rounded"
property string mono: "CaskaydiaCove NF"
property string sans: "Segoe UI Variable Text"
}
component FontSize: JsonObject {
property int extraLarge: 28 * scale
property int large: 18 * scale
property int larger: 16 * scale
property int medium: 14 * scale
property int normal: 13 * scale
property real scale: 1
property int small: 11 * scale
property int smaller: 12 * scale
}
component FontStuff: JsonObject {
property FontFamily family: FontFamily {
}
property FontSize size: FontSize {
}
}
component Padding: JsonObject {
property int extraLarge: 28 * scale
property int extraLargeIncreased: 32 * scale
property int extraSmall: 4 * scale
property int large: 16 * scale
property int largeIncreased: 20 * scale
property int larger: 12 * scale
property int normal: 8 * scale
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
property int smallest: 2 * scale
}
component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale
property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale
property real scale: 1
property int small: 12 * scale
property int smallest: 8 * scale
}
component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale
property int larger: 16 * scale
property int normal: 12 * scale
property real scale: 1
property int small: 8 * scale
property int smaller: 10 * scale
}
component Transparency: JsonObject {
property real base: 0.85
property bool enabled: false
property real layers: 0.4
}
}
+14
View File
@@ -0,0 +1,14 @@
import Quickshell.Io
import qs.Config
JsonObject {
property bool enabled: true
property int wallFadeDuration: MaterialEasing.standardTime
property real alignX: 0.5
property real alignY: 0.5
property real zoom: 1.0
property real sourceClipX: 0
property real sourceClipY: 0
property real sourceClipW: 0
property real sourceClipH: 0
}
+79
View File
@@ -0,0 +1,79 @@
import Quickshell.Io
JsonObject {
property bool autoHide: false
property int border: 8
property list<var> entries: [
{
id: "workspaces",
enabled: true
},
{
id: "media",
enabled: true
},
{
id: "resources",
enabled: true
},
{
id: "updates",
enabled: true
},
{
id: "spacer",
enabled: true
},
{
id: "activeWindow",
enabled: true
},
{
id: "spacer",
enabled: true
},
{
id: "hyprsunset",
enabled: true
},
{
id: "tray",
enabled: true
},
{
id: "clock",
enabled: true
},
{
id: "notifBell",
enabled: true
},
]
property int height: 34
property bool hideWhenNotif: false
property Popouts popouts: Popouts {
}
property int rounding: 8
property int smoothing: 32
property Tray tray: Tray {
}
component Popouts: JsonObject {
property bool activeWindow: true
property bool audio: true
property bool clock: true
property bool network: true
property bool resources: true
property bool tray: true
property bool upower: true
}
component Tray: JsonObject {
property bool showAudio: true
property bool showBluetooth: false
property bool showMicrophone: true
property bool showNetwork: false
property bool showPower: true
property bool showWifi: false
property int trayIconSize: 24
}
}
+15
View File
@@ -0,0 +1,15 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int maxEntriesShown: 10
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int itemHeight: 60
property int minPreviewWidth: 200
property int previewWidth: 500
property int width: 500
}
}
+13
View File
@@ -0,0 +1,13 @@
import Quickshell.Io
JsonObject {
property Presets presets: Presets {
}
property string schemeType: "vibrant"
component Presets: JsonObject {
property string accent: ""
property string name: ""
property string variant: ""
}
}
+186
View File
@@ -0,0 +1,186 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import ZShell
import QtQuick
import qs.Helpers
import qs.Paths
Singleton {
id: root
property alias appearance: adapter.appearance
property alias background: adapter.background
property alias bar: adapter.bar
property alias clipboard: adapter.clipboard
property alias colors: adapter.colors
property alias dashboard: adapter.dashboard
property alias dock: adapter.dock
property alias general: adapter.general
property alias launcher: adapter.launcher
property alias lock: adapter.lock
property bool migratingLegacyBar: false
property alias notifs: adapter.notifs
property alias osd: adapter.osd
property alias overview: adapter.overview
property bool recentlySaved: false
property alias screenshot: adapter.screenshot
property alias services: adapter.services
property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities
function applyLegacyBarConfig(legacy): void {
const tray = legacy.tray ?? {};
const popouts = legacy.popouts ?? {};
bar.autoHide = pick(legacy.autoHide, bar.autoHide);
bar.hideWhenNotif = pick(legacy.hideWhenNotif, bar.hideWhenNotif);
bar.rounding = pick(legacy.rounding, bar.rounding);
bar.border = pick(legacy.border, bar.border);
bar.smoothing = pick(legacy.smoothing, bar.smoothing);
bar.height = pick(legacy.height, bar.height);
bar.tray.trayIconSize = pick(tray.trayIconSize, bar.tray.trayIconSize);
bar.popouts.tray = pick(popouts.tray, bar.popouts.tray);
bar.popouts.audio = pick(popouts.audio, bar.popouts.audio);
bar.popouts.activeWindow = pick(popouts.activeWindow, bar.popouts.activeWindow);
bar.popouts.resources = pick(popouts.resources, bar.popouts.resources);
bar.popouts.clock = pick(popouts.clock, bar.popouts.clock);
bar.popouts.network = pick(popouts.network, bar.popouts.network);
bar.popouts.upower = pick(popouts.upower, bar.popouts.upower);
bar.entries = pick(legacy.entries, bar.entries);
}
function migrateLegacyBarConfig(raw): bool {
if (raw.bar !== undefined || raw.barConfig === undefined)
return false;
migratingLegacyBar = true;
applyLegacyBarConfig(raw.barConfig);
migratingLegacyBar = false;
return true;
}
function pick(value, fallback) {
return value === undefined ? fallback : value;
}
function save(): void {
saveTimer.restart();
recentlySaved = true;
recentSaveCooldown.restart();
}
function saveNoToast(): void {
saveTimer.restart();
}
ElapsedTimer {
id: timer
}
Timer {
id: saveTimer
interval: 500
onTriggered: {
timer.restart();
fileView.writeAdapter();
}
}
Timer {
id: recentSaveCooldown
interval: 2000
onTriggered: {
root.recentlySaved = false;
}
}
FileView {
id: fileView
path: `${Paths.config}/config.json`
watchChanges: true
onAdapterUpdated: {
if (!root.migratingLegacyBar)
root.save();
}
onFileChanged: {
if (!root.recentlySaved) {
timer.restart();
reload();
} else {
reload();
}
}
onLoadFailed: err => {
if (err !== FileViewError.FileNotFound)
Toaster.toast(qsTr("Failed to read config"), FileViewError.toString(err), "settings_alert", Toast.Warning);
}
onLoaded: {
ModeScheduler.checkStartup();
Hyprsunset.checkStartup();
try {
const raw = JSON.parse(text());
const migrated = root.migrateLegacyBarConfig(raw);
if (migrated)
root.save();
const elapsed = timer.elapsedMs();
if (adapter.utilities.toasts.configLoaded && !root.recentlySaved) {
Toaster.toast(qsTr("Config loaded"), qsTr("Config loaded in %1ms").arg(elapsed), "rule_settings");
} else if (adapter.utilities.toasts.configLoaded && root.recentlySaved) {
Toaster.toast(qsTr("Config saved"), qsTr("Config reloaded in %1ms").arg(elapsed), "settings_alert");
}
} catch (e) {
Toaster.toast(qsTr("Failed to load config"), e.message, "settings_alert", Toast.Error);
}
}
onSaveFailed: err => Toaster.toast(qsTr("Failed to save config"), FileViewError.toString(err), "settings_alert", Toast.Error)
JsonAdapter {
id: adapter
property AppearanceConf appearance: AppearanceConf {
}
property BackgroundConfig background: BackgroundConfig {
}
property BarConfig bar: BarConfig {
}
property ClipboardConfig clipboard: ClipboardConfig {
}
property Colors colors: Colors {
}
property DashboardConfig dashboard: DashboardConfig {
}
property DockConfig dock: DockConfig {
}
property General general: General {
}
property Launcher launcher: Launcher {
}
property LockConf lock: LockConf {
}
property NotifConfig notifs: NotifConfig {
}
property Osd osd: Osd {
}
property Overview overview: Overview {
}
property Screenshot screenshot: Screenshot {
}
property Services services: Services {
}
property SidebarConfig sidebar: SidebarConfig {
}
property UtilConfig utilities: UtilConfig {
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int mediaUpdateInterval: 500
property Performance performance: Performance {
}
property int resourceUpdateInterval: 1000
property Sizes sizes: Sizes {
}
component Performance: JsonObject {
property bool enabled: true
property bool showBattery: true
property bool showCpu: true
property bool showGpu: true
property bool showMemory: true
property bool showNetwork: true
property bool showStorage: true
}
component Sizes: JsonObject {
readonly property int dateTimeWidth: 110
readonly property int infoIconSize: 25
readonly property int infoWidth: 200
readonly property int mediaCoverArtSize: 150
readonly property int mediaProgressSweep: 180
readonly property int mediaProgressThickness: 8
readonly property int mediaVisualizerSize: 200
readonly property int mediaWidth: 200
readonly property int resourceProgessThickness: 10
readonly property int resourceSize: 200
readonly property int tabIndicatorHeight: 3
readonly property int tabIndicatorSpacing: 5
readonly property int weatherWidth: 250
}
}
+10
View File
@@ -0,0 +1,10 @@
import Quickshell.Io
JsonObject {
property bool enable: false
property real height: 60
property bool hoverToReveal: true
property list<string> ignoredAppRegexes: []
property list<string> pinnedApps: ["org.kde.dolphin", "kitty",]
property bool pinnedOnStartup: false
}
@@ -6,7 +6,6 @@ import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
import ZShell
import ZShell.Config
import qs.Helpers
import qs.Paths
@@ -69,7 +68,7 @@ Singleton {
}
for (const [name, color] of Object.entries(scheme.colors)) {
const propName = `m3${name}`;
const propName = name.startsWith("term") ? name : `m3${name}`;
if (colors.hasOwnProperty(propName))
colors[propName] = `${color}`;
}
@@ -113,6 +112,7 @@ Singleton {
function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.general.color.mode = mode;
Config.save();
}
function swapRG(c: color): color {
@@ -280,9 +280,9 @@ Singleton {
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
}
component Transparency: QtObject {
readonly property real base: Math.max(0, Math.min(1, Config.appearance.transparency.base - (root.light ? 0.1 : 0)))
readonly property bool enabled: Config.appearance.transparency.enabled
readonly property real layers: Config.appearance.transparency.layers
readonly property real base: Math.max(0, Math.min(1, Appearance.transparency.base - (root.light ? 0.1 : 0)))
readonly property bool enabled: Appearance.transparency.enabled
readonly property real layers: Appearance.transparency.layers
onBaseChanged: debounceTimer.restart()
onEnabledChanged: debounceTimer.restart()
+68
View File
@@ -0,0 +1,68 @@
import Quickshell.Io
import Quickshell
JsonObject {
property Apps apps: Apps {
}
property Battery battery: Battery {
}
property Color color: Color {
}
property string dateFormat: "ddd d MMM - hh:mm:ss"
property bool desktopIcons: false
property Idle idle: Idle {
}
property string logo: ""
property bool showOverFullscreen: true
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
component Apps: JsonObject {
property list<string> archiver: ["ark"]
property list<string> audio: ["pavucontrol"]
property list<string> document: ["libreoffice"]
property list<string> editor: ["kate"]
property list<string> explorer: ["dolphin"]
property list<string> image: ["imv"]
property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"]
}
component Battery: JsonObject {
property int critPerc: 5
property list<var> popupThresholds: [
{
perc: 20,
name: qsTr("Low battery"),
message: qsTr("Battery is low"),
icon: "battery_android_frame_2"
},
]
}
component Color: JsonObject {
property int hyprsunsetTemp: 5000
property string mode: "dark"
property bool neovimColors: false
property bool scheduleDark: false
property int scheduleDarkEnd: 0
property int scheduleDarkStart: 0
property bool scheduleHyprsunset: false
property int scheduleHyprsunsetEnd: 0
property int scheduleHyprsunsetStart: 0
property bool schemeGeneration: true
property bool smart: false
}
component Idle: JsonObject {
property list<var> timeouts: [
{
name: "Lock",
timeout: 180,
idleAction: "lock"
},
{
name: "Screen",
timeout: 300,
idleAction: "dpms off",
activeAction: "dpms on"
}
]
}
}
+15
View File
@@ -0,0 +1,15 @@
import Quickshell.Io
JsonObject {
property list<var> timeouts: [
{
timeout: 180,
idleAction: "lock"
},
{
timeout: 300,
idleAction: "dpms off",
activeAction: "dpms on"
}
]
}
+114
View File
@@ -0,0 +1,114 @@
import Quickshell.Io
JsonObject {
property string actionPrefix: ">"
property list<var> actions: [
{
name: "Calculator",
icon: "calculate",
description: "Do simple math equations",
command: ["autocomplete", "calc"],
enabled: true,
dangerous: false
},
{
name: "Light",
icon: "light_mode",
description: "Change to light mode",
command: ["setMode", "light"],
enabled: true,
dangerous: false
},
{
name: "Dark",
icon: "dark_mode",
description: "Change to dark mode",
command: ["setMode", "dark"],
enabled: true,
dangerous: false
},
{
name: "Wallpaper",
icon: "image",
description: "Change the current wallpaper",
command: ["autocomplete", "wallpaper"],
enabled: true,
dangerous: false
},
{
name: "Variant",
icon: "colors",
description: "Change the current scheme variant",
command: ["autocomplete", "variant"],
enabled: true,
dangerous: false
},
{
name: "Shutdown",
icon: "power_settings_new",
description: "Shutdown the system",
command: ["systemctl", "poweroff"],
enabled: true,
dangerous: true
},
{
name: "Reboot",
icon: "cached",
description: "Reboot the system",
command: ["systemctl", "reboot"],
enabled: true,
dangerous: true
},
{
name: "Logout",
icon: "logout",
description: "Log out of the current session",
command: ["loginctl", "terminate-user", ""],
enabled: true,
dangerous: true
},
{
name: "Lock",
icon: "lock",
description: "Lock the current session",
command: ["loginctl", "lock-session"],
enabled: true,
dangerous: false
},
{
name: "Sleep",
icon: "bedtime",
description: "Suspend then hibernate",
command: ["systemctl", "suspend-then-hibernate"],
enabled: true,
dangerous: false
},
]
property int dragThreshold: 50
property bool enableDangerousActions: false
property bool enabled: true
property list<string> favoriteApps: []
property list<string> hiddenApps: []
property int maxAppsShown: 10
property int maxWallpapers: 7
property Sizes sizes: Sizes {
}
property string specialPrefix: "@"
property UseFuzzy useFuzzy: UseFuzzy {
}
property bool uwsm: true
component Sizes: JsonObject {
property int itemHeight: 50
property int itemWidth: 600
property int wallpaperHeight: 200
property int wallpaperWidth: 280
}
component UseFuzzy: JsonObject {
property bool actions: false
property bool apps: false
property bool schemes: false
property bool variants: false
property bool wallpapers: false
}
}
+18
View File
@@ -0,0 +1,18 @@
import Quickshell.Io
JsonObject {
property int blurAmount: 40
property bool enableFprint: true
property int maxFprintTries: 3
property bool recolorLogo: false
property bool showNotifContent: false
property bool showNotifIcon: true
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int centerWidth: 600
property real heightMult: 0.7
property real ratio: 16 / 9
}
}
+26
View File
@@ -0,0 +1,26 @@
pragma Singleton
import Quickshell
Singleton {
id: root
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property int emphasizedAccelTime: 200 * scale
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
readonly property int emphasizedDecelTime: 400 * scale
readonly property int emphasizedTime: 500 * scale
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1]
readonly property int expressiveDefaultSpatialTime: 500 * scale
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1]
readonly property int expressiveEffectsTime: 200 * scale
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1]
readonly property int expressiveFastSpatialTime: 350 * scale
property real scale: Appearance.anim.durations.scale
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
readonly property int standardAccelTime: 200 * scale
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
readonly property int standardDecelTime: 250 * scale
readonly property int standardTime: 300 * scale
}
+21
View File
@@ -0,0 +1,21 @@
import Quickshell.Io
JsonObject {
property bool actionOnClick: false
property int appNotifCooldown: 0
property real clearThreshold: 0.3
property int defaultExpireTimeout: 5000
property int expandThreshold: 20
property bool expire: true
property int groupPreviewNum: 3
property bool openExpanded: false
property bool showInFullscreen: true
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int badge: 20
property int image: 41
property int width: 400
}
}
+16
View File
@@ -0,0 +1,16 @@
import Quickshell.Io
JsonObject {
property bool allMonBrightness: false
property bool enableBrightness: true
property bool enableMicrophone: true
property bool enabled: true
property int hideDelay: 3000
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int sliderHeight: 150
property int sliderWidth: 30
}
}
+8
View File
@@ -0,0 +1,8 @@
import Quickshell.Io
JsonObject {
property int columns: 5
property bool enable: false
property int rows: 2
property real scale: 0.16
}
+13
View File
@@ -0,0 +1,13 @@
import Quickshell.Io
JsonObject {
property bool enable_pp: true
property string mode: "manual"
property real radius: 12.0
property bool rounding: false
property bool shadow: true
property real shadow_blur: 22.0
property list<int> shadow_color: [0, 0, 0, 160]
property real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0
}
+23
View File
@@ -0,0 +1,23 @@
import Quickshell.Io
import QtQuick
JsonObject {
property real audioIncrement: 0.1
property real brightnessIncrement: 0.1
property bool ddcutilService: false
property string defaultPlayer: "Spotify"
property string gpuType: ""
property real maxVolume: 1.0
property real minBrightness: 0.01
property list<var> playerAliases: [
{
"from": "com.github.th_ch.youtube_music",
"to": "YT Music"
}
]
property bool updates: true
property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property int visualizerBars: 30
property string weatherLocation: ""
}
+12
View File
@@ -0,0 +1,12 @@
import Quickshell.Io
JsonObject {
property int dragThreshold: 30
property bool enabled: true
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int width: 430
}
}
+7
View File
@@ -0,0 +1,7 @@
import Quickshell.Io
JsonObject {
property real base: 0.85
property bool enabled: false
property real layers: 0.4
}
+35
View File
@@ -0,0 +1,35 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int maxToasts: 4
property Sizes sizes: Sizes {
}
property Toasts toasts: Toasts {
}
property Vpn vpn: Vpn {
}
component Sizes: JsonObject {
property int toastWidth: 430
property int width: 430
}
component Toasts: JsonObject {
property bool audioInputChanged: true
property bool audioOutputChanged: true
property bool capsLockChanged: true
property bool chargingChanged: true
property bool configLoaded: true
property bool dndChanged: true
property bool gameModeChanged: true
property bool kbLayoutChanged: true
property bool kbLimit: true
property bool nowPlaying: false
property bool numLockChanged: true
property bool vpnChanged: true
}
component Vpn: JsonObject {
property bool enabled: false
property list<var> provider: ["netbird"]
}
}
+6
View File
@@ -0,0 +1,6 @@
import Quickshell.Io
JsonObject {
property string inactiveTextColor: "white"
property string textColor: "black"
}
+1 -1
View File
@@ -5,7 +5,7 @@ import ZShell
import Quickshell
import Quickshell.Services.Pipewire
import QtQuick
import ZShell.Config
import qs.Config
Singleton {
id: root
+1 -1
View File
@@ -2,7 +2,7 @@ import Quickshell
import Quickshell.Services.UPower
import QtQuick
import ZShell
import ZShell.Config
import qs.Config
import qs.Components.Toast
import qs.Helpers
+1 -1
View File
@@ -11,7 +11,7 @@ import qs.Components
import qs.Modules
import qs.Helpers
import qs.Paths
import ZShell.Config
import qs.Config
Singleton {
id: root
+2 -33
View File
@@ -11,46 +11,15 @@ Variants {
required property ShellScreen modelData
function rebuild(): void {
loader.active = false;
loader.active = true;
}
LazyLoader {
id: loader
active: true
Drawer {
screen: scope.modelData
}
}
Connections {
function onConfigPositionChanged(): void {
Qt.callLater(scope.rebuild);
}
target: (loader.item as Drawer)?.geometry ?? null
}
}
component Drawer: Scope {
id: drawer
readonly property alias geometry: content.geometry
required property ShellScreen screen
Exclusions {
bar: content.bar
geometry: content.geometry
screen: drawer.screen
screen: scope.modelData
}
Windows {
id: content
screen: drawer.screen
screen: scope.modelData
}
}
}
-55
View File
@@ -1,55 +0,0 @@
import QtQuick
import qs.Modules.Bar as Bar
QtObject {
id: root
required property Bar.BarLoader bar
readonly property real barClamped: bar.clampedExtent
readonly property real barExtent: bar.extent
readonly property bool barOnBottom: position === "bottom"
readonly property bool barOnLeft: position === "left"
readonly property bool barOnTop: position === "top"
required property string configPosition
readonly property bool horizontal: position !== "left"
readonly property string position: configPosition === "top" || configPosition === "bottom" ? configPosition : "left"
required property var win
function axisPos(x: real, y: real): real {
return horizontal ? x : y;
}
function barContains(x: real, y: real, clamped = false): bool {
const extent = clamped ? barClamped : barExtent;
if (barOnTop)
return y < extent;
if (barOnBottom)
return y > win.height - extent;
return x < extent;
}
function insetBottom(border: real, clamped = false): real {
return barOnBottom ? (clamped ? barClamped : barExtent) : border;
}
function insetLeft(border: real, clamped = false): real {
return barOnLeft ? (clamped ? barClamped : barExtent) : border;
}
function insetTop(border: real, clamped = false): real {
return barOnTop ? (clamped ? barClamped : barExtent) : border;
}
function inwardDrag(dragX: real, dragY: real): real {
if (barOnTop)
return dragY;
if (barOnBottom)
return -dragY;
return dragX;
}
onConfigPositionChanged: {
if (!["left", "top", "bottom"].includes(configPosition))
console.warn(`Invalid bar position '${configPosition}', falling back to left`);
}
}
+3 -12
View File
@@ -1,33 +1,27 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import QtQuick
import ZShell.Config
import qs.Config
import qs.Components
Scope {
id: root
required property Item bar
required property EdgeGeometry geometry
required property ShellScreen screen
ExclusionZone {
id: top
WlrLayershell.layer: WlrLayer.Overlay
anchors.left: true
anchors.right: true
anchors.top: true
hasBar: root.geometry.barOnTop
exclusiveZone: root.bar.exclusiveZone
}
ExclusionZone {
id: left
anchors.left: true
hasBar: root.geometry.barOnLeft
}
ExclusionZone {
@@ -40,7 +34,6 @@ Scope {
id: bottom
anchors.bottom: true
hasBar: root.geometry.barOnBottom
}
Timer {
@@ -51,9 +44,7 @@ Scope {
}
component ExclusionZone: CustomWindow {
property bool hasBar
exclusiveZone: hasBar ? root.bar.exclusiveZone : Config.bar.border
exclusiveZone: Config.bar.border
implicitHeight: 1
implicitWidth: 1
name: "Bar-Exclusion"
+38 -45
View File
@@ -1,9 +1,9 @@
import Quickshell
import QtQuick
import qs.Components
import ZShell.Config
import qs.Config
import qs.Helpers
import qs.Modules.Bar.Popouts
import qs.Modules as BarPopouts
Item {
id: root
@@ -12,47 +12,43 @@ Item {
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 BarPopouts.Wrapper popouts
required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
required property PersistentProperties visibilities
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);
return y > root.height - panel.height - Config.bar.border && 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);
return x < panel.x + panel.width + Config.bar.border && withinPanelHeight(panel, x, y);
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
return x > panel.x - Config.bar.border && 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);
return y < bar.implicitHeight + panel.height && withinPanelWidth(panel, x, y);
}
function onWheel(event: WheelEvent): void {
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness);
const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height;
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + geometry.insetLeft(borderThickness);
const panelX = panel.x + root.borderThickness;
return x >= panelX && x <= panelX + panel.width;
}
@@ -85,7 +81,7 @@ Item {
if (root.singleGestureTriggered)
return;
if (root.geometry.insetTop(root.borderThickness) > centroid.pressPosition.y) {
if (centroid.pressPosition.y < root.bar.implicitHeight) {
if (dragY > 20) {
root.visibilities.settings = true;
root.singleGestureTriggered = true;
@@ -95,10 +91,10 @@ Item {
}
}
if (centroid.pressPosition.y > root.screen.height - root.geometry.insetBottom(root.borderThickness) && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
if (centroid.pressPosition.y > root.screen.height - Config.bar.border && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
root.visibilities.clipboard = true;
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.geometry.insetTop(root.borderThickness) && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher)
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher)
if (dragY < -10) {
root.visibilities.dock = true;
root.singleGestureTriggered = true;
@@ -162,19 +158,6 @@ Item {
}
}
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (hoverHandler.hovered)
root.bar.isHovered = true;
}
}
HoverHandler {
id: hoverHandler
@@ -187,11 +170,11 @@ Item {
root.panels.osd.hovered = false;
}
if (!root.popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
if (!root.popouts.currentName.startsWith("traymenu")) {
root.popouts.hasCurrent = false;
}
if (Config.bar.autoHide || root.bar.fullscreen)
if (Config.bar.autoHide)
root.bar.isHovered = false;
}
}
@@ -210,13 +193,10 @@ Item {
return;
}
if (!root.visibilities.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true))
if (!root.visibilities.bar && Config.bar.autoHide && y < root.bar.implicitHeight)
root.bar.isHovered = true;
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (root.panels.sidebar.offsetScale === 1) {
if (root.panels.sidebar.width === 0) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
if (showOsd) {
@@ -224,7 +204,7 @@ Item {
root.panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < root.width - root.panels.sidebar.width * (1 - root.panels.sidebar.offsetScale);
const outOfSidebar = x < root.width - root.panels.sidebar.width;
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!root.osdShortcutActive) {
@@ -239,8 +219,8 @@ Item {
if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.visibilities.dock = true;
if (root.geometry.barContains(x, y))
root.bar.checkPopout(root.geometry.axisPos(x, y));
if (y < root.bar.implicitHeight)
root.bar.checkPopout(x);
}
}
@@ -318,6 +298,8 @@ Item {
root.visibilities.sidebar = false;
root.panels.popouts.hasCurrent = false;
root.visibilities.launcher = false;
} else {
Config.save();
}
}
@@ -329,6 +311,17 @@ Item {
}
}
function onUtilitiesChanged() {
if (root.visibilities.utilities) {
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY);
if (!inUtilitiesArea) {
root.utilitiesShortcutActive = true;
}
} else {
root.utilitiesShortcutActive = false;
}
}
target: root.visibilities
}
}
+27 -26
View File
@@ -1,7 +1,7 @@
import Quickshell
import QtQuick
import qs.Components
import qs.Modules.Bar.Popouts as Popouts
import qs.Modules as Modules
import qs.Modules.Notifications as Notifications
import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Notifications.Sidebar.Utils as Utils
@@ -14,7 +14,7 @@ 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
import qs.Config
Item {
id: root
@@ -27,7 +27,6 @@ Item {
readonly property alias dock: dock
readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
@@ -44,26 +43,24 @@ Item {
readonly property alias utilities: utilities
required property PersistentProperties visibilities
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness)
anchors.margins: borderThickness
anchors.topMargin: geometry.insetTop(borderThickness)
anchors.topMargin: bar.implicitHeight
Item {
id: resourcesWrapper
anchors.bottom: root.geometry.position === "bottom" ? parent.bottom : undefined
anchors.left: parent.left
anchors.top: root.geometry.position !== "bottom" ? parent.top : undefined
anchors.top: parent.top
clip: true
implicitHeight: root.geometry.horizontal ? resources.implicitHeight * (1 - resources.offsetScale) : resources.implicitHeight
implicitWidth: root.geometry.horizontal ? resources.implicitWidth : resources.implicitWidth * (1 - resources.offsetScale)
implicitHeight: resources.implicitHeight * (1 - resources.offsetScale)
implicitWidth: resources.implicitWidth
Resources.Wrapper {
id: resources
position: root.geometry.position
anchors.left: parent.left
anchors.top: parent.top
visibilities: root.visibilities
}
}
@@ -99,11 +96,11 @@ Item {
visibilities: root.visibilities
}
Popouts.ClipWrapper {
Modules.ClipWrapper {
id: popouts
anchors.top: parent.top
borderThickness: root.borderThickness
position: root.geometry.position
screen: root.screen
}
@@ -111,7 +108,7 @@ Item {
id: toasts
anchors.bottom: sidebar.visible ? parent.bottom : utilities.top
anchors.margins: Tokens.padding.small
anchors.margins: Appearance.padding.normal
anchors.right: sidebar.left
}
@@ -150,26 +147,26 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.bottom: root.geometry.barOnLeft || root.geometry.barOnBottom ? parent.bottom : undefined
anchors.left: root.geometry.barOnLeft ? parent.left : undefined
anchors.right: root.geometry.barOnLeft ? undefined : parent.right
anchors.top: root.geometry.barOnBottom || root.geometry.barOnLeft ? undefined : parent.top
anchors.right: parent.right
anchors.top: parent.top
clip: true
implicitHeight: root.geometry.horizontal ? dashboard.implicitHeight * (1 - offsetScale) : dashboard.implicitHeight
implicitWidth: root.geometry.horizontal ? dashboard.implicitWidth : dashboard.implicitWidth * (1 - offsetScale)
implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
implicitWidth: dashboard.implicitWidth
Behavior on offsetScale {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Dashboard.Wrapper {
id: dashboard
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: (-implicitHeight - 5) * offsetScale
offsetScale: dashboardWrapper.offsetScale
position: root.geometry.position
visibilities: root.visibilities
}
}
@@ -187,14 +184,18 @@ Item {
Item {
id: settingsWrapper
anchors.fill: parent
clip: true
implicitHeight: settings.implicitHeight
implicitWidth: settings.implicitWidth
x: (root.width - settings.implicitWidth) / 2
y: (settings.implicitHeight + (root.height - root.bar.implicitHeight - settings.implicitHeight) / 2) * (1 - settings.offsetScale) - settings.implicitHeight - 5
Settings.Wrapper {
id: settings
anchors.centerIn: parent
anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
// anchors.centerIn: parent
panels: root
screen: root.screen
visibilities: root.visibilities
+10 -14
View File
@@ -7,18 +7,17 @@ import qs.Modules.Bar as Bar
Region {
id: root
// required property Bar.BarLoader bar
required property Bar.BarLoader bar
readonly property real borderThickness: win.borderThickness
required property EdgeGeometry geometry
readonly property alias menuPopoutRegion: menuPopoutRegion
required property Panels panels
required property var win
height: win.height - geometry.insetTop(borderThickness, true) - geometry.insetBottom(borderThickness, true) - win.dragMaskPadding * 2
height: win.height - bar.implicitHeight - win.borderThickness - win.dragMaskPadding * 2
intersection: Intersection.Xor
width: win.width - geometry.insetLeft(borderThickness, true) * 2 - win.dragMaskPadding * 2
x: geometry.insetLeft(borderThickness, true) + win.dragMaskPadding
y: geometry.insetTop(borderThickness, true) + win.dragMaskPadding
width: win.width - win.borderThickness * 2 - win.dragMaskPadding * 2
x: win.borderThickness + win.dragMaskPadding
y: bar.implicitHeight + win.dragMaskPadding
R {
panel: root.panels.dashboardWrapper
@@ -39,24 +38,21 @@ Region {
R {
panel: root.panels.osdWrapper
width: panel.width * (1 - root.panels.osd.offsetScale) + root.borderThickness
x: root.win.width - width
}
R {
height: panel.height + root.geometry.insetTop(root.borderThickness)
panel: root.panels.notifications
y: 0
}
R {
height: panel.height * (1 - root.panels.utilities.offsetScale) + root.geometry.insetBottom(root.borderThickness)
height: panel.height * (1 - root.panels.utilities.offsetScale) + root.borderThickness
panel: root.panels.utilities
y: root.win.height - height
}
R {
height: root.geometry.horizontal ? panel.height * (1 - root.panels.popoutsWrapper.offsetScale) : panel.height
panel: root.panels.popoutsWrapper
width: root.geometry.horizontal ? panel.width : panel.width * (1 - root.panels.popoutsWrapper.offsetScale)
}
R {
@@ -64,7 +60,7 @@ Region {
}
R {
panel: root.panels.settings
panel: root.panels.settingsWrapper
}
R {
@@ -87,7 +83,7 @@ Region {
height: panel.height
intersection: Intersection.Subtract
width: panel.width
x: panel.x + root.geometry.insetLeft(root.borderThickness)
y: panel.y + root.geometry.insetTop(root.borderThickness)
x: panel.x + root.borderThickness
y: panel.y + root.bar.implicitHeight
}
}
+57 -65
View File
@@ -1,7 +1,6 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Io
@@ -11,10 +10,9 @@ import ZShell.Blobs
import qs.Daemons
import qs.Components
import qs.Modules.Bar
import ZShell.Config
import qs.Config
import qs.Helpers
import qs.Drawers
import qs.Services
CustomWindow {
id: root
@@ -27,13 +25,12 @@ CustomWindow {
if (focusGrab.active)
return 0;
if (monitor?.lastIpcObject.specialWorkspace?.name || monitor?.activeWorkspace?.lastIpcObject.windows > 0)
if (monitor?.lastIpcObject.specialWorkspace?.name || monitor?.activeWorkspace.lastIpcObject.windows > 0)
return 0;
return 100;
}
property real fsTransitionProg: hasFullscreen ? 1 : 0
readonly property alias geometry: geometry
readonly property bool hasFullscreen: {
if (hasSpecialWorkspace) {
const specialName = monitor?.lastIpcObject.specialWorkspace?.name;
@@ -51,15 +48,15 @@ CustomWindow {
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
property var root: Quickshell.shellDir
readonly property real sdfBorderOffset: 2 * fsTransitionProg
readonly property real shadowOpacity: 0.7 * (1 - (bar.isHovered ? 0 : fsTransitionProg))
property color surfaceColor: Colors.tPalette.m3surface
readonly property real shadowOpacity: 0.7 * (1 - fsTransitionProg)
property color surfaceColor: DynamicColors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.settings ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent"
contentItem.focus: true
mask: visibilities.isDrawing ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
mask: visibilities.isDrawing ? null : (hasFullscreen ? emptyRegion : regions)
name: "Bar"
Behavior on fsTransitionProg {
@@ -96,21 +93,13 @@ CustomWindow {
panels.popouts.hasCurrent = false;
}
EdgeGeometry {
id: geometry
bar: bar
configPosition: Config.bar.position
win: root
}
Region {
id: emptyRegion
height: panels.notifications.height
width: panels.notifications.width
x: panels.notifications.x + geometry.insetLeft(root.borderThickness)
y: panels.notifications.y + geometry.insetTop(root.borderThickness)
x: panels.notifications.x + root.borderThickness
y: panels.notifications.y + bar.implicitHeight
Region {
height: panels.osd.height
@@ -118,18 +107,12 @@ CustomWindow {
x: root.width - width
y: panels.osdWrapper.y + bar.implicitHeight
}
Region {
height: geometry.horizontal ? 1 : root.screen.height
width: geometry.horizontal ? root.screen.width : 1
y: geometry.position === "bottom" ? root.screen.height - 1 : 0
}
}
Regions {
id: regions
geometry: geometry
bar: bar
panels: panels
win: root
}
@@ -144,7 +127,7 @@ CustomWindow {
HyprlandFocusGrab {
id: focusGrab
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && (!Config.bar.tray.showOnHover || (panels.popouts.current as StackView)?.depth > 1))
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [root]
onCleared: {
@@ -178,6 +161,14 @@ CustomWindow {
Component.onCompleted: Visibilities.load(root.screen, this)
}
IpcHandler {
function toggleLauncher(fix: string): void {
visibilities.launcher = !visibilities.launcher;
}
target: "visibilities"
}
Binding {
property: "bar"
target: visibilities
@@ -194,7 +185,7 @@ CustomWindow {
layer.effect: MultiEffect {
blurMax: 32
shadowColor: Qt.alpha(Colors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowEnabled: true
}
@@ -208,10 +199,10 @@ CustomWindow {
BlobInvertedRect {
anchors.fill: parent
anchors.margins: -50
borderBottom: geometry.insetBottom(root.borderThickness) - anchors.margins - root.sdfBorderOffset
borderLeft: geometry.insetLeft(root.borderThickness) - anchors.margins - root.sdfBorderOffset
borderBottom: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderLeft: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderTop: geometry.insetTop(root.borderThickness) - anchors.margins - root.sdfBorderOffset
borderTop: bar.implicitHeight - anchors.margins - root.sdfBorderOffset
group: blobGroup
radius: root.borderRounding
}
@@ -219,15 +210,15 @@ CustomWindow {
PanelBg {
id: dashBg
property real extraExtent: 0.2
property real extraHeight: 0.2
deformAmount: 0.06
implicitHeight: panels.dashboard.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.dashboard.width * (geometry.horizontal ? 1 : 1 + extraExtent)
implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper
radius: Tokens.rounding.extraLarge
x: panels.dashboardWrapper.x + panels.dashboard.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.dashboard.width * extraExtent)
y: panels.dashboardWrapper.y + panels.dashboard.y + geometry.insetTop(root.borderThickness) - (geometry.horizontal && geometry.position !== "bottom" ? panels.dashboard.height * extraExtent : 0)
radius: Appearance.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + root.borderThickness
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
}
PanelBg {
@@ -238,8 +229,8 @@ CustomWindow {
deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher
radius: Tokens.rounding.medium + (Tokens.padding.largeIncreased / 4)
y: panels.launcher.y + geometry.insetTop(root.borderThickness)
radius: Appearance.rounding.smallest + 5
y: panels.launcher.y + bar.implicitHeight
}
PanelBg {
@@ -260,14 +251,15 @@ CustomWindow {
implicitWidth: panels.osd.width
panel: panels.osdWrapper
radius: 20
x: panels.osdWrapper.x + panels.osd.x + geometry.insetLeft(root.borderThickness)
x: panels.osdWrapper.x + panels.osd.x + root.borderThickness
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
}
PanelBg {
id: notifsBg
panel: panels.notifications
radius: Tokens.rounding.large
radius: Appearance.rounding.normal
}
PanelBg {
@@ -282,17 +274,17 @@ CustomWindow {
PanelBg {
id: popoutBg
property real extraExtent: 0.2
property real extraHeight: 0.2
deformAmount: panels.popouts.currentName.startsWith("traymenu") ? 0.15 : 0.08
implicitHeight: panels.popouts.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.popouts.width * (geometry.horizontal ? 1 : 1 + extraExtent)
implicitHeight: panels.popouts.height * (1 + extraHeight)
implicitWidth: panels.popouts.width
panel: panels.popoutsWrapper
radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.large
x: panels.popoutsWrapper.x + panels.popouts.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.popouts.width * extraExtent)
y: panels.popoutsWrapper.y + panels.popouts.y + geometry.insetTop(root.borderThickness) - (geometry.barOnTop ? panels.popouts.height * extraExtent : 0)
radius: panels.popouts.current?.panelRadius ?? Appearance.rounding.normal
x: panels.popoutsWrapper.x + panels.popouts.x + root.borderThickness
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
Behavior on extraExtent {
Behavior on extraHeight {
Anim {
}
}
@@ -305,17 +297,23 @@ CustomWindow {
implicitHeight: panels.resources.height
implicitWidth: panels.resources.width
panel: panels.resourcesWrapper
radius: Tokens.rounding.largeIncreased + Tokens.padding.small
x: panels.resourcesWrapper.x + panels.resources.x + geometry.insetLeft(root.borderThickness)
y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness)
radius: Appearance.rounding.large
x: panels.resourcesWrapper.x + panels.resources.x + root.borderThickness
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
}
PanelBg {
id: settingsBg
property real extraHeight: 0
deformAmount: 0.03
panel: panels.settings
radius: Tokens.rounding.largeIncreased + Tokens.padding.small
implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width
panel: panels.settingsWrapper
radius: Appearance.rounding.large + Appearance.padding.normal
x: panels.settingsWrapper.x + panels.settings.x + root.borderThickness
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
}
PanelBg {
@@ -323,7 +321,7 @@ CustomWindow {
deformAmount: 0.08
panel: panels.dock
radius: Tokens.rounding.medium
radius: Appearance.rounding.normal
}
PanelBg {
@@ -331,7 +329,7 @@ CustomWindow {
deformAmount: 0.08
panel: panels.drawing
radius: Tokens.rounding.medium
radius: Appearance.rounding.normal
}
PanelBg {
@@ -385,7 +383,6 @@ CustomWindow {
borderThickness: root.borderLayoutThickness
drawing: drawing
enabled: true
geometry: geometry
panels: panels
popouts: panels.popouts
screen: root.screen
@@ -397,7 +394,6 @@ CustomWindow {
bar: bar
borderThickness: root.borderThickness
drawingItem: drawing
geometry: geometry
screen: root.screen
visibilities: visibilities
@@ -425,7 +421,7 @@ CustomWindow {
resources.transform: Matrix4x4 {
matrix: resourcesBg.deformMatrix
}
settings.transform: Matrix4x4 {
settingsWrapper.transform: Matrix4x4 {
matrix: settingsBg.deformMatrix
}
sidebar.transform: Matrix4x4 {
@@ -439,18 +435,14 @@ CustomWindow {
BarLoader {
id: bar
anchors.bottom: geometry.barOnBottom ? parent.bottom : undefined
anchors.left: parent.left
anchors.top: geometry.barOnBottom ? undefined : parent.top
anchors.right: parent.right
enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen
height: geometry.horizontal ? implicitHeight : parent.height
popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper
position: geometry.position
screen: root.screen
visibilities: visibilities
width: geometry.horizontal ? parent.width : implicitWidth
}
}
@@ -462,8 +454,8 @@ CustomWindow {
group: blobGroup
implicitHeight: panel.height
implicitWidth: panel.width
radius: Tokens.rounding.small
x: panel.x + geometry.insetLeft(root.borderThickness)
y: panel.y + geometry.insetTop(root.borderThickness)
radius: Appearance.rounding.smallest
x: panel.x + root.borderThickness
y: panel.y + bar.implicitHeight
}
}

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