4 Commits

217 changed files with 4511 additions and 14810 deletions
-42
View File
@@ -1,42 +0,0 @@
name: Lint & Format (JS/TS)
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git
- name: Prettier
continue-on-error: true
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
npx --yes prettier --check "**/*.{js,jsx,ts,tsx,mjs,cjs}" --ignore-path .prettierignore
else
echo "No JS/TS files found"
fi
- name: ESLint
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
if [ -f package.json ]; then
npm install --no-audit --no-fund
fi
if [ -f eslint.config.js ] || [ -f eslint.config.mjs ] || [ -f eslint.config.cjs ] || [ -f .eslintrc ] || [ -f .eslintrc.js ] || [ -f .eslintrc.cjs ] || [ -f .eslintrc.json ] || [ -f .eslintrc.yaml ] || [ -f .eslintrc.yml ]; then
npx --yes eslint . && echo "ESLint passed" || echo "ESLint failed"
else
echo "No eslint config found"
fi
else
echo "No JS/TS files found"
fi
-65
View File
@@ -1,65 +0,0 @@
name: Python
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git \
python3 \
py3-pip
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir ruff
- name: Format check
continue-on-error: true
run: |
. .venv/bin/activate
ruff format --check .
- name: Lint
run: |
. .venv/bin/activate
ruff check .
test:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git \
python3 \
py3-pip \
py3-pillow \
build-base
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir \
typer \
pillow \
materialyoucolor \
jinja2 \
pytest
- name: Test
run: |
. .venv/bin/activate
cd cli
python -m pytest tests/ -v
-72
View File
@@ -1,72 +0,0 @@
name: Lint & Format (Rust)
on:
pull_request:
jobs:
lint-format:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rustfmt \
rust-clippy
- name: Format check
continue-on-error: true
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
echo "$manifest: needs formatting"
done
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
- name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" --all-targets --all-features -- -D warnings && \
echo "$manifest: Clippy passed" || \
{ echo "$manifest: Clippy failed"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
+1 -2
View File
@@ -1,4 +1,4 @@
**/__pycache__/ ./__pycache__/
./result/ ./result/
.pyre/ .pyre/
.cache/ .cache/
@@ -12,4 +12,3 @@ pkg/
uv.lock uv.lock
.qtcreator/ .qtcreator/
dist/ dist/
**/target/
-3
View File
@@ -1,3 +0,0 @@
.venv/
scripts/fzf.js
scripts/fuzzysort.js
-13
View File
@@ -1,13 +0,0 @@
{
"semi": true,
"singleQuote": false,
"jsxSingleQuote": false,
"tabWidth": 4,
"printWidth": 80,
"trailingComma": "es5",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"endOfLine": "lf",
"proseWrap": "preserve"
}
+1 -8
View File
@@ -31,13 +31,6 @@ if("shell" IN_LIST ENABLE_MODULES)
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths) foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}") install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
endforeach() endforeach()
install(FILES shell.qml DESTINATION "${INSTALL_QSCONFDIR}")
# Disable watching for changes
file(READ shell.qml SHELL_QML)
string(REPLACE "settings.watchFiles: true" "settings.watchFiles: false" SHELL_QML "${SHELL_QML}")
file(WRITE "${CMAKE_BINARY_DIR}/qml/shell.qml" "${SHELL_QML}")
install(FILES "${CMAKE_BINARY_DIR}/qml/shell.qml" DESTINATION "${INSTALL_QSCONFDIR}")
# Greeter
install(DIRECTORY Greeter/ DESTINATION "${INSTALL_GREETERCONFDIR}") install(DIRECTORY Greeter/ DESTINATION "${INSTALL_GREETERCONFDIR}")
endif() endif()
+2 -2
View File
@@ -2,7 +2,7 @@ import QtQuick
import qs.Config import qs.Config
NumberAnimation { NumberAnimation {
duration: Appearance.anim.durations.normal duration: MaterialEasing.standardTime
easing.bezierCurve: Appearance.anim.curves.standard easing.bezierCurve: MaterialEasing.standard
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
} }
+2 -16
View File
@@ -8,34 +8,20 @@ Item {
id: root id: root
property alias active: splitButton.active property alias active: splitButton.active
property alias buttonAlias: splitButton
property bool enabled: true 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
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: root.expanded ? expandedZ : -1 z: root.expanded ? expandedZ : -1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout { RowLayout {
id: row id: row
-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
-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;
}
}
}
+2 -1
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
} }
} }
-4
View File
@@ -7,8 +7,4 @@ JsonObject {
property real alignX: 0.5 property real alignX: 0.5
property real alignY: 0.5 property real alignY: 0.5
property real zoom: 1.0 property real zoom: 1.0
property real sourceClipX: 0
property real sourceClipY: 0
property real sourceClipW: 0
property real sourceClipH: 0
} }
+12 -1
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
@@ -40,6 +48,10 @@ JsonObject {
id: "tray", id: "tray",
enabled: true enabled: true
}, },
{
id: "upower",
enabled: false
},
{ {
id: "network", id: "network",
enabled: false enabled: false
@@ -58,7 +70,6 @@ JsonObject {
property Popouts popouts: Popouts { property Popouts popouts: Popouts {
} }
property int rounding: 8 property int rounding: 8
property int smoothing: 32
component Popouts: JsonObject { component Popouts: JsonObject {
property bool activeWindow: true property bool activeWindow: true
-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: ""
}
} }
+2 -44
View File
@@ -22,9 +22,7 @@ Singleton {
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 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
@@ -84,10 +82,6 @@ Singleton {
wallFadeDuration: background.wallFadeDuration, wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled, enabled: background.enabled,
alignX: background.alignX, alignX: background.alignX,
sourceClipX: background.sourceClipX,
sourceClipY: background.sourceClipY,
sourceClipW: background.sourceClipW,
sourceClipH: background.sourceClipH,
alignY: background.alignY, alignY: background.alignY,
zoom: background.zoom zoom: background.zoom
}; };
@@ -99,7 +93,6 @@ Singleton {
hideWhenNotif: barConfig.hideWhenNotif, hideWhenNotif: barConfig.hideWhenNotif,
rounding: barConfig.rounding, rounding: barConfig.rounding,
border: barConfig.border, border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height, height: barConfig.height,
popouts: { popouts: {
tray: barConfig.popouts.tray, tray: barConfig.popouts.tray,
@@ -116,12 +109,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,9 +128,7 @@ Singleton {
background: serializeBackground(), background: serializeBackground(),
launcher: serializeLauncher(), launcher: serializeLauncher(),
colors: serializeColors(), colors: serializeColors(),
dock: serializeDock(), dock: serializeDock()
screenshot: serializeScreenshot(),
plugins: serializePlugins()
}; };
} }
@@ -194,7 +180,6 @@ Singleton {
logo: general.logo, logo: general.logo,
wallpaperPath: general.wallpaperPath, wallpaperPath: general.wallpaperPath,
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,
@@ -248,8 +233,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: {
@@ -291,27 +274,6 @@ Singleton {
}; };
} }
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 { function serializeServices(): var {
return { return {
weatherLocation: services.weatherLocation, weatherLocation: services.weatherLocation,
@@ -467,10 +429,6 @@ Singleton {
} }
property Overview overview: Overview { property Overview overview: Overview {
} }
property PluginConfig plugins: PluginConfig {
}
property Screenshot screenshot: Screenshot {
}
property Services services: Services { property Services services: Services {
} }
property SidebarConfig sidebar: SidebarConfig { property SidebarConfig sidebar: SidebarConfig {
+6 -38
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Hyprland
import QtQuick import QtQuick
import ZShell import ZShell
import qs.Helpers import qs.Helpers
@@ -30,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));
@@ -81,32 +79,10 @@ Singleton {
} }
function reloadHyprRules(): void { function reloadHyprRules(): void {
const blur = transparency.enabled ? 1 : 0; const barStr = "keyword layerrule %1 %2, match:namespace ZShell-Bar";
const alpha = transparency.base - 0.03; const authStr = "keyword layerrule %1 %2, match:namespace ZShell-Auth";
Hypr.extras.batchMessage([barStr.arg("blur").arg(transparency.enabled ? 1 : 0), barStr.arg("ignore_alpha").arg(transparency.base - 0.03)]);
const rules = ` Hypr.extras.batchMessage([authStr.arg("blur").arg(transparency.enabled ? 1 : 0), authStr.arg("ignore_alpha").arg(transparency.base - 0.03)]);
hl.layer_rule({
match = { namespace = "ZShell-Bar" },
blur = ${blur}
})
hl.layer_rule({
match = { namespace = "ZShell-Bar" },
ignore_alpha = ${alpha}
})
hl.layer_rule({
match = { namespace = "ZShell-Auth" },
blur = ${blur}
})
hl.layer_rule({
match = { namespace = "ZShell-Auth" },
ignore_alpha = ${alpha}
})
`;
Hypr.extras.message(`eval ${rules}`);
} }
function setMode(mode: string): void { function setMode(mode: string): void {
@@ -117,14 +93,6 @@ Singleton {
Component.onCompleted: debounceTimer.triggered() Component.onCompleted: debounceTimer.triggered()
Connections {
function onUsingLuaChanged(): void {
root.reloadHyprRules();
}
target: Hyprland
}
Connections { Connections {
function onConfigReloaded(): void { function onConfigReloaded(): void {
root.reloadHyprRules(); root.reloadHyprRules();
-1
View File
@@ -6,7 +6,6 @@ JsonObject {
} }
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 {
} }
-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 {
} }
-11
View File
@@ -1,11 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enabled: false
property list<var> entries: [
{
id: "Plugin",
enabled: false
},
]
}
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Io
JsonObject {
property real corner_radius: 12.0
property bool drop_shadow: true
property bool enable_pp: true
property string mode: "manual"
property bool rounded_corners: false
property real shadow_blur_radius: 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
}
+175 -24
View File
@@ -3,33 +3,184 @@ import QtQuick
Canvas { Canvas {
id: root id: root
property rect dirtyRect: Qt.rect(0, 0, 0, 0)
property bool frameQueued: false
property bool fullRepaintPending: true
property point lastPoint: Qt.point(0, 0)
property real minPointDistance: 2.0
property color penColor: "white" property color penColor: "white"
property real penWidth: 4 property real penWidth: 4
property var points: [] property var pendingSegments: []
property bool strokeActive: false
property var strokes: []
function clear(): void { function appendPoint(x, y) {
var ctx = getContext('2d'); if (!strokeActive || strokes.length === 0)
root.points = [];
ctx.reset();
root.requestPaint();
}
renderStrategy: Canvas.Cooperative
onPaint: {
if (points.length < 2)
return; return;
var ctx = root.getContext('2d'); const dx = x - lastPoint.x;
ctx.save(); const dy = y - lastPoint.y;
ctx.lineWidth = root.penWidth;
ctx.strokeStyle = root.penColor; if ((dx * dx + dy * dy) < (minPointDistance * minPointDistance))
ctx.lineCap = "round"; return;
ctx.beginPath(); const x1 = lastPoint.x;
ctx.moveTo(points[0].x, points[0].y); const y1 = lastPoint.y;
for (var i = 1; i < points.length; i++) const x2 = x;
ctx.lineTo(points[i].x, points[i].y); const y2 = y;
ctx.stroke();
points = points.slice(points.length - 2); strokes[strokes.length - 1].push(Qt.point(x2, y2));
ctx.restore();
pendingSegments.push({
dot: false,
x1: x1,
y1: y1,
x2: x2,
y2: y2
});
lastPoint = Qt.point(x2, y2);
queueDirty(segmentDirtyRect(x1, y1, x2, y2));
} }
function beginStroke(x, y) {
const p = Qt.point(x, y);
strokes.push([p]);
lastPoint = p;
strokeActive = true;
pendingSegments.push({
dot: true,
x: x,
y: y
});
queueDirty(pointDirtyRect(x, y));
}
function clear() {
strokes = [];
pendingSegments = [];
dirtyRect = Qt.rect(0, 0, 0, 0);
fullRepaintPending = true;
markDirty(Qt.rect(0, 0, width, height));
}
function drawDot(ctx, x, y) {
ctx.beginPath();
ctx.arc(x, y, penWidth / 2, 0, Math.PI * 2);
ctx.fill();
}
function drawSegment(ctx, x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
function endStroke() {
strokeActive = false;
}
function pointDirtyRect(x, y) {
const pad = penWidth + 2;
return Qt.rect(x - pad, y - pad, pad * 2, pad * 2);
}
function queueDirty(r) {
dirtyRect = unionRects(dirtyRect, r);
if (frameQueued)
return;
frameQueued = true;
requestAnimationFrame(function () {
frameQueued = false;
if (dirtyRect.width > 0 && dirtyRect.height > 0) {
markDirty(dirtyRect);
dirtyRect = Qt.rect(0, 0, 0, 0);
}
});
}
function replayAll(ctx) {
ctx.clearRect(0, 0, width, height);
for (const stroke of strokes) {
if (!stroke || stroke.length === 0)
continue;
if (stroke.length === 1) {
const p = stroke[0];
drawDot(ctx, p.x, p.y);
continue;
}
ctx.beginPath();
ctx.moveTo(stroke[0].x, stroke[0].y);
for (let i = 1; i < stroke.length; ++i)
ctx.lineTo(stroke[i].x, stroke[i].y);
ctx.stroke();
}
}
function requestFullRepaint() {
fullRepaintPending = true;
markDirty(Qt.rect(0, 0, width, height));
}
function segmentDirtyRect(x1, y1, x2, y2) {
const pad = penWidth + 2;
const left = Math.min(x1, x2) - pad;
const top = Math.min(y1, y2) - pad;
const right = Math.max(x1, x2) + pad;
const bottom = Math.max(y1, y2) + pad;
return Qt.rect(left, top, right - left, bottom - top);
}
function unionRects(a, b) {
if (a.width <= 0 || a.height <= 0)
return b;
if (b.width <= 0 || b.height <= 0)
return a;
const left = Math.min(a.x, b.x);
const top = Math.min(a.y, b.y);
const right = Math.max(a.x + a.width, b.x + b.width);
const bottom = Math.max(a.y + a.height, b.y + b.height);
return Qt.rect(left, top, right - left, bottom - top);
}
anchors.fill: parent
contextType: "2d"
renderStrategy: Canvas.Threaded
renderTarget: Canvas.Image
onHeightChanged: requestFullRepaint()
onPaint: region => {
const ctx = getContext("2d");
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.lineWidth = penWidth;
ctx.strokeStyle = penColor;
ctx.fillStyle = penColor;
if (fullRepaintPending) {
fullRepaintPending = false;
replayAll(ctx);
pendingSegments = [];
return;
}
for (const seg of pendingSegments) {
if (seg.dot)
drawDot(ctx, seg.x, seg.y);
else
drawSegment(ctx, seg.x1, seg.y1, seg.x2, seg.y2);
}
pendingSegments = [];
}
onWidthChanged: requestFullRepaint()
} }
+5 -7
View File
@@ -30,10 +30,8 @@ CustomMouseArea {
const x = event.x; const x = event.x;
const y = event.y; const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) { if (event.buttons & Qt.LeftButton)
root.drawing.points.push(Qt.point(x, y)); root.drawing.appendPoint(x, y);
root.drawing.requestPaint();
}
if (root.inLeftPanel(root.popout, x, y)) { if (root.inLeftPanel(root.popout, x, y)) {
root.z = -2; root.z = -2;
@@ -46,8 +44,7 @@ CustomMouseArea {
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) { if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.panels.drawing.expanded = false; root.panels.drawing.expanded = false;
root.drawing.points.push(Qt.point(x, y)); root.drawing.beginStroke(x, y);
root.drawing.requestPaint();
return; return;
} }
@@ -55,6 +52,7 @@ CustomMouseArea {
root.drawing.clear(); root.drawing.clear();
} }
onReleased: { onReleased: {
root.drawing.points = []; if (root.visibilities.isDrawing)
root.drawing.endStroke();
} }
} }
-15
View File
@@ -12,37 +12,22 @@ Scope {
required property ShellScreen screen required property ShellScreen screen
ExclusionZone { ExclusionZone {
id: top
anchors.top: true anchors.top: true
exclusiveZone: root.bar.exclusiveZone exclusiveZone: root.bar.exclusiveZone
} }
ExclusionZone { ExclusionZone {
id: left
anchors.left: true anchors.left: true
} }
ExclusionZone { ExclusionZone {
id: right
anchors.right: true anchors.right: true
} }
ExclusionZone { ExclusionZone {
id: bottom
anchors.bottom: true anchors.bottom: true
} }
Timer {
interval: 5000
running: true
onTriggered: console.log("top height:", top.exclusiveZone, "left width:", left.exclusiveZone, "right width:", right.exclusiveZone, "bottom height:", bottom.exclusiveZone)
}
component ExclusionZone: CustomWindow { component ExclusionZone: CustomWindow {
exclusiveZone: Config.barConfig.border exclusiveZone: Config.barConfig.border
implicitHeight: 1 implicitHeight: 1
+1 -1
View File
@@ -78,7 +78,7 @@ CustomMouseArea {
const dragY = y - dragStart.y; const dragY = y - dragStart.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) { if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
// root.input.z = 2; root.input.z = 2;
root.panels.drawing.expanded = false; root.panels.drawing.expanded = false;
} }
-11
View File
@@ -34,7 +34,6 @@ Item {
readonly property alias resourcesWrapper: resourcesWrapper readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen required property ShellScreen screen
readonly property alias settings: settings readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar readonly property alias sidebar: sidebar
readonly property alias toasts: toasts readonly property alias toasts: toasts
readonly property alias utilities: utilities readonly property alias utilities: utilities
@@ -177,15 +176,6 @@ Item {
visibilities: root.visibilities visibilities: root.visibilities
} }
Item {
id: settingsWrapper
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
clip: true
implicitHeight: settings.implicitHeight * (1 - settings.offsetScale)
implicitWidth: settings.implicitWidth
Settings.Wrapper { Settings.Wrapper {
id: settings id: settings
@@ -196,7 +186,6 @@ Item {
screen: root.screen screen: root.screen
visibilities: root.visibilities visibilities: root.visibilities
} }
}
Dock.Wrapper { Dock.Wrapper {
id: dock id: dock
+18 -40
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
import ZShell.Blobs import ZShell.Blobs
@@ -128,14 +127,6 @@ Variants {
Component.onCompleted: Visibilities.load(scope.modelData, this) Component.onCompleted: Visibilities.load(scope.modelData, this)
} }
IpcHandler {
function toggleLauncher(fix: string): void {
visibilities.launcher = !visibilities.launcher;
}
target: "visibilities"
}
Binding { Binding {
property: "bar" property: "bar"
target: visibilities target: visibilities
@@ -158,7 +149,6 @@ Variants {
id: blobGroup id: blobGroup
color: DynamicColors.palette.m3surface color: DynamicColors.palette.m3surface
smoothing: Config.barConfig.smoothing
Behavior on color { Behavior on color {
CAnim { CAnim {
@@ -180,34 +170,28 @@ Variants {
PanelBg { PanelBg {
id: dashBg id: dashBg
property real extraHeight: 0.2 deformAmount: 0.08 * Config.appearance.deform.scale
implicitHeight: panels.dashboard.height
deformAmount: 0.06
implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper panel: panels.dashboard
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border 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
} }
PanelBg { PanelBg {
id: launcherBg id: launcherBg
property real extraHeight: 0.2 deformAmount: 0.08 * Config.appearance.deform.scale
deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher panel: panels.launcher
radius: Appearance.rounding.smallest + 5 radius: Appearance.rounding.smallest + 5
y: panels.launcher.y + bar.implicitHeight
} }
PanelBg { PanelBg {
id: sidebarBg id: sidebarBg
bottomLeftRadius: 0 bottomLeftRadius: 0
deformAmount: 0.04 deformAmount: 0.08 * Config.appearance.deform.scale
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg] exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2 implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
panel: panels.sidebar panel: panels.sidebar
@@ -216,10 +200,10 @@ Variants {
PanelBg { PanelBg {
id: osdBg id: osdBg
deformAmount: 0.1 deformAmount: 0.1 * Config.appearance.deform.scale
implicitHeight: panels.osd.height implicitHeight: panels.osd.height
implicitWidth: panels.osd.width implicitWidth: panels.osd.width
panel: panels.osdWrapper panel: panels.osd
radius: 20 radius: 20
x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border 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
@@ -234,7 +218,7 @@ Variants {
PanelBg { PanelBg {
id: utilsBg id: utilsBg
deformAmount: panels.sidebar.visible ? (0.1) : (0.1) deformAmount: panels.sidebar.visible ? (0.1 * Config.appearance.deform.scale) : (0.1 * Config.appearance.deform.scale)
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg] exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
panel: panels.utilities panel: panels.utilities
topLeftRadius: 0 topLeftRadius: 0
@@ -245,11 +229,11 @@ Variants {
property real extraHeight: panels.popouts.isDetached ? 0 : 0.2 property real extraHeight: panels.popouts.isDetached ? 0 : 0.2
deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1 deformAmount: panels.popouts.isDetached ? 0.05 * Config.appearance.deform.scale : panels.popouts.hasCurrent ? 0.15 * Config.appearance.deform.scale : 0.1 * Config.appearance.deform.scale
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.popouts
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : Appearance.rounding.smallest
x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border 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
@@ -262,10 +246,10 @@ Variants {
PanelBg { PanelBg {
id: resourcesBg id: resourcesBg
deformAmount: 0.05 deformAmount: 0.08 * Config.appearance.deform.scale
implicitHeight: panels.resources.height implicitHeight: panels.resources.height
implicitWidth: panels.resources.width implicitWidth: panels.resources.width
panel: panels.resourcesWrapper panel: panels.resources
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border 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
@@ -274,23 +258,17 @@ Variants {
PanelBg { PanelBg {
id: settingsBg id: settingsBg
property real extraHeight: 0.2 deformAmount: 0.08 * Config.appearance.deform.scale
deformAmount: 0.03
implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width
panel: panels.settings panel: panels.settings
radius: Appearance.rounding.large radius: Appearance.rounding.large
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
topRightRadius: 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
} }
PanelBg { PanelBg {
id: dockBg id: dockBg
deformAmount: 0.08 deformAmount: 0.08 * Config.appearance.deform.scale
panel: panels.dock panel: panels.dock
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
} }
@@ -298,7 +276,7 @@ Variants {
PanelBg { PanelBg {
id: drawingBg id: drawingBg
deformAmount: 0.08 deformAmount: 0.08 * Config.appearance.deform.scale
panel: panels.drawing panel: panels.drawing
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
} }
@@ -393,7 +371,7 @@ Variants {
property real deformAmount: 0.15 property real deformAmount: 0.15
required property Item panel required property Item panel
deformScale: (deformAmount * Config.appearance.deform.scale) / 10000 deformScale: deformAmount / 10000
group: blobGroup group: blobGroup
implicitHeight: panel.height implicitHeight: panel.height
implicitWidth: panel.width implicitWidth: panel.width
-18
View File
@@ -1,18 +0,0 @@
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
@@ -1,17 +0,0 @@
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
}
}
+20 -20
View File
@@ -16,14 +16,27 @@ Scope {
property bool launching: false property bool launching: false
property string promptMessage: "" property string promptMessage: ""
readonly property var selectedSession: sessionIndex >= 0 ? sessions[sessionIndex] : null readonly property var selectedSession: sessionIndex >= 0 ? sessions[sessionIndex] : null
readonly property var selectedUser: Users.selectedUser
property int sessionIndex: sessions.length > 0 ? 0 : -1 property int sessionIndex: sessions.length > 0 ? 0 : -1
property var sessions: [] property var sessions: []
readonly property string userFace: selectedUser ? selectedUser.face : ""
readonly property string username: Users.selectedUsername
// User handling - now uses the Users singleton // User handling - now uses the Users singleton
readonly property var users: Users.users readonly property var users: Users.users
readonly property var selectedUser: Users.selectedUser
readonly property string username: Users.selectedUsername
readonly property string userFace: selectedUser ? selectedUser.face : ""
// User selection functions (delegate to Users singleton)
function selectUser(username: string): bool {
return Users.selectUser(username);
}
function selectNextUser(): void {
Users.selectNext();
}
function selectPreviousUser(): void {
Users.selectPrevious();
}
signal flashMsg signal flashMsg
@@ -45,11 +58,11 @@ Scope {
event.accepted = true; event.accepted = true;
return; return;
} else if (event.key === Qt.Key_Escape) { }
buffer = "";
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) { if (event.text && !/[\r\n]/.test(event.text)) {
// No illegal characters (you are insane if you use unicode in your password)
buffer += event.text; buffer += event.text;
event.accepted = true;
} }
} }
@@ -68,19 +81,6 @@ Scope {
Greetd.launch(selectedSession.command, [], true); Greetd.launch(selectedSession.command, [], true);
} }
function selectNextUser(): void {
Users.selectNext();
}
function selectPreviousUser(): void {
Users.selectPrevious();
}
// User selection functions (delegate to Users singleton)
function selectUser(username: string): bool {
return Users.selectUser(username);
}
function submit(): void { function submit(): void {
errorMessage = ""; errorMessage = "";
+1
View File
@@ -346,6 +346,7 @@ Singleton {
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
console.log("this is running");
if (root.gpuType === "GENERIC") { if (root.gpuType === "GENERIC") {
const percs = text.trim().split("\n"); const percs = text.trim().split("\n");
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0); const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
+44 -20
View File
@@ -7,30 +7,23 @@ import QtQuick
Singleton { Singleton {
id: root id: root
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user" // The list of users that can log in graphically
property int selectedIndex: 0 // Each user object has: username, uid, home, shell, gecos (full name), face (avatar path)
readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
property var users: [] property var users: []
function saveDefaultUser(): void { // The currently selected user index
if (selectedUser) { property int selectedIndex: 0
defaultUserStorage.setText(selectedUser.username);
}
}
function selectNext(): void { // The currently selected user object (or null if none)
if (users.length === 0) readonly property var selectedUser: selectedIndex >= 0 && selectedIndex < users.length ? users[selectedIndex] : null
return;
selectedIndex = (selectedIndex + 1) % users.length;
}
function selectPrevious(): void { // Convenience property for the selected username
if (users.length === 0) readonly property string selectedUsername: selectedUser ? selectedUser.username : ""
return;
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
}
// Path to store the default user preference
readonly property string defaultUserFile: "/etc/zshell-greeter/default-user"
// Select a user by username
function selectUser(username: string): bool { function selectUser(username: string): bool {
for (let i = 0; i < users.length; i++) { for (let i = 0; i < users.length; i++) {
if (users[i].username === username) { if (users[i].username === username) {
@@ -41,6 +34,28 @@ Singleton {
return false; return false;
} }
// Select the next user in the list (wraps around)
function selectNext(): void {
if (users.length === 0)
return;
selectedIndex = (selectedIndex + 1) % users.length;
}
// Select the previous user in the list (wraps around)
function selectPrevious(): void {
if (users.length === 0)
return;
selectedIndex = (selectedIndex - 1 + users.length) % users.length;
}
// Save the current user as the default for next login
function saveDefaultUser(): void {
if (selectedUser) {
defaultUserStorage.setText(selectedUser.username);
}
}
// Process to fetch the list of graphical users
Process { Process {
id: userLister id: userLister
@@ -52,10 +67,13 @@ Singleton {
try { try {
root.users = JSON.parse(text); root.users = JSON.parse(text);
// If we have users and no selection yet, try to select the default user
if (root.users.length > 0) { if (root.users.length > 0) {
// Try to load the default user
if (defaultUserStorage.loaded) { if (defaultUserStorage.loaded) {
const defaultUsername = defaultUserStorage.text().trim(); const defaultUsername = defaultUserStorage.text().trim();
if (defaultUsername && !root.selectUser(defaultUsername)) { if (defaultUsername && !root.selectUser(defaultUsername)) {
// Default user not found, select first user
root.selectedIndex = 0; root.selectedIndex = 0;
} }
} else { } else {
@@ -69,14 +87,15 @@ Singleton {
} }
} }
// FileView for persisting the default user
FileView { FileView {
id: defaultUserStorage id: defaultUserStorage
path: root.defaultUserFile path: root.defaultUserFile
preload: true preload: true
onLoadFailed: {}
onLoaded: { onLoaded: {
// If users are already loaded, try to select the default user
if (root.users.length > 0) { if (root.users.length > 0) {
const defaultUsername = text().trim(); const defaultUsername = text().trim();
if (defaultUsername) { if (defaultUsername) {
@@ -84,5 +103,10 @@ Singleton {
} }
} }
} }
onLoadFailed: {
// File doesn't exist yet, that's fine - we'll create it on first save
console.log("No default user file found, will use first user");
}
} }
} }
-63
View File
@@ -1,63 +0,0 @@
// FetchPresets.qml
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property var parsedPresets: ({})
readonly property var presets: parsedPresets
property bool ready: false
function accents(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.accents ?? [];
}
function defaultAccent(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.default_accent ?? "";
}
function modes(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.modes ?? [];
}
function presetNames() {
return Object.keys(parsedPresets);
}
function variantNames(presetName) {
const preset = parsedPresets[presetName];
if (!preset || !preset.variants)
return [];
return Object.keys(preset.variants);
}
Process {
command: ["zshell-cli", "scheme", "list-presets", "--json"]
running: true
stdout: StdioCollector {
onStreamFinished: {
try {
const parsed = JSON.parse(text);
root.parsedPresets = parsed.presets ?? {};
root.ready = true;
} catch (e) {
console.error("Failed to parse presets JSON:", e);
}
}
}
}
}
-72
View File
@@ -1,72 +0,0 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import ZShell
import qs.Config
Singleton {
id: root
property alias enabled: props.enabled
function setHyprConf(): void {
Hypr.extras.applyOptions({
"animations.enabled": 0,
"decoration.shadow.enabled": 0,
"decoration.blur.enabled": 0,
"general.border_size": 0,
"decoration.rounding": 0
});
}
onEnabledChanged: {
if (enabled) {
setHyprConf();
if (Config.utilities.toasts.gameModeChanged)
Toaster.toast(qsTr("Game mode enabled"), qsTr("Disabled Hyprland animations, blur, shadows and corner radius"), "gamepad");
} else {
Hypr.extras.message("reload");
if (Config.utilities.toasts.gameModeChanged)
Toaster.toast(qsTr("Game mode disabled"), qsTr("Hyprland settings restored"), "gamepad");
}
}
PersistentProperties {
id: props
property bool enabled: Hypr.options["animations:enabled"] === 0
reloadableId: "gamemode"
}
Connections {
function onConfigReloaded(): void {
if (props.enabled)
root.setHyprConf();
}
target: Hypr
}
IpcHandler {
function disable(): void {
props.enabled = false;
}
function enable(): void {
props.enabled = true;
}
function isEnabled(): bool {
return props.enabled;
}
function toggle(): void {
props.enabled = !props.enabled;
}
target: "gameMode"
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ Singleton {
readonly property int darkEnd: Config.general.color.scheduleDarkEnd readonly property int darkEnd: Config.general.color.scheduleDarkEnd
readonly property int darkStart: Config.general.color.scheduleDarkStart readonly property int darkStart: Config.general.color.scheduleDarkStart
readonly property bool enabled: Config.general.color.scheduleDark && Config.general.color.schemeGeneration readonly property bool enabled: Config.general.color.scheduleDark
function applyDarkMode() { function applyDarkMode() {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "dark"]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "dark"]);
+11
View File
@@ -0,0 +1,11 @@
pragma Singleton
import Quickshell
import Quickshell.Networking
Singleton {
id: root
property NetworkDevice activeDevice: devices.find(d => d.connected)
property list<NetworkDevice> devices: Networking.devices.values
}
+1 -2
View File
@@ -66,8 +66,7 @@ MouseArea {
function save(): void { function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--image"] : ["swappy", "-f"]; ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path]));
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path]));
closeAnim.start(); closeAnim.start();
} }
+41 -82
View File
@@ -1,82 +1,41 @@
pragma Singleton // pragma Singleton
//
import Quickshell // import Quickshell
import Quickshell.Io // import QtQuick
import QtQuick //
// Singleton {
Singleton { // id: root
id: root //
// function start(extraArgs = []): void {
readonly property alias elapsed: props.elapsed // needsStart = true;
property bool needsPause // startArgs = extraArgs;
property bool needsStart // checkProc.running = true;
property bool needsStop // }
readonly property alias paused: props.paused //
readonly property alias running: props.running // PersistentProperties {
property list<string> startArgs // id: props
//
function start(extraArgs = []): void { // property real elapsed: 0
needsStart = true; // property bool paused: false
startArgs = extraArgs; // property bool running: false
checkProc.running = true; //
} // reloadableId: "recorder"
// }
function stop(): void { //
needsStop = true; // Process {
checkProc.running = true; // id: checkProc
} //
// command: ["pidof", "gpu-screen-recorder"]
function togglePause(): void { // running: true
needsPause = true; //
checkProc.running = true; // onExited: code => {
} // props.running = code === 0;
//
PersistentProperties { // if (code === 0) {
id: props // if (root.needsStop) {
// Quickshell.execDetached(["zshell-cli"]);
property real elapsed: 0 // }
property bool paused: false // }
property bool running: false // }
// }
reloadableId: "recorder" // }
}
Process {
id: checkProc
command: ["pidof", "gpu-screen-recorder"]
running: true
onExited: code => {
props.running = code === 0;
if (code === 0) {
if (root.needsStop) {
Quickshell.execDetached(["zshell-cli", "record", "record"]);
props.running = false;
props.paused = false;
} else if (root.needsPause) {
Quickshell.execDetached(["zshell-cli", "record", "record", "-p"]);
props.paused = !props.paused;
}
} else if (root.needsStart) {
Quickshell.execDetached(["zshell-cli", "record", "record", ...root.startArgs]);
props.running = true;
props.paused = false;
props.elapsed = 0;
}
root.needsStart = false;
root.needsStop = false;
root.needsPause = false;
}
}
Connections {
function onSecondsChanged(): void {
props.elapsed++;
}
target: Time // qmllint disable incompatible-type
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import Quickshell
import "../scripts/fzf.js" as Fzf import "../scripts/fzf.js" as Fzf
import "../scripts/fuzzysort.js" as Fuzzy import "../scripts/fuzzysort.js" as Fuzzy
import QtQuick import QtQuick
import Quickshell
Singleton { Singleton {
property var extraOpts: ({}) property var extraOpts: ({})
+2 -7
View File
@@ -1,23 +1,18 @@
pragma Singleton pragma Singleton
import Quickshell import Quickshell
import QtQuick
import qs.Config
Singleton { Singleton {
id: root readonly property string amPmStr: timeComponents[2] ?? ""
readonly property date date: clock.date readonly property date date: clock.date
readonly property string dateStr: format(Config.general.dateFormat)
property alias enabled: clock.enabled property alias enabled: clock.enabled
readonly property string hourStr: timeComponents[0] ?? "" readonly property string hourStr: timeComponents[0] ?? ""
readonly property int hours: clock.hours readonly property int hours: clock.hours
readonly property string minuteStr: timeComponents[1] ?? "" readonly property string minuteStr: timeComponents[1] ?? ""
readonly property int minutes: clock.minutes readonly property int minutes: clock.minutes
readonly property string secondStr: timeComponents[2] ?? ""
readonly property int seconds: clock.seconds readonly property int seconds: clock.seconds
readonly property list<string> timeComponents: timeStr.split(":") readonly property list<string> timeComponents: timeStr.split(":")
readonly property string timeStr: format("hh:mm:ss") readonly property string timeStr: format("hh:mm")
function format(fmt: string): string { function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt); return Qt.formatDateTime(clock.date, fmt);
-14
View File
@@ -6,21 +6,7 @@ import Quickshell.Services.UPower
Singleton { Singleton {
id: root id: root
readonly property real batteryPercent: UPower.displayDevice.percentage
readonly property list<UPowerDevice> devices: UPower.devices.values readonly property list<UPowerDevice> devices: UPower.devices.values
readonly property UPowerDevice displayDevice: UPower.displayDevice readonly property UPowerDevice displayDevice: UPower.displayDevice
readonly property bool onBattery: UPower.onBattery readonly property bool onBattery: UPower.onBattery
// property bool toastShown
//
// Connections {
// target: UPower
//
// function onPercentageChanged(): {
// if (root.batteryPercent >= 0.2 && toastShown)
// return;
//
// root.toastShown = true;
// Toaster.toast(qsTr("Battery "))
// }
// }
} }
-79
View File
@@ -11,13 +11,10 @@ Singleton {
id: root id: root
property int availableUpdates: 0 property int availableUpdates: 0
property string cmd: ""
property bool commandReady property bool commandReady
property bool loaded property bool loaded
property double now: Date.now() property double now: Date.now()
property var updates: ({}) property var updates: ({})
property bool updating
property string updatingPackage: ""
function formatUpdateTime(timestamp) { function formatUpdateTime(timestamp) {
const diffMs = root.now - timestamp; const diffMs = root.now - timestamp;
@@ -37,22 +34,6 @@ Singleton {
return Qt.formatDateTime(new Date(timestamp), "dd hh:mm"); return Qt.formatDateTime(new Date(timestamp), "dd hh:mm");
} }
function performPackageUpdate(pkg: string): void {
if (root.cmd === "pacman")
pkgUpdateProc.command = ["pkexec", root.cmd, "--noconfirm", "-Sy", pkg];
else
pkgUpdateProc.command = [root.cmd, "--noconfirm", "--sudo", "pkexec", "-Sy", pkg];
pkgUpdateProc.running = true;
}
function performSystemUpdate(): void {
if (root.cmd === "pacman")
sysUpdateProc.command = ["pkexec", root.cmd, "--noconfirm", "-Syu"];
else
sysUpdateProc.command = [root.cmd, "--noconfirm", "--sudo", "pkexec", "-Syu"];
sysUpdateProc.running = true;
}
onUpdatesChanged: { onUpdatesChanged: {
if (!root.loaded) if (!root.loaded)
return; return;
@@ -111,28 +92,6 @@ Singleton {
} }
} }
Process {
id: updateCmdDetect
command: ["sh", "-c", "command -v yay || command -v paru"]
running: true
stdout: StdioCollector {
onStreamFinished: {
const cmd = this.text.trim();
let helper;
if (cmd.length > 0) {
helper = cmd.split("/").pop();
} else {
helper = "pacman";
}
root.cmd = helper;
}
}
}
Process { Process {
id: updatesProc id: updatesProc
@@ -156,44 +115,6 @@ Singleton {
} }
} }
Process {
id: sysUpdateProc
command: []
running: false
stdout: StdioCollector {
onStreamFinished: {
root.updating = false;
}
}
onRunningChanged: {
if (running)
root.updating = true;
}
}
Process {
id: pkgUpdateProc
command: []
running: false
stdout: StdioCollector {
onStreamFinished: {
root.updating = false;
}
}
onRunningChanged: {
if (running) {
root.updatingPackage = command[command.length - 1];
root.updating = true;
}
}
}
Timer { Timer {
id: saveTimer id: saveTimer
-51
View File
@@ -1,9 +1,7 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import QtQuick
import ZShell.Models import ZShell.Models
import qs.Config import qs.Config
import qs.Modules import qs.Modules
@@ -14,17 +12,10 @@ Searcher {
id: root id: root
property string actualCurrent: WallpaperPath.currentWallpaperPath property string actualCurrent: WallpaperPath.currentWallpaperPath
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent readonly property string current: showPreview ? previewPath : actualCurrent
property alias monitorCrops: monitorCrops
property string previewPath property string previewPath
property bool recentlyChanged
property bool showPreview: false property bool showPreview: false
function getCrop(screen: string): var {
return root.crops[screen];
}
function preview(path: string): void { function preview(path: string): void {
previewPath = path; previewPath = path;
if (Config.general.color.schemeGeneration) if (Config.general.color.schemeGeneration)
@@ -32,35 +23,9 @@ Searcher {
showPreview = true; showPreview = true;
} }
function setCrop(screen: string, rect: rect, scaledRect: rect, zoom: real): void {
let updated = Object.assign({}, root.crops);
if (zoom <= 0)
zoom = 1.0;
else if (zoom > 5.0)
zoom = 5.0;
updated[screen] = {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
scaledX: scaledRect.x,
scaledY: scaledRect.y,
scaledWidth: scaledRect.width,
scaledHeight: scaledRect.height,
zoom: zoom
};
root.crops = updated;
monitorCrops.writeAdapter();
monitorCrops.reload();
}
function setWallpaper(path: string): void { function setWallpaper(path: string): void {
actualCurrent = path; actualCurrent = path;
WallpaperPath.currentWallpaperPath = path; WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 0, 0), Qt.rect(0, 0, 0, 0), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]); Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.general.color.schemeGeneration) if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
@@ -87,22 +52,6 @@ Searcher {
target: "wallpaper" target: "wallpaper"
} }
FileView {
id: monitorCrops
path: `${Paths.state}/wallpaper-crops.json`
watchChanges: true
onAdapterUpdated: writeAdapter()
onFileChanged: reload()
JsonAdapter {
id: adapter
property var monitorCrops: ({})
}
}
FileSystemModel { FileSystemModel {
id: wallpapers id: wallpapers
+1 -1
View File
@@ -3,7 +3,7 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Config import qs.Config
import qs.Modules import qs.Modules
import qs.Helpers import qs.Helpers as Helpers
import qs.Components import qs.Components
CustomRect { CustomRect {
+31
View File
@@ -0,0 +1,31 @@
import Quickshell
import QtQuick
import qs.Config
import qs.Helpers
import qs.Components
CustomRect {
id: root
required property PersistentProperties visibilities
anchors.bottom: parent.bottom
anchors.bottomMargin: 6
anchors.top: parent.top
anchors.topMargin: 6
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: 40
radius: Appearance.rounding.full
StateLayer {
onClicked: {
root.visibilities.dashboard = !root.visibilities.dashboard;
}
}
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onSurface
text: "widgets"
}
}
+74 -22
View File
@@ -12,29 +12,90 @@ Item {
required property Canvas drawing required property Canvas drawing
property bool expanded: true property bool expanded: true
property real offsetScale: shouldBeActive ? 0 : 1
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeActive: visibilities.isDrawing readonly property bool shouldBeActive: visibilities.isDrawing
required property var visibilities required property var visibilities
anchors.leftMargin: (-implicitWidth - 5) * offsetScale
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
implicitWidth: root.expanded ? content.implicitWidth : icon.implicitWidth implicitWidth: 0
opacity: 1 - offsetScale visible: width > 0
visible: offsetScale < 1
Behavior on implicitWidth { states: [
State {
name: "hidden"
when: !root.shouldBeActive
PropertyChanges {
root.implicitWidth: 0
}
PropertyChanges {
icon.opacity: 0
}
PropertyChanges {
content.opacity: 0
}
},
State {
name: "collapsed"
when: root.shouldBeActive && !root.expanded
PropertyChanges {
root.implicitWidth: icon.implicitWidth
}
PropertyChanges {
icon.opacity: 1
}
PropertyChanges {
content.opacity: 0
}
},
State {
name: "visible"
when: root.shouldBeActive && root.expanded
PropertyChanges {
root.implicitWidth: content.implicitWidth
}
PropertyChanges {
icon.opacity: 0
}
PropertyChanges {
content.opacity: 1
}
}
]
transitions: [
Transition {
from: "*"
to: "*"
ParallelAnimation {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial easing.bezierCurve: MaterialEasing.expressiveEffects
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial property: "implicitWidth"
target: root
} }
}
Behavior on offsetScale {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial duration: Appearance.anim.durations.small
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial property: "opacity"
target: icon
}
Anim {
duration: Appearance.anim.durations.small
property: "opacity"
target: content
} }
} }
}
]
onVisibleChanged: { onVisibleChanged: {
if (!visible) if (!visible)
@@ -48,12 +109,8 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
height: content.contentItem.height height: content.contentItem.height
opacity: root.expanded ? 0 : 1 opacity: 1
Behavior on opacity {
Anim {
}
}
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: "arrow_forward_ios" text: "arrow_forward_ios"
@@ -65,12 +122,7 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
opacity: root.expanded ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: Content { sourceComponent: Content {
drawing: root.drawing drawing: root.drawing
visibilities: root.visibilities visibilities: root.visibilities
+1
View File
@@ -13,6 +13,7 @@ Searcher {
function launch(entry: DesktopEntry): void { function launch(entry: DesktopEntry): void {
appDb.incrementFrequency(entry.id); appDb.incrementFrequency(entry.id);
console.log(root.command);
if (entry.runInTerminal) if (entry.runInTerminal)
Quickshell.execDetached({ Quickshell.execDetached({
+9 -14
View File
@@ -8,25 +8,21 @@ import qs.Helpers
Scope { Scope {
id: root id: root
readonly property bool enabled: !Players.list.some(p => p.isPlaying)
required property Lock lock required property Lock lock
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
function handleIdleAction(action: var): void { function handleIdleAction( action: var ): void {
if (!action) if ( !action )
return; return;
if (action === "lock") if ( action === "lock" )
lock.lock.locked = true; lock.lock.locked = true;
else if (action === "unlock") else if ( action === "unlock" )
lock.lock.locked = false; lock.lock.locked = false;
else if (action === "dpms on") else if ( typeof action === "string" )
Hypr.dispatch('hl.dsp.dpms({ action = "enable" })'); Hypr.dispatch( action );
else if (action === "dpms off")
Hypr.dispatch('hl.dsp.dpms({ action = "disable" })');
else if (typeof action === "string")
Hypr.dispatch(action);
else else
Quickshell.execDetached(action); Quickshell.execDetached( action );
} }
Variants { Variants {
@@ -37,8 +33,7 @@ Scope {
enabled: root.enabled && modelData.timeout > 0 ? true : false enabled: root.enabled && modelData.timeout > 0 ? true : false
timeout: modelData.timeout timeout: modelData.timeout
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
onIsIdleChanged: root.handleIdleAction(isIdle ? modelData.idleAction : modelData.activeAction)
} }
} }
} }
-3
View File
@@ -58,7 +58,6 @@ CustomRect {
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
height: Config.notifs.sizes.image height: Config.notifs.sizes.image
source: Qt.resolvedUrl(root.image) source: Qt.resolvedUrl(root.image)
visible: Config.lock.showNotifIcon
width: Config.notifs.sizes.image width: Config.notifs.sizes.image
} }
} }
@@ -285,8 +284,6 @@ CustomRect {
Layout.fillWidth: true Layout.fillWidth: true
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: { text: {
if (!Config.lock.showNotifContent)
return "Unlock to view";
const summary = modelData.summary.replace(/\n/g, " "); const summary = modelData.summary.replace(/\n/g, " ");
const body = modelData.body.replace(/\n/g, " "); const body = modelData.body.replace(/\n/g, " ");
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline; const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
-2
View File
@@ -30,8 +30,6 @@ Scope {
} else { } else {
buffer = buffer.slice(0, -1); buffer = buffer.slice(0, -1);
} }
} else if (event.key === Qt.Key_Escape) {
buffer = "";
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) { } else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
// No illegal characters (you are insane if you use unicode in your password) // No illegal characters (you are insane if you use unicode in your password)
buffer += event.text; buffer += event.text;
+9 -3
View File
@@ -10,6 +10,7 @@ Item {
readonly property int padding: 6 readonly property int padding: 6
required property Item panels required property Item panels
required property Item sidebarPanel
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -43,7 +44,7 @@ Item {
return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2); return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
} }
implicitWidth: Config.notifs.sizes.width + padding * 2 implicitWidth: Math.max(sidebarPanel.width * (1 - sidebarPanel.offsetScale), Config.notifs.sizes.width + padding * 2)
Behavior on implicitHeight { Behavior on implicitHeight {
Anim { Anim {
@@ -72,8 +73,9 @@ Item {
required property NotifServer.Notif modelData required property NotifServer.Notif modelData
readonly property alias nonAnimHeight: notif.nonAnimHeight readonly property alias nonAnimHeight: notif.nonAnimHeight
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8) implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
implicitWidth: notif.implicitWidth
ListView.onRemove: removeAnim.start() ListView.onRemove: removeAnim.start()
onIndexChanged: { onIndexChanged: {
@@ -124,16 +126,20 @@ Item {
} }
ClippingRectangle { ClippingRectangle {
// implicitWidth: notif.implicitWidth
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: wrapper.idx === 0 ? 0 : 8 anchors.topMargin: wrapper.idx === 0 ? 0 : 8
color: "transparent" color: "transparent"
implicitHeight: notif.implicitHeight implicitHeight: notif.implicitHeight
implicitWidth: notif.implicitWidth
radius: Appearance.rounding.smallest / 2 radius: Appearance.rounding.smallest / 2
Notification { Notification {
id: notif id: notif
anchors.left: parent.left
anchors.right: parent.right
modelData: wrapper.modelData modelData: wrapper.modelData
} }
} }
+1 -1
View File
@@ -20,9 +20,9 @@ CustomRect {
required property NotifServer.Notif modelData required property NotifServer.Notif modelData
readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2 readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2
// implicitWidth: Config.notifs.sizes.width
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
implicitHeight: inner.implicitHeight implicitHeight: inner.implicitHeight
implicitWidth: Config.notifs.sizes.width
radius: 6 radius: 6
x: Config.notifs.sizes.width x: Config.notifs.sizes.width
-3
View File
@@ -136,10 +136,7 @@ CustomRect {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
onLinkActivated: link => { onLinkActivated: link => {
if (Config.launcher.uwsm)
Quickshell.execDetached(["app2unit", "-O", "--", link]); Quickshell.execDetached(["app2unit", "-O", "--", link]);
else
Quickshell.execDetached(["xdg-open", link]);
root.visibilities.sidebar = false; root.visibilities.sidebar = false;
} }
} }
@@ -78,10 +78,6 @@ LazyListView {
} }
} }
onDoubleClicked: event => {
if (event.button === Qt.LeftButton)
notifInner.toggleExpand(!notifInner.expanded);
}
onPositionChanged: event => { onPositionChanged: event => {
if (pressed) { if (pressed) {
const diffY = event.y - startY; const diffY = event.y - startY;
@@ -79,10 +79,6 @@ LazyListView {
Component.onCompleted: modelData?.lock(this) Component.onCompleted: modelData?.lock(this)
Component.onDestruction: modelData?.unlock(this) Component.onDestruction: modelData?.unlock(this)
onDoubleClicked: event => {
if (event.button === Qt.LeftButton)
root.requestToggleExpand(!root.expanded);
}
onPositionChanged: event => { onPositionChanged: event => {
if (pressed && !root.expanded) { if (pressed && !root.expanded) {
const diffY = event.y - startY; const diffY = event.y - startY;
@@ -1,290 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers
CustomRect {
id: root
required property var props
required property PersistentProperties visibilities
Layout.fillWidth: true
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
radius: Appearance.rounding.smallest
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: Appearance.padding.large
spacing: Appearance.spacing.normal
RowLayout {
spacing: Appearance.spacing.normal
z: 1
CustomRect {
color: Recorder.running ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3secondaryContainer
implicitHeight: {
const h = icon.implicitHeight + Appearance.padding.smaller * 2;
return h - (h % 2);
}
implicitWidth: implicitHeight
radius: Appearance.rounding.full
MaterialIcon {
id: icon
anchors.centerIn: parent
anchors.horizontalCenterOffset: -0.5
anchors.verticalCenterOffset: 1.5
color: Recorder.running ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: Appearance.font.size.large
text: "screen_record"
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
CustomText {
Layout.fillWidth: true
elide: Text.ElideRight
font.pointSize: Appearance.font.size.normal
text: qsTr("Screen Recorder")
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: Appearance.font.size.small
text: Recorder.paused ? qsTr("Recording paused") : Recorder.running ? qsTr("Recording running") : qsTr("Recording off")
}
}
CustomSplitButton {
active: menuItems.find(m => root.props.recordingMode === m.icon + m.text) ?? menuItems[0]
disabled: Recorder.running
menuItems: [
MenuItem {
activeText: qsTr("Fullscreen")
icon: "fullscreen"
text: qsTr("Record fullscreen")
onClicked: Recorder.start()
},
MenuItem {
activeText: qsTr("Region")
icon: "screenshot_region"
text: qsTr("Record region")
onClicked: Recorder.start(["-r"])
},
MenuItem {
activeText: qsTr("Fullscreen")
icon: "select_to_speak"
text: qsTr("Record fullscreen with sound")
onClicked: Recorder.start(["-s"])
},
MenuItem {
activeText: qsTr("Region")
icon: "volume_up"
text: qsTr("Record region with sound")
onClicked: Recorder.start(["-s", "-r"])
}
]
menu.onItemSelected: item => root.props.recordingMode = item.icon + item.text
}
}
Loader {
id: listOrControls
property bool running: Recorder.running
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
asynchronous: true
sourceComponent: running ? recordingControls : recordingList
Behavior on Layout.preferredHeight {
id: locHeightAnim
enabled: false
Anim {
}
}
Behavior on running {
SequentialAnimation {
ParallelAnimation {
Anim {
duration: Appearance.anim.durations.small
easing: Appearance.anim.curves.standardAccel
property: "scale"
target: listOrControls
to: 0.7
}
Anim {
duration: Appearance.anim.durations.small
easing: Appearance.anim.curves.standardAccel
property: "opacity"
target: listOrControls
to: 0
}
}
PropertyAction {
property: "enabled"
target: locHeightAnim
value: true
}
PropertyAction {
}
PropertyAction {
property: "enabled"
target: locHeightAnim
value: false
}
ParallelAnimation {
Anim {
duration: Appearance.anim.durations.small
easing: Appearance.anim.curves.standardDecel
property: "scale"
target: listOrControls
to: 1
}
Anim {
duration: Appearance.anim.durations.small
easing: Appearance.anim.curves.standardDecel
property: "opacity"
target: listOrControls
to: 1
}
}
}
}
}
}
Component {
id: recordingList
RecordingList {
props: root.props
visibilities: root.visibilities
}
}
Component {
id: recordingControls
RowLayout {
spacing: Appearance.spacing.normal
CustomRect {
color: Recorder.paused ? DynamicColors.palette.m3tertiary : DynamicColors.palette.m3error
implicitHeight: recText.implicitHeight + Appearance.padding.smaller * 2
implicitWidth: recText.implicitWidth + Appearance.padding.normal * 2
radius: Appearance.rounding.full
Behavior on implicitWidth {
Anim {
}
}
SequentialAnimation on opacity {
alwaysRunToEnd: true
loops: Animation.Infinite
running: !Recorder.paused
Anim {
duration: Appearance.anim.durations.large
easing: Appearance.anim.curves.emphasizedAccel
from: 1
to: 0
}
Anim {
duration: Appearance.anim.durations.extraLarge
easing: Appearance.anim.curves.emphasizedDecel
from: 0
to: 1
}
}
CustomText {
id: recText
anchors.centerIn: parent
animate: true
color: Recorder.paused ? DynamicColors.palette.m3onTertiary : DynamicColors.palette.m3onError
font.family: Appearance.font.family.mono
text: Recorder.paused ? "PAUSED" : "REC"
}
}
CustomText {
font.pointSize: Appearance.font.size.normal
text: {
const elapsed = Recorder.elapsed;
const hours = Math.floor(elapsed / 3600);
const mins = Math.floor((elapsed % 3600) / 60);
const secs = Math.floor(elapsed % 60).toString().padStart(2, "0");
let time;
if (hours > 0)
time = `${hours}:${mins.toString().padStart(2, "0")}:${secs}`;
else
time = `${mins}:${secs}`;
return qsTr("Recording for %1").arg(time);
}
}
Item {
Layout.fillWidth: true
}
IconButton {
checked: Recorder.paused
font.pointSize: Appearance.font.size.large
icon: Recorder.paused ? "play_arrow" : "pause"
label.animate: true
toggle: true
type: IconButton.Tonal
onClicked: {
Recorder.togglePause();
internalChecked = Recorder.paused;
}
}
IconButton {
font.pointSize: Appearance.font.size.large
icon: "stop"
inactiveColour: DynamicColors.palette.m3error
inactiveOnColour: DynamicColors.palette.m3onError
onClicked: Recorder.stop()
}
}
}
}
@@ -1,226 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import ZShell.Models
import qs.Components
import qs.Helpers
import qs.Paths
import qs.Config
ColumnLayout {
id: root
required property var props
required property PersistentProperties visibilities
spacing: 0
WrapperMouseArea {
Layout.fillWidth: true
cursorShape: Qt.PointingHandCursor
onClicked: root.props.recordingListExpanded = !root.props.recordingListExpanded
RowLayout {
spacing: Appearance.spacing.smaller
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
font.pointSize: Appearance.font.size.large
text: "list"
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.normal
text: qsTr("Recordings")
}
IconButton {
icon: root.props.recordingListExpanded ? "unfold_less" : "unfold_more"
label.animate: true
type: IconButton.Text
onClicked: root.props.recordingListExpanded = !root.props.recordingListExpanded
}
}
}
CustomListView {
id: list
Layout.fillWidth: true
Layout.rightMargin: -Appearance.spacing.small
clip: true
implicitHeight: (Appearance.font.size.larger + Appearance.padding.small) * (root.props.recordingListExpanded ? 10 : 3)
CustomScrollBar.vertical: CustomScrollBar {
flickable: list
}
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
Anim {
from: 0.5
property: "scale"
to: 1
}
}
delegate: RowLayout {
id: recording
property string baseName
required property FileSystemEntry modelData
anchors.left: list.contentItem.left
anchors.right: list.contentItem.right
anchors.rightMargin: Appearance.spacing.small
spacing: Appearance.spacing.small / 2
Component.onCompleted: baseName = modelData.baseName
CustomText {
Layout.fillWidth: true
Layout.rightMargin: Appearance.spacing.small / 2
color: DynamicColors.palette.m3onSurfaceVariant
elide: Text.ElideRight
text: {
const time = recording.baseName;
const matches = time.match(/^recording_(\d{4})(\d{2})(\d{2})_(\d{2})-(\d{2})-(\d{2})/);
if (!matches)
return time;
const date = new Date(...matches.slice(1));
date.setMonth(date.getMonth() - 1);
return qsTr("Recording at %1").arg(Qt.formatDateTime(date, Qt.locale()));
}
}
IconButton {
icon: "play_arrow"
type: IconButton.Text
onClicked: {
root.visibilities.sidebar = false;
Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.playback, recording.modelData.path]);
}
}
IconButton {
icon: "folder"
type: IconButton.Text
onClicked: {
root.visibilities.sidebar = false;
Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.explorer, recording.modelData.path]);
}
}
}
displaced: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim {
property: "y"
}
}
Behavior on implicitHeight {
Anim {
}
}
model: FileSystemModel {
nameFilters: ["recording_*.mp4"]
path: Paths.recsdir
sortReverse: true
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
Anim {
property: "scale"
to: 0.5
}
}
Loader {
active: opacity > 0
anchors.centerIn: parent
asynchronous: true
opacity: list.count === 0 ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: ColumnLayout {
spacing: Appearance.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: root.props.recordingListExpanded ? implicitHeight : 0
color: DynamicColors.palette.m3outline
font.pointSize: Appearance.font.size.extraLarge
opacity: root.props.recordingListExpanded ? 1 : 0
scale: root.props.recordingListExpanded ? 1 : 0
text: "scan_delete"
Behavior on Layout.preferredHeight {
Anim {
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
}
RowLayout {
spacing: Appearance.spacing.smaller
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: !root.props.recordingListExpanded ? implicitWidth : 0
color: DynamicColors.palette.m3outline
opacity: !root.props.recordingListExpanded ? 1 : 0
scale: !root.props.recordingListExpanded ? 1 : 0
text: "scan_delete"
Behavior on Layout.preferredWidth {
Anim {
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
}
CustomText {
color: DynamicColors.palette.m3outline
text: qsTr("No recordings found")
}
}
}
}
}
}
@@ -5,7 +5,6 @@ import QtQuick.Layouts
import qs.Components import qs.Components
import qs.Config import qs.Config
import qs.Modules import qs.Modules
import qs.Helpers
import qs.Daemons import qs.Daemons
CustomRect { CustomRect {
@@ -33,7 +32,7 @@ CustomRect {
Toggle { Toggle {
checked: Network.wifiEnabled checked: Network.wifiEnabled
icon: Network.wifiEnabled ? "wifi" : "wifi_off" icon: Network.wifiEnabled ? "wifi" : "wifi_off"
visible: QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi) visible: QSNetwork.Networking.devices.values.length > 0
onClicked: Network.toggleWifi() onClicked: Network.toggleWifi()
} }
@@ -80,13 +79,6 @@ CustomRect {
adapter.enabled = !adapter.enabled; adapter.enabled = !adapter.enabled;
} }
} }
Toggle {
checked: GameMode.enabled
icon: GameMode.enabled ? "videogame_asset" : "videogame_asset_off"
onClicked: GameMode.enabled = !GameMode.enabled
}
} }
} }
@@ -1,14 +1,13 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Modules.Notifications.Sidebar.Utils.Cards import qs.Modules.Notifications.Sidebar.Utils.Cards
import qs.Config import qs.Config
import QtQuick
import QtQuick.Layouts
Item { Item {
id: root id: root
required property Item popouts required property Item popouts
required property PersistentProperties props required property var props
required property var visibilities required property var visibilities
implicitHeight: layout.implicitHeight implicitHeight: layout.implicitHeight
@@ -23,12 +22,6 @@ Item {
IdleInhibit { IdleInhibit {
} }
Record {
props: root.props
visibilities: root.visibilities
z: 1
}
Toggles { Toggles {
popouts: root.popouts popouts: root.popouts
visibilities: root.visibilities visibilities: root.visibilities
+1
View File
@@ -17,6 +17,7 @@ Item {
id: content id: content
panels: root.panels panels: root.panels
sidebarPanel: root.sidebarPanel
visibilities: root.visibilities visibilities: root.visibilities
} }
} }
+3 -5
View File
@@ -100,15 +100,13 @@ Item {
icon: `brightness_${(Math.round(value * 6) + 1)}` icon: `brightness_${(Math.round(value * 6) + 1)}`
value: root.brightness value: root.brightness
onPressedChanged: { onMoved: {
if (!pressed) {
if (Config.osd.allMonBrightness) { if (Config.osd.allMonBrightness) {
root.monitor?.setBrightness(value);
} else {
for (const mon of Brightness.monitors) { for (const mon of Brightness.monitors) {
mon.setBrightness(value); mon.setBrightness(value);
} }
} else {
root.monitor?.setBrightness(value);
}
} }
} }
} }
+1 -1
View File
@@ -17,7 +17,7 @@ CustomRect {
color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2 implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2 implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2
radius: Appearance.rounding.full radius: height / 2
StateLayer { StateLayer {
onClicked: root.visibilities.resources = !root.visibilities.resources onClicked: root.visibilities.resources = !root.visibilities.resources
-24
View File
@@ -104,24 +104,6 @@ Item {
key: "launcher" key: "launcher"
name: "Launcher" name: "Launcher"
} }
ListElement {
icon: "screenshot_region"
key: "screenshot"
name: "Screenshot"
}
ListElement {
icon: "cached"
key: "updates"
name: "Updates"
}
ListElement {
icon: "extension"
key: "plugins"
name: "Extensions"
}
} }
CustomClippingRect { CustomClippingRect {
@@ -189,15 +171,9 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
Layout.preferredWidth: icon.contentWidth Layout.preferredWidth: icon.contentWidth
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
fill: categoryItem.index === clayout.currentIndex ? 1 : 0
font.pointSize: Appearance.font.size.small * 2 font.pointSize: Appearance.font.size.small * 2
text: categoryItem.icon text: categoryItem.icon
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
Behavior on fill {
Anim {
}
}
} }
CustomText { CustomText {
@@ -1,4 +1,3 @@
import QtQuick
import qs.Modules.Settings.Controls import qs.Modules.Settings.Controls
import qs.Config import qs.Config
@@ -81,7 +80,6 @@ SettingsPage {
name: "Sans family" name: "Sans family"
object: Config.appearance.font.family object: Config.appearance.font.family
setting: "sans" setting: "sans"
stringList: Qt.fontFamilies()
} }
Separator { Separator {
@@ -91,7 +89,6 @@ SettingsPage {
name: "Monospace family" name: "Monospace family"
object: Config.appearance.font.family object: Config.appearance.font.family
setting: "mono" setting: "mono"
stringList: Qt.fontFamilies()
} }
} }
+8 -8
View File
@@ -1,4 +1,3 @@
import Quickshell
import QtQuick.Layouts import QtQuick.Layouts
import qs.Modules.Settings.Controls import qs.Modules.Settings.Controls
import qs.Config import qs.Config
@@ -6,8 +5,6 @@ import qs.Config
SettingsPage { SettingsPage {
id: root id: root
required property ShellScreen screen
SettingsSection { SettingsSection {
sectionId: "Wallpaper" sectionId: "Wallpaper"
@@ -32,17 +29,20 @@ SettingsPage {
step: 50 step: 50
} }
Separator { // Separator {
} // }
//
WallpaperCropper { // WallpaperCropper {
} // Layout.fillWidth: true
// Layout.preferredHeight: 300
// }
} }
SettingsSection { SettingsSection {
sectionId: "Wallpapers" sectionId: "Wallpapers"
WallpaperGrid { WallpaperGrid {
Layout.fillWidth: true
} }
} }
} }
+6 -16
View File
@@ -19,8 +19,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Height" name: "Height"
min: 1
object: Config.barConfig object: Config.barConfig
setting: "height" setting: "height"
} }
@@ -29,8 +29,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Rounding" name: "Rounding"
min: 0
object: Config.barConfig object: Config.barConfig
setting: "rounding" setting: "rounding"
} }
@@ -39,21 +39,11 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Border" name: "Border"
min: 0
object: Config.barConfig object: Config.barConfig
setting: "border" setting: "border"
} }
Separator {
}
SettingSpinBox {
min: 0
name: "Smoothing"
object: Config.barConfig
setting: "smoothing"
}
} }
SettingsSection { SettingsSection {
@@ -155,8 +145,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Dock height" name: "Dock height"
min: 1
object: Config.dock object: Config.dock
setting: "height" setting: "height"
} }
@@ -183,8 +173,8 @@ SettingsPage {
} }
SettingStringList { SettingStringList {
addLabel: qsTr("Add pinned app")
name: "Pinned apps" name: "Pinned apps"
addLabel: qsTr("Add pinned app")
object: Config.dock object: Config.dock
setting: "pinnedApps" setting: "pinnedApps"
} }
@@ -193,8 +183,8 @@ SettingsPage {
} }
SettingStringList { SettingStringList {
addLabel: qsTr("Add ignored regex")
name: "Ignored app regexes" name: "Ignored app regexes"
addLabel: qsTr("Add ignored regex")
object: Config.dock object: Config.dock
setting: "ignoredAppRegexes" setting: "ignoredAppRegexes"
} }
+111 -111
View File
@@ -19,8 +19,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Media update interval" name: "Media update interval"
min: 0
object: Config.dashboard object: Config.dashboard
setting: "mediaUpdateInterval" setting: "mediaUpdateInterval"
step: 50 step: 50
@@ -30,8 +30,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Resource update interval" name: "Resource update interval"
min: 0
object: Config.dashboard object: Config.dashboard
setting: "resourceUpdateInterval" setting: "resourceUpdateInterval"
step: 50 step: 50
@@ -41,8 +41,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 0
name: "Drag threshold" name: "Drag threshold"
min: 0
object: Config.dashboard object: Config.dashboard
setting: "dragThreshold" setting: "dragThreshold"
} }
@@ -107,112 +107,112 @@ SettingsPage {
} }
} }
// SettingsSection { SettingsSection {
// sectionId: "Layout Sizes" sectionId: "Layout Sizes"
//
// SettingsHeader { SettingsHeader {
// name: "Layout Sizes" name: "Layout Sizes"
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Tab indicator height" name: "Tab indicator height"
// value: String(Config.dashboard.sizes.tabIndicatorHeight) value: String(Config.dashboard.sizes.tabIndicatorHeight)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Tab indicator spacing" name: "Tab indicator spacing"
// value: String(Config.dashboard.sizes.tabIndicatorSpacing) value: String(Config.dashboard.sizes.tabIndicatorSpacing)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Info width" name: "Info width"
// value: String(Config.dashboard.sizes.infoWidth) value: String(Config.dashboard.sizes.infoWidth)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Info icon size" name: "Info icon size"
// value: String(Config.dashboard.sizes.infoIconSize) value: String(Config.dashboard.sizes.infoIconSize)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Date time width" name: "Date time width"
// value: String(Config.dashboard.sizes.dateTimeWidth) value: String(Config.dashboard.sizes.dateTimeWidth)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Media width" name: "Media width"
// value: String(Config.dashboard.sizes.mediaWidth) value: String(Config.dashboard.sizes.mediaWidth)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Media progress sweep" name: "Media progress sweep"
// value: String(Config.dashboard.sizes.mediaProgressSweep) value: String(Config.dashboard.sizes.mediaProgressSweep)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Media progress thickness" name: "Media progress thickness"
// value: String(Config.dashboard.sizes.mediaProgressThickness) value: String(Config.dashboard.sizes.mediaProgressThickness)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Resource progress thickness" name: "Resource progress thickness"
// value: String(Config.dashboard.sizes.resourceProgessThickness) value: String(Config.dashboard.sizes.resourceProgessThickness)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Weather width" name: "Weather width"
// value: String(Config.dashboard.sizes.weatherWidth) value: String(Config.dashboard.sizes.weatherWidth)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Media cover art size" name: "Media cover art size"
// value: String(Config.dashboard.sizes.mediaCoverArtSize) value: String(Config.dashboard.sizes.mediaCoverArtSize)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Media visualiser size" name: "Media visualiser size"
// value: String(Config.dashboard.sizes.mediaVisualiserSize) value: String(Config.dashboard.sizes.mediaVisualiserSize)
// } }
//
// Separator { Separator {
// } }
//
// SettingReadOnly { SettingReadOnly {
// name: "Resource size" name: "Resource size"
// value: String(Config.dashboard.sizes.resourceSize) value: String(Config.dashboard.sizes.resourceSize)
// } }
// } }
} }
-61
View File
@@ -1,6 +1,4 @@
import Quickshell import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Modules.Settings.Controls import qs.Modules.Settings.Controls
import qs.Config import qs.Config
import qs.Components import qs.Components
@@ -49,15 +47,6 @@ SettingsPage {
object: Config.general object: Config.general
setting: "desktopIcons" setting: "desktopIcons"
} }
Separator {
}
SettingInput {
name: "Date format"
object: Config.general
setting: "dateFormat"
}
} }
SettingsSection { SettingsSection {
@@ -69,7 +58,6 @@ SettingsPage {
CustomSplitButtonRow { CustomSplitButtonRow {
active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1] active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1]
buttonAlias.disabled: !Config.general.color.schemeGeneration
label: qsTr("Scheme mode") label: qsTr("Scheme mode")
menuItems: [ menuItems: [
@@ -103,7 +91,6 @@ SettingsPage {
id: schemeType id: schemeType
active: root.schemeTypeItem(menuItems, Config.colors.schemeType) active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
buttonAlias.disabled: !Config.general.color.schemeGeneration
label: qsTr("Scheme type") label: qsTr("Scheme type")
z: 2 z: 2
@@ -173,69 +160,21 @@ SettingsPage {
} }
Separator { Separator {
shouldBeActive: Config.general.color.schemeGeneration ? 0 : 1
}
SchemesListView {
name: "Color scheme presets"
object: Config.colors.presets
setting: "name"
shouldBeActive: Config.general.color.schemeGeneration ? 0 : 1
stringList: FetchPresets.presetNames()
}
Separator {
shouldBeActive: Config.colors.presets.name !== "" && !Config.general.color.schemeGeneration
}
SchemesListView {
name: "Preset variant"
object: Config.colors.presets
setting: "variant"
shouldBeActive: Config.colors.presets.name !== "" && !Config.general.color.schemeGeneration
stringList: FetchPresets.variantNames(Config.colors.presets.name)
onOptionSet: item => {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${Config.colors.presets.name.toLowerCase()}:${item}`]);
}
}
Separator {
shouldBeActive: Config.colors.presets.variant !== "" && FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant).length > 0 && !Config.general.color.schemeGeneration
}
SchemesListView {
name: "Preset accent"
object: Config.colors.presets
setting: "accent"
shouldBeActive: Config.colors.presets.variant !== "" && FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant).length > 0 && !Config.general.color.schemeGeneration
stringList: FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant)
onOptionSet: item => {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${Config.colors.presets.name.toLowerCase()}:${Config.colors.presets.variant}`, "--accent", `${item}`]);
}
}
Separator {
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
} }
SettingSwitch { SettingSwitch {
name: "Smart color scheme" name: "Smart color scheme"
object: Config.general.color object: Config.general.color
setting: "smart" setting: "smart"
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
} }
Separator { Separator {
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
} }
SettingSpinner { SettingSpinner {
name: "Schedule dark mode" name: "Schedule dark mode"
object: Config.general.color object: Config.general.color
settings: ["scheduleDarkStart", "scheduleDarkEnd", "scheduleDark"] settings: ["scheduleDarkStart", "scheduleDarkEnd", "scheduleDark"]
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
} }
Separator { Separator {
+5 -35
View File
@@ -31,8 +31,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Max fingerprint tries" name: "Max fingerprint tries"
min: 1
object: Config.lock object: Config.lock
setting: "maxFprintTries" setting: "maxFprintTries"
step: 1 step: 1
@@ -41,27 +41,9 @@ SettingsPage {
Separator { Separator {
} }
SettingSwitch {
name: "Show notification details"
object: Config.lock
setting: "showNotifContent"
}
Separator {
}
SettingSwitch {
name: "Show notification icon"
object: Config.lock
setting: "showNotifIcon"
}
Separator {
}
SettingSpinBox { SettingSpinBox {
min: 0
name: "Blur amount" name: "Blur amount"
min: 0
object: Config.lock object: Config.lock
setting: "blurAmount" setting: "blurAmount"
step: 1 step: 1
@@ -71,9 +53,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Height multiplier"
max: 2 max: 2
min: 0.1 min: 0.1
name: "Height multiplier"
object: Config.lock.sizes object: Config.lock.sizes
setting: "heightMult" setting: "heightMult"
step: 0.05 step: 0.05
@@ -83,9 +65,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Aspect ratio"
max: 4 max: 4
min: 0.5 min: 0.5
name: "Aspect ratio"
object: Config.lock.sizes object: Config.lock.sizes
setting: "ratio" setting: "ratio"
step: 0.05 step: 0.05
@@ -95,26 +77,14 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 100
name: "Center width" name: "Center width"
min: 100
object: Config.lock.sizes object: Config.lock.sizes
setting: "centerWidth" setting: "centerWidth"
step: 10 step: 10
} }
} }
SettingsSection {
sectionId: "Greeter"
SettingsHeader {
name: "Greeter"
}
SettingsIconButton {
name: "Install wallpaper and color scheme to greeter"
}
}
SettingsSection { SettingsSection {
sectionId: "Idle" sectionId: "Idle"
@@ -9,8 +9,6 @@ import qs.Modules.Settings.Controls
ColumnLayout { ColumnLayout {
id: root id: root
property bool shouldBeActive: true
function addTimeoutEntry() { function addTimeoutEntry() {
let list = [...Config.general.idle.timeouts]; let list = [...Config.general.idle.timeouts];
@@ -24,13 +22,6 @@ ColumnLayout {
Config.save(); Config.save();
} }
function deleteTimeoutEntry(index) {
let list = [...Config.general.idle.timeouts];
list.splice(index, 1);
Config.general.idle.timeouts = list;
Config.save();
}
function updateTimeoutEntry(i, key, value) { function updateTimeoutEntry(i, key, value) {
const list = [...Config.general.idle.timeouts]; const list = [...Config.general.idle.timeouts];
let entry = list[i]; let entry = list[i];
@@ -42,26 +33,8 @@ ColumnLayout {
Config.save(); Config.save();
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right
height: shouldBeActive ? implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
spacing: Appearance.spacing.smaller spacing: Appearance.spacing.smaller
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Settings { Settings {
name: "Idle Monitors" name: "Idle Monitors"
@@ -72,15 +45,10 @@ ColumnLayout {
SettingList { SettingList {
Layout.fillWidth: true Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
onAddActiveActionRequested: { onAddActiveActionRequested: {
root.updateTimeoutEntry(index, "activeAction", ""); root.updateTimeoutEntry(index, "activeAction", "");
} }
onDeleteRequested: function (index) {
root.deleteTimeoutEntry(index);
}
onFieldEdited: function (key, value) { onFieldEdited: function (key, value) {
root.updateTimeoutEntry(index, key, value); root.updateTimeoutEntry(index, key, value);
} }
-18
View File
@@ -1,18 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
SettingsPage {
SettingsSection {
sectionId: "Plugins"
SettingsHeader {
name: "Plugins"
}
SettingBarEntryList {
name: "Enable or disable plugins"
object: Config.plugins
setting: "entries"
}
}
}
-130
View File
@@ -1,130 +0,0 @@
import qs.Modules.Settings.Controls
import qs.Config
import qs.Components
SettingsPage {
SettingsSection {
sectionId: "Screenshot"
SettingsHeader {
name: "Screenshot"
}
SettingSwitch {
name: "Enable effects"
object: Config.screenshot
setting: "enable_pp"
}
Separator {
}
CustomSplitButtonRow {
// active: true
label: qsTr("Effects mode")
menuItems: [
MenuItem {
icon: "build"
text: qsTr("Manual")
value: "manual"
},
MenuItem {
icon: "rotate_auto"
text: qsTr("Auto")
value: "auto"
}
]
onSelected: item => {
Config.screenshot.mode = item.value;
Config.save();
}
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Corner radius"
object: Config.screenshot
setting: "corner_radius"
step: 1
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Enable drop shadow"
object: Config.screenshot
setting: "drop_shadow"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Enable rounded corners"
object: Config.screenshot
setting: "rounded_corners"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Shadow blur radius"
object: Config.screenshot
setting: "shadow_blur_radius"
step: 1
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Shadow color broken atm"
object: Config.Screenshot
setting: "shadow_color"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Shadow offset X"
object: Config.screenshot
setting: "shadow_offset_x"
step: 1
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Shadow offset Y"
object: Config.screenshot
setting: "shadow_offset_y"
step: 1
visible: Config.screenshot.mode === "manual"
}
}
}
@@ -1,197 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick.Layouts
import QtQuick
import qs.Config
import qs.Helpers
import qs.Components
import qs.Modules.Settings.Controls
CustomClippingRect {
id: root
radius: Appearance.rounding.normal - Appearance.padding.smaller
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.margins: Appearance.padding.large
spacing: Appearance.spacing.large
MaterialIcon {
font.pointSize: Appearance.font.size.larger * 4
text: "update"
}
ColumnLayout {
CustomText {
font.pointSize: Appearance.font.size.large * 2
text: "System updates"
}
RowLayout {
id: row
Layout.fillWidth: true
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: `${Updates.availableUpdates} available updates`
}
CustomRect {
Layout.preferredHeight: 40
Layout.preferredWidth: 150
color: Updates.updating ? DynamicColors.layer(DynamicColors.palette.m3outline, 2) : DynamicColors.palette.m3primary
radius: Appearance.rounding.full
RowLayout {
anchors.centerIn: parent
MaterialIcon {
animate: true
color: DynamicColors.palette.m3onPrimary
font.pointSize: Appearance.font.size.large
text: Updates.updating ? "update" : "download"
}
CustomText {
color: Updates.updating ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onPrimary
text: "Update all"
}
}
StateLayer {
color: DynamicColors.palette.m3onPrimary
disabled: Updates.updating
onClicked: Updates.performSystemUpdate()
}
}
}
}
}
CustomListView {
id: view
readonly property int itemHeight: 50 + Appearance.padding.smaller * 2
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
contentHeight: height
spacing: Appearance.spacing.normal
delegate: CustomRect {
id: update
required property var modelData
readonly property list<string> sections: modelData.update.split(" ")
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: view.itemHeight
implicitWidth: parent.width
radius: Appearance.rounding.small - Appearance.padding.small
RowLayout {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.smaller
anchors.rightMargin: Appearance.padding.smaller
MaterialIcon {
font.pointSize: Appearance.font.size.large * 2
text: "package_2"
}
ColumnLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.preferredHeight: 25
elide: Text.ElideRight
font.pointSize: Appearance.font.size.large
text: update.sections[0]
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
text: Updates.formatUpdateTime(update.modelData.timestamp)
}
}
RowLayout {
Layout.fillHeight: true
Layout.preferredWidth: 500
MarqueeText {
id: versionFrom
Layout.fillHeight: true
Layout.preferredWidth: 225
animate: true
color: DynamicColors.palette.m3tertiary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: true
pauseMs: 4000
text: update.sections[1]
width: 225
}
MaterialIcon {
Layout.fillHeight: true
color: DynamicColors.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge
horizontalAlignment: Text.AlignHCenter
text: "arrow_right_alt"
verticalAlignment: Text.AlignVCenter
}
MarqueeText {
id: versionTo
Layout.fillHeight: true
Layout.preferredWidth: 225
animate: true
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: true
pauseMs: 4000
text: update.sections[3]
width: 225
}
}
IconButton {
Layout.preferredHeight: width
icon: "download"
onClicked: {
Updates.performPackageUpdate(update.sections[0]);
}
}
}
}
model: ScriptModel {
id: script
objectProp: "update"
values: Object.entries(Updates.updates).sort((a, b) => b[1] - a[1]).map(([update, timestamp]) => ({
update,
timestamp
}))
}
}
}
}
+97 -97
View File
@@ -19,8 +19,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Max toasts" name: "Max toasts"
min: 1
object: Config.utilities object: Config.utilities
setting: "maxToasts" setting: "maxToasts"
} }
@@ -29,8 +29,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Panel width" name: "Panel width"
min: 1
object: Config.utilities.sizes object: Config.utilities.sizes
setting: "width" setting: "width"
} }
@@ -39,8 +39,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
min: 1
name: "Toast width" name: "Toast width"
min: 1
object: Config.utilities.sizes object: Config.utilities.sizes
setting: "toastWidth" setting: "toastWidth"
} }
@@ -77,100 +77,100 @@ SettingsPage {
setting: "gameModeChanged" setting: "gameModeChanged"
} }
// Separator { Separator {
// }
//
// SettingSwitch {
// name: "Do not disturb changed"
// object: Config.utilities.toasts
// setting: "dndChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Audio output changed"
// object: Config.utilities.toasts
// setting: "audioOutputChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Audio input changed"
// object: Config.utilities.toasts
// setting: "audioInputChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Caps lock changed"
// object: Config.utilities.toasts
// setting: "capsLockChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Num lock changed"
// object: Config.utilities.toasts
// setting: "numLockChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Keyboard layout changed"
// object: Config.utilities.toasts
// setting: "kbLayoutChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "VPN changed"
// object: Config.utilities.toasts
// setting: "vpnChanged"
// }
//
// Separator {
// }
//
// SettingSwitch {
// name: "Now playing"
// object: Config.utilities.toasts
// setting: "nowPlaying"
// }
} }
// SettingsSection { SettingSwitch {
// sectionId: "VPN" name: "Do not disturb changed"
// object: Config.utilities.toasts
// SettingsHeader { setting: "dndChanged"
// name: "VPN" }
// }
// Separator {
// SettingSwitch { }
// name: "Enable VPN integration"
// object: Config.utilities.vpn SettingSwitch {
// setting: "enabled" name: "Audio output changed"
// } object: Config.utilities.toasts
// setting: "audioOutputChanged"
// Separator { }
// }
// Separator {
// SettingStringList { }
// name: "Provider"
// addLabel: qsTr("Add VPN provider") SettingSwitch {
// object: Config.utilities.vpn name: "Audio input changed"
// setting: "provider" object: Config.utilities.toasts
// } setting: "audioInputChanged"
// } }
Separator {
}
SettingSwitch {
name: "Caps lock changed"
object: Config.utilities.toasts
setting: "capsLockChanged"
}
Separator {
}
SettingSwitch {
name: "Num lock changed"
object: Config.utilities.toasts
setting: "numLockChanged"
}
Separator {
}
SettingSwitch {
name: "Keyboard layout changed"
object: Config.utilities.toasts
setting: "kbLayoutChanged"
}
Separator {
}
SettingSwitch {
name: "VPN changed"
object: Config.utilities.toasts
setting: "vpnChanged"
}
Separator {
}
SettingSwitch {
name: "Now playing"
object: Config.utilities.toasts
setting: "nowPlaying"
}
}
SettingsSection {
sectionId: "VPN"
SettingsHeader {
name: "VPN"
}
SettingSwitch {
name: "Enable VPN integration"
object: Config.utilities.vpn
setting: "enabled"
}
Separator {
}
SettingStringList {
name: "Provider"
addLabel: qsTr("Add VPN provider")
object: Config.utilities.vpn
setting: "provider"
}
}
} }
+2 -31
View File
@@ -1,5 +1,3 @@
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
@@ -22,6 +20,7 @@ Item {
required property PersistentProperties visibilities required property PersistentProperties visibilities
function scrollToSetting(section: string, settingName: string) { function scrollToSetting(section: string, settingName: string) {
// Wait for the StackView transition to complete, then scroll
root.pendingSection = section; root.pendingSection = section;
root.pendingSetting = settingName; root.pendingSetting = settingName;
scrollTimer.restart(); scrollTimer.restart();
@@ -75,12 +74,6 @@ Item {
stack.push(osd); stack.push(osd);
else if (currentCategory === "launcher") else if (currentCategory === "launcher")
stack.push(launcher); stack.push(launcher);
else if (currentCategory === "screenshot")
stack.push(screenshot);
else if (currentCategory === "updates")
stack.push(updates);
else if (currentCategory === "plugins")
stack.push(plugins);
} }
target: root target: root
@@ -136,7 +129,7 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.top: searchBar.bottom anchors.top: searchBar.bottom
anchors.topMargin: Appearance.spacing.smaller anchors.topMargin: Appearance.spacing.smaller
color: DynamicColors.tPalette.m3surfaceContainerLowest color: DynamicColors.tPalette.m3surfaceContainer
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
StackView { StackView {
@@ -160,7 +153,6 @@ Item {
id: background id: background
Cat.Background { Cat.Background {
screen: root.screen
} }
} }
@@ -233,25 +225,4 @@ Item {
Cat.Launcher { Cat.Launcher {
} }
} }
Component {
id: screenshot
Cat.Screenshot {
}
}
Component {
id: updates
Cat.SystemUpdates {
}
}
Component {
id: plugins
Cat.Plugins {
}
}
} }
@@ -1,161 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Components
Item {
id: root
required property string name
required property var object
property alias row: row
required property string setting
property bool shouldBeActive: true
required property list<var> stringList
signal optionSet(option: string)
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.height : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
CustomClippingRect {
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
Layout.preferredWidth: 500
color: DynamicColors.tPalette.m3surfaceContainer
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
CustomRect {
id: searchBox
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: parent.top
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: 42
radius: Appearance.rounding.full
MaterialIcon {
id: searchIcon
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.large
anchors.top: parent.top
font.pointSize: Appearance.font.size.large
text: "search"
verticalAlignment: Text.AlignVCenter
}
CustomTextField {
id: textSearch
anchors.left: searchIcon.right
anchors.leftMargin: Appearance.spacing.small
anchors.right: parent.right
anchors.rightMargin: Appearance.spacing.normal
anchors.verticalCenter: parent.verticalCenter
placeholderText: "Search..."
}
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: searchBox.bottom
bottomLeftRadius: 21
bottomRightRadius: 21
CustomListView {
anchors.fill: parent
clip: true
spacing: Appearance.spacing.small
delegate: CustomRect {
id: delegate
required property string modelData
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 42
radius: Appearance.rounding.smallest
CustomText {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
text: modelData
verticalAlignment: Text.AlignVCenter
}
MaterialIcon {
anchors.fill: parent
anchors.rightMargin: Appearance.padding.normal
color: DynamicColors.palette.m3primary
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignRight
text: "check_circle"
verticalAlignment: Text.AlignVCenter
visible: root.object[root.setting] === delegate.modelData
}
StateLayer {
onClicked: {
root.object[root.setting] = delegate.modelData;
root.optionSet(delegate.modelData);
Config.save();
}
}
}
model: ScriptModel {
values: {
const values = root.stringList;
const search = textSearch.text;
var regex = new RegExp(search, "i");
return values.filter(n => regex.test(n));
}
}
}
}
}
}
}
+2 -21
View File
@@ -6,26 +6,7 @@ import qs.Config
CustomRect { CustomRect {
id: root id: root
property bool shouldBeActive: true Layout.fillWidth: true
Layout.preferredHeight: 1
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.tPalette.m3outlineVariant color: DynamicColors.tPalette.m3outlineVariant
implicitHeight: shouldBeActive ? 1 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
} }
@@ -13,7 +13,6 @@ ColumnLayout {
required property string name required property string name
required property var object required property var object
required property string setting required property string setting
property bool shouldBeActive: true
function addAction() { function addAction() {
const list = [...root.object[root.setting]]; const list = [...root.object[root.setting]];
@@ -45,26 +44,8 @@ ColumnLayout {
Config.save(); Config.save();
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right
height: shouldBeActive ? implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
spacing: Appearance.spacing.smaller spacing: Appearance.spacing.smaller
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
@@ -127,9 +108,6 @@ ColumnLayout {
} }
Separator { Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
} }
RowLayout { RowLayout {
@@ -210,8 +188,6 @@ ColumnLayout {
StringListEditor { StringListEditor {
Layout.fillWidth: true Layout.fillWidth: true
addLabel: qsTr("Add command argument") addLabel: qsTr("Add command argument")
anchors.left: undefined
anchors.right: undefined
values: [...(modelData.command ?? [])] values: [...(modelData.command ?? [])]
onListEdited: function (values) { onListEdited: function (values) {
@@ -220,9 +196,6 @@ ColumnLayout {
} }
Separator { Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
} }
RowLayout { RowLayout {
@@ -241,9 +214,6 @@ ColumnLayout {
} }
Separator { Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
} }
RowLayout { RowLayout {
+13 -44
View File
@@ -6,14 +6,13 @@ import qs.Components
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
CustomRect { ColumnLayout {
id: root id: root
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name required property string name
required property var object required property var object
required property string setting required property string setting
property bool shouldBeActive: true
function addAlias() { function addAlias() {
const list = [...root.object[root.setting]]; const list = [...root.object[root.setting]];
@@ -41,25 +40,8 @@ CustomRect {
Config.save(); Config.save();
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right spacing: Appearance.spacing.smaller
height: shouldBeActive ? layout.implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
@@ -76,14 +58,6 @@ CustomRect {
} }
} }
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.smaller
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
@@ -128,17 +102,15 @@ CustomRect {
} }
CustomRect { CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: 33 Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(fromTextField.contentWidth + Appearance.padding.large * 2, 550), 50)
color: DynamicColors.tPalette.m3surfaceContainerHigh color: DynamicColors.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.full radius: Appearance.rounding.full
CustomTextField { CustomTextField {
id: fromTextField anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.centerIn: parent anchors.rightMargin: Appearance.padding.normal
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
text: modelData.from ?? "" text: modelData.from ?? ""
onEditingFinished: root.updateAlias(index, "from", text) onEditingFinished: root.updateAlias(index, "from", text)
@@ -163,17 +135,15 @@ CustomRect {
} }
CustomRect { CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: 33 Layout.preferredHeight: 33
Layout.preferredWidth: Math.max(Math.min(toTextField.contentWidth + Appearance.padding.large * 2, 550), 50) color: DynamicColors.tPalette.m3surface
color: DynamicColors.tPalette.m3surfaceContainerHigh radius: Appearance.rounding.small
radius: Appearance.rounding.full
CustomTextField { CustomTextField {
id: toTextField anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.centerIn: parent anchors.rightMargin: Appearance.padding.normal
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
text: modelData.to ?? "" text: modelData.to ?? ""
onEditingFinished: root.updateAlias(index, "to", text) onEditingFinished: root.updateAlias(index, "to", text)
@@ -199,5 +169,4 @@ CustomRect {
text: qsTr("Add alias") text: qsTr("Add alias")
} }
} }
}
} }
+346 -47
View File
@@ -25,15 +25,75 @@ Item {
required property var object required property var object
property var pendingCommitEntries: [] property var pendingCommitEntries: []
required property string setting required property string setting
property bool shouldBeActive: true
property int uidCounter: 0 property int uidCounter: 0
property var visualEntries: [] property var visualEntries: []
function beginVisualDrag(uid, modelData, item) {
const pos = item.mapToItem(root, 0, 0);
root.draggedUid = uid;
root.draggedModelData = modelData;
root.dragHeight = item.height;
root.dragStartX = pos.x;
root.dragStartY = pos.y;
root.dragX = pos.x;
root.dragY = pos.y;
root.dragActive = true;
root.dropAnimating = false;
root.pendingCommitEntries = [];
}
function commitVisualOrder(entries) {
const list = [];
for (let i = 0; i < entries.length; i++)
list.push(entries[i].entry);
root.object[root.setting] = list;
Config.save();
root.rebuildVisualEntries();
}
function endVisualDrag() {
const entries = root.visualEntries.slice();
const finalIndex = root.indexForUid(root.draggedUid);
const finalItem = listView.itemAtIndex(finalIndex);
root.dragActive = false;
if (!finalItem) {
root.pendingCommitEntries = entries;
root.finishVisualDrag();
return;
}
const pos = finalItem.mapToItem(root, 0, 0);
root.pendingCommitEntries = entries;
root.dropAnimating = true;
settleX.to = pos.x;
settleY.to = pos.y;
settleAnim.start();
}
function ensureVisualEntries() { function ensureVisualEntries() {
if (!root.dragActive && !root.dropAnimating) if (!root.dragActive && !root.dropAnimating)
root.rebuildVisualEntries(); root.rebuildVisualEntries();
} }
function finishVisualDrag() {
const entries = root.pendingCommitEntries.slice();
root.dragActive = false;
root.dropAnimating = false;
root.draggedUid = "";
root.draggedModelData = null;
root.pendingCommitEntries = [];
root.dragHeight = 0;
root.commitVisualOrder(entries);
}
function iconForId(id) { function iconForId(id) {
switch (id) { switch (id) {
case "workspaces": case "workspaces":
@@ -93,7 +153,7 @@ Item {
case "spacer": case "spacer":
return qsTr("Spacer"); return qsTr("Spacer");
case "activeWindow": case "activeWindow":
return qsTr("Title"); return qsTr("Active window");
case "tray": case "tray":
return qsTr("Tray"); return qsTr("Tray");
case "upower": case "upower":
@@ -103,14 +163,34 @@ Item {
case "clock": case "clock":
return qsTr("Clock"); return qsTr("Clock");
case "notifBell": case "notifBell":
return qsTr("Notifs"); return qsTr("Notification bell");
case "hyprsunset":
return qsTr("Night light");
default: default:
return id; return id;
} }
} }
function moveArrayItem(list, from, to) {
const next = list.slice();
const [item] = next.splice(from, 1);
next.splice(to, 0, item);
return next;
}
function previewVisualMove(from, hovered, before) {
let to = hovered + (before ? 0 : 1);
if (to > from)
to -= 1;
to = Math.max(0, Math.min(visualModel.items.count - 1, to));
if (from === to)
return;
visualModel.items.move(from, to);
root.visualEntries = root.moveArrayItem(root.visualEntries, from, to);
}
function rebuildVisualEntries() { function rebuildVisualEntries() {
const entries = root.object[root.setting] ?? []; const entries = root.object[root.setting] ?? [];
const next = []; const next = [];
@@ -145,31 +225,15 @@ Item {
list[index] = entry; list[index] = entry;
root.object[root.setting] = list; root.object[root.setting] = list;
Config.save(); Config.save();
root.ensureVisualEntries();
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right implicitHeight: layout.implicitHeight
implicitHeight: shouldBeActive ? layout.implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Component.onCompleted: root.rebuildVisualEntries() Component.onCompleted: root.rebuildVisualEntries()
CustomRect { Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.margins: -Appearance.padding.smaller anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer color: DynamicColors.palette.m3primaryContainer
@@ -184,12 +248,39 @@ Item {
} }
} }
RowLayout { ParallelAnimation {
id: settleAnim
onFinished: root.finishVisualDrag()
Anim {
id: settleX
duration: Appearance.anim.durations.normal
property: "dragX"
target: root
}
Anim {
id: settleY
duration: Appearance.anim.durations.normal
property: "dragY"
target: root
}
}
ColumnLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
spacing: Appearance.spacing.smaller
// spacing: Appearance.spacing.smaller CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
DelegateModel { DelegateModel {
id: visualModel id: visualModel
@@ -202,40 +293,248 @@ Item {
} }
} }
Repeater { ListView {
delegate: entryDelegate id: listView
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
boundsBehavior: Flickable.StopAtBounds
clip: false
implicitHeight: contentHeight
implicitWidth: width
interactive: !(root.dragActive || root.dropAnimating)
model: visualModel model: visualModel
spacing: Appearance.spacing.small
add: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
}
addDisplaced: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
displaced: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
move: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
removeDisplaced: Transition {
Anim {
duration: Appearance.anim.durations.normal
property: "y"
}
}
}
}
Loader {
active: root.dragActive || root.dropAnimating
asynchronous: false
sourceComponent: Item {
Drag.active: root.dragActive
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
height: proxyRect.implicitHeight
implicitHeight: proxyRect.implicitHeight
implicitWidth: listView.width
visible: root.draggedModelData !== null
width: listView.width
x: root.dragX
y: root.dragY
z: 100
Drag.source: QtObject {
property string uid: root.draggedUid
property int visualIndex: root.indexForUid(root.draggedUid)
}
CustomRect {
id: proxyRect
color: DynamicColors.tPalette.m3surface
implicitHeight: proxyRow.implicitHeight + Appearance.padding.small * 2
implicitWidth: parent.width
opacity: 0.95
radius: Appearance.rounding.normal
width: parent.width
RowLayout {
id: proxyRow
anchors.fill: parent
anchors.margins: Appearance.padding.small
spacing: Appearance.spacing.normal
CustomRect {
color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.12)
implicitHeight: 32
implicitWidth: implicitHeight
radius: Appearance.rounding.small
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onSurfaceVariant
text: "drag_indicator"
}
}
MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
text: root.iconForId(root.draggedModelData?.entry?.id ?? "")
}
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.labelForId(root.draggedModelData?.entry?.id ?? "")
}
CustomSwitch {
checked: root.draggedModelData?.entry?.enabled ?? true
enabled: false
}
}
}
} }
} }
Component { Component {
id: entryDelegate id: entryDelegate
IconButton { DropArea {
required property int index id: slot
readonly property var entryData: modelData.entry
required property var modelData required property var modelData
readonly property string uid: modelData.uid
Layout.fillWidth: true function previewReorder(drag) {
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0) const source = drag.source;
checked: modelData.entry.enabled ?? true if (!source || !source.uid || source.uid === slot.uid)
font: Appearance.font.family.sans return;
// icon: root.iconForId(modelData.entry.id)
icon: root.labelForId(modelData.entry.id)
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
toggle: true
visible: !["spacer", "upower", "dash", "audio"].some(prefix => modelData.entry.id.startsWith(prefix))
Behavior on Layout.preferredWidth { const from = source.visualIndex;
const hovered = slot.DelegateModel.itemsIndex;
if (from < 0 || hovered < 0)
return;
root.previewVisualMove(from, hovered, drag.y < height / 2);
}
height: entryRow.implicitHeight
implicitHeight: entryRow.implicitHeight
implicitWidth: listView.width
width: ListView.view ? ListView.view.width : listView.width
onEntered: drag => previewReorder(drag)
onPositionChanged: drag => previewReorder(drag)
CustomRect {
id: entryRow
anchors.fill: parent
color: DynamicColors.tPalette.m3surface
implicitHeight: entryLayout.implicitHeight + Appearance.padding.small * 2
implicitWidth: parent.width
opacity: root.draggedUid === slot.uid ? 0 : 1
radius: Appearance.rounding.full
Behavior on opacity {
Anim { Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
} }
} }
onClicked: root.updateEntry(index, internalChecked) RowLayout {
id: entryLayout
anchors.fill: parent
anchors.margins: Appearance.padding.small
spacing: Appearance.spacing.normal
CustomRect {
id: handle
color: Qt.alpha(DynamicColors.palette.m3onSurface, handleDrag.active ? 0.12 : handleHover.hovered ? 0.09 : 0.06)
implicitHeight: 32
implicitWidth: implicitHeight
radius: Appearance.rounding.full
Behavior on color {
CAnim {
}
}
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onSurfaceVariant
text: "drag_indicator"
}
HoverHandler {
id: handleHover
cursorShape: handleDrag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
}
DragHandler {
id: handleDrag
enabled: true
grabPermissions: PointerHandler.CanTakeOverFromAnything
target: null
xAxis.enabled: false
yAxis.enabled: true
onActiveChanged: {
if (active) {
root.beginVisualDrag(slot.uid, slot.modelData, entryRow);
} else if (root.draggedUid === slot.uid) {
root.endVisualDrag();
}
}
onActiveTranslationChanged: {
if (!active || root.draggedUid !== slot.uid)
return;
root.dragX = root.dragStartX;
root.dragY = root.dragStartY + activeTranslation.y;
}
}
}
MaterialIcon {
color: DynamicColors.palette.m3onSurfaceVariant
text: root.iconForId(slot.entryData.id)
}
CustomText {
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.labelForId(slot.entryData.id)
}
CustomSwitch {
Layout.rightMargin: Appearance.padding.small
checked: slot.entryData.enabled ?? true
onToggled: root.updateEntry(slot.DelegateModel.itemsIndex, checked)
}
}
}
} }
} }
} }
@@ -11,7 +11,6 @@ Item {
required property string name required property string name
required property var object required property var object
required property list<string> settings required property list<string> settings
property bool shouldBeActive: true
function commitChoice(choice: int, setting: string): void { function commitChoice(choice: int, setting: string): void {
root.object[setting] = choice; root.object[setting] = choice;
@@ -33,25 +32,8 @@ Item {
return Qt.formatTime(d, "h AP"); return Qt.formatTime(d, "h AP");
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+2 -20
View File
@@ -11,7 +11,6 @@ Item {
required property string name required property string name
required property var object required property var object
required property string setting required property string setting
property bool shouldBeActive: true
function formattedValue(): string { function formattedValue(): string {
const value = root.object[root.setting]; const value = root.object[root.setting];
@@ -22,25 +21,8 @@ Item {
return String(value); return String(value);
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+10 -57
View File
@@ -8,31 +8,12 @@ Item {
required property int index required property int index
required property var modelData required property var modelData
property bool shouldBeActive: true
signal addActiveActionRequested signal addActiveActionRequested
signal deleteRequested(int index)
signal fieldEdited(string key, var value) signal fieldEdited(string key, var value)
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
CustomRect { CustomRect {
anchors.left: parent.left anchors.left: parent.left
@@ -84,64 +65,42 @@ Item {
HoverHandler { HoverHandler {
id: nameHover id: nameHover
}
HoverIconButton {
id: editButton
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large
icon: "edit"
shouldBeVisible: nameHover.hovered && !nameCell.editing
onClicked: nameCell.beginEdit()
} }
CustomText { CustomText {
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: nameHover.hovered ? editButton.width + Appearance.spacing.smaller * 2 : 0 anchors.right: editButton.left
anchors.right: deleteButton.left
anchors.rightMargin: Appearance.spacing.small anchors.rightMargin: Appearance.spacing.small
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight // enable if CustomText supports it elide: Text.ElideRight // enable if CustomText supports it
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: root.modelData.name text: root.modelData.name
visible: !nameCell.editing visible: !nameCell.editing
Behavior on anchors.leftMargin {
Anim {
}
}
} }
HoverIconButton { IconButton {
id: deleteButton id: editButton
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large font.pointSize: Appearance.font.size.large
icon: "delete" icon: "edit"
shouldBeVisible: nameHover.hovered && !nameCell.editing visible: nameHover.hovered && !nameCell.editing
onClicked: root.deleteRequested(root.index) onClicked: nameCell.beginEdit()
} }
CustomRect { CustomRect {
anchors.left: parent.left anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.tPalette.m3surface color: DynamicColors.tPalette.m3surface
implicitHeight: nameEditor.implicitHeight + (Appearance.padding.normal * 2)
implicitWidth: Math.min(nameEditor.contentWidth + (Appearance.padding.normal * 2), parent.width - Appearance.padding.normal)
radius: Appearance.rounding.small radius: Appearance.rounding.small
visible: nameCell.editing visible: nameCell.editing
CustomTextField { CustomTextField {
id: nameEditor id: nameEditor
anchors.centerIn: parent anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, nameCell.width - Appearance.padding.normal)
text: nameCell.draftName text: nameCell.draftName
Keys.onEscapePressed: { Keys.onEscapePressed: {
@@ -194,9 +153,6 @@ Item {
} }
Separator { Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
} }
RowLayout { RowLayout {
@@ -228,9 +184,6 @@ Item {
} }
Separator { Separator {
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
} }
Item { Item {
+5 -28
View File
@@ -11,32 +11,10 @@ Item {
required property string name required property string name
required property var object required property var object
property alias row: row
required property string setting required property string setting
property bool shouldBeActive: true
required property list<var> stringList
signal optionSet Layout.fillWidth: true
Layout.preferredHeight: row.height
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.height : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
RowLayout { RowLayout {
id: row id: row
@@ -60,7 +38,7 @@ Item {
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5 Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
Layout.preferredWidth: 500 Layout.preferredWidth: 500
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale radius: 21 + Appearance.padding.normal
CustomRect { CustomRect {
id: searchBox id: searchBox
@@ -142,18 +120,17 @@ Item {
StateLayer { StateLayer {
onClicked: { onClicked: {
root.object[root.setting] = fontDelegate.modelData; root.object[root.setting] = fontDelegate.modelData;
root.optionSet();
Config.save(); Config.save();
} }
} }
} }
model: ScriptModel { model: ScriptModel {
values: { values: {
const values = root.stringList; const fonts = Qt.fontFamilies();
const search = fontSearch.text; const search = fontSearch.text;
var regex = new RegExp(search, "i"); var regex = new RegExp(search, "i");
return values.filter(n => regex.test(n)); return fonts.filter(n => regex.test(n));
} }
} }
} }
+2 -20
View File
@@ -9,28 +9,10 @@ Item {
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name required property string name
property bool shouldBeActive: true
required property string value required property string value
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+2 -20
View File
@@ -13,28 +13,10 @@ Item {
required property string name required property string name
required property var object required property var object
required property string setting required property string setting
property bool shouldBeActive: true
property real step: 1 property real step: 1
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+2 -20
View File
@@ -11,7 +11,6 @@ Item {
required property string name required property string name
required property var object required property var object
required property list<string> settings required property list<string> settings
property bool shouldBeActive: true
function commitChoice(choice: int, setting: string): void { function commitChoice(choice: int, setting: string): void {
root.object[setting] = choice; root.object[setting] = choice;
@@ -33,25 +32,8 @@ Item {
return Qt.formatTime(d, "h AP"); return Qt.formatTime(d, "h AP");
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
@@ -12,27 +12,9 @@ Item {
required property string name required property string name
required property var object required property var object
required property string setting required property string setting
property bool shouldBeActive: true
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: layout.implicitHeight
implicitHeight: shouldBeActive ? layout.implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
@@ -64,9 +46,6 @@ Item {
StringListEditor { StringListEditor {
Layout.fillWidth: true Layout.fillWidth: true
addLabel: root.addLabel addLabel: root.addLabel
anchors.left: undefined
anchors.right: undefined
anchors.verticalCenter: undefined
values: [...(root.object[root.setting] ?? [])] values: [...(root.object[root.setting] ?? [])]
onListEdited: function (values) { onListEdited: function (values) {
+2 -20
View File
@@ -11,27 +11,9 @@ Item {
required property string name required property string name
required property var object required property var object
required property string setting required property string setting
property bool shouldBeActive: true
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+2 -6
View File
@@ -7,13 +7,9 @@ CustomRect {
id: root id: root
required property string name required property string name
property bool shouldBeActive: true
implicitHeight: 60 Layout.preferredHeight: 60
implicitWidth: 200 Layout.preferredWidth: 200
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
CustomText { CustomText {
anchors.fill: parent anchors.fill: parent
@@ -1,83 +0,0 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Paths
import qs.Components
import qs.Config
import qs.Helpers
Item {
id: root
property alias button: iButton
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
required property string name
property bool shouldBeActive: true
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -Appearance.padding.smaller
color: DynamicColors.palette.m3primaryContainer
opacity: root.highlighted ? 0.5 : 0
radius: Appearance.rounding.small
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.normal
}
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Appearance.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
CustomText {
id: text
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
font.pointSize: Appearance.font.size.larger
text: root.name
}
IconButton {
id: iButton
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
icon: "download"
onClicked: {
const lockBg = `${Paths.state}/lockscreen_bg.png`;
const scheme = `${Paths.state}/scheme.json`;
const face = `${Paths.home}/.face`;
const destination = "/etc/zshell-greeter/images";
Quickshell.execDetached(["pkexec", "sh", "-c", `mkdir -p ${destination}; cp ${lockBg} ${destination}; cp ${scheme} /etc/zshell-greeter; cp ${face} ${destination}`]);
}
}
}
}
+1 -7
View File
@@ -60,18 +60,12 @@ CustomClippingRect {
} }
} }
Column { ColumnLayout {
id: clayout id: clayout
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: Appearance.spacing.small spacing: Appearance.spacing.small
// move: Transition {
// Anim {
// properties: "y"
// }
// }
} }
} }
} }
+4 -25
View File
@@ -10,39 +10,18 @@ CustomRect {
property real contentPadding: Appearance.padding.large property real contentPadding: Appearance.padding.large
property string sectionId: "" property string sectionId: ""
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.preferredHeight: layout.implicitHeight + contentPadding * 2
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: layout.height + contentPadding * 2
radius: Appearance.rounding.normal - Appearance.padding.smaller radius: Appearance.rounding.normal - Appearance.padding.smaller
Behavior on implicitHeight { ColumnLayout {
Anim {
}
}
Behavior on y {
Anim {
}
}
Column {
id: layout id: layout
anchors.left: parent.left anchors.left: parent.left
anchors.margins: root.contentPadding anchors.margins: root.contentPadding
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.verticalCenter: parent.verticalCenter
// anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.normal spacing: Appearance.spacing.normal
Behavior on height {
Anim {
}
}
move: Transition {
Anim {
properties: "y"
}
}
} }
} }
@@ -12,29 +12,12 @@ CustomRect {
property alias expanded: menu.expanded property alias expanded: menu.expanded
property alias label: label property alias label: label
property alias menu: menu property alias menu: menu
property bool shouldBeActive: true
property alias text: label.text property alias text: label.text
color: enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2) color: enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
opacity: shouldBeActive ? 1 : 0
radius: Appearance.rounding.full radius: Appearance.rounding.full
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
z: expanded ? 100 : 0 z: expanded ? 100 : 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
CustomText { CustomText {
id: label id: label
+1 -20
View File
@@ -9,7 +9,6 @@ ColumnLayout {
id: root id: root
property string addLabel: qsTr("Add entry") property string addLabel: qsTr("Add entry")
property bool shouldBeActive: true
property var values: [] property var values: []
signal listEdited(var values) signal listEdited(var values)
@@ -32,26 +31,8 @@ ColumnLayout {
root.listEdited(list); root.listEdited(list);
} }
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right
height: shouldBeActive ? implicitHeight : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
spacing: Appearance.spacing.smaller spacing: Appearance.spacing.smaller
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
Repeater { Repeater {
model: [...root.values] model: [...root.values]
+71 -248
View File
@@ -1,279 +1,102 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Hyprland
import ZShell.Internal
import qs.Config import qs.Config
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
Item { ColumnLayout {
id: wrapper
property bool changesMade: false
property bool shouldBeActive: true
signal requestCrop
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: shouldBeActive ? 400 : 0
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
}
IconButton {
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: parent.top
icon: "check"
opacity: wrapper.changesMade ? 1 : 0
scale: wrapper.changesMade ? 1 : 0
z: 2
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
onClicked: {
wrapper.requestCrop();
wrapper.changesMade = false;
}
}
RowLayout {
id: root id: root
anchors.fill: parent spacing: 15
spacing: Appearance.spacing.normal width: Math.min(parent ? parent.width : 600, 600)
Repeater { Rectangle {
model: ScriptModel { id: previewContainer
values: [...Quickshell.screens].sort((a, b) => {
return a.x - b.x;
})
}
Item {
id: delegate
required property ShellScreen modelData
function applyCrop(): void {
const croprect = cropRect.mapToItem(scaledImg, 0, 0, cropRect.width, cropRect.height);
const upscaledRect = Qt.rect((croprect.x - cropRect.imageX) / scaledImg.paintedWidth, (croprect.y - cropRect.imageY) / scaledImg.paintedHeight, croprect.width / scaledImg.paintedWidth, croprect.height / scaledImg.paintedHeight);
Wallpapers.setCrop(delegate.modelData.name, upscaledRect, croprect, cropRect.zoom);
}
function zoomClipRect(zoom: real): void {
let oldCenterX = cropRect.x + cropRect.width * 0.5;
let oldCenterY = cropRect.y + cropRect.height * 0.5;
cropRect.zoom = zoom;
cropRect.x = oldCenterX - cropRect.width * 0.5;
cropRect.y = oldCenterY - cropRect.height * 0.5;
cropRect.clampToBounds();
}
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.preferredWidth: height * (Quickshell.screens.length > 0 ? (Quickshell.screens[0].height / Math.max(1, Quickshell.screens[0].width)) : 16 / 9)
clip: true
color: DynamicColors.palette.m3surfaceContainer
radius: Config.appearance.rounding.scale * 10
Connections { Image {
function onRequestCrop(): void { id: img
delegate.applyCrop();
}
target: wrapper anchors.fill: parent
}
RowLayout {
id: sliderLayout
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 30
spacing: Appearance.spacing.large
CustomText {
text: qsTr("Crop scale")
}
CustomSlider {
id: zoomSlider
Layout.fillWidth: true
Layout.preferredHeight: 10
from: 1.0
to: 5.0
value: cropRect.zoom
onMoved: {
delegate.zoomClipRect(value);
wrapper.changesMade = true;
}
}
}
CachingImage {
id: scaledImg
property var displayData
property real monitorScale: 1.0
anchors.bottom: sliderLayout.top
anchors.bottomMargin: Appearance.spacing.normal
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
// retainWhileLoading: true
source: Wallpapers.current source: Wallpapers.current
sourceSize.height: parent.height
sourceSize.width: parent.width
onPaintedWidthChanged: { Rectangle {
if (paintedWidth > 0) {
scaledImg.displayData = Wallpapers.getCrop(delegate.modelData.name);
cropRect.zoom = Wallpapers.getCrop(delegate.modelData.name).zoom;
cropRect.restoreFromData();
}
}
onSourceChanged: cropRect.clampToBounds()
onStatusChanged: if (scaledImg.status == Image.Ready)
cropRect.clampToBounds()
CustomText {
id: monitorId
anchors.centerIn: parent
color: Qt.alpha(DynamicColors.palette.m3surface, 0.85)
font.pointSize: Appearance.font.size.large * 4
style: Text.Outline
styleColor: DynamicColors.palette.m3onSurface
text: delegate.modelData.name
}
CustomRect {
id: cropRect id: cropRect
property real aspectRatio: delegate.modelData.width / delegate.modelData.height property real cropHeight: (imageAspect > screenAspect ? paintedHeight : paintedWidth / screenAspect) / Config.background.zoom
readonly property real baseHeight: baseWidth / aspectRatio property real cropWidth: (imageAspect > screenAspect ? paintedHeight * screenAspect : paintedWidth) / Config.background.zoom
readonly property real baseWidth: { property real imageAspect: Math.max(1, paintedWidth) / Math.max(1, paintedHeight)
let fittedHeight = scaledImg.paintedHeight; property real paintedHeight: img.paintedHeight > 0 ? img.paintedHeight : img.height
let fittedWidth = fittedHeight * aspectRatio; property real paintedWidth: img.paintedWidth > 0 ? img.paintedWidth : img.width
property real paintedX: (img.width - paintedWidth) / 2
if (fittedWidth > scaledImg.paintedWidth) { property real paintedY: (img.height - paintedHeight) / 2
fittedWidth = scaledImg.paintedWidth; property real screenAspect: Quickshell.screens.length > 0 ? (Quickshell.screens[0].width / Math.max(1, Quickshell.screens[0].height)) : 16 / 9
fittedHeight = fittedWidth / aspectRatio;
}
return fittedWidth;
}
readonly property real imageX: (scaledImg.width - scaledImg.paintedWidth) / 2
readonly property real imageY: (scaledImg.height - scaledImg.paintedHeight) / 2
property real imgAspectRatio: scaledImg.paintedWidth / scaledImg.paintedHeight
property real zoom: scaledImg.displayData.zoom
function centerInImage() {
x = imageX + (scaledImg.paintedWidth - width) / 2;
y = imageY + (scaledImg.paintedHeight - height) / 2;
}
function clampToBounds() {
x = Math.max(imageX, Math.min(x, imageX + scaledImg.paintedWidth - width));
y = Math.max(imageY, Math.min(y, imageY + scaledImg.paintedHeight - height));
}
function restoreFromData() {
let data = scaledImg.displayData;
if (data && data.scaledX !== 0 || data.scaledY !== 0 || data.scaledWidth !== 0 || data.scaledHeight !== 0) {
x = data.scaledX;
y = data.scaledY;
clampToBounds();
} else {
zoom = 1.0;
centerInImage();
}
}
border.color: DynamicColors.palette.m3primary border.color: DynamicColors.palette.m3primary
border.width: 2 border.width: 2
height: baseHeight / zoom color: Qt.alpha(DynamicColors.palette.m3primaryContainer, 0.3)
opacity: 1 height: cropHeight
width: baseWidth / zoom width: cropWidth
x: paintedX + (paintedWidth - width) * Config.background.alignX
y: paintedY + (paintedHeight - height) * Config.background.alignY
Behavior on opacity { DragHandler {
Anim { target: null
onActiveTranslationChanged: {
if (active) {
let newX = cropRect.x - cropRect.paintedX + translation.x;
let newY = cropRect.y - cropRect.paintedY + translation.y;
let rangeX = cropRect.paintedWidth - cropRect.width;
let rangeY = cropRect.paintedHeight - cropRect.height;
if (rangeX > 0) {
let valX = newX / rangeX;
Config.background.alignX = Math.max(0.0, Math.min(1.0, valX));
Config.save();
}
if (rangeY > 0) {
let valY = newY / rangeY;
Config.background.alignY = Math.max(0.0, Math.min(1.0, valY));
Config.save();
}
}
} }
} }
Component.onCompleted: clampToBounds() PinchHandler {
onHeightChanged: clampToBounds() maximumScale: 5.0
onWidthChanged: clampToBounds() minimumScale: 1.0
target: null
onActiveScaleChanged: {
if (active) {
let newZoom = Config.background.zoom * (1 / (1 + (activeScale - 1) * 0.1));
Config.background.zoom = Math.max(1.0, Math.min(newZoom, 5.0));
}
}
}
}
}
} }
MouseArea { SettingSpinBox {
id: mouse max: 5.0
min: 1.0
function updateCrop(mouseX, mouseY) { name: "Zoom"
let nx = mouseX - cropRect.width * 0.5; object: Config.background
let ny = mouseY - cropRect.height * 0.5; setting: "zoom"
step: 0.1
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + scaledImg.paintedWidth - cropRect.width));
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + scaledImg.paintedHeight - cropRect.height));
cropRect.x = nx;
cropRect.y = ny;
}
anchors.fill: parent
hoverEnabled: true
preventStealing: true
onPositionChanged: mouse => {
if (pressed) {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
}
onPressed: mouse => {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
onReleased: {
wrapper.changesMade = true;
}
}
}
}
}
} }
} }
+35 -47
View File
@@ -12,30 +12,37 @@ GridView {
readonly property int columnsCount: Math.max(1, Math.floor(width / minCellWidth)) readonly property int columnsCount: Math.max(1, Math.floor(width / minCellWidth))
readonly property int minCellWidth: 200 + Appearance.spacing.normal readonly property int minCellWidth: 200 + Appearance.spacing.normal
property bool shouldBeActive: true
anchors.left: parent.left Layout.preferredHeight: contentHeight
anchors.right: parent.right
cellHeight: 140 + Appearance.spacing.normal cellHeight: 140 + Appearance.spacing.normal
cellWidth: width / columnsCount cellWidth: width / columnsCount
clip: true clip: true
implicitHeight: shouldBeActive ? contentHeight : 0
interactive: false interactive: false
model: Wallpapers.list model: Wallpapers.list
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
visible: opacity > 0
delegate: Item { delegate: Item {
required property int index required property int index
readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent
readonly property real itemMargin: Appearance.spacing.normal readonly property real itemMargin: Appearance.spacing.normal / 2
readonly property real itemRadius: Appearance.rounding.small readonly property real itemRadius: Appearance.rounding.normal
required property var modelData required property var modelData
height: root.cellHeight height: root.cellHeight
width: root.cellWidth width: root.cellWidth
StateLayer {
function onClicked(): void {
Wallpapers.setWallpaper(modelData.path);
}
anchors.bottomMargin: itemMargin
anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
radius: itemRadius
}
CustomClippingRect { CustomClippingRect {
id: image id: image
@@ -46,6 +53,8 @@ GridView {
anchors.topMargin: itemMargin anchors.topMargin: itemMargin
antialiasing: true antialiasing: true
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
layer.enabled: true
layer.smooth: true
radius: itemRadius radius: itemRadius
CachingImage { CachingImage {
@@ -91,13 +100,29 @@ GridView {
} }
} }
Timer {
id: fallbackTimer
property bool triggered: false
interval: 800
running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null
onTriggered: triggered = true
}
}
Rectangle { Rectangle {
anchors.bottomMargin: itemMargin
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
antialiasing: true antialiasing: true
border.color: DynamicColors.palette.m3primary border.color: DynamicColors.palette.m3primary
border.width: isCurrent ? 2 : 0 border.width: isCurrent ? 2 : 0
color: "transparent" color: "transparent"
radius: itemRadius + 2 radius: itemRadius - border.width
smooth: true smooth: true
Behavior on border.width { Behavior on border.width {
@@ -117,42 +142,5 @@ GridView {
visible: isCurrent visible: isCurrent
} }
} }
Timer {
id: fallbackTimer
property bool triggered: false
interval: 800
running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null
onTriggered: triggered = true
}
}
StateLayer {
function onClicked(): void {
Wallpapers.setWallpaper(modelData.path);
}
anchors.bottomMargin: itemMargin
anchors.fill: parent
anchors.leftMargin: itemMargin
anchors.rightMargin: itemMargin
anchors.topMargin: itemMargin
radius: itemRadius
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on y {
Anim {
}
} }
} }
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -6,7 +6,7 @@ import QtQuick.Controls
import qs.Components import qs.Components
import qs.Config import qs.Config
import "../../scripts/fuzzysort.js" as Fuzzy import "../../scripts/fuzzysort.js" as Fuzzy
import "../../scripts/SettingsIndex.mjs" as SettingsIndex import "./SettingsIndex.mjs" as SettingsIndex
Item { Item {
id: root id: root
@@ -53,10 +53,11 @@ Item {
Shortcut { Shortcut {
sequence: "/" sequence: "/"
onActivated: searchField.forceActiveFocus() onActivated: searchField.forceActiveFocus()
} }
Component.onCompleted: console.log(root.height)
ListModel { ListModel {
id: resultsModel id: resultsModel
} }
+30 -9
View File
@@ -7,24 +7,45 @@ import qs.Helpers
Item { Item {
id: root id: root
property real offsetScale: shouldBeActive ? 0 : 1
required property var panels required property var panels
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeActive: visibilities.settings
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.topMargin: (-implicitHeight - 5) * offsetScale implicitHeight: 0
implicitHeight: content.implicitHeight
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
opacity: 1 - offsetScale visible: height > 0
visible: offsetScale < 1
states: State {
name: "visible"
when: root.visibilities.settings
PropertyChanges {
root.implicitHeight: content.implicitHeight
}
}
transitions: [
Transition {
from: ""
to: "visible"
Behavior on offsetScale {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial easing.bezierCurve: MaterialEasing.expressiveEffects
property: "implicitHeight"
target: root
}
},
Transition {
from: "visible"
to: ""
Anim {
easing.bezierCurve: MaterialEasing.expressiveEffects
property: "implicitHeight"
target: root
} }
} }
]
CustomClippingRect { CustomClippingRect {
anchors.fill: parent anchors.fill: parent

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