Compare commits
10
Commits
10d763cfe5
...
7cc673430c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cc673430c | ||
|
|
08bb221699 | ||
|
|
60293abe61 | ||
|
|
a22022e969 | ||
|
|
62e19b5261 | ||
|
|
d76aaf093b | ||
|
|
4ebb4ce859 | ||
|
|
be3c938c39 | ||
|
|
f6fa2f0230 | ||
|
|
4a021d81ac |
@@ -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,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
@@ -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
@@ -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
@@ -375,7 +375,7 @@ CustomWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Interactions {
|
||||
Interactions2 {
|
||||
id: interactions
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 ?? "";
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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.
@@ -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"
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user