diff --git a/modules/hyprland/hyprland.conf b/modules/hyprland/hyprland.conf index ba19a12..566ce4c 100644 --- a/modules/hyprland/hyprland.conf +++ b/modules/hyprland/hyprland.conf @@ -107,7 +107,9 @@ layerrule = noanim, termspawner layerrule = animation fade, shell:background layerrule = blur, shell:bar +layerrule = blurpopups, shell:bar layerrule = ignorezero, shell:bar +layerrule = noanim, shell:bar layerrule = noanim, shell:screenshot @@ -138,9 +140,8 @@ bind = $mod+SHIFT, return, exec, alacritty --class AlacrittyFloating bind = $mod, grave, exec, $launcher bind = $mod+SHIFT, q, hy3:killactive -bind = $mod+SHIFT, s, exec, echo "screenshot" | nc -w 0 -U /run/user/1000/quickshell.sock -bind = $mod, c, exec, echo "termselect:start" | nc -w 0 -U /run/user/1000/quickshell.sock -bindr = $mod, c, exec, echo "termselect:stop" | nc -w 0 -U /run/user/1000/quickshell.sock +bind = $mod+SHIFT, s, global, shell:screenshot +bind = $mod, c, global, shell:termselect bind = $mod, PERIOD, exec, quickshell -c lockscreen bind = $mod, f, fullscreen, 1 @@ -161,6 +162,7 @@ bindm = $mod, mouse:273, resizewindow bindn = , mouse:272, hy3:focustab, mouse bindn = , mouse_down, hy3:focustab, l, require_hovered bindn = , mouse_up, hy3:focustab, r, require_hovered +bind = $mod, q, hy3:warpcursor bind = ,XF86AudioPlay, exec, playerctl play-pause bind = ,XF86AudioStop, exec, playerctl -a stop @@ -176,14 +178,14 @@ bind = $mod, down, hy3:movefocus, d bind = $mod, up, hy3:movefocus, u bind = $mod, right, hy3:movefocus, r -bind = $mod+CONTROL, h, hy3:movefocus, l, visible -bind = $mod+CONTROL, j, hy3:movefocus, d, visible -bind = $mod+CONTROL, k, hy3:movefocus, u, visible -bind = $mod+CONTROL, l, hy3:movefocus, r, visible -bind = $mod+CONTROL, left, hy3:movefocus, l, visible -bind = $mod+CONTROL, down, hy3:movefocus, d, visible -bind = $mod+CONTROL, up, hy3:movefocus, u, visible -bind = $mod+CONTROL, right, hy3:movefocus, r, visible +bind = $mod+CONTROL, h, hy3:movefocus, l, visible, nowarp +bind = $mod+CONTROL, j, hy3:movefocus, d, visible, nowarp +bind = $mod+CONTROL, k, hy3:movefocus, u, visible, nowarp +bind = $mod+CONTROL, l, hy3:movefocus, r, visible, nowarp +bind = $mod+CONTROL, left, hy3:movefocus, l, visible, nowarp +bind = $mod+CONTROL, down, hy3:movefocus, d, visible, nowarp +bind = $mod+CONTROL, up, hy3:movefocus, u, visible, nowarp +bind = $mod+CONTROL, right, hy3:movefocus, r, visible, nowarp bind = $mod+SHIFT, h, hy3:movewindow, l, once bind = $mod+SHIFT, j, hy3:movewindow, d, once diff --git a/modules/user/modules/quickshell/BackgroundImage.qml b/modules/user/modules/quickshell/BackgroundImage.qml index 14b9790..53cbac5 100644 --- a/modules/user/modules/quickshell/BackgroundImage.qml +++ b/modules/user/modules/quickshell/BackgroundImage.qml @@ -5,4 +5,5 @@ Image { required property ShellScreen screen; source: Qt.resolvedUrl(screen.name == "DP-1" ? "5120x1440.png" : "1920x1080.png") asynchronous: false + cache: false } diff --git a/modules/user/modules/quickshell/lockscreen/Lockscreen.qml b/modules/user/modules/quickshell/lockscreen/Lockscreen.qml index 0913b67..b21cb18 100644 --- a/modules/user/modules/quickshell/lockscreen/Lockscreen.qml +++ b/modules/user/modules/quickshell/lockscreen/Lockscreen.qml @@ -6,18 +6,17 @@ Item { Item { anchors.centerIn: parent - scale: 2 Text { id: timeText anchors { bottom: entryBox.top - bottomMargin: 50 + bottomMargin: 100 horizontalCenter: parent.horizontalCenter } font { - pointSize: 60 + pointSize: 120 hintingPreference: Font.PreferFullHinting family: "Noto Sans" } @@ -34,13 +33,11 @@ Item { Text { anchors { top: timeText.bottom - topMargin: -10 + topMargin: -20 horizontalCenter: parent.horizontalCenter } - font { - pointSize: 20 - } + font.pointSize: 40 color: "#50ffffff" text: "Locked" @@ -49,7 +46,8 @@ Item { TextField { id: entryBox anchors.centerIn: parent - width: 300 + width: 600 + font.pointSize: 24 enabled: context.status != AuthContext.Status.Authenticating focus: true @@ -86,11 +84,12 @@ Item { Text { id: status color: "white" + font.pointSize: 24 anchors { horizontalCenter: entryBox.horizontalCenter top: entryBox.bottom - topMargin: 20 + topMargin: 40 } text: { diff --git a/modules/user/modules/quickshell/lockscreen/shell.qml b/modules/user/modules/quickshell/lockscreen/shell.qml index 479e093..e44d7ac 100644 --- a/modules/user/modules/quickshell/lockscreen/shell.qml +++ b/modules/user/modules/quickshell/lockscreen/shell.qml @@ -1,3 +1,5 @@ +//@ pragma NativeTextRendering + import QtQuick import Quickshell import Quickshell.Wayland diff --git a/modules/user/modules/quickshell/lockscreen/test.qml b/modules/user/modules/quickshell/lockscreen/test.qml index 2917665..9792c89 100644 --- a/modules/user/modules/quickshell/lockscreen/test.qml +++ b/modules/user/modules/quickshell/lockscreen/test.qml @@ -1,5 +1,6 @@ import QtQuick import Quickshell +import ".." ShellRoot { AuthContext { @@ -8,10 +9,30 @@ ShellRoot { } FloatingWindow { - color: "#303030" + BackgroundImage { + anchors.fill: parent + screen: Quickshell.screens.filter(s => s.name == "eDP-1")[0] + } Lockscreen { - anchors.fill: parent + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom + right: parent.horizontalCenter + } + + context: authContext + } + + Lockscreen { + anchors { + left: parent.horizontalCenter + top: parent.top + bottom: parent.bottom + right: parent.right + } + context: authContext } } diff --git a/modules/user/modules/quickshell/shell/Bar.qml b/modules/user/modules/quickshell/shell/Bar.qml deleted file mode 100644 index e67f0fe..0000000 --- a/modules/user/modules/quickshell/shell/Bar.qml +++ /dev/null @@ -1,73 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import Quickshell -import Quickshell.Wayland - -PanelWindow { - id: root - - PersistentProperties { - id: persist - reloadableId: "persist" - - property bool barVisible: false - } - - PopupSurface { - id: popupSurface - reloadableId: "popupSurface" - - screen: root.screen - bar: barRect - visible: persist.barVisible - } - - onBackingWindowVisibleChanged: { - persist.barVisible = Qt.binding(() => backingWindowVisible); - } - - anchors { - left: true - top: true - bottom: true - } - - width: 70 - color: "transparent" - WlrLayershell.namespace: "shell:bar" - - Rectangle { - id: barRect - anchors { - fill: parent - margins: 10 - rightMargin: 5 - } - - color: ShellGlobals.colors.bar - radius: 5 - border.color: ShellGlobals.colors.barOutline - border.width: 1 - - Item { - anchors { - fill: parent - margins: 5 - } - - ColumnLayout { - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - } - - ClockWidget { - Layout.fillWidth: true - popupSurface: popupSurface - barWindow: root - } - } - } - } -} diff --git a/modules/user/modules/quickshell/shell/ShellGlobals.qml b/modules/user/modules/quickshell/shell/ShellGlobals.qml index c4e047e..21822c4 100644 --- a/modules/user/modules/quickshell/shell/ShellGlobals.qml +++ b/modules/user/modules/quickshell/shell/ShellGlobals.qml @@ -11,6 +11,7 @@ Singleton { readonly property var barOutline: "#50ffffff"; readonly property var widget: "#40ceffff"; readonly property var widgetOutline: "#60ffffff"; + readonly property var separator: "#60ffffff"; } readonly property var popoutXCurve: EasingCurve { diff --git a/modules/user/modules/quickshell/shell/ShellIpc.qml b/modules/user/modules/quickshell/shell/ShellIpc.qml index 6d8f7de..3431a6a 100644 --- a/modules/user/modules/quickshell/shell/ShellIpc.qml +++ b/modules/user/modules/quickshell/shell/ShellIpc.qml @@ -1,35 +1,19 @@ pragma Singleton import Quickshell -import Quickshell.Io +import Quickshell.Hyprland Singleton { - property bool termSelect: false; + readonly property alias termSelect: termSelectBind.pressed; signal screenshot(); - SocketServer { - active: true - path: "/run/user/1000/quickshell.sock" + Shortcut { + name: "screenshot" + onPressed: screenshot() + } - handler: Socket { - parser: SplitParser { - onRead: message => { - console.log(message) - switch (message) { - case "screenshot": - screenshot(); - break; - case "termselect:start": - termSelect = true; - break; - case "termselect:stop": - termSelect = false; - break; - default: - console.log(`socket received unknown message: ${message}`) - } - } - } - } + Shortcut { + id: termSelectBind + name: "termselect" } } diff --git a/modules/user/modules/quickshell/shell/bar/Bar.qml b/modules/user/modules/quickshell/shell/bar/Bar.qml new file mode 100644 index 0000000..70e06a3 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/Bar.qml @@ -0,0 +1,33 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import "systray" as SysTray + +BarContainment { + id: root + ColumnLayout { + anchors { + left: parent.left + right: parent.right + top: parent.top + } + } + + ColumnLayout { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + + SysTray.SysTray { + bar: root + Layout.fillWidth: true + //width: 24 + } + + ClockWidget { + Layout.fillWidth: true + } + } +} diff --git a/modules/user/modules/quickshell/shell/bar/BarContainment.qml b/modules/user/modules/quickshell/shell/bar/BarContainment.qml new file mode 100644 index 0000000..758c621 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/BarContainment.qml @@ -0,0 +1,114 @@ +import QtQuick +import Quickshell +import Quickshell.Wayland +import ".." + +PanelWindow { + id: root + + default property list widgetSurfaceData; + readonly property var widgetSurface: widgetSurface; + property list overlays: []; + + anchors { + left: true + top: true + bottom: true + } + + width: 70 + color: "transparent" + WlrLayershell.namespace: "shell:bar" + + Rectangle { + id: barRect + + anchors { + fill: parent + margins: 10 + rightMargin: 5 + } + + color: ShellGlobals.colors.bar + radius: 5 + border.color: ShellGlobals.colors.barOutline + border.width: 1 + + Item { + id: containment + + anchors { + fill: parent + margins: 5 + } + } + } + + // note: must be above the widgetSurface due to reload order + PersistentProperties { + id: persist + reloadableId: "persist" + + property bool visible: false + } + + onBackingWindowVisibleChanged: { + persist.visible = Qt.binding(() => backingWindowVisible); + } + + PanelWindow { + id: widgetSurface + reloadableId: "widgetSurface" + + visible: persist.visible + anchors: root.anchors + screen: root.screen + exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "shell:bar" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + width: { + const extents = overlays + .filter(overlay => !overlay.fullyCollapsed) + .map(overlay => overlayXOffset + overlay.expandedWidth); + + return Math.max(root.width, ...extents); + } + + readonly property real overlayXOffset: root.width + 10; + readonly property real tooltipXOffset: root.width + 2; + + function overlayRect(targetY: real, size: rect): rect { + const y = Math.max(barRect.y, Math.min((barRect.y + barRect.height) - size.height, targetY)); + return Qt.rect(overlayXOffset, y, size.width, size.height); + } + + function boundedY(targetY: real, height: real): real { + return Math.max(0, Math.min(barRect.height - height, targetY)) + } + + Item { + id: contentArea + data: widgetSurfaceData + } + + readonly property var tooltip: tooltip; + Tooltip { + id: tooltip + bar: root + } + + function repositionContentArea() { + // abusing the knowledge that both bars are in the same position onscreen + const contentRect = containment.mapToItem(root.contentItem, 0, 0, containment.width, containment.height) + + contentArea.x = contentRect.x + contentArea.y = contentRect.y + contentArea.width = contentRect.width + contentArea.height = contentRect.height + } + } + + onWindowTransformChanged: widgetSurface.repositionContentArea() +} diff --git a/modules/user/modules/quickshell/shell/BarWidgetInner.qml b/modules/user/modules/quickshell/shell/bar/BarWidgetInner.qml similarity index 92% rename from modules/user/modules/quickshell/shell/BarWidgetInner.qml rename to modules/user/modules/quickshell/shell/bar/BarWidgetInner.qml index bf0fd93..619e7e7 100644 --- a/modules/user/modules/quickshell/shell/BarWidgetInner.qml +++ b/modules/user/modules/quickshell/shell/bar/BarWidgetInner.qml @@ -1,4 +1,5 @@ import QtQuick +import ".." Rectangle { color: ShellGlobals.colors.widget diff --git a/modules/user/modules/quickshell/shell/bar/BugTester.qml b/modules/user/modules/quickshell/shell/bar/BugTester.qml new file mode 100644 index 0000000..6becec6 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/BugTester.qml @@ -0,0 +1,31 @@ +import QtQuick + +BarWidgetInner { + implicitHeight: 50 + + SequentialAnimation on implicitHeight { + loops: Animation.Infinite + PropertyAnimation { to: 70; duration: 1000 } + PropertyAnimation { to: 40; duration: 1000 } + } + + property int len: 1 + + Text { + anchors.centerIn: parent + text: `8${'='.repeat(len)}D` + font.pointSize: 16 + color: "white" + + PropertyAnimation on rotation { + loops: Animation.Infinite + to: 365 + duration: 1000 + } + } + + MouseArea { + anchors.fill: parent + onClicked: len += 1; + } +} diff --git a/modules/user/modules/quickshell/shell/ClockWidget.qml b/modules/user/modules/quickshell/shell/bar/ClockWidget.qml similarity index 98% rename from modules/user/modules/quickshell/shell/ClockWidget.qml rename to modules/user/modules/quickshell/shell/bar/ClockWidget.qml index e6fd13d..a843a43 100644 --- a/modules/user/modules/quickshell/shell/ClockWidget.qml +++ b/modules/user/modules/quickshell/shell/bar/ClockWidget.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Layouts +import ".." OverlayWidget { expandedWidth: 600 diff --git a/modules/user/modules/quickshell/shell/OverlayWidget.qml b/modules/user/modules/quickshell/shell/bar/ExpandingWidget.qml similarity index 82% rename from modules/user/modules/quickshell/shell/OverlayWidget.qml rename to modules/user/modules/quickshell/shell/bar/ExpandingWidget.qml index 5a47ce0..909bd6a 100644 --- a/modules/user/modules/quickshell/shell/OverlayWidget.qml +++ b/modules/user/modules/quickshell/shell/bar/ExpandingWidget.qml @@ -1,14 +1,13 @@ import QtQuick import Quickshell +import ".." Item { - required property PopupSurface popupSurface; + required property var bar; required property real expandedWidth; required property real expandedHeight; required default property Item widget; - required property var barWindow; - property bool expanded: false; onExpandedChanged: { @@ -22,14 +21,22 @@ Item { if (fullyCollapsed && popupSurface.activeOverlay == this) { popupSurface.activeOverlay = null; } + + /*if (fullyCollapsed) { + widget.x = Qt.binding(() => this.x) + }*/ } readonly property rect collapsedLayerRect: { - void [barWindow.windowTransform, popupSurface.windowTransform]; + void [barWindow.windowTransform, popupSurface.windowTransform, y, parent.y]; return this.mapToItem(popupSurface.contentItem, 0, 0, width, height); } - readonly property rect expandedLayerRect: popupSurface.expandedPosition(this) + onCollapsedLayerRectChanged: console.log(`clr: ${collapsedLayerRect}`) + onLayerRectChanged: console.log(`lr: ${layerRect}`) + onYChanged: console.log(`y: ${y}`) + + readonly property rect expandedLayerRect: bar.widgetSurface.expandedPosition(this) readonly property rect layerRect: { const [p, xCurve, yCurve] = [animationProgress, ShellGlobals.popoutXCurve, ShellGlobals.popoutYCurve]; @@ -46,7 +53,7 @@ Item { implicitHeight: widget.implicitHeight Component.onCompleted: { - popupSurface.connectOverlay(this) + popupSurface.connectOverlay(this); widget.x = Qt.binding(() => layerRect.x); widget.y = Qt.binding(() => layerRect.y); widget.width = Qt.binding(() => layerRect.width); diff --git a/modules/user/modules/quickshell/shell/bar/OverlayWidget.qml b/modules/user/modules/quickshell/shell/bar/OverlayWidget.qml new file mode 100644 index 0000000..7d31693 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/OverlayWidget.qml @@ -0,0 +1,17 @@ +import QtQuick + +Item { + default property Item item; + property int expandedWidth; + property int expandedHeight; + + implicitHeight: item.implicitHeight + implicitWidth: item.implicitWidth + + Component.onCompleted: { + item.width = Qt.binding(() => this.width) + item.height = Qt.binding(() => this.height) + } + + children: [ item ] +} diff --git a/modules/user/modules/quickshell/shell/bar/Tooltip.qml b/modules/user/modules/quickshell/shell/bar/Tooltip.qml new file mode 100644 index 0000000..91b4a22 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/Tooltip.qml @@ -0,0 +1,127 @@ +import QtQuick +import Quickshell +import Quickshell.Hyprland + +Scope { + id: root + required property var bar; + + property TooltipItem activeTooltip: null; + property TooltipItem activeMenu: null; + + readonly property TooltipItem activeItem: activeMenu ?? activeTooltip; + property TooltipItem lastActiveItem: null; + + onActiveItemChanged: { + if (activeItem != null) activeItem.visible = true; + if (lastActiveItem != null) lastActiveItem.visible = false; + lastActiveItem = activeItem; + } + + function setItem(item: TooltipItem) { + if (item.isMenu) { + activeMenu = item; + } else { + activeTooltip = item; + } + } + + function removeItem(item: TooltipItem) { + if (item.isMenu && activeMenu == item) { + activeMenu = null + } else if (!item.isMenu && activeTooltip == item) { + activeTooltip = null + } + } + + LazyLoader { + id: popupLoader + activeAsync: activeItem != null + + PopupWindow { + id: popup + parentWindow: bar.widgetSurface + relativeX: bar.widgetSurface.tooltipXOffset + relativeY: 0 + height: bar.widgetSurface.height + width: tooltipItem.width + visible: true + color: "transparent" + + mask: Region { + item: (activeItem?.isMenu ?? false) ? tooltipItem : null + } + + HyprlandFocusGrab { + active: activeItem?.isMenu ?? false + windows: [ popup, bar.widgetSurface ] + onActiveChanged: { + if (!active && activeItem?.isMenu) { + activeMenu.close() + } + } + } + + BarWidgetInner { + id: tooltipItem + + readonly property var targetWidth: activeItem?.implicitWidth ?? 10; + readonly property var targetHeight: (activeItem?.implicitHeight ?? 0) + 10; + + readonly property real targetY: { + if (activeItem == null) return 0; + const target = bar.widgetSurface.contentItem.mapFromItem(activeItem.owner, 0, activeItem.targetRelativeY).y; + return bar.widgetSurface.boundedY(target, activeItem.implicitHeight / 2); + } + + width: targetWidth + 10 + + property var y1: -1 + property var y2: -1 + + y: y1 + height: y2 - y1 + + SmoothedAnimation { + target: tooltipItem; + property: "y1"; + to: tooltipItem.targetY - tooltipItem.targetHeight / 2; + onToChanged: { + if (tooltipItem.y1 == -1 || !(activeItem?.animateSize ?? true)) { + stop(); + tooltipItem.y1 = to; + } else { + velocity = (Math.max(tooltipItem.y1, to) - Math.min(tooltipItem.y1, to)) * 5; + restart(); + } + } + } + + SmoothedAnimation { + target: tooltipItem + property: "y2" + to: tooltipItem.targetY + tooltipItem.targetHeight / 2; + onToChanged: { + if (tooltipItem.y2 == -1 || !(activeItem?.animateSize ?? true)) { + stop(); + tooltipItem.y2 = to; + } else { + velocity = (Math.max(tooltipItem.y2, to) - Math.min(tooltipItem.y2, to)) * 5; + restart(); + } + } + } + + Item { + clip: true + children: [ activeItem ] + + anchors { + fill: parent + margins: 5 + } + } + } + } + } +} diff --git a/modules/user/modules/quickshell/shell/bar/TooltipItem.qml b/modules/user/modules/quickshell/shell/bar/TooltipItem.qml new file mode 100644 index 0000000..1733d5d --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/TooltipItem.qml @@ -0,0 +1,31 @@ +import QtQuick +import Quickshell + +Item { + id: root + required property var tooltip; + required property Item owner; + property bool isMenu: false; + property bool animateSize: true; + property bool show: false; + + property real targetRelativeY: owner.height / 2; + property real hangTime: isMenu ? 0 : 200; + + signal close(); + + onShowChanged: { + if (show) { + hangTimer.stop(); + tooltip.setItem(this); + } else if (hangTime == 0) { + tooltip.removeItem(this); + } else hangTimer.start(); + } + + Timer { + id: hangTimer + interval: hangTime + onTriggered: tooltip.removeItem(root); + } +} diff --git a/modules/user/modules/quickshell/shell/bar/systray/MenuCheckBox.qml b/modules/user/modules/quickshell/shell/bar/systray/MenuCheckBox.qml new file mode 100644 index 0000000..c01e8fe --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/systray/MenuCheckBox.qml @@ -0,0 +1,44 @@ +import QtQuick +import QtQuick.Shapes +import "../.." + +Rectangle { + property var checkState: Qt.Unchecked; + implicitHeight: 18 + implicitWidth: 18 + radius: 3 + color: ShellGlobals.colors.widget + + Shape { + visible: checkState == Qt.Checked + anchors.fill: parent + layer.enabled: true + layer.samples: 10 + + ShapePath { + strokeWidth: 2 + capStyle: ShapePath.RoundCap + joinStyle: ShapePath.RoundJoin + fillColor: "transparent" + + startX: start.x + startY: start.y + + PathLine { + id: start + x: width * 0.8 + y: height * 0.2 + } + + PathLine { + x: width * 0.35 + y: height * 0.8 + } + + PathLine { + x: width * 0.2 + y: height * 0.6 + } + } + } +} diff --git a/modules/user/modules/quickshell/shell/bar/systray/MenuChildrenRevealer.qml b/modules/user/modules/quickshell/shell/bar/systray/MenuChildrenRevealer.qml new file mode 100644 index 0000000..c937a4b --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/systray/MenuChildrenRevealer.qml @@ -0,0 +1,56 @@ +import QtQuick +import QtQuick.Shapes +import Quickshell + +Item { + property bool expanded: false; + + readonly property bool open: progress != 0; + readonly property bool animating: internalProgress != -1 && internalProgress != 101; + + implicitHeight: 16 + implicitWidth: 16 + property var xStart: Math.round(width * 0.3) + property var yStart: Math.round(height * 0.1) + property var xEnd: Math.round(width * 0.7) + property var yEnd: Math.round(height * 0.9) + + property real internalProgress: expanded ? 101 : -1; + Behavior on internalProgress { SmoothedAnimation { velocity: 300 } } + + EasingCurve { + id: curve + curve.type: Easing.InOutQuad + } + + readonly property real progress: curve.valueAt(Math.min(100, Math.max(internalProgress, 0)) * 0.01) + + rotation: progress * 90; + + Shape { + anchors.fill: parent + + layer.enabled: true + layer.samples: 3 + + ShapePath { + strokeWidth: 2 + capStyle: ShapePath.RoundCap + joinStyle: ShapePath.MiterJoin + fillColor: "transparent" + + startX: xStart + startY: yStart + + PathLine { + x: xEnd + y: height / 2 + } + + PathLine { + y: yEnd + x: xStart + } + } + } +} diff --git a/modules/user/modules/quickshell/shell/bar/systray/MenuItem.qml b/modules/user/modules/quickshell/shell/bar/systray/MenuItem.qml new file mode 100644 index 0000000..9da1e95 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/systray/MenuItem.qml @@ -0,0 +1,110 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.DBusMenu +import "../.." + +MouseArea { + id: root + required property var entry; + property alias expanded: childrenRevealer.expanded; + property bool animating: childrenRevealer.animating || childrenList.animating; + // appears it won't actually create the handler when only used from MenuItemList. + onExpandedChanged: {} + onAnimatingChanged: {} + + signal close(); + + implicitWidth: row.implicitWidth + 4 + implicitHeight: row.implicitHeight + 4 + + hoverEnabled: true + onClicked: { + if (entry.hasChildren) childrenRevealer.expanded = !childrenRevealer.expanded + else { + entry.click(); + if (entry.toggleType == ToggleButtonType.None) close(); + } + } + + ColumnLayout { + id: row + anchors.fill: parent + anchors.margins: 2 + spacing: 0 + + RowLayout { + Item { + implicitWidth: 22 + implicitHeight: 22 + + MenuCheckBox { + anchors.centerIn: parent + visible: entry.toggleType == ToggleButtonType.CheckBox + checkState: entry.checkState + } + + MenuRadioButton { + anchors.centerIn: parent + visible: entry.toggleType == ToggleButtonType.RadioButton + checkState: entry.checkState + } + + MenuChildrenRevealer { + id: childrenRevealer + anchors.centerIn: parent + visible: entry.hasChildren + onOpenChanged: entry.showChildren = open + } + } + + Text { + text: entry.cleanLabel + color: entry.enabled ? "white" : "#bbbbbb" + } + + Item { + Layout.fillWidth: true + implicitWidth: 22 + implicitHeight: 22 + + Image { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + visible: entry.icon != "" + source: entry.icon + sourceSize.height: parent.height + sourceSize.width: parent.height + } + } + } + + Item { + Layout.fillWidth: true + implicitHeight: childrenList.implicitHeight * childrenRevealer.progress + clip: true + + MenuItemList { + id: childrenList + items: entry.children + onClose: root.close() + + anchors { + top: parent.top + left: parent.left + right: parent.right + } + } + } + } + + Rectangle { + anchors.fill: parent + visible: root.containsMouse || childrenRevealer.expanded + + color: ShellGlobals.colors.widget + border.width: 1 + border.color: ShellGlobals.colors.widgetOutline + radius: 5 + } +} diff --git a/modules/user/modules/quickshell/shell/bar/systray/MenuItemList.qml b/modules/user/modules/quickshell/shell/bar/systray/MenuItemList.qml new file mode 100644 index 0000000..8a809e1 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/systray/MenuItemList.qml @@ -0,0 +1,80 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.DBusMenu +import "../.." + +ColumnLayout { + id: root + required property var items; + property Item animatingItem: null; + property bool animating: animatingItem != null; + + signal close(); + signal submenuExpanded(item: var); + + spacing: 0 + + Repeater { + model: items + + Loader { + required property var modelData; + + property var item: Component { + BoundComponent { + id: itemComponent + source: "MenuItem.qml" + + property var entry: modelData + + function onClose() { + root.close() + } + + function onExpandedChanged() { + if (item.expanded) root.submenuExpanded(item); + } + + function onAnimatingChanged() { + if (item.animating) { + root.animatingItem = this; + } else if (root.animatingItem == this) { + root.animatingItem = null; + } + } + + Connections { + target: root + + function onSubmenuExpanded(expandedItem) { + if (item != expandedItem) item.expanded = false; + } + } + } + } + + property var separator: Component { + Item { + implicitHeight: seprect.height + 6 + + Rectangle { + id: seprect + + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + } + + color: ShellGlobals.colors.separator + height: 1 + } + } + } + + sourceComponent: modelData.isSeparator ? separator : item + Layout.fillWidth: true + } + } +} diff --git a/modules/user/modules/quickshell/shell/bar/systray/MenuRadioButton.qml b/modules/user/modules/quickshell/shell/bar/systray/MenuRadioButton.qml new file mode 100644 index 0000000..9a2d8d0 --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/systray/MenuRadioButton.qml @@ -0,0 +1,19 @@ +import QtQuick +import "../.." + +Rectangle { + property var checkState: Qt.Unchecked; + implicitHeight: 18 + implicitWidth: 18 + radius: width / 2 + color: ShellGlobals.colors.widget + + Rectangle { + x: parent.width * 0.25 + y: parent.height * 0.25 + visible: checkState == Qt.Checked + width: parent.width * 0.5 + height: width + radius: width / 2 + } +} diff --git a/modules/user/modules/quickshell/shell/bar/systray/SysTray.qml b/modules/user/modules/quickshell/shell/bar/systray/SysTray.qml new file mode 100644 index 0000000..3cbc6cb --- /dev/null +++ b/modules/user/modules/quickshell/shell/bar/systray/SysTray.qml @@ -0,0 +1,147 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Effects +import Quickshell +import Quickshell.Services.SystemTray +import ".." + +OverlayWidget { + id: root + expandedWidth: 600 + expandedHeight: 800 + required property var bar; + + BarWidgetInner { + implicitHeight: column.implicitHeight + 10 + + ColumnLayout { + id: column + implicitHeight: childrenRect.height + spacing: 5 + + anchors { + fill: parent + margins: 5 + } + + Repeater { + model: SystemTray.items; + + Item { + required property var modelData; + readonly property alias menu: menuWatcher.menu; + + SystemTrayMenuWatcher { + id: menuWatcher; + trayItem: modelData; + } + + property bool targetMenuOpen: false; + onTargetMenuOpenChanged: menu.showChildren = targetMenuOpen + + Layout.fillWidth: true + implicitHeight: width + Behavior on implicitHeight { + SmoothedAnimation { velocity: 50 } + } + + MouseArea { + id: mouseArea + anchors { + top: parent.top + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + width: height + + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + + Image { + id: image + anchors.fill: parent + + anchors.margins: mouseArea.pressed || targetMenuOpen ? 3 : 0 + Behavior on anchors.margins { SmoothedAnimation { velocity: 30 } } + + source: modelData.icon + sourceSize.width: width + sourceSize.height: height + cache: false + visible: false + } + + MultiEffect { + anchors.fill: image + source: image + + property real targetBrightness: mouseArea.pressed || targetMenuOpen ? -25 : mouseArea.containsMouse ? 75 : 0 + Behavior on targetBrightness { SmoothedAnimation { velocity: 600 } } + brightness: targetBrightness / 1000 + } + + onClicked: event => { + event.accepted = true; + + if (event.button == Qt.LeftButton) { + modelData.activate(); + } else if (event.button == Qt.MiddleButton) { + modelData.secondaryActivate(); + } else if (event.button == Qt.RightButton && menu != null) { + targetMenuOpen = !targetMenuOpen; + } + } + + onWheel: event => { + event.accepted = true; + const points = event.angleDelta.y / 120 + modelData.scroll(points, false); + } + + property var tooltip: TooltipItem { + anchors.fill: parent + tooltip: bar.widgetSurface.tooltip + owner: image + + show: mouseArea.containsMouse + implicitWidth: tooltipText.implicitWidth + implicitHeight: tooltipText.implicitHeight + + Text { + id: tooltipText + anchors.fill: parent + text: modelData.tooltipTitle != "" ? modelData.tooltipTitle : modelData.id + color: "white" + } + } + + property var rightclickMenu: TooltipItem { + anchors.fill: parent + tooltip: bar.widgetSurface.tooltip + owner: image + + isMenu: true + show: targetMenuOpen && menu.showChildren + animateSize: !rightclickItems.animating + implicitHeight: rightclickItems.implicitHeight + implicitWidth: rightclickItems.implicitWidth + + onVisibleChanged: { + if (!visible) targetMenuOpen = false; + } + + onClose: targetMenuOpen = false; + + MenuItemList { + id: rightclickItems + anchors.fill: parent + items: menu == null ? [] : menu.children + onClose: targetMenuOpen = false; + } + } + } + } + } + } + } +} diff --git a/modules/user/modules/quickshell/shell/screenshot/Controller.qml b/modules/user/modules/quickshell/shell/screenshot/Controller.qml index ff000c1..e02e6a4 100644 --- a/modules/user/modules/quickshell/shell/screenshot/Controller.qml +++ b/modules/user/modules/quickshell/shell/screenshot/Controller.qml @@ -79,8 +79,6 @@ Scope { LazyLoader { loading: root.shooting active: root.visible - onLoadingChanged: console.log(`loading set to ${loading} at ${new Date()}`) - onActiveChanged: console.log(`active set to ${active} at ${new Date()}`) Variants { model: Quickshell.screens @@ -128,8 +126,6 @@ Scope { } onReleased: { - console.log(`area: ${selection.x} ${selection.y} ${selection.w} ${selection.h}`) - if (selection.w > 0 && selection.h > 0) { magickProc.running = true selectionComplete = true diff --git a/modules/user/modules/quickshell/shell/selection/SelectionLayer.qml b/modules/user/modules/quickshell/shell/selection/SelectionLayer.qml new file mode 100644 index 0000000..367d866 --- /dev/null +++ b/modules/user/modules/quickshell/shell/selection/SelectionLayer.qml @@ -0,0 +1,13 @@ +import Quickshell +import Quickshell.Wayland + +PanelWindow { + visible: false + + anchors { + left: true + right: true + top: true + bottom: true + } +} diff --git a/modules/user/modules/quickshell/shell/shell.qml b/modules/user/modules/quickshell/shell/shell.qml index 9c1c423..e0e9b7b 100644 --- a/modules/user/modules/quickshell/shell/shell.qml +++ b/modules/user/modules/quickshell/shell/shell.qml @@ -5,6 +5,7 @@ import QtQuick import QtQuick.Layouts import ".." import "screenshot" as Screenshot +import "bar" as Bar ShellRoot { Process { @@ -34,7 +35,10 @@ ShellRoot { Scope { property var modelData - Bar { + /*Bar { + screen: modelData + }*/ + Bar.Bar { screen: modelData } @@ -52,8 +56,6 @@ ShellRoot { right: true } - color: "#111111" - BackgroundImage { anchors.fill: parent screen: window.screen @@ -118,6 +120,7 @@ ShellRoot { } PanelWindow { + visible: false screen: modelData WlrLayershell.layer: WlrLayer.Overlay diff --git a/modules/user/modules/walker/default.nix b/modules/user/modules/walker/default.nix index 8075426..32ad833 100644 --- a/modules/user/modules/walker/default.nix +++ b/modules/user/modules/walker/default.nix @@ -6,11 +6,16 @@ runAsService = true; config = { + show_initial_entries = true; fullscreen = true; scrollbar_policy = "external"; - search.hide_spinner = true; activation_mode.use_alt = true; + search = { + hide_icons = false; + hide_spinner = true; + }; + align = { width = 500; horizontal = "center"; @@ -21,8 +26,11 @@ list = { height = 500; fixed_height = true; + always_show = true; }; + icons.hide = false; + modules = [ { name = "applications";