10 Commits
Author SHA1 Message Date
zach 7cc673430c separate dashboard and settings changes to new branch
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 5s
JS/TS / lint (pull_request) Successful in 7s
Python / static (pull_request) Successful in 26s
Rust / build (pull_request) Successful in 46s
Python / verify (pull_request) Successful in 1m38s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 49s
2026-09-30 14:48:38 +02:00
zach 08bb221699 feat: config object migration path and add new options to settings 2026-09-30 14:40:22 +02:00
zach 60293abe61 fix: define declared method + insert new bar entries to config file list + rename Hyprsunset -> Nightlight 2026-09-30 14:33:58 +02:00
zach a22022e969 fix: changesMade bool now binding for robustness 2026-09-30 14:26:59 +02:00
zach 62e19b5261 fix: show bar on hover 2026-09-30 14:26:59 +02:00
zach d76aaf093b fix: normalize zoom slider to linear scale 2026-09-30 14:26:59 +02:00
zach 4ebb4ce859 feat: wallpaper cropper sdf shader rectangle, better confirmation controls + hide passive tray icons 2026-09-30 14:26:59 +02:00
zach be3c938c39 feat: progress on changing interactions to mousearea again 2026-09-30 14:26:59 +02:00
zach f6fa2f0230 fix: remove clippingrectangle in favor of passing radius to sub-items 2026-09-30 14:26:59 +02:00
zach 4a021d81ac chore: formatter 2026-09-30 14:26:59 +02:00
35 changed files with 950 additions and 582 deletions
+2 -5
View File
@@ -17,12 +17,9 @@ ScrollBar {
property bool shouldBeActive
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
property bool shouldBeActive
property bool inverted
enabled: !ShellState.forActive().isDrawing
implicitWidth: size === 1 ? 0 : isHorizontal ? 0 : Tokens.padding.extraSmall * 2
implicitHeight: size === 1 ? 0 : isHorizontal ? Tokens.padding.extraSmall * 2 : 0
implicitHeight: Tokens.padding.extraSmall * 2
implicitWidth: Tokens.padding.extraSmall * 2
contentItem: Item {
}
+1 -1
View File
@@ -1,7 +1,7 @@
import Quickshell
import QtQuick
import qs.Components
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Modules.Bar.Popouts
+213 -207
View File
@@ -1,309 +1,315 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import QtQuick
import ZShell.Config
import ZShell.Internal
import qs.Components
import qs.Modules.Bar as Bar
import qs.Modules.Bar.Popouts as BarPopouts
import qs.Helpers
import qs.Modules.Bar.Popouts
CustomMouseArea {
id: root
required property ShellScreen screen
required property BarPopouts.Wrapper popouts
required property ScreenState screenState
required property Panels panels
required property Bar.BarLoader bar
required property Item bar
required property real borderThickness
required property bool fullscreen
required property ScreenState screenState
required property Drawing drawing
required property EdgeGeometry geometry
required property Panels panels
required property Wrapper popouts
required property ShellScreen screen
property point dragStart
property bool dashboardShortcutActive
property bool osdShortcutActive
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
property bool setInitialPoint: false
property point dragStart
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = root.borderThickness + panel.y;
return y >= panelY - Config.border.rounding && y <= panelY + panel.height + Config.border.rounding;
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = bar.implicitWidth + panel.x;
return x >= panelX - Config.border.rounding && x <= panelX + panel.width + Config.border.rounding;
function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
}
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < bar.implicitWidth + panel.x + panel.width && withinPanelHeight(panel, x, y);
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inPopoutArea(x: real, y: real): bool {
const panel = panels.popoutsWrapper;
if (!geometry.horizontal)
return inLeftPanel(panel, x, y);
return withinPanelWidth(panel, x, y) && withinPanelHeight(panel, x, y);
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panel.x) && withinPanelHeight(panel, x, y);
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inTopPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property
return y < Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) && withinPanelWidth(panel, x, y);
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
}
function inBottomPanel(panel: Item, x: real, y: real, isCorner = false): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property
return y > height - Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) - (isCorner ? Config.border.rounding : 0) && withinPanelWidth(panel, x, y);
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness);
return y >= panelY && y <= panelY + panel.height;
}
function onWheel(event: WheelEvent): void {
if (fullscreen)
return;
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + geometry.insetLeft(borderThickness);
return x >= panelX && x <= panelX + panel.width;
}
anchors.fill: parent
acceptedButtons: fullscreen ? Qt.NoButton : Qt.AllButtons
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
hoverEnabled: true
onPressed: event => dragStart = Qt.point(event.x, event.y)
onPressed: event => {
dragStart = Qt.point(event.x, event.y);
if (screenState.isDrawing) {
if (event.button & Qt.RightButton) {
drawing.content.clear();
return;
}
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.beginStroke(event.x, event.y);
}
}
onPressedChanged: {
if (!pressed) {
if (screenState.isDrawing) {
setInitialPoint = false;
drawing.content.endStroke();
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.showHover(mouseX, mouseY);
}
} else {
if (screenState.isDrawing) {
panels.drawing.collapse();
}
}
}
onContainsMouseChanged: {
if (!containsMouse) {
// Only hide if not activated by shortcut
if (!osdShortcutActive) {
screenState.osd = false;
root.panels.osd.hovered = false;
panels.osd.hovered = false;
}
if (!dashboardShortcutActive)
screenState.dashboard = false;
if (!utilitiesShortcutActive)
screenState.utilities = false;
if (!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) {
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
popouts.hasCurrent = false;
bar.closeTray();
}
if (Config.bar.showOnHover)
if (Config.bar.autoHide || bar.fullscreen)
bar.isHovered = false;
if (Config.sidebar.showOnHover)
screenState.sidebar = false;
if (screenState.dashboard && Config.dashboard.showOnHover)
screenState.dashboard = false;
}
}
onPositionChanged: event => {
if (popouts.isDetached)
return;
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (fullscreen) {
root.panels.osd.hovered = inRightPanel(panels.osdWrapper, x, y);
if (screenState.isDrawing) {
if (pressed) {
drawing.content.appendPoint(x, y);
} else {
if (inLeftPanel(panels.drawing, x, y)) {
if (!(pressedButtons & Qt.LeftButton)) {
panels.drawing.expand();
drawing.content.hideHover();
Cursor.clearOverride();
}
} else {
Cursor.setOverride(Qt.BlankCursor);
drawing.content.showHover(x, y);
}
}
if (drawing.content.hoverVisible)
drawing.content.showHover(x, y);
return;
}
// Show bar in non-exclusive mode on hover
if (!screenState.bar && Config.bar.showOnHover && x < bar.clampedWidth)
bar.isHovered = true;
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
// Show/hide bar on drag
if (pressed && dragStart.x < bar.clampedWidth) {
if (dragX > Config.bar.dragThreshold)
screenState.bar = true;
else if (dragX < -Config.bar.dragThreshold)
screenState.bar = false;
}
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
screenState.launcher = true;
if (panels.sidebar.offsetScale === 1) {
// Show osd on hover
const showOsd = inRightPanel(panels.osdWrapper, x, y);
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y);
// Always update visibility based on hover if not in shortcut mode
if (!osdShortcutActive) {
screenState.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
// If hovering over OSD area while in shortcut mode, transition to hover control
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
const showSidebar = pressed && dragStart.x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x);
// Show sidebar on hover (top-right corner, bounded by notification panel height)
if (Config.sidebar.showOnHover) {
const sidebarTriggerY = Math.max(Config.sidebar.minHoverThreshold, panels.notifications.y + panels.notifications.height + borderThickness);
const showSidebarHover = x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x) && y <= sidebarTriggerY;
if (showSidebarHover && !screenState.sidebar)
screenState.sidebar = true;
}
// Show/hide session on drag
if (pressed && inRightPanel(panels.sessionWrapper, dragStart.x, dragStart.y) && withinPanelHeight(panels.sessionWrapper, x, y)) {
if (dragX < -Config.session.dragThreshold)
screenState.session = true;
else if (dragX > Config.session.dragThreshold)
screenState.session = false;
// Show sidebar on drag if in session area and session is nearly fully visible
if (showSidebar && panels.session.offsetScale <= 0 && dragX < -Config.sidebar.dragThreshold)
screenState.sidebar = true;
} else if (showSidebar && dragX < -Config.sidebar.dragThreshold) {
// Show sidebar on drag if not in session area
screenState.sidebar = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
// Show osd on hover
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
// Always update visibility based on hover if not in shortcut mode
if (!osdShortcutActive) {
screenState.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
// If hovering over OSD area while in shortcut mode, transition to hover control
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
// Show/hide session on drag
if (pressed && outOfSidebar && inRightPanel(panels.sessionWrapper, dragStart.x, dragStart.y) && withinPanelHeight(panels.sessionWrapper, x, y)) {
if (dragX < -Config.session.dragThreshold)
screenState.session = true;
else if (dragX > Config.session.dragThreshold)
screenState.session = false;
}
// Show/hide sidebar on hover
if (Config.sidebar.showOnHover && !pressed) {
const sidebarTriggerY = Math.max(Config.sidebar.minHoverThreshold, panels.notifications.y + panels.notifications.height + borderThickness);
const showSidebarHover = x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x) && y <= sidebarTriggerY;
if (showSidebarHover && !screenState.sidebar) {
screenState.sidebar = true;
} else {
const inSidebarArea = inRightPanel(panels.sidebar, x, y) || inRightPanel(panels.sessionWrapper, x, y);
if (!inSidebarArea)
screenState.sidebar = false;
}
}
// Hide sidebar on drag
if (pressed && inRightPanel(panels.sidebar, dragStart.x, 0) && dragX > Config.sidebar.dragThreshold)
screenState.sidebar = false;
}
// Show launcher on hover, or show/hide on drag if hover is disabled
if (Config.launcher.showOnHover) {
if (!screenState.launcher && inBottomPanel(panels.launcher, x, y))
screenState.launcher = true;
} else if (pressed && inBottomPanel(panels.launcher, dragStart.x, dragStart.y) && withinPanelWidth(panels.launcher, x, y)) {
if (dragY < -Config.launcher.dragThreshold)
screenState.launcher = true;
else if (dragY > Config.launcher.dragThreshold)
screenState.launcher = false;
}
// Show dashboard on hover
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboard, x, y);
// Always update visibility based on hover if not in shortcut mode
if (!dashboardShortcutActive) {
screenState.dashboard = showDashboard;
} else if (showDashboard) {
// If hovering over dashboard area while in shortcut mode, transition to hover control
dashboardShortcutActive = false;
}
// Show/hide dashboard on drag (for touchscreen devices)
if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) {
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold)
screenState.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold)
screenState.dashboard = false;
}
// Show utilities on hover
const showUtilities = inBottomPanel(panels.utilities, x, y, true);
// Always update visibility based on hover if not in shortcut mode
if (!utilitiesShortcutActive) {
screenState.utilities = showUtilities;
} else if (showUtilities) {
// If hovering over utilities area while in shortcut mode, transition to hover control
utilitiesShortcutActive = false;
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && (Config.dashboard.showOnHover || !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y))) {
if (dragY > 20) {
screenState.settings = true;
} else if (dragY < -20) {
screenState.settings = false;
}
}
// Show popouts on hover
if (x < bar.implicitWidth) {
bar.checkPopout(y);
} else if ((!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) && !inLeftPanel(panels.popoutsWrapper, x, y)) {
popouts.hasCurrent = false;
bar.closeTray();
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
screenState.clipboard = true;
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
if (dragY < -10)
screenState.dock = true;
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
screenState.sidebar = true;
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
bar.isHovered = true;
if (panels.sidebar.offsetScale === 1) {
const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!osdShortcutActive) {
screenState.osd = showOsd;
panels.osd.hovered = showOsd;
} else if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
screenState.dock = true;
if (geometry.barContains(x, y))
bar.checkPopout(geometry.axisPos(x, y));
}
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (root.containsMouse)
root.bar.isHovered = true;
}
}
// Monitor individual visibility changes
Connections {
function onLauncherChanged() {
// If launcher is hidden, clear shortcut flags for dashboard and OSD
function onDashboardChanged(): void {
if (root.screenState.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
root.screenState.launcher = false;
root.screenState.settings = false;
root.screenState.sidebar = false;
root.screenState.resources = false;
root.popouts.hasCurrent = false;
} else {
root.dashboardShortcutActive = false;
}
}
function onIsDrawingChanged(): void {
if (root.screenState.isDrawing) {
Cursor.setOverride(Qt.BlankCursor);
} else {
Cursor.clearOverride();
}
}
function onLauncherChanged(): void {
if (!root.screenState.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
// Also hide dashboard and OSD if they're not being hovered
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
const inOsdArea = root.inRightPanel(root.panels.osdWrapper, root.mouseX, root.mouseY);
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.screenState.dashboard = false;
}
if (!inOsdArea) {
root.screenState.osd = false;
root.panels.osd.hovered = false;
}
}
}
function onDashboardChanged() {
if (root.screenState.dashboard) {
// Dashboard became visible, immediately check if this should be shortcut mode
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
} else {
// Dashboard hidden, clear shortcut flag
root.dashboardShortcutActive = false;
if (root.screenState.launcher) {
root.screenState.dashboard = false;
root.screenState.dock = false;
root.screenState.settings = false;
}
}
function onOsdChanged() {
function onOsdChanged(): void {
if (root.screenState.osd) {
// OSD became visible, immediately check if this should be shortcut mode
const inOsdArea = root.inRightPanel(root.panels.osdWrapper, root.mouseX, root.mouseY);
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.osdShortcutActive = true;
}
} else {
// OSD hidden, clear shortcut flag
root.osdShortcutActive = false;
}
}
function onUtilitiesChanged() {
if (root.screenState.utilities) {
// Utilities became visible, immediately check if this should be shortcut mode
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY);
if (!inUtilitiesArea) {
root.utilitiesShortcutActive = true;
}
} else {
// Utilities hidden, clear shortcut flag
root.utilitiesShortcutActive = false;
function onResourcesChanged(): void {
if (root.screenState.resources) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
root.popouts.hasCurrent = false;
root.screenState.settings = false;
root.screenState.dashboard = false;
}
}
function onSettingsChanged(): void {
if (root.screenState.settings) {
root.screenState.resources = false;
root.screenState.dashboard = false;
root.screenState.sidebar = false;
root.panels.popouts.hasCurrent = false;
root.screenState.launcher = false;
}
}
function onSidebarChanged(): void {
if (root.screenState.sidebar) {
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x)
root.screenState.dashboard = false;
root.screenState.settings = false;
root.popouts.hasCurrent = false;
}
}
+5 -6
View File
@@ -37,12 +37,12 @@ Item {
readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar
readonly property alias toasts: toasts
readonly property alias utilities: utilities
required property ScreenState screenState
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent
@@ -84,8 +84,8 @@ Item {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
screen: root.screen
sidebarOrSessionVisible: sidebar.visible
screenState: root.screenState
sidebarOrSessionVisible: sidebar.visible
}
}
@@ -121,8 +121,8 @@ Item {
anchors.right: parent.right
anchors.top: parent.top
panels: root
sidebarPanel: sidebar
screenState: root.screenState
sidebarPanel: sidebar
}
Launcher.Wrapper {
@@ -140,9 +140,8 @@ Item {
anchors.bottom: parent.bottom
anchors.right: parent.right
popouts: popouts
sidebar: sidebar
screenState: root.screenState
sidebar: sidebar
}
Item {
@@ -150,8 +149,8 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
clip: true
implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
implicitWidth: dashboard.implicitWidth
+1 -1
View File
@@ -375,7 +375,7 @@ CustomWindow {
}
}
Interactions {
Interactions2 {
id: interactions
anchors.fill: parent
+5 -10
View File
@@ -13,21 +13,17 @@ import qs.Paths
Singleton {
id: root
readonly property M3Palette current: M3Palette {
}
readonly property M3Palette current: M3Palette {}
property bool currentLight
property string flavour
readonly property bool light: showPreview ? previewLight : currentLight
readonly property M3Palette palette: showPreview ? preview : current
readonly property M3Palette preview: M3Palette {
}
readonly property M3Palette preview: M3Palette {}
property bool previewLight
property string scheme
property bool showPreview
readonly property M3TPalette tPalette: M3TPalette {
}
readonly property Transparency transparency: Transparency {
}
readonly property M3TPalette tPalette: M3TPalette {}
readonly property Transparency transparency: Transparency {}
readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color {
@@ -112,8 +108,7 @@ Singleton {
function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.general.color.mode = mode;
Config.save();
Config.colors.mode = mode;
}
function swapRG(c: color): color {
+6 -6
View File
@@ -11,14 +11,14 @@ import qs.Services
Singleton {
id: root
readonly property int darkEnd: Config.general.color.scheduleDarkEnd
readonly property int darkStart: Config.general.color.scheduleDarkStart
readonly property bool enabled: Config.general.color.scheduleDark && Config.general.color.schemeGeneration
readonly property int darkEnd: Config.colors.scheduleDarkEnd
readonly property int darkStart: Config.colors.scheduleDarkStart
readonly property bool enabled: Config.colors.scheduleDark && Config.colors.schemeGen
function applyDarkMode() {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "dark"]);
Config.general.color.mode = "dark";
Config.colors.mode = "dark";
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-dark'"]);
@@ -30,13 +30,13 @@ Singleton {
function applyLightMode() {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "light"]);
Config.general.color.mode = "light";
Config.colors.mode = "light";
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-light'"]);
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Dark.colors/color_scheme_path=\\1Light.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`]);
if (Config.general.color.neovimColors)
if (Config.colors.neovimColors)
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"onelight\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"]);
}
+7 -7
View File
@@ -9,13 +9,13 @@ Singleton {
id: root
readonly property bool enabled: service.enabled
readonly property int end: Config.general.color.scheduleHyprsunsetEnd
readonly property int end: Config.display.nightlight.scheduleEnd
property bool manualToggle: false
readonly property int start: Config.general.color.scheduleHyprsunsetStart
readonly property int temp: Config.general.color.hyprsunsetTemp
readonly property int start: Config.display.nightlight.scheduleStart
readonly property int temp: Config.display.nightlight.temp
function checkStartup(): void {
if (!Config.general.color.scheduleHyprsunset)
if (!Config.display.nightlight.schedule)
return;
service.apply();
@@ -29,11 +29,11 @@ Singleton {
NightlightManager {
id: service
activeAuto: Config.general.color.scheduleHyprsunset
activeAuto: Config.display.nightlight.schedule
endTime: root.end
startTime: root.start
temp: root.temp
fadeDuration: Config.general.color.nativeFadeDuration / 1000
useNativeNightlight: Config.general.color.useNativeNightlight
fadeDuration: Config.display.nightlight.fadeDuration / 1000
useNativeNightlight: Config.display.nightlight.useNative
}
}
+3 -3
View File
@@ -38,7 +38,7 @@ Searcher {
previewPath = path;
showPreview = true;
if (Config.general.color.schemeGeneration)
if (Config.colors.schemeGen)
previewColorsProc.running = true;
}
@@ -62,8 +62,8 @@ Searcher {
WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 1, 1), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
if (Config.colors.schemeGen)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
}
function stopPreview(): void {
+1 -1
View File
@@ -42,7 +42,7 @@ WidgetBase {
Repeater {
id: repeater
model: SystemTray.items
model: SystemTray.items.values.filter(i => i.status !== Status.Passive && i.hasMenu)
TrayItem {
id: trayItem
+77 -74
View File
@@ -11,8 +11,8 @@ import qs.Components
Item {
id: root
readonly property real nonAnimWidth: view.implicitWidth + clipWrapper.anchors.margins * 2
readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + clipWrapper.anchors.margins + clipWrapper.anchors.topMargin
readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins + viewWrapper.anchors.topMargin
required property ScreenState screenState
@@ -74,115 +74,118 @@ Item {
anchors.margins: Tokens.padding.small
}
CustomClippingRect {
id: clipWrapper
Item {
id: viewWrapper
property int radius: Tokens.rounding.extraLarge - anchors.margins
anchors.top: tabs.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.topMargin: Tokens.spacing.small
anchors.margins: Tokens.padding.small
radius: Tokens.rounding.extraLarge - anchors.margins
clip: true
Item {
id: viewWrapper
Flickable {
id: view
readonly property int currentIndex: root.screenState.dashboardTab
readonly property Item currentItem: {
repeater.count;
return repeater.itemAt(currentIndex);
}
anchors.fill: parent
Flickable {
id: view
implicitWidth: currentItem?.implicitWidth ?? 0
implicitHeight: currentItem?.implicitHeight ?? 0
readonly property int currentIndex: root.screenState.dashboardTab
readonly property Item currentItem: {
repeater.count;
return repeater.itemAt(currentIndex);
}
contentX: currentItem?.x ?? 0
contentWidth: row.implicitWidth
contentHeight: row.implicitHeight
anchors.fill: parent
interactive: false
implicitWidth: currentItem?.implicitWidth ?? 0
implicitHeight: currentItem?.implicitHeight ?? 0
onContentXChanged: {
if (!moving || !currentItem)
return;
contentX: currentItem?.x ?? 0
contentWidth: row.implicitWidth
contentHeight: row.implicitHeight
const x = contentX - currentItem.x;
if (x > currentItem.implicitWidth / 2)
root.screenState.dashboardTab = Math.min(root.screenState.dashboardTab + 1, tabs.count - 1);
else if (x < -currentItem.implicitWidth / 2)
root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0);
}
interactive: false
RowLayout {
id: row
onContentXChanged: {
if (!moving || !currentItem)
return;
Repeater {
id: repeater
const x = contentX - currentItem.x;
if (x > currentItem.implicitWidth / 2)
root.screenState.dashboardTab = Math.min(root.screenState.dashboardTab + 1, tabs.count - 1);
else if (x < -currentItem.implicitWidth / 2)
root.screenState.dashboardTab = Math.max(root.screenState.dashboardTab - 1, 0);
}
model: ScriptModel {
values: root.dashboardTabs
}
RowLayout {
id: row
delegate: Loader {
id: paneLoader
Repeater {
id: repeater
required property int index
required property var modelData
model: ScriptModel {
values: root.dashboardTabs
}
Layout.alignment: Qt.AlignTop
delegate: Loader {
id: paneLoader
sourceComponent: modelData.component
required property int index
required property var modelData
Layout.alignment: Qt.AlignTop
sourceComponent: modelData.component
Component.onCompleted: active = Qt.binding(() => {
if (index === view.currentIndex)
return true;
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
})
}
Component.onCompleted: active = Qt.binding(() => {
if (index === view.currentIndex)
return true;
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
})
}
}
}
Component {
id: dashComponent
Component {
id: dashComponent
Dash {
screenState: root.screenState
}
Dash {
screenState: root.screenState
radius: viewWrapper.radius
}
}
Component {
id: weatherComponent
Component {
id: weatherComponent
WeatherTab {}
WeatherTab {
radius: viewWrapper.radius
}
}
Component {
id: mediaComponent
Component {
id: mediaComponent
Media {
screenState: root.screenState
}
Media {
screenState: root.screenState
radius: viewWrapper.radius
}
}
Component {
id: procsComponent
Component {
id: procsComponent
ProcessesTab {}
ProcessesTab {
radius: viewWrapper.radius
}
}
Behavior on contentX {
Anim {
type: Anim.DefaultEffects
}
Behavior on contentX {
Anim {
type: Anim.DefaultEffects
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ GridLayout {
id: root
readonly property bool dashboardVisible: screenState.dashboard
property int radius: Tokens.rounding.medium
required property int radius
required property ScreenState screenState
columnSpacing: Tokens.spacing.small
+7 -1
View File
@@ -11,12 +11,18 @@ Item {
id: root
property ScreenState screenState
required property int radius
implicitWidth: 1000
implicitHeight: 320
BackgroundShapes {
CustomClippingRect {
anchors.fill: parent
radius: root.radius
BackgroundShapes {
anchors.fill: parent
}
}
RowLayout {
+3 -1
View File
@@ -11,6 +11,8 @@ import qs.Services
Item {
id: root
required property int radius
implicitHeight: layout.implicitHeight
ServiceRef {
@@ -53,7 +55,7 @@ Item {
property list<var> values
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.largeIncreased
radius: root.radius
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: mainLayout.implicitHeight + mainLayout.anchors.margins * 2
+3
View File
@@ -9,6 +9,8 @@ import qs.Services
Item {
id: root
required property int radius
implicitWidth: Tokens.sizes.dashboard.processesWidth
implicitHeight: Tokens.sizes.dashboard.processesHeight
@@ -47,5 +49,6 @@ Item {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
radius: root.radius
}
}
+4 -1
View File
@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
@@ -9,6 +11,7 @@ import qs.Helpers
Item {
id: root
required property int radius
readonly property var today: Weather.forecast && Weather.forecast.length > 0 ? Weather.forecast[0] : null
implicitWidth: layout.implicitWidth > 800 ? layout.implicitWidth : 840
@@ -136,7 +139,7 @@ Item {
Layout.fillWidth: true
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
radius: Tokens.rounding.large
radius: root.radius
color: Colors.tPalette.m3surfaceContainer
ColumnLayout {
+3 -5
View File
@@ -27,7 +27,7 @@ Item {
for (let i = 0; i < count; i++)
height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0;
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x || screenState.dashboard)
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x)
return 0;
if (screenState.osd) {
@@ -47,8 +47,7 @@ Item {
implicitWidth: Config.notifs.sizes.width + padding * 2
Behavior on implicitHeight {
Anim {
}
Anim {}
}
ClippingWrapperRectangle {
@@ -65,8 +64,7 @@ Item {
orientation: Qt.Vertical
spacing: 0
delegate: NotifWrapper {
}
delegate: NotifWrapper {}
displaced: Transition {
Anim {
property: "y"
@@ -8,6 +8,7 @@ import qs.Services
DialogRowButton {
id: root
property var initialSelect
required property var model
property var selectedItem
+282 -164
View File
@@ -13,25 +13,48 @@ import qs.Services
Item {
id: wrapper
property int buttonRowHeight: Tokens.padding.large * 3
property bool changesMade: false
readonly property var currentScreen: screens.length > selectedScreenIndex ? screens[selectedScreenIndex] : null
readonly property real maxZoom: 5.0
readonly property bool changesMade: {
cropRevision;
const r = cropRectLoader.item;
if (!r || !currentScreen || scaledImg.status !== Image.Ready)
return false;
const t = r.savedTarget();
return Math.abs(r.x - t.x) > 0.5 || Math.abs(r.y - t.y) > 0.5 || Math.abs(r.zoom - t.zoom) > 0.001;
}
property int cropRevision: 0
property int buttonRowHeight: Tokens.padding.large * 3
property var screens: []
property int selectedScreenIndex: 0
property bool shouldBeActive: true
function zoomFromSlider(t: real): real {
return 1 / (1 - t * (1 - 1 / maxZoom));
}
function sliderFromZoom(zoom: real): real {
return (1 - 1 / zoom) / (1 - 1 / maxZoom);
}
function applyCrop(): void {
if (!cropRectLoader.item || !currentScreen)
return;
const cropRect = cropRectLoader.item;
const cropXPercent = (cropRect.x - cropRect.imageX) / scaledImg.paintedWidth;
const cropYPercent = (cropRect.y - cropRect.imageY) / scaledImg.paintedHeight;
const cropWidthPercent = cropRect.width / scaledImg.paintedWidth;
const cropHeightPercent = cropRect.height / scaledImg.paintedHeight;
const cropXPercent = (cropRect.x - cropRect.imageX) / cropRect.imageW;
const cropYPercent = (cropRect.y - cropRect.imageY) / cropRect.imageH;
const cropWidthPercent = cropRect.width / cropRect.imageW;
const cropHeightPercent = cropRect.height / cropRect.imageH;
Wallpapers.setCrop(currentScreen.name, Qt.rect(cropXPercent, cropYPercent, cropWidthPercent, cropHeightPercent), cropRect.zoom);
cropRevision++;
}
function refreshScreens(): void {
@@ -79,16 +102,13 @@ Item {
visible: opacity > 0
Behavior on opacity {
Anim {
}
Anim {}
}
Behavior on scale {
Anim {
}
Anim {}
}
Behavior on y {
Anim {
}
Anim {}
}
Component.onCompleted: {
@@ -99,34 +119,10 @@ Item {
Qt.callLater(syncCropToScreen);
}
IconButton {
anchors.margins: Tokens.padding.small
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.applyCrop();
wrapper.changesMade = false;
}
}
ButtonRow {
id: screenSelector
anchors.bottom: scaledImg.top
anchors.bottom: imageSlot.top
anchors.bottomMargin: Tokens.spacing.medium
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.extraLarge * 2
@@ -149,6 +145,7 @@ Item {
isRound: true
shapeMorph: true
text: modelData.name
font: Tokens.font.body.builders.large.weight(Font.DemiBold).build()
onClicked: wrapper.selectScreen(index)
}
@@ -170,24 +167,54 @@ Item {
Layout.leftMargin: Tokens.padding.small
Layout.preferredHeight: Tokens.padding.medium * 3
Layout.rightMargin: Tokens.padding.small
from: 1.0
implicitHeight: Tokens.padding.small * 3
insetIcon: "crop"
to: 5.0
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
value: cropRectLoader.item ? wrapper.sliderFromZoom(cropRectLoader.item.zoom) : 0
onInteraction: value => {
wrapper.zoomClipRect(1 + (value * 4));
wrapper.changesMade = true;
wrapper.zoomClipRect(wrapper.zoomFromSlider(value));
}
}
ButtonRow {
spacing: Tokens.spacing.medium
IconTextButton {
isRound: true
icon: "close"
text: qsTr("Cancel")
font: Tokens.font.body.large
shapeMorph: true
horizontalPadding: Tokens.padding.large
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
inactiveOnColor: Colors.palette.m3onSurfaceVariant
enabled: wrapper.changesMade
onClicked: {
wrapper.syncCropToScreen();
}
}
IconTextButton {
isRound: true
icon: "check"
text: qsTr("Apply")
font: Tokens.font.body.large
shapeMorph: true
horizontalPadding: Tokens.padding.large
inactiveColor: Colors.palette.m3secondary
inactiveOnColor: Colors.palette.m3onSecondary
enabled: wrapper.changesMade
onClicked: {
wrapper.applyCrop();
}
}
}
}
Image {
id: scaledImg
property var displayData
property real monitorScale: 1.0
Item {
id: imageSlot
anchors.bottom: sliderLayout.top
anchors.bottomMargin: Tokens.spacing.medium
@@ -195,147 +222,238 @@ Item {
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: wrapper.buttonRowHeight + Tokens.spacing.medium
// anchors.top: screenSelector.bottom
asynchronous: true
fillMode: Image.PreserveAspectFit
retainWhileLoading: true
source: Wallpapers.current
sourceSize.height: parent.height
sourceSize.width: parent.width
onPaintedWidthChanged: {
if (paintedWidth > 0 && cropRectLoader.item) {
if (!wrapper.screens.length)
wrapper.refreshScreens();
CustomClippingRect {
id: fitBox
wrapper.syncCropToScreen();
}
}
onSourceChanged: {
if (cropRectLoader.item) {
if (!wrapper.screens.length)
wrapper.refreshScreens();
readonly property real aspect: scaledImg.implicitHeight > 0 ? scaledImg.implicitWidth / scaledImg.implicitHeight : 1
readonly property real fitW: Math.min(imageSlot.width, imageSlot.height * aspect)
wrapper.syncCropToScreen();
}
}
onStatusChanged: {
if (scaledImg.status == Image.Ready && cropRectLoader.item) {
if (!wrapper.screens.length)
wrapper.refreshScreens();
implicitHeight: Math.round(fitW / aspect)
implicitWidth: Math.round(fitW)
x: Math.round((imageSlot.width - width) / 2)
y: Math.round((imageSlot.height - height) / 2)
radius: Tokens.rounding.largeIncreased
wrapper.syncCropToScreen();
}
}
Loader {
anchors.centerIn: parent
Loader {
id: cropRectLoader
opacity: scaledImg.status === Image.Ready ? 0 : 1
active: opacity > 0
active: scaledImg.status === Image.Ready
sourceComponent: CustomRect {
implicitWidth: loadingIndicator.implicitSize + Tokens.padding.large * 2
implicitHeight: loadingIndicator.implicitSize + Tokens.padding.large * 2
sourceComponent: Component {
CustomRect {
id: cropRect
color: Colors.palette.m3primaryContainer
radius: Tokens.rounding.full
property real aspectRatio: wrapper.currentScreen ? wrapper.currentScreen.width / wrapper.currentScreen.height : 1
readonly property real baseHeight: baseWidth / aspectRatio
readonly property real baseWidth: {
let fittedHeight = scaledImg.paintedHeight;
let fittedWidth = fittedHeight * aspectRatio;
LoadingIndicator {
id: loadingIndicator
if (fittedWidth > scaledImg.paintedWidth) {
fittedWidth = scaledImg.paintedWidth;
fittedHeight = fittedWidth / aspectRatio;
}
anchors.centerIn: parent
return fittedWidth;
containsIcon: true
implicitSize: Math.min(fitBox.width, fitBox.height) * 0.3
}
readonly property real imageX: (scaledImg.width - scaledImg.paintedWidth) / 2
readonly property real imageY: (scaledImg.height - scaledImg.paintedHeight) / 2
property real imgAspectRatio: scaledImg.paintedWidth / scaledImg.paintedHeight
property real zoom: 1.0
}
function centerInImage() {
x = imageX + (scaledImg.paintedWidth - width) / 2;
y = imageY + (scaledImg.paintedHeight - height) / 2;
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
function clampToBounds() {
x = Math.max(imageX, Math.min(x, imageX + scaledImg.paintedWidth - width));
y = Math.max(imageY, Math.min(y, imageY + scaledImg.paintedHeight - height));
}
function restoreFromData() {
let data = Wallpapers.getCrop(wrapper.currentScreen.name);
if (data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001)) {
zoom = data.zoom > 0 ? data.zoom : 1.0;
x = imageX + (data.x * scaledImg.paintedWidth);
y = imageY + (data.y * scaledImg.paintedHeight);
clampToBounds();
} else {
zoom = 1.0;
centerInImage();
}
}
border.color: Colors.palette.m3primary
border.width: 2
height: baseHeight / zoom
opacity: 1
width: baseWidth / zoom
Behavior on opacity {
Anim {
}
}
Component.onCompleted: {
restoreFromData();
}
onHeightChanged: clampToBounds()
onWidthChanged: clampToBounds()
}
}
}
MouseArea {
id: mouse
Image {
id: scaledImg
function updateCrop(mouseX, mouseY) {
if (!cropRectLoader.item)
return;
anchors.fill: parent
asynchronous: true
fillMode: Image.Stretch
source: Wallpapers.current
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
retainWhileLoading: true
opacity: status === Image.Ready ? 1 : 0
const cropRect = cropRectLoader.item;
let nx = mouseX - cropRect.width * 0.5;
let ny = mouseY - cropRect.height * 0.5;
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + scaledImg.paintedWidth - cropRect.width));
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + scaledImg.paintedHeight - cropRect.height));
cropRect.x = nx;
cropRect.y = ny;
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
}
anchors.fill: parent
hoverEnabled: true
preventStealing: true
Loader {
id: cropRectLoader
onPositionChanged: mouse => {
if (pressed) {
active: scaledImg.status === Image.Ready
sourceComponent: Component {
CustomRect {
id: cropRect
property real aspectRatio: wrapper.currentScreen ? wrapper.currentScreen.width / wrapper.currentScreen.height : 1
readonly property real baseHeight: baseWidth / aspectRatio
readonly property real baseWidth: {
let fittedHeight = imageH;
let fittedWidth = fittedHeight * aspectRatio;
if (fittedWidth > imageW) {
fittedWidth = imageW;
fittedHeight = fittedWidth / aspectRatio;
}
return fittedWidth;
}
readonly property real imageH: Math.round(fitBox.height)
readonly property real imageW: Math.round(fitBox.width)
readonly property real imageX: 0
readonly property real imageY: 0
property real imgAspectRatio: imageH > 0 ? imageW / imageH : 1
property real zoom: 1.0
function centerInImage() {
x = Math.round(imageX + (imageW - width) / 2);
y = Math.round(imageY + (imageH - height) / 2);
}
function clampToBounds() {
x = Math.max(imageX, Math.min(x, imageX + imageW - width));
y = Math.max(imageY, Math.min(y, imageY + imageH - height));
}
// Where the rect should be according to saved data (same rules as a restore).
function savedTarget() {
const data = Wallpapers.getCrop(wrapper.currentScreen.name);
const hasCrop = data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001);
const z = hasCrop && data.zoom > 0 ? data.zoom : 1.0;
const w = Math.round(baseWidth / z);
const h = Math.round(baseHeight / z);
let tx = hasCrop ? Math.round(imageX + data.x * imageW) : Math.round(imageX + (imageW - w) / 2);
let ty = hasCrop ? Math.round(imageY + data.y * imageH) : Math.round(imageY + (imageH - h) / 2);
tx = Math.max(imageX, Math.min(tx, imageX + imageW - w));
ty = Math.max(imageY, Math.min(ty, imageY + imageH - h));
return {
x: tx,
y: ty,
zoom: z
};
}
function restoreFromData() {
const t = savedTarget();
zoom = t.zoom;
x = t.x;
y = t.y;
}
height: Math.round(baseHeight / zoom)
width: Math.round(baseWidth / zoom)
color: zoomSlider.dragging || mouse.pressed ? Qt.alpha(Colors.palette.m3tertiary, 0.3) : "transparent"
Behavior on color {
CAnim {}
}
Component.onCompleted: {
restoreFromData();
}
onHeightChanged: clampToBounds()
onWidthChanged: clampToBounds()
}
}
}
ShaderEffect {
anchors.fill: parent
readonly property real dpr: (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1
function snap(v: real): real {
return Math.round(v * dpr) / dpr;
}
property color borderColor: Colors.palette.m3tertiary
property vector4d cropBox: cropRectLoader.item ? Qt.vector4d(snap(cropRectLoader.item.x), snap(cropRectLoader.item.y), snap(cropRectLoader.item.width), snap(cropRectLoader.item.height)) : Qt.vector4d(0, 0, 0, 0)
property real borderWidth: 2
property real cornerRadius: fitBox.radius
property vector2d itemSize: Qt.vector2d(width, height)
property real pixelScale: dpr
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/crop_sdf.frag.qsb`)
}
MouseArea {
id: mouse
property real grabX: 0
property real grabY: 0
function insideCropRect(mx, my): bool {
const r = cropRectLoader.item;
if (!r)
return false;
return mx >= r.x && mx <= r.x + r.width && my >= r.y && my <= r.y + r.height;
}
function beginGrab(mx, my): void {
const r = cropRectLoader.item;
if (!r)
return;
const inside = insideCropRect(mx, my);
if (inside) {
grabX = mx - r.x;
grabY = my - r.y;
} else {
grabX = r.width * 0.5;
grabY = r.height * 0.5;
}
}
function updateCrop(mx, my): void {
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
let nx = mx - grabX;
let ny = my - grabY;
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + cropRect.imageW - cropRect.width));
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + cropRect.imageH - cropRect.height));
cropRect.x = nx;
cropRect.y = ny;
}
anchors.fill: parent
hoverEnabled: true
preventStealing: true
cursorShape: pressed ? Qt.ClosedHandCursor : insideCropRect(mouseX, mouseY) ? Qt.OpenHandCursor : Qt.PointingHandCursor
onPositionChanged: mouse => {
if (pressed) {
updateCrop(mouse.x, mouse.y);
}
}
onPressed: mouse => {
beginGrab(mouse.x, mouse.y);
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
}
onPressed: mouse => {
updateCrop(mouse.x, mouse.y);
wrapper.changesMade = true;
}
onReleased: {
wrapper.changesMade = true;
}
}
}
}
+11 -11
View File
@@ -43,17 +43,17 @@ Item {
Component.onCompleted: root.loadPage(root.sState.currentPageIdx)
CustomRect {
anchors.fill: parent
color: Qt.alpha(Colors.palette.m3shadow, 0.3)
opacity: root.sState.dimmed ? 1 : 0
visible: opacity > 0
z: 0
Behavior on opacity {
Anim {}
}
}
// CustomRect {
// anchors.fill: parent
// color: Qt.alpha(Colors.palette.m3shadow, 0.3)
// opacity: root.sState.dimmed ? 1 : 0
// visible: opacity > 0
// z: 0
//
// Behavior on opacity {
// Anim {}
// }
// }
}
Connections {
+2 -2
View File
@@ -202,12 +202,12 @@ PageBase {
}
ToggleRow {
checked: Config.general.color.smart
checked: Config.colors.smart
settingAnchor: "services-smart-color-scheme"
subtext: qsTr("Derive theme mode from the wallpaper")
text: qsTr("Smart color scheme")
onToggled: Config.general.color.smart = checked
onToggled: Config.colors.smart = checked
}
SelectRow {
@@ -73,15 +73,15 @@ PageBase {
ToggleRow {
text: qsTr("Dynamic colors")
first: true
subtext: qsTr("Automatic color scheme based on wallpaper is %1").arg(Config.general.color.schemeGeneration ? "enabled" : "disabled")
checked: Config.general.color.schemeGeneration
subtext: qsTr("Automatic color scheme based on wallpaper is %1").arg(Config.colors.schemeGen ? "enabled" : "disabled")
checked: Config.colors.schemeGen
settingAnchor: "colors-fonts-dynamic-colors"
onToggled: {
Config.general.color.schemeGeneration = checked;
Config.colors.schemeGen = checked;
if (checked)
Colors.setMode(Config.general.color.mode);
Colors.setMode(Config.colors.mode);
}
}
@@ -89,7 +89,7 @@ PageBase {
settingAnchor: "colors-fonts-preset-scheme"
model: root.schemes
acceptLabel: qsTr("Confirm")
enabled: !Config.general.color.schemeGeneration
enabled: !Config.colors.schemeGen
header: qsTr("Select scheme")
rowButton.activeItem: root.schemes.find(s => {
var regex = new RegExp(Config.colors.presets.name, "i");
@@ -157,7 +157,7 @@ PageBase {
}
// qmlformat on
acceptLabel: qsTr("Confirm")
enabled: !Config.general.color.schemeGeneration && model.length > 1
enabled: !Config.colors.schemeGen && model.length > 1
header: qsTr("Select variant")
rowButton.activeItem: {
const v = Config.colors.presets.variant ?? "";
@@ -210,7 +210,7 @@ PageBase {
}
// qmlformat on
acceptLabel: qsTr("Confirm")
enabled: !Config.general.color.schemeGeneration && model.length > 1
enabled: !Config.colors.schemeGen && model.length > 1
header: qsTr("Select accent")
rowButton.activeItem: {
const a = Config.colors.presets.accent ?? "";
+60 -19
View File
@@ -96,20 +96,21 @@ PageBase {
TimeDialogSelect {
readonly property string endTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(Config.general.color.scheduleDarkEnd);
d.setMinutes(Config.colors.scheduleDarkEnd);
return Qt.formatTime(d, "hh:mm AP");
}
readonly property string startTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(Config.general.color.scheduleDarkStart);
d.setMinutes(Config.colors.scheduleDarkStart);
return Qt.formatTime(d, "hh:mm AP");
}
rowButton.checked: Config.general.color.scheduleDark
rowButton.onToggled: Config.general.color.scheduleDark = rowButton.checked
start: Config.general.color.scheduleDarkStart
end: Config.general.color.scheduleDarkEnd
rowButton.checked: Config.colors.scheduleDark
rowButton.onToggled: Config.colors.scheduleDark = rowButton.checked
start: Config.colors.scheduleDarkStart
end: Config.colors.scheduleDarkEnd
first: true
last: true
settingAnchor: "style-schedule-dark-mode"
acceptLabel: qsTr("Apply")
rootParent: root.flickable
@@ -122,8 +123,8 @@ PageBase {
}
onAccepted: {
Config.general.color.scheduleDarkStart = startCurrent;
Config.general.color.scheduleDarkEnd = endCurrent;
Config.colors.scheduleDarkStart = startCurrent;
Config.colors.scheduleDarkEnd = endCurrent;
ModeScheduler.checkStartup();
}
}
@@ -131,27 +132,26 @@ PageBase {
TimeDialogSelect {
readonly property string endTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(Config.general.color.scheduleHyprsunsetEnd);
d.setMinutes(Config.display.nightlight.scheduleEnd);
return Qt.formatTime(d, "hh:mm AP");
}
readonly property string startTime: {
var d = new Date(0, 0, 0, 0, 0, 0, 0);
d.setMinutes(Config.general.color.scheduleHyprsunsetStart);
d.setMinutes(Config.display.nightlight.scheduleStart);
return Qt.formatTime(d, "hh:mm AP");
}
Layout.topMargin: Tokens.spacing.extraSmall / 2 - parent.spacing
rowButton.checked: Config.general.color.scheduleHyprsunset
rowButton.onToggled: Config.general.color.scheduleHyprsunset = rowButton.checked
start: Config.general.color.scheduleHyprsunsetStart
end: Config.general.color.scheduleHyprsunsetEnd
last: true
settingAnchor: "style-schedule-hyprsunset"
rowButton.checked: Config.display.nightlight.schedule
rowButton.onToggled: Config.display.nightlight.schedule = rowButton.checked
start: Config.display.nightlight.scheduleStart
end: Config.display.nightlight.scheduleEnd
first: true
settingAnchor: "style-schedule-nightlight"
acceptLabel: qsTr("Apply")
rootParent: root.flickable
header: qsTr("Select time")
subtext: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
label: qsTr("Schedule hyprsunset")
subtext: qsTr("Nightlight will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
label: qsTr("Schedule nightlight")
onOpenChanged: {
root.sState.dimmed = open;
@@ -163,5 +163,46 @@ PageBase {
Nightlight.checkStartup();
}
}
ToggleRow {
Layout.topMargin: Tokens.spacing.extraSmall / 2 - parent.spacing
checked: Config.display.nightlight.useNative
settingAnchor: "display-nightlight-useNative"
text: qsTr("Use native")
subtext: qsTr("Use native shader nightlight for more features")
onToggled: Config.display.nightlight.useNative = checked
}
SpinRow {
Layout.topMargin: Tokens.spacing.extraSmall / 2 - parent.spacing
from: 2000
settingAnchor: "display-nightlight-temp"
stepSize: 100
subtext: qsTr("How warm/orange the screen will be. Lower is warmer (Kelvin)")
text: qsTr("Temperature")
to: 6500
value: Config.display.nightlight.temp
onMoved: v => Config.display.nightlight.temp = v
}
SpinRow {
Layout.topMargin: Tokens.spacing.extraSmall / 2 - parent.spacing
from: 0
settingAnchor: "display-nightlight-fade-duration"
enabled: Config.display.nightlight.useNative
last: true
stepSize: 1
subtext: qsTr("How long it takes for the gradual nightlight to finish (s)")
text: qsTr("Fade duration")
to: 60 * 5
value: Config.display.nightlight.fadeDuration / 1000
onMoved: v => Config.display.nightlight.fadeDuration = v * 1000
}
}
}
+5 -2
View File
@@ -4,8 +4,10 @@ qml_module(ZShell-config
DEPENDENCIES
ZShell.Settings
SOURCES
common.hpp common.cpp
rootnodes.hpp rootnodes.cpp
confignode.hpp confignode.cpp
configobject.hpp configobject.cpp
configlist.hpp configlist.cpp
config.hpp config.cpp
migration.hpp migration.cpp
anim.hpp anim.cpp
fontbuilder.hpp fontbuilder.cpp
@@ -17,6 +19,7 @@ qml_module(ZShell-config
clipboard.hpp
colors.hpp
dashboard.hpp
display.hpp
dock.hpp
enums.hpp
general.hpp
+123
View File
@@ -0,0 +1,123 @@
#include "migration.hpp"
#include <QDebug>
namespace ZShell::config {
namespace {
// A stored value is "missing" if the path does not resolve to an entry.
// Note: default-constructed QJsonValue is Null, not Undefined.
bool isMissing(const QJsonValue& v) {
return v.isUndefined() || v.isNull();
}
QJsonValue valueAt(const QJsonObject& obj, const QStringList& path) {
QJsonObject cur = obj;
for (int i = 0; i < path.size() - 1; ++i) {
if (!cur.value(path.at(i)).isObject()) return QJsonValue::Undefined;
cur = cur.value(path.at(i)).toObject();
}
const QString key = path.last();
if (!cur.contains(key)) return QJsonValue::Undefined;
return cur.value(key);
}
QJsonObject placeAt(QJsonObject obj, QStringList path, const QJsonValue& value) {
const QString head = path.takeFirst();
if (path.isEmpty()) {
obj[head] = value;
return obj;
}
QJsonObject child = obj.value(head).isObject() ? obj.value(head).toObject()
: QJsonObject();
obj[head] = placeAt(child, path, value);
return obj;
}
// Removes `path` from `obj`. Returns true if a value was removed.
// Parent objects left empty are removed as well.
bool removeAt(QJsonObject& obj, const QStringList& path) {
const QString head = path.first();
if (path.size() == 1) {
if (!obj.contains(head)) return false;
obj.remove(head);
return true;
}
if (!obj.value(head).isObject()) return false;
QJsonObject child = obj.value(head).toObject();
const bool removed = removeAt(child, path.mid(1));
if (child.isEmpty())
obj.remove(head);
else
obj[head] = child;
return removed;
}
} // namespace
const QList<ConfigMigrationRule>& ConfigMigrations::rules() {
static const QList<ConfigMigrationRule> s_rules = {
migrate(
"general.color.scheduleHyprsunset", "display.nightlight.schedule"),
migrate(
"general.color.scheduleHyprsunsetStart",
"display.nightlight.scheduleStart"),
migrate(
"general.color.scheduleHyprsunsetEnd",
"display.nightlight.scheduleEnd"),
migrate("general.color.scheduleDark", "colors.scheduleDark"),
migrate("general.color.scheduleDarkStart", "colors.scheduleDarkStart"),
migrate("general.color.scheduleDarkEnd", "colors.scheduleDarkEnd"),
migrate("general.color.schemeGeneration", "colors.schemeGen"),
migrate(
"general.color.useNativeNightlight",
"display.nightlight.useNative"),
migrate(
"general.color.nativeFadeDuration",
"display.nightlight.fadeDuration"),
migrate("general.color.hyprsunsetTemp", "display.nightlight.temp"),
migrate("general.color.neovimColors", "colors.neovimColors"),
migrate("general.color.mode", "colors.mode"),
migrate("general.color.smart", "colors.smart"),
};
return s_rules;
}
QJsonObject ConfigMigrations::apply(const QJsonObject& json) {
QJsonObject obj = json;
for (const auto& rule : rules()) {
const QStringList from =
rule.from.split(QLatin1Char('.'), Qt::SkipEmptyParts);
const QStringList to =
rule.to.split(QLatin1Char('.'), Qt::SkipEmptyParts);
if (from.isEmpty() || to.isEmpty()) continue;
const QJsonValue oldValue = valueAt(obj, from);
if (isMissing(oldValue)) continue;
const bool targetExists = !isMissing(valueAt(obj, to));
if (!targetExists) obj = placeAt(obj, to, oldValue);
if (removeAt(obj, from)) {
qInfo() << "Config migration:" << rule.from << "->" << rule.to
<< (targetExists ? "(target already set, old value dropped)"
: "(value moved)");
}
}
return obj;
}
} // namespace ZShell::config
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <QJsonObject>
#include <QStringList>
namespace ZShell::config {
// A single migration rule: move the value stored at the dotted `from` path
// to the dotted `to` path, then delete the old key (and any parent objects
// left empty).
//
// Rules are applied to the raw JSON file on every load, before the schema
// loads it. They are idempotent: once the old key is gone the rule never
// fires again, so no version stamping is needed. Old rules can stay in the
// table forever.
//
// WARNING: the target path must exist in the schema (i.e. a ConfigObject
// property somewhere in the tree). If it does not, the migrated value is
// loaded as an unknown key and silently dropped on the next save.
struct ConfigMigrationRule {
QString from;
QString to;
};
// Helper for writing rules: migrate("general.color.foo", "display.bar.foo")
inline ConfigMigrationRule migrate(const QString& from, const QString& to) {
return {from, to};
}
class ConfigMigrations {
public:
[[nodiscard]] static const QList<ConfigMigrationRule>& rules();
// Applies all rules to `json`, returning the migrated object.
[[nodiscard]] static QJsonObject apply(const QJsonObject& json);
};
} // namespace ZShell::config
+9 -5
View File
@@ -2,6 +2,8 @@
#include <qcursor.h>
#include <qguiapplication.h>
#include <qlogging.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qqmlintegration.h>
@@ -14,23 +16,25 @@ class Cursor : public QObject {
public:
Q_INVOKABLE void setOverride(Qt::CursorShape shape) {
if (m_override)
if (m_shape == shape) return;
if (m_shape)
QGuiApplication::changeOverrideCursor(QCursor(shape));
else {
QGuiApplication::setOverrideCursor(QCursor(shape));
m_override = true;
}
m_shape = shape;
}
Q_INVOKABLE void clearOverride() {
if (!m_override) return;
if (!m_shape) return;
QGuiApplication::restoreOverrideCursor();
m_override = false;
m_shape.reset();
}
private:
bool m_override = false;
std::optional<Qt::CursorShape> m_shape;
};
} // namespace ZShell::internal
+7 -8
View File
@@ -45,8 +45,7 @@ void Memory::tick() {
QStringLiteral("MemTotal: *(\\d+)"));
static const QRegularExpression reAvail(
QStringLiteral("MemAvailable: *(\\d+)"));
static const QRegularExpression reCached(QStringLiteral(
"Cached: *(\\d+)"));
static const QRegularExpression reCached(QStringLiteral("Cached: *(\\d+)"));
const QString text = QString::fromLatin1(data);
const auto totalMatch = reTotal.match(text);
@@ -78,14 +77,14 @@ void Memory::tick() {
const auto pgin = rePgin.match(vtext);
const auto pgout = rePgout.match(vtext);
if (pgin.hasMatch()) {
readKib = static_cast<quint64>(static_cast<double>(
pgin.captured(1).toULongLong()) *
pageKib);
readKib = static_cast<quint64>(
static_cast<double>(pgin.captured(1).toULongLong()) *
pageKib);
}
if (pgout.hasMatch()) {
writeKib = static_cast<quint64>(static_cast<double>(
pgout.captured(1).toULongLong()) *
pageKib);
writeKib = static_cast<quint64>(
static_cast<double>(pgout.captured(1).toULongLong()) *
pageKib);
}
}
}
@@ -10,7 +10,6 @@ namespace ZShell::services {
namespace {
constexpr const char* NL_EVENT = "zshell-nightlight";
constexpr int NEUTRAL_KELVIN = 6500;
constexpr int RECONNECT_MS = 2000;
} // namespace
@@ -404,7 +403,6 @@ void NightlightManager::handleEventLine(const QByteArray& line) {
if (line.left(sep) != NL_EVENT) return;
// data = enabled,animating,last,current
const auto fields = line.mid(sep + 2).split(',');
if (fields.size() < 4) return;
+10 -9
View File
@@ -793,8 +793,7 @@ void ProcessModel::scanChunk(
nowTicks > prevTicks ? nowTicks - prevTicks : 0;
entry.cpuPercent =
(static_cast<double>(deltaTicks) /
(ctx.deltaSecs *
static_cast<double>(ctx.clockTicksPerSec))) *
(ctx.deltaSecs * static_cast<double>(ctx.clockTicksPerSec))) *
100.0;
} else {
entry.cpuPercent = 0.0;
@@ -839,7 +838,8 @@ ProcessModel::ScanResult ProcessModel::scanProcesses(
// syscall-bound (open/read/close per file per process) and serializes
// badly on large systems. Keep one pool thread free for this function,
// which itself runs inside the global pool.
int nThreads = qMin(16, QThreadPool::globalInstance()->maxThreadCount() - 1);
int nThreads =
qMin(16, QThreadPool::globalInstance()->maxThreadCount() - 1);
if (pids.size() < 128 || nThreads < 2) {
nThreads = 1;
}
@@ -860,8 +860,8 @@ ProcessModel::ScanResult ProcessModel::scanProcesses(
continue;
}
chunks[t] = pids.mid(a, b - a);
futures.append(QtConcurrent::run(
[&ctx, &chunks, &outs, &uidParts, t]() {
futures.append(
QtConcurrent::run([&ctx, &chunks, &outs, &uidParts, t]() {
scanChunk(ctx, chunks[t], outs[t], uidParts[t]);
}));
}
@@ -880,8 +880,9 @@ ProcessModel::ScanResult ProcessModel::scanProcesses(
}
}
std::sort(
fresh.begin(), fresh.end(), [](const ProcessEntry& a,
const ProcessEntry& b) {
fresh.begin(),
fresh.end(),
[](const ProcessEntry& a, const ProcessEntry& b) {
return a.pid < b.pid;
});
}
@@ -1012,8 +1013,8 @@ void ProcessModel::applyView() {
const FuzzyText prep = fuzzyPrepare(src.command);
fe.commandChars = std::move(prep.chars);
fe.commandClasses = std::move(prep.classes);
fe.hasCommandLine =
!src.commandLine.isEmpty() && src.commandLine != src.command;
fe.hasCommandLine = !src.commandLine.isEmpty() &&
src.commandLine != src.command;
if (fe.hasCommandLine) {
const FuzzyText prepCl = fuzzyPrepare(src.commandLine);
fe.commandLineChars = std::move(prepCl.chars);
+5 -9
View File
@@ -13,21 +13,17 @@ import qs.Paths
Singleton {
id: root
readonly property M3Palette current: M3Palette {
}
readonly property M3Palette current: M3Palette {}
property bool currentLight
property string flavour
readonly property bool light: showPreview ? previewLight : currentLight
readonly property M3Palette palette: showPreview ? preview : current
readonly property M3Palette preview: M3Palette {
}
readonly property M3Palette preview: M3Palette {}
property bool previewLight
property string scheme
property bool showPreview
readonly property M3TPalette tPalette: M3TPalette {
}
readonly property Transparency transparency: Transparency {
}
readonly property M3TPalette tPalette: M3TPalette {}
readonly property Transparency transparency: Transparency {}
readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color {
@@ -112,7 +108,7 @@ Singleton {
function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.general.color.mode = mode;
Config.colors.mode = mode;
}
function swapRG(c: color): color {
+40
View File
@@ -0,0 +1,40 @@
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
vec2 itemSize;
vec4 cropBox;
vec4 borderColor;
float cornerRadius;
float borderWidth;
float pixelScale;
};
float sdRoundBox(vec2 p, vec2 halfSize, float r) {
vec2 q = abs(p) - halfSize + r;
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
}
void main() {
vec2 sz = itemSize * pixelScale;
vec2 p = qt_TexCoord0 * sz;
vec4 box = cropBox * pixelScale;
float r = cornerRadius * pixelScale;
float bw = borderWidth * pixelScale;
float dImage = sdRoundBox(p - sz * 0.5, sz * 0.5, r);
float dCrop = sdRoundBox(p - (box.xy + box.zw * 0.5), box.zw * 0.5, 0.0);
float d = max(dImage, dCrop);
// ramp is exactly 1 physical pixel wide
float outer = clamp(0.5 - d, 0.0, 1.0);
float inner = clamp(0.5 - (d + bw), 0.0, 1.0);
float a = outer - inner;
fragColor = vec4(borderColor.rgb, 1.0) * (borderColor.a * a * qt_Opacity);
}
Binary file not shown.
+2 -2
View File
@@ -625,8 +625,8 @@ def generate(
scheme_type = config["colors"].get("schemeType", "fruit-salad")
scheme = scheme or scheme_type
assert isinstance(scheme, str)
config_mode = config["general"]["color"]["mode"]
smart = bool(config["general"]["color"].get("smart", False))
config_mode = config["colors"]["mode"]
smart = bool(config["colors"].get("smart", False))
scheme_class = get_scheme_class(scheme)
p_variant = "default"
+6 -12
View File
@@ -23,33 +23,27 @@ ShellRoot {
settings.watchFiles: true
Drawers {
}
Drawers {}
Wallpaper {
}
Wallpaper {}
AreaPicker {
}
AreaPicker {}
Lock {
id: lock
}
Shortcuts {
}
Shortcuts {}
IdleMonitors {
lock: lock
}
Polkit {
}
Polkit {}
LazyLoader {
activeAsync: root.laptop
component: BatteryService {
}
component: BatteryService {}
}
}