Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / clang-tidy (pull_request) Failing after 10s
C++ / build (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 35s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 51s

This commit is contained in:
2026-10-04 18:20:23 +02:00
9 changed files with 133 additions and 128 deletions
+3
View File
@@ -34,4 +34,7 @@ PersistentProperties {
// Drawing state // Drawing state
property color penColor: "white" property color penColor: "white"
property int penWidth: 4 property int penWidth: 4
// Screenshot state
property bool screenshot
} }
+18 -3
View File
@@ -56,11 +56,10 @@ CustomWindow {
property color surfaceColor: Colors.tPalette.m3surface property color surfaceColor: Colors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent" color: "transparent"
contentItem.focus: true mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
mask: screenState.isDrawing ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
name: "Bar" name: "Bar"
Behavior on fsTransitionProg { Behavior on fsTransitionProg {
@@ -427,6 +426,22 @@ CustomWindow {
} }
} }
LazyLoader {
id: loader
property bool closing: !root.screenState.screenshot
onClosingChanged: if (!closing)
activeAsync = true
Picker {
loader: loader
parent: root.contentItem
screen: root.screen
screenState: root.screenState
}
}
component PanelBg: BlobRect2 { component PanelBg: BlobRect2 {
property real deformAmount: 0.15 property real deformAmount: 0.15
required property Item panel required property Item panel
-88
View File
@@ -1,88 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import Quickshell.Hyprland
import ZShell
import qs.Components
Scope {
LazyLoader {
id: root
property bool closing
property bool freeze
Variants {
model: Quickshell.screens
PanelWindow {
id: win
required property ShellScreen modelData
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: root.closing ? WlrKeyboardFocus.None : WlrKeyboardFocus.Exclusive
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "areapicker"
color: "transparent"
mask: root.closing ? empty : null
screen: modelData
anchors {
bottom: true
left: true
right: true
top: true
}
Region {
id: empty
}
Picker {
loader: root
screen: win.modelData
}
}
}
}
IpcHandler {
function open(): void {
root.freeze = false;
root.closing = false;
root.activeAsync = true;
}
function openFreeze(): void {
root.freeze = true;
root.closing = false;
root.activeAsync = true;
}
target: "picker"
}
CustomShortcut {
name: "screenshot"
onPressed: {
root.freeze = false;
root.closing = false;
root.activeAsync = true;
}
}
CustomShortcut {
name: "screenshotFreeze"
onPressed: {
root.freeze = true;
root.closing = false;
root.activeAsync = true;
}
}
}
+67 -22
View File
@@ -41,6 +41,7 @@ Item {
property real rsy: Math.min(sy, ey) property real rsy: Math.min(sy, ey)
readonly property real scaleRatio: Hypr.monitorFor(screen).scale readonly property real scaleRatio: Hypr.monitorFor(screen).scale
required property ShellScreen screen required property ShellScreen screen
required property ScreenState screenState
property real sh: Math.abs(sy - ey) property real sh: Math.abs(sy - ey)
readonly property color shadowColor: Hypr.options.decoration.shadow?.color ?? "black" readonly property color shadowColor: Hypr.options.decoration.shadow?.color ?? "black"
readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled
@@ -97,10 +98,6 @@ Item {
const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : []; const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : [];
const postCmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : null; const postCmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : null;
overlay.visible = border.visible = false;
handles.visible = false;
toolbar.visible = false;
ZShellIo.saveItem(captureLayer, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => { ZShellIo.saveItem(captureLayer, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => {
const finalize = finalPath => { const finalize = finalPath => {
if (copyToClipboard) { if (copyToClipboard) {
@@ -154,7 +151,7 @@ Item {
Component.onCompleted: { Component.onCompleted: {
Hypr.extras.refreshOptions(); Hypr.extras.refreshOptions();
screencopy.active = true; root.forceActiveFocus();
opacity = 1; opacity = 1;
@@ -219,13 +216,63 @@ Item {
onReleased: root.enterEditMode() onReleased: root.enterEditMode()
} }
Connections {
function onClosingChanged(): void {
if (root.loader.closing)
closeAnim.start();
else {
closeAnim.stop();
returnAnim.start();
}
}
target: root.loader
}
ParallelAnimation {
id: returnAnim
Anim {
duration: 300
property: "opacity"
target: root
to: 1
}
Anim {
property: "rsx"
target: root
to: Math.min(root.sx, root.ex)
}
Anim {
property: "rsy"
target: root
to: Math.min(root.sy, root.ey)
}
Anim {
property: "sw"
target: root
to: Math.abs(root.sx - root.ex)
}
Anim {
property: "sh"
target: root
to: Math.abs(root.sy - root.ey)
}
}
SequentialAnimation { SequentialAnimation {
id: closeAnim id: closeAnim
PropertyAction { ScriptAction {
property: "closing" script: {
target: root.loader const states = ShellState.allStates();
value: true for (const s of states)
s.screenshot = false;
}
} }
ParallelAnimation { ParallelAnimation {
@@ -267,18 +314,13 @@ Item {
anchors.fill: parent anchors.fill: parent
Loader { ScreencopyView {
id: screencopy id: screencopy
active: false
anchors.fill: parent anchors.fill: parent
asynchronous: true
sourceComponent: ScreencopyView {
captureSource: root.screen captureSource: root.screen
paintCursor: false paintCursor: false
} }
}
AnnotationCanvas { AnnotationCanvas {
id: annotations id: annotations
@@ -305,8 +347,7 @@ Item {
color: "white" color: "white"
layer.enabled: true layer.enabled: true
opacity: 0.3 opacity: 0.3
radius: root.realRounding visible: screencopy.hasContent
visible: screencopy.item?.hasContent ?? false
layer.effect: MultiEffect { layer.effect: MultiEffect {
maskEnabled: true maskEnabled: true
@@ -343,7 +384,7 @@ Item {
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2 implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2 implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0 radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
visible: screencopy.item?.hasContent ?? false visible: screencopy.hasContent
x: selectionRect.x - root.realBorderWidth x: selectionRect.x - root.realBorderWidth
y: selectionRect.y - root.realBorderWidth y: selectionRect.y - root.realBorderWidth
@@ -365,10 +406,14 @@ Item {
visible: root.mode === "edit" && root.tool === "move" visible: root.mode === "edit" && root.tool === "move"
onBoundsChanged: (x, y, w, h) => { onBoundsChanged: (x, y, w, h) => {
root.sx = x; let nx = Math.max(0, x);
root.sy = y; let ny = Math.max(0, y);
root.ex = x + w; let nw = Math.min(w, root.screen.width - nx);
root.ey = y + h; let nh = Math.min(h, root.screen.height - ny);
root.sx = nx;
root.sy = ny;
root.ex = nx + nw;
root.ey = ny + nh;
} }
onMoveBy: (dx, dy) => { onMoveBy: (dx, dy) => {
const w = root.sw, h = root.sh; const w = root.sw, h = root.sh;
+6 -4
View File
@@ -59,6 +59,7 @@ Item {
} }
anchors.fill: parent anchors.fill: parent
enabled: tool !== "none"
StrokeCanvas { StrokeCanvas {
id: canvas id: canvas
@@ -77,15 +78,12 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent anchors.fill: parent
cursorShape: undefined cursorShape: undefined
enabled: root.tool !== "none" enabled: root.enabled
hoverEnabled: true
onPositionChanged: e => { onPositionChanged: e => {
const x = e.x; const x = e.x;
const y = e.y; const y = e.y;
canvas.hoverPoint = Qt.point(x, y);
if (!pressed) if (!pressed)
return; return;
@@ -125,12 +123,16 @@ Item {
const withinYHeight = y > root.selection.y && y < root.selection.y + root.selection.implicitHeight; const withinYHeight = y > root.selection.y && y < root.selection.y + root.selection.implicitHeight;
if (withinXWidth && withinYHeight) { if (withinXWidth && withinYHeight) {
if (!drawHandler.pressed)
canvas.showHover(x, y); canvas.showHover(x, y);
else
canvas.showHover(drawHandler.mouseX, drawHandler.mouseY);
return Qt.BlankCursor; return Qt.BlankCursor;
} else { } else {
canvas.hideHover(); canvas.hideHover();
return undefined; return undefined;
} }
} }
enabled: root.enabled
} }
} }
+13 -3
View File
@@ -1,13 +1,15 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import ZShell.Internal
import qs.Services
Item { Item {
id: root id: root
required property int border required property int border
property color handleBorder: "#3584e4" property color handleBorder: Colors.palette.m3primary
property color handleFill: "white" property color handleFill: Colors.palette.m3onPrimary
property real hs: 12 property real hs: 12
property real minSize: 24 property real minSize: 24
required property real selH required property real selH
@@ -26,6 +28,8 @@ Item {
x: selX x: selX
y: selY y: selY
Component.onDestruction: Cursor.clearOverride()
DragHandler { DragHandler {
id: moveHandler id: moveHandler
@@ -33,6 +37,7 @@ Item {
property real accumY: 0 property real accumY: 0
cursorShape: undefined cursorShape: undefined
dragThreshold: 0
target: null target: null
onActiveChanged: { onActiveChanged: {
@@ -54,7 +59,7 @@ Item {
if (root.tool !== "move") if (root.tool !== "move")
return undefined; return undefined;
if (moveHandler.centroid.pressedButtons & Qt.LeftButton) if ((moveHandler.centroid.pressedButtons & Qt.LeftButton) && moveHandler.active)
return Qt.ClosedHandCursor; return Qt.ClosedHandCursor;
else else
return Qt.OpenHandCursor; return Qt.OpenHandCursor;
@@ -123,6 +128,8 @@ Item {
z: 10 z: 10
HoverHandler { HoverHandler {
id: hover
cursorShape: { cursorShape: {
switch (handle.edge) { switch (handle.edge) {
case "tl": case "tl":
@@ -148,6 +155,7 @@ Item {
property real startX property real startX
property real startY property real startY
dragThreshold: 0
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.TakeOverForbidden grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.TakeOverForbidden
target: null target: null
@@ -157,8 +165,10 @@ Item {
startY = root.y; startY = root.y;
startW = root.width; startW = root.width;
startH = root.height; startH = root.height;
Cursor.setOverride(hover.cursorShape);
} else { } else {
root.dragFinished(); root.dragFinished();
Cursor.clearOverride();
} }
} }
onTranslationChanged: { onTranslationChanged: {
+17
View File
@@ -11,6 +11,18 @@ Scope {
readonly property bool hasFullscreen: Hypr.focusedWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) ?? false readonly property bool hasFullscreen: Hypr.focusedWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) ?? false
property bool launcherInterrupted property bool launcherInterrupted
CustomShortcut {
description: "Open screenshot"
name: "screenshot"
onPressed: {
const states = ShellState.allStates();
for (const s of states) {
s.screenshot = true;
}
}
}
CustomShortcut { CustomShortcut {
description: "Toggle launcher" description: "Toggle launcher"
name: "toggle-launcher" name: "toggle-launcher"
@@ -26,6 +38,7 @@ Scope {
} }
CustomShortcut { CustomShortcut {
description: "Toggle drawing"
name: "toggle-drawing" name: "toggle-drawing"
onPressed: { onPressed: {
@@ -35,6 +48,7 @@ Scope {
} }
CustomShortcut { CustomShortcut {
description: "Toggle sidebar"
name: "toggle-nc" name: "toggle-nc"
onPressed: { onPressed: {
@@ -44,6 +58,7 @@ Scope {
} }
CustomShortcut { CustomShortcut {
description: "Show On-Screen-Display"
name: "show-osd" name: "show-osd"
onPressed: { onPressed: {
@@ -53,6 +68,7 @@ Scope {
} }
CustomShortcut { CustomShortcut {
description: "Toggle settings"
name: "toggle-settings" name: "toggle-settings"
onPressed: { onPressed: {
@@ -62,6 +78,7 @@ Scope {
} }
CustomShortcut { CustomShortcut {
description: "Toggle clipboard"
name: "toggle-clipboard" name: "toggle-clipboard"
onPressed: { onPressed: {
+6 -1
View File
@@ -6,6 +6,10 @@ import qs.Components
import qs.Helpers import qs.Helpers
Singleton { Singleton {
function allStates(): list<ScreenState> {
return [...states.instances];
}
function anySidebarOpen(): bool { function anySidebarOpen(): bool {
return states.instances.some(s => s.sidebar); return states.instances.some(s => s.sidebar);
} }
@@ -30,6 +34,7 @@ Singleton {
model: Quickshell.screens model: Quickshell.screens
ScreenState {} ScreenState {
}
} }
} }
-4
View File
@@ -12,7 +12,6 @@ import qs.Modules
import qs.Modules.Wallpaper import qs.Modules.Wallpaper
import qs.Modules.Lock import qs.Modules.Lock
import qs.Drawers import qs.Drawers
import qs.Helpers
import qs.Modules.Polkit import qs.Modules.Polkit
import qs.Daemons import qs.Daemons
@@ -29,9 +28,6 @@ ShellRoot {
Wallpaper { Wallpaper {
} }
AreaPicker {
}
Lock { Lock {
id: lock id: lock
} }