27 Commits
Author SHA1 Message Date
Inorishio 6071223009 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 19s
Python / static (pull_request) Successful in 57s
Rust / fmt (pull_request) Successful in 1m9s
Rust / build (pull_request) Successful in 2m11s
C++ / build (pull_request) Successful in 2m34s
Rust / clippy (pull_request) Successful in 1m30s
Python / verify (pull_request) Successful in 2m55s
C++ / clang-tidy (pull_request) Successful in 3m59s
2026-08-16 22:57:54 +02:00
Inorishio 2d40ce33d1 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 17s
JS/TS / lint (pull_request) Successful in 16s
Python / fmt (pull_request) Successful in 31s
Python / lint (pull_request) Successful in 41s
Python / test (pull_request) Successful in 1m5s
Python / typecheck (pull_request) Successful in 1m15s
Rust / clippy (pull_request) Failing after 12m43s
Rust / fmt (pull_request) Failing after 12m57s
Rust / build (pull_request) Failing after 13m33s
Python / buildcheck (pull_request) Failing after 13m45s
C++ / clang-tidy (pull_request) Failing after 14m23s
C++ / build (pull_request) Failing after 14m25s
2026-08-15 20:38:54 +02:00
zach 954dc10fbb bluetooth model delegate animations
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 21s
Python / fmt (pull_request) Successful in 35s
Python / lint (pull_request) Successful in 36s
Python / test (pull_request) Successful in 1m3s
Python / typecheck (pull_request) Failing after 1m4s
C++ / build (pull_request) Successful in 1m52s
Rust / build (pull_request) Successful in 1m41s
Rust / fmt (pull_request) Successful in 1m6s
Python / buildcheck (pull_request) Successful in 2m28s
Rust / clippy (pull_request) Successful in 1m55s
C++ / clang-tidy (pull_request) Successful in 3m36s
2026-07-11 14:00:08 +02:00
zach 15ccfd24c6 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 16s
Python / fmt (pull_request) Successful in 32s
Python / lint (pull_request) Successful in 32s
Python / test (pull_request) Successful in 1m3s
Python / typecheck (pull_request) Failing after 1m3s
C++ / build (pull_request) Successful in 1m41s
Rust / build (pull_request) Successful in 1m42s
Rust / fmt (pull_request) Successful in 1m17s
Python / buildcheck (pull_request) Successful in 2m43s
Rust / clippy (pull_request) Successful in 2m7s
C++ / clang-tidy (pull_request) Successful in 3m43s
2026-07-10 18:20:25 +02:00
zach 33b08523e9 add preliminary bluetooth widget + popout
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 17s
Python / fmt (pull_request) Successful in 35s
Python / lint (pull_request) Successful in 34s
Python / test (pull_request) Successful in 1m0s
C++ / build (pull_request) Successful in 1m46s
Rust / fmt (pull_request) Successful in 1m19s
Rust / build (pull_request) Successful in 1m54s
Python / buildcheck (pull_request) Successful in 2m42s
Rust / clippy (pull_request) Successful in 1m54s
C++ / clang-tidy (pull_request) Successful in 3m42s
2026-07-10 16:17:47 +02:00
zach 742bece30b Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 7s
JS/TS / fmt (pull_request) Successful in 14s
JS/TS / lint (pull_request) Successful in 20s
Python / fmt (pull_request) Successful in 28s
Python / lint (pull_request) Successful in 29s
Python / test (pull_request) Successful in 59s
Rust / fmt (pull_request) Successful in 57s
C++ / build (pull_request) Successful in 3m47s
Rust / build (pull_request) Successful in 2m10s
Python / buildcheck (pull_request) Successful in 2m51s
Rust / clippy (pull_request) Successful in 1m32s
C++ / clang-tidy (pull_request) Successful in 5m9s
2026-07-10 15:23:23 +02:00
zach a64eadd14a feat: add password overlay for network popout + redesign
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 24s
Python / fmt (pull_request) Successful in 30s
Python / lint (pull_request) Successful in 34s
Python / test (pull_request) Successful in 53s
C++ / build (pull_request) Successful in 1m52s
Rust / fmt (pull_request) Successful in 1m14s
Rust / build (pull_request) Successful in 1m57s
Rust / clippy (pull_request) Successful in 2m0s
Python / buildcheck (pull_request) Successful in 2m37s
C++ / clang-tidy (pull_request) Successful in 3m43s
2026-07-09 17:33:13 +02:00
zach f9eeb793db Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 18s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 32s
Python / lint (pull_request) Successful in 36s
Python / test (pull_request) Successful in 55s
C++ / build (pull_request) Successful in 1m55s
Rust / fmt (pull_request) Successful in 1m15s
Rust / build (pull_request) Successful in 1m59s
Python / buildcheck (pull_request) Successful in 2m45s
Rust / clippy (pull_request) Successful in 1m55s
C++ / clang-tidy (pull_request) Successful in 3m41s
2026-07-09 14:22:34 +02:00
zach 6bb193c78c Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 22s
Python / lint (pull_request) Successful in 33s
Python / fmt (pull_request) Successful in 36s
Python / test (pull_request) Successful in 1m5s
C++ / build (pull_request) Successful in 1m42s
Rust / fmt (pull_request) Successful in 1m11s
Rust / build (pull_request) Successful in 1m56s
Python / buildcheck (pull_request) Successful in 2m40s
Rust / clippy (pull_request) Successful in 1m48s
C++ / clang-tidy (pull_request) Successful in 3m42s
2026-07-08 22:56:46 +02:00
Inorishio aadecbfe44 Chore: created Spacer component, moved x available networks, added connected networks
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 59s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m31s
C++ / build (pull_request) Successful in 2m37s
2026-07-08 22:22:03 +02:00
zach 53f0aa80ae Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 30s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m36s
C++ / build (pull_request) Successful in 2m20s
2026-07-08 13:41:27 +02:00
Inorishio d9856c9409 Chore: created NetworkItem component, renamed network header to Wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m51s
C++ / build (pull_request) Successful in 2m36s
2026-07-02 22:46:09 +02:00
zach 8dc9753e2b feat: don't add the same network twice
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 30s
Python / lint-format (pull_request) Successful in 48s
Python / test (pull_request) Successful in 40s
C++ / build (pull_request) Successful in 2m8s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m13s
2026-07-02 22:09:25 +02:00
zach 2a4bd3dfdd chore: remove old code
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Successful in 39s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m31s
C++ / build (pull_request) Successful in 2m17s
2026-07-02 21:58:01 +02:00
zach f75f51ef50 fix: sort is now correct
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 18s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 48s
C++ / build (pull_request) Successful in 2m59s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m12s
2026-07-02 21:54:58 +02:00
zach e0873304d1 add sorting by strength
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 32s
Python / test (pull_request) Successful in 39s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
C++ / build (pull_request) Successful in 2m47s
2026-07-02 21:53:42 +02:00
zach 99a0155c17 Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m30s
C++ / build (pull_request) Successful in 2m32s
2026-07-02 21:46:15 +02:00
zach f309debea5 set scannerEnabled when popout is visible + remove scan button
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 18s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 47s
C++ / build (pull_request) Successful in 3m9s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m15s
2026-07-02 16:06:52 +02:00
zach 0979fd014d Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 32s
Python / test (pull_request) Successful in 38s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m49s
C++ / build (pull_request) Successful in 3m5s
2026-07-02 15:16:22 +02:00
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
529 changed files with 10742 additions and 44246 deletions
-5
View File
@@ -1,5 +0,0 @@
CompileFlags:
Remove:
# GCC-only flags from the system's default spec that clang/clangd cannot
# parse. They appear in compile_commands.json but are not set by the project.
- -mno-direct-extern-access
+1
View File
@@ -38,6 +38,7 @@ body:
attributes:
label: OS
options:
- NixOS
- Arch
- Debian / Ubuntu
- Fedora
-1
View File
@@ -30,7 +30,6 @@ body:
- Refactor / style
- CI / build / tooling
- Documentation
- Other (Describe in description box with header "Issue type")
validations:
required: true
-3
View File
@@ -18,6 +18,3 @@ dist/
network-dev/
**/zshell.build/
**/zshell.dist/
.opencode/
run-agent.sh
AGENTS.md
-1
View File
@@ -1,4 +1,3 @@
.venv/
scripts/fzf.js
scripts/fuzzysort.js
scripts/settings-indexer.js
+1 -2
View File
@@ -3,9 +3,8 @@ FunctionsSpacing=true
IndentWidth=4
MaxColumnWidth=-1
NewlineType=native
GroupAttributesTogether=false
NormalizeOrder=true
ObjectsSpacing=true
SemicolonRule=always
SingleLineEmptyObjects=true
SortImports=false
UseTabs=true
+22 -31
View File
@@ -26,51 +26,49 @@ if(NOT DEFINED VERSION)
endif()
endif()
if(NOT DEFINED GIT_REVISION)
execute_process(COMMAND git rev-parse HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if("${GIT_REVISION}" STREQUAL "")
message(FATAL_ERROR "GIT_REVISION is not set and failed to get from git")
endif()
endif()
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
project(ZShell VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
message(STATUS "ZShell version: ${VERSION}")
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20)
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;cli;m3shapes" CACHE STRING "Modules to build/install")
set(ENABLE_MODULES "plugin;shell;m3shapes" CACHE STRING "Modules to build/install")
set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir")
set(INSTALL_QMLDIR "${CMAKE_INSTALL_LIBDIR}/qt6/qml" CACHE STRING "QML install dir")
set(INSTALL_QSCONFDIR "${CMAKE_INSTALL_SYSCONFDIR}/xdg/quickshell/zshell" CACHE STRING "Quickshell config install dir")
set(INSTALL_GREETERCONFDIR "${CMAKE_INSTALL_SYSCONFDIR}/xdg/quickshell/zshell-greeter" CACHE STRING "Quickshell greeter install dir")
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
-Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal
-Wold-style-cast -Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal -Woverloaded-virtual
-Wsign-conversion -Wredundant-decls -Wswitch
-Wunreachable-code
$<$<COMPILE_LANGUAGE:CXX>:-Wold-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
)
if("cli" IN_LIST ENABLE_MODULES)
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(
COMMAND ${Python3_EXECUTABLE}
"${CMAKE_SOURCE_DIR}/scripts/build-settings-index.py"
"${CMAKE_SOURCE_DIR}/Modules/Settings"
"${SETTINGS_INDEX_JSON}"
RESULT_VARIABLE SETTINGS_INDEX_RESULT
)
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")
@@ -190,11 +188,4 @@ if("m3shapes" IN_LIST ENABLE_MODULES)
if(TARGET m3shapesplugin)
set_target_properties(m3shapesplugin PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
# Silence warnings that come from m3shapes files we do not manage.
foreach(m3shapes_target m3shapes_core m3shapes_morph m3shapes_shapes m3shapes m3shapesplugin)
if(TARGET ${m3shapes_target})
target_compile_options(${m3shapes_target} PRIVATE -Wno-shadow -Wno-float-equal)
endif()
endforeach()
endif()
+9 -9
View File
@@ -27,25 +27,25 @@ AnchorAnimation {
if (type == AnchorAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial;
return Tokens.anim.durations.large;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
const idx = type % 4;
return Tokens.anim.durations[types[idx]];
}
easing: {
easing.bezierCurve: {
if (type == AnchorAnim.FastSpatial)
return Tokens.anim.expressiveFastSpatial;
return Tokens.anim.curves.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial;
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial;
return Tokens.anim.curves.expressiveSlowSpatial;
if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge)
return Tokens.anim.standard;
return Tokens.anim.curves.standard;
if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge)
return Tokens.anim.emphasized;
return Tokens.anim.curves.emphasized;
return Tokens.anim.expressiveDefaultSpatial;
return Tokens.anim.curves.expressiveDefaultSpatial;
}
}
+12 -12
View File
@@ -30,34 +30,34 @@ NumberAnimation {
if (type === Anim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial;
return Tokens.anim.durations.large;
if (type === Anim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Tokens.anim.durations.expressiveDefaultEffects;
return Tokens.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
const idx = type % 4;
return Tokens.anim.durations[types[idx]];
}
easing: {
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Tokens.anim.expressiveFastSpatial;
return Tokens.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial;
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial;
return Tokens.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Tokens.anim.expressiveFastEffects;
return Tokens.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Tokens.anim.expressiveDefaultEffects;
return Tokens.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Tokens.anim.expressiveSlowEffects;
return Tokens.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.emphasized;
return Tokens.anim.standard;
return Tokens.anim.curves.emphasized;
return Tokens.anim.curves.standard;
}
}
+44 -23
View File
@@ -80,29 +80,49 @@ Slider {
property bool moving
anchors.centerIn: parent
anchors.verticalCenterOffset: 1
text: moving ? Math.round(root.value * 100) : root.icon
color: Colors.palette.m3inverseOnSurface
font: moving ? Tokens.font.body.small : Tokens.font.icon.medium
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;
}
Behavior on moving {
SequentialAnimation {
Anim {
target: icon
property: "scale"
to: 0.3
duration: Tokens.anim.durations.small / 2
easing: Tokens.anim.standardAccel
}
PropertyAction {}
Anim {
target: icon
property: "scale"
to: 1
duration: Tokens.anim.durations.normal / 2
easing: Tokens.anim.standardDecel
}
anchors.centerIn: parent
color: Colors.palette.m3inverseOnSurface
text: root.icon
onMovingChanged: anim.restart()
Binding {
id: binding
property: "text"
target: icon
value: Math.round(root.value * root.multiplier)
when: false
}
SequentialAnimation {
id: anim
Anim {
duration: Tokens.anim.durations.normal / 2
easing.bezierCurve: Tokens.anim.curves.standardAccel
property: "scale"
target: icon
to: 0
}
ScriptAction {
script: icon.update()
}
Anim {
duration: Tokens.anim.durations.normal / 2
easing.bezierCurve: Tokens.anim.curves.standardDecel
property: "scale"
target: icon
to: 1
}
}
}
@@ -110,7 +130,7 @@ Slider {
}
Behavior on value {
Anim {
type: Anim.StandardLarge
duration: Tokens.anim.durations.large
}
}
@@ -118,6 +138,7 @@ Slider {
onValueChanged: {
if (!initialized) {
initialized = true;
oldValue = value;
return;
}
+8 -5
View File
@@ -1,6 +1,5 @@
import QtQuick
import ZShell.Config
import qs.Components
import qs.Services
CustomRect {
@@ -16,11 +15,15 @@ CustomRect {
property color activeOnColor
property bool checked
property real checkedRadius: Tokens.rounding.medium
property real defaultRadius: Tokens.rounding.large
property real defaultRadius: Math.min(width, height) / 2
property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(Colors.palette.m3onSurface, 0.38)
property bool fillWidth
property font font: Tokens.font.body.small
property font font: ({
family: Config.appearance.font.family.sans,
pointSize: Tokens.font.size.larger,
bold: false
})
property real horizontalPadding: padding
readonly property alias hovered: stateLayer.containsMouse
required implicitHeight
@@ -33,7 +36,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: Tokens.rounding.smallest
readonly property alias radiusAnim: radiusAnim
property bool radiusMorph: true
property alias shapeMorph: stateLayer.shapeMorph
@@ -51,7 +54,7 @@ CustomRect {
if (internalChecked)
return checkedRadius;
if (isRound)
return implicitHeight / 2 * Math.min(1, Tokens.rounding.scale);
return (height || implicitHeight) / 2 * Math.min(1, Tokens.rounding.scale);
return defaultRadius;
}
+58 -2
View File
@@ -2,6 +2,62 @@ import QtQuick
import ZShell.Config
ColorAnimation {
duration: Tokens.anim.durations.expressiveSlowEffects
easing: Tokens.anim.expressiveSlowEffects
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: CAnim.DefaultSpatial
duration: {
if (type < CAnim.StandardSmall || type > Anim.SlowEffects)
return Tokens.anim.durations.normal;
if (type === CAnim.FastSpatial)
return Tokens.anim.durations.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.durations.large;
if (type === CAnim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.durations.expressiveEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Tokens.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === CAnim.FastSpatial)
return Tokens.anim.curves.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.curves.expressiveSlowSpatial;
if (type === CAnim.FastEffects)
return Tokens.anim.curves.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.curves.expressiveDefaultEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.curves.expressiveSlowEffects;
if (type >= CAnim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.curves.emphasized;
return Tokens.anim.curves.standard;
}
}
+4 -2
View File
@@ -20,10 +20,10 @@ BusyIndicator {
property int animState
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.body.medium.pointSize * 3
property real implicitSize: Tokens.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8
property real strokeWidth: Tokens.padding.small * 0.8
property alias type: manager.indeterminateAnimationType
implicitHeight: implicitSize
@@ -34,6 +34,7 @@ BusyIndicator {
anchors.fill: parent
bgColor: root.bgColor
fgColor: root.fgColor
hasEndIndicator: false
padding: root.padding
rotation: manager.rotation
startAngle: manager.startFraction * 360
@@ -53,6 +54,7 @@ BusyIndicator {
Anim {
duration: manager.completeEndDuration * Tokens.anim.durations.scale
properties: "opacity,internalStrokeWidth"
type: Anim.DefaultEffects
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ Item {
readonly property real size: Math.min(width, height)
property int spacing: Tokens.spacing.small
property int startAngle: -90
property int strokeWidth: Tokens.padding.extraSmall
property int strokeWidth: Tokens.padding.small
property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value
+5 -5
View File
@@ -46,10 +46,10 @@ Item {
}
function syncFromPenColor() {
if (!drawing.screenState)
if (!drawing.drawingState)
return;
const c = drawing.screenState.penColor;
const c = drawing.drawingState.penColor;
if (c.hsvSaturation > 0) {
currentHue = c.hsvHue;
@@ -86,7 +86,7 @@ Item {
currentHue = relative / arcSweep;
lastChromaticHue = currentHue;
drawing.screenState.penColor = Qt.hsva(currentHue, drawing.screenState.penColor.hsvSaturation, drawing.screenState.penColor.hsvValue, drawing.screenState.penColor.a);
drawing.drawingState.penColor = Qt.hsva(currentHue, drawing.drawingState.penColor.hsvSaturation, drawing.drawingState.penColor.hsvValue, drawing.drawingState.penColor.a);
}
implicitHeight: 180
@@ -112,7 +112,7 @@ Item {
root.syncFromPenColor();
}
target: root.drawing.screenState
target: root.drawing.drawingState
}
Canvas {
@@ -152,7 +152,7 @@ Item {
CustomRect {
anchors.centerIn: parent
color: root.drawing?.screenState.penColor
color: root.drawing?.drawingState.penColor
implicitHeight: implicitWidth
implicitWidth: canvas.height - root.handleSize - Tokens.padding.extraLarge * 2
radius: Tokens.rounding.full
+1 -1
View File
@@ -23,7 +23,7 @@ CheckBox {
color: Colors.palette.m3surfaceVariant
implicitHeight: control.checkHeight
implicitWidth: control.checkWidth
radius: Tokens.rounding.small / 2
radius: Tokens.rounding.smallest / 2
CustomRect {
color: Colors.palette.m3primary
+3 -6
View File
@@ -1,12 +1,12 @@
import QtQuick
import qs.Services
import qs.Helpers
Flickable {
id: root
property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing
interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
@@ -28,9 +28,6 @@ Flickable {
interval: 10
running: root.doneFakeFlick
onTriggered: {
root.doneFakeFlick = false;
root.returnToBounds();
}
onTriggered: root.doneFakeFlick = false
}
}
+16 -19
View File
@@ -1,36 +1,33 @@
import QtQuick
import qs.Services
import qs.Helpers
ListView {
id: root
property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing
interactive: !Visibilities.getForActive()?.isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim {
properties: "x,y"
type: Anim.DefaultEffects
}
}
// Timer {
// interval: 10
// running: root.doneFakeFlick
//
// onTriggered: {
// root.doneFakeFlick = false;
// }
// }
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: root.doneFakeFlick = false
}
}
-1
View File
@@ -4,7 +4,6 @@ MouseArea {
property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void {
event.accepted = false;
}
onWheel: event => {
+4 -3
View File
@@ -6,7 +6,7 @@ import qs.Services
RadioButton {
id: root
font: Tokens.font.body.medium
font.pointSize: Tokens.font.size.normal
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
@@ -14,7 +14,7 @@ RadioButton {
anchors.left: outerCircle.right
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
font: root.font
font.pointSize: root.font.pointSize
text: root.text
}
indicator: Rectangle {
@@ -29,7 +29,8 @@ RadioButton {
radius: Tokens.rounding.full
Behavior on border.color {
CAnim {}
CAnim {
}
}
StateLayer {
+2 -1
View File
@@ -6,6 +6,7 @@ Rectangle {
color: "transparent"
Behavior on color {
CAnim {}
CAnim {
}
}
}
+81 -69
View File
@@ -1,41 +1,32 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import qs.Services
import qs.Helpers
import ZShell.Config
import qs.Services
ScrollBar {
id: root
required property Flickable flickable
readonly property bool isHorizontal: flickable && flickable.ScrollBar.horizontal === root
readonly property real axisSize: isHorizontal ? flickable.width : flickable.height
readonly property real axisContentSize: isHorizontal ? flickable.contentWidth : flickable.contentHeight
readonly property real axisContentPos: isHorizontal ? (reversed ? flickable.contentX : (flickable.contentX - flickable.originX)) : (reversed ? flickable.contentY : (flickable.contentY - flickable.originY))
readonly property real axisLength: isHorizontal ? root.width : root.height
readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize)
readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize)
readonly property real nonAnimHeight: root.axisSize / root.axisContentSize
readonly property real nonAnimY: root.axisContentPos / root.axisContentSize
required property Flickable flickable
readonly property real nonAnimHeight: flickable.height / flickable.contentHeight
readonly property real nonAnimY: flickable.contentY / flickable.contentHeight
readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight)
readonly property bool reversed: isHorizontal ? (flickable instanceof ListView && flickable.layoutDirection === Qt.RightToLeft) : (flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop)
readonly property bool reversed: flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop
property bool shouldBeActive
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
property bool shouldBeActive
property bool inverted
enabled: !Visibilities.getForActive().isDrawing
implicitWidth: Tokens.padding.extraSmall * 2
enabled: !ShellState.forActive().isDrawing
implicitWidth: size === 1 ? 0 : isHorizontal ? 0 : Tokens.padding.extraSmall * 2
implicitHeight: size === 1 ? 0 : isHorizontal ? Tokens.padding.extraSmall * 2 : 0
contentItem: Item {}
contentItem: Item {
}
Behavior on position {
enabled: !fullMouse.pressed
Anim {}
Anim {
}
}
onHoveredChanged: {
@@ -56,27 +47,54 @@ ScrollBar {
target: root.flickable
}
Loader {
anchors.fill: parent
active: root.size < 1
sourceComponent: root.isHorizontal ? horizontalTrack : verticalTrack
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full
Component {
id: verticalTrack
CustomRect {
id: handle
VerticalScrollBarTrack {
scrollBar: root
mouseArea: fullMouse
}
}
anchors.right: parent.right
color: Colors.palette.m3secondary
implicitHeight: root.height * root.effectiveSize
implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!root.enabled)
return 0;
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (fullMouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: root.reversed ? root.height * (1 + root.nonAnimY) * root.travelScale : root.height * root.nonAnimY * root.travelScale
Component {
id: horizontalTrack
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
HorizontalScrollBarTrack {
scrollBar: root
mouseArea: fullMouse
MouseArea {
id: mouse
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
@@ -93,40 +111,25 @@ ScrollBar {
property real pressOffset: 0
function contentPosFromThumbStart(thumbStart) {
var visualPos = root.effectiveTravel > 0 ? thumbStart / root.travelScale : 0;
return root.reversed ? (visualPos - 1) * root.axisContentSize : visualPos * root.axisContentSize;
function contentYFromThumbTop(thumbTop) {
var visualPos = root.effectiveTravel > 0 ? thumbTop / root.travelScale : 0;
return root.reversed ? (visualPos - 1) * root.flickable.contentHeight : visualPos * root.flickable.contentHeight;
}
function updateFromEvent(event) {
var eventPos = root.isHorizontal ? event.x : event.y;
var posInTrack = eventPos / root.axisLength;
var thumbStart = posInTrack - pressOffset;
thumbStart = Math.max(0, Math.min(root.effectiveTravel, thumbStart));
var posInTrack = event.y / root.height;
var thumbTop = posInTrack - pressOffset;
thumbTop = Math.max(0, Math.min(root.effectiveTravel, thumbTop));
var newPos = contentPosFromThumbStart(thumbStart);
if (root.isHorizontal)
root.flickable.contentX = newPos + (root.flickable.originX + (root.reversed ? root.flickable.contentWidth : 0));
else
root.flickable.contentY = newPos + (root.flickable.originY + (root.reversed ? root.flickable.contentHeight : 0));
root.flickable.contentY = contentYFromThumbTop(thumbTop);
}
function visualThumbStart() {
function visualThumbTop() {
const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY;
return visualPos * root.travelScale;
}
function onWheel(event: WheelEvent): void {
if (root.horizontal) {
event.accepted = false;
return;
}
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + (root.inverted ? -delta : delta)));
root.position = newPos;
}
anchors.fill: parent
cursorShape: undefined
hoverEnabled: true
@@ -137,15 +140,24 @@ ScrollBar {
updateFromEvent(event);
}
onPressed: event => {
var currentStart = visualThumbStart();
var currentEnd = currentStart + root.effectiveSize;
var eventPos = root.isHorizontal ? event.x : event.y;
var clickPos = eventPos / root.axisLength;
var currentTop = visualThumbTop();
var currentBottom = currentTop + root.effectiveSize;
var clickPos = event.y / root.height;
var clickedInsideThumb = clickPos >= currentStart && clickPos <= currentEnd;
pressOffset = clickedInsideThumb ? (clickPos - currentStart) : root.effectiveSize / 2;
var clickedInsideThumb = clickPos >= currentTop && clickPos <= currentBottom;
if (clickedInsideThumb) {
pressOffset = clickPos - currentTop;
} else {
pressOffset = root.effectiveSize / 2;
}
updateFromEvent(event);
}
onWheel: event => {
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta));
root.position = newPos;
}
}
}
+8 -13
View File
@@ -13,13 +13,11 @@ Slider {
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 real filledWidth
property alias inset: inset
property color insetColor: enabled ? (inset.attached ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onPrimary) : Colors.palette.m3onSurface
property string insetIcon: ""
property bool interactionOnMove: true
property real pos: visualPosition
property int radius: Tokens.rounding.small
property int waveDuration: 1000
@@ -100,19 +98,15 @@ Slider {
anchors.bottom: parent.bottom
anchors.top: parent.top
color: root.insetColor
fontStyle: {
if (root.height < 96)
return Tokens.font.icon.pixelSize(24).build();
else
return Tokens.font.icon.pixelSize(32).build();
}
font.pixelSize: Tokens.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)
Behavior on dockT {
Anim {}
Anim {
}
}
}
@@ -142,7 +136,8 @@ Slider {
startX: x
Behavior on color {
CAnim {}
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
@@ -159,7 +154,8 @@ Slider {
Behavior on filledWidth {
id: widthBehavior
Anim {}
Anim {
}
}
Component.onCompleted: {
@@ -190,8 +186,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;
+2 -2
View File
@@ -38,9 +38,9 @@ DoubleSpinBox {
horizontalAlignment: TextField.AlignHCenter
implicitWidth: 65
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Tokens.padding.small
leftPadding: Tokens.padding.larger
readOnly: !root.editable
rightPadding: Tokens.padding.small
rightPadding: Tokens.padding.larger
text: root.textFromValue(root.value, root.locale)
validator: root.validator
@@ -12,14 +12,14 @@ Row {
}
property alias active: menu.active
property color color: type == SplitButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondaryContainer
property color color: type == CustomSplitButton.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)
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: Tokens.padding.larger
readonly property alias iconLabel: iconLabel
readonly property alias label: label
readonly property alias menu: menu
@@ -27,20 +27,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 ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: SplitButton.Filled
property int type: CustomSplitButton.Filled
property real verticalPadding: Tokens.padding.small
spacing: Math.floor(Tokens.spacing.extraSmall / 2)
spacing: Math.floor(Tokens.spacing.extraSmall)
CustomRect {
bottomRightRadius: Tokens.rounding.medium / 2
bottomRightRadius: Tokens.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
topRightRadius: Tokens.rounding.small / 2
StateLayer {
id: stateLayer
@@ -92,7 +92,7 @@ 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, Tokens.rounding.scale) : Tokens.rounding.small / 2
bottomLeftRadius: rad
color: !root.enabled ? root.disabledColor : root.color
+70
View File
@@ -0,0 +1,70 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
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 + Tokens.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: Tokens.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Tokens.spacing.normal
CustomText {
Layout.fillWidth: true
color: root.enabled ? Colors.palette.m3onSurface : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.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;
}
}
}
}
+8 -20
View File
@@ -13,12 +13,8 @@ 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 ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Tokens.font.size.medium + Tokens.padding.normal * 2
implicitWidth: implicitHeight * 1.7
radius: Tokens.rounding.full
@@ -26,11 +22,7 @@ Switch {
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);
}
color: root.checked && root.enabled ? Colors.palette.m3onPrimary : Colors.layer(Colors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Tokens.padding.extraSmall
implicitWidth: nonAnimWidth
radius: Tokens.rounding.full
@@ -100,7 +92,7 @@ Switch {
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Tokens.padding.medium
height: parent.implicitHeight - Tokens.padding.larger
preferredRendererType: Shape.CurveRenderer
width: height
@@ -122,16 +114,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 ? Colors.palette.m3primary : Colors.palette.m3surfaceContainerHighest
strokeWidth: Tokens.font.size.larger * 0.15
Behavior on strokeColor {
CAnim {
@@ -165,6 +153,6 @@ Switch {
component PropAnim: PropertyAnimation {
duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
}
}
+3 -2
View File
@@ -14,9 +14,10 @@ Text {
property real animateTo: 1
color: Colors.palette.m3onSurface
font: Tokens.font.body.small
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.normal
linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.QtRendering
renderType: Text.NativeRendering
textFormat: Text.PlainText
Behavior on color {
+28 -30
View File
@@ -15,33 +15,29 @@ TextFieldBase {
readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius)
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real smallFontScale: smallFontSize / font.pointSize
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property int horizontalPadding: Tokens.padding.large
property int verticalPadding: Tokens.padding.large
property int radius: Tokens.rounding.small
property string supportingText
property string trailingIcon
property int smallFontSize: Tokens.font.label.small.pointSize
property int type: CustomTextField.Outlined
property var validate // Regex or function
property bool emptyIsValid: true
property string errorText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int horizontalPadding: Tokens.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
property alias sLayer: stateLayer
readonly property real smallFontScale: smallFontSize / font.pointSize
property int smallFontSize: Tokens.font.size.small
property string supportingText
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.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: verticalPadding + supportingTextOffset - filledOffset
bottomPadding: Tokens.padding.large + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: verticalPadding + filledOffset
topPadding: Tokens.padding.large + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
@@ -134,8 +130,8 @@ TextFieldBase {
}
AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
duration: Tokens.anim.durations.expressiveEffects
easing.bezierCurve: Tokens.anim.curves.expressiveEffects
}
}
}
@@ -145,13 +141,13 @@ TextFieldBase {
active: root.leadingIcon
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small
anchors.leftMargin: Tokens.padding.normal
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.larger
text: root.leadingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
@@ -160,13 +156,12 @@ TextFieldBase {
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.normal
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.trailingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
}
@@ -181,7 +176,7 @@ TextFieldBase {
sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font: Tokens.font.label.small
font.pointSize: Tokens.font.size.small
text: root.effectiveSupportingText
}
}
@@ -219,10 +214,12 @@ TextFieldBase {
}
}
Behavior on strokeColor {
CAnim {}
CAnim {
}
}
Behavior on strokeWidth {
Anim {}
Anim {
}
}
PathLine {
@@ -297,7 +294,8 @@ TextFieldBase {
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
Anim {}
Anim {
}
}
}
}
-42
View File
@@ -1,42 +0,0 @@
import QtQuick
import Quickshell
ShaderEffect {
id: root
property Item target: parent
property real glowSize: 40
property real innerGlow: 0
property real lineWidth: 2
property real intensity: 1.0
property real speed: 1.0
property int period: 6000
property bool running: true
property color color1: "#4285F4"
property color color2: "#9B72CB"
property color color3: "#D96570"
property color color4: "#F2A54A"
readonly property size itemSize: Qt.size(width, height)
readonly property size rectSize: target ? Qt.size(target.width, target.height) : Qt.size(0, 0)
readonly property real radius: (target as Rectangle).radius ?? 0
property real phase: 0
anchors.fill: target
anchors.margins: -glowSize
z: -1
enabled: false
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/geminiglow.frag.qsb`)
FrameAnimation {
running: root.running && root.visible
onTriggered: {
const dt = frameTime * 1000;
root.phase = (root.phase + dt * root.speed / root.period) % 1.0;
}
}
}
-56
View File
@@ -1,56 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
CustomClippingRect {
id: track
required property var scrollBar
required property var mouseArea
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight
radius: Tokens.rounding.full
CustomRect {
id: handle
anchors.bottom: parent.bottom
color: Colors.palette.m3secondary
implicitWidth: track.scrollBar.width * track.scrollBar.effectiveSize
implicitHeight: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
x: track.scrollBar.reversed ? track.scrollBar.width * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.width * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitHeight {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
+2 -3
View File
@@ -8,7 +8,6 @@ ButtonBase {
property alias icon: label.text
readonly property alias label: label
font: Tokens.font.icon.medium
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
implicitHeight: {
@@ -28,7 +27,7 @@ ButtonBase {
return Colors.palette.m3onPrimary;
return type === IconButton.Tonal ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onSurfaceVariant;
}
padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.extraSmall
padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.small
MaterialIcon {
id: label
@@ -37,7 +36,7 @@ ButtonBase {
anchors.verticalCenterOffset: 1
color: root.onColor
fill: !root.isToggle || root.internalChecked ? 1 : 0
fontStyle: root.font
font.pointSize: root.font.pointSize
Behavior on fill {
Anim {
+4 -3
View File
@@ -17,7 +17,7 @@ ButtonBase {
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary;
}
horizontalPadding: Tokens.padding.small
horizontalPadding: Tokens.padding.larger
implicitHeight: row.implicitHeight + verticalPadding * 2
implicitWidth: row.implicitWidth + horizontalPadding * 2
inactiveColor: {
@@ -32,7 +32,7 @@ ButtonBase {
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer;
}
verticalPadding: Tokens.padding.extraSmall
verticalPadding: Tokens.padding.small
RowLayout {
id: row
@@ -46,9 +46,10 @@ ButtonBase {
Layout.alignment: Qt.AlignVCenter
color: root.onColor
fill: root.internalChecked ? 1 : 0
fontStyle: {
font: {
const f = Qt.font(root.font);
f.pointSize = Math.round(root.font.pointSize * 1.2);
f.family = "Material Symbols Rounded";
return f;
}
-467
View File
@@ -1,467 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
Item {
id: root
required property list<var> values
property int xSteps: 24
property real yMin: NaN
property real yMax: NaN
property real yPaddingTop: 0
property real yPaddingBottom: 0
property int verticalLineStep: 0
property int horizontalLineStep: 0
property bool firstXLine: true
property bool lastXLine: false
property int minHorizontalLines: 0
property var xLabel: null
property var yLabel: null
property real smoothing: 0
property bool curveSmoothing: true
property real markerX: -1
property real markerGap: 2 // px gap between line and marker dot edge, each side
property color lineColor: "white"
property color gridColor: "white"
property color markerColor: "white"
property color labelColor: "white"
property real gridAlpha: 0.12
property real labelAlpha: 0.6
property real fillAlpha: 0.15
property real lineWidth: 2
property real markerDotRadius: 4
property real labelFontSize: 10
Canvas {
id: canvas
anchors.fill: parent
renderStrategy: Canvas.Immediate
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
function strokeCurve(ctx, pts) {
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
if (root.curveSmoothing && pts.length > 2) {
const k = 1 / 6;
const n = pts.length;
for (let i = 0; i < n - 1; i++) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[Math.min(i + 2, n - 1)];
let c1x = p1.x + (p2.x - p0.x) * k;
let c1y = p1.y + (p2.y - p0.y) * k;
let c2x = p2.x - (p3.x - p1.x) * k;
let c2y = p2.y - (p3.y - p1.y) * k;
const minY = Math.min(p1.y, p2.y);
const maxY = Math.max(p1.y, p2.y);
c1y = Math.max(minY, Math.min(maxY, c1y));
c2y = Math.max(minY, Math.min(maxY, c2y));
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, p2.x, p2.y);
}
} else {
for (let i = 0; i < pts.length - 1; i++) {
const p0 = pts[i];
const p1 = pts[i + 1];
const cx = (p0.x + p1.x) * 0.5;
ctx.bezierCurveTo(cx, p0.y, cx, p1.y, p1.x, p1.y);
}
}
}
// Control points for segment i (pts[i] -> pts[i+1]), matching strokeCurve
function segmentControls(pts, i) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[Math.min(i + 2, pts.length - 1)];
if (root.curveSmoothing && pts.length > 2) {
const k = 1 / 6;
let c1x = p1.x + (p2.x - p0.x) * k;
let c1y = p1.y + (p2.y - p0.y) * k;
let c2x = p2.x - (p3.x - p1.x) * k;
let c2y = p2.y - (p3.y - p1.y) * k;
const minY = Math.min(p1.y, p2.y);
const maxY = Math.max(p1.y, p2.y);
c1y = Math.max(minY, Math.min(maxY, c1y));
c2y = Math.max(minY, Math.min(maxY, c2y));
return [c1x, c1y, c2x, c2y];
}
const cx = (p1.x + p2.x) * 0.5;
return [cx, p1.y, cx, p2.y];
}
// Point on the curve at a given x (x is monotonic per segment)
function pointAtX(pts, targetX) {
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
if (targetX <= pts[i + 1].x) {
j = i;
break;
}
}
const p1 = pts[j];
const p2 = pts[j + 1];
const [c1x, c1y, c2x, c2y] = segmentControls(pts, j);
let tlo = 0;
let thi = 1;
for (let it = 0; it < 24; it++) {
const tm = (tlo + thi) / 2;
const u = 1 - tm;
const x = u * u * u * p1.x + 3 * u * u * tm * c1x + 3 * u * tm * tm * c2x + tm * tm * tm * p2.x;
if (x < targetX)
tlo = tm;
else
thi = tm;
}
const t = (tlo + thi) / 2;
const u = 1 - t;
const y = u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
return { x: targetX, y };
}
// Part of the curve left/right of a cut x, including the cut point
function cutLeft(pts, targetX) {
if (targetX <= pts[0].x)
return [];
if (targetX >= pts[pts.length - 1].x)
return pts.slice();
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
if (targetX <= pts[i + 1].x) {
j = i;
break;
}
}
const left = pts.slice(0, j + 1);
if (targetX > pts[j].x)
left.push(pointAtX(pts, targetX));
return left;
}
function cutRight(pts, targetX) {
if (targetX >= pts[pts.length - 1].x)
return [];
if (targetX <= pts[0].x)
return pts.slice();
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
if (targetX <= pts[i + 1].x) {
j = i;
break;
}
}
const right = pts.slice(j + 1);
if (targetX < pts[j + 1].x)
right.unshift(pointAtX(pts, targetX));
return right;
}
onPaint: {
const ctx = getContext("2d");
ctx.reset();
const W = width;
const H = height;
if (W < 2 || H < 2)
return;
const padLeft = root.labelFontSize * 3.4;
const padRight = 2;
const padTop = 2;
const padBottom = root.labelFontSize + 8;
const plot = {
x: padLeft,
y: padTop,
w: W - padLeft - padRight,
h: H - padTop - padBottom
};
if (plot.w < 2 || plot.h < 2)
return;
// Auto-fit: NaN yMin/yMax derive from the data, snapped to the
// horizontal step grid, honoring minHorizontalLines
let lo = root.yMin;
let hi = root.yMax;
const loAuto = Number.isNaN(lo);
const hiAuto = Number.isNaN(hi);
if (loAuto || hiAuto) {
let dmin = Infinity;
let dmax = -Infinity;
for (const v of root.values) {
if (v == null)
continue;
if (v < dmin)
dmin = v;
if (v > dmax)
dmax = v;
}
if (dmin === Infinity) {
dmin = 0;
dmax = 1;
}
const step = root.horizontalLineStep;
const minSteps = root.minHorizontalLines > 1 ? root.minHorizontalLines - 1 : 0;
if (step > 0) {
if (hiAuto) {
const baseLo = loAuto ? Math.floor(dmin / step) * step : lo;
const span = Math.max(Math.ceil((dmax - baseLo) / step), minSteps, 1);
lo = baseLo;
hi = baseLo + span * step;
} else {
const span = Math.max(Math.ceil((hi - dmin) / step), minSteps, 1);
lo = hi - span * step;
}
} else {
if (loAuto)
lo = dmin;
if (hiAuto)
hi = dmax;
if (hi <= lo)
hi = lo + 1;
}
}
const scaleBottom = lo - root.yPaddingBottom;
const scaleTop = hi + root.yPaddingTop;
const range = scaleTop - scaleBottom;
if (range <= 0 || root.xSteps < 1)
return;
let series = root.values;
if (root.smoothing > 0 && series.length > 0) {
const radius = Math.round(root.smoothing);
const raw = series;
series = raw.map((v, i) => {
if (v == null)
return null;
let sum = 0;
let n = 0;
for (let j = -radius; j <= radius; j++) {
const u = raw[i + j];
if (u != null) {
sum += u;
n++;
}
}
return n > 0 ? sum / n : null;
});
}
const xAt = i => plot.x + (i / root.xSteps) * plot.w;
const yAt = v => plot.y + (1 - (v - scaleBottom) / range) * plot.h;
const crisp = v => Math.floor(v) + 0.5;
// 1. Grid lines
ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha);
ctx.lineWidth = 1;
if (root.verticalLineStep > 0) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
if (i === 0 && !root.firstXLine)
continue;
if (i === root.xSteps && !root.lastXLine)
continue;
const x = crisp(xAt(i));
ctx.beginPath();
ctx.moveTo(x, plot.y);
ctx.lineTo(x, H);
ctx.stroke();
}
}
// Lines extend down into yPaddingBottom, snapped to the grid
const vStart = lo - (root.yPaddingBottom > 0 ? Math.floor(root.yPaddingBottom / root.horizontalLineStep) * root.horizontalLineStep : 0);
if (root.horizontalLineStep > 0) {
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) {
const y = crisp(yAt(v));
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(plot.x + plot.w, y);
ctx.stroke();
}
}
// 2. Labels
ctx.fillStyle = Qt.alpha(root.labelColor, root.labelAlpha);
ctx.font = root.labelFontSize + "px sans-serif";
ctx.textAlign = "left";
ctx.textBaseline = "top";
if (root.verticalLineStep > 0 && root.xLabel) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
if (i === 0 && !root.firstXLine)
continue;
if (i === root.xSteps && !root.lastXLine)
continue;
const text = root.xLabel(i);
if (text)
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 4);
}
}
ctx.textBaseline = "bottom";
if (root.horizontalLineStep > 0 && root.yLabel) {
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) {
const text = root.yLabel(v);
if (text)
ctx.fillText(text, 2, yAt(v) - 2);
}
}
// 3. Curve, split into segments at null gaps
const segments = [];
let current = null;
for (let i = 0; i < series.length; i++) {
if (series[i] == null) {
if (current)
segments.push(current);
current = null;
continue;
}
const pt = {
x: xAt(i),
y: yAt(series[i])
};
if (!current)
current = [];
current.push(pt);
}
if (current)
segments.push(current);
ctx.lineCap = "round";
ctx.lineJoin = "round";
const hasMarker = root.markerX >= 0 && root.markerX <= root.xSteps;
const markerPx = hasMarker ? xAt(root.markerX) : 0;
let gapPx = 0;
if (hasMarker) {
// markerGap is measured from the dot edge, perpendicular to the
// curve; project onto x via the local curve slope
let slope = 0;
const seg = segments.find(s => s[0].x <= markerPx && markerPx <= s[s.length - 1].x);
if (seg && seg.length > 2 && markerPx - 1 >= seg[0].x && markerPx + 1 <= seg[seg.length - 1].x)
slope = (pointAtX(seg, markerPx + 1).y - pointAtX(seg, markerPx - 1).y) / 2;
gapPx = (root.markerDotRadius + root.markerGap) / Math.sqrt(1 + slope * slope);
}
for (const seg of segments) {
if (seg.length < 2)
continue;
// Fill stays continuous under the marker
ctx.fillStyle = Qt.alpha(root.lineColor, root.fillAlpha);
strokeCurve(ctx, seg);
ctx.lineTo(seg[seg.length - 1].x, plot.y + plot.h);
ctx.lineTo(seg[0].x, plot.y + plot.h);
ctx.closePath();
ctx.fill();
// Stroke has a gap around the marker
const pieces = hasMarker ? [cutLeft(seg, markerPx - gapPx), cutRight(seg, markerPx + gapPx)] : [seg];
for (const pts of pieces) {
if (pts.length < 2)
continue;
ctx.strokeStyle = root.lineColor;
ctx.lineWidth = root.lineWidth;
strokeCurve(ctx, pts);
ctx.stroke();
}
}
// 4. Marker: dashed vertical + dot
if (root.markerX >= 0 && root.markerX <= root.xSteps) {
const x = markerPx;
ctx.setLineDash([4, 4]);
ctx.strokeStyle = Qt.alpha(root.markerColor, 0.6);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(crisp(x), plot.y);
ctx.lineTo(crisp(x), H);
ctx.stroke();
ctx.setLineDash([]);
const i0 = Math.floor(root.markerX);
const v0 = series[i0];
const v1 = series[i0 + 1];
let y = null;
if (v0 != null && v1 != null)
y = yAt(v0 + (v1 - v0) * (root.markerX - i0));
else if (v0 != null)
y = yAt(v0);
if (y != null) {
ctx.fillStyle = root.markerColor;
ctx.beginPath();
ctx.arc(x, y, root.markerDotRadius, 0, Math.PI * 2);
ctx.fill();
}
}
}
}
onValuesChanged: canvas.requestPaint()
onXStepsChanged: canvas.requestPaint()
onYMinChanged: canvas.requestPaint()
onYMaxChanged: canvas.requestPaint()
onYPaddingTopChanged: canvas.requestPaint()
onYPaddingBottomChanged: canvas.requestPaint()
onVerticalLineStepChanged: canvas.requestPaint()
onHorizontalLineStepChanged: canvas.requestPaint()
onFirstXLineChanged: canvas.requestPaint()
onLastXLineChanged: canvas.requestPaint()
onMinHorizontalLinesChanged: canvas.requestPaint()
onXLabelChanged: canvas.requestPaint()
onYLabelChanged: canvas.requestPaint()
onSmoothingChanged: canvas.requestPaint()
onCurveSmoothingChanged: canvas.requestPaint()
onMarkerXChanged: canvas.requestPaint()
onMarkerGapChanged: canvas.requestPaint()
onLineColorChanged: canvas.requestPaint()
onGridColorChanged: canvas.requestPaint()
onMarkerColorChanged: canvas.requestPaint()
onLabelColorChanged: canvas.requestPaint()
onGridAlphaChanged: canvas.requestPaint()
onLabelAlphaChanged: canvas.requestPaint()
onFillAlphaChanged: canvas.requestPaint()
onLineWidthChanged: canvas.requestPaint()
onMarkerDotRadiusChanged: canvas.requestPaint()
onLabelFontSizeChanged: canvas.requestPaint()
}
+17 -4
View File
@@ -1,14 +1,27 @@
import QtQuick
import ZShell.Config
import qs.Services
CustomText {
id: root
readonly property list<int> allowed: [20, 24, 40, 48]
property real fill: 0
property int grade: Colors.light ? 0 : -25
property font fontStyle: Tokens.font.icon.small
readonly property int opsz: clampOpsz(optical)
property int optical: fontInfo.pixelSize
renderType: Text.NativeRendering
font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build()
function clampOpsz(value): int {
return allowed.reduce((closest, current) => {
return Math.abs(current - value) < Math.abs(closest - value) ? current : closest;
});
}
font.family: "Material Symbols Rounded"
font.pointSize: Tokens.font.size.larger
font.variableAxes: ({
FILL: fill,
GRAD: grade,
opsz: opsz,
wght: fontInfo.weight
})
}
+17 -18
View File
@@ -78,7 +78,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: Tokens.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
@@ -124,7 +124,7 @@ MouseArea {
model: root.items
CustomRect {
id: menuItem
id: item
readonly property bool active: modelData === root?.active
required property int index
@@ -132,9 +132,9 @@ MouseArea {
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
implicitHeight: menuOptionRow.implicitHeight + Tokens.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Tokens.padding.larger * 2
radius: Tokens.rounding.small
Behavior on radius {
Anim {
@@ -142,13 +142,13 @@ MouseArea {
}
StateLayer {
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
enabled: root.expanded
onClicked: {
root.itemSelected(menuItem.modelData);
root.active = menuItem.modelData;
menuItem.modelData.clicked();
root.itemSelected(item.modelData);
root.active = item.modelData;
item.modelData.clicked();
root.expanded = false;
}
}
@@ -157,32 +157,31 @@ MouseArea {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Tokens.padding.medium
anchors.margins: Tokens.padding.larger
spacing: Tokens.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: menuItem.modelData?.icon ?? ""
visible: text !== ""
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: item.modelData?.icon ?? ""
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
text: menuItem.modelData?.text ?? ""
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
text: item.modelData?.text ?? ""
}
Loader {
Layout.alignment: Qt.AlignVCenter
active: menuItem.modelData?.trailingIcon.length > 0
active: item.modelData?.trailingIcon.length > 0
asynchronous: true
visible: active
sourceComponent: MaterialIcon {
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: menuItem.modelData.trailingIcon
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon
}
}
}
-40
View File
@@ -1,40 +0,0 @@
import QtQuick
import Quickshell
PersistentProperties {
required property ShellScreen modelData
reloadableId: "screenState"
// Drawer visibilities
property bool bar
property bool clipboard
property bool dashboard
property bool dock
property bool isDrawing
property bool launcher
property bool osd
property bool resources
property bool settings
property bool sidebar
// Dashboard state
property int dashboardTab: 0
property date dashboardDate: new Date()
// Sidebar state
property int sidebarTab: 0
property list<string> expandedNotifs: []
// Utilities state
property string recordingConfirmDelete
property bool recordingListExpanded
property string recordingMode
// Drawing state
property color penColor: "white"
property int penWidth: 4
// Screenshot state
property bool screenshot
}
+2 -2
View File
@@ -62,7 +62,7 @@ TextFieldBase {
anchors.leftMargin: Tokens.padding.large
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
font.pointSize: Tokens.font.size.large
text: "search"
}
@@ -70,7 +70,7 @@ TextFieldBase {
id: clearIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.rightMargin: Tokens.padding.larger
anchors.verticalCenter: parent.verticalCenter
enabled: root.text
icon: "clear"
+6 -4
View File
@@ -1,6 +1,8 @@
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config
import qs.Services
@@ -51,7 +53,7 @@ MouseArea {
anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled && !ShellState.forActive()?.isDrawing
enabled: parent.enabled && !Visibilities.getForActive()?.isDrawing
hoverEnabled: true
Behavior on stateOpacity {
@@ -79,7 +81,7 @@ MouseArea {
alwaysRunToEnd: true
duration: Tokens.anim.durations.expressiveSlowEffects * 2
easing: Tokens.anim.standard
easing.bezierCurve: Tokens.anim.curves.standard
property: "circleRadius"
target: root
to: root.endRadius
@@ -98,13 +100,13 @@ MouseArea {
id: base
anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
-86
View File
@@ -1,86 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
TextArea {
id: root
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: TextArea.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
}
Behavior on selectionColor {
CAnim {}
}
cursorDelegate: Item {}
CustomRect {
id: cursor
property bool disableBlink
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on x {
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 500
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
}
+2 -2
View File
@@ -14,7 +14,7 @@ ButtonBase {
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary;
}
horizontalPadding: Tokens.padding.small
horizontalPadding: Tokens.padding.normal
implicitHeight: label.implicitHeight + verticalPadding * 2
implicitWidth: label.implicitWidth + horizontalPadding * 2
inactiveColor: {
@@ -29,7 +29,7 @@ ButtonBase {
return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer;
}
verticalPadding: Tokens.padding.extraSmall
verticalPadding: Tokens.padding.small
CustomText {
id: label
-96
View File
@@ -1,96 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
TextEdit {
id: root
property bool animateCursor: true
property alias cursor: cursor
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
renderType: TextField.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
}
cursorDelegate: Item {}
Behavior on selectionColor {
CAnim {}
}
CustomRect {
id: cursor
property bool disableBlink
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Behavior on x {
enabled: root.animateCursor
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Behavior on y {
enabled: root.animateCursor
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 500
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
}
+8 -5
View File
@@ -8,7 +8,7 @@ TextField {
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
font.pointSize: Tokens.font.size.small
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
@@ -18,11 +18,14 @@ TextField {
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
CAnim {
}
}
cursorDelegate: Item {
}
cursorDelegate: Item {}
Behavior on selectionColor {
CAnim {}
CAnim {
}
}
CustomRect {
@@ -33,7 +36,7 @@ TextField {
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
radius: Tokens.rounding.large
x: root.cursorRectangle.x
y: root.cursorRectangle.y
+12 -11
View File
@@ -32,11 +32,12 @@ CustomRect {
return Colors.palette.m3errorContainer;
return Colors.palette.m3surface;
}
implicitHeight: layout.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium
implicitHeight: layout.implicitHeight + Tokens.padding.smaller * 2
radius: Tokens.rounding.normal
Behavior on border.color {
CAnim {}
CAnim {
}
}
Elevation {
@@ -51,10 +52,10 @@ 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: Tokens.padding.normal
anchors.margins: Tokens.padding.smaller
anchors.rightMargin: Tokens.padding.normal
spacing: Tokens.spacing.normal
CustomRect {
color: {
@@ -66,9 +67,9 @@ CustomRect {
return Colors.palette.m3error;
return Colors.palette.m3surfaceContainerHigh;
}
implicitHeight: icon.implicitHeight + Tokens.padding.small * 2
implicitHeight: icon.implicitHeight + Tokens.padding.smaller * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.medium
radius: Tokens.rounding.normal
MaterialIcon {
id: icon
@@ -83,7 +84,7 @@ CustomRect {
return Colors.palette.m3onError;
return Colors.palette.m3onSurfaceVariant;
}
fontStyle: Tokens.font.icon.builders.large.scale(1.2).build()
font.pointSize: Math.round(Tokens.font.size.large * 1.2)
text: root.modelData.icon
}
}
@@ -106,7 +107,7 @@ CustomRect {
return Colors.palette.m3onSurface;
}
elide: Text.ElideRight
font: Tokens.font.title.small
font.pointSize: Tokens.font.size.normal
text: root.modelData.title
}
+3 -3
View File
@@ -21,7 +21,7 @@ Item {
}
return h;
}
implicitWidth: Config.utilities.sizes.toastWidth - Tokens.padding.small * 2
implicitWidth: Config.utilities.sizes.toastWidth - Tokens.padding.normal * 2
Repeater {
id: repeater
@@ -82,7 +82,7 @@ Item {
Behavior on anchors.bottomMargin {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity {
@@ -105,7 +105,7 @@ Item {
id: initAnim
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
from: 0
properties: "opacity,scale"
target: toast
+3 -7
View File
@@ -7,9 +7,7 @@ CustomListView {
id: root
property real endFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property bool fadeEnabled: true
property real fadeAmount: 0.1
property real fadeThreshold: 0.0
readonly property bool horizontal: orientation === ListView.Horizontal
property real startFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
@@ -18,9 +16,7 @@ CustomListView {
}
function fadeShouldBeActive(isStart: bool): bool {
if (!fadeEnabled)
return false;
// When content is smaller than flickable size, hide fade when rebound starts.
if (contentSize() + marginStart() + marginEnd() < viewportSize() && rebound.running && ((isStart ? overshootStart() > 0 : overshootStart() < 0))) {
return false;
}
@@ -32,11 +28,11 @@ CustomListView {
}
function marginEnd(): real {
return horizontal ? rightMargin - fadeThreshold : bottomMargin - fadeThreshold;
return horizontal ? rightMargin : bottomMargin;
}
function marginStart(): real {
return horizontal ? leftMargin - fadeThreshold : topMargin - fadeThreshold;
return horizontal ? leftMargin : topMargin;
}
function overshootStart(): real {
-56
View File
@@ -1,56 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
CustomClippingRect {
id: track
required property var scrollBar
required property var mouseArea
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full
CustomRect {
id: handle
anchors.right: parent.right
color: Colors.palette.m3secondary
implicitHeight: track.scrollBar.height * track.scrollBar.effectiveSize
implicitWidth: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: track.scrollBar.reversed ? track.scrollBar.height * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.height * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitWidth {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
+1 -2
View File
@@ -10,7 +10,6 @@ import ZShell
import qs.Components
import qs.Modules
import qs.Helpers
import qs.Services
import qs.Paths
import ZShell.Config
@@ -26,7 +25,7 @@ Singleton {
property alias server: server
function shouldThrottle(appName: string): bool {
if (props.dnd || ShellState.anySidebarOpen())
if (props.dnd || [...Visibilities.screens.values()].some(v => v.sidebar))
return false;
const key = (appName || "unknown").trim().toLowerCase();
+10 -5
View File
@@ -3,23 +3,28 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import ZShell.Internal
import qs.Components
Item {
id: root
readonly property alias content: contentLoader.item
required property ScreenState screenState
readonly property PersistentProperties drawingState: PersistentProperties {
property color penColor: "white"
property int penWidth: 4
reloadableId: "drawingState"
}
required property PersistentProperties visibilities
Loader {
id: contentLoader
active: root.screenState.isDrawing
active: root.visibilities.isDrawing
anchors.fill: parent
sourceComponent: StrokeCanvas {
penColor: root.screenState.penColor
penWidth: root.screenState.penWidth
penColor: root.drawingState.penColor
penWidth: root.drawingState.penWidth
}
}
}
-4
View File
@@ -1,7 +1,6 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import QtQuick
import ZShell.Config
import qs.Components
@@ -16,9 +15,6 @@ Scope {
ExclusionZone {
id: top
WlrLayershell.layer: WlrLayer.Overlay
anchors.left: true
anchors.right: true
anchors.top: true
hasBar: root.geometry.barOnTop
}
+53 -93
View File
@@ -1,7 +1,7 @@
import Quickshell
import QtQuick
import ZShell.Config
import qs.Components
import ZShell.Config
import qs.Helpers
import qs.Modules.Bar.Popouts
@@ -19,7 +19,7 @@ Item {
required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
required property ScreenState screenState
required property PersistentProperties visibilities
function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
@@ -63,7 +63,7 @@ Item {
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0
enabled: !root.screenState.isDrawing
enabled: !root.visibilities.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1
minimumPointCount: 1
@@ -76,59 +76,45 @@ Item {
onCentroidChanged: {
const x = centroid.position.x;
const y = centroid.position.y;
const pressX = centroid.pressPosition.x;
const pressY = centroid.pressPosition.y;
const dragX = x - pressX;
const dragY = y - pressY;
const dragX = x - centroid.pressPosition.x;
const dragY = y - centroid.pressPosition.y;
if (pressY >= root.screen.height - Config.bar.border && pressX > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold)
root.screenState.launcher = true;
if (centroid.pressPosition.y >= root.screen.height - Config.bar.border && centroid.pressPosition.x > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold)
root.visibilities.launcher = true;
if (root.singleGestureTriggered)
return;
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, pressX, pressY);
if (inDashboard && root.withinPanelWidth(root.panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold) {
root.screenState.dashboard = true;
root.singleGestureTriggered = true;
} else if (dragY < -Config.dashboard.dragThreshold) {
root.screenState.dashboard = false;
root.singleGestureTriggered = true;
}
}
if (root.geometry.insetTop(root.borderThickness) > pressY && (Config.dashboard.showOnHover || !inDashboard)) {
if (root.geometry.insetTop(root.borderThickness) > centroid.pressPosition.y) {
if (dragY > 20) {
root.screenState.settings = true;
root.visibilities.settings = true;
root.singleGestureTriggered = true;
} else if (dragY < -20) {
root.screenState.settings = false;
root.visibilities.settings = false;
root.singleGestureTriggered = true;
}
}
if (pressY > root.screen.height - root.geometry.insetBottom(root.borderThickness) && pressX < root.screen.width / 5 && dragY < -50)
root.screenState.clipboard = true;
if (centroid.pressPosition.y > root.screen.height - root.geometry.insetBottom(root.borderThickness) && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
root.visibilities.clipboard = true;
if (!Config.dock.hoverToReveal && pressY > root.screen.height - root.geometry.insetTop(root.borderThickness) && pressX > root.screen.width / 5 && !root.screenState.launcher)
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 (dragY < -10) {
root.screenState.dock = true;
root.visibilities.dock = true;
root.singleGestureTriggered = true;
}
if (pressX > root.screen.width - Config.bar.border && pressY < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
root.screenState.sidebar = true;
if (centroid.pressPosition.x > root.screen.width - Config.bar.border && centroid.pressPosition.y < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
root.visibilities.sidebar = true;
root.singleGestureTriggered = true;
}
if (pressX >= root.screen.width - Config.bar.border && pressY > (root.screen.height / 2) && dragX < -20) {
if (centroid.pressPosition.x >= root.screen.width - Config.bar.border && centroid.pressPosition.y > (root.screen.height / 2) && dragX < -20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
if (pressX <= Config.bar.border && dragX > 20) {
if (centroid.pressPosition.x <= Config.bar.border && dragX > 20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
@@ -141,7 +127,7 @@ Item {
property bool setInitialPoint: false
acceptedButtons: Qt.LeftButton | Qt.RightButton
enabled: root.screenState.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
enabled: root.visibilities.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
onActiveChanged: {
if (!active) {
@@ -176,28 +162,15 @@ Item {
}
}
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (hoverHandler.hovered)
root.bar.isHovered = true;
}
}
HoverHandler {
id: hoverHandler
cursorShape: root.screenState.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
cursorShape: root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
onHoveredChanged: {
if (!hovered) {
if (!root.osdShortcutActive) {
root.screenState.osd = false;
root.visibilities.osd = false;
root.panels.osd.hovered = false;
}
@@ -205,18 +178,15 @@ Item {
root.popouts.hasCurrent = false;
}
if (Config.bar.autoHide || root.bar.fullscreen)
if (Config.bar.autoHide)
root.bar.isHovered = false;
if (root.screenState.dashboard && Config.dashboard.showOnHover)
root.screenState.dashboard = false;
}
}
onPointChanged: {
const x = point.position.x;
const y = point.position.y;
if (root.screenState.isDrawing) {
if (root.visibilities.isDrawing) {
if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) {
root.panels.drawing.expand();
root.drawing.content.hideHover();
@@ -227,12 +197,9 @@ Item {
return;
}
if (!root.screenState.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true))
if (!root.visibilities.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true))
root.bar.isHovered = true;
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (root.panels.sidebar.offsetScale === 1) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
@@ -245,7 +212,7 @@ Item {
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!root.osdShortcutActive) {
root.screenState.osd = showOsd;
root.visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
root.osdShortcutActive = false;
@@ -253,13 +220,8 @@ Item {
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !root.screenState.dock && !root.screenState.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.screenState.dock = true;
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, x, y);
if (Config.dashboard.showOnHover)
root.screenState.dashboard = inDashboard;
if (Config.dock.enabled && 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));
@@ -268,17 +230,16 @@ Item {
Connections {
function onDashboardChanged() {
if (root.screenState.dashboard) {
if (root.visibilities.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x)
root.screenState.launcher = false;
root.visibilities.launcher = false;
root.screenState.settings = false;
root.screenState.sidebar = false;
root.screenState.resources = false;
root.visibilities.settings = false;
root.visibilities.sidebar = false;
root.popouts.hasCurrent = false;
} else {
root.dashboardShortcutActive = false;
@@ -286,12 +247,12 @@ Item {
}
function onIsDrawingChanged() {
if (!root.screenState.isDrawing)
if (!root.visibilities.isDrawing)
root.drawing.clear();
}
function onLauncherChanged() {
if (!root.screenState.launcher) {
if (!root.visibilities.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
@@ -299,23 +260,23 @@ Item {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.screenState.osd = false;
root.visibilities.osd = false;
root.panels.osd.hovered = false;
}
}
if (root.screenState.launcher) {
if (root.visibilities.launcher) {
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
root.screenState.dashboard = false;
root.visibilities.dashboard = false;
}
root.screenState.dock = false;
root.screenState.settings = false;
root.visibilities.dock = false;
root.visibilities.settings = false;
}
}
function onOsdChanged() {
if (root.screenState.osd) {
if (root.visibilities.osd) {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.osdShortcutActive = true;
@@ -326,33 +287,32 @@ Item {
}
function onResourcesChanged() {
if (root.screenState.resources) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
root.popouts.hasCurrent = false;
root.screenState.settings = false;
root.screenState.dashboard = false;
if (root.visibilities.resources && (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))) {
root.popouts.hasCurrent = false;
}
if (root.visibilities.resources)
root.visibilities.settings = false;
}
function onSettingsChanged() {
if (root.screenState.settings) {
root.screenState.resources = false;
root.screenState.dashboard = false;
root.screenState.sidebar = false;
if (root.visibilities.settings) {
root.visibilities.resources = false;
root.visibilities.dashboard = false;
root.visibilities.sidebar = false;
root.panels.popouts.hasCurrent = false;
root.screenState.launcher = false;
root.visibilities.launcher = false;
}
}
function onSidebarChanged() {
if (root.screenState.sidebar) {
root.screenState.dashboard = false;
root.screenState.settings = false;
if (root.visibilities.sidebar) {
root.visibilities.dashboard = false;
root.visibilities.settings = false;
root.popouts.hasCurrent = false;
}
}
target: root.screenState
target: root.visibilities
}
}
-315
View File
@@ -1,315 +0,0 @@
import Quickshell
import QtQuick
import ZShell.Config
import ZShell.Internal
import qs.Components
import qs.Helpers
import qs.Modules.Bar.Popouts
CustomMouseArea {
id: root
required property Item bar
required property real borderThickness
property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing
required property EdgeGeometry geometry
property bool osdShortcutActive
required property Panels panels
required property Wrapper popouts
required property ShellScreen screen
required property ScreenState screenState
property bool setInitialPoint: false
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
}
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inPopoutArea(x: real, y: real): bool {
const panel = panels.popoutsWrapper;
if (!geometry.horizontal)
return inLeftPanel(panel, x, y);
return withinPanelWidth(panel, x, y) && withinPanelHeight(panel, x, y);
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inTopPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness);
return y >= panelY && y <= panelY + panel.height;
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + geometry.insetLeft(borderThickness);
return x >= panelX && x <= panelX + panel.width;
}
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if (!containsMouse) {
if (!osdShortcutActive) {
screenState.osd = false;
panels.osd.hovered = false;
}
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
popouts.hasCurrent = false;
}
if (Config.bar.autoHide || bar.fullscreen)
bar.isHovered = false;
if (screenState.dashboard && Config.dashboard.showOnHover)
screenState.dashboard = false;
}
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (screenState.isDrawing) {
if (pressed) {
drawing.content.appendPoint(x, y);
} else {
if (inLeftPanel(panels.drawing, x, y)) {
if (!(pressedButtons & Qt.LeftButton)) {
panels.drawing.expand();
drawing.content.hideHover();
Cursor.clearOverride();
}
} else {
Cursor.setOverride(Qt.BlankCursor);
drawing.content.showHover(x, y);
}
}
if (drawing.content.hoverVisible)
drawing.content.showHover(x, y);
return;
}
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
screenState.launcher = true;
const entryAt = bar.entryAt(geometry.axisPos(x, y));
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y) && (!entryAt || entryAt === "activeWindow");
if (!dashboardShortcutActive) {
screenState.dashboard = showDashboard;
} else if (showDashboard) {
dashboardShortcutActive = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold)
screenState.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold)
screenState.dashboard = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y)) {
if (dragY > 20) {
screenState.settings = true;
} else if (dragY < -20) {
screenState.settings = false;
}
}
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
screenState.clipboard = true;
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
if (dragY < -10)
screenState.dock = true;
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
screenState.sidebar = true;
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
bar.isHovered = true;
if (panels.sidebar.offsetScale === 1) {
const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!osdShortcutActive) {
screenState.osd = showOsd;
panels.osd.hovered = showOsd;
} else if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
screenState.dock = true;
if (geometry.barContains(x, y))
bar.checkPopout(geometry.axisPos(x, y));
}
onPressed: event => {
dragStart = Qt.point(event.x, event.y);
if (screenState.isDrawing) {
if (event.button & Qt.RightButton) {
drawing.content.clear();
return;
}
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.beginStroke(event.x, event.y);
}
}
onPressedChanged: {
if (!pressed) {
if (screenState.isDrawing) {
setInitialPoint = false;
drawing.content.endStroke();
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.showHover(mouseX, mouseY);
}
} else {
if (screenState.isDrawing) {
panels.drawing.collapse();
}
}
}
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (root.containsMouse)
root.bar.isHovered = true;
}
}
Connections {
function onDashboardChanged(): void {
if (root.screenState.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
root.screenState.launcher = false;
root.screenState.settings = false;
root.screenState.sidebar = false;
root.screenState.resources = false;
root.popouts.hasCurrent = false;
} else {
root.dashboardShortcutActive = false;
}
}
function onIsDrawingChanged(): void {
if (root.screenState.isDrawing) {
Cursor.setOverride(Qt.BlankCursor);
} else {
Cursor.clearOverride();
}
}
function onLauncherChanged(): void {
if (!root.screenState.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.screenState.osd = false;
root.panels.osd.hovered = false;
}
}
if (root.screenState.launcher) {
root.screenState.dashboard = false;
root.screenState.dock = false;
root.screenState.settings = false;
}
}
function onOsdChanged(): void {
if (root.screenState.osd) {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.osdShortcutActive = true;
}
} else {
root.osdShortcutActive = false;
}
}
function onResourcesChanged(): void {
if (root.screenState.resources) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
root.popouts.hasCurrent = false;
root.screenState.settings = false;
root.screenState.dashboard = false;
}
}
function onSettingsChanged(): void {
if (root.screenState.settings) {
root.screenState.resources = false;
root.screenState.dashboard = false;
root.screenState.sidebar = false;
root.panels.popouts.hasCurrent = false;
root.screenState.launcher = false;
}
}
function onSidebarChanged(): void {
if (root.screenState.sidebar) {
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x)
root.screenState.dashboard = false;
root.screenState.settings = false;
root.popouts.hasCurrent = false;
}
}
target: root.screenState
}
}
+23 -19
View File
@@ -37,12 +37,12 @@ Item {
readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar
readonly property alias toasts: toasts
readonly property alias utilities: utilities
required property PersistentProperties visibilities
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent
@@ -64,7 +64,7 @@ Item {
id: resources
position: root.geometry.position
screenState: root.screenState
visibilities: root.visibilities
}
}
@@ -84,8 +84,8 @@ Item {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
screen: root.screen
screenState: root.screenState
sidebarOrSessionVisible: sidebar.visible
visibilities: root.visibilities
}
}
@@ -96,7 +96,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
drawing: root.drawingItem
screen: root.screen
screenState: root.screenState
visibilities: root.visibilities
}
Popouts.ClipWrapper {
@@ -111,7 +111,7 @@ Item {
id: toasts
anchors.bottom: sidebar.visible ? parent.bottom : utilities.top
anchors.margins: Tokens.padding.small
anchors.margins: Tokens.padding.normal
anchors.right: sidebar.left
}
@@ -121,8 +121,8 @@ Item {
anchors.right: parent.right
anchors.top: parent.top
panels: root
screenState: root.screenState
sidebarPanel: sidebar
visibilities: root.visibilities
}
Launcher.Wrapper {
@@ -132,7 +132,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
panels: root
screen: root.screen
screenState: root.screenState
visibilities: root.visibilities
}
Utils.Wrapper {
@@ -140,8 +140,9 @@ Item {
anchors.bottom: parent.bottom
anchors.right: parent.right
screenState: root.screenState
popouts: popouts
sidebar: sidebar
visibilities: root.visibilities
}
Item {
@@ -149,16 +150,18 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
clip: offsetScale > 0
implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
implicitWidth: dashboard.implicitWidth
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
clip: true
implicitHeight: root.geometry.horizontal ? dashboard.implicitHeight * (1 - offsetScale) : dashboard.implicitHeight
implicitWidth: root.geometry.horizontal ? dashboard.implicitWidth : dashboard.implicitWidth * (1 - offsetScale)
Behavior on offsetScale {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
}
}
@@ -166,7 +169,8 @@ Item {
id: dashboard
offsetScale: dashboardWrapper.offsetScale
screenState: root.screenState
position: root.geometry.position
visibilities: root.visibilities
}
}
@@ -177,7 +181,7 @@ Item {
anchors.right: parent.right
anchors.top: notifications.bottom
panels: root
screenState: root.screenState
visibilities: root.visibilities
}
Item {
@@ -193,7 +197,7 @@ Item {
anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
panels: root
screen: root.screen
screenState: root.screenState
visibilities: root.visibilities
}
}
@@ -204,7 +208,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
panels: root
screen: root.screen
screenState: root.screenState
visibilities: root.visibilities
}
Clipboard.Wrapper {
@@ -213,6 +217,6 @@ Item {
anchors.bottom: parent.bottom
anchors.left: parent.left
screen: root.screen
screenState: root.screenState
visibilities: root.visibilities
}
}
+116 -104
View File
@@ -11,6 +11,7 @@ import ZShell.Blobs
import qs.Daemons
import qs.Components
import qs.Modules.Bar
import qs.Modules.Bar.Popouts.Network as N
import ZShell.Config
import qs.Helpers
import qs.Drawers
@@ -50,47 +51,49 @@ CustomWindow {
readonly property alias menuRegion: regions.menuPopoutRegion
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
property var root: Quickshell.shellDir
readonly property ScreenState screenState: ShellState.forScreen(screen)
readonly property real sdfBorderOffset: 2 * fsTransitionProg
readonly property real shadowOpacity: 0.5 * (1 - (bar.isHovered ? 0 : fsTransitionProg))
readonly property real shadowOpacity: 0.7 * (1 - fsTransitionProg)
property color surfaceColor: Colors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.settings ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent"
mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
contentItem.focus: true
mask: visibilities.isDrawing ? null : (hasFullscreen ? emptyRegion : regions)
name: "Bar"
Behavior on fsTransitionProg {
Anim {}
Anim {
}
}
Behavior on surfaceColor {
CAnim {}
CAnim {
}
}
contentItem.Keys.onEscapePressed: {
if (Config.bar.autoHide)
screenState.bar = false;
screenState.launcher = false;
screenState.sidebar = false;
screenState.dashboard = false;
screenState.osd = false;
screenState.settings = false;
screenState.resources = false;
screenState.dock = false;
screenState.clipboard = false;
visibilities.bar = false;
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.dock = false;
visibilities.clipboard = false;
panels.popouts.hasCurrent = false;
}
onHasFullscreenChanged: {
screenState.launcher = false;
screenState.sidebar = false;
screenState.dashboard = false;
screenState.osd = false;
screenState.settings = false;
screenState.resources = false;
screenState.clipboard = false;
screenState.dock = false;
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.clipboard = false;
visibilities.dock = false;
panels.popouts.hasCurrent = false;
}
@@ -116,12 +119,6 @@ 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 {
@@ -143,40 +140,53 @@ CustomWindow {
id: focusGrab
active: {
const s = root.screenState;
const c = Config;
if ((s.launcher && c.launcher.enabled) || (s.sidebar && c.sidebar.enabled) || (s.resources && c.dashboard.performance.enabled) || (s.clipboard && c.clipboard.enabled))
const v = visibilities;
const conf = Config;
if (!N.PopupManager.closed && panels.popouts.hasCurrent)
return true;
if (s.dashboard && !c.dashboard.showOnHover && c.dashboard.enabled)
if ((v.launcher && conf.launcher.enabled) || (v.sidebar && conf.sidebar.enabled) || (v.resources && conf.dashboard.performance.enabled) || (v.dashboard && conf.dashboard.enabled) || v.settings || (v.clipboard && conf.clipboard.enabled) || (v.dock && conf.dock.enabled))
return true;
if (!c.dock.hoverToReveal && s.dock && c.dock.enable)
return true;
if (panels.popouts.currentName.startsWith("traymenu") && (panels.popouts.current as StackView)?.depth > 1)
return true;
if (s.settings)
if (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && (!conf.bar.tray.showOnHover || (panels.popouts.current as StackView)?.depth > 1))
return true;
return false;
}
windows: [root]
onCleared: {
root.screenState.launcher = false;
root.screenState.sidebar = false;
root.screenState.dashboard = false;
root.screenState.osd = false;
root.screenState.settings = false;
root.screenState.resources = false;
root.screenState.clipboard = false;
root.screenState.dock = false;
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.clipboard = false;
visibilities.dock = false;
panels.popouts.hasCurrent = false;
}
}
PersistentProperties {
id: visibilities
property bool bar
property bool clipboard
property bool dashboard
property bool dock
property bool isDrawing
property bool launcher
property bool notif: NotifServer.popups.length > 0
property bool osd
property bool resources
property bool settings
property bool sidebar
Component.onCompleted: Visibilities.load(root.screen, this)
}
Binding {
property: "bar"
target: root.screenState
value: root.screenState.sidebar || root.screenState.dashboard || root.screenState.osd || (!Config.bar.hideWhenNotif && NotifServer.popups.length > 0) || root.screenState.resources || root.screenState.settings || bar.isHovered
target: visibilities
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || (!Config.bar.hideWhenNotif && visibilities.notif) || visibilities.resources || visibilities.settings || bar.isHovered
when: Config.bar.autoHide
}
@@ -184,19 +194,23 @@ CustomWindow {
id: surface
anchors.fill: parent
layer.enabled: true
opacity: root.surfaceColor.a
BlobGroup2 {
layer.effect: MultiEffect {
blurMax: 32
shadowColor: Qt.alpha(Colors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowEnabled: true
}
BlobGroup {
id: blobGroup
anchors.fill: parent
color: root.surfaceColor
shadowColor: Qt.alpha(Colors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowRadius: 18
smoothing: Config.bar.smoothing
}
BlobInvertedRect2 {
BlobInvertedRect {
anchors.fill: parent
anchors.margins: -50
borderBottom: geometry.insetBottom(root.borderThickness) - anchors.margins - root.sdfBorderOffset
@@ -216,7 +230,7 @@ CustomWindow {
implicitHeight: panels.dashboard.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.dashboard.width * (geometry.horizontal ? 1 : 1 + extraExtent)
panel: panels.dashboardWrapper
radius: Tokens.rounding.extraLarge
radius: Tokens.rounding.normal
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)
}
@@ -229,7 +243,7 @@ CustomWindow {
deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher
radius: Tokens.rounding.medium + (Tokens.padding.largeIncreased / 4)
radius: Tokens.rounding.smallest + 5
y: panels.launcher.y + geometry.insetTop(root.borderThickness)
}
@@ -246,7 +260,6 @@ CustomWindow {
PanelBg {
id: osdBg
atRestOverride: panels.osd.offsetScale < 0.99
deformAmount: 0.1
implicitHeight: panels.osd.height
implicitWidth: panels.osd.width
@@ -258,9 +271,8 @@ CustomWindow {
PanelBg {
id: notifsBg
atRestOverride: panel.implicitHeight > 0
panel: panels.notifications
radius: Tokens.rounding.large
radius: Tokens.rounding.normal
}
PanelBg {
@@ -281,28 +293,26 @@ CustomWindow {
implicitHeight: panels.popouts.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.popouts.width * (geometry.horizontal ? 1 : 1 + extraExtent)
panel: panels.popoutsWrapper
radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.large
radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.normal
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)
Behavior on extraExtent {
Anim {}
Anim {
}
}
}
PanelBg {
id: resourcesBg
property real extraExtent: 0.2
atRestOverride: panels.resources.offsetScale < 0.99
deformAmount: 0.05
implicitHeight: panels.resources.height * (1 + extraExtent)
implicitWidth: panels.resources.width * (1 + extraExtent)
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) - panels.resources.width * extraExtent
y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness) - panels.resources.height * extraExtent
radius: Tokens.rounding.large
x: panels.resourcesWrapper.x + panels.resources.x + geometry.insetLeft(root.borderThickness)
y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness)
}
PanelBg {
@@ -310,7 +320,7 @@ CustomWindow {
deformAmount: 0.03
panel: panels.settings
radius: Tokens.rounding.largeIncreased + Tokens.padding.small
radius: Tokens.rounding.large + Tokens.padding.normal
}
PanelBg {
@@ -318,7 +328,7 @@ CustomWindow {
deformAmount: 0.08
panel: panels.dock
radius: Tokens.rounding.medium
radius: Tokens.rounding.normal
}
PanelBg {
@@ -326,21 +336,15 @@ CustomWindow {
deformAmount: 0.08
panel: panels.drawing
radius: Tokens.rounding.medium
radius: Tokens.rounding.normal
}
PanelBg {
id: clipboardBg
property real extraExtent: 0.2
deformAmount: 0.03
implicitHeight: panels.clipboard.height * (1 + extraExtent)
implicitWidth: panels.clipboard.width * (1 + extraExtent)
panel: panels.clipboard
radius: 29
x: panels.clipboard.x + geometry.insetLeft(root.borderThickness) - panels.clipboard.width * extraExtent
y: panels.clipboard.y + geometry.insetTop(root.borderThickness)
}
}
@@ -348,11 +352,37 @@ CustomWindow {
id: drawing
anchors.fill: parent
screenState: root.screenState
layer.enabled: true
visibilities: visibilities
z: 2
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: maskSource
}
}
Interactions2 {
Item {
id: maskSource
anchors.fill: parent
layer.enabled: true
visible: false
CustomRect {
readonly property int extraWidth: radius
color: "white"
implicitHeight: panels.drawing.height
implicitWidth: panels.drawing.width + extraWidth
radius: drawingBg.radius
x: -extraWidth + root.borderThickness + panels.drawing.x
y: panels.drawing.y + bar.implicitHeight
}
}
Interactions {
id: interactions
anchors.fill: parent
@@ -364,7 +394,7 @@ CustomWindow {
panels: panels
popouts: panels.popouts
screen: root.screen
screenState: root.screenState
visibilities: visibilities
Panels {
id: panels
@@ -374,7 +404,7 @@ CustomWindow {
drawingItem: drawing
geometry: geometry
screen: root.screen
screenState: root.screenState
visibilities: visibilities
clipboard.transform: Matrix4x4 {
matrix: clipboardBg.deformMatrix
@@ -417,45 +447,27 @@ CustomWindow {
anchors.bottom: geometry.barOnBottom ? parent.bottom : undefined
anchors.left: parent.left
anchors.top: geometry.barOnBottom ? undefined : parent.top
enabled: !screenState.isDrawing
enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen
height: geometry.horizontal ? implicitHeight : parent.height
popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper
position: geometry.position
screen: root.screen
screenState: root.screenState
visibilities: visibilities
width: geometry.horizontal ? parent.width : implicitWidth
}
}
LazyLoader {
id: loader
property bool closing: !root.screenState.screenshot
onClosingChanged: if (!closing)
activeAsync = true
Picker {
loader: loader
parent: root.contentItem
screen: root.screen
screenState: root.screenState
}
}
component PanelBg: BlobRect2 {
component PanelBg: BlobRect {
property real deformAmount: 0.15
required property Item panel
atRestOverride: panel?.offsetScale < 0.99 ?? false
damping: 28.3
deformScale: (deformAmount * Config.appearance.deform.scale) / 10000
group: blobGroup
implicitHeight: panel.height
implicitWidth: panel.width
radius: Tokens.rounding.small
radius: Tokens.rounding.smallest
x: panel.x + geometry.insetLeft(root.borderThickness)
y: panel.y + geometry.insetTop(root.borderThickness)
}
+33
View File
@@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Effects
Item {
id: root
property real radius
Rectangle {
id: shadowRect
anchors.fill: root
color: "black"
layer.enabled: true
radius: root.radius
visible: false
}
MultiEffect {
id: effects
anchors.fill: shadowRect
autoPaddingEnabled: true
maskEnabled: true
maskInverted: true
maskSource: shadowRect
shadowBlur: 2.0
shadowColor: "black"
shadowEnabled: true
shadowOpacity: 1
source: shadowRect
}
}
+8 -8
View File
@@ -18,7 +18,7 @@ ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: false
Layout.preferredWidth: centerWidth
spacing: Tokens.spacing.largeIncreased * 2
spacing: Tokens.spacing.large * 2
RowLayout {
Layout.alignment: Qt.AlignHCenter
@@ -64,7 +64,7 @@ ColumnLayout {
CustomClippingRect {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2
Layout.topMargin: Tokens.spacing.large * 2
color: Colors.tPalette.m3surfaceContainer
implicitHeight: root.centerWidth / 2
implicitWidth: root.centerWidth / 2
@@ -99,7 +99,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
color: Colors.tPalette.m3surfaceContainer
focus: true
implicitHeight: input.implicitHeight + Tokens.padding.extraSmall * 2
implicitHeight: input.implicitHeight + Tokens.padding.small * 2
implicitWidth: root.centerWidth * 0.8
radius: Tokens.rounding.full
@@ -130,11 +130,11 @@ ColumnLayout {
id: input
anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.medium
anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.normal
Item {
implicitHeight: statusIcon.implicitHeight + Tokens.padding.extraSmall * 2
implicitHeight: statusIcon.implicitHeight + Tokens.padding.small * 2
implicitWidth: implicitHeight
MaterialIcon {
@@ -174,7 +174,7 @@ ColumnLayout {
CustomRect {
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: enterIcon.implicitHeight + Tokens.padding.extraSmall * 2
implicitHeight: enterIcon.implicitHeight + Tokens.padding.small * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.full
@@ -200,7 +200,7 @@ ColumnLayout {
Item {
Layout.fillWidth: true
Layout.topMargin: -Tokens.spacing.largeIncreased
Layout.topMargin: -Tokens.spacing.large
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
Behavior on implicitHeight {
+11 -11
View File
@@ -30,11 +30,11 @@ NumberAnimation {
if (type === Anim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial;
return Tokens.anim.durations.large;
if (type === Anim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Tokens.anim.durations.expressiveDefaultEffects;
return Tokens.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
@@ -42,22 +42,22 @@ NumberAnimation {
const idx = type % 4; // 0-7 are the 4 standard types
return Tokens.anim.durations[types[idx]];
}
easing: {
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Tokens.anim.expressiveFastSpatial;
return Tokens.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial;
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial;
return Tokens.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Tokens.anim.expressiveFastEffects;
return Tokens.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Tokens.anim.expressiveDefaultEffects;
return Tokens.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Tokens.anim.expressiveSlowEffects;
return Tokens.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.emphasized;
return Tokens.anim.standard;
return Tokens.anim.curves.emphasized;
return Tokens.anim.curves.standard;
}
}
+58 -2
View File
@@ -2,6 +2,62 @@ import QtQuick
import ZShell.Config
ColorAnimation {
duration: Tokens.anim.durations.expressiveSlowEffects
easing: Tokens.anim.expressiveSlowEffects
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: CAnim.DefaultSpatial
duration: {
if (type < CAnim.StandardSmall || type > Anim.SlowEffects)
return Tokens.anim.durations.normal;
if (type === CAnim.FastSpatial)
return Tokens.anim.durations.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.durations.large;
if (type === CAnim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.durations.expressiveEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Tokens.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === CAnim.FastSpatial)
return Tokens.anim.curves.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.curves.expressiveSlowSpatial;
if (type === CAnim.FastEffects)
return Tokens.anim.curves.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.curves.expressiveDefaultEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.curves.expressiveSlowEffects;
if (type >= CAnim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.curves.emphasized;
return Tokens.anim.curves.standard;
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ BusyIndicator {
property real implicitSize: Tokens.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8
property real strokeWidth: Tokens.padding.small * 0.8
property alias type: manager.indeterminateAnimationType
implicitHeight: implicitSize
+1 -1
View File
@@ -21,7 +21,7 @@ Item {
readonly property real size: Math.min(width, height)
property int spacing: Tokens.spacing.small
property int startAngle: -90
property int strokeWidth: Tokens.padding.extraSmall
property int strokeWidth: Tokens.padding.small
property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value
+1 -1
View File
@@ -27,7 +27,7 @@ TextField {
color: Colors.palette.m3primary
implicitWidth: 2
radius: Tokens.rounding.medium
radius: Tokens.rounding.normal
Behavior on opacity {
Anim {
+2 -1
View File
@@ -1,4 +1,5 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
@@ -10,6 +11,6 @@ TextInput {
font {
family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting
pixelSize: Tokens?.font.size.normal ?? 15
pixelSize: Config.appearance?.font.size.normal ?? 15
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ MouseArea {
alwaysRunToEnd: true
duration: Tokens.anim.durations.expressiveSlowEffects * 2
easing: Tokens.anim.standard
easing.bezierCurve: Tokens.anim.curves.standard
property: "circleRadius"
target: root
to: root.endRadius
+7 -7
View File
@@ -11,18 +11,18 @@ RowLayout {
required property var greeter
required property real screenHeight
spacing: Tokens.spacing.largeIncreased * 2
spacing: Tokens.spacing.large * 2
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
spacing: Tokens.spacing.normal
CustomRect {
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer
implicitHeight: weather.implicitHeight
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.largeIncreased
topLeftRadius: Tokens.rounding.large
WeatherInfo {
id: weather
@@ -34,7 +34,7 @@ RowLayout {
CustomClippingRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomLeftRadius: Tokens.rounding.largeIncreased
bottomLeftRadius: Tokens.rounding.large
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
@@ -51,15 +51,15 @@ RowLayout {
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.medium
spacing: Tokens.spacing.normal
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
bottomRightRadius: Tokens.rounding.large
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
topRightRadius: Tokens.rounding.large
SessionDock {
greeter: root.greeter
+5 -5
View File
@@ -43,7 +43,7 @@ Scope {
ParallelAnimation {
Anim {
duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
property: "scale"
target: lockContent
to: 1
@@ -65,7 +65,7 @@ Scope {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
property: "scale"
target: content
to: 1
@@ -74,12 +74,12 @@ Scope {
Anim {
property: "radius"
target: lockBg
to: Tokens.rounding.largeIncreased * 1.5
to: Tokens.rounding.large * 1.5
}
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
property: "implicitWidth"
target: lockContent
to: (win.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
@@ -87,7 +87,7 @@ Scope {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
property: "implicitHeight"
target: lockContent
to: (win.screen?.height ?? 0) * Config.lock.sizes.heightMult
+1 -1
View File
@@ -113,7 +113,7 @@ Item {
Behavior on scale {
Anim {
duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
}
}
+10 -5
View File
@@ -13,17 +13,21 @@ import qs.Paths
Singleton {
id: root
readonly property M3Palette current: M3Palette {}
readonly property M3Palette current: M3Palette {
}
property bool currentLight
property string flavour
readonly property bool light: showPreview ? previewLight : currentLight
readonly property M3Palette palette: showPreview ? preview : current
readonly property M3Palette preview: M3Palette {}
readonly property M3Palette preview: M3Palette {
}
property bool previewLight
property string scheme
property bool showPreview
readonly property M3TPalette tPalette: M3TPalette {}
readonly property Transparency transparency: Transparency {}
readonly property M3TPalette tPalette: M3TPalette {
}
readonly property Transparency transparency: Transparency {
}
readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color {
@@ -108,7 +112,8 @@ Singleton {
function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.colors.mode = mode;
Config.general.color.mode = mode;
Config.save();
}
function swapRG(c: color): color {
+8 -8
View File
@@ -13,7 +13,7 @@ ColumnLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
spacing: Tokens.spacing.smaller
CustomText {
Layout.fillWidth: true
@@ -41,7 +41,7 @@ ColumnLayout {
}
}
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
spacing: Tokens.spacing.large
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
@@ -79,8 +79,8 @@ ColumnLayout {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium - Tokens.padding.small
implicitHeight: row.implicitHeight + Tokens.padding.normal * 2
radius: Tokens.rounding.normal - Tokens.padding.smaller
StateLayer {
onClicked: {
@@ -92,8 +92,8 @@ ColumnLayout {
id: row
anchors.fill: parent
anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium
anchors.margins: Tokens.padding.normal
spacing: Tokens.spacing.normal
MaterialIcon {
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
@@ -129,13 +129,13 @@ ColumnLayout {
color: Colors.palette.m3primary
implicitHeight: sessions.currentItem?.implicitHeight ?? 0
implicitWidth: sessions.width
radius: Tokens.rounding.medium - Tokens.padding.small
radius: Tokens.rounding.normal - Tokens.padding.smaller
y: sessions.currentItem?.y ?? 0
Behavior on y {
Anim {
duration: Tokens.anim.durations.small
easing: Tokens.anim.expressiveDefaultEffects
easing.bezierCurve: Tokens.anim.curves.expressiveEffects
}
}
}
+9 -9
View File
@@ -14,7 +14,7 @@ ColumnLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
spacing: Tokens.spacing.smaller
CustomText {
Layout.fillWidth: true
@@ -42,7 +42,7 @@ ColumnLayout {
}
}
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
spacing: Tokens.spacing.large
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
@@ -81,8 +81,8 @@ ColumnLayout {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium - Tokens.padding.small
implicitHeight: row.implicitHeight + Tokens.padding.normal * 2
radius: Tokens.rounding.normal - Tokens.padding.smaller
StateLayer {
onClicked: {
@@ -94,8 +94,8 @@ ColumnLayout {
id: row
anchors.fill: parent
anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium
anchors.margins: Tokens.padding.normal
spacing: Tokens.spacing.normal
CustomClippingRect {
Layout.preferredHeight: 50
@@ -135,13 +135,13 @@ ColumnLayout {
color: Colors.palette.m3primary
implicitHeight: users.currentItem?.implicitHeight ?? 0
implicitWidth: users.width
radius: Tokens.rounding.medium - Tokens.padding.small
radius: Tokens.rounding.normal - Tokens.padding.smaller
y: users.currentItem?.y ?? 0
Behavior on y {
Anim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
duration: Tokens.anim.durations.small
easing.bezierCurve: Tokens.anim.curves.expressiveEffects
}
}
}
+5 -6
View File
@@ -34,7 +34,7 @@ ColumnLayout {
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.largeIncreased
spacing: Tokens.spacing.large
MaterialIcon {
animate: true
@@ -67,7 +67,7 @@ ColumnLayout {
}
Loader {
Layout.rightMargin: Tokens.padding.small
Layout.rightMargin: Tokens.padding.smaller
active: root.width > 400
visible: active
@@ -103,12 +103,12 @@ ColumnLayout {
Layout.bottomMargin: Tokens.padding.large * 2
Layout.fillWidth: true
Layout.topMargin: Tokens.spacing.small
Layout.topMargin: Tokens.spacing.smaller
active: root.rootHeight > 820
visible: active
sourceComponent: RowLayout {
spacing: Tokens.spacing.largeIncreased
spacing: Tokens.spacing.large
Repeater {
model: {
@@ -119,8 +119,7 @@ ColumnLayout {
length: count
}, () => null);
const start = Math.max(0, Weather.currentHourIndex);
return forecast.slice(start, start + count);
return forecast.slice(0, count);
}
ColumnLayout {
+88
View File
@@ -0,0 +1,88 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import Quickshell.Hyprland
import ZShell
import qs.Components
Scope {
LazyLoader {
id: root
property bool closing
property bool freeze
Variants {
model: Quickshell.screens
PanelWindow {
id: win
required property ShellScreen modelData
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: root.closing ? WlrKeyboardFocus.None : WlrKeyboardFocus.Exclusive
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "areapicker"
color: "transparent"
mask: root.closing ? empty : null
screen: modelData
anchors {
bottom: true
left: true
right: true
top: true
}
Region {
id: empty
}
Picker {
loader: root
screen: win.modelData
}
}
}
}
IpcHandler {
function open(): void {
root.freeze = false;
root.closing = false;
root.activeAsync = true;
}
function openFreeze(): void {
root.freeze = true;
root.closing = false;
root.activeAsync = true;
}
target: "picker"
}
CustomShortcut {
name: "screenshot"
onPressed: {
root.freeze = false;
root.closing = false;
root.activeAsync = true;
}
}
CustomShortcut {
name: "screenshotFreeze"
onPressed: {
root.freeze = true;
root.closing = false;
root.activeAsync = true;
}
}
}
+70
View File
@@ -0,0 +1,70 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Components
import ZShell.Config
Singleton {
id: root
property list<var> apps: {
var map = new Map();
const pinnedApps = Config.dock?.pinnedApps ?? [];
for (const appId of pinnedApps) {
if (!map.has(appId.toLowerCase()))
map.set(appId.toLowerCase(), ({
pinned: true,
toplevels: []
}));
}
if (pinnedApps.length > 0) {
map.set("SEPARATOR", {
pinned: false,
toplevels: []
});
}
var values = [];
for (const [key, value] of map) {
values.push(appEntryComp.createObject(null, {
appId: key,
toplevels: value.toplevels,
pinned: value.pinned
}));
}
return values;
}
function isPinned(appId) {
return Config.dock.pinnedApps.indexOf(appId) !== -1;
}
function togglePin(appId) {
if (root.isPinned(appId)) {
Config.dock.pinnedApps = Config.dock.pinnedApps.filter(id => id !== appId);
} else {
Config.dock.pinnedApps = Config.dock.pinnedApps.concat([appId]);
}
}
Component {
id: appEntryComp
TaskbarAppEntry {
}
}
component TaskbarAppEntry: QtObject {
id: wrapper
required property string appId
required property bool pinned
required property list<var> toplevels
}
}
+63
View File
@@ -0,0 +1,63 @@
// FetchPresets.qml
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property var parsedPresets: ({})
readonly property var presets: parsedPresets
property bool ready: false
function accents(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.accents ?? [];
}
function defaultAccent(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.default_accent ?? "";
}
function modes(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.modes ?? [];
}
function presetNames() {
return Object.keys(parsedPresets);
}
function variantNames(presetName) {
const preset = parsedPresets[presetName];
if (!preset || !preset.variants)
return [];
return Object.keys(preset.variants);
}
Process {
command: ["zshell-cli", "scheme", "list-presets", "--json"]
running: true
stdout: StdioCollector {
onStreamFinished: {
try {
const parsed = JSON.parse(text);
root.parsedPresets = parsed.presets ?? {};
root.ready = true;
} catch (e) {
console.error("Failed to parse presets JSON:", e);
}
}
}
}
}
@@ -9,13 +9,13 @@ Singleton {
id: root
readonly property bool enabled: service.enabled
readonly property int end: Config.display.nightlight.scheduleEnd
readonly property int end: Config.general.color.scheduleHyprsunsetEnd
property bool manualToggle: false
readonly property int start: Config.display.nightlight.scheduleStart
readonly property int temp: Config.display.nightlight.temp
readonly property int start: Config.general.color.scheduleHyprsunsetStart
readonly property int temp: Config.general.color.hyprsunsetTemp
function checkStartup(): void {
if (!Config.display.nightlight.schedule)
if (!Config.general.color.scheduleHyprsunset)
return;
service.apply();
@@ -26,14 +26,12 @@ Singleton {
service.toggle();
}
NightlightManager {
HyprsunsetManager {
id: service
activeAuto: Config.display.nightlight.schedule
activeAuto: Config.general.color.scheduleHyprsunset
endTime: root.end
startTime: root.start
temp: root.temp
fadeDuration: Config.display.nightlight.fadeDuration / 1000
useNativeNightlight: Config.display.nightlight.useNative
}
}
-10
View File
@@ -77,16 +77,6 @@ Singleton {
"99": "thunderstorm"
})
function getAppCategoryIcon(name: string, fallback: string): string {
const categories = DesktopEntries.heuristicLookup(name)?.categories;
if (categories)
for (const [key, value] of Object.entries(categoryIcons))
if (categories.includes(key))
return value;
return fallback;
}
function getAppIcon(name: string, fallback: string): string {
const icon = DesktopEntries.heuristicLookup(name)?.icon;
if (fallback !== "undefined")
+6 -6
View File
@@ -11,14 +11,14 @@ import qs.Services
Singleton {
id: root
readonly property int darkEnd: Config.colors.scheduleDarkEnd
readonly property int darkStart: Config.colors.scheduleDarkStart
readonly property bool enabled: Config.colors.scheduleDark && Config.colors.schemeGen
readonly property int darkEnd: Config.general.color.scheduleDarkEnd
readonly property int darkStart: Config.general.color.scheduleDarkStart
readonly property bool enabled: Config.general.color.scheduleDark && Config.general.color.schemeGeneration
function applyDarkMode() {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "dark"]);
Config.colors.mode = "dark";
Config.general.color.mode = "dark";
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-dark'"]);
@@ -30,13 +30,13 @@ Singleton {
function applyLightMode() {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "light"]);
Config.colors.mode = "light";
Config.general.color.mode = "light";
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-light'"]);
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Dark.colors/color_scheme_path=\\1Light.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`]);
if (Config.colors.neovimColors)
if (Config.general.color.neovimColors)
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"onelight\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"]);
}
+114
View File
@@ -0,0 +1,114 @@
pragma Singleton
import Quickshell
import Quickshell.Networking
import QtQuick
Singleton {
id: root
property bool active: false
readonly property list<Network> connectedNetworks: networks.filter(n => n.connected)
property Network connectingNetwork
readonly property list<NetworkDevice> devices: Networking.devices.values
property Network failedNetwork
readonly property list<Network> knownNetworks: networks.filter(n => n.known && !n.connected)
readonly property list<Network> networks: {
const list = [];
for (const d of wifiDevices) {
for (const n of d.networks.values) {
if (!list.includes(n))
list.push(n);
}
}
return list;
}
property bool scanning: false
readonly property list<Network> unknownNetworks: networks.filter(n => !n.known)
readonly property list<WifiDevice> wifiDevices: devices.filter(d => wifiDevice(d))
readonly property bool wifiEnabled: Networking.wifiEnabled
signal networkConnected
function isSecure(security): bool {
return (security === WifiSecurityType.WpaPsk || security === WifiSecurityType.Wpa2Psk || security === WifiSecurityType.Sae);
}
function requestConnect(network: Network, secret = undefined): void {
connectingNetwork = network;
if (secret === undefined || secret === "") {
network.connect();
} else {
(network as WifiNetwork).connectWithPsk(secret);
}
}
function setScan(value: bool): void {
for (const d of wifiDevices) {
d.scannerEnabled = value;
}
}
function setWifi(value: bool): void {
Networking.wifiEnabled = value;
}
function wifiDevice(dev): bool {
return dev.type === DeviceType.Wifi;
}
onActiveChanged: {
for (const d of wifiDevices)
d.scannerEnabled = active;
}
Timer {
id: flushFailed
interval: 5000
running: root.failedNetwork
onTriggered: {
root.failedNetwork = null;
}
}
Timer {
id: checkConnection
interval: 500
repeat: true
running: root.connectingNetwork
onTriggered: {
var completedNetwork;
switch (root.connectingNetwork.state) {
case ConnectionState.Connecting:
break;
case ConnectionState.Connected:
completedNetwork = root.connectingNetwork;
root.networkConnected();
break;
case ConnectionState.Disconnected:
root.failedNetwork = root.connectingNetwork;
completedNetwork = root.connectingNetwork;
break;
}
if (completedNetwork) {
root.connectingNetwork = null;
}
}
}
Timer {
interval: 10000
running: checkConnection.running
onTriggered: {
root.failedNetwork = root.connectingNetwork;
root.connectingNetwork = null;
}
}
}
+242
View File
@@ -0,0 +1,242 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import ZShell.Internal
import ZShell.Config
Singleton {
id: root
property real _downloadSpeed: 0
property real _downloadTotal: 0
property real _initialRxBytes: 0
property real _initialTxBytes: 0
property bool _initialized: false
property real _prevRxBytes: 0
property real _prevTimestamp: 0
property real _prevTxBytes: 0
property real _uploadSpeed: 0
property real _uploadTotal: 0
readonly property CircularBuffer downloadBuffer: _downloadBuffer
readonly property real downloadSpeed: _downloadSpeed
readonly property real downloadTotal: _downloadTotal
readonly property int historyLength: Config.dashboard.performance.showVram ? 70 : 30
property int refCount: 0
readonly property CircularBuffer uploadBuffer: _uploadBuffer
readonly property real uploadSpeed: _uploadSpeed
readonly property real uploadTotal: _uploadTotal
function resetSampling(): void {
netDevFile.reload();
const content = netDevFile.text();
if (!content)
return;
const data = root.parseNetDev(content);
const now = Date.now();
root._prevRxBytes = data.rx;
root._prevTxBytes = data.tx;
root._prevTimestamp = now;
}
function formatBytes(bytes: real): var {
// Handle negative or invalid values
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
return {
value: 0,
unit: "B/s"
};
}
if (bytes < 1024) {
return {
value: bytes,
unit: "B/s"
};
} else if (bytes < 1024 * 1024) {
return {
value: bytes / 1024,
unit: "KB/s"
};
} else if (bytes < 1024 * 1024 * 1024) {
return {
value: bytes / (1024 * 1024),
unit: "MB/s"
};
} else {
return {
value: bytes / (1024 * 1024 * 1024),
unit: "GB/s"
};
}
}
function formatBytesTotal(bytes: real): var {
// Handle negative or invalid values
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
return {
value: 0,
unit: "B"
};
}
if (bytes < 1024) {
return {
value: bytes,
unit: "B"
};
} else if (bytes < 1024 * 1024) {
return {
value: bytes / 1024,
unit: "KB"
};
} else if (bytes < 1024 * 1024 * 1024) {
return {
value: bytes / (1024 * 1024),
unit: "MB"
};
} else {
return {
value: bytes / (1024 * 1024 * 1024),
unit: "GB"
};
}
}
function parseNetDev(content: string): var {
const lines = content.split("\n");
let totalRx = 0;
let totalTx = 0;
for (let i = 2; i < lines.length; i++) {
const line = lines[i].trim();
if (!line)
continue;
const parts = line.split(/\s+/);
if (parts.length < 10)
continue;
const iface = parts[0].replace(":", "");
// Skip loopback interface
if (iface === "lo")
continue;
const rxBytes = parseFloat(parts[1]) || 0;
const txBytes = parseFloat(parts[9]) || 0;
totalRx += rxBytes;
totalTx += txBytes;
}
return {
rx: totalRx,
tx: totalTx
};
}
CircularBuffer {
id: _downloadBuffer
capacity: root.historyLength + 1
}
CircularBuffer {
id: _uploadBuffer
capacity: root.historyLength + 1
}
FileView {
id: netDevFile
path: "/proc/net/dev"
}
Timer {
interval: Config.dashboard.resourceUpdateInterval
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
if (root.refCount <= 0) {
root.resetSampling();
return;
}
netDevFile.reload();
const content = netDevFile.text();
if (!content)
return;
const data = root.parseNetDev(content);
const now = Date.now();
if (!root._initialized) {
root._initialRxBytes = data.rx;
root._initialTxBytes = data.tx;
root._prevRxBytes = data.rx;
root._prevTxBytes = data.tx;
root._prevTimestamp = now;
root._initialized = true;
return;
}
const timeDelta = (now - root._prevTimestamp) / 1000; // seconds
if (timeDelta > 0) {
// Calculate byte deltas
let rxDelta = data.rx - root._prevRxBytes;
let txDelta = data.tx - root._prevTxBytes;
// Handle counter overflow (when counters wrap around from max to 0)
// This happens when counters exceed 32-bit or 64-bit limits
if (rxDelta < 0) {
// Counter wrapped around - assume 64-bit counter
rxDelta += Math.pow(2, 64);
}
if (txDelta < 0) {
txDelta += Math.pow(2, 64);
}
// Calculate speeds
root._downloadSpeed = rxDelta / timeDelta;
root._uploadSpeed = txDelta / timeDelta;
if (root._downloadSpeed >= 0 && isFinite(root._downloadSpeed))
_downloadBuffer.push(root._downloadSpeed);
if (root._uploadSpeed >= 0 && isFinite(root._uploadSpeed))
_uploadBuffer.push(root._uploadSpeed);
}
// Calculate totals with overflow handling
let downTotal = data.rx - root._initialRxBytes;
let upTotal = data.tx - root._initialTxBytes;
// Handle counter overflow for totals
if (downTotal < 0) {
downTotal += Math.pow(2, 64);
}
if (upTotal < 0) {
upTotal += Math.pow(2, 64);
}
root._downloadTotal = downTotal;
root._uploadTotal = upTotal;
root._prevRxBytes = data.rx;
root._prevTxBytes = data.tx;
root._prevTimestamp = now;
}
}
}
+61 -101
View File
@@ -41,9 +41,8 @@ Item {
property real rsy: Math.min(sy, ey)
readonly property real scaleRatio: Hypr.monitorFor(screen).scale
required property ShellScreen screen
required property ScreenState screenState
property real sh: Math.abs(sy - ey)
readonly property color shadowColor: Hypr.options.decoration.shadow?.color ?? "black"
readonly property color shadowColor: Hypr.options.decoration.shadow.color
readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled
readonly property var shadowOffset: Hypr.options.decoration.shadow.offset
readonly property int shadowRange: Hypr.options.decoration.shadow.range
@@ -98,6 +97,10 @@ Item {
const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : [];
const postCmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : null;
overlay.visible = border.visible = false;
handles.visible = false;
toolbar.visible = false;
ZShellIo.saveItem(captureLayer, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => {
const finalize = finalPath => {
if (copyToClipboard) {
@@ -123,35 +126,39 @@ Item {
focus: true
opacity: 0
Behavior on rsx {
enabled: !selectArea.pressed && root.mode === "select"
Anim {}
}
Behavior on rsy {
enabled: !selectArea.pressed && root.mode === "select"
Anim {}
}
Behavior on sh {
enabled: !selectArea.pressed && root.mode === "select"
Anim {}
}
Behavior on sw {
enabled: !selectArea.pressed && root.mode === "select"
Anim {}
}
Behavior on opacity {
Anim {
duration: 300
}
}
Behavior on rsx {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {
}
}
Behavior on rsy {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {
}
}
Behavior on sh {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {
}
}
Behavior on sw {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {
}
}
Component.onCompleted: {
Hypr.extras.refreshOptions();
root.forceActiveFocus();
screencopy.active = true;
opacity = 1;
@@ -216,63 +223,13 @@ Item {
onReleased: root.enterEditMode()
}
Connections {
function onClosingChanged(): void {
if (root.loader.closing)
closeAnim.start();
else {
closeAnim.stop();
returnAnim.start();
}
}
target: root.loader
}
ParallelAnimation {
id: returnAnim
Anim {
duration: 300
property: "opacity"
target: root
to: 1
}
Anim {
property: "rsx"
target: root
to: Math.min(root.sx, root.ex)
}
Anim {
property: "rsy"
target: root
to: Math.min(root.sy, root.ey)
}
Anim {
property: "sw"
target: root
to: Math.abs(root.sx - root.ex)
}
Anim {
property: "sh"
target: root
to: Math.abs(root.sy - root.ey)
}
}
SequentialAnimation {
id: closeAnim
ScriptAction {
script: {
const states = ShellState.allStates();
for (const s of states)
s.screenshot = false;
}
PropertyAction {
property: "closing"
target: root.loader
value: true
}
ParallelAnimation {
@@ -283,19 +240,19 @@ Item {
to: 0
}
Anim {
ExAnim {
properties: "rsx,rsy"
target: root
to: 0
}
Anim {
ExAnim {
property: "sw"
target: root
to: root.screen.width
}
Anim {
ExAnim {
property: "sh"
target: root
to: root.screen.height
@@ -314,12 +271,17 @@ Item {
anchors.fill: parent
ScreencopyView {
Loader {
id: screencopy
active: false
anchors.fill: parent
captureSource: root.screen
paintCursor: false
asynchronous: true
sourceComponent: ScreencopyView {
captureSource: root.screen
paintCursor: false
}
}
AnnotationCanvas {
@@ -328,7 +290,6 @@ Item {
anchors.fill: parent
inkColor: root.inkColor
layer.enabled: true
selection: selectionRect
tool: root.mode === "edit" && root.tool !== "move" ? root.tool : "none"
layer.effect: MultiEffect {
@@ -340,14 +301,15 @@ Item {
}
}
CustomRect {
Rectangle {
id: overlay
anchors.fill: parent
color: "white"
layer.enabled: true
opacity: 0.3
visible: screencopy.hasContent
radius: root.realRounding
visible: screencopy.item?.hasContent ?? false
layer.effect: MultiEffect {
maskEnabled: true
@@ -376,44 +338,39 @@ Item {
}
}
CustomRect {
Rectangle {
id: border
border.color: Colors.palette.m3primary
border.width: root.realBorderWidth
color: "transparent"
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
visible: screencopy.hasContent
visible: screencopy.item?.hasContent ?? false
x: selectionRect.x - root.realBorderWidth
y: selectionRect.y - root.realBorderWidth
Behavior on border.color {
Anim {}
Anim {
}
}
}
SelectionHandles {
id: handles
border: root.realBorderWidth
selH: root.sh
selW: root.sw
selX: root.rsx
selY: root.rsy
selection: selectionRect
tool: root.tool
visible: root.mode === "edit" && root.tool === "move"
onBoundsChanged: (x, y, w, h) => {
let nx = Math.max(0, x);
let ny = Math.max(0, y);
let nw = Math.min(w, root.screen.width - nx);
let nh = Math.min(h, root.screen.height - ny);
root.sx = nx;
root.sy = ny;
root.ex = nx + nw;
root.ey = ny + nh;
root.sx = x;
root.sy = y;
root.ex = x + w;
root.ey = y + h;
}
onMoveBy: (dx, dy) => {
const w = root.sw, h = root.sh;
@@ -434,7 +391,7 @@ Item {
tool: root.tool
visible: root.mode === "edit"
x: Math.max(8, Math.min(root.rsx + root.sw / 2 - width / 2, root.screen.width - width - 8))
y: (root.rsy + root.sh + height + Tokens.padding.small <= root.screen.height) ? root.rsy + root.sh + Tokens.padding.small : (root.rsy - height - Tokens.padding.small <= 0) ? root.rsy + root.sh - height - Tokens.padding.small : root.rsy - height - Tokens.padding.small
y: (root.rsy + root.sh + height + Tokens.padding.normal <= root.screen.height) ? root.rsy + root.sh + Tokens.padding.normal : (root.rsy - height - Tokens.padding.normal <= 0) ? root.rsy + root.sh - height - Tokens.padding.normal : root.rsy - height - Tokens.padding.normal
onCancelRequested: root.cancel()
onClearRequested: annotations.clearAll()
@@ -444,4 +401,7 @@ Item {
onToolSelected: t => root.tool = t
onUndoRequested: annotations.undo()
}
component ExAnim: Anim {
}
}
+57 -52
View File
@@ -8,13 +8,13 @@ Item {
property color inkColor: "#ff3b30"
property real inkWidth: 4
required property Rectangle selection
property string tool: "none"
signal changed
function clearAll() {
canvas.clear();
committedCanvas.clear();
previewCanvas.clear();
changed();
}
@@ -54,85 +54,90 @@ Item {
}
function undo() {
canvas.undoLastGroup();
committedCanvas.undoLastGroup();
changed();
}
anchors.fill: parent
enabled: tool !== "none"
StrokeCanvas {
id: canvas
id: committedCanvas
anchors.fill: parent
penColor: root.inkColor
penWidth: root.inkWidth
}
MouseArea {
StrokeCanvas {
id: previewCanvas
anchors.fill: parent
penColor: root.inkColor
penWidth: root.inkWidth
}
PointHandler {
id: drawHandler
property real startX
property real startY
property bool started: false
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
cursorShape: undefined
enabled: root.enabled
enabled: root.tool !== "none"
onPositionChanged: e => {
const x = e.x;
const y = e.y;
if (!pressed)
onActiveChanged: {
if (active)
return;
if (root.tool === "pen") {
canvas.appendPoint(x, y);
committedCanvas.endStroke();
} else if (started) {
const pts = root.sampleShape(root.tool, startX, startY, point.position.x, point.position.y);
if (pts.length > 0) {
committedCanvas.beginStroke(pts[0][0], pts[0][1], true);
for (let i = 1; i < pts.length; i++)
committedCanvas.appendPoint(pts[i][0], pts[i][1]);
committedCanvas.endStroke();
}
previewCanvas.clear();
}
started = false;
root.changed();
}
onPointChanged: {
if (!active)
return;
if (point.pressedButtons & Qt.RightButton) {
root.clearAll();
return;
}
const pts = root.sampleShape(root.tool, startX, startY, x, y);
if (pts.length > 0) {
canvas.beginStroke(pts[0][0], pts[0][1], true);
for (let i = 1; i < pts.length; i++)
canvas.appendPoint(pts[i][0], pts[i][1]);
}
}
onPressed: e => {
if (e.button & Qt.LeftButton) {
startX = e.x;
startY = e.y;
canvas.beginStroke(e.x, e.y);
} else if (e.button & Qt.RightButton) {
root.clearAll();
}
}
onPressedChanged: if (!pressed)
canvas.endStroke()
}
HoverHandler {
cursorShape: {
if (root.tool === "none")
return undefined;
const x = point.position.x;
const y = point.position.y;
const withinXWidth = x > root.selection.x && x < root.selection.x + root.selection.implicitWidth;
const withinYHeight = y > root.selection.y && y < root.selection.y + root.selection.implicitHeight;
if (withinXWidth && withinYHeight) {
if (!drawHandler.pressed)
canvas.showHover(x, y);
else
canvas.showHover(drawHandler.mouseX, drawHandler.mouseY);
return Qt.BlankCursor;
} else {
canvas.hideHover();
return undefined;
if (root.tool === "pen") {
if (!started) {
started = true;
committedCanvas.beginStroke(x, y);
return;
}
committedCanvas.appendPoint(x, y);
return;
}
if (!started) {
started = true;
startX = point.pressPosition.x;
startY = point.pressPosition.y;
}
const pts = root.sampleShape(root.tool, startX, startY, x, y);
if (pts.length > 0) {
previewCanvas.beginStroke(pts[0][0], pts[0][1], true);
for (let i = 1; i < pts.length; i++)
previewCanvas.appendPoint(pts[i][0], pts[i][1]);
}
}
enabled: root.enabled
}
}
+4 -10
View File
@@ -49,9 +49,9 @@ CustomRect {
signal undoRequested
color: Colors.palette.m3surface
height: row.implicitHeight + Tokens.padding.small * 2
height: row.implicitHeight + Tokens.padding.normal * 2
radius: Tokens.rounding.full
width: row.implicitWidth + Tokens.padding.small * 2
width: row.implicitWidth + Tokens.padding.normal * 2
RowLayout {
id: row
@@ -73,7 +73,6 @@ CustomRect {
required property var modelData
color: root.tool === modelData.id ? "#3584e4" : "transparent"
font: Tokens.font.icon.medium
icon: tool.modelData.glyph
inactiveColor: root.tool === modelData.id ? Colors.palette.m3primary : "transparent"
inactiveOnColor: root.tool === modelData.id ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
@@ -99,11 +98,11 @@ CustomRect {
model: root.colorPalette
delegate: IconButton {
readonly property real buttonSize: toolRow.implicitHeight - Tokens.padding.small
readonly property real buttonSize: toolRow.implicitHeight - Tokens.padding.normal
required property color modelData
fillWidth: false
font: Tokens.font.icon.medium
font.pointSize: 0
icon: ""
implicitHeight: buttonSize
implicitWidth: buttonSize
@@ -128,7 +127,6 @@ CustomRect {
spacing: Tokens.spacing.extraSmall
IconButton {
font: Tokens.font.icon.medium
icon: "undo"
inactiveColor: "transparent"
inactiveOnColor: Colors.palette.m3onSurface
@@ -140,7 +138,6 @@ CustomRect {
}
IconButton {
font: Tokens.font.icon.medium
icon: "delete_history"
inactiveColor: "transparent"
inactiveOnColor: Colors.palette.m3onSurface
@@ -152,7 +149,6 @@ CustomRect {
}
IconButton {
font: Tokens.font.icon.medium
icon: "content_copy"
inactiveColor: "transparent"
inactiveOnColor: Colors.palette.m3onSurface
@@ -164,7 +160,6 @@ CustomRect {
}
IconButton {
font: Tokens.font.icon.medium
icon: "delete_forever"
inactiveColor: "transparent"
inactiveOnColor: Colors.palette.m3onSurface
@@ -176,7 +171,6 @@ CustomRect {
}
IconButton {
font: Tokens.font.icon.medium
icon: "check"
inactiveColor: "transparent"
inactiveOnColor: Colors.palette.m3onSurface
+14 -40
View File
@@ -1,23 +1,18 @@
pragma ComponentBehavior: Bound
import QtQuick
import ZShell.Internal
import qs.Services
Item {
id: root
required property int border
property color handleBorder: Colors.palette.m3primary
property color handleFill: Colors.palette.m3onPrimary
property color handleBorder: "#3584e4"
property color handleFill: "white"
property real hs: 12
property real minSize: 24
required property real selH
required property real selW
required property real selX
required property real selY
required property Rectangle selection
required property string tool
signal boundsChanged(x: real, y: real, w: real, h: real)
signal dragFinished
@@ -28,16 +23,12 @@ Item {
x: selX
y: selY
Component.onDestruction: Cursor.clearOverride()
DragHandler {
id: moveHandler
property real accumX: 0
property real accumY: 0
cursorShape: undefined
dragThreshold: 0
target: null
onActiveChanged: {
@@ -54,63 +45,51 @@ Item {
}
}
HoverHandler {
cursorShape: {
if (root.tool !== "move")
return undefined;
if ((moveHandler.centroid.pressedButtons & Qt.LeftButton) && moveHandler.active)
return Qt.ClosedHandCursor;
else
return Qt.OpenHandCursor;
}
}
Handle {
edge: "tl"
x: -(root.hs + root.border) / 2
y: -(root.hs + root.border) / 2
x: -root.hs / 2
y: -root.hs / 2
}
Handle {
edge: "t"
x: root.width / 2 - root.hs / 2
y: -(root.hs + root.border) / 2
y: -root.hs / 2
}
Handle {
edge: "tr"
x: root.width - root.hs / 2 + root.border / 2
y: -(root.hs + root.border) / 2
x: root.width - root.hs / 2
y: -root.hs / 2
}
Handle {
edge: "r"
x: root.width - root.hs / 2 + root.border / 2
x: root.width - root.hs / 2
y: root.height / 2 - root.hs / 2
}
Handle {
edge: "br"
x: root.width - root.hs / 2 + root.border / 2
y: root.height - root.hs / 2 + root.border / 2
x: root.width - root.hs / 2
y: root.height - root.hs / 2
}
Handle {
edge: "b"
x: root.width / 2 - root.hs / 2
y: root.height - root.hs / 2 + root.border / 2
y: root.height - root.hs / 2
}
Handle {
edge: "bl"
x: -(root.hs + root.border) / 2
y: root.height - root.hs / 2 + root.border / 2
x: -root.hs / 2
y: root.height - root.hs / 2
}
Handle {
edge: "l"
x: -(root.hs + root.border) / 2
x: -root.hs / 2
y: root.height / 2 - root.hs / 2
}
@@ -128,8 +107,6 @@ Item {
z: 10
HoverHandler {
id: hover
cursorShape: {
switch (handle.edge) {
case "tl":
@@ -155,7 +132,6 @@ Item {
property real startX
property real startY
dragThreshold: 0
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.TakeOverForbidden
target: null
@@ -165,10 +141,8 @@ Item {
startY = root.y;
startW = root.width;
startH = root.height;
Cursor.setOverride(hover.cursorShape);
} else {
root.dragFinished();
Cursor.clearOverride();
}
}
onTranslationChanged: {
-9
View File
@@ -5,15 +5,6 @@ import Quickshell
Singleton {
property var _regexCache: ({})
function percent(value: int): string {
// TRANSLATORS: %1 = a number
return qsTr("%1%").arg(value);
}
function percentOne(value: real): string {
return percent(Math.round(value * 100));
}
function testRegexList(filterList: list<string>, target: string): bool {
const regexChecker = /^\^.*\$$/;
for (const filter of filterList) {
+16
View File
@@ -0,0 +1,16 @@
pragma Singleton
import Quickshell
Singleton {
property var bars: new Map()
property var screens: new Map()
function getForActive(): PersistentProperties {
return screens.get(Hypr.focusedMonitor);
}
function load(screen: ShellScreen, visibilities: var): void {
screens.set(Hypr.monitorFor(screen), visibilities);
}
}
+1 -4
View File
@@ -17,10 +17,7 @@ Singleton {
watchChanges: true
onAdapterUpdated: writeAdapter()
onFileChanged: {
reload();
Wallpapers.previewColorLock = false;
}
onFileChanged: reload()
JsonAdapter {
id: adapter
+8 -68
View File
@@ -5,10 +5,8 @@ import Quickshell
import Quickshell.Io
import QtQuick
import ZShell.Models
import ZShell.Internal
import ZShell.Config
import qs.Components
import qs.Services
import qs.Modules
import qs.Helpers
import qs.Paths
@@ -19,8 +17,6 @@ Searcher {
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent
property alias monitorCrops: monitorCrops
property bool pendingPreviewClear
property bool previewColorLock
property string previewPath
property bool recentlyChanged
property bool showPreview: false
@@ -38,10 +34,9 @@ Searcher {
function preview(path: string): void {
previewPath = path;
if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${previewPath}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
showPreview = true;
if (Config.colors.schemeGen)
previewColorsProc.running = true;
}
function setCrop(screen: string, rect: rect, zoom: real): void {
@@ -57,6 +52,7 @@ Searcher {
height: rect.height,
zoom: zoom
};
// root.crops = updated;
}
function setWallpaper(path: string): void {
@@ -64,16 +60,14 @@ Searcher {
WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 1, 1), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.colors.schemeGen)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
}
function stopPreview(): void {
showPreview = false;
if (previewColorLock)
pendingPreviewClear = true;
else
Colors.showPreview = false;
if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
}
extraOpts: useFuzzy ? ({}) : ({
@@ -83,11 +77,6 @@ Searcher {
list: wallpapers.entries
useFuzzy: true
onPreviewColorLockChanged: {
if (!previewColorLock && pendingPreviewClear)
Colors.showPreview = false;
}
IpcHandler {
function set(path: string): void {
root.setWallpaper(path);
@@ -129,53 +118,4 @@ Searcher {
path: Config.general.wallpaperPath
recursive: true
}
Process {
id: previewColorsProc
command: ["zshell-cli", "scheme", "generate", "-p", "-i", root.previewPath]
stdout: StdioCollector {
onStreamFinished: {
Colors.load(text, true);
Colors.showPreview = true;
}
}
}
WallpaperService {
id: wallpaper
path: root.current
}
Variants {
model: Quickshell.screens
WallpaperObject {
readonly property var crops: root.getCrop(modelData.name)
required property var modelData
cropHeight: crops?.height ?? 1.0
cropWidth: crops?.width ?? 1.0
cropX: crops?.x ?? 0.0
cropY: crops?.y ?? 0.0
screen: modelData.name
service: wallpaper
zoom: crops?.zoom ?? 1.0
Behavior on cropHeight {
Anim {}
}
Behavior on cropWidth {
Anim {}
}
Behavior on cropX {
Anim {}
}
Behavior on cropY {
Anim {}
}
}
}
}
+7 -28
View File
@@ -3,36 +3,25 @@ pragma Singleton
import QtQuick
import Quickshell
import ZShell
import ZShell.Internal
import ZShell.Config
Singleton {
id: root
readonly property var cachedCities: new Map()
property var cc
property string city
readonly property string description: cc?.weatherDesc ?? qsTr("No weather")
readonly property string feelsLike: formatTemp(cc?.feelsLikeC)
property list<var> forecast
property list<var> hourlyForecast
readonly property int humidity: cc?.humidity ?? 0
readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert"
property string loc
readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), Config.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--"
readonly property string sunset: cc ? Qt.formatDateTime(new Date(cc.sunset), Config.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--"
readonly property string temp: formatTemp(cc?.tempC)
readonly property real windSpeed: cc?.windSpeed ?? 0
readonly property CircularBuffer precipitationForecast: precipitationBuffer
property var cc
property string city
property list<var> forecast
property list<var> hourlyForecast
readonly property int currentHourIndex: {
const now = new Date();
for (let i = 0; i < hourlyForecast.length; i++) {
if (new Date(hourlyForecast[i].timestamp.replace("T", " ")) >= now)
return i;
}
return -1;
}
property string loc
function fetchCityFromCoords(coords: string): void {
if (cachedCities.has(coords)) {
@@ -123,12 +112,11 @@ Singleton {
forecast = forecastList;
const hourlyList = [];
const startOfToday = new Date();
startOfToday.setHours(0, 0, 0, 0);
const now = new Date();
for (let i = 0; i < json.hourly.time.length; i++) {
const time = new Date(json.hourly.time[i].replace("T", " "));
if (time < startOfToday)
if (time < now)
continue;
hourlyList.push({
@@ -141,10 +129,6 @@ Singleton {
});
}
hourlyForecast = hourlyList;
precipitationBuffer.clear();
for (const hour of hourlyList)
precipitationBuffer.push(hour.precipChance);
});
}
@@ -274,11 +258,6 @@ Singleton {
onLocChanged: fetchWeatherData()
CircularBuffer {
id: precipitationBuffer
capacity: 24
}
Connections {
function onWeatherLocationChanged(): void {
root.reload();
+17 -29
View File
@@ -4,7 +4,6 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Components
import qs.Modules.Bar.Popouts
@@ -13,16 +12,15 @@ GridLayout {
required property bool fullscreen
required property bool horizontal
required property bool isHovered
required property Wrapper popouts
required property ClipWrapper popoutsWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property int vPadding: 6
required property PersistentProperties visibilities
function axisCenterOf(item: Item): real {
const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2);
return horizontal ? Math.floor(c.x) : Math.floor(c.y);
return horizontal ? c.x : c.y;
}
function checkPopout(pos: real): void {
@@ -35,7 +33,7 @@ GridLayout {
return;
}
if (screenState.sidebar || screenState.dashboard || screenState.resources || screenState.settings)
if (visibilities.sidebar || visibilities.dashboard || visibilities.resources || visibilities.settings)
return;
if (id === "statusIcons" && Config.bar.popouts.statusIcons) {
@@ -89,12 +87,12 @@ GridLayout {
role: "id"
DelegateChoice {
roleValue: "nightlight"
roleValue: "hyprsunset"
delegate: EntryWrapper {
NightlightWidget {
HyprsunsetWidget {
horizontal: root.horizontal
visible: !root.fullscreen || root.isHovered
visible: !root.fullscreen
}
}
}
@@ -115,7 +113,7 @@ GridLayout {
Workspaces {
horizontal: root.horizontal
screen: root.screen
visible: !root.fullscreen || root.isHovered
visible: !root.fullscreen
}
}
}
@@ -128,7 +126,7 @@ GridLayout {
horizontal: root.horizontal
loader: root
popouts: root.popouts
visible: !root.fullscreen || root.isHovered
visible: !root.fullscreen
}
}
}
@@ -139,7 +137,6 @@ GridLayout {
delegate: EntryWrapper {
StatusIcons {
horizontal: root.horizontal
visible: !root.fullscreen || root.isHovered
}
}
}
@@ -150,8 +147,8 @@ GridLayout {
delegate: EntryWrapper {
Resources {
horizontal: root.horizontal
screenState: root.screenState
visible: !root.fullscreen || root.isHovered
visibilities: root.visibilities
visible: !root.fullscreen
}
}
}
@@ -162,7 +159,7 @@ GridLayout {
delegate: EntryWrapper {
UpdatesWidget {
horizontal: root.horizontal
visible: !root.fullscreen || root.isHovered
visible: !root.fullscreen
}
}
}
@@ -174,8 +171,8 @@ GridLayout {
NotifBell {
horizontal: root.horizontal
popouts: root.popouts
screenState: root.screenState
visible: !root.fullscreen || root.isHovered
visibilities: root.visibilities
visible: !root.fullscreen
}
}
}
@@ -188,8 +185,8 @@ GridLayout {
horizontal: root.horizontal
loader: root
popouts: root.popouts
screenState: root.screenState
visible: !root.fullscreen || root.isHovered
visibilities: root.visibilities
visible: !root.fullscreen
}
}
}
@@ -201,16 +198,7 @@ GridLayout {
WindowTitle {
bar: root
horizontal: root.horizontal
visible: !root.fullscreen || root.isHovered
}
}
}
DelegateChoice {
roleValue: "network"
delegate: EntryWrapper {
NetworkWidget {
visible: !root.fullscreen
}
}
}
@@ -221,7 +209,7 @@ GridLayout {
delegate: EntryWrapper {
MediaWidget {
horizontal: root.horizontal
visible: !root.fullscreen || root.isHovered
visible: !root.fullscreen
}
}
}
+5 -7
View File
@@ -10,20 +10,20 @@ Item {
id: root
readonly property int clampedExtent: Math.max(Config.bar.border, extent)
readonly property int contentThickness: Math.floor(Math.max(Config.bar.height, Tokens.sizes.bar.innerSize * (horizontal ? 1 : 1.5))) + padding * 2
readonly property int contentThickness: Math.floor(Math.max(Config.bar.height, Tokens.bar.innerSize * (horizontal ? 1 : 1.5))) + padding * 2
readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentThickness
property real extent: fullscreen ? 0 : Config.bar.border
required property bool fullscreen
readonly property bool horizontal: position !== "left"
property bool isHovered
readonly property int padding: Math.max(Tokens.padding.small, Config.bar.border)
readonly property int padding: Math.max(Tokens.padding.smaller, Config.bar.border)
required property Wrapper popouts
required property ClipWrapper popoutsWrapper
required property string position
required property ShellScreen screen
readonly property bool shouldBeVisible: (!fullscreen && (!Config.bar.autoHide || screenState.bar)) || isHovered
readonly property bool shouldBeVisible: !fullscreen && (!Config.bar.autoHide || visibilities.bar || isHovered)
readonly property int vPadding: 6
required property ScreenState screenState
required property PersistentProperties visibilities
function checkPopout(pos: real): void {
(content.item as Bar)?.checkPopout(pos);
@@ -79,13 +79,11 @@ Item {
sourceComponent: Bar {
fullscreen: root.fullscreen
height: root.contentThickness
horizontal: root.horizontal
isHovered: root.isHovered
popouts: root.popouts
popoutsWrapper: root.popoutsWrapper
screen: root.screen
screenState: root.screenState
visibilities: root.visibilities
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ Item {
id: root
required property Item bar
required property ScreenState screenState
required property PersistentProperties visibilities
anchors.fill: parent
+28 -15
View File
@@ -13,8 +13,7 @@ import qs.Services
WidgetBase {
id: root
readonly property font font: Tokens.font.clock.family(Config.appearance.font.family.clock).build()
readonly property color contentColor: Colors.palette.m3onSurface
readonly property color contentColor: visibilities.dashboard ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
readonly property string effectiveFormat: {
let fmt = Config.general.dateFormat;
if (!root.horizontal)
@@ -45,9 +44,10 @@ WidgetBase {
}
required property GridLayout loader
required property Wrapper popouts
readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
required property ScreenState screenState
readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.normal * 2 : verticalColumn.implicitHeight + Tokens.padding.larger * 2
required property PersistentProperties visibilities
color: visibilities.dashboard ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : size
implicitWidth: horizontal ? size : baseSize
radius: Tokens.rounding.full
@@ -57,13 +57,14 @@ WidgetBase {
anchors.centerIn: parent
color: root.contentColor
font: root.font
font: Config.appearance.font.family.mono // qmllint disable incompatible-type
height: implicitHeight
text: Time.dateStr
visible: root.horizontal
Behavior on color {
CAnim {}
CAnim {
}
}
}
@@ -71,7 +72,7 @@ WidgetBase {
id: verticalColumn
anchors.centerIn: parent
spacing: Tokens.padding.extraSmall ?? 2
spacing: Tokens.padding.small ?? 2
visible: !root.horizontal
width: root.baseSize
@@ -83,19 +84,20 @@ WidgetBase {
Layout.alignment: Qt.AlignHCenter
color: root.contentColor
font: root.font
font: Config.appearance.font.family.mono // qmllint disable incompatible-type
text: Qt.formatDateTime(Time.date, modelData)
Behavior on color {
CAnim {}
CAnim {
}
}
}
}
Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Tokens.padding.extraSmall ?? 2
Layout.topMargin: Tokens.padding.extraSmall ?? 2
Layout.bottomMargin: Tokens.padding.small ?? 2
Layout.topMargin: Tokens.padding.small ?? 2
color: root.contentColor
height: 1
implicitWidth: root.baseSize * 0.5
@@ -112,7 +114,7 @@ WidgetBase {
Layout.alignment: Qt.AlignHCenter
color: root.contentColor
font: root.font
font: Config.appearance.font.family.mono // qmllint disable incompatible-type
text: {
if (modelData.includes("h"))
return Time.hourStr;
@@ -124,7 +126,8 @@ WidgetBase {
}
Behavior on color {
CAnim {}
CAnim {
}
}
}
}
@@ -132,13 +135,23 @@ WidgetBase {
CustomText {
Layout.alignment: Qt.AlignHCenter
color: root.contentColor
font: root.font
font: Config.appearance.font.family.mono // qmllint disable incompatible-type
text: Qt.formatDateTime(Time.date, "AP")
visible: Config.services.useTwelveHourClock && root.formatParts.timeTokens.length > 0
Behavior on color {
CAnim {}
CAnim {
}
}
}
}
StateLayer {
acceptedButtons: Qt.LeftButton
color: root.contentColor
onClicked: {
root.visibilities.dashboard = !root.visibilities.dashboard;
}
}
}
@@ -7,13 +7,13 @@ import qs.Helpers
Row {
id: root
property real batHeight: Tokens.padding.small * 2
property real batWidth: Tokens.padding.medium * 2
property real batHeight: Tokens.padding.smaller * 2
property real batWidth: Tokens.padding.larger * 2
required property string devState
property real nubHeight: Tokens.padding.extraSmall
property real nubHeight: Tokens.padding.small
property real nubWidth: 2
required property real percentage
property real radius: Tokens.rounding.small / 2
property real radius: Tokens.rounding.smallest / 2
spacing: 1
@@ -28,7 +28,8 @@ Row {
CustomText {
color: Battery.getColors(root.percentage, root.devState).fg
font: Tokens.font.body.builders.small.size(11).weight(Font.Bold).build()
font.pointSize: Tokens.font.size.larger / 1.5
font.weight: 800
height: track.height
horizontalAlignment: Text.AlignHCenter
text: Math.round(root.percentage * 100)
@@ -59,7 +60,8 @@ Row {
clip: true
color: Battery.getColors(root.percentage, root.devState).bg
font: Tokens.font.body.builders.small.size(11).weight(Font.Bold).build()
font.pointSize: Tokens.font.size.larger / 1.5
font.weight: 800
height: track.height
horizontalAlignment: Text.AlignHCenter
text: Math.round(root.percentage * 100)
+1 -1
View File
@@ -6,7 +6,7 @@ import qs.Services
CustomRect {
id: root
readonly property int baseSize: horizontal ? Math.max(Config.bar.height, Tokens.sizes.bar.innerSize) : Math.max(Config.bar.height, Math.floor(Tokens.sizes.bar.innerSize * 1.5))
readonly property int baseSize: horizontal ? Math.max(Config.bar.height, Tokens.bar.innerSize) : Math.max(Config.bar.height, Math.floor(Tokens.bar.innerSize * 1.5))
required property bool horizontal
color: Colors.tPalette.m3surfaceContainer
@@ -8,15 +8,15 @@ import qs.Services
WidgetBase {
id: root
property bool tempEnabled: Nightlight.enabled
property bool tempEnabled: Hyprsunset.enabled
color: root.tempEnabled ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.full
StateLayer {
onClicked: {
Nightlight.toggleNightLight();
Nightlight.manualToggle = true;
Hyprsunset.toggleNightLight();
Hyprsunset.manualToggle = true;
}
}
@@ -25,11 +25,12 @@ WidgetBase {
animate: true
color: root.tempEnabled ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
fill: root.tempEnabled ? 1 : 0
fontStyle: Tokens.font.icon.small
font.pointSize: Tokens.font.size.larger
text: root.tempEnabled ? "lightbulb" : "light_off"
Behavior on fill {
Anim {}
Anim {
}
}
}
}
+14 -10
View File
@@ -12,19 +12,21 @@ WidgetBase {
readonly property string currentMedia: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
readonly property int textWidth: Math.min(metrics.width, 200)
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.small * 2
implicitWidth: horizontal ? layout.implicitWidth + Tokens.padding.small * 2 : baseSize
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.normal * 2
implicitWidth: horizontal ? layout.implicitWidth + Tokens.padding.normal * 2 : baseSize
radius: Tokens.rounding.full
Behavior on implicitHeight {
enabled: !root.horizontal
Anim {}
Anim {
}
}
Behavior on implicitWidth {
enabled: root.horizontal
Anim {}
Anim {
}
}
TextMetrics {
@@ -37,22 +39,23 @@ WidgetBase {
GridLayout {
id: layout
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.small
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.normal
anchors.fill: parent
anchors.leftMargin: root.horizontal ? Tokens.padding.small : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.small : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.small
anchors.leftMargin: root.horizontal ? Tokens.padding.normal : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.normal : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.normal
columns: root.horizontal ? -1 : 1
Behavior on implicitWidth {
Anim {}
Anim {
}
}
MaterialIcon {
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
animate: true
color: Players.active?.isPlaying ? Colors.palette.m3primary : Colors.palette.m3onSurface
fontStyle: Tokens.font.icon.small
font.pointSize: Tokens.font.size.larger
text: Players.active?.isPlaying ? "music_note" : "music_off"
}
@@ -62,6 +65,7 @@ WidgetBase {
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
animate: true
color: Players.active?.isPlaying ? Colors.palette.m3primary : Colors.palette.m3onSurface
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Text.AlignHCenter
implicitHeight: root.horizontal ? root.implicitHeight : root.textWidth
implicitWidth: root.horizontal ? root.textWidth : root.implicitWidth
+11 -9
View File
@@ -11,33 +11,35 @@ WidgetBase {
id: root
required property Wrapper popouts
required property ScreenState screenState
required property PersistentProperties visibilities
color: screenState.sidebar ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
color: visibilities.sidebar ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.full
MaterialIcon {
id: notificationCenterIcon
anchors.centerIn: parent
color: root.screenState.sidebar ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
fill: root.screenState.sidebar ? 1 : 0
fontStyle: Tokens.font.icon.size(16).build()
color: root.visibilities.sidebar ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
fill: root.visibilities.sidebar ? 1 : 0
font.pointSize: Tokens.font.size.larger
text: NotifServer.list.length ? "\uf4fe" : "\ue7f4"
Behavior on color {
CAnim {}
CAnim {
}
}
Behavior on fill {
Anim {}
Anim {
}
}
}
StateLayer {
color: root.screenState.sidebar ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
color: root.visibilities.sidebar ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
onClicked: {
root.screenState.sidebar = !root.screenState.sidebar;
root.visibilities.sidebar = !root.visibilities.sidebar;
}
}
}
+19 -32
View File
@@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import QtQuick.Shapes
import qs.Components
import ZShell.Config
import qs.Services
@@ -12,7 +12,6 @@ GridLayout {
property real animatedPercentage: 0
readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI
readonly property real dpr: (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1
required property bool horizontal
property string icon
required property color iconColor
@@ -23,11 +22,7 @@ GridLayout {
property bool warning: percentage * 100 >= warningThreshold
property int warningThreshold: 80
function snap(v) {
return Math.round(v * dpr) / dpr;
}
columnSpacing: Tokens.spacing.medium
columnSpacing: Tokens.spacing.smaller
columns: horizontal ? -1 : 1
percentage: 0
@@ -49,39 +44,31 @@ GridLayout {
Layout.preferredWidth: 16
color: root.iconColor
fontStyle: Tokens.font.icon.small
font.pointSize: Tokens.font.size.larger
text: root.icon
}
CustomRect {
id: track
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4
CustomClippingRect {
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.small : 4
Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
radius: Math.min(width, height) / 2
radius: Tokens.rounding.full
Item {
id: clipper
CustomRect {
id: fill
anchors.bottom: root.horizontal ? parent.bottom : undefined
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
clip: true
height: root.horizontal ? root.snap(root.percentage * track.height) : track.height * 2
width: root.horizontal ? track.width * 2 : root.snap(root.percentage * track.width)
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: root.horizontal ? parent.right : undefined
anchors.top: root.horizontal ? undefined : parent.top
color: root.mainColor
implicitHeight: root.horizontal ? Math.ceil(root.percentage * parent.height) : 0
implicitWidth: root.horizontal ? 0 : Math.ceil(root.percentage * parent.width)
CustomRect {
anchors.bottom: root.horizontal ? parent.bottom : undefined
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
color: root.mainColor
height: track.height
radius: track.radius
width: track.width
}
// Behavior on implicitHeight {
// Anim {
// }
// }
}
}
}
+15 -15
View File
@@ -12,18 +12,18 @@ import qs.Components
WidgetBase {
id: root
required property ScreenState screenState
required property PersistentProperties visibilities
clip: true
color: screenState.resources ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : gridLayout.implicitHeight + Tokens.padding.medium * 2
implicitWidth: horizontal ? gridLayout.implicitWidth + Tokens.padding.medium * 2 : baseSize
color: visibilities.resources ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : gridLayout.implicitHeight + Tokens.padding.normal * 2
implicitWidth: horizontal ? gridLayout.implicitWidth + Tokens.padding.larger * 2 : baseSize
radius: Tokens.rounding.full
StateLayer {
color: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
color: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
onClicked: root.screenState.resources = !root.screenState.resources
onClicked: root.visibilities.resources = !root.visibilities.resources
}
GridLayout {
@@ -32,7 +32,7 @@ WidgetBase {
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.horizontal ? 0 : 1
anchors.verticalCenterOffset: root.horizontal ? 0 : -3
columnSpacing: Tokens.spacing.small
columnSpacing: Tokens.spacing.smaller
columns: root.horizontal ? -1 : 1
ServiceRef {
@@ -53,8 +53,8 @@ WidgetBase {
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "memory"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary
percentage: Cpu.percentage
warningThreshold: 95
}
@@ -64,8 +64,8 @@ WidgetBase {
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "memory_alt"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3secondary
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3secondary
percentage: Memory.percentage
warningThreshold: 80
}
@@ -75,8 +75,8 @@ WidgetBase {
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "gamepad"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3tertiary
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3tertiary
percentage: Gpu.percentage
}
@@ -85,8 +85,8 @@ WidgetBase {
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "developer_board"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary
percentage: Gpu.memoryUsed / Gpu.memoryTotal
}
}
+31 -9
View File
@@ -30,8 +30,8 @@ WidgetBase {
return i;
return -1;
}
readonly property real size: horizontal ? grid.implicitWidth + grid.anchors.leftMargin + grid.anchors.rightMargin : grid.implicitHeight + grid.anchors.bottomMargin + grid.anchors.topMargin
readonly property int spacing: Tokens.spacing.medium / 2
readonly property real size: horizontal ? grid.implicitWidth + Tokens.padding.small * 2 : grid.implicitHeight + Tokens.padding.small * 2
readonly property int spacing: Tokens.spacing.normal / 2
// Entries that can shrink to nothing, spacing included
function collapsed(entry: var): bool {
@@ -40,13 +40,13 @@ WidgetBase {
return false;
}
bottomLeftRadius: horizontal ? Tokens.rounding.small / 2 : Tokens.rounding.full
bottomLeftRadius: horizontal ? Tokens.rounding.smallest / 2 : Tokens.rounding.full
color: Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : size
implicitWidth: horizontal ? size : baseSize
radius: Tokens.rounding.full
topLeftRadius: Tokens.rounding.small / 2
topRightRadius: horizontal ? Tokens.rounding.full : Tokens.rounding.small / 2
topLeftRadius: Tokens.rounding.smallest / 2
topRightRadius: horizontal ? Tokens.rounding.full : Tokens.rounding.smallest / 2
Behavior on implicitHeight {
enabled: !root.horizontal
@@ -64,11 +64,11 @@ WidgetBase {
GridLayout {
id: grid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.medium
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.small
anchors.fill: parent
anchors.leftMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
anchors.leftMargin: root.horizontal ? Tokens.padding.small : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.small : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.small
columns: root.horizontal ? -1 : 1
Repeater {
@@ -105,6 +105,28 @@ WidgetBase {
}
}
DelegateChoice {
roleValue: "bluetooth"
delegate: EntryWrapper {
name: "bluetooth"
BluetoothWidget {
}
}
}
DelegateChoice {
roleValue: "network"
delegate: EntryWrapper {
name: "network"
NetworkWidget {
}
}
}
DelegateChoice {
roleValue: "power"
@@ -13,16 +13,19 @@ MaterialIcon {
animate: true
color: Audio.muted ? Colors.palette.m3error : Colors.palette.m3onSurface
fill: 1
fontStyle: Tokens.font.icon.small
font.pointSize: Tokens.font.size.larger
text: Audio.muted ? "volume_off" : "volume_up"
Behavior on Layout.maximumWidth {
Anim {}
Anim {
}
}
Behavior on opacity {
Anim {}
Anim {
}
}
Behavior on scale {
Anim {}
Anim {
}
}
}

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