409 changed files with 15961 additions and 22210 deletions
-58
View File
@@ -1,58 +0,0 @@
---
BasedOnStyle: LLVM
AccessModifierOffset: 0
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: Align
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: WithoutElse
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IndentCaseLabels: false
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 100
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: false
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++20
TabWidth: 4
UseTab: Always
-27
View File
@@ -1,27 +0,0 @@
---
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-*,
cppcoreguidelines-init-variables,
misc-no-recursion,
concurrency-mt-unsafe,
readability-else-after-return,
readability-make-member-function-const,
readability-redundant-*,
-readability-redundant-inline-specifier,
readability-simplify-*,
modernize-use-override,
modernize-use-nullptr,
modernize-deprecated-headers,
HeaderFilterRegex: ".*"
FormatStyle: file
...
-30
View File
@@ -1,30 +0,0 @@
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
@@ -1,29 +0,0 @@
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'
+3 -16
View File
@@ -38,18 +38,15 @@ jobs:
rustfmt \ rustfmt \
rust-clippy rust-clippy
- id: format-check - name: Format check
name: Format check
continue-on-error: true continue-on-error: true
run: | run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \ cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \ echo "$manifest: formatting OK" || \
{ echo "$manifest: needs formatting"; status=1; } echo "$manifest: needs formatting"
done done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml" echo "Rust files found but no Cargo.toml"
exit 1 exit 1
@@ -57,8 +54,7 @@ jobs:
echo "No Rust project found" echo "No Rust project found"
fi fi
- id: clippy - name: Clippy
name: Clippy
run: | run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0 status=0
@@ -74,12 +70,3 @@ jobs:
else else
echo "No Rust project found" echo "No Rust project found"
fi fi
- name: Check results
if: always()
run: |
if [ "${{ steps.format-check.outcome }}" = "failure" ] || [ "${{ steps.clippy.outcome }}" = "failure" ]; then
echo "One or more checks failed"
exit 1
fi
echo "All checks passed"
-2
View File
@@ -13,5 +13,3 @@ uv.lock
.qtcreator/ .qtcreator/
dist/ dist/
**/target/ **/target/
**/test-plugins/
**/Charts/
+1 -31
View File
@@ -1,35 +1,6 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
if(NOT DEFINED VERSION) project(ZShell)
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 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -54,7 +25,6 @@ add_compile_options(
if("plugin" IN_LIST ENABLE_MODULES) if("plugin" IN_LIST ENABLE_MODULES)
add_subdirectory(Plugins) add_subdirectory(Plugins)
endif() endif()
if("shell" IN_LIST ENABLE_MODULES) if("shell" IN_LIST ENABLE_MODULES)
-51
View File
@@ -1,51 +0,0 @@
import QtQuick
import qs.Config
AnchorAnimation {
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial
}
property int type: AnchorAnim.DefaultSpatial
duration: {
if (type < AnchorAnim.StandardSmall || type > AnchorAnim.SlowSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Appearance.anim.durations.large;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type == AnchorAnim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge)
return Appearance.anim.curves.standard;
if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.expressiveDefaultSpatial;
}
}
+3 -58
View File
@@ -2,62 +2,7 @@ import QtQuick
import qs.Config import qs.Config
NumberAnimation { NumberAnimation {
enum Type { duration: Appearance.anim.durations.normal
StandardSmall = 0, easing.bezierCurve: Appearance.anim.curves.standard
Standard, easing.type: Easing.BezierSpline
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: Anim.DefaultSpatial
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
} }
+3
View File
@@ -12,8 +12,11 @@ Slider {
readonly property bool isVertical: orientation === Qt.Vertical readonly property bool isVertical: orientation === Qt.Vertical
property real multiplier: 100 property real multiplier: 100
property real oldValue property real oldValue
// Wrapper components can inject their own track visuals here.
property Component trackContent property Component trackContent
// Keep current behavior for existing usages.
orientation: Qt.Vertical orientation: Qt.Vertical
background: CustomRect { background: CustomRect {
-87
View File
@@ -1,87 +0,0 @@
import QtQuick
import qs.Config
CustomRect {
id: root
enum ButtonType {
Filled,
Tonal,
Text
}
property color activeColor
property color activeOnColor
property bool checked
property real checkedRadius: Appearance.rounding.medium
property real defaultRadius: Appearance.rounding.large
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property bool fillWidth
property font font: ({
family: Appearance.font.family.sans,
pointSize: Appearance.font.size.larger,
bold: false
})
property real horizontalPadding: padding
readonly property alias hovered: stateLayer.containsMouse
required implicitHeight
required implicitWidth
property color inactiveColor
property color inactiveOnColor
property bool internalChecked
property bool isRound
property bool isToggle
readonly property color onColor: !enabled ? disabledOnColor : internalChecked ? activeOnColor : inactiveOnColor
property real padding
readonly property alias pressed: stateLayer.pressed
property real pressedRadius: Appearance.rounding.small
readonly property alias radiusAnim: radiusAnim
property bool radiusMorph: true
property alias shapeMorph: stateLayer.shapeMorph
property real shapeMorphExpansion: shapeMorph && pressed ? 24 : 0
readonly property alias stateLayer: stateLayer
property int type: ButtonBase.Filled
property real verticalPadding: padding
signal clicked
color: type === ButtonBase.Text ? "transparent" : !enabled ? disabledColor : internalChecked ? activeColor : inactiveColor
radius: {
if (radiusMorph && pressed)
return pressedRadius;
if (internalChecked)
return checkedRadius;
if (isRound)
return (height || implicitHeight) / 2 * Math.min(1, Appearance.rounding.scale);
return defaultRadius;
}
Behavior on radius {
Anim {
id: radiusAnim
type: Anim.DefaultEffects
}
}
Behavior on shapeMorphExpansion {
Anim {
type: Anim.FastSpatial
}
}
onCheckedChanged: internalChecked = checked
StateLayer {
id: stateLayer
color: root.internalChecked ? root.activeOnColor : root.inactiveOnColor
enabled: root.enabled
onClicked: {
if (root.isToggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
}
}
+5 -4
View File
@@ -17,8 +17,8 @@ BusyIndicator {
} }
property int animState property int animState
property color bgColor: DynamicColors.palette.m3secondaryContainer property color bgColour: DynamicColors.palette.m3secondaryContainer
property color fgColor: DynamicColors.palette.m3primary property color fgColour: DynamicColors.palette.m3primary
property real implicitSize: Appearance.font.size.normal * 3 property real implicitSize: Appearance.font.size.normal * 3
property real internalStrokeWidth: strokeWidth property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress readonly property alias progress: manager.progress
@@ -31,8 +31,8 @@ BusyIndicator {
contentItem: CircularProgress { contentItem: CircularProgress {
anchors.fill: parent anchors.fill: parent
bgColor: root.bgColor bgColour: root.bgColour
fgColor: root.fgColor fgColour: root.fgColour
padding: root.padding padding: root.padding
rotation: manager.rotation rotation: manager.rotation
startAngle: manager.startFraction * 360 startAngle: manager.startFraction * 360
@@ -73,6 +73,7 @@ BusyIndicator {
CircularIndicatorManager { CircularIndicatorManager {
id: manager id: manager
} }
NumberAnimation { NumberAnimation {
+26 -76
View File
@@ -1,116 +1,66 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Shapes
import ZShell.Components
import qs.Config import qs.Config
Item { Shape {
id: root id: root
readonly property real arcRadius: (size - padding - strokeWidth * (1 + waveAmplitude * 2)) / 2 readonly property real arcRadius: (size - padding - strokeWidth) / 2
property color bgColor: DynamicColors.palette.m3secondaryContainer property color bgColour: DynamicColors.palette.m3secondaryContainer
property real clampedVal: Math.max(1 / 360, Math.min(1, isNaN(value) ? 0 : value)) property color fgColour: DynamicColors.palette.m3primary
readonly property real dotAngleRad: (startAngle + sweepAngle - gapAngle * (sweepAngle < 360 ? 0 : 1)) * Math.PI / 180
property color fgColor: DynamicColors.palette.m3primary
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI) readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
property alias hasEndIndicator: dot.active
property real implicitSize
property int padding: 0 property int padding: 0
readonly property real size: Math.min(width, height) readonly property real size: Math.min(width, height)
property int spacing: Appearance.spacing.small property int spacing: Appearance.spacing.small
property int startAngle: -90 property int startAngle: -90
property int strokeWidth: Appearance.padding.small property int strokeWidth: Appearance.padding.smaller
property int sweepAngle: 360 readonly property real vValue: value || 1 / 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value property real value
property alias waveAmplitude: wave.amplitudeMultiplier
property alias waveDuration: waveProgAnim.duration
property alias waveFrequency: wave.frequency
property bool wavePaused
property bool wavy: false
implicitHeight: implicitSize
implicitWidth: implicitSize
Shape {
asynchronous: true asynchronous: true
opacity: Math.min(1, remainingArc.sweepAngle)
preferredRendererType: Shape.CurveRenderer preferredRendererType: Shape.CurveRenderer
ShapePath { ShapePath {
capStyle: ShapePath.RoundCap capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent" fillColor: "transparent"
strokeColor: root.bgColor strokeColor: root.bgColour
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth strokeWidth: root.strokeWidth
Behavior on strokeColor { Behavior on strokeColor {
CAnim { CAnim {
duration: Appearance.anim.durations.large
} }
} }
PathAngleArc { PathAngleArc {
id: remainingArc
centerX: root.size / 2 centerX: root.size / 2
centerY: root.size / 2 centerY: root.size / 2
radiusX: root.arcRadius radiusX: root.arcRadius
radiusY: root.arcRadius radiusY: root.arcRadius
startAngle: root.startAngle + root.clampedVal * root.sweepAngle + root.gapAngle startAngle: root.startAngle + 360 * root.vValue + root.gapAngle
sweepAngle: Math.max(1 / 360, root.sweepAngle * (1 - root.clampedVal) - root.gapAngle * (root.sweepAngle < 360 ? 1 : 2)) sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2)
}
} }
} }
WavyLine { ShapePath {
id: wave capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.fgColour
strokeWidth: root.strokeWidth
amplitudeMultiplier: root.wavy ? 0.5 : 0 Behavior on strokeColor {
anchors.fill: parent
anchors.margins: -lineWidth * amplitudeMultiplier
color: root.fgColor
frequency: 8
fullAngle: root.sweepAngle
lineWidth: root.strokeWidth
pathType: WavyLine.Arc
radius: root.arcRadius
startAngle: root.startAngle
value: root.clampedVal
Behavior on amplitudeMultiplier {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on color {
CAnim { CAnim {
} duration: Appearance.anim.durations.large
}
Anim on waveProgress {
id: waveProgAnim
duration: 2000
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: root.wavePaused || wave.amplitudeMultiplier === 0
running: true
to: 1
} }
} }
Loader { PathAngleArc {
id: dot centerX: root.size / 2
centerY: root.size / 2
x: root.size / 2 + root.arcRadius * Math.cos(root.dotAngleRad) - width / 2 radiusX: root.arcRadius
y: root.size / 2 + root.arcRadius * Math.sin(root.dotAngleRad) - height / 2 radiusY: root.arcRadius
startAngle: root.startAngle
sourceComponent: CustomRect { sweepAngle: 360 * root.vValue
color: root.fgColor
implicitHeight: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
implicitWidth: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
opacity: Math.min(1, remainingArc.sweepAngle)
radius: Appearance.rounding.full
} }
} }
} }
+5 -5
View File
@@ -59,15 +59,15 @@ ColumnLayout {
} }
StateLayer { StateLayer {
function onClicked(): void {
root.toggleRequested();
root.expanded = !root.expanded;
}
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3onSurface color: DynamicColors.palette.m3onSurface
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
showHoverBackground: false showHoverBackground: false
onClicked: {
root.toggleRequested();
root.expanded = !root.expanded;
}
} }
} }
+5 -28
View File
@@ -45,10 +45,10 @@ Item {
} }
function syncFromPenColor() { function syncFromPenColor() {
if (!drawing.drawingState) if (!drawing)
return; return;
const c = drawing.drawingState.penColor; const c = drawing.penColor;
if (c.hsvSaturation > 0) { if (c.hsvSaturation > 0) {
currentHue = c.hsvHue; currentHue = c.hsvHue;
@@ -85,20 +85,12 @@ Item {
currentHue = relative / arcSweep; currentHue = relative / arcSweep;
lastChromaticHue = currentHue; lastChromaticHue = currentHue;
drawing.drawingState.penColor = Qt.hsva(currentHue, drawing.drawingState.penColor.hsvSaturation, drawing.drawingState.penColor.hsvValue, drawing.drawingState.penColor.a); drawing.penColor = Qt.hsva(currentHue, drawing.penColor.hsvSaturation, drawing.penColor.hsvValue, drawing.penColor.a);
} }
implicitHeight: 180 implicitHeight: 180
implicitWidth: 220 implicitWidth: 220
Behavior on currentHue {
enabled: !root.dragActive
Anim {
type: Anim.StandardLarge
}
}
Component.onCompleted: syncFromPenColor() Component.onCompleted: syncFromPenColor()
onCurrentHueChanged: canvas.requestPaint() onCurrentHueChanged: canvas.requestPaint()
onDrawingChanged: syncFromPenColor() onDrawingChanged: syncFromPenColor()
@@ -111,7 +103,7 @@ Item {
root.syncFromPenColor(); root.syncFromPenColor();
} }
target: root.drawing.drawingState target: root.drawing
} }
Canvas { Canvas {
@@ -149,21 +141,6 @@ Item {
} }
} }
CustomRect {
anchors.centerIn: parent
color: root.drawing?.drawingState.penColor
implicitHeight: implicitWidth
implicitWidth: canvas.height - root.handleSize - Appearance.padding.extraLarge * 2
radius: Appearance.rounding.full
Behavior on color {
enabled: false
CAnim {
}
}
}
Item { Item {
id: handle id: handle
@@ -197,7 +174,7 @@ Item {
Rectangle { Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
color: Qt.hsla(root.currentHue, 1.0, 0.5, 1.0) color: root.drawing ? root.drawing.penColor : Qt.hsla(root.currentHue, 1.0, 0.5, 1.0)
height: width height: width
radius: width / 2 radius: width / 2
width: parent.width - 12 width: parent.width - 12
+3 -3
View File
@@ -23,10 +23,10 @@ Button {
} }
StateLayer { StateLayer {
radius: control.radius function onClicked(): void {
onClicked: {
control.clicked(); control.clicked();
} }
radius: control.radius
} }
} }
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Widgets
import QtQuick
ClippingWrapperRectangle {
id: root
color: "transparent"
Behavior on color {
CAnim {
}
}
}
-20
View File
@@ -1,33 +1,13 @@
import QtQuick import QtQuick
import qs.Helpers
Flickable { Flickable {
id: root id: root
property bool doneFakeFlick
interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim { Anim {
properties: "x,y" properties: "x,y"
} }
} }
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: root.doneFakeFlick = false
}
} }
-20
View File
@@ -1,33 +1,13 @@
import QtQuick import QtQuick
import qs.Helpers
ListView { ListView {
id: root id: root
property bool doneFakeFlick
interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim { Anim {
properties: "x,y" properties: "x,y"
} }
} }
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: root.doneFakeFlick = false
}
} }
-237
View File
@@ -1,237 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell
import ZShell.Components
import ZShell.Internal
import qs.Config
ProgressBar {
id: root
enum IndeterminateAnimState {
Running,
Completing,
Stopped
}
property color bgColor: DynamicColors.palette.m3secondaryContainer
property color fgColor: DynamicColors.palette.m3primary
property int indeterminateAnimState: CustomProgressBar.Stopped
property real waveAmplitude: 0.5
property int waveDuration: 1000
property int waveFrequency: 6
property bool wavePaused
property bool wavy
function toBounds(startFrac: real, endFrac: real, gapSize: real): point {
startFrac = ZUtils.clamp(startFrac, 0, 1);
endFrac = ZUtils.clamp(endFrac, 0, 1);
// Ramp down gap size
const GAP_RAMP_DOWN_THRESHOLD = 0.01;
gapSize += height / 2;
const startGapSize = (gapSize * ZUtils.clamp(startFrac, 0, GAP_RAMP_DOWN_THRESHOLD) / GAP_RAMP_DOWN_THRESHOLD);
const endGapSize = (gapSize * (1 - ZUtils.clamp(endFrac, 1 - GAP_RAMP_DOWN_THRESHOLD, 1)) / GAP_RAMP_DOWN_THRESHOLD);
const start = width * startFrac + startGapSize;
const end = width * endFrac - endGapSize;
return start >= end ? Qt.point(0, 0) : Qt.point(start, end);
}
function updateIAnimState(): void {
if (indeterminate) {
manager.completeEndProgress = 0;
indeterminateAnimState = CustomProgressBar.Running;
} else if (indeterminateAnimState === CustomProgressBar.Running) {
indeterminateAnimState = CustomProgressBar.Completing;
}
}
implicitHeight: 4
implicitWidth: 200
contentItem: Loader {
anchors.fill: parent
asynchronous: true
sourceComponent: root.indeterminate || root.indeterminateAnimState !== CustomProgressBar.Stopped ? indeterminateComp : determinateComp
}
Component.onCompleted: updateIAnimState()
onIndeterminateChanged: updateIAnimState()
LinearIndicatorManager {
id: manager
gap: Appearance.spacing.extraSmall
Anim on completeEndProgress {
duration: manager.completeEndDuration
running: root.indeterminateAnimState === CustomProgressBar.Completing
to: 1
onFinished: {
if (root.indeterminateAnimState === CustomProgressBar.Completing)
root.indeterminateAnimState = CustomProgressBar.Stopped;
}
}
Anim on progress {
duration: manager.duration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.indeterminateAnimState !== CustomProgressBar.Stopped
to: 1
}
}
Component {
id: determinateComp
Item {
Line {
id: remaining
anchors.right: parent.right
implicitWidth: parent.width - wave.implicitWidth - Appearance.spacing.extraSmall
}
Line {
property real implicitSize
anchors.right: parent.right
anchors.rightMargin: (parent.height - implicitHeight) / 2
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: implicitSize
implicitWidth: implicitSize
radius: Appearance.rounding.full
Behavior on implicitSize {
Anim {
type: Anim.FastSpatial
}
}
Component.onCompleted: implicitSize = Qt.binding(() => parent.width - wave.implicitWidth < parent.height ? parent.height : 4)
}
Wave {
id: wave
anchors.left: parent.left
Behavior on implicitWidth {
Anim {
}
}
Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition)
}
}
}
Component {
id: indeterminateComp
Item {
id: content
Line {
bounds: {
const i = manager.activeIndicators[0]; // qmllint disable unresolved-type
return i ? root.toBounds(0, i.startFraction, i.gapSize / 2) : Qt.point(0, 0);
}
}
Line {
bounds: {
const i = manager.activeIndicators[manager.activeIndicators.length - 1]; // qmllint disable unresolved-type
return i ? root.toBounds(i.endFraction, 1, i.gapSize / 2) : Qt.point(0, 0);
}
}
Instantiator {
model: Math.max(manager.activeIndicators.length, 1) - 1 // qmllint disable unresolved-type
delegate: Line {
readonly property LinearIndicatorSegment cur: manager.activeIndicators[index] // qmllint disable unresolved-type
required property int index
readonly property LinearIndicatorSegment next: manager.activeIndicators[index + 1 % manager.activeIndicators.length] // qmllint disable unresolved-type
bounds: root.toBounds(cur.endFraction, next.startFraction, cur.gapSize / 2)
}
onObjectAdded: (_, obj) => content.data.push(obj)
onObjectRemoved: (_, obj) => {
const idx = content.data.indexOf(obj);
if (idx !== -1)
content.data.splice(idx, 1);
}
}
Instantiator {
model: manager.activeIndicators // qmllint disable unresolved-type
delegate: Wave {
readonly property point bounds: root.toBounds(modelData.startFraction, modelData.endFraction, modelData.gapSize / 2)
required property LinearIndicatorSegment modelData
color: root.fgColor
implicitWidth: bounds.y - bounds.x
x: bounds.x
}
onObjectAdded: (_, obj) => content.data.push(obj)
onObjectRemoved: (_, obj) => {
const idx = content.data.indexOf(obj);
if (idx !== -1)
content.data.splice(idx, 1);
}
}
}
}
component Line: CustomRect {
property point bounds
anchors.verticalCenter: parent.verticalCenter
color: root.bgColor
implicitHeight: parent.height
implicitWidth: bounds.y - bounds.x
radius: Appearance.rounding.full
x: bounds.x
}
component Wave: WavyLine {
id: wave
amplitudeMultiplier: root.wavy ? root.waveAmplitude : 0
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
frequency: root.waveFrequency
fullLength: parent.width
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
lineWidth: parent.height
startX: x
Behavior on amplitudeMultiplier {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: wave.amplitudeMultiplier === 0 || root.wavePaused
running: true
to: 1
}
}
}
+4 -4
View File
@@ -33,13 +33,13 @@ RadioButton {
} }
StateLayer { StateLayer {
function onClicked(): void {
root.click();
}
anchors.margins: -7 anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1 z: -1
onClicked: {
root.click();
}
} }
CustomRect { CustomRect {
+125 -98
View File
@@ -1,88 +1,38 @@
import qs.Config
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Templates
import qs.Helpers
import qs.Config
ScrollBar { ScrollBar {
id: root id: root
readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize) property bool _updatingFromFlickable: false
readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize) property bool _updatingFromUser: false
property bool animating
required property Flickable flickable required property Flickable flickable
readonly property real nonAnimHeight: flickable.height / flickable.contentHeight property real nonAnimPosition
readonly property real nonAnimY: flickable.contentY / flickable.contentHeight
readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight)
readonly property bool reversed: flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop
property bool shouldBeActive property bool shouldBeActive
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
enabled: !Visibilities.getForActive().isDrawing implicitWidth: 8
implicitWidth: Appearance.padding.extraSmall * 2
contentItem: Item {
}
Behavior on position {
enabled: !fullMouse.pressed
Anim {
}
}
onHoveredChanged: {
if (hovered)
shouldBeActive = hovered;
else
hideDelay.restart();
}
Connections {
function onMovingChanged() {
if (root.flickable.moving)
root.shouldBeActive = true;
else
hideDelay.restart();
}
target: root.flickable
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Appearance.rounding.full
CustomRect {
id: handle
contentItem: CustomRect {
anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: DynamicColors.palette.m3secondary color: DynamicColors.palette.m3secondary
implicitHeight: root.height * root.effectiveSize
implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Appearance.padding.extraSmall * 2 : Appearance.padding.extraSmall
opacity: { opacity: {
if (!root.enabled)
return 0;
if (root.size === 1) if (root.size === 1)
return 0; return 0;
if (fullMouse.pressed) if (fullMouse.pressed)
return 1; return 1;
if (fullMouse.containsMouse) if (mouse.containsMouse)
return 0.8; return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive) if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6; return 0.6;
return 0; return 0;
} }
radius: Appearance.rounding.full radius: Appearance.rounding.full
y: root.reversed ? root.height * (1 + root.nonAnimY) * root.travelScale : root.height * root.nonAnimY * root.travelScale
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects
} }
} }
@@ -95,6 +45,72 @@ ScrollBar {
hoverEnabled: true hoverEnabled: true
} }
} }
Behavior on position {
enabled: !fullMouse.pressed
Anim {
}
}
Component.onCompleted: {
if (flickable) {
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
}
}
}
onHoveredChanged: {
if (hovered)
shouldBeActive = true;
else
shouldBeActive = flickable.moving;
}
// Sync nonAnimPosition with Qt's automatic position binding
onPositionChanged: {
if (_updatingFromUser) {
_updatingFromUser = false;
return;
}
if (position === nonAnimPosition) {
animating = false;
return;
}
if (!animating && !_updatingFromFlickable && !fullMouse.pressed) {
nonAnimPosition = position;
}
}
// Sync nonAnimPosition with flickable when not animating
Connections {
function onContentYChanged() {
if (!animating && !fullMouse.pressed) {
_updatingFromFlickable = true;
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
} else {
nonAnimPosition = 0;
}
_updatingFromFlickable = false;
}
}
target: flickable
}
Connections {
function onMovingChanged(): void {
if (root.flickable.moving)
root.shouldBeActive = true;
else
hideDelay.restart();
}
target: root.flickable
} }
Timer { Timer {
@@ -108,55 +124,66 @@ ScrollBar {
CustomMouseArea { CustomMouseArea {
id: fullMouse id: fullMouse
property real pressOffset: 0 function onWheel(event: WheelEvent): void {
root.animating = true;
function contentYFromThumbTop(thumbTop) { root._updatingFromUser = true;
var visualPos = root.effectiveTravel > 0 ? thumbTop / root.travelScale : 0; let newPos = root.nonAnimPosition;
if (event.angleDelta.y > 0)
return root.reversed ? (visualPos - 1) * root.flickable.contentHeight : visualPos * root.flickable.contentHeight; newPos = Math.max(0, root.nonAnimPosition - 0.1);
else if (event.angleDelta.y < 0)
newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1);
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
} }
function updateFromEvent(event) {
var posInTrack = event.y / root.height;
var thumbTop = posInTrack - pressOffset;
thumbTop = Math.max(0, Math.min(root.effectiveTravel, thumbTop));
root.flickable.contentY = contentYFromThumbTop(thumbTop);
} }
function visualThumbTop() {
const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY;
return visualPos * root.travelScale;
} }
anchors.fill: parent anchors.fill: parent
cursorShape: undefined
hoverEnabled: true
preventStealing: true preventStealing: true
onPositionChanged: event => { onPositionChanged: event => {
if (pressed) root._updatingFromUser = true;
updateFromEvent(event); const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
} }
onPressed: event => { onPressed: event => {
var currentTop = visualThumbTop(); root.animating = true;
var currentBottom = currentTop + root.effectiveSize; root._updatingFromUser = true;
var clickPos = event.y / root.height; const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
var clickedInsideThumb = clickPos >= currentTop && clickPos <= currentBottom; // Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (clickedInsideThumb) { if (root.flickable) {
pressOffset = clickPos - currentTop; const contentHeight = root.flickable.contentHeight;
} else { const height = root.flickable.height;
pressOffset = root.effectiveSize / 2; if (contentHeight > height) {
} const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
updateFromEvent(event); const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
} root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
onWheel: event => { }
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1; }
var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta));
root.position = newPos;
} }
} }
} }
+26 -180
View File
@@ -1,201 +1,47 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import qs.Config import qs.Config
Slider { Slider {
id: root id: root
property bool animateWave property color color: DynamicColors.palette.m3primary
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property alias inset: inset
property color insetColor: enabled ? (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) : DynamicColors.palette.m3onSurface
property string insetIcon: ""
property real pos: visualPosition
property int radius: Appearance.rounding.small
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
signal interaction(v: real)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
anchors.fill: parent
background: Item {
CustomRect { CustomRect {
id: remaining
anchors.left: handle.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
bottomLeftRadius: Appearance.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: root.radius
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
anchors.right: parent.right
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Appearance.rounding.full
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const t = ZUtils.clamp((parent.height - 12) / 16, 0, 1);
const lerp = (a, b) => a + (b - a) * t;
return parent.height * (mouse.pressed ? lerp(3.5, 1.5) : lerp(3, 1.2));
}
implicitWidth: 4
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: root.wavy ? waveComp : lineComp
}
MaterialIcon {
id: inset
readonly property bool attached: root.width ? (root.width - handle.implicitWidth - handle.anchors.leftMargin) * root.pos < inset.paintedWidth + Appearance.spacing.extraSmall * 2 : false
property real dockT: attached ? 1 : 0
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
color: root.insetColor bottomRightRadius: root.implicitHeight / 6
font.pixelSize: Appearance.font.size.smaller * 2 color: root.color
text: root.insetIcon implicitWidth: root.handle.x - root.implicitHeight / 2
verticalAlignment: Text.AlignVCenter radius: Appearance.rounding.full
visible: !root.wavy && root.insetIcon !== "" topRightRadius: root.implicitHeight / 6
x: Appearance.spacing.extraSmall + dockT * (handle.x + Appearance.spacing.small)
Behavior on dockT {
Anim {
} }
}
}
Component {
id: lineComp
CustomRect { CustomRect {
bottomRightRadius: Appearance.rounding.extraSmall / 2 anchors.bottom: parent.bottom
color: root.fgColor anchors.right: parent.right
implicitHeight: root.height anchors.top: parent.top
implicitWidth: root.filledWidth bottomLeftRadius: root.implicitHeight / 6
radius: root.radius color: DynamicColors.tPalette.m3surfaceContainer
topRightRadius: Appearance.rounding.extraSmall / 2 implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
radius: Appearance.rounding.full
topLeftRadius: root.implicitHeight / 6
} }
} }
handle: CustomRect {
Component { anchors.verticalCenter: parent.verticalCenter
id: waveComp color: root.color
implicitHeight: 15
WavyLine { implicitWidth: 5
color: root.fgColor radius: Appearance.rounding.full
frequency: root.waveFrequency x: root.visualPosition * root.availableWidth - implicitWidth / 2
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
Behavior on filledWidth {
id: widthBehavior
Anim {
}
}
Component.onCompleted: {
filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos);
}
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea { MouseArea {
id: mouse acceptedButtons: Qt.NoButton
anchors.fill: parent
property real dragMovement cursorShape: Qt.PointingHandCursor
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
} }
} }
} }
+133 -96
View File
@@ -1,132 +1,169 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Layouts
import qs.Config import qs.Config
DoubleSpinBox { RowLayout {
id: root id: root
property int cLayer: 1 property string displayText: root.value.toString()
property int repeatDecay: 50 property bool isEditing: false
property int repeatRate: 400 property real max: Infinity
property real min: -Infinity
property alias repeatRate: timer.interval
property real step: 1
property real value
function decrease(): void { signal valueModified(value: real)
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 spacing: Appearance.spacing.small
contentItem: TextFieldBase { onValueChanged: {
horizontalAlignment: TextField.AlignHCenter if (!root.isEditing) {
implicitWidth: 65 root.displayText = root.value.toString();
}
}
CustomTextField {
id: textField
implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.larger leftPadding: Appearance.padding.normal
readOnly: !root.editable padding: Appearance.padding.small
rightPadding: Appearance.padding.larger rightPadding: Appearance.padding.normal
text: root.textFromValue(root.value, root.locale) text: root.isEditing ? text : root.displayText
validator: root.validator
background: CustomRect { background: CustomRect {
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.extraSmall 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
} }
down.indicator: IconButton {
id: downButton
bottomRightRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall onAccepted: {
color: enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor const numValue = parseFloat(text);
disabledColor: Qt.alpha(DynamicColors.palette.m3surfaceContainerHighest, 0.4) if (!isNaN(numValue)) {
icon: "remove" const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
isRound: true root.value = clampedValue;
label.anchors.horizontalCenterOffset: pressed ? 0 : 2 root.displayText = clampedValue.toString();
padding: Appearance.padding.extraSmall root.valueModified(clampedValue);
topRightRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall } else {
type: IconButton.Text 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;
}
}
Behavior on bottomRightRadius { CustomRect {
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 id: upButton
anchors.right: parent.right color: DynamicColors.palette.m3primary
bottomLeftRadius: pressed ? Appearance.rounding.small : Appearance.rounding.extraSmall implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
color: enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) : disabledColor implicitWidth: implicitHeight
disabledColor: Qt.alpha(DynamicColors.palette.m3surfaceContainerHighest, 0.4) radius: Appearance.rounding.full
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 bottomLeftRadius { StateLayer {
Anim { id: upState
type: Anim.DefaultEffects
function onClicked(): void {
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);
}
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start()
onReleased: timer.stop()
}
MaterialIcon {
id: upIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
text: "keyboard_arrow_up"
} }
} }
Behavior on label.anchors.horizontalCenterOffset {
Anim { CustomRect {
type: Anim.DefaultEffects color: DynamicColors.palette.m3primary
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
StateLayer {
id: downState
function onClicked(): void {
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);
} }
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start()
onReleased: timer.stop()
} }
Behavior on topLeftRadius {
Anim { MaterialIcon {
type: Anim.DefaultEffects id: downIcon
}
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
text: "keyboard_arrow_down"
} }
} }
Timer { Timer {
id: timer id: timer
interval: root.repeatRate interval: 100
repeat: true repeat: true
running: upButton.pressed || downButton.pressed
triggeredOnStart: true triggeredOnStart: true
onRunningChanged: {
if (!running)
interval = root.repeatRate;
}
onTriggered: { onTriggered: {
if (upButton.pressed) if (upState.pressed)
root.increase(); upState.onClicked();
else if (downButton.pressed) else if (downState.pressed)
root.decrease(); downState.onClicked();
if (interval > root.repeatDecay)
interval -= root.repeatDecay;
} }
} }
} }
+64 -30
View File
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Config import qs.Config
import qs.Helpers
Row { Row {
id: root id: root
@@ -12,44 +13,65 @@ Row {
property alias active: menu.active property alias active: menu.active
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property bool disabled
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1) property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38) property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded property alias expanded: menu.expanded
property string fallbackIcon property string fallbackIcon
property string fallbackText property string fallbackText
property real horizontalPadding: Appearance.padding.larger property real horizontalPadding: Appearance.padding.normal
readonly property alias iconLabel: iconLabel property alias iconLabel: iconLabel
readonly property alias label: label property alias label: label
readonly property alias menu: menu property alias menu: menu
property alias menuItems: menu.items property alias menuItems: menu.items
property bool menuOnTop property bool menuOnTop
property real minLeftWidth property alias stateLayer: stateLayer
readonly property alias stateLayer: stateLayer
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.small property real verticalPadding: Appearance.padding.smaller
spacing: Math.floor(Appearance.spacing.extraSmall) function closeDropdown(): void {
SettingsDropdowns.close(menu);
}
function openDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.open(menu, root);
}
function toggleDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.toggle(menu, root);
}
spacing: Math.floor(Appearance.spacing.small / 2)
onExpandedChanged: {
if (!expanded)
SettingsDropdowns.forget(menu);
}
CustomRect { CustomRect {
bottomRightRadius: Appearance.rounding.small / 2 bottomRightRadius: Appearance.rounding.small / 2
color: !root.enabled ? root.disabledColor : root.color color: root.disabled ? root.disabledColor : root.color
implicitHeight: expandBtn.implicitHeight implicitHeight: expandBtn.implicitHeight
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2) implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2 topRightRadius: Appearance.rounding.small / 2
StateLayer { StateLayer {
id: stateLayer id: stateLayer
bottomRightRadius: parent.bottomRightRadius function onClicked(): void {
color: root.textColor root.active?.clicked();
enabled: root.enabled }
topRightRadius: parent.topRightRadius
onClicked: root.active?.clicked() color: root.textColor
disabled: root.disabled
rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius
} }
RowLayout { RowLayout {
@@ -64,7 +86,7 @@ Row {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
animate: true animate: true
color: !root.enabled ? root.disabledTextColor : root.textColor color: root.disabled ? root.disabledTextColor : root.textColor
fill: 1 fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon text: root.active?.activeIcon ?? root.fallbackIcon
} }
@@ -76,12 +98,12 @@ Row {
Layout.preferredWidth: implicitWidth Layout.preferredWidth: implicitWidth
animate: true animate: true
clip: true clip: true
color: !root.enabled ? root.disabledTextColor : root.textColor color: root.disabled ? root.disabledTextColor : root.textColor
text: root.active?.activeText ?? root.fallbackText text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth { Behavior on Layout.preferredWidth {
Anim { Anim {
type: Anim.Emphasized easing.bezierCurve: Appearance.anim.curves.emphasized
} }
} }
} }
@@ -94,7 +116,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2 property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad bottomLeftRadius: rad
color: !root.enabled ? root.disabledColor : root.color color: root.disabled ? root.disabledColor : root.color
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2 implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -108,12 +130,14 @@ Row {
StateLayer { StateLayer {
id: expandStateLayer id: expandStateLayer
function onClicked(): void {
root.toggleDropdown();
}
color: root.textColor color: root.textColor
enabled: root.enabled disabled: root.disabled
rect.bottomLeftRadius: parent.bottomLeftRadius rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
} }
MaterialIcon { MaterialIcon {
@@ -121,7 +145,7 @@ Row {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4) anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: !root.enabled ? root.disabledTextColor : root.textColor color: root.disabled ? root.disabledTextColor : root.textColor
rotation: root.expanded ? 180 : 0 rotation: root.expanded ? 180 : 0
text: "expand_more" text: "expand_more"
@@ -134,14 +158,24 @@ Row {
} }
} }
} }
}
Menu { Menu {
id: menu id: menu
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom anchors.bottomMargin: Appearance.spacing.small
attachTo: expandBtn anchors.right: parent.right
marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1) anchors.top: parent.bottom
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top anchors.topMargin: Appearance.spacing.small
states: State {
when: root.menuOnTop
AnchorChanges {
anchors.bottom: expandBtn.top
anchors.top: undefined
target: menu
}
}
}
} }
} }
+6 -3
View File
@@ -9,6 +9,7 @@ Item {
property alias active: splitButton.active property alias active: splitButton.active
property alias buttonAlias: splitButton property alias buttonAlias: splitButton
property bool enabled: true
property alias expanded: splitButton.expanded property alias expanded: splitButton.expanded
property int expandedZ: 100 property int expandedZ: 100
required property string label required property string label
@@ -24,7 +25,7 @@ Item {
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2 implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0 opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8 scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1 z: root.expanded ? expandedZ : -1
Behavior on opacity { Behavior on opacity {
Anim { Anim {
@@ -49,6 +50,7 @@ Item {
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: root.label text: root.label
z: root.expanded ? root.expandedZ : -1
} }
CustomSplitButton { CustomSplitButton {
@@ -56,13 +58,14 @@ Item {
enabled: root.enabled enabled: root.enabled
type: CustomSplitButton.Filled type: CustomSplitButton.Filled
z: 2 z: root.expanded ? root.expandedZ : -1
menu.onItemSelected: item => { menu.onItemSelected: item => {
root.selected(item); root.selected(item);
splitButton.closeDropdown();
} }
stateLayer.onClicked: { stateLayer.onClicked: {
splitButton.expanded = !splitButton.expanded; splitButton.toggleDropdown();
} }
} }
} }
+16 -18
View File
@@ -1,6 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Shapes
import QtQuick.Templates import QtQuick.Templates
import QtQuick.Shapes
import qs.Config import qs.Config
Switch { Switch {
@@ -12,41 +12,38 @@ Switch {
implicitWidth: implicitIndicatorWidth implicitWidth: implicitIndicatorWidth
indicator: CustomRect { indicator: CustomRect {
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2 implicitHeight: 13 + 7 * 2
implicitWidth: implicitHeight * 1.7 implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full radius: Appearance.rounding.full
CustomRect { CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1) color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall implicitHeight: parent.implicitHeight - 10
implicitWidth: nonAnimWidth implicitWidth: nonAnimWidth
radius: Appearance.rounding.full radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2 x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
Behavior on implicitWidth { Behavior on implicitWidth {
Anim { Anim {
type: Anim.FastSpatial
} }
} }
Behavior on x { Behavior on x {
Anim { Anim {
type: Anim.FastSpatial
} }
} }
CustomRect { CustomRect {
anchors.fill: parent anchors.fill: parent
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0 opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius radius: parent.radius
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects
} }
} }
} }
@@ -66,14 +63,14 @@ Switch {
} }
property point end2: { property point end2: {
if (root.pressed) if (root.pressed)
return Qt.point(width * 0.8, height / 2); return Qt.point(width, height / 2);
if (root.checked) if (root.checked)
return Qt.point(width * 0.85, height * 0.2); return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15); return Qt.point(width * 0.85, height * 0.15);
} }
property point start1: { property point start1: {
if (root.pressed) if (root.pressed)
return Qt.point(width * 0.2, height / 2); return Qt.point(width * 0.1, height / 2);
if (root.checked) if (root.checked)
return Qt.point(width * 0.15, height / 2); return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15); return Qt.point(width * 0.15, height * 0.15);
@@ -91,7 +88,7 @@ Switch {
anchors.centerIn: parent anchors.centerIn: parent
asynchronous: true asynchronous: true
height: parent.implicitHeight - Appearance.padding.larger height: parent.implicitHeight - Appearance.padding.small * 2
preferredRendererType: Shape.CurveRenderer preferredRendererType: Shape.CurveRenderer
width: height width: height
@@ -113,11 +110,11 @@ Switch {
} }
ShapePath { ShapePath {
capStyle: ShapePath.RoundCap capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent" fillColor: "transparent"
startX: icon.start1.x startX: icon.start1.x
startY: icon.start1.y startY: icon.start1.y
strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15 strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor { Behavior on strokeColor {
@@ -151,7 +148,8 @@ Switch {
} }
component PropAnim: PropertyAnimation { component PropAnim: PropertyAnimation {
duration: Appearance.anim.durations.expressiveFastSpatial duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial easing.bezierCurve: MaterialEasing.expressiveEffects
easing.type: Easing.BezierSpline
} }
} }
+6 -7
View File
@@ -27,25 +27,24 @@ Text {
enabled: root.animate enabled: root.animate
SequentialAnimation { SequentialAnimation {
TAnim { Anim {
target: root easing.bezierCurve: MaterialEasing.standardAccel
to: root.animateFrom to: root.animateFrom
type: Anim.FastEffects
} }
PropertyAction { PropertyAction {
} }
TAnim { Anim {
target: root easing.bezierCurve: MaterialEasing.standardDecel
to: root.animateTo to: root.animateTo
type: Anim.DefaultEffects
} }
} }
} }
component TAnim: Anim { component Anim: NumberAnimation {
duration: root.animateDuration / 2 duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : "" properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : "" property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root target: root
+51 -271
View File
@@ -1,295 +1,75 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Controls
import qs.Config import qs.Config
TextFieldBase { TextField {
id: root id: root
enum TextFieldType { background: null
Outlined, color: DynamicColors.palette.m3onSurface
Filled 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) Behavior on color {
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
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
}
}
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
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
}
}
}
Loader {
id: leadingIconLoader
active: root.leadingIcon
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: root.leadingIcon
}
}
Loader {
id: trailingIconLoader
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
text: root.trailingIcon
}
}
}
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 { CAnim {
} }
} }
Behavior on strokeWidth { cursorDelegate: CustomRect {
id: cursor
property bool disableBlink
color: DynamicColors.palette.m3primary
implicitWidth: 2
radius: Appearance.rounding.normal
Behavior on opacity {
Anim { Anim {
duration: Appearance.anim.durations.small
} }
} }
PathLine { Connections {
x: bg.width - root.clampedRadius function onCursorPositionChanged(): void {
} if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
PathArc { cursor.disableBlink = true;
radiusX: root.clampedRadius enableBlink.restart();
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 { target: root
id: filledComp }
CustomRect { Timer {
color: root.activeFocus ? DynamicColors.tPalette.m3surfaceContainerHighest : DynamicColors.tPalette.m3surfaceContainerHigh id: enableBlink
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
CustomRect { interval: 100
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 { onTriggered: cursor.disableBlink = false
Anim { }
}
} Timer {
} interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
Behavior on placeholderTextColor {
CAnim {
} }
} }
} }
+1 -2
View File
@@ -1,6 +1,6 @@
import qs.Config
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import qs.Config
RectangularShadow { RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level] property real dp: [0, 1, 3, 6, 8, 12][level]
@@ -13,7 +13,6 @@ RectangularShadow {
Behavior on dp { Behavior on dp {
Anim { Anim {
type: Anim.SlowEffects
} }
} }
} }
-79
View File
@@ -1,79 +0,0 @@
import QtQuick
import Quickshell
import qs.Config
Image {
id: root
property int fadeInAnim: Anim.DefaultEffects
property int fadeInLargeAnim: Anim.StandardLarge
property int fadeOutAnim: Anim.FastEffects
property bool fadingOut
property bool hadPrevious
property bool preventInit
function maybeStartInAnim(): void {
if (!preventInit && !opacityInAnim.running && status === Image.Ready) {
opacityInAnim.type = hadPrevious ? fadeInAnim : fadeInLargeAnim;
opacityInAnim.start();
}
}
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: 0
retainWhileLoading: true
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Anim on opacity {
id: opacityInAnim
running: false
to: 1
}
Behavior on source {
SequentialAnimation {
ScriptAction {
script: opacityInAnim.stop()
}
PropertyAction {
property: "fadingOut"
target: root
value: true
}
Anim {
property: "opacity"
target: root
to: 0
type: root.fadeOutAnim
}
PropertyAction {
property: "fadingOut"
target: root
value: false
}
PropertyAction {
property: "hadPrevious"
target: root
value: root.source
}
PropertyAction {
}
ScriptAction {
script: root.maybeStartInAnim()
}
}
}
onPreventInitChanged: maybeStartInAnim()
onStatusChanged: maybeStartInAnim()
}
+56 -23
View File
@@ -1,45 +1,78 @@
import QtQuick
import qs.Config import qs.Config
import QtQuick
ButtonBase { CustomRect {
id: root id: root
property alias icon: label.text enum Type {
readonly property alias label: label Filled,
Tonal,
activeColor: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary Text
activeOnColor: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
implicitHeight: {
const h = label.implicitHeight + padding * 2;
if (h % 2 !== 0)
return h + 1;
return h;
} }
implicitWidth: implicitHeight
inactiveColor: { property color activeColour: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
if (!isToggle && type === IconButton.Filled) property color activeOnColour: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
property bool checked
property bool disabled
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property alias font: label.font
property alias icon: label.text
property color inactiveColour: {
if (!toggle && type === IconButton.Filled)
return DynamicColors.palette.m3primary; return DynamicColors.palette.m3primary;
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer; return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
} }
inactiveOnColor: { property color inactiveOnColour: {
if (!isToggle && type === IconButton.Filled) if (!toggle && type === IconButton.Filled)
return DynamicColors.palette.m3onPrimary; return DynamicColors.palette.m3onPrimary;
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant; return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
} }
padding: type === IconButton.Text ? Appearance.padding.extraSmall / 2 : Appearance.padding.small property bool internalChecked
property alias label: label
property real padding: type === IconButton.Text ? 10 / 2 : 7
property alias radiusAnim: radiusAnim
property alias stateLayer: stateLayer
property bool toggle
property int type: IconButton.Filled
signal clicked
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale
Behavior on radius {
Anim {
id: radiusAnim
}
}
onCheckedChanged: internalChecked = checked
StateLayer {
id: stateLayer
function onClicked(): void {
if (root.toggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
}
MaterialIcon { MaterialIcon {
id: label id: label
anchors.centerIn: parent anchors.centerIn: parent
anchors.verticalCenterOffset: 1 color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
color: root.onColor fill: !root.toggle || root.internalChecked ? 1 : 0
fill: !root.isToggle || root.internalChecked ? 1 : 0
font.pointSize: root.font.pointSize
Behavior on fill { Behavior on fill {
Anim { Anim {
type: Anim.DefaultEffects
} }
} }
} }
-71
View File
@@ -1,71 +0,0 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
ButtonBase {
id: root
property alias icon: iconLabel.text
readonly property alias iconLabel: iconLabel
readonly property alias label: label
property alias text: label.text
activeColor: type === TextButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: {
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondary;
}
horizontalPadding: Appearance.padding.larger
implicitHeight: row.implicitHeight + verticalPadding * 2
implicitWidth: row.implicitWidth + horizontalPadding * 2
inactiveColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3onPrimary;
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer;
}
verticalPadding: Appearance.padding.small
RowLayout {
id: row
anchors.centerIn: parent
spacing: Appearance.spacing.small
MaterialIcon {
id: iconLabel
Layout.alignment: Qt.AlignVCenter
color: root.onColor
fill: root.internalChecked ? 1 : 0
font: {
const f = Qt.font(root.font);
f.pointSize = Math.round(root.font.pointSize * 1.2);
f.family = "Material Symbols Rounded";
return f;
}
Behavior on fill {
Anim {
type: Anim.DefaultEffects
}
}
}
CustomText {
id: label
Layout.alignment: Qt.AlignVCenter
Layout.topMargin: -1
color: root.onColor
font: root.font
}
}
}
-309
View File
@@ -1,309 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Config
Item {
id: root
enum Variant {
Outlined,
Filled
}
readonly property color accent: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline))
readonly property real disabledOpacity: 0.38
readonly property string effectiveTrailingIcon: root.trailingIcon.length > 0 ? root.trailingIcon : (root.password ? (field.echoMode === TextInput.Password ? "visibility" : "visibility_off") : (root.isError ? "error" : ""))
property string errorText: ""
readonly property alias field: field
readonly property bool floating: focused || hasContent || prefixText.length > 0
readonly property bool focused: field.activeFocus
readonly property bool hasContent: field.text.length > 0
readonly property color iconColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant))
property int inputMethodHints: Qt.ImhNone
property bool isError: false
property string label: ""
readonly property color labelColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant))
property string leadingIcon: ""
property int maxLength: 0
property color notchColor: DynamicColors.palette.m3surface
readonly property bool outlined: variant === M3TextField.Variant.Outlined
property bool password: false
property string placeholder: ""
property string prefixText: ""
property string suffixText: ""
readonly property color supportColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurfaceVariant)
property string supportingText: ""
property alias text: field.text
property string trailingIcon: ""
readonly property bool trailingInteractive: root.password && root.trailingIcon.length === 0
property var validator: null
property int variant: M3TextField.Variant.Outlined
signal accepted
function forceFieldFocus(): void {
field.forceActiveFocus();
}
implicitHeight: 56 + (supportRow.visible ? supportRow.implicitHeight + Appearance.spacing.extraSmall : 0)
implicitWidth: 240
CustomRect {
id: filledBg
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
color: root.enabled ? (root.focused ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) : DynamicColors.palette.m3surfaceContainerHigh) : DynamicColors.palette.m3onSurface
implicitHeight: 56
opacity: root.enabled ? 1 : 0.04 // M3 filled disabled container @ 4%
topLeftRadius: Appearance.rounding.small
topRightRadius: Appearance.rounding.small
visible: !root.outlined
Behavior on color {
CAnim {
}
}
StateLayer {
anchors.fill: parent
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
enabled: root.enabled
topLeftRadius: Appearance.rounding.small
topRightRadius: Appearance.rounding.small
onClicked: field.forceActiveFocus()
}
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.accent
implicitHeight: root.focused || root.isError ? 2 : 1
opacity: root.enabled ? 1 : root.disabledOpacity
Behavior on color {
CAnim {
}
}
Behavior on implicitHeight {
Anim {
type: Anim.StandardSmall
}
}
}
}
CustomRect {
id: outline
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
border.color: root.accent
border.width: root.focused || root.isError ? 2 : 1
color: "transparent"
implicitHeight: 56
opacity: root.enabled ? 1 : root.disabledOpacity
radius: Appearance.rounding.small
visible: root.outlined
Behavior on border.color {
CAnim {
}
}
Behavior on border.width {
Anim {
type: Anim.StandardSmall
}
}
StateLayer {
anchors.fill: parent
anchors.margins: outline.border.width
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
enabled: root.enabled
radius: Appearance.rounding.small
onClicked: field.forceActiveFocus()
}
}
Rectangle {
color: root.notchColor
height: outline.border.width
opacity: root.enabled ? 1 : root.disabledOpacity
visible: root.outlined && root.floating && root.label.length > 0
width: labelText.width + Appearance.spacing.extraSmall
x: labelText.x - Appearance.spacing.extraSmall / 2
y: 0
}
CustomText {
id: labelText
color: root.labelColor
font.pointSize: root.floating ? Appearance.font.size.small : Appearance.font.size.medium
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.label
visible: root.label.length > 0
x: root.floating ? (Appearance.padding.larger + Appearance.spacing.extraSmall / 2) : (leading.visible ? leading.x + leading.width + Appearance.spacing.small : Appearance.padding.larger)
y: root.floating ? (root.outlined ? -height / 2 : Appearance.padding.extraSmall) : (56 - height) / 2
Behavior on color {
CAnim {
}
}
Behavior on x {
Anim {
type: Anim.StandardSmall
}
}
Behavior on y {
Anim {
type: Anim.StandardSmall
}
}
}
MaterialIcon {
id: leading
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: (56 - height) / 2
color: root.iconColor
font.pointSize: Appearance.font.size.medium
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.leadingIcon
visible: root.leadingIcon.length > 0
Behavior on color {
CAnim {
}
}
}
MaterialIcon {
id: trailing
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: (56 - height) / 2
color: root.isError ? DynamicColors.palette.m3error : (root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface)
font.pointSize: Appearance.font.size.medium
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.effectiveTrailingIcon
visible: root.effectiveTrailingIcon.length > 0
MouseArea {
anchors.fill: parent
anchors.margins: -Appearance.padding.small
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: root.trailingInteractive && root.enabled
onClicked: field.echoMode = field.echoMode === TextInput.Password ? TextInput.Normal : TextInput.Password
}
}
CustomText {
id: prefix
anchors.left: leading.visible ? leading.right : parent.left
anchors.leftMargin: leading.visible ? Appearance.spacing.small : Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface
font.pointSize: Appearance.font.size.medium
height: 56
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.prefixText
verticalAlignment: Text.AlignVCenter
visible: root.prefixText.length > 0 && root.floating
}
CustomText {
id: suffix
anchors.right: trailing.visible ? trailing.left : parent.right
anchors.rightMargin: trailing.visible ? Appearance.spacing.small : Appearance.padding.larger
anchors.top: parent.top
anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface
font.pointSize: Appearance.font.size.medium
height: 56
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.suffixText
verticalAlignment: Text.AlignVCenter
visible: root.suffixText.length > 0 && root.floating
}
CustomTextField {
id: field
anchors.left: prefix.visible ? prefix.right : (leading.visible ? leading.right : parent.left)
anchors.leftMargin: prefix.visible ? Appearance.spacing.extraSmall : (leading.visible ? Appearance.spacing.small : Appearance.padding.larger)
anchors.right: suffix.visible ? suffix.left : (trailing.visible ? trailing.left : parent.right)
anchors.rightMargin: suffix.visible ? Appearance.spacing.extraSmall : (trailing.visible ? Appearance.spacing.small : Appearance.padding.larger)
anchors.top: parent.top
color: DynamicColors.palette.m3onSurface
echoMode: root.password ? TextInput.Password : TextInput.Normal
enabled: root.enabled
font.pointSize: Appearance.font.size.medium
height: 56
inputMethodHints: root.inputMethodHints
maximumLength: root.maxLength > 0 ? root.maxLength : 32767
opacity: root.enabled ? 1 : root.disabledOpacity
placeholderText: root.focused ? root.placeholder : ""
topPadding: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
validator: root.validator
verticalAlignment: TextInput.AlignVCenter
onAccepted: root.accepted()
onTextEdited: if (root.isError)
root.isError = false
}
Item {
id: supportRow
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 56 + Appearance.spacing.extraSmall
implicitHeight: Math.max(supportText.implicitHeight, counter.implicitHeight)
visible: (root.isError && root.errorText.length > 0) || root.supportingText.length > 0 || root.maxLength > 0
CustomText {
id: supportText
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.right: counter.visible ? counter.left : parent.right
anchors.rightMargin: Appearance.spacing.small
color: root.supportColor
font.pointSize: Appearance.font.size.small
opacity: root.enabled ? 1 : root.disabledOpacity
text: root.isError && root.errorText.length > 0 ? root.errorText : root.supportingText
wrapMode: Text.WordWrap
}
CustomText {
id: counter
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.larger
color: root.supportColor
font.pointSize: Appearance.font.size.small
opacity: root.enabled ? 1 : root.disabledOpacity
text: `${root.text.length}/${root.maxLength}`
visible: root.maxLength > 0
}
}
}
+3 -14
View File
@@ -1,26 +1,15 @@
import qs.Config import qs.Config
CustomText { CustomText {
id: root property real fill
readonly property list<int> allowed: [20, 24, 40, 48]
property real fill: 0
property int grade: DynamicColors.light ? 0 : -25 property int grade: DynamicColors.light ? 0 : -25
readonly property int opsz: clampOpsz(optical)
property int optical: fontInfo.pixelSize
function clampOpsz(value): int {
return allowed.reduce((closest, current) => {
return Math.abs(current - value) < Math.abs(closest - value) ? current : closest;
});
}
font.family: "Material Symbols Rounded" font.family: "Material Symbols Rounded"
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
font.variableAxes: ({ font.variableAxes: ({
FILL: fill, FILL: fill.toFixed(1),
GRAD: grade, GRAD: grade,
opsz: opsz, opsz: fontInfo.pixelSize,
wght: fontInfo.weight wght: fontInfo.weight
}) })
} }
+36 -113
View File
@@ -2,124 +2,49 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import qs.Config import qs.Config
import qs.Drawers
MouseArea { Elevation {
id: root id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded property bool expanded
property list<MenuItem> items property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem) signal itemSelected(item: MenuItem)
anchors.fill: parent implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0
cursorShape: expanded ? Qt.ArrowCursor : undefined implicitWidth: Math.max(200, column.implicitWidth)
enabled: expanded level: 2
hoverEnabled: expanded opacity: root.expanded ? 1 : 0
layer.enabled: opacity < 1 radius: Appearance.rounding.normal
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on implicitHeight {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects duration: Appearance.anim.durations.expressiveDefaultSpatial
} }
} }
onClicked: expanded = false CustomClippingRect {
onExpandedChanged: {
const win = QsWindow.window;
const contentWin = win as Windows;
if (expanded) {
contentWin.menuRegion.x = menu.x;
contentWin.menuRegion.y = menu.y;
contentWin.menuRegion.width = menu.width;
contentWin.menuRegion.height = menu.height;
} else {
contentWin.menuRegion.x = 0;
contentWin.menuRegion.y = 0;
contentWin.menuRegion.width = 0;
contentWin.menuRegion.height = 0;
}
}
TransformWatcher {
id: watcher
a: root.parent
b: root.attachTo
}
Elevation {
id: menu
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainerLow color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius radius: parent.radius
ColumnLayout { ColumnLayout {
id: column id: column
anchors.fill: parent anchors.left: parent.left
anchors.margins: Appearance.padding.extraSmall anchors.right: parent.right
spacing: Appearance.spacing.extraSmall anchors.verticalCenter: parent.verticalCenter
spacing: 5
Repeater { Repeater {
id: repeater
model: root.items model: root.items
CustomRect { CustomRect {
@@ -130,56 +55,55 @@ MouseArea {
required property MenuItem modelData required property MenuItem modelData
Layout.fillWidth: true Layout.fillWidth: true
color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0) implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2 implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
Behavior on radius { CustomRect {
Anim { anchors.fill: parent
} anchors.leftMargin: Appearance.padding.small
} anchors.rightMargin: Appearance.padding.small
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
radius: Appearance.rounding.normal - Appearance.padding.small
StateLayer { StateLayer {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface function onClicked(): void {
enabled: root.expanded
onClicked: {
root.itemSelected(item.modelData); root.itemSelected(item.modelData);
root.active = item.modelData; root.active = item.modelData;
item.modelData.clicked();
root.expanded = false; root.expanded = false;
} }
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded
}
} }
RowLayout { RowLayout {
id: menuOptionRow id: menuOptionRow
anchors.fill: parent anchors.fill: parent
anchors.margins: Appearance.padding.larger anchors.margins: Appearance.padding.normal
spacing: Appearance.spacing.small spacing: Appearance.spacing.small
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon text: item.modelData.icon
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text text: item.modelData.text
} }
Loader { Loader {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0 active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active visible: active
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.trailingIcon text: item.modelData.trailingIcon
} }
} }
@@ -189,4 +113,3 @@ MouseArea {
} }
} }
} }
}
-94
View File
@@ -1,94 +0,0 @@
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
}
}
+68 -172
View File
@@ -1,199 +1,95 @@
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import qs.Config import qs.Config
import QtQuick
MouseArea { MouseArea {
id: root id: root
property alias bottomLeftRadius: base.bottomLeftRadius property color color: DynamicColors.palette.m3onSurface
property alias bottomRightRadius: base.bottomRightRadius property bool disabled
property real circleRadius property real radius: parent?.radius ?? 0
property alias color: base.color property alias rect: hoverLayer
readonly property real endRadius: {
const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
const d3 = distSq(0, height);
const d4 = distSq(width, height);
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualHoverOverride
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
property alias radius: base.radius
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse || manualHoverOverride ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
function clamp(r: real): real { function onClicked(): void {
return Math.max(0, Math.min(r, width / 2, height / 2));
}
function distSq(x: real, y: real): real {
return (pressX - x) ** 2 + (pressY - y) ** 2;
}
function press(x: real, y: real): void {
pressX = x;
pressY = y;
fadeAnim.complete();
circleRadius = 0;
circle.opacity = 0.1;
rippleAnim.restart();
endRadiusAtPress = endRadius;
} }
anchors.fill: parent anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled && !Visibilities.getForActive().isDrawing enabled: !disabled
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity { onClicked: event => !disabled && onClicked(event)
Anim { onPressed: event => {
type: Anim.DefaultEffects if (disabled)
} return;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
} }
onCircleRadiusChanged: { SequentialAnimation {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onManualPressOverrideChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onPressed: e => press(e.x, e.y)
onPressedChanged: {
if (!(pressed || manualPressOverride) && !rippleAnim.running && circle.opacity > 0)
fadeAnim.start();
}
Anim {
id: rippleAnim id: rippleAnim
alwaysRunToEnd: true property real radius
duration: Appearance.anim.durations.expressiveSlowEffects * 2 property real x
easing.bezierCurve: Appearance.anim.curves.standard property real y
property: "circleRadius"
target: root PropertyAction {
to: root.endRadius property: "x"
target: ripple
value: rippleAnim.x
}
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
} }
Anim { Anim {
id: fadeAnim easing.bezierCurve: MaterialEasing.standardDecel
from: 0
property: "opacity" properties: "implicitWidth,implicitHeight"
target: circle target: ripple
to: 0 to: rippleAnim.radius * 2
type: Anim.SlowEffects
} }
Anim {
property: "opacity"
target: ripple
to: 0
}
}
CustomClippingRect {
id: hoverLayer
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
CustomRect { CustomRect {
id: base id: ripple
anchors.fill: parent border.pixelAligned: false
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0 color: root.color
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
}
Shape {
id: circle
anchors.fill: parent
opacity: 0 opacity: 0
preferredRendererType: Shape.CurveRenderer radius: Appearance.rounding.full
ShapePath { transform: Translate {
fillColor: base.color x: -ripple.width / 2
startX: root.clamp(base.topLeftRadius) y: -ripple.height / 2
startY: 0
strokeColor: "transparent"
strokeWidth: 0
fillGradient: RadialGradient {
centerRadius: root.circleRadius
centerX: root.pressX
centerY: root.pressY
focalX: centerX
focalY: centerY
GradientStop {
color: Qt.alpha(base.color, 1)
position: 0
}
GradientStop {
color: Qt.alpha(base.color, 1)
position: ZUtils.clamp(1 - 0.2 * root.endRadius / root.circleRadius, 0.01, 0.99)
}
GradientStop {
color: Qt.alpha(base.color, ZUtils.clamp((root.circleRadius / root.endRadius - 0.9) / 0.1, 0, 1))
position: 1
}
}
PathLine {
x: root.width - root.clamp(base.topRightRadius)
y: 0
}
PathArc {
radiusX: root.clamp(base.topRightRadius)
radiusY: root.clamp(base.topRightRadius)
relativeX: root.clamp(base.topRightRadius)
relativeY: root.clamp(base.topRightRadius)
}
PathLine {
x: root.width
y: root.height - root.clamp(base.bottomRightRadius)
}
PathArc {
radiusX: root.clamp(base.bottomRightRadius)
radiusY: root.clamp(base.bottomRightRadius)
relativeX: -root.clamp(base.bottomRightRadius)
relativeY: root.clamp(base.bottomRightRadius)
}
PathLine {
x: root.clamp(base.bottomLeftRadius)
y: root.height
}
PathArc {
radiusX: root.clamp(base.bottomLeftRadius)
radiusY: root.clamp(base.bottomLeftRadius)
relativeX: -root.clamp(base.bottomLeftRadius)
relativeY: -root.clamp(base.bottomLeftRadius)
}
PathLine {
x: 0
y: root.clamp(base.topLeftRadius)
}
PathArc {
radiusX: root.clamp(base.topLeftRadius)
radiusY: root.clamp(base.topLeftRadius)
x: root.clamp(base.topLeftRadius)
y: 0
} }
} }
} }
-40
View File
@@ -1,40 +0,0 @@
import QtQuick
import qs.Config
ButtonBase {
id: root
readonly property alias label: label
property alias text: label.text
activeColor: type === TextButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: {
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondary;
}
horizontalPadding: Appearance.padding.normal
implicitHeight: label.implicitHeight + verticalPadding * 2
implicitWidth: label.implicitWidth + horizontalPadding * 2
inactiveColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3onPrimary;
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer;
}
verticalPadding: Appearance.padding.small
CustomText {
id: label
anchors.centerIn: parent
color: root.onColor
font: root.font
}
}
-88
View File
@@ -1,88 +0,0 @@
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
}
}
}
-71
View File
@@ -1,71 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Effects
CustomFlickable {
id: root
property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property real fadeAmount: 0.1
property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
function fadeShouldBeActive(isStart: bool): bool {
// When content is smaller than flickable size, hide fade when rebound starts
if (contentHeight + topMargin + bottomMargin < height && rebound.running && ((isStart ? verticalOvershoot > 0 : verticalOvershoot < 0)))
return false;
if (isStart)
return visibleArea.yPosition > 0;
return visibleArea.yPosition + visibleArea.heightRatio < 1;
}
flickableDirection: Flickable.VerticalFlick
layer.enabled: true
Behavior on bottomFadeOpacity {
Anim {
type: Anim.SlowEffects
}
}
layer.effect: Mask {
maskSource: mask
Rectangle {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
gradient: Gradient {
orientation: Gradient.Vertical
GradientStop {
color: Qt.rgba(0, 0, 0, root.topFadeOpacity)
position: 0
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: root.fadeAmount
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: 1 - root.fadeAmount
}
GradientStop {
color: Qt.rgba(0, 0, 0, root.bottomFadeOpacity)
position: 1
}
}
}
}
Behavior on topFadeOpacity {
Anim {
type: Anim.SlowEffects
}
}
}
-72
View File
@@ -1,72 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Effects
CustomListView {
id: root
property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property real fadeAmount: 0.1
property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
function fadeShouldBeActive(isStart: bool): bool {
// When content is smaller than flickable size, hide fade when rebound starts
if (contentHeight + topMargin + bottomMargin < height && rebound.running && ((isStart ? verticalOvershoot > 0 : verticalOvershoot < 0)))
return false;
if (isStart)
return visibleArea.yPosition > 0;
return visibleArea.yPosition + visibleArea.heightRatio < 1;
}
flickableDirection: Flickable.VerticalFlick
layer.enabled: true
orientation: ListView.Vertical
Behavior on bottomFadeOpacity {
Anim {
type: Anim.SlowEffects
}
}
layer.effect: Mask {
maskSource: mask
Rectangle {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
gradient: Gradient {
orientation: Gradient.Vertical
GradientStop {
color: Qt.rgba(0, 0, 0, root.topFadeOpacity)
position: 0
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: root.fadeAmount
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: 1 - root.fadeAmount
}
GradientStop {
color: Qt.rgba(0, 0, 0, root.bottomFadeOpacity)
position: 1
}
}
}
}
Behavior on topFadeOpacity {
Anim {
type: Anim.SlowEffects
}
}
}
+2
View File
@@ -7,6 +7,8 @@ Singleton {
readonly property AppearanceConf.Deform deform: Config.appearance.deform readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
+4 -18
View File
@@ -28,13 +28,9 @@ JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1] property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1] property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1] property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1] property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1] property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1] property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1] property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1] property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
@@ -42,9 +38,7 @@ JsonObject {
component AnimDurations: JsonObject { component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale property int extraLarge: 1000 * scale
property int large: 600 * scale property int large: 600 * scale
property int normal: 400 * scale property int normal: 400 * scale
@@ -63,8 +57,7 @@ JsonObject {
component FontSize: JsonObject { component FontSize: JsonObject {
property int extraLarge: 28 * scale property int extraLarge: 28 * scale
property int large: 18 * scale property int large: 18 * scale
property int larger: 16 * scale property int larger: 15 * scale
property int medium: 14 * scale
property int normal: 13 * scale property int normal: 13 * scale
property real scale: 1 property real scale: 1
property int small: 11 * scale property int small: 11 * scale
@@ -77,30 +70,23 @@ JsonObject {
} }
} }
component Padding: JsonObject { component Padding: JsonObject {
property int extraLarge: 28 * scale property int large: 15 * scale
property int extraLargeIncreased: 32 * scale property int larger: 13 * scale
property int extraSmall: 4 * scale property int normal: 9 * scale
property int large: 16 * scale
property int largeIncreased: 20 * scale
property int larger: 12 * scale
property int normal: 8 * scale
property real scale: 1 property real scale: 1
property int small: 5 * scale property int small: 5 * scale
property int smaller: 7 * scale property int smaller: 7 * scale
property int smallest: 2 * scale property int smallest: 2 * scale
} }
component Rounding: JsonObject { component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale property int full: 1000 * scale
property int large: 24 * scale property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale property int normal: 18 * scale
property real scale: 1 property real scale: 1
property int small: 12 * scale property int small: 12 * scale
property int smallest: 8 * scale property int smallest: 8 * scale
} }
component Spacing: JsonObject { component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale property int large: 20 * scale
property int larger: 16 * scale property int larger: 16 * scale
property int normal: 12 * scale property int normal: 12 * scale
-11
View File
@@ -59,8 +59,6 @@ JsonObject {
} }
property int rounding: 8 property int rounding: 8
property int smoothing: 32 property int smoothing: 32
property Tray tray: Tray {
}
component Popouts: JsonObject { component Popouts: JsonObject {
property bool activeWindow: true property bool activeWindow: true
@@ -71,13 +69,4 @@ JsonObject {
property bool tray: true property bool tray: true
property bool upower: true property bool upower: true
} }
component Tray: JsonObject {
property bool showAudio: true
property bool showBluetooth: false
property bool showMicrophone: true
property bool showNetwork: false
property bool showPower: true
property bool showWifi: false
property int trayIconSize: 24
}
} }
-15
View File
@@ -1,15 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int maxEntriesShown: 10
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int itemHeight: 60
property int minPreviewWidth: 200
property int previewWidth: 500
property int width: 500
}
}
+347 -51
View File
@@ -12,59 +12,23 @@ Singleton {
property alias appearance: adapter.appearance property alias appearance: adapter.appearance
property alias background: adapter.background property alias background: adapter.background
property alias bar: adapter.bar property alias barConfig: adapter.barConfig
property alias clipboard: adapter.clipboard
property alias colors: adapter.colors property alias colors: adapter.colors
property alias dashboard: adapter.dashboard property alias dashboard: adapter.dashboard
property alias dock: adapter.dock property alias dock: adapter.dock
property alias general: adapter.general property alias general: adapter.general
property alias launcher: adapter.launcher property alias launcher: adapter.launcher
property alias lock: adapter.lock property alias lock: adapter.lock
property bool migratingLegacyBar: false
property alias notifs: adapter.notifs property alias notifs: adapter.notifs
property alias osd: adapter.osd property alias osd: adapter.osd
property alias overview: adapter.overview property alias overview: adapter.overview
property alias plugins: adapter.plugins
property bool recentlySaved: false property bool recentlySaved: false
property alias screenshot: adapter.screenshot property alias screenshot: adapter.screenshot
property alias services: adapter.services property alias services: adapter.services
property alias sidebar: adapter.sidebar property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities property alias utilities: adapter.utilities
function applyLegacyBarConfig(legacy): void {
const tray = legacy.tray ?? {};
const popouts = legacy.popouts ?? {};
bar.autoHide = pick(legacy.autoHide, bar.autoHide);
bar.hideWhenNotif = pick(legacy.hideWhenNotif, bar.hideWhenNotif);
bar.rounding = pick(legacy.rounding, bar.rounding);
bar.border = pick(legacy.border, bar.border);
bar.smoothing = pick(legacy.smoothing, bar.smoothing);
bar.height = pick(legacy.height, bar.height);
bar.tray.trayIconSize = pick(tray.trayIconSize, bar.tray.trayIconSize);
bar.popouts.tray = pick(popouts.tray, bar.popouts.tray);
bar.popouts.audio = pick(popouts.audio, bar.popouts.audio);
bar.popouts.activeWindow = pick(popouts.activeWindow, bar.popouts.activeWindow);
bar.popouts.resources = pick(popouts.resources, bar.popouts.resources);
bar.popouts.clock = pick(popouts.clock, bar.popouts.clock);
bar.popouts.network = pick(popouts.network, bar.popouts.network);
bar.popouts.upower = pick(popouts.upower, bar.popouts.upower);
bar.entries = pick(legacy.entries, bar.entries);
}
function migrateLegacyBarConfig(raw): bool {
if (raw.bar !== undefined || raw.barConfig === undefined)
return false;
migratingLegacyBar = true;
applyLegacyBarConfig(raw.barConfig);
migratingLegacyBar = false;
return true;
}
function pick(value, fallback) {
return value === undefined ? fallback : value;
}
function save(): void { function save(): void {
saveTimer.restart(); saveTimer.restart();
recentlySaved = true; recentlySaved = true;
@@ -75,6 +39,333 @@ Singleton {
saveTimer.restart(); saveTimer.restart();
} }
function serializeAppearance(): var {
return {
rounding: {
scale: appearance.rounding.scale
},
spacing: {
scale: appearance.spacing.scale
},
padding: {
scale: appearance.padding.scale
},
deform: {
scale: appearance.deform.scale
},
font: {
family: {
sans: appearance.font.family.sans,
mono: appearance.font.family.mono,
material: appearance.font.family.material,
clock: appearance.font.family.clock
},
size: {
scale: appearance.font.size.scale
}
},
anim: {
mediaGifSpeedAdjustment: appearance.anim.mediaGifSpeedAdjustment,
sessionGifSpeed: appearance.anim.sessionGifSpeed,
durations: {
scale: appearance.anim.durations.scale
}
},
transparency: {
enabled: appearance.transparency.enabled,
base: appearance.transparency.base,
layers: appearance.transparency.layers
}
};
}
function serializeBackground(): var {
return {
wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled,
alignX: background.alignX,
sourceClipX: background.sourceClipX,
sourceClipY: background.sourceClipY,
sourceClipW: background.sourceClipW,
sourceClipH: background.sourceClipH,
alignY: background.alignY,
zoom: background.zoom
};
}
function serializeBar(): var {
return {
autoHide: barConfig.autoHide,
hideWhenNotif: barConfig.hideWhenNotif,
rounding: barConfig.rounding,
border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height,
popouts: {
tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio,
activeWindow: barConfig.popouts.activeWindow,
resources: barConfig.popouts.resources,
clock: barConfig.popouts.clock,
network: barConfig.popouts.network,
upower: barConfig.popouts.upower
},
entries: barConfig.entries
};
}
function serializeColors(): var {
return {
schemeType: colors.schemeType,
presets: {
name: colors.presets.name,
variant: colors.presets.variant,
accent: colors.presets.accent
}
};
}
function serializeConfig(): var {
return {
barConfig: serializeBar(),
lock: serializeLock(),
general: serializeGeneral(),
services: serializeServices(),
notifs: serializeNotifs(),
sidebar: serializeSidebar(),
utilities: serializeUtilities(),
dashboard: serializeDashboard(),
appearance: serializeAppearance(),
osd: serializeOsd(),
background: serializeBackground(),
launcher: serializeLauncher(),
colors: serializeColors(),
dock: serializeDock(),
screenshot: serializeScreenshot(),
plugins: serializePlugins()
};
}
function serializeDashboard(): var {
return {
enabled: dashboard.enabled,
mediaUpdateInterval: dashboard.mediaUpdateInterval,
resourceUpdateInterval: dashboard.resourceUpdateInterval,
dragThreshold: dashboard.dragThreshold,
performance: {
showBattery: dashboard.performance.showBattery,
showGpu: dashboard.performance.showGpu,
showCpu: dashboard.performance.showCpu,
showMemory: dashboard.performance.showMemory,
showStorage: dashboard.performance.showStorage,
showNetwork: dashboard.performance.showNetwork
},
sizes: {
tabIndicatorHeight: dashboard.sizes.tabIndicatorHeight,
tabIndicatorSpacing: dashboard.sizes.tabIndicatorSpacing,
infoWidth: dashboard.sizes.infoWidth,
infoIconSize: dashboard.sizes.infoIconSize,
dateTimeWidth: dashboard.sizes.dateTimeWidth,
mediaWidth: dashboard.sizes.mediaWidth,
mediaProgressSweep: dashboard.sizes.mediaProgressSweep,
mediaProgressThickness: dashboard.sizes.mediaProgressThickness,
resourceProgessThickness: dashboard.sizes.resourceProgessThickness,
weatherWidth: dashboard.sizes.weatherWidth,
mediaCoverArtSize: dashboard.sizes.mediaCoverArtSize,
mediaVisualiserSize: dashboard.sizes.mediaVisualiserSize,
resourceSize: dashboard.sizes.resourceSize
}
};
}
function serializeDock(): var {
return {
enable: dock.enable,
height: dock.height,
hoverToReveal: dock.hoverToReveal,
pinnedApps: dock.pinnedApps,
pinnedOnStartup: dock.pinnedOnStartup,
ignoredAppRegexes: dock.ignoredAppRegexes
};
}
function serializeGeneral(): var {
return {
logo: general.logo,
wallpaperPath: general.wallpaperPath,
desktopIcons: general.desktopIcons,
dateFormat: general.dateFormat,
color: {
mode: general.color.mode,
smart: general.color.smart,
scheduleDark: general.color.scheduleDark,
scheduleHyprsunset: general.color.scheduleHyprsunset,
scheduleHyprsunsetStart: general.color.scheduleHyprsunsetStart,
hyprsunsetTemp: general.color.hyprsunsetTemp,
scheduleHyprsunsetEnd: general.color.scheduleHyprsunsetEnd,
schemeGeneration: general.color.schemeGeneration,
scheduleDarkStart: general.color.scheduleDarkStart,
scheduleDarkEnd: general.color.scheduleDarkEnd,
neovimColors: general.color.neovimColors
},
apps: {
terminal: general.apps.terminal,
audio: general.apps.audio,
playback: general.apps.playback,
explorer: general.apps.explorer
},
idle: {
timeouts: general.idle.timeouts
}
};
}
function serializeLauncher(): var {
return {
maxAppsShown: launcher.maxAppsShown,
maxWallpapers: launcher.maxWallpapers,
uwsm: launcher.uwsm,
actionPrefix: launcher.actionPrefix,
specialPrefix: launcher.specialPrefix,
useFuzzy: {
apps: launcher.useFuzzy.apps,
actions: launcher.useFuzzy.actions,
schemes: launcher.useFuzzy.schemes,
variants: launcher.useFuzzy.variants,
wallpapers: launcher.useFuzzy.wallpapers
},
sizes: {
itemWidth: launcher.sizes.itemWidth,
itemHeight: launcher.sizes.itemHeight,
wallpaperWidth: launcher.sizes.wallpaperWidth,
wallpaperHeight: launcher.sizes.wallpaperHeight
},
actions: launcher.actions
};
}
function serializeLock(): var {
return {
recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint,
showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount,
sizes: {
heightMult: lock.sizes.heightMult,
ratio: lock.sizes.ratio,
centerWidth: lock.sizes.centerWidth
}
};
}
function serializeNotifs(): var {
return {
expire: notifs.expire,
defaultExpireTimeout: notifs.defaultExpireTimeout,
appNotifCooldown: notifs.appNotifCooldown,
clearThreshold: notifs.clearThreshold,
expandThreshold: notifs.expandThreshold,
actionOnClick: notifs.actionOnClick,
groupPreviewNum: notifs.groupPreviewNum,
sizes: {
width: notifs.sizes.width,
image: notifs.sizes.image,
badge: notifs.sizes.badge
}
};
}
function serializeOsd(): var {
return {
enabled: osd.enabled,
hideDelay: osd.hideDelay,
enableBrightness: osd.enableBrightness,
enableMicrophone: osd.enableMicrophone,
allMonBrightness: osd.allMonBrightness,
sizes: {
sliderWidth: osd.sizes.sliderWidth,
sliderHeight: osd.sizes.sliderHeight
}
};
}
function serializePlugins(): var {
return {
enabled: plugins.enabled,
entries: plugins.entries
};
}
function serializeScreenshot(): var {
return {
enable_pp: screenshot.enable_pp,
mode: screenshot.mode,
corner_radius: screenshot.corner_radius,
drop_shadow: screenshot.drop_shadow,
rounded_corners: screenshot.rounded_corners,
shadow_blur_radius: screenshot.shadow_blur_radius,
shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y
};
}
function serializeServices(): var {
return {
weatherLocation: services.weatherLocation,
updates: services.updates,
useFahrenheit: services.useFahrenheit,
ddcutilService: services.ddcutilService,
useTwelveHourClock: services.useTwelveHourClock,
gpuType: services.gpuType,
audioIncrement: services.audioIncrement,
brightnessIncrement: services.brightnessIncrement,
maxVolume: services.maxVolume,
defaultPlayer: services.defaultPlayer,
playerAliases: services.playerAliases,
visualizerBars: services.visualizerBars
};
}
function serializeSidebar(): var {
return {
enabled: sidebar.enabled,
sizes: {
width: sidebar.sizes.width
}
};
}
function serializeUtilities(): var {
return {
enabled: utilities.enabled,
maxToasts: utilities.maxToasts,
sizes: {
width: utilities.sizes.width,
toastWidth: utilities.sizes.toastWidth
},
toasts: {
configLoaded: utilities.toasts.configLoaded,
chargingChanged: utilities.toasts.chargingChanged,
gameModeChanged: utilities.toasts.gameModeChanged,
dndChanged: utilities.toasts.dndChanged,
audioOutputChanged: utilities.toasts.audioOutputChanged,
audioInputChanged: utilities.toasts.audioInputChanged,
capsLockChanged: utilities.toasts.capsLockChanged,
numLockChanged: utilities.toasts.numLockChanged,
kbLayoutChanged: utilities.toasts.kbLayoutChanged,
vpnChanged: utilities.toasts.vpnChanged,
nowPlaying: utilities.toasts.nowPlaying
},
vpn: {
enabled: utilities.vpn.enabled,
provider: utilities.vpn.provider
}
};
}
ElapsedTimer { ElapsedTimer {
id: timer id: timer
} }
@@ -86,7 +377,20 @@ Singleton {
onTriggered: { onTriggered: {
timer.restart(); timer.restart();
fileView.writeAdapter(); try {
let config = {};
try {
config = JSON.parse(fileView.text());
} catch (e) {
config = {};
}
config = root.serializeConfig();
fileView.setText(JSON.stringify(config, null, 4));
} catch (e) {
Toaster.toast(qsTr("Failed to serialize config"), e.message, "settings_alert", Toast.Error);
}
} }
} }
@@ -106,10 +410,6 @@ Singleton {
path: `${Paths.config}/config.json` path: `${Paths.config}/config.json`
watchChanges: true watchChanges: true
onAdapterUpdated: {
if (!root.migratingLegacyBar)
root.save();
}
onFileChanged: { onFileChanged: {
if (!root.recentlySaved) { if (!root.recentlySaved) {
timer.restart(); timer.restart();
@@ -126,11 +426,7 @@ Singleton {
ModeScheduler.checkStartup(); ModeScheduler.checkStartup();
Hyprsunset.checkStartup(); Hyprsunset.checkStartup();
try { try {
const raw = JSON.parse(text()); JSON.parse(text());
const migrated = root.migrateLegacyBarConfig(raw);
if (migrated)
root.save();
const elapsed = timer.elapsedMs(); const elapsed = timer.elapsedMs();
if (adapter.utilities.toasts.configLoaded && !root.recentlySaved) { if (adapter.utilities.toasts.configLoaded && !root.recentlySaved) {
@@ -151,9 +447,7 @@ Singleton {
} }
property BackgroundConfig background: BackgroundConfig { property BackgroundConfig background: BackgroundConfig {
} }
property BarConfig bar: BarConfig { property BarConfig barConfig: BarConfig {
}
property ClipboardConfig clipboard: ClipboardConfig {
} }
property Colors colors: Colors { property Colors colors: Colors {
} }
@@ -173,6 +467,8 @@ Singleton {
} }
property Overview overview: Overview { property Overview overview: Overview {
} }
property PluginConfig plugins: PluginConfig {
}
property Screenshot screenshot: Screenshot { property Screenshot screenshot: Screenshot {
} }
property Services services: Services { property Services services: Services {
+2 -2
View File
@@ -1,6 +1,7 @@
import Quickshell.Io import Quickshell.Io
JsonObject { JsonObject {
property int dragThreshold: 50
property bool enabled: true property bool enabled: true
property int mediaUpdateInterval: 500 property int mediaUpdateInterval: 500
property Performance performance: Performance { property Performance performance: Performance {
@@ -10,7 +11,6 @@ JsonObject {
} }
component Performance: JsonObject { component Performance: JsonObject {
property bool enabled: true
property bool showBattery: true property bool showBattery: true
property bool showCpu: true property bool showCpu: true
property bool showGpu: true property bool showGpu: true
@@ -25,7 +25,7 @@ JsonObject {
readonly property int mediaCoverArtSize: 150 readonly property int mediaCoverArtSize: 150
readonly property int mediaProgressSweep: 180 readonly property int mediaProgressSweep: 180
readonly property int mediaProgressThickness: 8 readonly property int mediaProgressThickness: 8
readonly property int mediaVisualizerSize: 200 readonly property int mediaVisualiserSize: 80
readonly property int mediaWidth: 200 readonly property int mediaWidth: 200
readonly property int resourceProgessThickness: 10 readonly property int resourceProgessThickness: 10
readonly property int resourceSize: 200 readonly property int resourceSize: 200
-4
View File
@@ -115,10 +115,6 @@ Singleton {
Config.save(); Config.save();
} }
function swapRG(c: color): color {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
Component.onCompleted: debounceTimer.triggered() Component.onCompleted: debounceTimer.triggered()
Connections { Connections {
-18
View File
@@ -4,8 +4,6 @@ import Quickshell
JsonObject { JsonObject {
property Apps apps: Apps { property Apps apps: Apps {
} }
property Battery battery: Battery {
}
property Color color: Color { property Color color: Color {
} }
property string dateFormat: "ddd d MMM - hh:mm:ss" property string dateFormat: "ddd d MMM - hh:mm:ss"
@@ -13,30 +11,14 @@ JsonObject {
property Idle idle: Idle { property Idle idle: Idle {
} }
property string logo: "" property string logo: ""
property bool showOverFullscreen: true
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers" property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
component Apps: JsonObject { component Apps: JsonObject {
property list<string> archiver: ["ark"]
property list<string> audio: ["pavucontrol"] property list<string> audio: ["pavucontrol"]
property list<string> document: ["libreoffice"]
property list<string> editor: ["kate"]
property list<string> explorer: ["dolphin"] property list<string> explorer: ["dolphin"]
property list<string> image: ["imv"]
property list<string> playback: ["mpv"] property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"] property list<string> terminal: ["kitty"]
} }
component Battery: JsonObject {
property int critPerc: 5
property list<var> popupThresholds: [
{
perc: 20,
name: qsTr("Low battery"),
message: qsTr("Battery is low"),
icon: "battery_android_frame_2"
},
]
}
component Color: JsonObject { component Color: JsonObject {
property int hyprsunsetTemp: 5000 property int hyprsunsetTemp: 5000
property string mode: "dark" property string mode: "dark"
-5
View File
@@ -84,11 +84,6 @@ JsonObject {
dangerous: false dangerous: false
}, },
] ]
property int dragThreshold: 50
property bool enableDangerousActions: false
property bool enabled: true
property list<string> favoriteApps: []
property list<string> hiddenApps: []
property int maxAppsShown: 10 property int maxAppsShown: 10
property int maxWallpapers: 7 property int maxWallpapers: 7
property Sizes sizes: Sizes { property Sizes sizes: Sizes {
-1
View File
@@ -9,7 +9,6 @@ JsonObject {
property bool expire: true property bool expire: true
property int groupPreviewNum: 3 property int groupPreviewNum: 3
property bool openExpanded: false property bool openExpanded: false
property bool showInFullscreen: true
property Sizes sizes: Sizes { property Sizes sizes: Sizes {
} }
+11
View File
@@ -0,0 +1,11 @@
import Quickshell.Io
JsonObject {
property bool enabled: false
property list<var> entries: [
{
id: "Plugin",
enabled: false
},
]
}
+4 -4
View File
@@ -1,12 +1,12 @@
import Quickshell.Io import Quickshell.Io
JsonObject { JsonObject {
property real corner_radius: 12.0
property bool drop_shadow: true
property bool enable_pp: true property bool enable_pp: true
property string mode: "manual" property string mode: "manual"
property real radius: 12.0 property bool rounded_corners: false
property bool rounding: false property real shadow_blur_radius: 22.0
property bool shadow: true
property real shadow_blur: 22.0
property list<int> shadow_color: [0, 0, 0, 160] property list<int> shadow_color: [0, 0, 0, 160]
property real shadow_offset_x: 5.0 property real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0 property real shadow_offset_y: 5.0
-1
View File
@@ -8,7 +8,6 @@ JsonObject {
property string defaultPlayer: "Spotify" property string defaultPlayer: "Spotify"
property string gpuType: "" property string gpuType: ""
property real maxVolume: 1.0 property real maxVolume: 1.0
property real minBrightness: 0.01
property list<var> playerAliases: [ property list<var> playerAliases: [
{ {
"from": "com.github.th_ch.youtube_music", "from": "com.github.th_ch.youtube_music",
-1
View File
@@ -1,7 +1,6 @@
import Quickshell.Io import Quickshell.Io
JsonObject { JsonObject {
property int dragThreshold: 30
property bool enabled: true property bool enabled: true
property Sizes sizes: Sizes { property Sizes sizes: Sizes {
} }
-70
View File
@@ -1,70 +0,0 @@
import Quickshell
import Quickshell.Services.UPower
import QtQuick
import ZShell
import qs.Config
import qs.Components.Toast
import qs.Helpers
Scope {
id: root
readonly property list<var> popupThresholds: [...Config.general.battery.popupThresholds].sort((a, b) => b.perc - a.perc)
function nearestThresholdAbove(p: real): var {
const thresholds = [...root.popupThresholds];
for (const perc of thresholds) {
if (p < perc.perc)
return perc;
}
return null;
}
Connections {
function onOnBatteryChanged(): void {
if (!Battery.ready)
return;
if (Battery.onBattery) {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger unplugged"), qsTr("Battery is discharging"), "power_off");
const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p);
if (perc)
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
} else {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger plugged in"), qsTr("Battery is charging"), "power");
}
}
target: Battery
}
Connections {
function onCurrentPercChanged(): void {
if (!Battery.onBattery)
return;
const p = Battery.currentPerc * 100;
for (const perc of root.popupThresholds) {
if (p == perc.perc) {
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery perc is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
}
}
function onReadyChanged(): void {
if (!Battery.ready)
return;
const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p);
if (perc)
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
target: Battery
}
}
+63 -39
View File
@@ -25,7 +25,7 @@ Singleton {
property alias server: server property alias server: server
function shouldThrottle(appName: string): bool { function shouldThrottle(appName: string): bool {
if (props.dnd || [...Visibilities.screens.values()].some(v => v.sidebar)) if (props.dnd)
return false; return false;
const key = (appName || "unknown").trim().toLowerCase(); const key = (appName || "unknown").trim().toLowerCase();
@@ -49,6 +49,11 @@ Singleton {
if (loaded) { if (loaded) {
saveTimer.restart(); saveTimer.restart();
} }
if (root.list.length > 0) {
HasNotifications.hasNotifications = true;
} else {
HasNotifications.hasNotifications = false;
}
} }
Timer { Timer {
@@ -174,8 +179,6 @@ Singleton {
property string appIcon property string appIcon
property string appName property string appName
property string body property string body
property string cachedImageSource: ""
property bool cachingImage: false
property bool closed property bool closed
readonly property Connections conn: Connections { readonly property Connections conn: Connections {
function onActionsChanged(): void { function onActionsChanged(): void {
@@ -210,14 +213,10 @@ Singleton {
notif.hasActionIcons = notif.notification.hasActionIcons; notif.hasActionIcons = notif.notification.hasActionIcons;
} }
function onHintsChanged(): void {
notif.hints = notif.notification.hints;
}
function onImageChanged(): void { function onImageChanged(): void {
notif.imageSource = notif.notification.image || ""; notif.image = notif.notification.image;
notif.image = notif.imageSource; if (notif.notification?.image)
notif.cacheImageIfNeeded(); notif.dummyImageLoader.active = true;
} }
function onResidentChanged(): void { function onResidentChanged(): void {
@@ -234,13 +233,60 @@ Singleton {
target: notif.notification target: notif.notification
} }
readonly property LazyLoader dummyImageLoader: LazyLoader {
active: false
PanelWindow {
color: "transparent"
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
mask: Region {
}
Image {
function tryCache(): void {
if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image)
return;
const cacheKey = notif.appName + notif.summary + notif.id;
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch;
for (let i = 0; i < cacheKey.length; i++) {
ch = cacheKey.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0);
const cache = `${Paths.notifimagecache}/${hash}.png`;
ZShellIo.saveItem(this, Qt.resolvedUrl(cache), () => {
notif.image = cache;
notif.dummyImageLoader.active = false;
});
}
anchors.fill: parent
asynchronous: true
cache: false
fillMode: Image.PreserveAspectCrop
opacity: 0
source: Qt.resolvedUrl(notif.image)
onHeightChanged: tryCache()
onStatusChanged: tryCache()
onWidthChanged: tryCache()
}
}
}
property real expireTimeout: 5 property real expireTimeout: 5
property bool hasActionIcons property bool hasActionIcons
property var hints property string id
property string image property string image
property string imageSource
property var locks: new Set() property var locks: new Set()
property string notifId
property Notification notification property Notification notification
property bool popup property bool popup
property bool resident property bool resident
@@ -283,26 +329,6 @@ Singleton {
} }
property int urgency: NotificationUrgency.Normal property int urgency: NotificationUrgency.Normal
function cacheImageIfNeeded(): void {
const source = imageSource;
if (!source || cachingImage)
return;
if (cachedImageSource === source)
return;
cachingImage = true;
ZShellIo.cacheImage(Qt.resolvedUrl(source), Paths.notifimagecache, (path, url) => {
cachedImageSource = source;
image = path;
cachingImage = false;
}, () => {
cachingImage = false;
});
}
function close(): void { function close(): void {
closed = true; closed = true;
if (locks.size === 0 && root.list.includes(this)) { if (locks.size === 0 && root.list.includes(this)) {
@@ -326,14 +352,14 @@ Singleton {
if (!notification) if (!notification)
return; return;
notifId = notification.id; id = notification.id;
summary = notification.summary; summary = notification.summary;
body = notification.body; body = notification.body;
appIcon = notification.appIcon; appIcon = notification.appIcon;
appName = notification.appName; appName = notification.appName;
imageSource = notification.image || ""; image = notification.image;
image = imageSource; if (notification?.image)
hints = notification.hints; dummyImageLoader.active = true;
expireTimeout = notification.expireTimeout; expireTimeout = notification.expireTimeout;
urgency = notification.urgency; urgency = notification.urgency;
resident = notification.resident; resident = notification.resident;
@@ -343,8 +369,6 @@ Singleton {
text: a.text, text: a.text,
invoke: () => a.invoke() invoke: () => a.invoke()
})); }));
cacheImageIfNeeded();
} }
} }
} }
+107
View File
@@ -0,0 +1,107 @@
import Quickshell
import QtQuick
import QtQuick.Shapes
import qs.Components
import qs.Config
import qs.Modules as Modules
import qs.Modules.Notifications as Notifications
import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Notifications.Sidebar.Utils as Utils
import qs.Modules.Dashboard as Dashboard
import qs.Modules.Osd as Osd
import qs.Modules.Launcher as Launcher
import qs.Modules.Resources as Resources
import qs.Modules.Drawing as Drawing
import qs.Modules.Settings as Settings
import qs.Modules.Dock as Dock
Shape {
id: root
required property Item bar
required property Panels panels
required property PersistentProperties visibilities
anchors.fill: parent
anchors.margins: Config.barConfig.border
anchors.topMargin: bar.implicitHeight
asynchronous: true
preferredRendererType: Shape.CurveRenderer
Drawing.Background {
startX: 0
startY: wrapper.y - rounding
wrapper: root.panels.drawing
}
Resources.Background {
startX: 0 - rounding
startY: 0
wrapper: root.panels.resources
}
Osd.Background {
startX: root.width - root.panels.sidebar.width
startY: (root.height - wrapper.height) / 2 - rounding
wrapper: root.panels.osd
}
Modules.Background {
invertBottomRounding: wrapper.x <= 0
rounding: root.panels.popouts.currentName.startsWith("updates") || root.panels.popouts.currentName.startsWith("audio") ? Appearance.rounding.normal : Appearance.rounding.smallest
startX: wrapper.x - rounding
startY: wrapper.y
wrapper: root.panels.popouts
}
Notifications.Background {
sidebar: sidebar
startX: root.width
startY: 0
wrapper: root.panels.notifications
}
Launcher.Background {
startX: (root.width - wrapper.width) / 2 - rounding
startY: root.height
wrapper: root.panels.launcher
}
Dashboard.Background {
startX: root.width - root.panels.dashboard.width - rounding
startY: 0
wrapper: root.panels.dashboard
}
Utils.Background {
sidebar: sidebar
startX: root.width
startY: root.height
wrapper: root.panels.utilities
}
Sidebar.Background {
id: sidebar
panels: root.panels
startX: root.width
startY: root.panels.notifications.height
wrapper: root.panels.sidebar
}
Settings.Background {
id: settings
startX: (root.width - wrapper.width) / 2 - rounding
startY: 0
wrapper: root.panels.settings
}
Dock.Background {
id: dock
startX: (root.width - wrapper.width) / 2 - rounding
startY: root.height
wrapper: root.panels.dock
}
}
-25
View File
@@ -1,25 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Variants {
model: Quickshell.screens
Scope {
id: scope
required property ShellScreen modelData
Exclusions {
bar: content.bar
screen: scope.modelData
}
Windows {
id: content
screen: scope.modelData
}
}
}
+24 -19
View File
@@ -1,30 +1,35 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick import QtQuick
import ZShell.Internal
Item { Canvas {
id: root id: root
readonly property alias content: contentLoader.item
readonly property PersistentProperties drawingState: PersistentProperties {
property color penColor: "white" property color penColor: "white"
property int penWidth: 4 property real penWidth: 4
property var points: []
reloadableId: "drawingState" function clear(): void {
var ctx = getContext('2d');
root.points = [];
ctx.reset();
root.requestPaint();
} }
required property PersistentProperties visibilities
Loader { renderStrategy: Canvas.Cooperative
id: contentLoader
active: root.visibilities.isDrawing onPaint: {
anchors.fill: parent if (points.length < 2)
return;
sourceComponent: StrokeCanvas { var ctx = root.getContext('2d');
penColor: root.drawingState.penColor ctx.save();
penWidth: root.drawingState.penWidth ctx.lineWidth = root.penWidth;
} ctx.strokeStyle = root.penColor;
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 1; i < points.length; i++)
ctx.lineTo(points[i].x, points[i].y);
ctx.stroke();
points = points.slice(points.length - 2);
ctx.restore();
} }
} }
+60
View File
@@ -0,0 +1,60 @@
import Quickshell
import QtQuick
import qs.Components
import qs.Config
CustomMouseArea {
id: root
required property var bar
required property Drawing drawing
required property Panels panels
required property var popout
required property PersistentProperties visibilities
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + Config.barConfig.border && withinPanelHeight(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height;
}
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: root.visibilities.isDrawing ? parent : undefined
hoverEnabled: true
visible: root.visibilities.isDrawing
onPositionChanged: event => {
const x = event.x;
const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.drawing.points.push(Qt.point(x, y));
root.drawing.requestPaint();
}
if (root.inLeftPanel(root.popout, x, y)) {
root.z = -2;
root.panels.drawing.expanded = true;
}
}
onPressed: event => {
const x = event.x;
const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.panels.drawing.expanded = false;
root.drawing.points.push(Qt.point(x, y));
root.drawing.requestPaint();
return;
}
if (event.buttons & Qt.RightButton)
root.drawing.clear();
}
onReleased: {
root.drawing.points = [];
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ Scope {
} }
component ExclusionZone: CustomWindow { component ExclusionZone: CustomWindow {
exclusiveZone: Config.bar.border exclusiveZone: Config.barConfig.border
implicitHeight: 1 implicitHeight: 1
implicitWidth: 1 implicitWidth: 1
name: "Bar-Exclusion" name: "Bar-Exclusion"
+49 -156
View File
@@ -2,34 +2,33 @@ import Quickshell
import QtQuick import QtQuick
import qs.Components import qs.Components
import qs.Config import qs.Config
import qs.Helpers
import qs.Modules as BarPopouts import qs.Modules as BarPopouts
Item { CustomMouseArea {
id: root id: root
required property Item bar required property Item bar
required property real borderThickness
property bool dashboardShortcutActive property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing required property Drawing drawing
required property DrawingInput input
property bool osdShortcutActive property bool osdShortcutActive
required property Panels panels required property Panels panels
required property BarPopouts.Wrapper popouts required property BarPopouts.Wrapper popouts
required property ShellScreen screen required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive property bool utilitiesShortcutActive
required property PersistentProperties visibilities required property PersistentProperties visibilities
function inBottomPanel(panel: Item, x: real, y: real): bool { function inBottomPanel(panel: Item, x: real, y: real): bool {
return y > root.height - panel.height - Config.bar.border && withinPanelWidth(panel, x, y); return y > root.height - panel.height - Config.barConfig.border && withinPanelWidth(panel, x, y);
} }
function inLeftPanel(panel: Item, x: real, y: real): bool { function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + Config.bar.border && withinPanelHeight(panel, x, y); return x < panel.x + panel.width + Config.barConfig.border && withinPanelHeight(panel, x, y);
} }
function inRightPanel(panel: Item, x: real, y: real): bool { function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x - Config.bar.border && withinPanelHeight(panel, x, y); return x > panel.x - Config.barConfig.border && withinPanelHeight(panel, x, y);
} }
function inTopPanel(panel: Item, x: real, y: real): bool { function inTopPanel(panel: Item, x: real, y: real): bool {
@@ -48,181 +47,83 @@ Item {
} }
function withinPanelWidth(panel: Item, x: real, y: real): bool { function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + root.borderThickness; const panelX = panel.x;
return x >= panelX && x <= panelX + panel.width; return x >= panelX && x <= panelX + panel.width;
} }
anchors.fill: parent anchors.fill: parent
cursorShape: (pressed && dragStart.y < bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
hoverEnabled: true
propagateComposedEvents: false
DragHandler { onContainsMouseChanged: {
id: singleHandler if (!containsMouse) {
if (!osdShortcutActive) {
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined visibilities.osd = false;
dragThreshold: 0
enabled: !root.visibilities.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1
minimumPointCount: 1
target: null
onActiveChanged: {
if (!active)
root.singleGestureTriggered = false;
}
onCentroidChanged: {
const x = centroid.position.x;
const y = centroid.position.y;
const dragX = x - centroid.pressPosition.x;
const dragY = y - centroid.pressPosition.y;
if (centroid.pressPosition.y >= root.screen.height - Config.bar.border && centroid.pressPosition.x > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold)
root.visibilities.launcher = true;
if (root.singleGestureTriggered)
return;
if (centroid.pressPosition.y < root.bar.implicitHeight) {
if (dragY > 20) {
root.visibilities.settings = true;
root.singleGestureTriggered = true;
} else if (dragY < -20) {
root.visibilities.settings = false;
root.singleGestureTriggered = true;
}
}
if (centroid.pressPosition.y > root.screen.height - Config.bar.border && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
root.visibilities.clipboard = true;
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher)
if (dragY < -10) {
root.visibilities.dock = true;
root.singleGestureTriggered = true;
}
if (centroid.pressPosition.x > root.screen.width - Config.bar.border && centroid.pressPosition.y < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
root.visibilities.sidebar = true;
root.singleGestureTriggered = true;
}
if (centroid.pressPosition.x >= root.screen.width - Config.bar.border && centroid.pressPosition.y > (root.screen.height / 2) && dragX < -20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
if (centroid.pressPosition.x <= Config.bar.border && dragX > 20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
}
}
PointHandler {
id: drawingHandler
property bool setInitialPoint: false
acceptedButtons: Qt.LeftButton | Qt.RightButton
enabled: root.visibilities.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
onActiveChanged: {
if (!active) {
setInitialPoint = false;
root.drawing.content.endStroke();
} else {
root.panels.drawing.collapse();
}
}
onPointChanged: {
if (!active)
return;
const x = point.position.x;
const y = point.position.y;
const origX = point.pressPosition.x;
const origY = point.pressPosition.y;
if (point.pressedButtons & Qt.RightButton) {
root.drawing.content.clear();
return;
}
if (x === 0 && y === 0 && origX === 0 && origY === 0)
return;
if (!setInitialPoint) {
setInitialPoint = true;
root.drawing.content.beginStroke(origX, origY);
return;
}
root.drawing.content.appendPoint(x, y);
}
}
HoverHandler {
id: hoverHandler
cursorShape: root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
onHoveredChanged: {
if (!hovered) {
if (!root.osdShortcutActive) {
root.visibilities.osd = false;
root.panels.osd.hovered = false; root.panels.osd.hovered = false;
} }
if (!root.popouts.currentName.startsWith("traymenu")) { if (!popouts.currentName.startsWith("traymenu")) {
root.popouts.hasCurrent = false; popouts.hasCurrent = false;
} }
if (Config.bar.autoHide) if (Config.barConfig.autoHide)
root.bar.isHovered = false; bar.isHovered = false;
} }
} }
onPointChanged: { onPositionChanged: event => {
const x = point.position.x; const x = event.x;
const y = point.position.y; const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (root.visibilities.isDrawing) { if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) { // root.input.z = 2;
root.panels.drawing.expand(); root.panels.drawing.expanded = false;
root.drawing.content.hideHover();
return;
} }
root.drawing.content.showHover(x, y); if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight)
return; bar.isHovered = true;
if (pressed && dragStart.y < bar.implicitHeight) {
if (dragY > 20)
visibilities.settings = true;
else if (dragY < -20)
visibilities.settings = false;
} }
if (!root.visibilities.bar && Config.bar.autoHide && y < root.bar.implicitHeight) if (Config.dock.hoverToReveal && pressed && dragStart.y > root.screen.height - root.bar.implicitHeight)
root.bar.isHovered = true; if (dragY < -10)
visibilities.dock = true;
if (root.panels.sidebar.width === 0) { if (panels.sidebar.width === 0) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y); const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (showOsd) { if (showOsd) {
root.osdShortcutActive = false; osdShortcutActive = false;
root.panels.osd.hovered = true; root.panels.osd.hovered = true;
} }
} else { } else {
const outOfSidebar = x < root.width - root.panels.sidebar.width; const outOfSidebar = x < width - panels.sidebar.width;
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y); const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!root.osdShortcutActive) { if (!osdShortcutActive) {
root.visibilities.osd = showOsd; visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd; root.panels.osd.hovered = showOsd;
} else if (showOsd) { } else if (showOsd) {
root.osdShortcutActive = false; osdShortcutActive = false;
root.panels.osd.hovered = true; root.panels.osd.hovered = true;
} }
} }
if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y)) if (Config.dock.enable && !Config.dock.hoverToReveal && !visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y))
root.visibilities.dock = true; visibilities.dock = true;
if (y < root.bar.implicitHeight) if (y < root.bar.implicitHeight) {
root.bar.checkPopout(x); root.bar.checkPopout(x);
} }
} }
onPressed: event => dragStart = Qt.point(event.x, event.y)
Connections { Connections {
function onDashboardChanged() { function onDashboardChanged() {
@@ -231,8 +132,6 @@ Item {
if (!inDashboardArea) { if (!inDashboardArea) {
root.dashboardShortcutActive = true; root.dashboardShortcutActive = true;
} }
if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x)
root.visibilities.launcher = false;
root.visibilities.settings = false; root.visibilities.settings = false;
root.visibilities.sidebar = false; root.visibilities.sidebar = false;
@@ -262,10 +161,6 @@ Item {
} }
if (root.visibilities.launcher) { if (root.visibilities.launcher) {
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
root.visibilities.dashboard = false;
}
root.visibilities.dock = false; root.visibilities.dock = false;
root.visibilities.settings = false; root.visibilities.settings = false;
} }
@@ -298,8 +193,6 @@ Item {
root.visibilities.sidebar = false; root.visibilities.sidebar = false;
root.panels.popouts.hasCurrent = false; root.panels.popouts.hasCurrent = false;
root.visibilities.launcher = false; root.visibilities.launcher = false;
} else {
Config.save();
} }
} }
+5 -18
View File
@@ -13,20 +13,17 @@ import qs.Modules.Resources as Resources
import qs.Modules.Settings as Settings import qs.Modules.Settings as Settings
import qs.Modules.Drawing as Drawing import qs.Modules.Drawing as Drawing
import qs.Modules.Dock as Dock import qs.Modules.Dock as Dock
import qs.Modules.Clipboard as Clipboard
import qs.Config import qs.Config
Item { Item {
id: root id: root
required property Item bar required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock readonly property alias dock: dock
readonly property alias drawing: drawing readonly property alias drawing: drawing
required property var drawingItem required property Canvas drawingItem
readonly property alias launcher: launcher readonly property alias launcher: launcher
readonly property alias notifications: notifications readonly property alias notifications: notifications
readonly property alias osd: osd readonly property alias osd: osd
@@ -44,7 +41,7 @@ Item {
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.fill: parent anchors.fill: parent
anchors.margins: borderThickness anchors.margins: Config.barConfig.border
anchors.topMargin: bar.implicitHeight anchors.topMargin: bar.implicitHeight
Item { Item {
@@ -100,7 +97,6 @@ Item {
id: popouts id: popouts
anchors.top: parent.top anchors.top: parent.top
borderThickness: root.borderThickness
screen: root.screen screen: root.screen
} }
@@ -184,11 +180,11 @@ Item {
Item { Item {
id: settingsWrapper id: settingsWrapper
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
clip: true clip: true
implicitHeight: settings.implicitHeight implicitHeight: settings.implicitHeight * (1 - settings.offsetScale)
implicitWidth: settings.implicitWidth implicitWidth: settings.implicitWidth
x: (root.width - settings.implicitWidth) / 2
y: (settings.implicitHeight + (root.height - root.bar.implicitHeight - settings.implicitHeight) / 2) * (1 - settings.offsetScale) - settings.implicitHeight - 5
Settings.Wrapper { Settings.Wrapper {
id: settings id: settings
@@ -211,13 +207,4 @@ Item {
screen: root.screen screen: root.screen
visibilities: root.visibilities visibilities: root.visibilities
} }
Clipboard.Wrapper {
id: clipboard
anchors.bottom: parent.bottom
anchors.left: parent.left
screen: root.screen
visibilities: root.visibilities
}
} }
-89
View File
@@ -1,89 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Modules.Bar as Bar
Region {
id: root
required property Bar.BarLoader bar
readonly property real borderThickness: win.borderThickness
readonly property alias menuPopoutRegion: menuPopoutRegion
required property Panels panels
required property var win
height: win.height - bar.implicitHeight - win.borderThickness - win.dragMaskPadding * 2
intersection: Intersection.Xor
width: win.width - win.borderThickness * 2 - win.dragMaskPadding * 2
x: win.borderThickness + win.dragMaskPadding
y: bar.implicitHeight + win.dragMaskPadding
R {
panel: root.panels.dashboardWrapper
}
R {
panel: root.panels.launcher
}
R {
id: sidebarRegion
panel: root.panels.sidebar
width: panel.width * (1 - root.panels.sidebar.offsetScale) + root.borderThickness
x: root.win.width - width
}
R {
panel: root.panels.osdWrapper
width: panel.width * (1 - root.panels.osd.offsetScale) + root.borderThickness
x: root.win.width - width
}
R {
panel: root.panels.notifications
}
R {
height: panel.height * (1 - root.panels.utilities.offsetScale) + root.borderThickness
panel: root.panels.utilities
y: root.win.height - height
}
R {
panel: root.panels.popoutsWrapper
}
R {
panel: root.panels.resourcesWrapper
}
R {
panel: root.panels.settingsWrapper
}
R {
panel: root.panels.dock
}
R {
panel: root.panels.clipboard
}
Region {
id: menuPopoutRegion
intersection: Intersection.Subtract
}
component R: Region {
required property Item panel
height: panel.height
intersection: Intersection.Subtract
width: panel.width
x: panel.x + root.borderThickness
y: panel.y + root.bar.implicitHeight
}
}
+97 -154
View File
@@ -9,112 +9,65 @@ import Quickshell.Hyprland
import ZShell.Blobs import ZShell.Blobs
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Modules
import qs.Modules.Bar import qs.Modules.Bar
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
import qs.Drawers import qs.Drawers
Variants {
model: Quickshell.screens
Scope {
id: scope
required property var modelData
Exclusions {
bar: bar
screen: scope.modelData
}
CustomWindow { CustomWindow {
id: root id: win
readonly property alias bar: bar readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2)
readonly property real borderLayoutThickness: hasFullscreen ? 0 : Config.bar.border
readonly property real borderRounding: Config.bar.rounding * (1 - fsTransitionProg)
readonly property real borderThickness: Config.bar.border * (1 - fsTransitionProg)
readonly property int dragMaskPadding: {
if (focusGrab.active)
return 0;
if (monitor?.lastIpcObject.specialWorkspace?.name || monitor?.activeWorkspace.lastIpcObject.windows > 0)
return 0;
return 100;
}
property real fsTransitionProg: hasFullscreen ? 1 : 0
readonly property bool hasFullscreen: {
if (hasSpecialWorkspace) {
const specialName = monitor?.lastIpcObject.specialWorkspace?.name;
if (!specialName)
return false;
const specialWs = Hypr.workspaces.values.find(ws => ws.name === specialName);
return specialWs?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
}
return hasFullscreenOnNormalWs;
}
readonly property bool hasFullscreenOnNormalWs: monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false
readonly property bool hasSpecialWorkspace: (monitor?.lastIpcObject.specialWorkspace?.name.length ?? 0) > 0
readonly property alias interactionWrapper: interactions
readonly property alias menuRegion: regions.menuPopoutRegion
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
property var root: Quickshell.shellDir property var root: Quickshell.shellDir
readonly property real sdfBorderOffset: 2 * fsTransitionProg
readonly property real shadowOpacity: 0.7 * (1 - fsTransitionProg)
property color surfaceColor: DynamicColors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.settings ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: visibilities.dock || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.resources ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent" color: "transparent"
contentItem.focus: true contentItem.focus: true
mask: visibilities.isDrawing ? null : (hasFullscreen ? emptyRegion : regions) mask: visibilities.isDrawing ? null : region
name: "Bar" name: "Bar"
screen: scope.modelData
Behavior on fsTransitionProg {
Anim {
}
}
Behavior on surfaceColor {
CAnim {
}
}
contentItem.Keys.onEscapePressed: { contentItem.Keys.onEscapePressed: {
if (Config.bar.autoHide) if (Config.barConfig.autoHide)
visibilities.bar = false; visibilities.bar = false;
visibilities.launcher = false;
visibilities.sidebar = false; visibilities.sidebar = false;
visibilities.dashboard = false; visibilities.dashboard = false;
visibilities.osd = false; visibilities.osd = false;
visibilities.settings = false; visibilities.settings = false;
visibilities.resources = false; visibilities.resources = false;
visibilities.dock = false;
visibilities.clipboard = false;
panels.popouts.hasCurrent = false;
} }
onHasFullscreenChanged: { onHasFullscreenChanged: {
visibilities.launcher = false; visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false; visibilities.dashboard = false;
visibilities.osd = false; visibilities.osd = false;
visibilities.settings = false; visibilities.settings = false;
visibilities.resources = false; visibilities.resources = false;
visibilities.clipboard = false;
visibilities.dock = false;
panels.popouts.hasCurrent = false;
} }
Region { Region {
id: emptyRegion id: region
height: panels.notifications.height height: win.height - bar.implicitHeight - Config.barConfig.border
width: panels.notifications.width intersection: Intersection.Xor
x: panels.notifications.x + root.borderThickness regions: popoutRegions.instances
y: panels.notifications.y + bar.implicitHeight width: win.width - Config.barConfig.border * 2
x: Config.barConfig.border
Region { y: bar.implicitHeight
height: panels.osd.height
width: panels.osdWrapper.width * (1 - panels.osd.offsetScale) + root.borderThickness
x: root.width - width
y: panels.osdWrapper.y + bar.implicitHeight
}
}
Regions {
id: regions
bar: bar
panels: panels
win: root
} }
anchors { anchors {
@@ -124,11 +77,27 @@ CustomWindow {
top: true top: true
} }
Variants {
id: popoutRegions
model: panels.children
Region {
required property Item modelData
height: modelData.height
intersection: Intersection.Subtract
width: modelData.width
x: modelData.x + Config.barConfig.border
y: modelData.y + bar.implicitHeight
}
}
HyprlandFocusGrab { HyprlandFocusGrab {
id: focusGrab id: focusGrab
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu")) active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [root] windows: [win]
onCleared: { onCleared: {
visibilities.launcher = false; visibilities.launcher = false;
@@ -137,7 +106,6 @@ CustomWindow {
visibilities.osd = false; visibilities.osd = false;
visibilities.settings = false; visibilities.settings = false;
visibilities.resources = false; visibilities.resources = false;
visibilities.clipboard = false;
visibilities.dock = false; visibilities.dock = false;
panels.popouts.hasCurrent = false; panels.popouts.hasCurrent = false;
} }
@@ -147,7 +115,6 @@ CustomWindow {
id: visibilities id: visibilities
property bool bar property bool bar
property bool clipboard
property bool dashboard property bool dashboard
property bool dock property bool dock
property bool isDrawing property bool isDrawing
@@ -158,7 +125,7 @@ CustomWindow {
property bool settings property bool settings
property bool sidebar property bool sidebar
Component.onCompleted: Visibilities.load(root.screen, this) Component.onCompleted: Visibilities.load(scope.modelData, this)
} }
IpcHandler { IpcHandler {
@@ -172,39 +139,42 @@ CustomWindow {
Binding { Binding {
property: "bar" property: "bar"
target: visibilities target: visibilities
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || (!Config.bar.hideWhenNotif && visibilities.notif) || visibilities.resources || visibilities.settings || bar.isHovered value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || (!Config.barConfig.hideWhenNotif && visibilities.notif) || visibilities.resources || visibilities.settings || bar.isHovered
when: Config.bar.autoHide when: Config.barConfig.autoHide
} }
Item { Item {
id: surface
anchors.fill: parent anchors.fill: parent
layer.enabled: true layer.enabled: true
opacity: root.surfaceColor.a opacity: Appearance.transparency.enabled ? DynamicColors.transparency.base : 1
layer.effect: MultiEffect { layer.effect: MultiEffect {
blurMax: 32 blurMax: 32
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, Math.max(0, root.shadowOpacity)) shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 1)
shadowEnabled: true shadowEnabled: true
} }
BlobGroup { BlobGroup {
id: blobGroup id: blobGroup
color: root.surfaceColor color: DynamicColors.palette.m3surface
smoothing: Config.bar.smoothing smoothing: Config.barConfig.smoothing
Behavior on color {
CAnim {
}
}
} }
BlobInvertedRect { BlobInvertedRect {
anchors.fill: parent anchors.fill: parent
anchors.margins: -50 anchors.margins: -50
borderBottom: root.borderThickness - anchors.margins - root.sdfBorderOffset borderBottom: Config.barConfig.border - anchors.margins
borderLeft: root.borderThickness - anchors.margins - root.sdfBorderOffset borderLeft: Config.barConfig.border - anchors.margins
borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset borderRight: Config.barConfig.border - anchors.margins
borderTop: bar.implicitHeight - anchors.margins - root.sdfBorderOffset borderTop: bar.implicitHeight - anchors.margins
group: blobGroup group: blobGroup
radius: root.borderRounding radius: Config.barConfig.rounding
} }
PanelBg { PanelBg {
@@ -217,7 +187,7 @@ CustomWindow {
implicitWidth: panels.dashboard.width implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper panel: panels.dashboardWrapper
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + root.borderThickness x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
} }
@@ -251,7 +221,7 @@ CustomWindow {
implicitWidth: panels.osd.width implicitWidth: panels.osd.width
panel: panels.osdWrapper panel: panels.osdWrapper
radius: 20 radius: 20
x: panels.osdWrapper.x + panels.osd.x + root.borderThickness x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
} }
@@ -259,13 +229,12 @@ CustomWindow {
id: notifsBg id: notifsBg
panel: panels.notifications panel: panels.notifications
radius: Appearance.rounding.normal
} }
PanelBg { PanelBg {
id: utilsBg id: utilsBg
deformAmount: 0.1 deformAmount: panels.sidebar.visible ? (0.1) : (0.1)
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg] exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
panel: panels.utilities panel: panels.utilities
topLeftRadius: 0 topLeftRadius: 0
@@ -274,14 +243,14 @@ CustomWindow {
PanelBg { PanelBg {
id: popoutBg id: popoutBg
property real extraHeight: 0.2 property real extraHeight: panels.popouts.isDetached ? 0 : 0.2
deformAmount: panels.popouts.currentName.startsWith("traymenu") ? 0.15 : 0.08 deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1
implicitHeight: panels.popouts.height * (1 + extraHeight) implicitHeight: panels.popouts.height * (1 + extraHeight)
implicitWidth: panels.popouts.width implicitWidth: panels.popouts.width
panel: panels.popoutsWrapper panel: panels.popoutsWrapper
radius: panels.popouts.current?.panelRadius ?? Appearance.rounding.normal radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale
x: panels.popoutsWrapper.x + panels.popouts.x + root.borderThickness x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
Behavior on extraHeight { Behavior on extraHeight {
@@ -297,22 +266,24 @@ CustomWindow {
implicitHeight: panels.resources.height implicitHeight: panels.resources.height
implicitWidth: panels.resources.width implicitWidth: panels.resources.width
panel: panels.resourcesWrapper panel: panels.resourcesWrapper
radius: Appearance.rounding.large radius: Appearance.rounding.normal
x: panels.resourcesWrapper.x + panels.resources.x + root.borderThickness x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
} }
PanelBg { PanelBg {
id: settingsBg id: settingsBg
property real extraHeight: 0 property real extraHeight: 0.2
deformAmount: 0.03 deformAmount: 0.03
implicitHeight: panels.settings.height * (1 + extraHeight) implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width implicitWidth: panels.settings.width
panel: panels.settingsWrapper panel: panels.settings
radius: Appearance.rounding.large + Appearance.padding.normal radius: Appearance.rounding.large
x: panels.settingsWrapper.x + panels.settings.x + root.borderThickness topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
x: panels.settingsWrapper.x + panels.settings.x + Config.barConfig.border
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
} }
@@ -331,75 +302,47 @@ CustomWindow {
panel: panels.drawing panel: panels.drawing
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
} }
PanelBg {
id: clipboardBg
deformAmount: 0.03
panel: panels.clipboard
radius: 29
}
} }
Drawing { Drawing {
id: drawing id: drawing
anchors.fill: parent anchors.fill: parent
layer.enabled: true z: 2
}
DrawingInput {
id: input
bar: bar
drawing: drawing
panels: panels
popout: panels.drawing
visibilities: visibilities visibilities: visibilities
z: 2 z: 2
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: maskSource
}
}
Item {
id: maskSource
anchors.fill: parent
layer.enabled: true
visible: false
CustomRect {
readonly property int extraWidth: radius
color: "white"
implicitHeight: panels.drawing.height
implicitWidth: panels.drawing.width + extraWidth
radius: drawingBg.radius
x: -extraWidth + root.borderThickness + panels.drawing.x
y: panels.drawing.y + bar.implicitHeight
}
} }
Interactions { Interactions {
id: interactions id: mouseArea
anchors.fill: parent anchors.fill: parent
bar: bar bar: bar
borderThickness: root.borderLayoutThickness
drawing: drawing drawing: drawing
enabled: true input: input
panels: panels panels: panels
popouts: panels.popouts popouts: panels.popouts
screen: root.screen screen: scope.modelData
visibilities: visibilities visibilities: visibilities
z: 1
Panels { Panels {
id: panels id: panels
bar: bar bar: bar
borderThickness: root.borderThickness
drawingItem: drawing drawingItem: drawing
screen: root.screen screen: scope.modelData
visibilities: visibilities visibilities: visibilities
clipboard.transform: Matrix4x4 {
matrix: clipboardBg.deformMatrix
}
dashboard.transform: Matrix4x4 { dashboard.transform: Matrix4x4 {
matrix: dashBg.deformMatrix matrix: dashBg.deformMatrix
} }
@@ -421,7 +364,7 @@ CustomWindow {
resources.transform: Matrix4x4 { resources.transform: Matrix4x4 {
matrix: resourcesBg.deformMatrix matrix: resourcesBg.deformMatrix
} }
settingsWrapper.transform: Matrix4x4 { settings.transform: Matrix4x4 {
matrix: settingsBg.deformMatrix matrix: settingsBg.deformMatrix
} }
sidebar.transform: Matrix4x4 { sidebar.transform: Matrix4x4 {
@@ -437,14 +380,14 @@ CustomWindow {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen
popouts: panels.popouts popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper popoutsWrapper: panels.popoutsWrapper
screen: root.screen screen: scope.modelData
visibilities: visibilities visibilities: visibilities
} }
} }
}
}
component PanelBg: BlobRect { component PanelBg: BlobRect {
property real deformAmount: 0.15 property real deformAmount: 0.15
@@ -455,7 +398,7 @@ CustomWindow {
implicitHeight: panel.height implicitHeight: panel.height
implicitWidth: panel.width implicitWidth: panel.width
radius: Appearance.rounding.smallest radius: Appearance.rounding.smallest
x: panel.x + root.borderThickness x: panel.x + Config.barConfig.border
y: panel.y + bar.implicitHeight y: panel.y + bar.implicitHeight
} }
} }
-7
View File
@@ -1,7 +0,0 @@
import QtQuick.Effects
MultiEffect {
maskEnabled: true
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
+18
View File
@@ -0,0 +1,18 @@
pragma Singleton
import Quickshell
import ZShell.Models
Singleton {
id: root
property alias plugins: plugins.entries
FileSystemModel {
id: plugins
nameFilters: ["*.qml"]
path: Quickshell.env("HOME") + "/.config/zshell"
recursive: false
}
}
+17
View File
@@ -0,0 +1,17 @@
import Quickshell
import QtQuick
import ZShell.Models
import qs.Config
Repeater {
model: FetchPlugins.plugins
LazyLoader {
required property FileSystemEntry modelData
activeAsync: Config.plugins.entries.some(p => {
return p.id === modelData.baseName && p.enabled;
})
source: modelData.path
}
}
+7 -7
View File
@@ -118,12 +118,12 @@ ColumnLayout {
} }
StateLayer { StateLayer {
cursorShape: Qt.IBeamCursor function onClicked(): void {
hoverEnabled: false
onClicked: {
parent.forceActiveFocus(); parent.forceActiveFocus();
} }
cursorShape: Qt.IBeamCursor
hoverEnabled: false
} }
RowLayout { RowLayout {
@@ -179,11 +179,11 @@ ColumnLayout {
radius: Appearance.rounding.full radius: Appearance.rounding.full
StateLayer { StateLayer {
color: root.greeter.buffer && !root.greeter.launching ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface function onClicked(): void {
onClicked: {
root.greeter.submit(); root.greeter.submit();
} }
color: root.greeter.buffer && !root.greeter.launching ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
} }
MaterialIcon { MaterialIcon {
+3 -58
View File
@@ -2,62 +2,7 @@ import QtQuick
import qs.Config import qs.Config
NumberAnimation { NumberAnimation {
enum Type { duration: MaterialEasing.standardTime
StandardSmall = 0, easing.bezierCurve: MaterialEasing.standard
Standard, easing.type: Easing.BezierSpline
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: Anim.DefaultSpatial
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
} }
+5 -5
View File
@@ -59,15 +59,15 @@ ColumnLayout {
} }
StateLayer { StateLayer {
function onClicked(): void {
root.toggleRequested();
root.expanded = !root.expanded;
}
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3onSurface color: DynamicColors.palette.m3onSurface
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
showHoverBackground: false showHoverBackground: false
onClicked: {
root.toggleRequested();
root.expanded = !root.expanded;
}
} }
} }
+3 -3
View File
@@ -23,10 +23,10 @@ Button {
} }
StateLayer { StateLayer {
radius: control.radius function onClicked(): void {
onClicked: {
control.clicked(); control.clicked();
} }
radius: control.radius
} }
} }
+4 -4
View File
@@ -33,13 +33,13 @@ RadioButton {
} }
StateLayer { StateLayer {
function onClicked(): void {
root.click();
}
anchors.margins: -7 anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1 z: -1
onClicked: {
root.click();
}
} }
CustomRect { CustomRect {
+26 -155
View File
@@ -1,174 +1,45 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import qs.Config import qs.Config
Slider { Slider {
id: root id: root
property bool animateWave background: Item {
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property real pos: visualPosition
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
signal interaction(v: real)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
anchors.fill: parent
CustomRect { CustomRect {
id: remaining anchors.bottom: parent.bottom
anchors.left: handle.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
bottomLeftRadius: Appearance.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: Appearance.rounding.small
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
anchors.right: parent.right
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Appearance.rounding.full
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const mult = parent.height <= 12 ? 3 : 1.2;
const pressMult = parent.height <= 12 ? 4 : 1.5;
return parent.height * (mouse.pressed ? pressMult : mult);
}
implicitWidth: 4
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter anchors.top: parent.top
asynchronous: true bottomRightRadius: root.implicitHeight / 6
sourceComponent: root.wavy ? waveComp : lineComp color: DynamicColors.palette.m3primary
implicitWidth: root.handle.x - root.implicitHeight / 2
radius: Appearance.rounding.full
topRightRadius: root.implicitHeight / 6
} }
Component {
id: lineComp
CustomRect { CustomRect {
bottomRightRadius: Appearance.rounding.extraSmall / 2 anchors.bottom: parent.bottom
color: root.fgColor anchors.right: parent.right
implicitHeight: root.height anchors.top: parent.top
implicitWidth: root.filledWidth bottomLeftRadius: root.implicitHeight / 6
radius: Appearance.rounding.small color: DynamicColors.tPalette.m3surfaceContainer
topRightRadius: Appearance.rounding.extraSmall / 2 implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
radius: Appearance.rounding.full
topLeftRadius: root.implicitHeight / 6
} }
} }
handle: CustomRect {
Component { anchors.verticalCenter: parent.verticalCenter
id: waveComp color: DynamicColors.palette.m3primary
implicitHeight: 15
WavyLine { implicitWidth: 5
color: root.fgColor radius: Appearance.rounding.full
frequency: root.waveFrequency x: root.visualPosition * root.availableWidth - implicitWidth / 2
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
Behavior on filledWidth {
id: widthBehavior
Anim {
}
}
Component.onCompleted: filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos)
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea { MouseArea {
id: mouse acceptedButtons: Qt.NoButton
anchors.fill: parent
property real dragMovement cursorShape: Qt.PointingHandCursor
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
} }
} }
} }
+13 -12
View File
@@ -28,7 +28,6 @@ RowLayout {
CustomTextField { CustomTextField {
id: textField id: textField
color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.normal leftPadding: Appearance.padding.normal
@@ -37,7 +36,7 @@ RowLayout {
text: root.isEditing ? text : root.displayText text: root.isEditing ? text : root.displayText
background: CustomRect { background: CustomRect {
color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow color: DynamicColors.tPalette.m3surfaceContainerHigh
implicitWidth: 100 implicitWidth: 100
radius: Appearance.rounding.full radius: Appearance.rounding.full
} }
@@ -86,7 +85,7 @@ RowLayout {
CustomRect { CustomRect {
id: upButton id: upButton
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) color: DynamicColors.palette.m3primary
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: Appearance.rounding.full radius: Appearance.rounding.full
@@ -94,9 +93,7 @@ RowLayout {
StateLayer { StateLayer {
id: upState id: upState
color: DynamicColors.palette.m3onPrimary function onClicked(): void {
onClicked: {
let newValue = Math.min(root.max, root.value + root.step); let newValue = Math.min(root.max, root.value + root.step);
// Round to avoid floating point precision errors // Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0; const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
@@ -105,6 +102,9 @@ RowLayout {
root.displayText = newValue.toString(); root.displayText = newValue.toString();
root.valueModified(newValue); root.valueModified(newValue);
} }
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start() onPressAndHold: timer.start()
onReleased: timer.stop() onReleased: timer.stop()
} }
@@ -113,13 +113,13 @@ RowLayout {
id: upIcon id: upIcon
anchors.centerIn: parent anchors.centerIn: parent
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5) color: DynamicColors.palette.m3onPrimary
text: "keyboard_arrow_up" text: "keyboard_arrow_up"
} }
} }
CustomRect { CustomRect {
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) color: DynamicColors.palette.m3primary
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: Appearance.rounding.full radius: Appearance.rounding.full
@@ -127,9 +127,7 @@ RowLayout {
StateLayer { StateLayer {
id: downState id: downState
color: DynamicColors.palette.m3onPrimary function onClicked(): void {
onClicked: {
let newValue = Math.max(root.min, root.value - root.step); let newValue = Math.max(root.min, root.value - root.step);
// Round to avoid floating point precision errors // Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0; const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
@@ -138,6 +136,9 @@ RowLayout {
root.displayText = newValue.toString(); root.displayText = newValue.toString();
root.valueModified(newValue); root.valueModified(newValue);
} }
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start() onPressAndHold: timer.start()
onReleased: timer.stop() onReleased: timer.stop()
} }
@@ -146,7 +147,7 @@ RowLayout {
id: downIcon id: downIcon
anchors.centerIn: parent anchors.centerIn: parent
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5) color: DynamicColors.palette.m3onPrimary
text: "keyboard_arrow_down" text: "keyboard_arrow_down"
} }
} }
+67 -34
View File
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Config import qs.Config
import qs.Helpers
Row { Row {
id: root id: root
@@ -11,46 +12,66 @@ Row {
} }
property alias active: menu.active property alias active: menu.active
property color colour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property bool disabled property bool disabled
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1) property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38) property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded property alias expanded: menu.expanded
property string fallbackIcon property string fallbackIcon
property string fallbackText property string fallbackText
property real horizontalPadding: Appearance.padding.larger property real horizontalPadding: Appearance.padding.normal
readonly property alias iconLabel: iconLabel property alias iconLabel: iconLabel
readonly property alias label: label property alias label: label
readonly property alias menu: menu property alias menu: menu
property alias menuItems: menu.items property alias menuItems: menu.items
property bool menuOnTop property bool menuOnTop
property real minLeftWidth property alias stateLayer: stateLayer
readonly property alias stateLayer: stateLayer property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
property color textColour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.small property real verticalPadding: Appearance.padding.smaller
spacing: Math.floor(Appearance.spacing.extraSmall) function closeDropdown(): void {
SettingsDropdowns.close(menu);
}
function openDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.open(menu, root);
}
function toggleDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.toggle(menu, root);
}
spacing: Math.floor(Appearance.spacing.small / 2)
onExpandedChanged: {
if (!expanded)
SettingsDropdowns.forget(menu);
}
CustomRect { CustomRect {
bottomRightRadius: Appearance.rounding.small / 2 bottomRightRadius: Appearance.rounding.small / 2
color: root.disabled ? root.disabledColour : root.colour color: root.disabled ? root.disabledColor : root.color
implicitHeight: expandBtn.implicitHeight implicitHeight: expandBtn.implicitHeight
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2) implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2 topRightRadius: Appearance.rounding.small / 2
StateLayer { StateLayer {
id: stateLayer id: stateLayer
bottomRightRadius: parent.bottomRightRadius function onClicked(): void {
color: root.textColour root.active?.clicked();
disabled: root.disabled }
topRightRadius: parent.topRightRadius
onClicked: root.active?.clicked() color: root.textColor
disabled: root.disabled
rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius
} }
RowLayout { RowLayout {
@@ -65,7 +86,7 @@ Row {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
animate: true animate: true
color: root.disabled ? root.disabledTextColour : root.textColour color: root.disabled ? root.disabledTextColor : root.textColor
fill: 1 fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon text: root.active?.activeIcon ?? root.fallbackIcon
} }
@@ -77,12 +98,12 @@ Row {
Layout.preferredWidth: implicitWidth Layout.preferredWidth: implicitWidth
animate: true animate: true
clip: true clip: true
color: root.disabled ? root.disabledTextColour : root.textColour color: root.disabled ? root.disabledTextColor : root.textColor
text: root.active?.activeText ?? root.fallbackText text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth { Behavior on Layout.preferredWidth {
Anim { Anim {
type: Anim.Emphasized easing.bezierCurve: Appearance.anim.curves.emphasized
} }
} }
} }
@@ -95,7 +116,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2 property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad bottomLeftRadius: rad
color: root.disabled ? root.disabledColour : root.colour color: root.disabled ? root.disabledColor : root.color
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2 implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -109,12 +130,14 @@ Row {
StateLayer { StateLayer {
id: expandStateLayer id: expandStateLayer
color: root.textColour function onClicked(): void {
root.toggleDropdown();
}
color: root.textColor
disabled: root.disabled disabled: root.disabled
rect.bottomLeftRadius: parent.bottomLeftRadius rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
} }
MaterialIcon { MaterialIcon {
@@ -122,7 +145,7 @@ Row {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4) anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: root.disabled ? root.disabledTextColour : root.textColour color: root.disabled ? root.disabledTextColor : root.textColor
rotation: root.expanded ? 180 : 0 rotation: root.expanded ? 180 : 0
text: "expand_more" text: "expand_more"
@@ -135,14 +158,24 @@ Row {
} }
} }
} }
}
Menu { Menu {
id: menu id: menu
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom anchors.bottomMargin: Appearance.spacing.small
attachTo: expandBtn anchors.right: parent.right
marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1) anchors.top: parent.bottom
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top anchors.topMargin: Appearance.spacing.small
states: State {
when: root.menuOnTop
AnchorChanges {
anchors.bottom: expandBtn.top
anchors.top: undefined
target: menu
}
}
}
} }
} }
+8 -19
View File
@@ -8,32 +8,19 @@ Item {
id: root id: root
property alias active: splitButton.active property alias active: splitButton.active
property alias buttonAlias: splitButton property bool enabled: true
property alias expanded: splitButton.expanded property alias expanded: splitButton.expanded
property int expandedZ: 100 property int expandedZ: 100
required property string label required property string label
property alias menuItems: splitButton.menuItems property alias menuItems: splitButton.menuItems
property bool shouldBeActive: true
property alias type: splitButton.type property alias type: splitButton.type
signal selected(item: MenuItem) signal selected(item: MenuItem)
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
clip: false clip: false
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2 z: root.expanded ? expandedZ : -1
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout { RowLayout {
id: row id: row
@@ -49,6 +36,7 @@ Item {
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: root.label text: root.label
z: root.expanded ? root.expandedZ : -1
} }
CustomSplitButton { CustomSplitButton {
@@ -56,13 +44,14 @@ Item {
enabled: root.enabled enabled: root.enabled
type: CustomSplitButton.Filled type: CustomSplitButton.Filled
z: 2 z: root.expanded ? root.expandedZ : -1
menu.onItemSelected: item => { menu.onItemSelected: item => {
root.selected(item); root.selected(item);
splitButton.closeDropdown();
} }
stateLayer.onClicked: { stateLayer.onClicked: {
splitButton.expanded = !splitButton.expanded; splitButton.toggleDropdown();
} }
} }
} }
+16 -18
View File
@@ -1,6 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Shapes
import QtQuick.Templates import QtQuick.Templates
import QtQuick.Shapes
import qs.Config import qs.Config
Switch { Switch {
@@ -12,41 +12,38 @@ Switch {
implicitWidth: implicitIndicatorWidth implicitWidth: implicitIndicatorWidth
indicator: CustomRect { indicator: CustomRect {
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2 implicitHeight: 13 + 7 * 2
implicitWidth: implicitHeight * 1.7 implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full radius: Appearance.rounding.full
CustomRect { CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1) color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall implicitHeight: parent.implicitHeight - 10
implicitWidth: nonAnimWidth implicitWidth: nonAnimWidth
radius: Appearance.rounding.full radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2 x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
Behavior on implicitWidth { Behavior on implicitWidth {
Anim { Anim {
type: Anim.FastSpatial
} }
} }
Behavior on x { Behavior on x {
Anim { Anim {
type: Anim.FastSpatial
} }
} }
CustomRect { CustomRect {
anchors.fill: parent anchors.fill: parent
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0 opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius radius: parent.radius
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects
} }
} }
} }
@@ -66,14 +63,14 @@ Switch {
} }
property point end2: { property point end2: {
if (root.pressed) if (root.pressed)
return Qt.point(width * 0.8, height / 2); return Qt.point(width, height / 2);
if (root.checked) if (root.checked)
return Qt.point(width * 0.85, height * 0.2); return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15); return Qt.point(width * 0.85, height * 0.15);
} }
property point start1: { property point start1: {
if (root.pressed) if (root.pressed)
return Qt.point(width * 0.2, height / 2); return Qt.point(width * 0.1, height / 2);
if (root.checked) if (root.checked)
return Qt.point(width * 0.15, height / 2); return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15); return Qt.point(width * 0.15, height * 0.15);
@@ -91,7 +88,7 @@ Switch {
anchors.centerIn: parent anchors.centerIn: parent
asynchronous: true asynchronous: true
height: parent.implicitHeight - Appearance.padding.larger height: parent.implicitHeight - Appearance.padding.small * 2
preferredRendererType: Shape.CurveRenderer preferredRendererType: Shape.CurveRenderer
width: height width: height
@@ -113,11 +110,11 @@ Switch {
} }
ShapePath { ShapePath {
capStyle: ShapePath.RoundCap capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent" fillColor: "transparent"
startX: icon.start1.x startX: icon.start1.x
startY: icon.start1.y startY: icon.start1.y
strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15 strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor { Behavior on strokeColor {
@@ -151,7 +148,8 @@ Switch {
} }
component PropAnim: PropertyAnimation { component PropAnim: PropertyAnimation {
duration: Appearance.anim.durations.expressiveFastSpatial duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial easing.bezierCurve: MaterialEasing.expressiveEffects
easing.type: Easing.BezierSpline
} }
} }
-1
View File
@@ -15,7 +15,6 @@ Text {
color: DynamicColors.palette.m3onSurface color: DynamicColors.palette.m3onSurface
font.family: Appearance.font.family.sans font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.normal font.pointSize: Appearance.font.size.normal
linkColor: DynamicColors.palette.m3onPrimaryFixedVariant
renderType: Text.NativeRendering renderType: Text.NativeRendering
textFormat: Text.PlainText textFormat: Text.PlainText
+1 -2
View File
@@ -1,6 +1,6 @@
import qs.Config
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import qs.Config
RectangularShadow { RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level] property real dp: [0, 1, 3, 6, 8, 12][level]
@@ -13,7 +13,6 @@ RectangularShadow {
Behavior on dp { Behavior on dp {
Anim { Anim {
type: Anim.SlowEffects
} }
} }
} }
-33
View File
@@ -1,33 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Config
IconButton {
id: root
required property bool shouldBeVisible
opacity: 0
scale: 0
visible: root.scale > 0
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
Behavior on scale {
Anim {
}
}
onShouldBeVisibleChanged: {
if (root.shouldBeVisible) {
root.opacity = 1;
root.scale = 1;
} else {
root.opacity = 0;
root.scale = 0;
}
}
}
+6 -5
View File
@@ -41,11 +41,12 @@ CustomRect {
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2 implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
Behavior on radius { Behavior on radius {
Anim { Anim {
id: radiusAnim id: radiusAnim
} }
} }
@@ -54,14 +55,14 @@ CustomRect {
StateLayer { StateLayer {
id: stateLayer id: stateLayer
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour function onClicked(): void {
disabled: root.disabled
onClicked: {
if (root.toggle) if (root.toggle)
root.internalChecked = !root.internalChecked; root.internalChecked = !root.internalChecked;
root.clicked(); root.clicked();
} }
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
} }
MaterialIcon { MaterialIcon {
-2
View File
@@ -102,7 +102,6 @@ Item {
animate: root.animate animate: root.animate
animateProp: "opacity" animateProp: "opacity"
color: root.color color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text text: elideText.text
} }
@@ -112,7 +111,6 @@ Item {
animate: root.animate animate: root.animate
animateProp: "opacity" animateProp: "opacity"
color: root.color color: root.color
font.pointSize: elideText.font.pointSize
text: t1.text text: t1.text
x: t1.width + root.gap x: t1.width + root.gap
} }
+36 -96
View File
@@ -2,107 +2,49 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import qs.Config import qs.Config
import qs.Drawers
MouseArea { Elevation {
id: root id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded property bool expanded
property list<MenuItem> items property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem) signal itemSelected(item: MenuItem)
anchors.fill: parent implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0
enabled: expanded implicitWidth: Math.max(200, column.implicitWidth)
layer.enabled: opacity < 1 level: 2
opacity: expanded ? 1 : 0 opacity: root.expanded ? 1 : 0
parent: { radius: Appearance.rounding.normal
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on implicitHeight {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects duration: Appearance.anim.durations.expressiveDefaultSpatial
} }
} }
onClicked: expanded = false CustomClippingRect {
TransformWatcher {
id: watcher
a: root.parent
b: root.attachTo
}
Elevation {
id: menu
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainerLow color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius radius: parent.radius
ColumnLayout { ColumnLayout {
id: column id: column
anchors.fill: parent anchors.left: parent.left
anchors.margins: Appearance.padding.extraSmall anchors.right: parent.right
spacing: Appearance.spacing.extraSmall anchors.verticalCenter: parent.verticalCenter
spacing: 5
Repeater { Repeater {
id: repeater
model: root.items model: root.items
CustomRect { CustomRect {
@@ -113,56 +55,55 @@ MouseArea {
required property MenuItem modelData required property MenuItem modelData
Layout.fillWidth: true Layout.fillWidth: true
color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0) implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2 implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
Behavior on radius { CustomRect {
Anim { anchors.fill: parent
} anchors.leftMargin: Appearance.padding.small
} anchors.rightMargin: Appearance.padding.small
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
radius: Appearance.rounding.normal - Appearance.padding.small
StateLayer { StateLayer {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface function onClicked(): void {
disabled: !root.expanded
onClicked: {
root.itemSelected(item.modelData); root.itemSelected(item.modelData);
root.active = item.modelData; root.active = item.modelData;
item.modelData.clicked();
root.expanded = false; root.expanded = false;
} }
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded
}
} }
RowLayout { RowLayout {
id: menuOptionRow id: menuOptionRow
anchors.fill: parent anchors.fill: parent
anchors.margins: Appearance.padding.larger anchors.margins: Appearance.padding.normal
spacing: Appearance.spacing.small spacing: Appearance.spacing.small
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon text: item.modelData.icon
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text text: item.modelData.text
} }
Loader { Loader {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0 active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active visible: active
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.trailingIcon text: item.modelData.trailingIcon
} }
} }
@@ -172,4 +113,3 @@ MouseArea {
} }
} }
} }
}
-2
View File
@@ -29,7 +29,6 @@ Elevation {
level: root.expanded ? 2 : 0 level: root.expanded ? 2 : 0
radius: itemHeight / 2 radius: itemHeight / 2
visible: implicitHeight > 0 visible: implicitHeight > 0
z: root.expanded ? 100 : 0
Behavior on implicitHeight { Behavior on implicitHeight {
Anim { Anim {
@@ -69,7 +68,6 @@ Elevation {
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius radius: parent.radius
z: root.z
// Main visible spinner: normal/outside text color // Main visible spinner: normal/outside text color
PathView { PathView {
+64 -169
View File
@@ -1,53 +1,15 @@
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import qs.Config import qs.Config
import QtQuick
MouseArea { MouseArea {
id: root id: root
property alias bottomLeftRadius: base.bottomLeftRadius property color color: DynamicColors.palette.m3onSurface
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
property bool disabled property bool disabled
readonly property real endRadius: { property real radius: parent?.radius ?? 0
const d1 = distSq(0, 0); property alias rect: hoverLayer
const d2 = distSq(width, 0);
const d3 = distSq(0, height);
const d4 = distSq(width, height);
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
property alias radius: base.radius
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
function clamp(r: real): real { function onClicked(): void {
return Math.max(0, Math.min(r, width / 2, height / 2));
}
function distSq(x: real, y: real): real {
return (pressX - x) ** 2 + (pressY - y) ** 2;
}
function press(x: real, y: real): void {
pressX = x;
pressY = y;
fadeAnim.complete();
circleRadius = 0;
circle.opacity = 0.1;
rippleAnim.restart();
endRadiusAtPress = endRadius;
} }
anchors.fill: parent anchors.fill: parent
@@ -55,146 +17,79 @@ MouseArea {
enabled: !disabled enabled: !disabled
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity {
Anim {
type: Anim.DefaultEffects
}
}
onCircleRadiusChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onClicked: event => !disabled && onClicked(event) onClicked: event => !disabled && onClicked(event)
onManualPressOverrideChanged: { onPressed: event => {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running) if (disabled)
fadeAnim.start(); return;
}
onPressed: e => press(e.x, e.y) rippleAnim.x = event.x;
onPressedChanged: { rippleAnim.y = event.y;
if (!(pressed || manualPressOverride) && !rippleAnim.running && circle.opacity > 0)
fadeAnim.start(); const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
} }
Anim { SequentialAnimation {
id: rippleAnim id: rippleAnim
alwaysRunToEnd: true property real radius
duration: Appearance.anim.durations.expressiveSlowEffects * 2 property real x
easing.bezierCurve: Appearance.anim.curves.standard property real y
property: "circleRadius"
target: root PropertyAction {
to: root.endRadius property: "x"
target: ripple
value: rippleAnim.x
}
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
} }
Anim { Anim {
id: fadeAnim easing.bezierCurve: MaterialEasing.standardDecel
from: 0
property: "opacity" properties: "implicitWidth,implicitHeight"
target: circle target: ripple
to: 0 to: rippleAnim.radius * 2
type: Anim.SlowEffects
} }
Anim {
property: "opacity"
target: ripple
to: 0
}
}
CustomClippingRect {
id: hoverLayer
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
CustomRect { CustomRect {
id: base id: ripple
anchors.fill: parent border.pixelAligned: false
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0 color: root.color
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
}
Shape {
id: circle
anchors.fill: parent
opacity: 0 opacity: 0
preferredRendererType: Shape.CurveRenderer radius: Appearance.rounding.full
ShapePath { transform: Translate {
fillColor: base.color x: -ripple.width / 2
startX: root.clamp(base.topLeftRadius) y: -ripple.height / 2
startY: 0
strokeColor: "transparent"
strokeWidth: 0
fillGradient: RadialGradient {
centerRadius: root.circleRadius
centerX: root.pressX
centerY: root.pressY
focalX: centerX
focalY: centerY
GradientStop {
color: Qt.alpha(base.color, 1)
position: 0
}
GradientStop {
color: Qt.alpha(base.color, 1)
position: ZUtils.clamp(1 - 0.2 * root.endRadius / root.circleRadius, 0.01, 0.99)
}
GradientStop {
color: Qt.alpha(base.color, ZUtils.clamp((root.circleRadius / root.endRadius - 0.9) / 0.1, 0, 1))
position: 1
}
}
PathLine {
x: root.width - root.clamp(base.topRightRadius)
y: 0
}
PathArc {
radiusX: root.clamp(base.topRightRadius)
radiusY: root.clamp(base.topRightRadius)
relativeX: root.clamp(base.topRightRadius)
relativeY: root.clamp(base.topRightRadius)
}
PathLine {
x: root.width
y: root.height - root.clamp(base.bottomRightRadius)
}
PathArc {
radiusX: root.clamp(base.bottomRightRadius)
radiusY: root.clamp(base.bottomRightRadius)
relativeX: -root.clamp(base.bottomRightRadius)
relativeY: root.clamp(base.bottomRightRadius)
}
PathLine {
x: root.clamp(base.bottomLeftRadius)
y: root.height
}
PathArc {
radiusX: root.clamp(base.bottomLeftRadius)
radiusY: root.clamp(base.bottomLeftRadius)
relativeX: -root.clamp(base.bottomLeftRadius)
relativeY: -root.clamp(base.bottomLeftRadius)
}
PathLine {
x: 0
y: root.clamp(base.topLeftRadius)
}
PathArc {
radiusX: root.clamp(base.topLeftRadius)
radiusY: root.clamp(base.topLeftRadius)
x: root.clamp(base.topLeftRadius)
y: 0
} }
} }
} }
+2 -1
View File
@@ -4,9 +4,10 @@ import Quickshell
Singleton { Singleton {
readonly property AppearanceConf.Anim anim: Config.appearance.anim readonly property AppearanceConf.Anim anim: Config.appearance.anim
readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
+7 -24
View File
@@ -3,8 +3,6 @@ import Quickshell.Io
JsonObject { JsonObject {
property Anim anim: Anim { property Anim anim: Anim {
} }
property Deform deform: Deform {
}
property FontStuff font: FontStuff { property FontStuff font: FontStuff {
} }
property Padding padding: Padding { property Padding padding: Padding {
@@ -28,13 +26,9 @@ JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1] property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1] property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1] property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1] property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1] property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1] property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1] property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1] property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
@@ -42,18 +36,13 @@ JsonObject {
component AnimDurations: JsonObject { component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale property int extraLarge: 1000 * scale
property int large: 600 * scale property int large: 600 * scale
property int normal: 400 * scale property int normal: 400 * scale
property real scale: 1 property real scale: 1
property int small: 200 * scale property int small: 200 * scale
} }
component Deform: JsonObject {
property real scale: 1
}
component FontFamily: JsonObject { component FontFamily: JsonObject {
property string clock: "Rubik" property string clock: "Rubik"
property string material: "Material Symbols Rounded" property string material: "Material Symbols Rounded"
@@ -63,8 +52,7 @@ JsonObject {
component FontSize: JsonObject { component FontSize: JsonObject {
property int extraLarge: 28 * scale property int extraLarge: 28 * scale
property int large: 18 * scale property int large: 18 * scale
property int larger: 16 * scale property int larger: 15 * scale
property int medium: 14 * scale
property int normal: 13 * scale property int normal: 13 * scale
property real scale: 1 property real scale: 1
property int small: 11 * scale property int small: 11 * scale
@@ -77,33 +65,28 @@ JsonObject {
} }
} }
component Padding: JsonObject { component Padding: JsonObject {
property int extraLargeIncreased: 32 * scale property int large: 15 * scale
property int extraSmall: 4 * scale
property int large: 16 * scale
property int larger: 12 * scale property int larger: 12 * scale
property int normal: 8 * scale property int normal: 10 * scale
property real scale: 1 property real scale: 1
property int small: 5 * scale property int small: 5 * scale
property int smaller: 7 * scale property int smaller: 7 * scale
property int smallest: 2 * scale property int smallest: 2 * scale
} }
component Rounding: JsonObject { component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale property int full: 1000 * scale
property int large: 24 * scale property int large: 25 * scale
property int medium: 16 * scale property int normal: 17 * scale
property int normal: 18 * scale
property real scale: 1 property real scale: 1
property int small: 12 * scale property int small: 12 * scale
property int smallest: 8 * scale property int smallest: 8 * scale
} }
component Spacing: JsonObject { component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale property int large: 20 * scale
property int larger: 16 * scale property int larger: 15 * scale
property int normal: 12 * scale property int normal: 12 * scale
property real scale: 1 property real scale: 1
property int small: 8 * scale property int small: 7 * scale
property int smaller: 10 * scale property int smaller: 10 * scale
} }
component Transparency: JsonObject { component Transparency: JsonObject {
-7
View File
@@ -4,11 +4,4 @@ import qs.Config
JsonObject { JsonObject {
property bool enabled: true property bool enabled: true
property int wallFadeDuration: MaterialEasing.standardTime property int wallFadeDuration: MaterialEasing.standardTime
property real alignX: 0.5
property real alignY: 0.5
property real zoom: 1.0
property real sourceClipX: 0
property real sourceClipY: 0
property real sourceClipW: 0
property real sourceClipH: 0
} }
+11 -10
View File
@@ -8,6 +8,10 @@ JsonObject {
id: "workspaces", id: "workspaces",
enabled: true enabled: true
}, },
{
id: "audio",
enabled: true
},
{ {
id: "media", id: "media",
enabled: true enabled: true
@@ -20,6 +24,10 @@ JsonObject {
id: "updates", id: "updates",
enabled: true enabled: true
}, },
{
id: "dash",
enabled: true
},
{ {
id: "spacer", id: "spacer",
enabled: true enabled: true
@@ -33,12 +41,12 @@ JsonObject {
enabled: true enabled: true
}, },
{ {
id: "hyprsunset", id: "tray",
enabled: true enabled: true
}, },
{ {
id: "tray", id: "upower",
enabled: true enabled: false
}, },
{ {
id: "network", id: "network",
@@ -54,13 +62,9 @@ JsonObject {
}, },
] ]
property int height: 34 property int height: 34
property bool hideWhenNotif: false
property Popouts popouts: Popouts { property Popouts popouts: Popouts {
} }
property int rounding: 8 property int rounding: 8
property int smoothing: 32
property Tray tray: Tray {
}
component Popouts: JsonObject { component Popouts: JsonObject {
property bool activeWindow: true property bool activeWindow: true
@@ -71,7 +75,4 @@ JsonObject {
property bool tray: true property bool tray: true
property bool upower: true property bool upower: true
} }
component Tray: JsonObject {
property int trayIconSize: 24
}
} }
-8
View File
@@ -1,13 +1,5 @@
import Quickshell.Io import Quickshell.Io
JsonObject { JsonObject {
property Presets presets: Presets {
}
property string schemeType: "vibrant" property string schemeType: "vibrant"
component Presets: JsonObject {
property string accent: ""
property string name: ""
property string variant: ""
}
} }
+7 -55
View File
@@ -4,6 +4,8 @@ import Quickshell
import Quickshell.Io import Quickshell.Io
import ZShell import ZShell
import QtQuick import QtQuick
import qs.Helpers
import qs.Paths
Singleton { Singleton {
id: root id: root
@@ -21,7 +23,6 @@ Singleton {
property alias osd: adapter.osd property alias osd: adapter.osd
property alias overview: adapter.overview property alias overview: adapter.overview
property bool recentlySaved: false property bool recentlySaved: false
property alias screenshot: adapter.screenshot
property alias services: adapter.services property alias services: adapter.services
property alias sidebar: adapter.sidebar property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities property alias utilities: adapter.utilities
@@ -47,9 +48,6 @@ Singleton {
padding: { padding: {
scale: appearance.padding.scale scale: appearance.padding.scale
}, },
deform: {
scale: appearance.deform.scale
},
font: { font: {
family: { family: {
sans: appearance.font.family.sans, sans: appearance.font.family.sans,
@@ -79,28 +77,16 @@ Singleton {
function serializeBackground(): var { function serializeBackground(): var {
return { return {
wallFadeDuration: background.wallFadeDuration, wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled, enabled: background.enabled
alignX: background.alignX,
sourceClipX: background.sourceClipX,
sourceClipY: background.sourceClipY,
sourceClipW: background.sourceClipW,
sourceClipH: background.sourceClipH,
alignY: background.alignY,
zoom: background.zoom
}; };
} }
function serializeBar(): var { function serializeBar(): var {
return { return {
autoHide: barConfig.autoHide, autoHide: barConfig.autoHide,
hideWhenNotif: barConfig.hideWhenNotif,
rounding: barConfig.rounding, rounding: barConfig.rounding,
border: barConfig.border, border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height, height: barConfig.height,
tray: {
trayIconSize: barConfig.tray.trayIconSize
},
popouts: { popouts: {
tray: barConfig.popouts.tray, tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio, audio: barConfig.popouts.audio,
@@ -116,12 +102,7 @@ Singleton {
function serializeColors(): var { function serializeColors(): var {
return { return {
schemeType: colors.schemeType, schemeType: colors.schemeType
presets: {
name: colors.presets.name,
variant: colors.presets.variant,
accent: colors.presets.accent
}
}; };
} }
@@ -140,8 +121,7 @@ Singleton {
background: serializeBackground(), background: serializeBackground(),
launcher: serializeLauncher(), launcher: serializeLauncher(),
colors: serializeColors(), colors: serializeColors(),
dock: serializeDock(), dock: serializeDock()
screenshot: serializeScreenshot()
}; };
} }
@@ -192,16 +172,11 @@ Singleton {
return { return {
logo: general.logo, logo: general.logo,
wallpaperPath: general.wallpaperPath, wallpaperPath: general.wallpaperPath,
username: general.username,
desktopIcons: general.desktopIcons, desktopIcons: general.desktopIcons,
dateFormat: general.dateFormat,
color: { color: {
mode: general.color.mode, mode: general.color.mode,
smart: general.color.smart, smart: general.color.smart,
scheduleDark: general.color.scheduleDark,
scheduleHyprsunset: general.color.scheduleHyprsunset,
scheduleHyprsunsetStart: general.color.scheduleHyprsunsetStart,
hyprsunsetTemp: general.color.hyprsunsetTemp,
scheduleHyprsunsetEnd: general.color.scheduleHyprsunsetEnd,
schemeGeneration: general.color.schemeGeneration, schemeGeneration: general.color.schemeGeneration,
scheduleDarkStart: general.color.scheduleDarkStart, scheduleDarkStart: general.color.scheduleDarkStart,
scheduleDarkEnd: general.color.scheduleDarkEnd, scheduleDarkEnd: general.color.scheduleDarkEnd,
@@ -215,10 +190,6 @@ Singleton {
}, },
idle: { idle: {
timeouts: general.idle.timeouts timeouts: general.idle.timeouts
},
battery: {
popupThresholds: general.battery.popupThresholds,
critPerc: general.battery.critPerc
} }
}; };
} }
@@ -227,7 +198,6 @@ Singleton {
return { return {
maxAppsShown: launcher.maxAppsShown, maxAppsShown: launcher.maxAppsShown,
maxWallpapers: launcher.maxWallpapers, maxWallpapers: launcher.maxWallpapers,
uwsm: launcher.uwsm,
actionPrefix: launcher.actionPrefix, actionPrefix: launcher.actionPrefix,
specialPrefix: launcher.specialPrefix, specialPrefix: launcher.specialPrefix,
useFuzzy: { useFuzzy: {
@@ -251,8 +221,6 @@ Singleton {
return { return {
recolorLogo: lock.recolorLogo, recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint, enableFprint: lock.enableFprint,
showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries, maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount, blurAmount: lock.blurAmount,
sizes: { sizes: {
@@ -294,24 +262,9 @@ Singleton {
}; };
} }
function serializeScreenshot(): var {
return {
enable_pp: screenshot.enable_pp,
mode: screenshot.mode,
radius: screenshot.radius,
shadow: screenshot.shadow,
rounding: screenshot.rounding,
shadow_blur: screenshot.shadow_blur,
shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y
};
}
function serializeServices(): var { function serializeServices(): var {
return { return {
weatherLocation: services.weatherLocation, weatherLocation: services.weatherLocation,
updates: services.updates,
useFahrenheit: services.useFahrenheit, useFahrenheit: services.useFahrenheit,
ddcutilService: services.ddcutilService, ddcutilService: services.ddcutilService,
useTwelveHourClock: services.useTwelveHourClock, useTwelveHourClock: services.useTwelveHourClock,
@@ -364,6 +317,7 @@ Singleton {
ElapsedTimer { ElapsedTimer {
id: timer id: timer
} }
Timer { Timer {
@@ -461,8 +415,6 @@ Singleton {
} }
property Overview overview: Overview { property Overview overview: Overview {
} }
property Screenshot screenshot: Screenshot {
}
property Services services: Services { property Services services: Services {
} }
property SidebarConfig sidebar: SidebarConfig { property SidebarConfig sidebar: SidebarConfig {
+64 -12
View File
@@ -29,10 +29,9 @@ Singleton {
readonly property alias wallLuminance: analyser.luminance readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color { function alterColor(c: color, a: real, layer: int): color {
const initLuminance = getLuminance(c); const luminance = getLuminance(c);
const luminance = Math.max(initLuminance, 0.001);
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (0.2 + 0.3 * (1 - transparency.base)) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5)); const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const scale = (luminance + offset) / luminance; const scale = (luminance + offset) / luminance;
const r = Math.max(0, Math.min(1, c.r * scale)); const r = Math.max(0, Math.min(1, c.r * scale));
const g = Math.max(0, Math.min(1, c.g * scale)); const g = Math.max(0, Math.min(1, c.g * scale));
@@ -85,10 +84,6 @@ Singleton {
Config.save(); Config.save();
} }
function swapRG(c: color): color {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
FileView { FileView {
path: "/etc/zshell-greeter/scheme.json" path: "/etc/zshell-greeter/scheme.json"
watchChanges: true watchChanges: true
@@ -100,9 +95,69 @@ Singleton {
ImageAnalyser { ImageAnalyser {
id: analyser id: analyser
source: WallpaperPath.lockscreenBg source: WallpaperPath.currentWallpaperPath
} }
component M3MaccchiatoPalette: QtObject {
property color m3background: "#131317"
property color m3error: "#ffb4ab"
property color m3errorContainer: "#93000a"
property color m3inverseOnSurface: "#303034"
property color m3inversePrimary: "#525b92"
property color m3inverseSurface: "#e4e1e7"
property color m3neutral_paletteKeyColor: "#77767b"
property color m3neutral_variant_paletteKeyColor: "#767680"
property color m3onBackground: "#e4e1e7"
property color m3onError: "#690005"
property color m3onErrorContainer: "#ffdad6"
property color m3onPrimary: "#232c60"
property color m3onPrimaryContainer: "#ffffff"
property color m3onPrimaryFixed: "#0b154b"
property color m3onPrimaryFixedVariant: "#3a4378"
property color m3onSecondary: "#2c2f44"
property color m3onSecondaryContainer: "#b1b3ce"
property color m3onSecondaryFixed: "#171a2e"
property color m3onSecondaryFixedVariant: "#42455c"
property color m3onSuccess: "#213528"
property color m3onSuccessContainer: "#D1E9D6"
property color m3onSurface: "#e4e1e7"
property color m3onSurfaceVariant: "#c6c5d1"
property color m3onTertiary: "#4c1f48"
property color m3onTertiaryContainer: "#000000"
property color m3onTertiaryFixed: "#340831"
property color m3onTertiaryFixedVariant: "#66365f"
property color m3outline: "#90909a"
property color m3outlineVariant: "#46464f"
property color m3primary: "#bac3ff"
property color m3primaryContainer: "#6a73ac"
property color m3primaryFixed: "#dee0ff"
property color m3primaryFixedDim: "#bac3ff"
property color m3primary_paletteKeyColor: "#6a73ac"
property color m3scrim: "#000000"
property color m3secondary: "#c3c5e0"
property color m3secondaryContainer: "#42455c"
property color m3secondaryFixed: "#dfe1fd"
property color m3secondaryFixedDim: "#c3c5e0"
property color m3secondary_paletteKeyColor: "#72758e"
property color m3shadow: "#000000"
property color m3success: "#B5CCBA"
property color m3successContainer: "#374B3E"
property color m3surface: "#131317"
property color m3surfaceBright: "#39393d"
property color m3surfaceContainer: "#1f1f23"
property color m3surfaceContainerHigh: "#2a2a2e"
property color m3surfaceContainerHighest: "#353438"
property color m3surfaceContainerLow: "#1b1b1f"
property color m3surfaceContainerLowest: "#0e0e12"
property color m3surfaceDim: "#131317"
property color m3surfaceTint: "#bac3ff"
property color m3surfaceVariant: "#46464f"
property color m3tertiary: "#f1b3e5"
property color m3tertiaryContainer: "#b77ead"
property color m3tertiaryFixed: "#ffd7f4"
property color m3tertiaryFixedDim: "#f1b3e5"
property color m3tertiary_paletteKeyColor: "#9b6592"
}
component M3Palette: QtObject { component M3Palette: QtObject {
property color m3background: "#191114" property color m3background: "#191114"
property color m3error: "#ffb4ab" property color m3error: "#ffb4ab"
@@ -224,11 +279,8 @@ Singleton {
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor) readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
} }
component Transparency: QtObject { component Transparency: QtObject {
readonly property real base: Math.max(0, Math.min(1, Appearance.transparency.base - (root.light ? 0.1 : 0))) readonly property real base: Appearance.transparency.base - (root.light ? 0.1 : 0)
readonly property bool enabled: Appearance.transparency.enabled readonly property bool enabled: Appearance.transparency.enabled
readonly property real layers: Appearance.transparency.layers readonly property real layers: Appearance.transparency.layers
onBaseChanged: debounceTimer.restart()
onEnabledChanged: debounceTimer.restart()
} }
} }
+1 -19
View File
@@ -4,15 +4,13 @@ import Quickshell
JsonObject { JsonObject {
property Apps apps: Apps { property Apps apps: Apps {
} }
property Battery battery: Battery {
}
property Color color: Color { property Color color: Color {
} }
property string dateFormat: "ddd d MMM - hh:mm:ss"
property bool desktopIcons: false property bool desktopIcons: false
property Idle idle: Idle { property Idle idle: Idle {
} }
property string logo: "" property string logo: ""
property string username: ""
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers" property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
component Apps: JsonObject { component Apps: JsonObject {
@@ -21,27 +19,11 @@ JsonObject {
property list<string> playback: ["mpv"] property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"] property list<string> terminal: ["kitty"]
} }
component Battery: JsonObject {
property int critPerc: 5
property list<var> popupThresholds: [
{
perc: 20,
name: qsTr("Low battery"),
message: qsTr("Battery is low"),
icon: "battery_android_frame_2"
},
]
}
component Color: JsonObject { component Color: JsonObject {
property int hyprsunsetTemp: 5000
property string mode: "dark" property string mode: "dark"
property bool neovimColors: false property bool neovimColors: false
property bool scheduleDark: false
property int scheduleDarkEnd: 0 property int scheduleDarkEnd: 0
property int scheduleDarkStart: 0 property int scheduleDarkStart: 0
property bool scheduleHyprsunset: false
property int scheduleHyprsunsetEnd: 0
property int scheduleHyprsunsetStart: 0
property bool schemeGeneration: true property bool schemeGeneration: true
property bool smart: false property bool smart: false
} }
-1
View File
@@ -91,7 +91,6 @@ JsonObject {
property string specialPrefix: "@" property string specialPrefix: "@"
property UseFuzzy useFuzzy: UseFuzzy { property UseFuzzy useFuzzy: UseFuzzy {
} }
property bool uwsm: true
component Sizes: JsonObject { component Sizes: JsonObject {
property int itemHeight: 50 property int itemHeight: 50
-2
View File
@@ -5,8 +5,6 @@ JsonObject {
property bool enableFprint: true property bool enableFprint: true
property int maxFprintTries: 3 property int maxFprintTries: 3
property bool recolorLogo: false property bool recolorLogo: false
property bool showNotifContent: false
property bool showNotifIcon: true
property Sizes sizes: Sizes { property Sizes sizes: Sizes {
} }
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enable_pp: true
property string mode: "manual"
property real radius: 12.0
property bool rounding: false
property bool shadow: true
property real shadow_blur: 22.0
property list<int> shadow_color: [0, 0, 0, 160]
property real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0
}
-1
View File
@@ -14,7 +14,6 @@ JsonObject {
"to": "YT Music" "to": "YT Music"
} }
] ]
property bool updates: true
property bool useFahrenheit: false property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a") property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property int visualizerBars: 30 property int visualizerBars: 30

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