38 Commits
Author SHA1 Message Date
AramJonghu 951c31ab3d Merge remote-tracking branch 'origin/feat/settings-revamp' 2026-06-30 15:29:27 +02:00
zach 60831de7c5 Merge pull request 'tidy, format, and resolving tidy warnings' (#132) from tidy-warning-fixes into main
Reviewed-on: #132
2026-06-30 14:46:39 +02:00
zach 38b9362515 remove old Settings, rename new
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
C++ / build (pull_request) Successful in 2m26s
2026-06-30 13:43:34 +02:00
zach deaeac4dbd section headers for screenshot page + rename
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
C++ / build (pull_request) Successful in 2m23s
2026-06-30 13:31:23 +02:00
zach 157bdd8fd9 new spin box, textfield and searchbar
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 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m38s
C++ / build (pull_request) Successful in 2m18s
2026-06-30 13:25:12 +02:00
AramJonghu 45350210cc fix&revert(qalculator): unassigned this to calculator pointer fixed, revert of succesful test
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
Python / lint-format (pull_request) Successful in 41s
Python / test (pull_request) Successful in 1m51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 2m0s
C++ / build (pull_request) Successful in 3m41s
2026-06-30 04:49:55 +02:00
AramJonghu 85944ec966 force harmless lint warning to check ci/cd
Python / lint-format (pull_request) Successful in 26s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 30s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m43s
C++ / build (pull_request) Successful in 2m25s
2026-06-30 04:42:39 +02:00
AramJonghu 3c9df82594 CI/CD: attempt only user tidy warnings
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 29s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
C++ / build (pull_request) Successful in 2m35s
2026-06-30 04:36:22 +02:00
AramJonghu 1cdcecae68 CI/CD: revert
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 25s
Python / lint-format (pull_request) Successful in 29s
Python / test (pull_request) Successful in 41s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
C++ / build (pull_request) Successful in 2m28s
2026-06-30 04:27:44 +02:00
AramJonghu d2368c8c10 CI/CD: testing warnings as errors for CI/CD only
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
Python / lint-format (pull_request) Successful in 49s
Python / test (pull_request) Successful in 1m11s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m32s
C++ / build (pull_request) Failing after 2m26s
2026-06-30 04:17:43 +02:00
AramJonghu b8f79c51ae CI/CD: updated, researched other CI/CD implementations
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Successful in 44s
C++ / build (pull_request) Successful in 2m18s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m47s
2026-06-30 04:02:22 +02:00
AramJonghu 25e1610e2b CI/CD: updated, should not stop the check now due to diagnostic demotion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
C++ / build (pull_request) Successful in 2m26s
2026-06-30 03:54:47 +02:00
AramJonghu 49a65becff CI/CD: updated cpp to reduce false flags, focus on dev tidy check
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 30s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m37s
C++ / build (pull_request) Successful in 2m23s
2026-06-30 03:44:35 +02:00
AramJonghu 87c9f85ac7 fix(hyprextras): false flag recursion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m53s
C++ / build (pull_request) Successful in 2m22s
2026-06-30 03:38:39 +02:00
AramJonghu 532e5a0a03 clang-tidy: reordering 2026-06-30 03:38:16 +02:00
AramJonghu dcc535d213 unused nolint
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
C++ / build (pull_request) Successful in 2m26s
2026-06-30 03:27:01 +02:00
AramJonghu e347671edd missing commas clang-tidy 2026-06-30 03:23:36 +02:00
AramJonghu d4e4152bdc leftover format file
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 19s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 46s
C++ / build (pull_request) Successful in 3m19s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
2026-06-30 03:18:42 +02:00
AramJonghu 728bdc85bc fix(clang-tidy/analyzer): resolving false positives
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 26s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m54s
C++ / build (pull_request) Successful in 3m25s
2026-06-30 03:17:52 +02:00
AramJonghu 944694966b tidy(blobmaterial&circularindicatormanager): warning fixes, readability and modernize headers.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m41s
C++ / build (pull_request) Successful in 2m29s
2026-06-30 03:08:32 +02:00
AramJonghu 1e31e14de8 format(remainder): clang-format remainder of files
Python / lint-format (pull_request) Successful in 27s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 48s
Python / test (pull_request) Successful in 1m2s
C++ / build (pull_request) Successful in 2m15s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m25s
2026-06-30 02:10:37 +02:00
AramJonghu 658be09da2 fix(buttonrow): restore private specifier removed by clang-format 2026-06-30 02:10:17 +02:00
AramJonghu 467e44bb9f tidy&format(all): all files formatted and relevant warnings resolved
C++ / build (pull_request) Failing after 15s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 28s
Python / lint-format (pull_request) Successful in 45s
Python / test (pull_request) Successful in 38s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
2026-06-30 02:04:24 +02:00
AramJonghu fed9372b35 refactor(tickingservice): flatten nested if blocks with early returns 2026-06-30 01:54:22 +02:00
AramJonghu b2cddea636 fix(gpu): use epsilon comparison instead of == for floating-point guards 2026-06-30 01:36:40 +02:00
AramJonghu 396825d785 fix(desktopmodel): cast qsizetype to int to suppress -Wshorten-64-to-32 2026-06-30 01:36:14 +02:00
AramJonghu f439456ff4 fix(strokecanvasrenderer): use qreal instead of float in drawHoverCursor
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 27s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Successful in 1m0s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
C++ / build (pull_request) Successful in 2m17s
2026-06-30 01:26:19 +02:00
AramJonghu 4a18edfc78 fix(strokecanvasitem): use qreal instead of float to suppress -Wdouble-promotion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 48s
C++ / build (pull_request) Successful in 2m20s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m59s
2026-06-30 01:22:40 +02:00
AramJonghu 3eecbc16bc perf(lazylistview): collect pending indices and hoist layoutSize cast in updatePolishdbus workaround for session 2026-06-30 00:23:23 +02:00
zach 437f935f73 add about page in settings
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 13s
Python / test (pull_request) Successful in 53s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m35s
C++ / build (pull_request) Successful in 2m32s
2026-06-29 23:54:54 +02:00
AramJonghu b939224e9d action secret added so we have push permissions for docker
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 25s
Python / lint-format (pull_request) Successful in 28s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m53s
C++ / build (pull_request) Successful in 2m31s
2026-06-29 23:01:54 +02:00
AramJonghu 644a1e0c78 workflow update; node and docker deps added
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m40s
C++ / build (pull_request) Successful in 2m43s
2026-06-29 22:40:28 +02:00
zach 3f7bbc01aa Merge branch 'main' into feat/settings-revamp
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 18s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 1m13s
Lint & Format (Rust) / lint-format (pull_request) Successful in 2m21s
C++ / build (pull_request) Successful in 4m35s
2026-06-29 22:10:39 +02:00
AramJonghuandzach bc9d8af0fe update to clang-tidy + workflow improvement cpp build & lint check (#130)
### Goal

Idea is to reduce the annoyances and research which clang-tidy rules make sense and actually have benefits in our project.

Also considering immediately resolving most if not all tidy warnings.

### Current Changes

- `.clang-tidy` has changes to follow the guidelines we have been following silently. Some rules were different from how we program.
    - Note that naming is completely removed and won't be checked. In a future PR (where we go over the code to fix linting issues), we can reintroduce them and make sure they fit our codebase consistently.
- tidy rules are not considered errors anymore (stopped compile).
- CI/CD addition to build check and clang-tidy check. Tried to get clang-tidy check to show when failed, but that causes complications in its checks; so now it is simply silent when failing.
- Dockerfile that creates an arch container as cache for the CI/CD, prevents redownload of dependencies each run. Instead, it runs weekly (or optionally a different interval).

### Current complications

- To push the docker container build, it would require this webserver to allow larger filesize pushes (need ~2GB). Current workaround is arguably better where docker build is pushed to runner server.
- Figuring out which clang-tidy rules make sense.

Reviewed-on: #130
Co-authored-by: AramJonghu <aramjonghu@tutamail.com>
Co-committed-by: AramJonghu <aramjonghu@tutamail.com>
2026-06-29 22:10:26 +02:00
zach 01d521b42d Merge branch 'main' into feat/settings-revamp
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 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-29 19:37:24 +02:00
zach 7d65578a5f proper VERSION var 2026-06-29 19:37:11 +02:00
zach 77ec1c87f8 Merge branch 'main' into feat/settings-revamp
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 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-29 18:59:24 +02:00
zach f883b3d7fe versioning 2026-06-29 18:57:32 +02:00
204 changed files with 5599 additions and 12934 deletions
+3 -3
View File
@@ -1,15 +1,15 @@
---
BasedOnStyle: LLVM
AccessModifierOffset: 0
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: Align
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortIfStatementsOnASingleLine: WithoutElse
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
+15 -19
View File
@@ -2,29 +2,25 @@
Checks: >
-*,
bugprone-*,
-bugprone-throwing-static-initialization,
-bugprone-invalid-enum-default-initialization,
-bugprone-suspicious-include,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-narrowing-conversions,
-bugprone-implicit-widening-of-multiplication-result,
clang-analyzer-*,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
readability-braces-around-statements,
cppcoreguidelines-init-variables,
misc-no-recursion,
concurrency-mt-unsafe,
readability-else-after-return,
readability-identifier-naming,
readability-make-member-function-const,
readability-redundant-*,
-readability-redundant-inline-specifier,
readability-simplify-*,
CheckOptions:
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.MemberCase: camelBack
readability-identifier-naming.MemberPrefix: m_
readability-identifier-naming.MethodCase: camelBack
readability-identifier-naming.NamespaceCase: CamelCase
readability-identifier-naming.ParameterCase: camelBack
readability-identifier-naming.PrivateMemberPrefix: m_
readability-identifier-naming.StaticConstantCase: UPPER_CASE
readability-identifier-naming.StaticConstantPrefix: k
readability-identifier-naming.VariableCase: camelBack
WarningsAsErrors: "*"
modernize-use-override,
modernize-use-nullptr,
modernize-deprecated-headers,
HeaderFilterRegex: ".*"
FormatStyle: file
+30
View File
@@ -0,0 +1,30 @@
name: Rebuild CI Image
on:
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:
jobs:
build:
runs-on: alpine
container:
image: node:26-alpine
env:
IMAGE: git.aramjonghu.nl/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Docker CLI
run: apk add --no-cache docker-cli
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.nl --username aramjonghu --password-stdin
- name: Build image
run: docker build -t "$IMAGE" -f ci/Dockerfile .
- name: Push image
run: docker push "$IMAGE"
+29
View File
@@ -0,0 +1,29 @@
name: C++
on:
pull_request:
jobs:
build:
runs-on: alpine
container:
image: git.aramjonghu.nl/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Build
run: ninja -C build
- name: clang-tidy
run: |
sed -i 's/-mno-direct-extern-access //g' build/compile_commands.json
set -o pipefail
run-clang-tidy -p build -j $(nproc) -header-filter="Plugins/.*" 2>&1 | \
grep -v 'Suppressed\|non-user\|non-system\|unknown argument.*mno-direct\|Found compiler error\|warnings generated\.\|^Use -header-filter'
+37 -8
View File
@@ -1,6 +1,35 @@
cmake_minimum_required(VERSION 3.19)
project(ZShell)
if(NOT DEFINED VERSION)
execute_process(
COMMAND git describe --tags --abbrev=0
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_LAST_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if (GIT_LAST_TAG)
string(REGEX REPLACE "^v" "" GIT_LAST_TAG "${GIT_LAST_TAG}")
string(REPLACE "." ";" VERSION_LIST "${GIT_LAST_TAG}")
list(GET VERSION_LIST 0 VERSION_MAJOR)
list(GET VERSION_LIST 1 VERSION_MINOR)
execute_process(
COMMAND git rev-list v${VERSION_MAJOR}.${VERSION_MINOR}.0..HEAD --count
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE VERSION_PATCH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
endif()
endif()
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
project(ZShell VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
message(STATUS "ZShell version: ${VERSION}")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -15,11 +44,11 @@ set(INSTALL_QSCONFDIR "etc/xdg/quickshell/zshell" CACHE STRING "Quickshell confi
set(INSTALL_GREETERCONFDIR "etc/xdg/quickshell/zshell-greeter" CACHE STRING "Quickshell greeter install dir")
add_compile_options(
-Wall -Wextra -Wpedantic -Wshadow -Wconversion
-Wold-style-cast -Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal -Woverloaded-virtual
-Wsign-conversion -Wredundant-decls -Wswitch
-Wunreachable-code
-Wall -Wextra -Wpedantic -Wshadow -Wconversion
-Wold-style-cast -Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal -Woverloaded-virtual
-Wsign-conversion -Wredundant-decls -Wswitch
-Wunreachable-code
)
@@ -30,8 +59,8 @@ endif()
if("shell" IN_LIST ENABLE_MODULES)
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
endforeach()
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
endforeach()
# Disable watching for changes
file(READ shell.qml SHELL_QML)
+94 -142
View File
@@ -1,180 +1,132 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQuick.Templates
import qs.Config
RowLayout {
DoubleSpinBox {
id: root
property string displayText: root.value.toString()
property bool isEditing: false
property real max: Infinity
property real min: -Infinity
property alias repeatRate: timer.interval
property real step: 1
property real value
property int cLayer: 1
property int repeatDecay: 50
property int repeatRate: 400
signal valueModified(value: real)
function decrease(): void {
let newValue = Math.max(from, value - stepSize);
const decimals = stepSize < 1 ? Math.max(1, Math.ceil(-Math.log10(stepSize))) : 0;
newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
value = newValue;
valueModified();
}
function increase(): void {
let newValue = Math.min(to, value + stepSize);
const decimals = stepSize < 1 ? Math.max(1, Math.ceil(-Math.log10(stepSize))) : 0;
newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
value = newValue;
valueModified();
}
decimals: stepSize < 1 ? Math.max(1, Math.ceil(-Math.log10(stepSize))) : 0
editable: true
implicitHeight: Math.max(up.indicator.implicitHeight, down.indicator.implicitHeight, contentItem.implicitHeight) + topPadding + bottomPadding
implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding
leftPadding: up.indicator.implicitWidth + Appearance.spacing.extraSmall / 2
rightPadding: down.indicator.implicitWidth + Appearance.spacing.extraSmall / 2
spacing: Appearance.spacing.small
onValueChanged: {
if (!root.isEditing) {
root.displayText = root.value.toString();
}
}
CustomTextField {
id: textField
color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight
contentItem: TextFieldBase {
horizontalAlignment: TextField.AlignHCenter
implicitWidth: 65
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.normal
padding: Appearance.padding.small
rightPadding: Appearance.padding.normal
text: root.isEditing ? text : root.displayText
leftPadding: Appearance.padding.larger
readOnly: !root.editable
rightPadding: Appearance.padding.larger
text: root.textFromValue(root.value, root.locale)
validator: root.validator
background: CustomRect {
color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow
implicitWidth: 100
radius: Appearance.rounding.full
}
validator: DoubleValidator {
bottom: root.min
decimals: root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0
top: root.max
}
onAccepted: {
const numValue = parseFloat(text);
if (!isNaN(numValue)) {
const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
root.value = clampedValue;
root.displayText = clampedValue.toString();
root.valueModified(clampedValue);
} else {
text = root.displayText;
}
root.isEditing = false;
}
onActiveFocusChanged: {
if (activeFocus) {
root.isEditing = true;
} else {
root.isEditing = false;
root.displayText = root.value.toString();
}
}
onEditingFinished: {
if (text !== root.displayText) {
const numValue = parseFloat(text);
if (!isNaN(numValue)) {
const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
root.value = clampedValue;
root.displayText = clampedValue.toString();
root.valueModified(clampedValue);
} else {
text = root.displayText;
}
}
root.isEditing = false;
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
radius: Appearance.rounding.extraSmall
}
}
down.indicator: IconButton {
id: downButton
CustomRect {
bottomRightRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
color: enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor
disabledColor: Qt.alpha(DynamicColors.palette.m3surfaceContainerHighest, 0.4)
icon: "remove"
isRound: true
label.anchors.horizontalCenterOffset: pressed ? 0 : 2
padding: Appearance.padding.extraSmall
topRightRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
type: IconButton.Text
Behavior on bottomRightRadius {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on label.anchors.horizontalCenterOffset {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on topRightRadius {
Anim {
type: Anim.DefaultEffects
}
}
}
up.indicator: IconButton {
id: upButton
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: upIcon.implicitHeight + Appearance.padding.extraSmall * 2
implicitWidth: implicitHeight
radius: upState.pressed ? Appearance.rounding.small : Appearance.rounding.normal
anchors.right: parent.right
bottomLeftRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
color: enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor
disabledColor: Qt.alpha(DynamicColors.palette.m3surfaceContainerHighest, 0.4)
icon: "add"
isRound: true
label.anchors.horizontalCenterOffset: pressed ? 0 : -2
padding: Appearance.padding.extraSmall
topLeftRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall
type: IconButton.Text
Behavior on radius {
Behavior on bottomLeftRadius {
Anim {
type: Anim.FastEffects
type: Anim.DefaultEffects
}
}
StateLayer {
id: upState
color: DynamicColors.palette.m3onPrimary
onClicked: {
let newValue = Math.min(root.max, root.value + root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
root.value = newValue;
root.displayText = newValue.toString();
root.valueModified(newValue);
}
onPressAndHold: timer.start()
onReleased: timer.stop()
}
MaterialIcon {
id: upIcon
anchors.centerIn: parent
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_up"
}
}
CustomRect {
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: downIcon.implicitHeight + Appearance.padding.extraSmall * 2
implicitWidth: implicitHeight
radius: downState.pressed ? Appearance.rounding.small : Appearance.rounding.normal
Behavior on radius {
Behavior on label.anchors.horizontalCenterOffset {
Anim {
type: Anim.FastEffects
type: Anim.DefaultEffects
}
}
StateLayer {
id: downState
color: DynamicColors.palette.m3onPrimary
onClicked: {
let newValue = Math.max(root.min, root.value - root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
root.value = newValue;
root.displayText = newValue.toString();
root.valueModified(newValue);
Behavior on topLeftRadius {
Anim {
type: Anim.DefaultEffects
}
onPressAndHold: timer.start()
onReleased: timer.stop()
}
MaterialIcon {
id: downIcon
anchors.centerIn: parent
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_down"
}
}
Timer {
id: timer
interval: 100
interval: root.repeatRate
repeat: true
running: upButton.pressed || downButton.pressed
triggeredOnStart: true
onRunningChanged: {
if (!running)
interval = root.repeatRate;
}
onTriggered: {
if (upState.pressed)
upState.onClicked();
else if (downState.pressed)
downState.onClicked();
if (upButton.pressed)
root.increase();
else if (downButton.pressed)
root.decrease();
if (interval > root.repeatDecay)
interval -= root.repeatDecay;
}
}
}
+267 -49
View File
@@ -1,77 +1,295 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
import qs.Config
TextField {
TextFieldBase {
id: root
property int cursorHeight: root.height
enum TextFieldType {
Outlined,
Filled
}
background: null
color: DynamicColors.palette.m3onSurface
cursorVisible: !readOnly
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.smaller
placeholderTextColor: DynamicColors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius)
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
property bool emptyIsValid: true
property string errorText
readonly property int filledOffset: type === CustomTextField.Filled ? Appearance.spacing.small : 0
readonly property int horizontalPadding: Appearance.padding.large
property bool isError
property string leadingIcon
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real outlineGap: placeholder.width * root.smallFontScale + Appearance.spacing.extraSmall * 2
property real outlineGapScale: activeFocus || text ? 1 : 0
property int radius: Appearance.rounding.small
readonly property real smallFontScale: smallFontSize / font.pointSize
property int smallFontSize: Appearance.font.size.small
property string supportingText
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Appearance.spacing.extraSmall : 0
property string trailingIcon
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
property int type: CustomTextField.Outlined
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property var validate // Regex or function
Behavior on color {
CAnim {
bottomPadding: Appearance.padding.large + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: Appearance.padding.large + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
sourceComponent: root.type === CustomTextField.Filled ? filledComp : outlineComp
StateLayer {
id: stateLayer
cursorShape: Qt.IBeamCursor
enabled: !root.activeFocus
manualPressOverride: tapHandler.pressed
radius: root.type === CustomTextField.Outlined ? root.clampedRadius : 0
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
onClicked: root.focus = true
}
}
cursorDelegate: CustomRect {
id: cursor
property bool disableBlink
color: DynamicColors.palette.m3primary
implicitWidth: 2
radius: Appearance.rounding.normal
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
onEditingFinished: {
if (!valid)
isError = true;
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
onTextEdited: {
if (isError)
isError = false;
}
Item {
id: contentWrapper
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
CustomText {
id: placeholder
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.topMargin: Appearance.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.isError ? DynamicColors.palette.m3error : (root.activeFocus ? DynamicColors.palette.m3primary : root.text ? DynamicColors.palette.m3outline : root.placeholderTextColor)
font.family: root.font.family
font.pointSize: root.font.pointSize
font.variableAxes: root.font.variableAxes
font.weight: root.font.weight
renderType: Text.QtRendering
text: root.placeholderText
states: [
State {
name: "smallOutlined"
when: root.type === CustomTextField.Outlined && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + -Appearance.spacing.extraSmall
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.verticalCenter: contentWrapper.top
target: placeholder
}
},
State {
name: "smallFilled"
when: root.type === CustomTextField.Filled && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + root.leadingOffset
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.top: contentWrapper.top
anchors.verticalCenter: undefined
target: placeholder
}
}
]
transitions: Transition {
Anim {
properties: "scale,leftMargin"
type: Anim.DefaultEffects
}
AnchorAnim {
duration: Appearance.anim.durations.expressiveEffects
easing: Appearance.anim.curves.expressiveDefaultEffects
}
}
target: root
}
Timer {
id: enableBlink
Loader {
id: leadingIconLoader
interval: 100
active: root.leadingIcon
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
onTriggered: cursor.disableBlink = false
sourceComponent: MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: root.leadingIcon
}
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
Loader {
id: trailingIconLoader
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
sourceComponent: MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: root.trailingIcon
}
}
}
Behavior on placeholderTextColor {
CAnim {
Loader {
id: supportingTextLoader
active: root.effectiveSupportingText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: root.horizontalPadding
sourceComponent: CustomText {
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
text: root.effectiveSupportingText
}
}
TapHandler {
id: tapHandler
}
Component {
id: outlineComp
Shape {
id: bg
asynchronous: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: root.horizontalPadding - root.clampedRadius + root.outlineGap * (1 - root.outlineGapScale) / 2 + root.outlineGap * root.outlineGapScale
strokeColor: root.isError ? DynamicColors.palette.m3error : (root.activeFocus ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline)
strokeWidth: root.activeFocus ? 2 : 1
Behavior on strokeColor {
CAnim {
}
}
Behavior on strokeWidth {
Anim {
}
}
PathLine {
x: bg.width - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width
y: root.clampedRadius
}
PathLine {
x: bg.width
y: bg.height - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - root.clampedRadius
y: bg.height
}
PathLine {
x: root.clampedRadius
y: bg.height
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: 0
y: bg.height - root.clampedRadius
}
PathLine {
x: 0
y: root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: root.clampedRadius
y: 0
}
PathLine {
x: root.horizontalPadding - root.clampedRadius + root.outlineGap * (1 - root.outlineGapScale) / 2
}
}
}
}
Component {
id: filledComp
CustomRect {
color: root.activeFocus ? DynamicColors.tPalette.m3surfaceContainerHighest : DynamicColors.tPalette.m3surfaceContainerHigh
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.isError ? DynamicColors.palette.m3error : (root.activeFocus ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline)
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
Anim {
}
}
}
}
}
}
+2 -1
View File
@@ -30,8 +30,9 @@ MouseArea {
signal itemSelected(item: MenuItem)
anchors.fill: parent
cursorShape: undefined
cursorShape: expanded ? Qt.ArrowCursor : undefined
enabled: expanded
hoverEnabled: expanded
layer.enabled: opacity < 1
opacity: expanded ? 1 : 0
parent: {
+94
View File
@@ -0,0 +1,94 @@
import QtQuick
import qs.Config
TextFieldBase {
id: root
readonly property alias bg: bg
readonly property alias clearIcon: clearIcon
readonly property alias searchIcon: searchIcon
bottomPadding: Appearance.padding.large
leftPadding: searchIcon.width + searchIcon.anchors.leftMargin + Appearance.spacing.small
rightPadding: clearIcon.width + clearIcon.anchors.rightMargin + Appearance.spacing.small
topPadding: Appearance.padding.large
background: CustomRect {
id: bg
anchors.fill: parent
color: DynamicColors.tPalette.m3surfaceContainer
radius: Appearance.rounding.full
StateLayer {
id: stateLayer
cursorShape: Qt.IBeamCursor
enabled: !root.activeFocus
manualPressOverride: tapHandler.pressed
onClicked: root.focus = true
}
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
CustomText {
id: placeholder
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.verticalCenter: parent.verticalCenter
color: root.placeholderTextColor
font: root.font
opacity: root.text ? 0 : 1
text: root.placeholderText
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
MaterialIcon {
id: searchIcon
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.large
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: "search"
}
IconButton {
id: clearIcon
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
enabled: root.text
icon: "clear"
opacity: root.text ? 1 : 0
radius: Appearance.rounding.full
radiusMorph: false
stateLayer.hoverEnabled: enabled
type: IconButton.Text
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
onClicked: root.clear()
}
TapHandler {
id: tapHandler
}
}
+88
View File
@@ -0,0 +1,88 @@
import QtQuick
import QtQuick.Templates
import qs.Config
TextField {
id: root
color: DynamicColors.palette.m3onSurface
cursorVisible: !readOnly
font.pointSize: Appearance.font.size.small
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: DynamicColors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(DynamicColors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {
}
}
cursorDelegate: Item {
}
Behavior on selectionColor {
CAnim {
}
}
CustomRect {
id: cursor
property bool disableBlink
color: DynamicColors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Appearance.rounding.large
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Behavior on x {
Anim {
duration: Appearance.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
}
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import qs.Modules.Osd as Osd
import qs.Components.Toast as Toasts
import qs.Modules.Launcher as Launcher
import qs.Modules.Resources as Resources
import qs.Modules.SettingsNew as Settings
import qs.Modules.Settings as Settings
import qs.Modules.Drawing as Drawing
import qs.Modules.Dock as Dock
import qs.Modules.Clipboard as Clipboard
+64 -6
View File
@@ -1,17 +1,30 @@
pragma Singleton
import qs.Config
import QtQuick
import Quickshell
import Quickshell.Io
import QtQuick
import qs.Config
import qs.Paths
Singleton {
id: root
property string boardName
property string boardVendor
readonly property string device: {
if (!boardName)
return boardVendor;
if (!boardVendor || boardName.toLowerCase().startsWith(boardVendor.toLowerCase()))
return boardName;
return `${boardVendor} ${boardName}`;
}
property string firmware
property string hostname
property bool isDefaultLogo: true
property string kernel
property string osId
property list<string> osIdLike
property string osLogo
property string osLogo: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/logo.svg`)
property string osName
property string osPrettyName
readonly property string shell: Quickshell.env("SHELL").split("/").pop()
@@ -19,6 +32,12 @@ Singleton {
readonly property string user: Quickshell.env("USER")
readonly property string wm: Quickshell.env("XDG_CURRENT_DESKTOP") || Quickshell.env("XDG_SESSION_DESKTOP")
function sanitiseDmi(s: string): string {
const t = s.trim();
const junk = ["to be filled by o.e.m.", "system product name", "system manufacturer", "system version", "default string", "o.e.m.", "not specified", "not applicable", "unknown", "none", ""];
return junk.includes(t.toLowerCase()) ? "" : t;
}
FileView {
id: osRelease
@@ -35,7 +54,10 @@ Singleton {
root.osIdLike = fd("ID_LIKE").split(" ");
const logo = Quickshell.iconPath(fd("LOGO"), true);
if (Config.general.logo) {
if (Config.general.logo === "zshell") {
root.osLogo = Qt.resolvedUrl(`${Quickshell.shellDir}/assets/logo.svg`);
root.isDefaultLogo = true;
} else if (Config.general.logo) {
root.osLogo = Quickshell.iconPath(Config.general.logo, true) || "file://" + Paths.absolutePath(Config.general.logo);
root.isDefaultLogo = false;
} else if (logo) {
@@ -53,6 +75,39 @@ Singleton {
target: Config.general
}
FileView {
path: "/proc/sys/kernel/osrelease"
onLoaded: root.kernel = text().trim()
}
FileView {
path: "/proc/sys/kernel/hostname"
onLoaded: root.hostname = text().trim()
}
FileView {
path: "/sys/class/dmi/id/sys_vendor"
printErrors: false
onLoaded: root.boardVendor = root.sanitiseDmi(text())
}
FileView {
path: "/sys/class/dmi/id/product_name"
printErrors: false
onLoaded: root.boardName = root.sanitiseDmi(text())
}
FileView {
path: "/sys/class/dmi/id/bios_version"
printErrors: false
onLoaded: root.firmware = root.sanitiseDmi(text())
}
Timer {
interval: 15000
repeat: true
@@ -69,12 +124,15 @@ Singleton {
onLoaded: {
const up = parseInt(text().split(" ")[0] ?? 0);
const hours = Math.floor(up / 3600);
const days = Math.floor(up / 86400);
const hours = Math.floor((up % 86400) / 3600);
const minutes = Math.floor((up % 3600) / 60);
let str = "";
if (days > 0)
str += `${days} day${days === 1 ? "" : "s"}`;
if (hours > 0)
str += `${hours} hour${hours === 1 ? "" : "s"}`;
str += `${str ? ", " : ""}${hours} hour${hours === 1 ? "" : "s"}`;
if (minutes > 0 || !str)
str += `${str ? ", " : ""}${minutes} minute${minutes === 1 ? "" : "s"}`;
root.uptime = str;
+1 -1
View File
@@ -10,7 +10,7 @@ import qs.Config
CustomListView {
id: root
required property CustomTextField search
required property SearchBar search
required property PersistentProperties visibilities
highlightFollowsCurrentItem: false
+47 -109
View File
@@ -16,13 +16,13 @@ Item {
readonly property int rounding: Appearance.rounding.large
required property PersistentProperties visibilities
implicitHeight: searchWrapper.height + listWrapper.height + padding * 2
implicitHeight: search.height + listWrapper.height + padding * 2
implicitWidth: listWrapper.width + padding * 2
Item {
id: listWrapper
anchors.bottom: searchWrapper.top
anchors.bottom: search.top
anchors.bottomMargin: root.padding
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: list.height + root.padding
@@ -32,7 +32,7 @@ Item {
id: list
content: root
maxHeight: root.maxHeight - searchWrapper.implicitHeight - root.padding * 3
maxHeight: root.maxHeight - search.implicitHeight - root.padding * 3
padding: root.padding
panels: root.panels
rounding: root.rounding
@@ -41,131 +41,69 @@ Item {
}
}
CustomRect {
id: searchWrapper
SearchBar {
id: search
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: root.padding
anchors.right: parent.right
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight)
radius: Appearance.rounding.smallest
bottomPadding: Appearance.padding.larger
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
topPadding: Appearance.padding.larger
MaterialIcon {
id: searchIcon
anchors.left: parent.left
anchors.leftMargin: root.padding + 10
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3onSurfaceVariant
text: "search"
Component.onCompleted: {
console.log(search.color);
console.log(search.placeholderTextColor);
forceActiveFocus();
}
CustomTextField {
id: search
anchors.left: searchIcon.right
anchors.leftMargin: Appearance.spacing.small
anchors.right: clearIcon.left
anchors.rightMargin: Appearance.spacing.small
bottomPadding: Appearance.padding.larger
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
topPadding: Appearance.padding.larger
Component.onCompleted: forceActiveFocus()
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
Keys.onEscapePressed: root.visibilities.launcher = false
Keys.onPressed: event => {
if (!Config.launcher.vimKeybinds)
return;
if (event.modifiers & Qt.ControlModifier) {
if (event.key === Qt.Key_J) {
list.currentList?.incrementCurrentIndex();
event.accepted = true;
} else if (event.key === Qt.Key_K) {
list.currentList?.decrementCurrentIndex();
event.accepted = true;
}
} else if (event.key === Qt.Key_Tab) {
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
Keys.onEscapePressed: root.visibilities.launcher = false
Keys.onPressed: event => {
if (event.modifiers & Qt.ControlModifier) {
if (event.key === Qt.Key_J) {
list.currentList?.incrementCurrentIndex();
event.accepted = true;
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
} else if (event.key === Qt.Key_K) {
list.currentList?.decrementCurrentIndex();
event.accepted = true;
}
} else if (event.key === Qt.Key_Tab) {
list.currentList?.incrementCurrentIndex();
event.accepted = true;
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
list.currentList?.decrementCurrentIndex();
event.accepted = true;
}
Keys.onUpPressed: list.currentList?.incrementCurrentIndex()
onAccepted: {
const currentItem = list.currentList?.currentItem;
if (currentItem) {
if (list.showWallpapers) {
if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
Wallpapers.previewColourLock = true;
Wallpapers.setWallpaper(currentItem.modelData.path);
root.visibilities.launcher = false;
} else if (text.startsWith(Config.launcher.actionPrefix)) {
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
currentItem.onClicked();
else
currentItem.modelData.onClicked(list.currentList);
} else {
Apps.launch(currentItem.modelData);
root.visibilities.launcher = false;
}
}
Keys.onUpPressed: list.currentList?.incrementCurrentIndex()
onAccepted: {
const currentItem = list.currentList?.currentItem;
if (currentItem) {
if (list.showWallpapers) {
if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
Wallpapers.previewColourLock = true;
Wallpapers.setWallpaper(currentItem.modelData.path);
root.visibilities.launcher = false;
} else if (text.startsWith(Config.launcher.actionPrefix)) {
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
currentItem.onClicked();
else
currentItem.modelData.onClicked(list.currentList);
} else {
Apps.launch(currentItem.modelData);
root.visibilities.launcher = false;
}
}
Connections {
function onLauncherChanged(): void {
if (!root.visibilities.launcher)
search.text = "";
}
target: root.visibilities
}
}
MaterialIcon {
id: clearIcon
anchors.right: parent.right
anchors.rightMargin: root.padding + 10
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3onSurfaceVariant
opacity: {
if (!search.text)
return 0;
if (mouse.pressed)
return 0.7;
if (mouse.containsMouse)
return 0.8;
return 1;
}
text: "close"
width: search.text ? implicitWidth : implicitWidth / 2
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
Behavior on width {
Anim {
duration: Appearance.anim.durations.small
}
Connections {
function onLauncherChanged(): void {
if (!root.visibilities.launcher)
search.text = "";
}
MouseArea {
id: mouse
anchors.fill: parent
cursorShape: search.text ? Qt.PointingHandCursor : undefined
hoverEnabled: true
onClicked: search.text = ""
}
target: root.visibilities
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ Item {
required property int padding
required property var panels
required property int rounding
required property CustomTextField search
required property SearchBar search
readonly property bool showWallpapers: search.text.startsWith(`${Config.launcher.actionPrefix}wallpaper `)
required property PersistentProperties visibilities
+1 -1
View File
@@ -37,7 +37,7 @@ PathView {
return visible;
}
required property var panels
required property CustomTextField search
required property SearchBar search
required property var visibilities
cacheItemCount: 4
-370
View File
@@ -1,370 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Modules as Modules
import qs.Config
import qs.Helpers
Item {
id: root
required property Item content
signal settingSelected(string category, string section, string settingName)
// Function to select category by key
function selectCategory(categoryKey: string) {
for (let i = 0; i < appearanceCats.count; i++) {
const item = appearanceCats.get(i);
if (item.key === categoryKey) {
appearanceView.currentIndex = i;
root.content.setCategory(item.key, item._index);
return;
}
}
for (let i = 0; i < systemCats.count; i++) {
const item = systemCats.get(i);
if (item.key === categoryKey) {
sysView.currentIndex = i;
root.content.setCategory(item.key, item._index);
return;
}
}
for (let i = 0; i < panelCats.count; i++) {
const item = panelCats.get(i);
if (item.key === categoryKey) {
panelsView.currentIndex = i;
root.content.setCategory(item.key, item._index);
return;
}
}
}
implicitHeight: searchBar.implicitHeight + Appearance.spacing.smaller + clayout.contentHeight + clayout.anchors.margins * 2
implicitWidth: clayout.contentWidth + clayout.anchors.margins * 2
ListModel {
id: systemCats
ListElement {
_index: 0
icon: "settings"
key: "general"
name: "General"
}
ListElement {
_index: 1
icon: "build_circle"
key: "services"
name: "Services"
}
ListElement {
_index: 2
icon: "notifications"
key: "notifications"
name: "Notifications"
}
ListElement {
_index: 3
icon: "handyman"
key: "utilities"
name: "Utilities"
}
ListElement {
_index: 4
icon: "cached"
key: "updates"
name: "Updates"
}
}
ListModel {
id: panelCats
ListElement {
_index: 5
icon: "settop_component"
key: "bar"
name: "Bar"
}
ListElement {
_index: 6
icon: "lock"
key: "lockscreen"
name: "Lockscreen"
}
ListElement {
_index: 7
icon: "view_sidebar"
key: "sidebar"
name: "Sidebar"
}
ListElement {
_index: 8
icon: "dashboard"
key: "dashboard"
name: "Dashboard"
}
ListElement {
_index: 9
icon: "display_settings"
key: "osd"
name: "On screen display"
}
ListElement {
_index: 10
icon: "rocket_launch"
key: "launcher"
name: "Launcher"
}
}
ListModel {
id: appearanceCats
ListElement {
_index: 11
icon: "wallpaper"
key: "wallpaper"
name: "Wallpaper"
}
ListElement {
_index: 12
icon: "screenshot_region"
key: "screenshot"
name: "Screenshot"
}
ListElement {
_index: 13
icon: "colors"
key: "appearance"
name: "Appearance"
}
}
CustomClippingRect {
anchors.fill: parent
color: DynamicColors.tPalette.m3surfaceContainer
radius: Appearance.rounding.normal
CustomFlickable {
id: clayout
anchors.fill: parent
anchors.margins: Appearance.padding.extraSmall
contentHeight: contentItem.childrenRect.height
contentWidth: contentItem.childrenRect.width
flickableDirection: Flickable.VerticalFlick
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
spacing: Appearance.spacing.normal
CustomListView {
id: sysView
Layout.fillWidth: true
Layout.preferredHeight: contentItem.childrenRect.height
Layout.preferredWidth: contentItem.childrenRect.width
boundsBehavior: Flickable.StopAtBounds
highlightFollowsCurrentItem: false
interactive: false
model: systemCats
spacing: 2
delegate: Category {
view: sysView
}
// highlight: CustomRect {
// color: DynamicColors.palette.m3primary
// implicitHeight: sysView.currentItem?.implicitHeight ?? 0
// implicitWidth: sysView.width
// radius: Appearance.rounding.normal - Appearance.padding.smaller
// y: sysView.currentItem?.y ?? 0
//
// Behavior on y {
// Anim {
// duration: Appearance.anim.durations.small
// easing.bezierCurve: Appearance.anim.curves.expressiveEffects
// }
// }
// }
}
CustomListView {
id: panelsView
Layout.fillWidth: true
Layout.preferredHeight: contentItem.childrenRect.height
Layout.preferredWidth: contentItem.childrenRect.width
boundsBehavior: Flickable.StopAtBounds
contentWidth: contentItem.childrenRect.width
highlightFollowsCurrentItem: false
interactive: false
model: panelCats
spacing: 2
delegate: Category {
view: panelsView
}
// highlight: CustomRect {
// color: DynamicColors.palette.m3primary
// implicitHeight: panelsView.currentItem?.implicitHeight ?? 0
// implicitWidth: panelsView.width
// radius: Appearance.rounding.normal - Appearance.padding.smaller
// y: panelsView.currentItem?.y ?? 0
//
// Behavior on y {
// Anim {
// duration: Appearance.anim.durations.small
// easing.bezierCurve: Appearance.anim.curves.expressiveEffects
// }
// }
// }
}
CustomListView {
id: appearanceView
Layout.fillWidth: true
Layout.preferredHeight: contentItem.childrenRect.height
Layout.preferredWidth: contentItem.childrenRect.width
boundsBehavior: Flickable.StopAtBounds
contentWidth: contentItem.childrenRect.width
highlightFollowsCurrentItem: false
interactive: false
model: appearanceCats
spacing: 2
delegate: Category {
view: appearanceView
}
// highlight: CustomRect {
// color: DynamicColors.palette.m3primary
// implicitHeight: appearanceView.currentItem?.implicitHeight ?? 0
// implicitWidth: appearanceView.width
// radius: Appearance.rounding.normal - Appearance.padding.smaller
// y: appearanceView.currentItem?.y ?? 0
//
// Behavior on y {
// Anim {
// duration: Appearance.anim.durations.small
// easing.bezierCurve: Appearance.anim.curves.expressiveEffects
// }
// }
// }
}
}
}
}
component Category: CustomRect {
id: categoryItem
required property int _index
readonly property bool first: index === 0
required property string icon
required property int index
required property string key
readonly property bool last: index === view.model.count - 1
required property string name
readonly property bool selected: key === root.content.currentCategory
required property ListView view
bottomLeftRadius: layer.pressed ? Appearance.rounding.smallest : selected || last ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
bottomRightRadius: layer.pressed ? Appearance.rounding.smallest : selected || last ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
color: selected ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
implicitHeight: 50
implicitWidth: 256
topLeftRadius: layer.pressed ? Appearance.rounding.smallest : selected || first ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
topRightRadius: layer.pressed ? Appearance.rounding.smallest : selected || first ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
Behavior on bottomLeftRadius {
Anim {
type: Anim.FastEffects
}
}
Behavior on bottomRightRadius {
Anim {
type: Anim.FastEffects
}
}
Behavior on topLeftRadius {
Anim {
type: Anim.FastEffects
}
}
Behavior on topRightRadius {
Anim {
type: Anim.FastEffects
}
}
RowLayout {
id: layout
anchors.left: parent.left
anchors.margins: Appearance.padding.smaller
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
MaterialIcon {
id: icon
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillHeight: true
Layout.leftMargin: Appearance.padding.small
Layout.preferredWidth: icon.contentWidth
color: categoryItem.key === root.content.currentCategory ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
fill: categoryItem.key === root.content.currentCategory ? 1 : 0
font.pointSize: Appearance.font.size.small * 2
text: categoryItem.icon
verticalAlignment: Text.AlignVCenter
Behavior on fill {
Anim {
}
}
}
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillHeight: true
Layout.fillWidth: true
Layout.leftMargin: Appearance.spacing.normal
color: categoryItem.key === root.content.currentCategory ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
text: categoryItem.name
verticalAlignment: Text.AlignVCenter
}
}
StateLayer {
id: layer
color: categoryItem.key === root.content.currentCategory ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
onClicked: {
root.content.setCategory(categoryItem.key, categoryItem._index);
categoryItem.view.currentIndex = categoryItem.index;
}
}
}
}
-162
View File
@@ -1,162 +0,0 @@
import QtQuick
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
id: root
SettingsSection {
sectionId: "Scale"
SettingsHeader {
name: "Scale"
}
SettingSpinBox {
name: "Rounding scale"
object: Config.appearance.rounding
setting: "scale"
step: 0.1
}
Separator {
}
SettingSpinBox {
name: "Spacing scale"
object: Config.appearance.spacing
setting: "scale"
step: 0.1
}
Separator {
}
SettingSpinBox {
name: "Padding scale"
object: Config.appearance.padding
setting: "scale"
step: 0.1
}
Separator {
}
SettingSpinBox {
name: "Font size scale"
object: Config.appearance.font.size
setting: "scale"
step: 0.1
}
Separator {
}
SettingSpinBox {
name: "Animation duration scale"
object: Config.appearance.anim.durations
setting: "scale"
step: 0.1
}
Separator {
}
SettingSpinBox {
name: "Deform animation scale"
object: Config.appearance.deform
setting: "scale"
step: 0.1
}
}
SettingsSection {
sectionId: "Fonts"
SettingsHeader {
name: "Fonts"
}
SettingListView {
name: "Sans family"
object: Config.appearance.font.family
setting: "sans"
stringList: Qt.fontFamilies()
}
Separator {
}
SettingListView {
name: "Monospace family"
object: Config.appearance.font.family
setting: "mono"
stringList: Qt.fontFamilies()
}
}
SettingsSection {
sectionId: "Animation"
SettingsHeader {
name: "Animation"
}
SettingSpinBox {
name: "Media GIF speed adjustment"
object: Config.appearance.anim
setting: "mediaGifSpeedAdjustment"
step: 10
}
Separator {
}
SettingSpinBox {
max: 5
min: 0
name: "Session GIF speed"
object: Config.appearance.anim
setting: "sessionGifSpeed"
step: 0.1
}
}
SettingsSection {
sectionId: "Transparency"
SettingsHeader {
name: "Transparency"
}
SettingSwitch {
name: "Enable transparency"
object: Config.appearance.transparency
setting: "enabled"
}
Separator {
}
SettingSpinBox {
max: 1
min: 0
name: "Base opacity"
object: Config.appearance.transparency
setting: "base"
step: 0.05
}
Separator {
}
SettingSpinBox {
max: 1
min: 0
name: "Layer opacity"
object: Config.appearance.transparency
setting: "layers"
step: 0.05
}
}
}
@@ -1,48 +0,0 @@
import Quickshell
import QtQuick.Layouts
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
id: root
required property ShellScreen screen
SettingsSection {
sectionId: "Wallpaper"
SettingsHeader {
name: "Wallpaper"
}
SettingSwitch {
name: "Enable wallpaper rendering"
object: Config.background
setting: "enabled"
}
Separator {
}
SettingSpinBox {
min: 0
name: "Fade duration"
object: Config.background
setting: "wallFadeDuration"
step: 50
}
Separator {
}
WallpaperCropper {
}
}
SettingsSection {
sectionId: "Wallpapers"
WallpaperGrid {
}
}
}
-217
View File
@@ -1,217 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Bar"
SettingsHeader {
name: "Bar"
}
SettingSwitch {
name: "Auto hide"
object: Config.bar
setting: "autoHide"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Height"
object: Config.bar
setting: "height"
}
Separator {
}
SettingSpinBox {
min: 0
name: "Rounding"
object: Config.bar
setting: "rounding"
}
Separator {
}
SettingSpinBox {
min: 0
name: "Border"
object: Config.bar
setting: "border"
}
Separator {
}
SettingSpinBox {
min: 0
name: "Smoothing"
object: Config.bar
setting: "smoothing"
}
}
SettingsSection {
sectionId: "Tray"
SettingsHeader {
name: "System tray"
}
SettingSpinBox {
min: 16
name: "Tray icon size"
object: Config.bar.tray
setting: "trayIconSize"
}
}
SettingsSection {
sectionId: "Popouts"
SettingsHeader {
name: "Popouts"
}
SettingSwitch {
name: "Tray"
object: Config.bar.popouts
setting: "tray"
}
Separator {
}
SettingSwitch {
name: "Audio"
object: Config.bar.popouts
setting: "audio"
}
Separator {
}
SettingSwitch {
name: "Active window"
object: Config.bar.popouts
setting: "activeWindow"
}
Separator {
}
SettingSwitch {
name: "Resources"
object: Config.bar.popouts
setting: "resources"
}
Separator {
}
SettingSwitch {
name: "Clock"
object: Config.bar.popouts
setting: "clock"
}
Separator {
}
SettingSwitch {
name: "Network"
object: Config.bar.popouts
setting: "network"
}
Separator {
}
SettingSwitch {
name: "Power"
object: Config.bar.popouts
setting: "upower"
}
}
SettingsSection {
sectionId: "Entries"
SettingsHeader {
name: "Entries"
}
SettingBarEntryList {
name: "Bar entries"
object: Config.bar
setting: "entries"
}
}
SettingsSection {
sectionId: "Dock"
SettingsHeader {
name: "Dock"
}
SettingSwitch {
name: "Enable dock"
object: Config.dock
setting: "enable"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Dock height"
object: Config.dock
setting: "height"
}
Separator {
}
SettingSwitch {
name: "Hover to reveal"
object: Config.dock
setting: "hoverToReveal"
}
Separator {
}
SettingSwitch {
name: "Pin on startup"
object: Config.dock
setting: "pinnedOnStartup"
}
Separator {
}
SettingStringList {
addLabel: qsTr("Add pinned app")
name: "Pinned apps"
object: Config.dock
setting: "pinnedApps"
}
Separator {
}
SettingStringList {
addLabel: qsTr("Add ignored regex")
name: "Ignored app regexes"
object: Config.dock
setting: "ignoredAppRegexes"
}
}
}
-218
View File
@@ -1,218 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Dashboard"
SettingsHeader {
name: "Dashboard"
}
SettingSwitch {
name: "Enable dashboard"
object: Config.dashboard
setting: "enabled"
}
Separator {
}
SettingSpinBox {
min: 0
name: "Media update interval"
object: Config.dashboard
setting: "mediaUpdateInterval"
step: 50
}
Separator {
}
SettingSpinBox {
min: 0
name: "Resource update interval"
object: Config.dashboard
setting: "resourceUpdateInterval"
step: 50
}
Separator {
}
SettingSpinBox {
min: 0
name: "Drag threshold"
object: Config.dashboard
setting: "dragThreshold"
}
}
SettingsSection {
sectionId: "Performance"
SettingsHeader {
name: "Performance"
}
SettingSwitch {
name: "Show battery"
object: Config.dashboard.performance
setting: "showBattery"
}
Separator {
}
SettingSwitch {
name: "Show GPU"
object: Config.dashboard.performance
setting: "showGpu"
}
Separator {
}
SettingSwitch {
name: "Show CPU"
object: Config.dashboard.performance
setting: "showCpu"
}
Separator {
}
SettingSwitch {
name: "Show memory"
object: Config.dashboard.performance
setting: "showMemory"
}
Separator {
}
SettingSwitch {
name: "Show storage"
object: Config.dashboard.performance
setting: "showStorage"
}
Separator {
}
SettingSwitch {
name: "Show network"
object: Config.dashboard.performance
setting: "showNetwork"
}
}
// SettingsSection {
// sectionId: "Layout Sizes"
//
// SettingsHeader {
// name: "Layout Sizes"
// }
//
// SettingReadOnly {
// name: "Tab indicator height"
// value: String(Config.dashboard.sizes.tabIndicatorHeight)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Tab indicator spacing"
// value: String(Config.dashboard.sizes.tabIndicatorSpacing)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Info width"
// value: String(Config.dashboard.sizes.infoWidth)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Info icon size"
// value: String(Config.dashboard.sizes.infoIconSize)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Date time width"
// value: String(Config.dashboard.sizes.dateTimeWidth)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Media width"
// value: String(Config.dashboard.sizes.mediaWidth)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Media progress sweep"
// value: String(Config.dashboard.sizes.mediaProgressSweep)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Media progress thickness"
// value: String(Config.dashboard.sizes.mediaProgressThickness)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Resource progress thickness"
// value: String(Config.dashboard.sizes.resourceProgessThickness)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Weather width"
// value: String(Config.dashboard.sizes.weatherWidth)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Media cover art size"
// value: String(Config.dashboard.sizes.mediaCoverArtSize)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Media visualiser size"
// value: String(Config.dashboard.sizes.mediaVisualiserSize)
// }
//
// Separator {
// }
//
// SettingReadOnly {
// name: "Resource size"
// value: String(Config.dashboard.sizes.resourceSize)
// }
// }
}
-308
View File
@@ -1,308 +0,0 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import qs.Modules.Settings.Controls
import qs.Config
import qs.Components
import qs.Helpers
SettingsPage {
id: root
function schemeTypeItem(items, value) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.value === value)
return item;
}
return items[0] ?? null;
}
SettingsSection {
sectionId: "General"
SettingsHeader {
name: "General"
}
SettingInput {
name: "Logo"
object: Config.general
setting: "logo"
}
Separator {
}
SettingInput {
name: "Wallpaper path"
object: Config.general
setting: "wallpaperPath"
}
Separator {
}
SettingSwitch {
name: "Desktop icons"
object: Config.general
setting: "desktopIcons"
}
Separator {
}
SettingInput {
name: "Date format"
object: Config.general
setting: "dateFormat"
}
}
SettingsSection {
sectionId: "Color"
z: 1
SettingsHeader {
name: "Color"
}
CustomSplitButtonRow {
active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1]
enabled: Config.general.color.schemeGeneration
label: qsTr("Scheme mode")
menuItems: [
MenuItem {
icon: "light_mode"
text: qsTr("Light")
value: "light"
},
MenuItem {
icon: "dark_mode"
text: qsTr("Dark")
value: "dark"
}
]
onSelected: item => {
Config.general.color.mode = item.value;
Config.save();
if (item.value === "light")
ModeScheduler.applyLightMode();
else if (item.value === "dark")
ModeScheduler.applyDarkMode();
}
}
Separator {
}
CustomSplitButtonRow {
id: schemeType
active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
enabled: Config.general.color.schemeGeneration
label: qsTr("Scheme type")
menuItems: [
MenuItem {
icon: "palette"
text: qsTr("Vibrant")
value: "vibrant"
},
MenuItem {
icon: "gesture"
text: qsTr("Expressive")
value: "expressive"
},
MenuItem {
icon: "contrast"
text: qsTr("Monochrome")
value: "monochrome"
},
MenuItem {
icon: "tonality"
text: qsTr("Neutral")
value: "neutral"
},
MenuItem {
icon: "gradient"
text: qsTr("Tonal spot")
value: "tonal-spot"
},
MenuItem {
icon: "target"
text: qsTr("Fidelity")
value: "fidelity"
},
MenuItem {
icon: "article"
text: qsTr("Content")
value: "content"
},
MenuItem {
icon: "colors"
text: qsTr("Rainbow")
value: "rainbow"
},
MenuItem {
icon: "nutrition"
text: qsTr("Fruit salad")
value: "fruit-salad"
}
]
onSelected: item => {
Config.colors.schemeType = item.value;
Config.save();
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--scheme", item.value]);
}
}
Separator {
}
SettingSwitch {
name: "Automatic color scheme"
object: Config.general.color
setting: "schemeGeneration"
}
Separator {
shouldBeActive: Config.general.color.schemeGeneration ? 0 : 1
}
SchemesListView {
name: "Color scheme presets"
object: Config.colors.presets
setting: "name"
shouldBeActive: Config.general.color.schemeGeneration ? 0 : 1
stringList: FetchPresets.presetNames()
}
Separator {
shouldBeActive: Config.colors.presets.name !== "" && !Config.general.color.schemeGeneration
}
SchemesListView {
name: "Preset variant"
object: Config.colors.presets
setting: "variant"
shouldBeActive: Config.colors.presets.name !== "" && !Config.general.color.schemeGeneration
stringList: FetchPresets.variantNames(Config.colors.presets.name)
onOptionSet: item => {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${Config.colors.presets.name.toLowerCase()}:${item}`]);
}
}
Separator {
shouldBeActive: Config.colors.presets.variant !== "" && FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant).length > 0 && !Config.general.color.schemeGeneration
}
SchemesListView {
name: "Preset accent"
object: Config.colors.presets
setting: "accent"
shouldBeActive: Config.colors.presets.variant !== "" && FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant).length > 0 && !Config.general.color.schemeGeneration
stringList: FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant)
onOptionSet: item => {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${Config.colors.presets.name.toLowerCase()}:${Config.colors.presets.variant}`, "--accent", `${item}`]);
}
}
Separator {
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
}
SettingSwitch {
name: "Smart color scheme"
object: Config.general.color
setting: "smart"
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
}
Separator {
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
}
TimeInput {
name: "Schedule dark mode"
object: Config.general.color
settings: ["scheduleDark", "scheduleDarkStart", "scheduleDarkEnd"]
shouldBeActive: Config.general.color.schemeGeneration
}
Separator {
}
HyprTimeInput {
name: "Schedule Hyprsunset"
object: Config.general.color
settings: ["scheduleHyprsunset", "scheduleHyprsunsetStart", "scheduleHyprsunsetEnd", "hyprsunsetTemp"]
}
Separator {
}
SettingSpinBox {
max: 20000
min: 1000
name: "Hyprsunset temperature"
object: Config.general.color
setting: "hyprsunsetTemp"
step: 200
}
}
SettingsSection {
sectionId: "Default Apps"
SettingsHeader {
name: "Default Apps"
}
SettingStringList {
addLabel: qsTr("Add terminal command")
name: "Terminal"
object: Config.general.apps
setting: "terminal"
}
Separator {
}
SettingStringList {
addLabel: qsTr("Add audio command")
name: "Audio"
object: Config.general.apps
setting: "audio"
}
Separator {
}
SettingStringList {
addLabel: qsTr("Add playback command")
name: "Playback"
object: Config.general.apps
setting: "playback"
}
Separator {
}
SettingStringList {
addLabel: qsTr("Add explorer command")
name: "Explorer"
object: Config.general.apps
setting: "explorer"
}
}
}
-165
View File
@@ -1,165 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Launcher"
SettingsHeader {
name: "Launcher"
}
SettingSpinBox {
min: 1
name: "Max apps shown"
object: Config.launcher
setting: "maxAppsShown"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Max wallpapers shown"
object: Config.launcher
setting: "maxWallpapers"
}
Separator {
}
SettingInput {
name: "Action prefix"
object: Config.launcher
setting: "actionPrefix"
}
Separator {
}
SettingInput {
name: "Special prefix"
object: Config.launcher
setting: "specialPrefix"
}
Separator {
}
SettingSwitch {
name: "Use UWSM launch command"
object: Config.launcher
setting: "uwsm"
}
}
SettingsSection {
sectionId: "Fuzzy Search"
SettingsHeader {
name: "Fuzzy Search"
}
SettingSwitch {
name: "Apps"
object: Config.launcher.useFuzzy
setting: "apps"
}
Separator {
}
SettingSwitch {
name: "Actions"
object: Config.launcher.useFuzzy
setting: "actions"
}
Separator {
}
SettingSwitch {
name: "Schemes"
object: Config.launcher.useFuzzy
setting: "schemes"
}
Separator {
}
SettingSwitch {
name: "Variants"
object: Config.launcher.useFuzzy
setting: "variants"
}
Separator {
}
SettingSwitch {
name: "Wallpapers"
object: Config.launcher.useFuzzy
setting: "wallpapers"
}
}
SettingsSection {
sectionId: "Sizes"
SettingsHeader {
name: "Sizes"
}
SettingSpinBox {
min: 1
name: "Item width"
object: Config.launcher.sizes
setting: "itemWidth"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Item height"
object: Config.launcher.sizes
setting: "itemHeight"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Wallpaper width"
object: Config.launcher.sizes
setting: "wallpaperWidth"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Wallpaper height"
object: Config.launcher.sizes
setting: "wallpaperHeight"
}
}
SettingsSection {
sectionId: "Actions"
SettingsHeader {
name: "Actions"
}
SettingActionList {
name: "Launcher actions"
object: Config.launcher
setting: "actions"
}
}
}
-125
View File
@@ -1,125 +0,0 @@
import qs.Modules.Settings.Categories.Lockscreen
import qs.Modules.Settings.Controls
import qs.Helpers
import qs.Config
SettingsPage {
id: root
SettingsSection {
sectionId: "Lockscreen"
SettingsHeader {
name: "Lockscreen"
}
SettingSwitch {
name: "Recolor logo"
object: Config.lock
setting: "recolorLogo"
}
Separator {
}
SettingSwitch {
name: "Enable fingerprint"
object: Config.lock
setting: "enableFprint"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Max fingerprint tries"
object: Config.lock
setting: "maxFprintTries"
step: 1
}
Separator {
}
SettingSwitch {
name: "Show notification details"
object: Config.lock
setting: "showNotifContent"
}
Separator {
}
SettingSwitch {
name: "Show notification icon"
object: Config.lock
setting: "showNotifIcon"
}
Separator {
}
SettingSpinBox {
min: 0
name: "Blur amount"
object: Config.lock
setting: "blurAmount"
step: 1
}
Separator {
}
SettingSpinBox {
max: 2
min: 0.1
name: "Height multiplier"
object: Config.lock.sizes
setting: "heightMult"
step: 0.05
}
Separator {
}
SettingSpinBox {
max: 4
min: 0.5
name: "Aspect ratio"
object: Config.lock.sizes
setting: "ratio"
step: 0.05
}
Separator {
}
SettingSpinBox {
min: 100
name: "Center width"
object: Config.lock.sizes
setting: "centerWidth"
step: 10
}
}
SettingsSection {
sectionId: "Greeter"
SettingsHeader {
name: "Greeter"
}
SettingsIconButton {
name: "Install wallpaper and color scheme to greeter"
}
}
SettingsSection {
sectionId: "Idle"
Idle {
}
}
}
@@ -1,115 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Modules.Settings.Controls
ColumnLayout {
id: root
property bool shouldBeActive: true
function addTimeoutEntry() {
let list = [...Config.general.idle.timeouts];
list.push({
name: "New Entry",
timeout: 600,
idleAction: "lock"
});
Config.general.idle.timeouts = list;
Config.save();
}
function deleteTimeoutEntry(index) {
let list = [...Config.general.idle.timeouts];
list.splice(index, 1);
Config.general.idle.timeouts = list;
Config.save();
}
function updateTimeoutEntry(i, key, value) {
const list = [...Config.general.idle.timeouts];
let entry = list[i];
entry[key] = value;
list[i] = entry;
Config.general.idle.timeouts = list;
Config.save();
}
anchors.left: parent.left
anchors.right: parent.right
height: shouldBeActive ? implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
spacing: Appearance.spacing.smaller
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Settings {
name: "Idle Monitors"
}
Repeater {
model: [...Config.general.idle.timeouts]
SettingList {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
onAddActiveActionRequested: {
root.updateTimeoutEntry(index, "activeAction", "");
}
onDeleteRequested: function (index) {
root.deleteTimeoutEntry(index);
}
onFieldEdited: function (key, value) {
root.updateTimeoutEntry(index, key, value);
}
}
}
IconButton {
font.pointSize: Appearance.font.size.large
icon: "add"
onClicked: root.addTimeoutEntry()
}
component Settings: CustomRect {
id: settingsItem
required property string name
Layout.preferredHeight: 60
Layout.preferredWidth: 200
CustomText {
id: text
anchors.fill: parent
font.bold: true
font.pointSize: Appearance.font.size.large * 2
text: settingsItem.name
verticalAlignment: Text.AlignVCenter
}
}
}
@@ -1,116 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Notifications"
SettingsHeader {
name: "Notifications"
}
SettingSwitch {
name: "Expire notifications"
object: Config.notifs
setting: "expire"
}
Separator {
}
SettingSpinBox {
name: "Default expire timeout"
min: 0
object: Config.notifs
setting: "defaultExpireTimeout"
step: 100
}
Separator {
}
SettingSpinBox {
name: "App notification cooldown"
min: 0
object: Config.notifs
setting: "appNotifCooldown"
step: 100
}
Separator {
}
SettingSpinBox {
name: "Clear threshold"
max: 1
min: 0
object: Config.notifs
setting: "clearThreshold"
step: 0.05
}
Separator {
}
SettingSpinBox {
name: "Expand threshold"
min: 0
object: Config.notifs
setting: "expandThreshold"
}
Separator {
}
SettingSwitch {
name: "Action on click"
object: Config.notifs
setting: "actionOnClick"
}
Separator {
}
SettingSpinBox {
name: "Group preview count"
min: 1
object: Config.notifs
setting: "groupPreviewNum"
}
}
SettingsSection {
sectionId: "Sizes"
SettingsHeader {
name: "Sizes"
}
SettingSpinBox {
name: "Width"
min: 1
object: Config.notifs.sizes
setting: "width"
}
Separator {
}
SettingSpinBox {
name: "Image size"
min: 1
object: Config.notifs.sizes
setting: "image"
}
Separator {
}
SettingSpinBox {
name: "Badge size"
min: 1
object: Config.notifs.sizes
setting: "badge"
}
}
}
-81
View File
@@ -1,81 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "On Screen Display"
SettingsHeader {
name: "On Screen Display"
}
SettingSwitch {
name: "Enable OSD"
object: Config.osd
setting: "enabled"
}
Separator {
}
SettingSpinBox {
name: "Hide delay"
min: 0
object: Config.osd
setting: "hideDelay"
step: 100
}
Separator {
}
SettingSwitch {
name: "Enable brightness OSD"
object: Config.osd
setting: "enableBrightness"
}
Separator {
}
SettingSwitch {
name: "Enable microphone OSD"
object: Config.osd
setting: "enableMicrophone"
}
Separator {
}
SettingSwitch {
name: "Brightness on all monitors"
object: Config.osd
setting: "allMonBrightness"
}
}
SettingsSection {
sectionId: "Sizes"
SettingsHeader {
name: "Sizes"
}
SettingSpinBox {
name: "Slider width"
min: 1
object: Config.osd.sizes
setting: "sliderWidth"
}
Separator {
}
SettingSpinBox {
name: "Slider height"
min: 1
object: Config.osd.sizes
setting: "sliderHeight"
}
}
}
-150
View File
@@ -1,150 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
import qs.Components
SettingsPage {
SettingsSection {
sectionId: "Screenshot"
SettingsHeader {
name: "Screenshot"
}
SettingSwitch {
name: "Enable effects"
object: Config.screenshot
setting: "enable_pp"
}
Separator {
}
CustomSplitButtonRow {
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
enabled: Config.screenshot.enable_pp
label: qsTr("Effects mode")
menuItems: [
MenuItem {
icon: "build"
text: qsTr("Manual")
value: "manual"
},
MenuItem {
icon: "rotate_auto"
text: qsTr("Auto")
value: "auto"
}
]
onSelected: item => {
Config.screenshot.mode = item.value;
Config.save();
}
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSwitch {
enabled: Config.screenshot.enable_pp
name: "Enable rounded corners"
object: Config.screenshot
setting: "rounding"
shouldBeActive: Config.screenshot.mode === "manual"
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
}
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Corner radius"
object: Config.screenshot
setting: "radius"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
step: 1
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSwitch {
enabled: Config.screenshot.enable_pp
name: "Enable shadow"
object: Config.screenshot
setting: "shadow"
shouldBeActive: Config.screenshot.mode === "manual"
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow blur amount"
object: Config.screenshot
setting: "shadow_blur"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
// SettingSwitch {
// name: "Shadow color broken atm"
// object: Config.Screenshot
// setting: "shadow_color"
// shouldBeActive: Config.screenshot.mode === "manual"
// }
//
// Separator {
// shouldBeActive: Config.screenshot.mode === "manual"
// }
// SettingSpinBox {
// min: 1
// name: "Shadow passes"
// object: Config.screenshot
// setting: "shadow_blur_passes"
// shouldBeActive: Config.screenshot.mode === "manual"
// step: 1
// }
//
// Separator {
// shouldBeActive: Config.screenshot.mode === "manual"
// }
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow offset X"
object: Config.screenshot
setting: "shadow_offset_x"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow offset Y"
object: Config.screenshot
setting: "shadow_offset_y"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1
}
}
}
-145
View File
@@ -1,145 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Services"
SettingsHeader {
name: "Services"
}
SettingInput {
name: "Weather location"
object: Config.services
setting: "weatherLocation"
}
Separator {
}
SettingSwitch {
name: "Check for updates"
object: Config.services
setting: "updates"
}
Separator {
}
SettingSwitch {
name: "Use Fahrenheit"
object: Config.services
setting: "useFahrenheit"
}
Separator {
}
SettingSwitch {
name: "Use twelve hour clock"
object: Config.services
setting: "useTwelveHourClock"
}
Separator {
}
SettingSwitch {
name: "Enable ddcutil service"
object: Config.services
setting: "ddcutilService"
}
Separator {
}
SettingInput {
name: "GPU type"
object: Config.services
setting: "gpuType"
}
}
SettingsSection {
sectionId: "Media"
SettingsHeader {
name: "Media"
}
SettingSpinBox {
max: 1
min: 0
name: "Audio increment"
object: Config.services
setting: "audioIncrement"
step: 0.05
}
Separator {
}
SettingSpinBox {
max: 1
min: 0
name: "Brightness increment"
object: Config.services
setting: "brightnessIncrement"
step: 0.05
}
Separator {
}
SettingSpinBox {
max: 1.0
min: 0
name: "Minimum brightness"
object: Config.services
setting: "minBrightness"
step: 0.01
}
Separator {
}
SettingSpinBox {
max: 5
min: 0
name: "Max volume"
object: Config.services
setting: "maxVolume"
step: 0.05
}
Separator {
}
SettingInput {
name: "Default player"
object: Config.services
setting: "defaultPlayer"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Visualizer resolution"
object: Config.services
setting: "visualizerBars"
step: 1
}
Separator {
}
SettingAliasList {
name: "Player aliases"
object: Config.services
setting: "playerAliases"
}
}
}
-28
View File
@@ -1,28 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Sidebar"
SettingsHeader {
name: "Sidebar"
}
SettingSwitch {
name: "Enable sidebar"
object: Config.sidebar
setting: "enabled"
}
Separator {
}
SettingSpinBox {
name: "Width"
min: 1
object: Config.sidebar.sizes
setting: "width"
}
}
}
@@ -1,197 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick.Layouts
import QtQuick
import qs.Config
import qs.Helpers
import qs.Components
import qs.Modules.Settings.Controls
CustomClippingRect {
id: root
radius: Appearance.rounding.normal - Appearance.padding.smaller
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.margins: Appearance.padding.large
spacing: Appearance.spacing.large
MaterialIcon {
font.pointSize: Appearance.font.size.larger * 4
text: "update"
}
ColumnLayout {
CustomText {
font.pointSize: Appearance.font.size.large * 2
text: "System updates"
}
RowLayout {
id: row
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: `${Updates.availableUpdates} available updates`
}
CustomRect {
Layout.preferredHeight: 40
Layout.preferredWidth: 150
color: Updates.updating ? DynamicColors.layer(DynamicColors.palette.m3outline, 2) : DynamicColors.palette.m3primary
radius: Appearance.rounding.full
RowLayout {
anchors.centerIn: parent
MaterialIcon {
animate: true
color: DynamicColors.palette.m3onPrimary
font.pointSize: Appearance.font.size.large
text: Updates.updating ? "update" : "download"
}
CustomText {
color: Updates.updating ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onPrimary
text: "Update all"
}
}
StateLayer {
color: DynamicColors.palette.m3onPrimary
enabled: !Updates.updating
onClicked: Updates.performSystemUpdate()
}
}
}
}
}
CustomListView {
id: view
readonly property int itemHeight: 50 + Appearance.padding.smaller * 2
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
contentHeight: height
spacing: Appearance.spacing.normal
delegate: CustomRect {
id: update
required property var modelData
readonly property list<string> sections: modelData.update.split(" ")
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: view.itemHeight
implicitWidth: parent.width
radius: Appearance.rounding.small - Appearance.padding.small
RowLayout {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.smaller
anchors.rightMargin: Appearance.padding.smaller
MaterialIcon {
font.pointSize: Appearance.font.size.large * 2
text: "package_2"
}
ColumnLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.preferredHeight: 25
elide: Text.ElideRight
font.pointSize: Appearance.font.size.large
text: update.sections[0]
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
text: Updates.formatUpdateTime(update.modelData.timestamp)
}
}
RowLayout {
Layout.fillHeight: true
Layout.preferredWidth: 500
MarqueeText {
id: versionFrom
Layout.fillHeight: true
Layout.preferredWidth: 225
animate: true
color: DynamicColors.palette.m3tertiary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: true
pauseMs: 4000
text: update.sections[1]
width: 225
}
MaterialIcon {
Layout.fillHeight: true
color: DynamicColors.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge
horizontalAlignment: Text.AlignHCenter
text: "arrow_right_alt"
verticalAlignment: Text.AlignVCenter
}
MarqueeText {
id: versionTo
Layout.fillHeight: true
Layout.preferredWidth: 225
animate: true
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: true
pauseMs: 4000
text: update.sections[3]
width: 225
}
}
IconButton {
Layout.preferredHeight: width
icon: "download"
onClicked: {
Updates.performPackageUpdate(update.sections[0]);
}
}
}
}
model: ScriptModel {
id: script
objectProp: "update"
values: Object.entries(Updates.updates).sort((a, b) => b[1] - a[1]).map(([update, timestamp]) => ({
update,
timestamp
}))
}
}
}
}
-245
View File
@@ -1,245 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Utilities"
SettingsHeader {
name: "Utilities"
}
SettingSwitch {
name: "Enable utilities"
object: Config.utilities
setting: "enabled"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Max toasts"
object: Config.utilities
setting: "maxToasts"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Panel width"
object: Config.utilities.sizes
setting: "width"
}
Separator {
}
SettingSpinBox {
min: 1
name: "Toast width"
object: Config.utilities.sizes
setting: "toastWidth"
}
}
SettingsSection {
sectionId: "Clipboard"
SettingsHeader {
name: "Clipboard"
}
SettingSwitch {
name: "Enable clipboard history viewer"
object: Config.clipboard
setting: "enabled"
}
Separator {
}
SettingSpinBox {
max: 20
min: 1
name: "Max entries visible"
object: Config.clipboard
setting: "maxEntriesShown"
}
Separator {
}
SettingSpinBox {
max: 80
min: 30
name: "Entry height"
object: Config.clipboard.sizes
setting: "itemHeight"
}
Separator {
}
SettingSpinBox {
max: 700
min: 300
name: "Entry width"
object: Config.clipboard.sizes
setting: "width"
}
Separator {
}
SettingSpinBox {
max: 1800
min: 50
name: "Minimum preview width"
object: Config.clipboard.sizes
setting: "minPreviewWidth"
}
Separator {
}
SettingSpinBox {
max: 1800
min: 50
name: "Maximum preview width"
object: Config.clipboard.sizes
setting: "previewWidth"
}
}
SettingsSection {
sectionId: "Toasts"
SettingsHeader {
name: "Toasts"
}
SettingSwitch {
name: "Config loaded"
object: Config.utilities.toasts
setting: "configLoaded"
}
Separator {
}
SettingSwitch {
name: "Charging changed"
object: Config.utilities.toasts
setting: "chargingChanged"
}
Separator {
}
SettingSwitch {
name: "Game mode changed"
object: Config.utilities.toasts
setting: "gameModeChanged"
}
// Separator {
// }
//
// SettingSwitch {
// name: "Do not disturb changed"
// object: Config.utilities.toasts
// setting: "dndChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Audio output changed"
// object: Config.utilities.toasts
// setting: "audioOutputChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Audio input changed"
// object: Config.utilities.toasts
// setting: "audioInputChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Caps lock changed"
// object: Config.utilities.toasts
// setting: "capsLockChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Num lock changed"
// object: Config.utilities.toasts
// setting: "numLockChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Keyboard layout changed"
// object: Config.utilities.toasts
// setting: "kbLayoutChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "VPN changed"
// object: Config.utilities.toasts
// setting: "vpnChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Now playing"
// object: Config.utilities.toasts
// setting: "nowPlaying"
// }
}
// SettingsSection {
// sectionId: "VPN"
//
// SettingsHeader {
// name: "VPN"
// }
//
// SettingSwitch {
// name: "Enable VPN integration"
// object: Config.utilities.vpn
// setting: "enabled"
// }
//
// Separator {
// }
//
// SettingStringList {
// name: "Provider"
// addLabel: qsTr("Add VPN provider")
// object: Config.utilities.vpn
// setting: "provider"
// }
// }
}
+78
View File
@@ -0,0 +1,78 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
ConnectedRect {
id: root
property string icon
property color iconColor: DynamicColors.palette.m3onSurfaceVariant
property alias label: label.text
property Component leadingComponent: icon ? iconComp : null
property string subtext
property alias value: value.text
Layout.fillWidth: true
implicitHeight: rowLayout.implicitHeight + rowLayout.anchors.margins * 2
Component {
id: iconComp
MaterialIcon {
color: root.iconColor
font.pointSize: Appearance.font.size.small
text: root.icon
}
}
RowLayout {
id: rowLayout
anchors.fill: parent
anchors.leftMargin: Appearance.padding.largeIncreased
anchors.margins: Appearance.padding.larger
anchors.rightMargin: Appearance.padding.largeIncreased
spacing: Appearance.spacing.small
Loader {
active: root.leadingComponent
sourceComponent: root.leadingComponent
visible: root.leadingComponent
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
CustomText {
id: label
Layout.fillWidth: true
elide: Text.ElideRight
font.pointSize: Appearance.font.size.small
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Appearance.font.size.small
text: root.subtext
visible: root.subtext
}
}
CustomText {
id: value
Layout.maximumWidth: root.width / 2
color: DynamicColors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: Appearance.font.size.small
horizontalAlignment: Text.AlignRight
}
}
}
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew
import qs.Modules.Settings
ConnectedRect {
id: root
@@ -2,7 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Modules.SettingsNew
import qs.Modules.Settings
import qs.Components
import qs.Config
@@ -25,6 +25,8 @@ ColumnLayout {
implicitWidth: header.implicitWidth
z: 1
onClicked: focus = true
RowLayout {
id: header
@@ -66,5 +68,9 @@ ColumnLayout {
contentItem.children: [root.contentChild]
fadeAmount: 0.1
topMargin: Appearance.padding.large
TapHandler {
onTapped: flickable.focus = true
}
}
}
@@ -85,7 +85,7 @@ ConnectedRect {
anchors.fill: parent
cursorShape: popup.open ? Qt.ArrowCursor : undefined
enabled: popup.open
hoverEnabled: true
hoverEnabled: popup.open
parent: {
if (root.keepPopupAsChild)
return triggerArea;
@@ -94,8 +94,6 @@ ConnectedRect {
const contentWin = win as Windows; // If inside the drawer content window, put it inside the interaction wrapper so hover works
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
preventStealing: true
propagateComposedEvents: false
z: popup.animDriver > 0 ? 1 : 0
onClicked: popup.open = false
@@ -52,12 +52,12 @@ ConnectedRect {
}
CustomSpinBox {
max: root.to
min: root.from
step: root.stepSize
from: root.from
stepSize: root.stepSize
to: root.to
value: root.value
onValueModified: v => root.moved(v)
onValueModified: root.moved(value)
}
}
}
@@ -1,7 +1,7 @@
import QtQuick
import QtQuick.Controls
import qs.Components
import qs.Modules.SettingsNew
import qs.Modules.Settings
import qs.Config
StackView {
@@ -68,7 +68,7 @@ CustomClippingRect {
}
}
CustomTextField {
TextFieldBase {
id: startHourField
function setConfigText(setting: string): string {
@@ -84,7 +84,6 @@ CustomClippingRect {
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
@@ -188,7 +187,7 @@ CustomClippingRect {
}
}
CustomTextField {
TextFieldBase {
id: startMinuteField
function setConfigText(setting: string): string {
@@ -204,7 +203,6 @@ CustomClippingRect {
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
@@ -307,7 +305,7 @@ CustomClippingRect {
}
}
CustomTextField {
TextFieldBase {
id: endHourField
function setConfigText(setting: string): string {
@@ -323,7 +321,6 @@ CustomClippingRect {
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
@@ -427,7 +424,7 @@ CustomClippingRect {
}
}
CustomTextField {
TextFieldBase {
id: endMinuteField
function setConfigText(setting: string): string {
@@ -443,7 +440,6 @@ CustomClippingRect {
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
+47 -369
View File
@@ -1,398 +1,76 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Controls
import ZShell.Blobs
import qs.Components
import qs.Modules as Modules
import qs.Modules.Settings.Categories as Cat
import qs.Config
import qs.Helpers
Item {
CustomClippingRect {
id: root
property string currentCategory: "general"
property int currentIndex: 0
property int lastIndex: 0
readonly property real nonAnimHeight: Math.floor(screen.height / 1.5) + viewWrapper.anchors.margins * 2
readonly property real nonAnimWidth: view.implicitWidth + Math.floor(screen.width / 2) + viewWrapper.anchors.margins * 2
property string pendingSection: ""
property string pendingSetting: ""
required property ShellScreen screen
required property PersistentProperties visibilities
property color blobColor: DynamicColors.tPalette.m3surfaceContainerLow
readonly property real ratio: 16.0 / 9.0
readonly property SettingsState sState: SettingsState {
id: sState
function scrollToSetting(section: string, settingName: string) {
root.pendingSection = section;
root.pendingSetting = settingName;
scrollTimer.restart();
onClose: root.close()
}
function setCategory(category: string, index: int): void {
currentIndex = index;
currentCategory = category;
}
signal close
implicitHeight: nonAnimHeight
implicitWidth: nonAnimWidth
implicitHeight: sState.screen.height * 0.7
implicitWidth: implicitHeight * ratio
radius: Appearance.rounding.large + Appearance.padding.normal
Timer {
id: scrollTimer
interval: 50
onTriggered: {
if (root.pendingSection && stack.currentItem) {
stack.currentItem.scrollToSectionAndHighlight(root.pendingSection, root.pendingSetting);
root.pendingSection = "";
root.pendingSetting = "";
}
Behavior on blobColor {
CAnim {
}
}
Connections {
function onCurrentCategoryChanged() {
if (root.currentCategory === "general") {
stack.replaceCurrentItem(general, [], StackView.PopTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "wallpaper") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(background, [], StackView.PopTransition);
else
stack.replaceCurrentItem(background, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "bar") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(bar, [], StackView.PopTransition);
else
stack.replaceCurrentItem(bar, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "appearance") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(appearance, [], StackView.PopTransition);
else
stack.replaceCurrentItem(appearance, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "lockscreen") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(lockscreen, [], StackView.PopTransition);
else
stack.replaceCurrentItem(lockscreen, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "services") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(services, [], StackView.PopTransition);
else
stack.replaceCurrentItem(services, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "notifications") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(notifications, [], StackView.PopTransition);
else
stack.replaceCurrentItem(notifications, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "sidebar") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(sidebar, [], StackView.PopTransition);
else
stack.replaceCurrentItem(sidebar, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "utilities") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(utilities, [], StackView.PopTransition);
else
stack.replaceCurrentItem(utilities, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "dashboard") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(dashboard, [], StackView.PopTransition);
else
stack.replaceCurrentItem(dashboard, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "osd") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(osd, [], StackView.PopTransition);
else
stack.replaceCurrentItem(osd, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "launcher") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(launcher, [], StackView.PopTransition);
else
stack.replaceCurrentItem(launcher, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "screenshot") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(screenshot, [], StackView.PopTransition);
else
stack.replaceCurrentItem(screenshot, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
} else if (root.currentCategory === "updates") {
if (root.currentIndex < root.lastIndex)
stack.replaceCurrentItem(updates, [], StackView.PopTransition);
else
stack.replaceCurrentItem(updates, [], StackView.PushTransition);
root.lastIndex = root.currentIndex;
}
}
target: root
TapHandler {
onTapped: root.focus = true
}
CustomClippingRect {
id: viewWrapper
BlobGroup {
id: blobGroup
color: root.blobColor
smoothing: Appearance.rounding.normal
}
BlobInvertedRect {
anchors.fill: parent
anchors.margins: Appearance.padding.smaller
radius: Appearance.rounding.large - Appearance.padding.smaller
SettingsSearch {
id: searchBar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
onSettingSelected: (category, section, settingName) => {
layout.selectCategory(category);
root.scrollToSetting(section, settingName);
}
}
Item {
id: view
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: searchBar.bottom
anchors.topMargin: Appearance.spacing.smaller
implicitWidth: layout.implicitWidth
Categories {
id: layout
anchors.fill: parent
content: root
onSettingSelected: (category, section, settingName) => {
root.scrollToSetting(section, settingName);
}
}
}
CustomClippingRect {
id: categoryContent
anchors.bottom: parent.bottom
anchors.left: view.right
anchors.leftMargin: Appearance.spacing.smaller
anchors.right: parent.right
anchors.top: searchBar.bottom
anchors.topMargin: Appearance.spacing.smaller
color: DynamicColors.tPalette.m3surfaceContainer
radius: Appearance.rounding.normal
StackView {
id: stack
anchors.fill: parent
initialItem: general
popEnter: Transition {
SequentialAnimation {
Anim {
duration: 0
from: 0
property: "opacity"
to: 0
}
PauseAnimation {
duration: Appearance.anim.durations.expressiveEffects
}
ParallelAnimation {
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 0
property: "opacity"
to: 1
}
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: -50
property: "y"
to: 0
}
}
}
}
popExit: Transition {
ParallelAnimation {
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 1
property: "opacity"
to: 0
}
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 0
property: "y"
to: 50
}
}
}
pushEnter: Transition {
SequentialAnimation {
Anim {
duration: 0
from: 0
property: "opacity"
to: 0
}
PauseAnimation {
duration: Appearance.anim.durations.expressiveEffects
}
ParallelAnimation {
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 0
property: "opacity"
to: 1
}
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 50
property: "y"
to: 0
}
}
}
}
pushExit: Transition {
ParallelAnimation {
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 1
property: "opacity"
to: 0
}
Anim {
duration: Appearance.anim.durations.expressiveFastSpatial
from: 0
property: "y"
to: -50
}
}
}
}
}
borderBottom: Appearance.padding.normal
borderLeft: navPane.width + navPane.anchors.margins * 2
borderRight: Appearance.padding.normal
borderTop: Appearance.padding.normal
group: blobGroup
opacity: root.blobColor.a
radius: Appearance.rounding.large
}
Component {
id: general
NavPane {
id: navPane
Cat.General {
}
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.large
anchors.top: parent.top
sState: root.sState
width: Math.min(600, Math.round(root.width / 3))
}
Component {
id: background
Cat.Background {
screen: root.screen
}
Pages {
anchors.bottom: parent.bottom
anchors.left: navPane.right
anchors.leftMargin: navPane.anchors.margins + anchors.margins
anchors.margins: Appearance.padding.extraLarge
anchors.right: parent.right
anchors.top: parent.top
sState: root.sState
}
Component {
id: appearance
Cat.Appearance {
}
}
Component {
id: bar
Cat.Bar {
}
}
Component {
id: lockscreen
Cat.Lockscreen {
}
}
Component {
id: services
Cat.Services {
}
}
Component {
id: notifications
Cat.Notifications {
}
}
Component {
id: sidebar
Cat.Sidebar {
}
}
Component {
id: utilities
Cat.Utilities {
}
}
Component {
id: dashboard
Cat.Dashboard {
}
}
Component {
id: osd
Cat.Osd {
}
}
Component {
id: launcher
Cat.Launcher {
}
}
Component {
id: screenshot
Cat.Screenshot {
}
}
Component {
id: updates
Cat.SystemUpdates {
}
PopupOverlay {
anchors.fill: parent
}
}
-660
View File
@@ -1,660 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Helpers
Item {
id: root
readonly property string endTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(object[settings[2]]);
return Qt.formatTime(d, "hh:mm AP");
}
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property list<string> settings
property bool shouldBeActive: true
readonly property string startTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(object[settings[1]]);
return Qt.formatTime(d, "hh:mm AP");
}
function commitChoice(choice: int, setting: string): void {
root.object[setting] = choice;
Config.save();
Hyprsunset.checkStartup();
}
function convertHour(timeValue: int): int {
return Math.floor(timeValue / 60);
}
function convertMinute(timeValue: int): int {
return timeValue % 60;
}
function convertToMinutes(hour: int, minute: int): int {
return hour * 60 + minute;
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomText {
Layout.alignment: Qt.AlignLeft
Layout.preferredWidth: Math.min(contentWidth, optionLayout.x - Appearance.spacing.normal)
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.normal
text: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(root.startTime).arg(root.endTime)
wrapMode: Text.WordWrap
}
}
ColumnLayout {
id: optionLayout
Layout.fillHeight: true
Layout.fillWidth: true
RowLayout {
CustomText {
Layout.preferredWidth: spacer.x + spacer.width
text: qsTr("Start")
}
CustomText {
Layout.preferredWidth: endMinuteRect.width + endHourRect.width
text: qsTr("End")
}
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
text: qsTr("Enabled: ")
}
CustomSwitch {
id: enabledSwitch
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
checked: root.object[root.settings[0]]
onToggled: {
root.object[root.settings[0]] = checked;
Config.save();
}
}
}
RowLayout {
Layout.fillHeight: true
CustomRect {
id: startHourRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: startHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: startHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: startHourField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: startHourField
function setConfigText(setting: string): string {
var val = root.convertHour(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[1])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (startHourField.text.length >= 2) {
startHourField.text = "0" + startHourField.text[0];
} else if (startHourField.text.length === 1) {
startHourField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
startHourField.text = setConfigText(root.settings[1]);
startHourField.focus = false;
} else if (event.key === Qt.Key_Return) {
startHourField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
startMinuteField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
endMinuteField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = startHourField.text.length;
if (textLen >= 2 && startHourField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(startHourField.text + digit);
} else {
val = parseInt(startHourField.text[1] + digit);
}
val = Math.max(0, Math.min(23, val));
if (textLen >= 2 && val < 10) {
startHourField.text = "0" + val;
} else {
startHourField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
}
onTextEdited: {
if (startHourField.text === "")
return;
var val = parseInt(startHourField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== startHourField.text)
startHourField.text = newText;
}
}
}
CustomText {
id: startSeparator
font.pointSize: Appearance.font.size.extraLarge
text: ":"
}
CustomRect {
id: startMinuteRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: startMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: startMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: startMinuteField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: startMinuteField
function setConfigText(setting: string): string {
var val = root.convertMinute(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[1])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (startMinuteField.text.length >= 2) {
startMinuteField.text = "0" + startMinuteField.text[0];
} else if (startMinuteField.text.length === 1) {
startMinuteField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
startMinuteField.text = setConfigText(root.settings[1]);
startMinuteField.focus = false;
} else if (event.key === Qt.Key_Return) {
startMinuteField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
endHourField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
startHourField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = startMinuteField.text.length;
if (textLen >= 2 && startMinuteField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(startMinuteField.text + digit);
} else {
val = parseInt(startMinuteField.text[1] + digit);
}
val = Math.max(0, Math.min(59, val));
if (textLen >= 2 && val < 10) {
startMinuteField.text = "0" + val;
} else {
startMinuteField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
}
onTextEdited: {
if (startMinuteField.text === "")
return;
var val = parseInt(startMinuteField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== startMinuteField.text)
startMinuteField.text = newText;
}
}
}
Item {
id: spacer
Layout.preferredWidth: Appearance.spacing.large
}
CustomRect {
id: endHourRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: endHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: endHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: endHourField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: endHourField
function setConfigText(setting: string): string {
var val = root.convertHour(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[2])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (endHourField.text.length >= 2) {
endHourField.text = "0" + endHourField.text[0];
} else if (endHourField.text.length === 1) {
endHourField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
endHourField.text = setConfigText(root.settings[2]);
endHourField.focus = false;
} else if (event.key === Qt.Key_Return) {
endHourField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
endMinuteField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
startMinuteField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = endHourField.text.length;
if (textLen >= 2 && endHourField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(endHourField.text + digit);
} else {
val = parseInt(endHourField.text[1] + digit);
}
val = Math.max(0, Math.min(23, val));
if (textLen >= 2 && val < 10) {
endHourField.text = "0" + val;
} else {
endHourField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
}
onTextEdited: {
if (endHourField.text === "")
return;
var val = parseInt(endHourField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== endHourField.text)
endHourField.text = newText;
}
}
}
CustomText {
id: endSeparator
font.pointSize: Appearance.font.size.extraLarge
text: ":"
}
CustomRect {
id: endMinuteRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: endMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: endMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: endMinuteField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: endMinuteField
function setConfigText(setting: string): string {
var val = root.convertMinute(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[2])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (endMinuteField.text.length >= 2) {
endMinuteField.text = "0" + endMinuteField.text[0];
} else if (endMinuteField.text.length === 1) {
endMinuteField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
endMinuteField.text = setConfigText(root.settings[2]);
endMinuteField.focus = false;
} else if (event.key === Qt.Key_Return) {
endMinuteField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
startHourField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
endHourField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = endMinuteField.text.length;
if (textLen >= 2 && endMinuteField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(endMinuteField.text + digit);
} else {
val = parseInt(endMinuteField.text[1] + digit);
}
val = Math.max(0, Math.min(59, val));
if (textLen >= 2 && val < 10) {
endMinuteField.text = "0" + val;
} else {
endMinuteField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
}
onTextEdited: {
if (endMinuteField.text === "")
return;
var val = parseInt(endMinuteField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== endMinuteField.text)
endMinuteField.text = newText;
}
}
}
}
RowLayout {
Layout.fillWidth: true
CustomText {
id: startHour
Layout.preferredWidth: startSeparator.x + startSeparator.width
text: qsTr("Hour")
}
CustomText {
id: startMinute
Layout.preferredWidth: spacer.x + spacer.width - x
text: qsTr("Minute")
}
CustomText {
Layout.preferredWidth: endSeparator.x + endSeparator.width - x
text: qsTr("Hour")
}
CustomText {
text: qsTr("Minute")
}
}
}
}
}
@@ -1,161 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
Item {
id: root
required property string name
required property var object
property alias row: row
required property string setting
property bool shouldBeActive: true
required property list<var> stringList
signal optionSet(option: string)
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.height : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomClippingRect {
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
Layout.preferredWidth: 500
color: DynamicColors.tPalette.m3surfaceContainer
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
CustomRect {
id: searchBox
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: parent.top
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: 42
radius: Appearance.rounding.full
MaterialIcon {
id: searchIcon
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.large
anchors.top: parent.top
font.pointSize: Appearance.font.size.large
text: "search"
verticalAlignment: Text.AlignVCenter
}
CustomTextField {
id: textSearch
anchors.left: searchIcon.right
anchors.leftMargin: Appearance.spacing.small
anchors.right: parent.right
anchors.rightMargin: Appearance.spacing.normal
anchors.verticalCenter: parent.verticalCenter
placeholderText: "Search..."
}
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: searchBox.bottom
bottomLeftRadius: 21
bottomRightRadius: 21
CustomListView {
anchors.fill: parent
clip: true
spacing: Appearance.spacing.small
delegate: CustomRect {
id: delegate
required property string modelData
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 42
radius: Appearance.rounding.smallest
CustomText {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
text: modelData
verticalAlignment: Text.AlignVCenter
}
MaterialIcon {
anchors.fill: parent
anchors.rightMargin: Appearance.padding.normal
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignRight
text: "check_circle"
verticalAlignment: Text.AlignVCenter
visible: root.object[root.setting] === delegate.modelData
}
StateLayer {
onClicked: {
root.object[root.setting] = delegate.modelData;
root.optionSet(delegate.modelData);
Config.save();
}
}
}
model: ScriptModel {
values: {
const values = root.stringList;
const search = textSearch.text;
var regex = new RegExp(search, "i");
return values.filter(n => regex.test(n));
}
}
}
}
}
}
}
-31
View File
@@ -1,31 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
CustomRect {
id: root
property bool shouldBeActive: true
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.tPalette.m3outlineVariant
implicitHeight: shouldBeActive ? 1 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
}
@@ -1,282 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
ColumnLayout {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property string setting
property bool shouldBeActive: true
function addAction() {
const list = [...root.object[root.setting]];
list.push({
name: "New Action",
icon: "bolt",
description: "",
command: [],
enabled: true,
dangerous: false
});
root.object[root.setting] = list;
Config.save();
}
function removeAction(index) {
const list = [...root.object[root.setting]];
list.splice(index, 1);
root.object[root.setting] = list;
Config.save();
}
function updateAction(index, key, value) {
const list = [...root.object[root.setting]];
const entry = list[index];
entry[key] = value;
list[index] = entry;
root.object[root.setting] = list;
Config.save();
}
anchors.left: parent.left
anchors.right: parent.right
height: shouldBeActive ? implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
spacing: Appearance.spacing.smaller
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
z: -1
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
Repeater {
model: [...root.object[root.setting]]
CustomRect {
required property int index
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.normal * 2
color: DynamicColors.tPalette.m3surfaceContainer
radius: Appearance.rounding.normal
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.small
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: modelData.name ?? qsTr("Action")
}
IconButton {
font.pointSize: Appearance.font.size.large
icon: "delete"
type: IconButton.Tonal
onClicked: root.removeAction(index)
}
}
Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Name")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: 350
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.rightMargin: Appearance.padding.normal
text: modelData.name ?? ""
onEditingFinished: root.updateAction(index, "name", text)
}
}
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Icon")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: 350
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.rightMargin: Appearance.padding.normal
text: modelData.icon ?? ""
onEditingFinished: root.updateAction(index, "icon", text)
}
}
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Description")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: 350
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.rightMargin: Appearance.padding.normal
text: modelData.description ?? ""
onEditingFinished: root.updateAction(index, "description", text)
}
}
}
StringListEditor {
Layout.fillWidth: true
addLabel: qsTr("Add command argument")
anchors.left: undefined
anchors.right: undefined
values: [...(modelData.command ?? [])]
onListEdited: function (values) {
root.updateAction(index, "command", values);
}
}
Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Enabled")
}
CustomSwitch {
checked: modelData.enabled ?? true
onToggled: root.updateAction(index, "enabled", checked)
}
}
Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Dangerous")
}
CustomSwitch {
checked: modelData.dangerous ?? false
onToggled: root.updateAction(index, "dangerous", checked)
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
IconButton {
font.pointSize: Appearance.font.size.large
icon: "add"
onClicked: root.addAction()
}
CustomText {
Layout.fillWidth: true
text: qsTr("Add action")
}
}
}
@@ -1,203 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
CustomRect {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property string setting
property bool shouldBeActive: true
function addAlias() {
const list = [...root.object[root.setting]];
list.push({
from: "",
to: ""
});
root.object[root.setting] = list;
Config.save();
}
function removeAlias(index) {
const list = [...root.object[root.setting]];
list.splice(index, 1);
root.object[root.setting] = list;
Config.save();
}
function updateAlias(index, key, value) {
const list = [...root.object[root.setting]];
const entry = [...list[index]];
entry[key] = value;
list[index] = entry;
root.object[root.setting] = list;
Config.save();
}
anchors.left: parent.left
anchors.right: parent.right
height: shouldBeActive ? layout.implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
z: -1
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.smaller
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
Repeater {
model: [...root.object[root.setting]]
Item {
required property int index
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.smaller * 2
CustomRect {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: -(Appearance.spacing.smaller / 2)
color: DynamicColors.tPalette.m3outlineVariant
implicitHeight: 1
visible: index !== 0
}
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.small
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("From")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(fromTextField.contentWidth + Appearance.padding.large * 2, 550), 50)
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
id: fromTextField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
text: modelData.from ?? ""
onEditingFinished: root.updateAlias(index, "from", text)
}
}
IconButton {
font.pointSize: Appearance.font.size.large
icon: "delete"
type: IconButton.Tonal
onClicked: root.removeAlias(index)
}
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("To")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(toTextField.contentWidth + Appearance.padding.large * 2, 550), 50)
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
id: toTextField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
text: modelData.to ?? ""
onEditingFinished: root.updateAlias(index, "to", text)
}
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
IconButton {
font.pointSize: Appearance.font.size.large
icon: "add"
onClicked: root.addAlias()
}
CustomText {
Layout.fillWidth: true
text: qsTr("Add alias")
}
}
}
}
@@ -1,241 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import QtQml.Models
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
property bool dragActive: false
property real dragHeight: 0
property real dragStartX: 0
property real dragStartY: 0
property real dragX: 0
property real dragY: 0
property var draggedModelData: null
property string draggedUid: ""
property bool dropAnimating: false
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
property var pendingCommitEntries: []
required property string setting
property bool shouldBeActive: true
property int uidCounter: 0
property var visualEntries: []
function ensureVisualEntries() {
if (!root.dragActive && !root.dropAnimating)
root.rebuildVisualEntries();
}
function iconForId(id) {
switch (id) {
case "workspaces":
return "dashboard";
case "audio":
return "volume_up";
case "media":
return "play_arrow";
case "resources":
return "monitoring";
case "updates":
return "system_update";
case "dash":
return "space_dashboard";
case "spacer":
return "horizontal_rule";
case "activeWindow":
return "web_asset";
case "tray":
return "widgets";
case "upower":
return "battery_full";
case "network":
return "wifi";
case "clock":
return "schedule";
case "notifBell":
return "notifications";
default:
return "drag_indicator";
}
}
function indexForUid(uid) {
for (let i = 0; i < root.visualEntries.length; i++) {
if (root.visualEntries[i].uid === uid)
return i;
}
return -1;
}
function labelForId(id) {
switch (id) {
case "workspaces":
return qsTr("Workspaces");
case "audio":
return qsTr("Audio");
case "media":
return qsTr("Media");
case "resources":
return qsTr("Resources");
case "updates":
return qsTr("Updates");
case "dash":
return qsTr("Dash");
case "spacer":
return qsTr("Spacer");
case "activeWindow":
return qsTr("Title");
case "tray":
return qsTr("Tray");
case "upower":
return qsTr("Power");
case "network":
return qsTr("Network");
case "clock":
return qsTr("Clock");
case "notifBell":
return qsTr("Notifs");
case "hyprsunset":
return qsTr("Night light");
default:
return id;
}
}
function rebuildVisualEntries() {
const entries = root.object[root.setting] ?? [];
const next = [];
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
let existing = null;
for (let j = 0; j < root.visualEntries.length; j++) {
if (root.visualEntries[j].entry === entry) {
existing = root.visualEntries[j];
break;
}
}
if (existing)
next.push(existing);
else
next.push({
uid: `entry-${root.uidCounter++}`,
entry
});
}
root.visualEntries = next;
}
function updateEntry(index, value) {
const list = [...root.object[root.setting]];
const entry = list[index];
entry.enabled = value;
list[index] = entry;
root.object[root.setting] = list;
Config.save();
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? layout.implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Component.onCompleted: root.rebuildVisualEntries()
CustomRect {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
z: -1
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: layout
anchors.fill: parent
// spacing: Appearance.spacing.smaller
DelegateModel {
id: visualModel
delegate: entryDelegate
model: ScriptModel {
objectProp: "uid"
values: root.visualEntries
}
}
Repeater {
delegate: entryDelegate
model: visualModel
}
}
Component {
id: entryDelegate
IconButton {
required property int index
required property var modelData
Layout.fillWidth: true
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
checked: modelData.entry.enabled ?? true
font: Appearance.font.family.sans
// icon: root.iconForId(modelData.entry.id)
icon: root.labelForId(modelData.entry.id)
inactiveColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
isToggle: true
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
visible: !["spacer", "upower", "dash", "audio"].some(prefix => modelData.entry.id.startsWith(prefix))
Behavior on Layout.preferredWidth {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
onClicked: root.updateEntry(index, internalChecked)
}
}
}
@@ -1,214 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property list<string> settings
property bool shouldBeActive: true
function commitChoice(choice: int, setting: string): void {
root.object[setting] = choice;
Config.save();
}
function formattedValue(setting: string): string {
const value = root.object[setting];
if (value === null || value === undefined)
return "";
return String(value);
}
function hourToAmPm(hour) {
var h = Number(hour) % 24;
var d = new Date(2000, 0, 1, h, 0, 0);
return Qt.formatTime(d, "h AP");
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomText {
Layout.alignment: Qt.AlignLeft
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.normal
text: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(root.hourToAmPm(root.object[root.settings[0]])).arg(root.hourToAmPm(root.object[root.settings[1]]))
}
}
ColumnLayout {
id: optionLayout
Layout.fillHeight: true
Layout.preferredWidth: 100
RowLayout {
Layout.preferredWidth: optionLayout.width
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Enabled: ")
}
CustomSwitch {
id: enabledSwitch
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
checked: root.object[root.settings[2]]
onToggled: {
root.object[root.settings[2]] = checked;
Config.save();
}
}
}
RowLayout {
Layout.preferredWidth: optionLayout.width
z: setting2.expanded ? -1 : 1
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Start: ")
}
SpinnerButton {
id: setting1
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
Layout.preferredWidth: height * 2
currentIndex: root.object[root.settings[0]]
enabled: enabledSwitch.checked
text: root.formattedValue(root.settings[0])
menu.onItemSelected: item => {
root.commitChoice(item, root.settings[0]);
}
}
}
RowLayout {
Layout.preferredWidth: optionLayout.width
z: setting1.expanded ? -1 : 1
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("End: ")
}
SpinnerButton {
id: setting2
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
Layout.preferredWidth: height * 2
currentIndex: root.object[root.settings[1]]
enabled: enabledSwitch.checked
text: root.formattedValue(root.settings[1])
menu.onItemSelected: item => {
root.commitChoice(item, root.settings[1]);
}
}
}
RowLayout {
Layout.preferredWidth: optionLayout.width
z: -2
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Temp: ")
}
CustomRect {
id: rect
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(textField.contentWidth + Appearance.padding.normal * 2, 200), 50)
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
id: textField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 200)
text: root.formattedValue(root.settings[3])
onEditingFinished: {
root.object[root.settings[3]] = textField.text;
Config.save();
}
}
}
}
}
}
}
@@ -1,99 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property string setting
property bool shouldBeActive: true
function formattedValue(): string {
const value = root.object[root.setting];
if (value === null || value === undefined)
return "";
return String(value);
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomRect {
id: rect
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(textField.contentWidth + Appearance.padding.large * 2, 550), 50)
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
id: textField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
text: root.formattedValue()
onEditingFinished: {
root.object[root.setting] = textField.text;
Config.save();
}
}
}
}
}
-297
View File
@@ -1,297 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
Item {
id: root
required property int index
required property var modelData
property bool shouldBeActive: true
signal addActiveActionRequested
signal deleteRequested(int index)
signal fieldEdited(string key, var value)
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
CustomRect {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: -(Appearance.spacing.smaller / 2)
color: DynamicColors.tPalette.m3outlineVariant
implicitHeight: 1
visible: root.index !== 0
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.large
Item {
id: nameCell
property string draftName: ""
property bool editing: false
function beginEdit() {
draftName = root.modelData.name ?? "";
editing = true;
nameEditor.forceActiveFocus();
}
function cancelEdit() {
draftName = root.modelData.name ?? "";
editing = false;
}
function commitEdit() {
editing = false;
if (draftName !== (root.modelData.name ?? "")) {
root.fieldEdited("name", draftName);
}
}
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillHeight: true
Layout.preferredWidth: root.width / 2
HoverHandler {
id: nameHover
}
HoverIconButton {
id: editButton
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large
icon: "edit"
shouldBeVisible: nameHover.hovered && !nameCell.editing
onClicked: nameCell.beginEdit()
}
CustomText {
anchors.left: parent.left
anchors.leftMargin: nameHover.hovered ? editButton.width + Appearance.spacing.smaller * 2 : 0
anchors.right: deleteButton.left
anchors.rightMargin: Appearance.spacing.small
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight // enable if CustomText supports it
font.pointSize: Appearance.font.size.larger
text: root.modelData.name
visible: !nameCell.editing
Behavior on anchors.leftMargin {
Anim {
}
}
}
HoverIconButton {
id: deleteButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large
icon: "delete"
shouldBeVisible: nameHover.hovered && !nameCell.editing
onClicked: root.deleteRequested(root.index)
}
CustomRect {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.tPalette.m3surface
implicitHeight: nameEditor.implicitHeight + (Appearance.padding.normal * 2)
implicitWidth: Math.min(nameEditor.contentWidth + (Appearance.padding.normal * 2), parent.width - Appearance.padding.normal)
radius: Appearance.rounding.small
visible: nameCell.editing
CustomTextField {
id: nameEditor
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, nameCell.width - Appearance.padding.normal)
text: nameCell.draftName
Keys.onEscapePressed: {
nameCell.cancelEdit();
}
onEditingFinished: {
nameCell.commitEdit();
}
onTextEdited: {
nameCell.draftName = nameEditor.text;
}
}
}
}
VSeparator {
}
ColumnLayout {
id: cLayout
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Timeout")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(timeField.contentWidth + Appearance.padding.normal * 3, 200), 50)
color: DynamicColors.tPalette.m3surface
radius: Appearance.rounding.small
CustomTextField {
id: timeField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: String(root.modelData.timeout ?? "")
onEditingFinished: {
root.fieldEdited("timeout", Number(text));
}
}
}
}
Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
}
RowLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
text: qsTr("Idle Action")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(idleField.contentWidth + Appearance.padding.normal * 3, 200), 50)
color: DynamicColors.tPalette.m3surface
radius: Appearance.rounding.small
CustomTextField {
id: idleField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: root.modelData.idleAction ?? ""
onEditingFinished: {
root.fieldEdited("idleAction", text);
}
}
}
}
Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
}
Item {
Layout.fillWidth: true
implicitHeight: activeActionRow.visible ? activeActionRow.implicitHeight : addButtonRow.implicitHeight
RowLayout {
id: activeActionRow
anchors.left: parent.left
anchors.right: parent.right
visible: root.modelData.activeAction !== undefined
CustomText {
Layout.fillWidth: true
text: qsTr("Active Action")
}
CustomRect {
Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(actionField.contentWidth + Appearance.padding.normal * 3, 200), 50)
color: DynamicColors.tPalette.m3surface
radius: Appearance.rounding.small
CustomTextField {
id: actionField
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: root.modelData.activeAction ?? ""
onEditingFinished: {
root.fieldEdited("activeAction", text);
}
}
}
}
RowLayout {
id: addButtonRow
anchors.left: parent.left
anchors.right: parent.right
visible: root.modelData.activeAction === undefined
IconButton {
id: button
Layout.alignment: Qt.AlignLeft
font.pointSize: Appearance.font.size.large
icon: "add"
onClicked: root.addActiveActionRequested()
}
CustomText {
Layout.alignment: Qt.AlignLeft
text: qsTr("Add active action")
}
}
}
}
}
}
@@ -1,163 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
Item {
id: root
required property string name
required property var object
property alias row: row
required property string setting
property bool shouldBeActive: true
required property list<var> stringList
signal optionSet
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.height : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomClippingRect {
id: fontArea
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
Layout.preferredWidth: 500
color: DynamicColors.tPalette.m3surfaceContainer
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
CustomRect {
id: searchBox
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: parent.top
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: 42
radius: Appearance.rounding.full
MaterialIcon {
id: searchIcon
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.large
anchors.top: parent.top
font.pointSize: Appearance.font.size.large
text: "search"
verticalAlignment: Text.AlignVCenter
}
CustomTextField {
id: fontSearch
anchors.left: searchIcon.right
anchors.leftMargin: Appearance.spacing.small
anchors.right: parent.right
anchors.rightMargin: Appearance.spacing.normal
anchors.verticalCenter: parent.verticalCenter
placeholderText: "Search..."
}
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: searchBox.bottom
bottomLeftRadius: 21
bottomRightRadius: 21
CustomListView {
anchors.fill: parent
clip: true
spacing: Appearance.spacing.small
delegate: CustomRect {
id: fontDelegate
required property string modelData
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 42
radius: Appearance.rounding.smallest
CustomText {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
text: modelData
verticalAlignment: Text.AlignVCenter
}
MaterialIcon {
anchors.fill: parent
anchors.rightMargin: Appearance.padding.normal
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignRight
text: "check_circle"
verticalAlignment: Text.AlignVCenter
visible: root.object[root.setting] === fontDelegate.modelData
}
StateLayer {
onClicked: {
root.object[root.setting] = fontDelegate.modelData;
root.optionSet();
Config.save();
}
}
}
model: ScriptModel {
values: {
const values = root.stringList;
const search = fontSearch.text;
var regex = new RegExp(search, "i");
return values.filter(n => regex.test(n));
}
}
}
}
}
}
}
@@ -1,70 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
property bool shouldBeActive: true
required property string value
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomText {
color: DynamicColors.palette.m3onSurfaceVariant
font.family: Appearance.font.family.mono
font.pointSize: Appearance.font.size.normal
text: root.value
}
}
}
@@ -1,81 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
property real max: Infinity
property real min: -Infinity
required property string name
required property var object
required property string setting
property bool shouldBeActive: true
property real step: 1
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomSpinBox {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
max: root.max
min: root.min
step: root.step
value: Number(root.object[root.setting] ?? 0)
onValueModified: function (value) {
root.object[root.setting] = value;
Config.save();
}
}
}
}
@@ -1,180 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property list<string> settings
property bool shouldBeActive: true
function commitChoice(choice: int, setting: string): void {
root.object[setting] = choice;
Config.save();
}
function formattedValue(setting: string): string {
const value = root.object[setting];
if (value === null || value === undefined)
return "";
return String(value);
}
function hourToAmPm(hour) {
var h = Number(hour) % 24;
var d = new Date(2000, 0, 1, h, 0, 0);
return Qt.formatTime(d, "h AP");
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomText {
Layout.alignment: Qt.AlignLeft
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.normal
text: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(root.hourToAmPm(root.object[root.settings[0]])).arg(root.hourToAmPm(root.object[root.settings[1]]))
}
}
ColumnLayout {
id: optionLayout
Layout.fillHeight: true
Layout.preferredWidth: 100
RowLayout {
Layout.preferredWidth: optionLayout.width
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Enabled: ")
}
CustomSwitch {
id: enabledSwitch
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
checked: root.object[root.settings[2]]
onToggled: {
root.object[root.settings[2]] = checked;
Config.save();
}
}
}
RowLayout {
Layout.preferredWidth: optionLayout.width
z: setting2.expanded ? -1 : 1
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("Start: ")
}
SpinnerButton {
id: setting1
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
Layout.preferredWidth: height * 2
currentIndex: root.object[root.settings[0]]
enabled: enabledSwitch.checked
text: root.formattedValue(root.settings[0])
menu.onItemSelected: item => {
root.commitChoice(item, root.settings[0]);
}
}
}
RowLayout {
Layout.preferredWidth: optionLayout.width
z: setting1.expanded ? -1 : 1
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
Layout.fillWidth: true
text: qsTr("End: ")
}
SpinnerButton {
id: setting2
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
Layout.preferredWidth: height * 2
currentIndex: root.object[root.settings[1]]
enabled: enabledSwitch.checked
text: root.formattedValue(root.settings[1])
menu.onItemSelected: item => {
root.commitChoice(item, root.settings[1]);
}
}
}
}
}
}
@@ -1,78 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
property string addLabel: qsTr("Add entry")
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property string setting
property bool shouldBeActive: true
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? layout.implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.right: parent.right
spacing: Appearance.spacing.small
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
StringListEditor {
Layout.fillWidth: true
addLabel: root.addLabel
anchors.left: undefined
anchors.right: undefined
anchors.verticalCenter: undefined
values: [...(root.object[root.setting] ?? [])]
onListEdited: function (values) {
root.object[root.setting] = values;
Config.save();
}
}
}
}
@@ -1,79 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property string setting
property bool shouldBeActive: true
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomSwitch {
id: cswitch
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
checked: root.object[root.setting]
onToggled: {
root.object[root.setting] = checked;
Config.save();
}
}
}
}
@@ -1,25 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
CustomRect {
id: root
required property string name
property bool shouldBeActive: true
implicitHeight: 60
implicitWidth: 200
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
CustomText {
anchors.fill: parent
font.bold: true
font.pointSize: Appearance.font.size.large * 2
text: root.name
verticalAlignment: Text.AlignVCenter
}
}
@@ -1,83 +0,0 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Paths
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
property alias button: iButton
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
property bool shouldBeActive: true
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
IconButton {
id: iButton
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
icon: "download"
onClicked: {
const lockBg = `${Paths.state}/lockscreen_bg.png`;
const scheme = `${Paths.state}/scheme.json`;
const face = `${Paths.home}/.face`;
const destination = "/etc/zshell-greeter/images";
Quickshell.execDetached(["pkexec", "sh", "-c", `mkdir -p ${destination}; cp ${lockBg} ${destination}; cp ${scheme} /etc/zshell-greeter; cp ${face} ${destination}`]);
}
}
}
}
@@ -1,76 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
default property alias contentData: clayout.data
function scrollToSectionAndHighlight(sectionId: string, settingName: string): bool {
for (let i = 0; i < clayout.children.length; i++) {
const section = clayout.children[i];
if (section.sectionId === sectionId) {
const targetY = section.y - Appearance.padding.normal;
flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height));
SettingsHighlight.highlight(settingName);
return true;
}
}
return false;
}
CustomFlickable {
id: flickable
anchors.fill: parent
// for future:
// anchors.leftMargin: Appearance.padding.extraLarge
// anchors.rightMargin: Appearance.padding.extraLarge
clip: true
contentHeight: clayout.implicitHeight + clayout.anchors.margins * 2
CustomScrollBar.vertical: CustomScrollBar {
flickable: flickable
}
TapHandler {
acceptedButtons: Qt.LeftButton
onTapped: function (eventPoint) {
const menu = SettingsDropdowns.activeMenu;
if (!menu)
return;
const p = eventPoint.scenePosition;
if (SettingsDropdowns.hit(SettingsDropdowns.activeTrigger, p))
return;
if (SettingsDropdowns.hit(menu, p))
return;
SettingsDropdowns.closeActive();
}
}
Column {
id: clayout
anchors.left: parent.left
anchors.margins: Appearance.padding.extraSmall
anchors.right: parent.right
anchors.top: parent.top
spacing: Appearance.spacing.small
// move: Transition {
// Anim {
// properties: "y"
// }
// }
}
}
}
@@ -1,48 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
CustomRect {
id: root
default property alias contentData: layout.data
property real contentPadding: Appearance.padding.large
property string sectionId: ""
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: layout.height + contentPadding * 2
radius: Appearance.rounding.normal - Appearance.padding.extraSmall
Behavior on implicitHeight {
Anim {
}
}
Behavior on y {
Anim {
}
}
Column {
id: layout
anchors.left: parent.left
anchors.margins: root.contentPadding
anchors.right: parent.right
anchors.top: parent.top
// anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.normal
Behavior on height {
Anim {
}
}
move: Transition {
Anim {
properties: "y"
}
}
}
}
@@ -1,63 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Helpers
import qs.Config
CustomRect {
id: root
property alias currentIndex: menu.currentIndex
property bool enabled
property alias expanded: menu.expanded
property alias label: label
property alias menu: menu
property bool shouldBeActive: true
property alias text: label.text
color: enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
opacity: shouldBeActive ? 1 : 0
radius: Appearance.rounding.full
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
z: expanded ? 100 : 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
CustomText {
id: label
anchors.centerIn: parent
color: root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3onSurface, 2)
font.pointSize: Appearance.font.size.large
}
StateLayer {
visible: root.enabled
onClicked: {
SettingsDropdowns.toggle(menu, root);
}
}
PathViewMenu {
id: menu
anchors.centerIn: parent
from: 1
implicitWidth: root.width
itemHeight: root.height
to: 24
}
}
@@ -1,126 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
ColumnLayout {
id: root
property string addLabel: qsTr("Add entry")
property bool shouldBeActive: true
property var values: []
signal listEdited(var values)
function addValue() {
const list = [...root.values];
list.push("");
root.listEdited(list);
}
function removeValue(index) {
const list = [...root.values];
list.splice(index, 1);
root.listEdited(list);
}
function updateValue(index, value) {
const list = [...root.values];
list[index] = value;
root.listEdited(list);
}
anchors.left: parent.left
anchors.right: parent.right
height: shouldBeActive ? implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
spacing: Appearance.spacing.smaller
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Repeater {
model: [...root.values]
Item {
required property int index
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
CustomRect {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: -(Appearance.spacing.smaller / 2)
color: DynamicColors.tPalette.m3outlineVariant
implicitHeight: 1
visible: index !== 0
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: 33
color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full
CustomTextField {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.rightMargin: Appearance.padding.normal
text: String(modelData ?? "")
onEditingFinished: root.updateValue(index, text)
}
}
IconButton {
font.pointSize: Appearance.font.size.large
icon: "delete"
type: IconButton.Tonal
onClicked: root.removeValue(index)
}
}
}
}
RowLayout {
Layout.fillWidth: true
IconButton {
font.pointSize: Appearance.font.size.large
icon: "add"
onClicked: root.addValue()
}
CustomText {
Layout.fillWidth: true
text: root.addLabel
}
}
}
-661
View File
@@ -1,661 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Helpers
Item {
id: root
readonly property string endTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(object[settings[2]]);
return Qt.formatTime(d, "hh:mm AP");
}
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
required property var object
required property list<string> settings
property bool shouldBeActive: true
readonly property string startTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(object[settings[1]]);
return Qt.formatTime(d, "hh:mm AP");
}
function commitChoice(choice: int, setting: string): void {
root.object[setting] = choice;
Config.save();
ModeScheduler.checkStartup();
}
function convertHour(timeValue: int): int {
return Math.floor(timeValue / 60);
}
function convertMinute(timeValue: int): int {
return timeValue % 60;
}
function convertToMinutes(hour: int, minute: int): int {
return hour * 60 + minute;
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomText {
Layout.alignment: Qt.AlignLeft
Layout.preferredWidth: Math.min(contentWidth, optionLayout.x - Appearance.spacing.normal)
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.normal
text: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(root.startTime).arg(root.endTime)
wrapMode: Text.WordWrap
}
}
ColumnLayout {
id: optionLayout
Layout.fillHeight: true
Layout.fillWidth: true
RowLayout {
CustomText {
Layout.preferredWidth: spacer.x + spacer.width
text: qsTr("Start")
}
CustomText {
Layout.preferredWidth: endMinuteRect.width + endHourRect.width
text: qsTr("End")
}
CustomText {
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
text: qsTr("Enabled: ")
}
CustomSwitch {
id: enabledSwitch
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
checked: root.object[root.settings[0]]
onToggled: {
root.object[root.settings[0]] = checked;
Config.save();
ModeScheduler.checkStartup();
}
}
}
RowLayout {
Layout.fillHeight: true
CustomRect {
id: startHourRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: startHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: startHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: startHourField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: startHourField
function setConfigText(setting: string): string {
var val = root.convertHour(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[1])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (startHourField.text.length >= 2) {
startHourField.text = "0" + startHourField.text[0];
} else if (startHourField.text.length === 1) {
startHourField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
startHourField.text = setConfigText(root.settings[1]);
startHourField.focus = false;
} else if (event.key === Qt.Key_Return) {
startHourField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
startMinuteField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
endMinuteField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = startHourField.text.length;
if (textLen >= 2 && startHourField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(startHourField.text + digit);
} else {
val = parseInt(startHourField.text[1] + digit);
}
val = Math.max(0, Math.min(23, val));
if (textLen >= 2 && val < 10) {
startHourField.text = "0" + val;
} else {
startHourField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
}
onTextEdited: {
if (startHourField.text === "")
return;
var val = parseInt(startHourField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== startHourField.text)
startHourField.text = newText;
}
}
}
CustomText {
id: startSeparator
font.pointSize: Appearance.font.size.extraLarge
text: ":"
}
CustomRect {
id: startMinuteRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: startMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: startMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: startMinuteField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: startMinuteField
function setConfigText(setting: string): string {
var val = root.convertMinute(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[1])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (startMinuteField.text.length >= 2) {
startMinuteField.text = "0" + startMinuteField.text[0];
} else if (startMinuteField.text.length === 1) {
startMinuteField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
startMinuteField.text = setConfigText(root.settings[1]);
startMinuteField.focus = false;
} else if (event.key === Qt.Key_Return) {
startMinuteField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
endHourField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
startHourField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = startMinuteField.text.length;
if (textLen >= 2 && startMinuteField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(startMinuteField.text + digit);
} else {
val = parseInt(startMinuteField.text[1] + digit);
}
val = Math.max(0, Math.min(59, val));
if (textLen >= 2 && val < 10) {
startMinuteField.text = "0" + val;
} else {
startMinuteField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
}
onTextEdited: {
if (startMinuteField.text === "")
return;
var val = parseInt(startMinuteField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== startMinuteField.text)
startMinuteField.text = newText;
}
}
}
Item {
id: spacer
Layout.preferredWidth: Appearance.spacing.large
}
CustomRect {
id: endHourRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: endHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: endHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: endHourField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: endHourField
function setConfigText(setting: string): string {
var val = root.convertHour(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[2])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (endHourField.text.length >= 2) {
endHourField.text = "0" + endHourField.text[0];
} else if (endHourField.text.length === 1) {
endHourField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
endHourField.text = setConfigText(root.settings[2]);
endHourField.focus = false;
} else if (event.key === Qt.Key_Return) {
endHourField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
endMinuteField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
startMinuteField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = endHourField.text.length;
if (textLen >= 2 && endHourField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(endHourField.text + digit);
} else {
val = parseInt(endHourField.text[1] + digit);
}
val = Math.max(0, Math.min(23, val));
if (textLen >= 2 && val < 10) {
endHourField.text = "0" + val;
} else {
endHourField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
}
onTextEdited: {
if (endHourField.text === "")
return;
var val = parseInt(endHourField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== endHourField.text)
endHourField.text = newText;
}
}
}
CustomText {
id: endSeparator
font.pointSize: Appearance.font.size.extraLarge
text: ":"
}
CustomRect {
id: endMinuteRect
Layout.preferredHeight: 72
Layout.preferredWidth: 96
color: endMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
implicitHeight: 72
implicitWidth: 96
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
border.color: endMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
border.width: endMinuteField.focus ? 2 : 0
radius: parent.radius - border.width
Behavior on border.width {
Anim {
}
}
}
CustomTextField {
id: endMinuteField
function setConfigText(setting: string): string {
var val = root.convertMinute(root.object[setting]);
if (val === 0) {
return "00";
}
return String(val);
}
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
cursorHeight: height - Appearance.padding.normal * 2
font.family: "Roboto"
font.letterSpacing: -0.25
font.pixelSize: 56
font.weight: 400
horizontalAlignment: TextInput.AlignHCenter
text: setConfigText(root.settings[2])
verticalAlignment: TextInput.AlignVCenter
Keys.onPressed: event => {
if (event.key === Qt.Key_Backspace) {
event.accepted = true;
if (endMinuteField.text.length >= 2) {
endMinuteField.text = "0" + endMinuteField.text[0];
} else if (endMinuteField.text.length === 1) {
endMinuteField.text = "0";
}
return;
} else if (event.key === Qt.Key_Escape) {
event.accepted = true;
endMinuteField.text = setConfigText(root.settings[2]);
endMinuteField.focus = false;
} else if (event.key === Qt.Key_Return) {
endMinuteField.focus = false;
return;
} else if (event.key === Qt.Key_Tab) {
startHourField.focus = true;
} else if (event.key === Qt.Key_Backtab) {
endHourField.focus = true;
}
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
event.accepted = true;
var digit = event.text;
var textLen = endMinuteField.text.length;
if (textLen >= 2 && endMinuteField.text[0] !== '0') {
return;
}
var val = 0;
if (textLen === 0) {
val = parseInt(digit);
} else if (textLen === 1) {
val = parseInt(endMinuteField.text + digit);
} else {
val = parseInt(endMinuteField.text[1] + digit);
}
val = Math.max(0, Math.min(59, val));
if (textLen >= 2 && val < 10) {
endMinuteField.text = "0" + val;
} else {
endMinuteField.text = val.toString();
}
}
event.accepted = true;
}
onCursorPositionChanged: cursorPosition = 2
onEditingFinished: {
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
}
onTextEdited: {
if (endMinuteField.text === "")
return;
var val = parseInt(endMinuteField.text);
if (isNaN(val))
return;
val = Math.max(0, Math.min(23, val));
var newText = val.toString();
if (newText !== endMinuteField.text)
endMinuteField.text = newText;
}
}
}
}
RowLayout {
Layout.fillWidth: true
CustomText {
id: startHour
Layout.preferredWidth: startSeparator.x + startSeparator.width
text: qsTr("Hour")
}
CustomText {
id: startMinute
Layout.preferredWidth: spacer.x + spacer.width - x
text: qsTr("Minute")
}
CustomText {
Layout.preferredWidth: endSeparator.x + endSeparator.width - x
text: qsTr("Hour")
}
CustomText {
text: qsTr("Minute")
}
}
}
}
}
-12
View File
@@ -1,12 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
CustomRect {
id: root
Layout.fillHeight: true
Layout.preferredWidth: 1
color: DynamicColors.tPalette.m3outlineVariant
}
@@ -1,314 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import ZShell.Internal
import qs.Config
import qs.Components
import qs.Helpers
Item {
id: wrapper
property bool changesMade: false
property bool shouldBeActive: true
signal requestCrop
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? 400 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
IconButton {
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: parent.top
icon: "check"
opacity: wrapper.changesMade ? 1 : 0
scale: wrapper.changesMade ? 1 : 0
z: 2
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
onClicked: {
wrapper.requestCrop();
wrapper.changesMade = false;
}
}
RowLayout {
id: root
anchors.fill: parent
spacing: Appearance.spacing.normal
Repeater {
model: ScriptModel {
values: [...Quickshell.screens].sort((a, b) => {
return a.x - b.x;
})
}
Item {
id: delegate
required property ShellScreen modelData
function applyCrop(): void {
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
// We need to calculate the exact percentage coordinates that map perfectly
// to our C++ backend, regardless of current display scaling
const cropXPercent = (cropRect.x - cropRect.imageX) / scaledImg.paintedWidth;
const cropYPercent = (cropRect.y - cropRect.imageY) / scaledImg.paintedHeight;
const cropWidthPercent = cropRect.width / scaledImg.paintedWidth;
const cropHeightPercent = cropRect.height / scaledImg.paintedHeight;
const finalRect = Qt.rect(cropXPercent, cropYPercent, cropWidthPercent, cropHeightPercent);
// We just pass the percentages directly to the backend
Wallpapers.setCrop(delegate.modelData.name, finalRect, cropRect.zoom);
}
function zoomClipRect(zoom: real): void {
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
let oldCenterX = cropRect.x + cropRect.width * 0.5;
let oldCenterY = cropRect.y + cropRect.height * 0.5;
cropRect.zoom = zoom;
cropRect.x = oldCenterX - cropRect.width * 0.5;
cropRect.y = oldCenterY - cropRect.height * 0.5;
cropRect.clampToBounds();
}
Layout.fillHeight: true
Layout.fillWidth: true
Connections {
function onRequestCrop(): void {
delegate.applyCrop();
}
target: wrapper
}
RowLayout {
id: sliderLayout
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 30
CustomSlider {
id: zoomSlider
Layout.fillWidth: true
Layout.leftMargin: Appearance.padding.normal
Layout.preferredHeight: Appearance.padding.larger * 3
Layout.rightMargin: Appearance.padding.normal
from: 1.0
implicitHeight: Appearance.padding.larger * 3
insetIcon: "crop"
to: 5.0
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
onInteraction: value => {
delegate.zoomClipRect(1 + (value * 4));
wrapper.changesMade = true;
}
}
}
CachingImage {
id: scaledImg
property var displayData
property real monitorScale: 1.0
anchors.bottom: sliderLayout.top
anchors.bottomMargin: Appearance.spacing.normal
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
asynchronous: true
fillMode: Image.PreserveAspectFit
retainWhileLoading: true
source: Wallpapers.current
sourceSize.height: parent.height
sourceSize.width: parent.width
onPaintedWidthChanged: {
if (paintedWidth > 0 && cropRectLoader.item) {
cropRectLoader.item.restoreFromData();
}
}
onSourceChanged: {
if (cropRectLoader.item) {
cropRectLoader.item.restoreFromData();
}
}
onStatusChanged: {
if (scaledImg.status == Image.Ready && cropRectLoader.item) {
cropRectLoader.item.restoreFromData();
}
}
CustomText {
id: monitorId
anchors.centerIn: parent
color: Qt.alpha(DynamicColors.palette.m3surface, 0.85)
font.pointSize: Appearance.font.size.large * 4
style: Text.Outline
styleColor: DynamicColors.palette.m3onSurface
text: delegate.modelData.name
}
Loader {
id: cropRectLoader
active: scaledImg.paintedWidth > 0
sourceComponent: Component {
CustomRect {
id: cropRect
property real aspectRatio: delegate.modelData.width / delegate.modelData.height
readonly property real baseHeight: baseWidth / aspectRatio
readonly property real baseWidth: {
let fittedHeight = scaledImg.paintedHeight;
let fittedWidth = fittedHeight * aspectRatio;
if (fittedWidth > scaledImg.paintedWidth) {
fittedWidth = scaledImg.paintedWidth;
fittedHeight = fittedWidth / aspectRatio;
}
return fittedWidth;
}
readonly property real imageX: (scaledImg.width - scaledImg.paintedWidth) / 2
readonly property real imageY: (scaledImg.height - scaledImg.paintedHeight) / 2
property real imgAspectRatio: scaledImg.paintedWidth / scaledImg.paintedHeight
property real zoom: 1.0
function centerInImage() {
x = imageX + (scaledImg.paintedWidth - width) / 2;
y = imageY + (scaledImg.paintedHeight - height) / 2;
}
function clampToBounds() {
x = Math.max(imageX, Math.min(x, imageX + scaledImg.paintedWidth - width));
y = Math.max(imageY, Math.min(y, imageY + scaledImg.paintedHeight - height));
}
function restoreFromData() {
let data = Wallpapers.getCrop(delegate.modelData.name);
if (data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001)) {
zoom = data.zoom > 0 ? data.zoom : 1.0;
x = imageX + (data.x * scaledImg.paintedWidth);
y = imageY + (data.y * scaledImg.paintedHeight);
clampToBounds();
} else {
zoom = 1.0;
centerInImage();
}
}
border.color: DynamicColors.palette.m3primary
border.width: 2
height: baseHeight / zoom
opacity: 1
width: baseWidth / zoom
Behavior on opacity {
Anim {
}
}
Component.onCompleted: {
restoreFromData();
}
onHeightChanged: clampToBounds()
onWidthChanged: clampToBounds()
}
}
}
MouseArea {
id: mouse
function updateCrop(mouseX, mouseY) {
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
let nx = mouseX - cropRect.width * 0.5;
let ny = mouseY - cropRect.height * 0.5;
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + scaledImg.paintedWidth - cropRect.width));
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + scaledImg.paintedHeight - cropRect.height));
cropRect.x = nx;
cropRect.y = ny;
}
anchors.fill: parent
hoverEnabled: true
preventStealing: true
onPositionChanged: mouse => {
if (pressed) {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
}
onPressed: mouse => {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
onReleased: {
wrapper.changesMade = true;
}
}
}
}
}
}
}
-158
View File
@@ -1,158 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Models
import qs.Components
import qs.Helpers
import qs.Config
GridView {
id: root
readonly property int columnsCount: Math.max(1, Math.floor(width / minCellWidth))
readonly property int minCellWidth: 200 + Appearance.spacing.normal
property bool shouldBeActive: true
anchors.left: parent.left
anchors.right: parent.right
cellHeight: 140 + Appearance.spacing.normal
cellWidth: width / columnsCount
clip: true
implicitHeight: shouldBeActive ? contentHeight : 0
interactive: false
model: Wallpapers.list
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
delegate: Item {
required property int index
readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent
readonly property real itemMargin: Appearance.spacing.normal
readonly property real itemRadius: Appearance.rounding.small
required property var modelData
height: root.cellHeight
width: root.cellWidth
CustomClippingRect {
id: image
anchors.bottomMargin: itemMargin
anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
antialiasing: true
color: DynamicColors.tPalette.m3surfaceContainer
radius: itemRadius
CachingImage {
id: cachingImage
anchors.fill: parent
antialiasing: true
cache: true
fillMode: Image.PreserveAspectCrop
opacity: status === Image.Ready ? 1 : 0
path: modelData.path
smooth: true
sourceSize: Qt.size(width, height)
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
duration: 1000
easing.type: Easing.OutQuad
}
}
}
Image {
id: fallbackImage
anchors.fill: parent
antialiasing: true
asynchronous: true
cache: true
fillMode: Image.PreserveAspectCrop
opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0
smooth: true
source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : ""
sourceSize: Qt.size(width, height)
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
duration: 1000
easing.type: Easing.OutQuad
}
}
}
Rectangle {
anchors.fill: parent
antialiasing: true
border.color: DynamicColors.palette.m3primary
border.width: isCurrent ? 2 : 0
color: "transparent"
radius: itemRadius + 2
smooth: true
Behavior on border.width {
NumberAnimation {
duration: 150
easing.type: Easing.OutQuad
}
}
MaterialIcon {
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.top: parent.top
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
text: "check_circle"
visible: isCurrent
}
}
Timer {
id: fallbackTimer
property bool triggered: false
interval: 800
running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null
onTriggered: triggered = true
}
}
StateLayer {
anchors.bottomMargin: itemMargin
anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
radius: itemRadius
onClicked: {
Wallpapers.setWallpaper(modelData.path);
}
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
}
+46
View File
@@ -0,0 +1,46 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Modules.Settings.NavPane
import qs.Config
ColumnLayout {
id: root
required property SettingsState sState
spacing: Appearance.spacing.large
SearchBar {
id: searchField
Layout.fillWidth: true
bg.border.color: DynamicColors.palette.m3outlineVariant
bg.color: DynamicColors.tPalette.m3surfaceContainerLowest
clearIcon.font.pointSize: Appearance.font.size.large
clearIcon.padding: Appearance.padding.extraSmall
font.pointSize: Appearance.font.size.normal
placeholderText: qsTr("Search settings")
searchIcon.anchors.leftMargin: Appearance.padding.largeIncreased
searchIcon.font.pointSize: Appearance.font.size.large
Behavior on bg.border.color {
CAnim {
}
}
Binding {
property: "searchOpen"
target: root.sState
value: searchField.text.length > 0
}
}
NavLocations {
Layout.bottomMargin: -bottomMargin
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: -topMargin
sState: root.sState
}
}
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Modules.SettingsNew
import qs.Modules.Settings
VerticalFadeFlickable {
id: root
@@ -16,6 +16,10 @@ VerticalFadeFlickable {
fadeAmount: 0.1
topMargin: Appearance.padding.large
TapHandler {
onTapped: root.focus = true
}
ColumnLayout {
id: content
@@ -4,14 +4,14 @@ import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.SettingsNew.Pages
import qs.Modules.SettingsNew.Pages.Wallpaper
import qs.Modules.SettingsNew.Pages.Audio
import qs.Modules.SettingsNew.Pages.Apps
import qs.Modules.SettingsNew.Pages.Panels
import qs.Modules.SettingsNew.Pages.Panels.Bar
import qs.Modules.SettingsNew.Pages.Services
import qs.Modules.Settings.Common
import qs.Modules.Settings.Pages
import qs.Modules.Settings.Pages.Wallpaper
import qs.Modules.Settings.Pages.Audio
import qs.Modules.Settings.Pages.Apps
import qs.Modules.Settings.Pages.Panels
import qs.Modules.Settings.Pages.Panels.Bar
import qs.Modules.Settings.Pages.Services
QtObject {
id: root
@@ -22,7 +22,7 @@ QtObject {
// Wallpaper & style
StackPage {
Component {
Wallpaper {
WallpaperPage {
}
}
@@ -36,7 +36,7 @@ QtObject {
// Screenshot
StackPage {
Component {
Screenshot {
ScreenshotPage {
}
}
}
@@ -150,6 +150,16 @@ QtObject {
}
}
}
},
// About
Component {
StackPage {
Component {
AboutPage {
}
}
}
}
]
readonly property Component placeholderComp: Component {
@@ -60,5 +60,11 @@ Singleton {
description: qsTr("Poll intervals, audio and brightness increments"),
category: "shell"
},
{
name: qsTr("About"),
icon: "info",
description: qsTr("System information"),
category: "about"
},
]
}
+117
View File
@@ -0,0 +1,117 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Io
import ZShell
import qs.Modules.Settings.Common
import qs.Config
import qs.Helpers
import qs.Components
PageBase {
id: root
property string cliVersion
readonly property int pluginCount: 0
property string quickshellVersion
title: qsTr("About")
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
spacing: Appearance.spacing.extraSmall / 2
width: root.cappedWidth
Process {
command: ["quickshell", "--version"]
running: true
stdout: StdioCollector {
onStreamFinished: root.quickshellVersion = text.trim().split(" ")[1] ?? ""
}
}
ConnectedRect {
Layout.fillWidth: true
first: true
implicitHeight: hero.implicitHeight + Appearance.padding.extraLarge * 2
last: true
ColumnLayout {
id: hero
anchors.centerIn: parent
spacing: Appearance.spacing.small
width: parent.width - Appearance.padding.largeIncreased * 2
CustomText {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Appearance.spacing.small
font.pointSize: Appearance.font.size.large
text: "ZShell"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.medium
text: ZUtils.version ? `v${ZUtils.version}` : "…"
}
}
}
// System
SectionHeader {
text: qsTr("System")
}
InfoRow {
first: true
label: qsTr("Hostname")
value: SystemInfo.hostname
}
InfoRow {
label: qsTr("Device")
value: SystemInfo.device
}
InfoRow {
label: qsTr("Distro")
value: SystemInfo.osPrettyName || SystemInfo.osName
}
InfoRow {
label: qsTr("Kernel")
value: SystemInfo.kernel
}
InfoRow {
label: qsTr("Firmware")
last: true
value: SystemInfo.firmware
}
// Software
SectionHeader {
text: qsTr("Software")
}
InfoRow {
first: true
label: qsTr("Shell")
value: ZUtils.version || "…"
}
InfoRow {
label: qsTr("Quickshell")
value: root.quickshellVersion || "…"
}
InfoRow {
label: qsTr("Qt")
last: true
value: ZUtils.qtVersion || "…"
}
}
}
@@ -7,7 +7,7 @@ import Quickshell.Widgets
import qs.Components
import qs.Config
import qs.Helpers
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -5,7 +5,7 @@ import Quickshell.Widgets
import qs.Components
import qs.Config
import qs.Helpers
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -8,7 +8,7 @@ import ZShell
import qs.Helpers
import qs.Components
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
import qs.Helpers
import qs.Components
import qs.Config
@@ -5,7 +5,7 @@ import QtQuick.Layouts
import qs.Daemons
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
import qs.Helpers
PageBase {
@@ -3,7 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -2,7 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick.Layouts
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -2,7 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick.Layouts
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -2,7 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick.Layouts
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -1,6 +1,6 @@
import QtQuick.Layouts
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -6,8 +6,8 @@ import ZShell.Components
import qs.Helpers
import qs.Components
import qs.Config
import qs.Modules.SettingsNew
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -17,9 +17,14 @@ PageBase {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
spacing: Appearance.spacing.large
spacing: Appearance.spacing.extraSmall / 2
width: root.cappedWidth
SectionHeader {
first: true
text: qsTr("Effects")
}
ToggleRow {
checked: Config.screenshot.enable_pp
first: true
@@ -29,7 +34,6 @@ PageBase {
}
SelectRow {
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
enabled: Config.screenshot.enable_pp
last: true
@@ -55,6 +59,10 @@ PageBase {
}
}
SectionHeader {
text: qsTr("Rounded corners")
}
ToggleRow {
checked: Config.screenshot.rounding
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
@@ -65,7 +73,6 @@ PageBase {
}
SpinRow {
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
from: 0
last: true
@@ -80,6 +87,10 @@ PageBase {
}
}
SectionHeader {
text: qsTr("Shadow")
}
ToggleRow {
checked: Config.screenshot.shadow
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
@@ -90,7 +101,6 @@ PageBase {
}
SpinRow {
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
from: 0
stepSize: 1
@@ -105,7 +115,6 @@ PageBase {
}
SpinRow {
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
from: -100
stepSize: 10
@@ -120,7 +129,6 @@ PageBase {
}
SpinRow {
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
from: -100
last: true
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -5,7 +5,7 @@ import ZShell.Services
import qs.Components
import qs.Helpers
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root
@@ -7,7 +7,7 @@ import qs.Paths
import qs.Helpers
import qs.Components
import qs.Config
import qs.Modules.SettingsNew.Common
import qs.Modules.Settings.Common
PageBase {
id: root

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