Merge branch 'end-4:main' into main

This commit is contained in:
jwihardi
2025-09-30 19:35:49 -04:00
committed by GitHub
56 changed files with 762 additions and 528 deletions
+17
View File
@@ -27,6 +27,23 @@ Singleton {
property bool superReleaseMightTrigger: true property bool superReleaseMightTrigger: true
property bool workspaceShowNumbers: false property bool workspaceShowNumbers: false
Connections {
target: Config
function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
GlobalStates.screenLocked = true;
}
}
}
Connections {
target: Persistent
function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
GlobalStates.screenLocked = true;
}
}
}
property real screenZoom: 1 property real screenZoom: 1
onScreenZoomChanged: { onScreenZoomChanged: {
Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]); Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]);
@@ -175,16 +175,10 @@ Variants {
anchors.fill: parent anchors.fill: parent
clip: true clip: true
Image { StyledImage {
id: wallpaper id: wallpaper
visible: opacity > 0 && !blurLoader.active visible: opacity > 0 && !blurLoader.active
opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0
Behavior on opacity {
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
}
cache: false cache: false
asynchronous: true
retainWhileLoading: true
smooth: false smooth: false
// Range = groups that workspaces span on // Range = groups that workspaces span on
property int chunkSize: Config?.options.bar.workspaces.shown ?? 10 property int chunkSize: Config?.options.bar.workspaces.shown ?? 10
@@ -300,9 +294,7 @@ Variants {
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
} }
} }
sourceComponent: ColumnLayout { sourceComponent: Column {
spacing: 8
Loader { Loader {
id: digitalClockLoader id: digitalClockLoader
visible: root.clockStyle === "digital" visible: root.clockStyle === "digital"
@@ -340,7 +332,6 @@ Variants {
Loader { Loader {
id: cookieClockLoader id: cookieClockLoader
Layout.alignment: Qt.AlignHCenter
visible: root.clockStyle === "cookie" visible: root.clockStyle === "cookie"
active: visible active: visible
sourceComponent: CookieClock {} sourceComponent: CookieClock {}
@@ -432,7 +423,7 @@ Variants {
styleColor: Appearance.colors.colShadow styleColor: Appearance.colors.colShadow
animateChange: true animateChange: true
} }
component ClockStatusText: RowLayout { component ClockStatusText: Row {
id: statusTextRow id: statusTextRow
property alias statusIcon: statusIconWidget.text property alias statusIcon: statusIconWidget.text
property alias statusText: statusTextWidget.text property alias statusText: statusTextWidget.text
@@ -443,10 +434,10 @@ Variants {
Behavior on opacity { Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
} }
Layout.fillWidth: false spacing: 4
MaterialSymbol { MaterialSymbol {
id: statusIconWidget id: statusIconWidget
Layout.fillWidth: false anchors.verticalCenter: statusTextRow.verticalCenter
iconSize: Appearance.font.pixelSize.huge iconSize: Appearance.font.pixelSize.huge
color: statusTextRow.textColor color: statusTextRow.textColor
style: Text.Raised style: Text.Raised
@@ -454,8 +445,8 @@ Variants {
} }
ClockText { ClockText {
id: statusTextWidget id: statusTextWidget
Layout.fillWidth: false
color: statusTextRow.textColor color: statusTextRow.textColor
anchors.verticalCenter: statusTextRow.verticalCenter
font { font {
family: Appearance.font.family.main family: Appearance.font.family.main
pixelSize: Appearance.font.pixelSize.large pixelSize: Appearance.font.pixelSize.large
@@ -97,7 +97,7 @@ Item { // Bar content region
} }
} }
RowLayout { // Middle section Row { // Middle section
id: middleSection id: middleSection
anchors { anchors {
top: parent.top top: parent.top
@@ -108,8 +108,8 @@ Item { // Bar content region
BarGroup { BarGroup {
id: leftCenterGroup id: leftCenterGroup
Layout.preferredWidth: root.centerSideModuleWidth anchors.verticalCenter: parent.verticalCenter
Layout.fillHeight: false implicitWidth: root.centerSideModuleWidth
Resources { Resources {
alwaysShowAllResources: root.useShortenedForm === 2 alwaysShowAllResources: root.useShortenedForm === 2
@@ -128,6 +128,7 @@ Item { // Bar content region
BarGroup { BarGroup {
id: middleCenterGroup id: middleCenterGroup
anchors.verticalCenter: parent.verticalCenter
padding: workspacesWidget.widgetPadding padding: workspacesWidget.widgetPadding
Workspaces { Workspaces {
@@ -153,9 +154,9 @@ Item { // Bar content region
MouseArea { MouseArea {
id: rightCenterGroup id: rightCenterGroup
implicitWidth: rightCenterGroupContent.implicitWidth anchors.verticalCenter: parent.verticalCenter
implicitWidth: root.centerSideModuleWidth
implicitHeight: rightCenterGroupContent.implicitHeight implicitHeight: rightCenterGroupContent.implicitHeight
Layout.preferredWidth: root.centerSideModuleWidth
onPressed: { onPressed: {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
@@ -14,11 +14,12 @@ StyledPopup {
spacing: 4 spacing: 4
// Header // Header
RowLayout { Row {
id: header id: header
spacing: 5 spacing: 5
MaterialSymbol { MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
fill: 0 fill: 0
font.weight: Font.Medium font.weight: Font.Medium
text: "battery_android_full" text: "battery_android_full"
@@ -27,6 +28,7 @@ StyledPopup {
} }
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter
text: "Battery" text: "Battery"
font { font {
weight: Font.Medium weight: Font.Medium
@@ -39,10 +39,11 @@ StyledPopup {
spacing: 4 spacing: 4
// Date + Time row // Date + Time row
RowLayout { Row {
spacing: 5 spacing: 5
MaterialSymbol { MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
fill: 0 fill: 0
font.weight: Font.Medium font.weight: Font.Medium
text: "calendar_month" text: "calendar_month"
@@ -50,6 +51,7 @@ StyledPopup {
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
} }
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
text: `${root.formattedDate}` text: `${root.formattedDate}`
@@ -79,26 +81,26 @@ StyledPopup {
} }
// Tasks // Tasks
ColumnLayout { Column {
spacing: 0 spacing: 0
Layout.fillWidth: true Layout.fillWidth: true
RowLayout { Row {
spacing: 4 spacing: 4
Layout.fillWidth: true
MaterialSymbol { MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
text: "checklist" text: "checklist"
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
font.pixelSize: Appearance.font.pixelSize.large font.pixelSize: Appearance.font.pixelSize.large
} }
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter
text: Translation.tr("To Do:") text: Translation.tr("To Do:")
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
} }
} }
StyledText { StyledText {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
wrapMode: Text.Wrap wrapMode: Text.Wrap
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
@@ -6,10 +6,16 @@ import qs.modules.common.widgets
Loader { Loader {
id: root id: root
property bool vertical: false property bool vertical: false
active: HyprlandXkb.layoutCodes.length > 1 active: HyprlandXkb.layoutCodes.length > 1
visible: active visible: active
function abbreviateLayoutCode(fullCode) {
return fullCode.split(':').map(layout => {
const baseLayout = layout.split('-')[0];
return baseLayout.slice(0, 4);
}).join('\n');
}
sourceComponent: Item { sourceComponent: Item {
implicitWidth: root.vertical ? null : layoutCodeText.implicitWidth implicitWidth: root.vertical ? null : layoutCodeText.implicitWidth
implicitHeight: root.vertical ? layoutCodeText.implicitHeight : null implicitHeight: root.vertical ? layoutCodeText.implicitHeight : null
@@ -18,7 +24,7 @@ Loader {
id: layoutCodeText id: layoutCodeText
anchors.centerIn: parent anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: HyprlandXkb.currentLayoutCode.split(":").join("\n") text: abbreviateLayoutCode(HyprlandXkb.currentLayoutCode)
font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small
color: rightSidebarButton.colText color: rightSidebarButton.colText
animateChange: true animateChange: true
@@ -20,7 +20,6 @@ StyledPopup {
required property string label required property string label
required property string value required property string value
spacing: 4 spacing: 4
Layout.fillWidth: true
MaterialSymbol { MaterialSymbol {
text: resourceItem.icon text: resourceItem.icon
@@ -40,13 +39,14 @@ StyledPopup {
} }
} }
component ResourceHeaderItem: RowLayout { component ResourceHeaderItem: Row {
id: headerItem id: headerItem
required property var icon required property var icon
required property var label required property var label
spacing: 5 spacing: 5
MaterialSymbol { MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
fill: 0 fill: 0
font.weight: Font.Medium font.weight: Font.Medium
text: headerItem.icon text: headerItem.icon
@@ -55,6 +55,7 @@ StyledPopup {
} }
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter
text: headerItem.label text: headerItem.label
font { font {
weight: Font.Medium weight: Font.Medium
@@ -64,19 +65,20 @@ StyledPopup {
} }
} }
RowLayout { Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 12 spacing: 12
ColumnLayout { Column {
Layout.alignment: Qt.AlignTop anchors.top: parent.top
spacing: 8 spacing: 8
ResourceHeaderItem { ResourceHeaderItem {
icon: "memory" icon: "memory"
label: "RAM" label: "RAM"
} }
ColumnLayout { Column {
spacing: 4
ResourceItem { ResourceItem {
icon: "clock_loader_60" icon: "clock_loader_60"
label: Translation.tr("Used:") label: Translation.tr("Used:")
@@ -95,16 +97,17 @@ StyledPopup {
} }
} }
ColumnLayout { Column {
visible: ResourceUsage.swapTotal > 0 visible: ResourceUsage.swapTotal > 0
Layout.alignment: Qt.AlignTop anchors.top: parent.top
spacing: 8 spacing: 8
ResourceHeaderItem { ResourceHeaderItem {
icon: "swap_horiz" icon: "swap_horiz"
label: "Swap" label: "Swap"
} }
ColumnLayout { Column {
spacing: 4
ResourceItem { ResourceItem {
icon: "clock_loader_60" icon: "clock_loader_60"
label: Translation.tr("Used:") label: Translation.tr("Used:")
@@ -123,15 +126,16 @@ StyledPopup {
} }
} }
ColumnLayout { Column {
Layout.alignment: Qt.AlignTop anchors.top: parent.top
spacing: 8 spacing: 8
ResourceHeaderItem { ResourceHeaderItem {
icon: "planner_review" icon: "planner_review"
label: "CPU" label: "CPU"
} }
ColumnLayout { Column {
spacing: 4
ResourceItem { ResourceItem {
icon: "bolt" icon: "bolt"
label: Translation.tr("Load:") label: Translation.tr("Load:")
@@ -2,7 +2,6 @@ import qs
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import QtQuick import QtQuick
import QtQuick.Layouts
Revealer { // Scroll hint Revealer { // Scroll hint
id: root id: root
@@ -11,10 +10,11 @@ Revealer { // Scroll hint
property string tooltipText: "" property string tooltipText: ""
MouseArea { MouseArea {
id: mouseArea
anchors.right: root.side === "left" ? parent.right : undefined anchors.right: root.side === "left" ? parent.right : undefined
anchors.left: root.side === "right" ? parent.left : undefined anchors.left: root.side === "right" ? parent.left : undefined
implicitWidth: contentColumnLayout.implicitWidth implicitWidth: contentColumn.implicitWidth
implicitHeight: contentColumnLayout.implicitHeight implicitHeight: contentColumn.implicitHeight
property bool hovered: false property bool hovered: false
hoverEnabled: true hoverEnabled: true
@@ -22,32 +22,36 @@ Revealer { // Scroll hint
onExited: hovered = false onExited: hovered = false
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
// StyledToolTip { property bool showHintTimedOut: false
// extraVisibleCondition: tooltipText.length > 0 onHoveredChanged: showHintTimedOut = false
// text: tooltipText Timer {
// } running: mouseArea.hovered
interval: 500
onTriggered: mouseArea.showHintTimedOut = true
}
ColumnLayout { PopupToolTip {
id: contentColumnLayout extraVisibleCondition: (tooltipText.length > 0 && mouseArea.showHintTimedOut)
anchors.centerIn: parent text: tooltipText
}
Column {
id: contentColumn
anchors {
fill: parent
}
spacing: -5 spacing: -5
MaterialSymbol { MaterialSymbol {
Layout.leftMargin: 5
Layout.rightMargin: 5
text: "keyboard_arrow_up" text: "keyboard_arrow_up"
iconSize: 14 iconSize: 14
color: Appearance.colors.colSubtext color: Appearance.colors.colSubtext
} }
MaterialSymbol { MaterialSymbol {
Layout.leftMargin: 5
Layout.rightMargin: 5
text: root.icon text: root.icon
iconSize: 14 iconSize: 14
color: Appearance.colors.colSubtext color: Appearance.colors.colSubtext
} }
MaterialSymbol { MaterialSymbol {
Layout.leftMargin: 5
Layout.rightMargin: 5
text: "keyboard_arrow_down" text: "keyboard_arrow_down"
iconSize: 14 iconSize: 14
color: Appearance.colors.colSubtext color: Appearance.colors.colSubtext
@@ -24,8 +24,8 @@ LazyLoader {
anchors.top: Config.options.bar.vertical || (!Config.options.bar.vertical && !Config.options.bar.bottom) anchors.top: Config.options.bar.vertical || (!Config.options.bar.vertical && !Config.options.bar.bottom)
anchors.bottom: !Config.options.bar.vertical && Config.options.bar.bottom anchors.bottom: !Config.options.bar.vertical && Config.options.bar.bottom
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 + root.popupBackgroundMargin
implicitHeight: popupBackground.implicitHeight + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin implicitHeight: popupBackground.implicitHeight + Appearance.sizes.elevationMargin * 2 + root.popupBackgroundMargin
mask: Region { mask: Region {
item: popupBackground item: popupBackground
@@ -63,10 +63,10 @@ LazyLoader {
readonly property real margin: 10 readonly property real margin: 10
anchors { anchors {
fill: parent fill: parent
leftMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.left) leftMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.left)
rightMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.right) rightMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.right)
topMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.top) topMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.top)
bottomMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.bottom) bottomMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.bottom)
} }
implicitWidth: root.contentItem.implicitWidth + margin * 2 implicitWidth: root.contentItem.implicitWidth + margin * 2
implicitHeight: root.contentItem.implicitHeight + margin * 2 implicitHeight: root.contentItem.implicitHeight + margin * 2
@@ -5,7 +5,6 @@ import qs.modules.common.widgets
import qs.modules.common.functions import qs.modules.common.functions
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
@@ -19,7 +18,8 @@ Item {
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown) readonly property int workspacesShown: Config.options.bar.workspaces.shown
readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown)
property list<bool> workspaceOccupied: [] property list<bool> workspaceOccupied: []
property int widgetPadding: 4 property int widgetPadding: 4
property int workspaceButtonWidth: 26 property int workspaceButtonWidth: 26
@@ -28,7 +28,7 @@ Item {
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
property real workspaceIconOpacityShrinked: 1 property real workspaceIconOpacityShrinked: 1
property real workspaceIconMarginShrinked: -4 property real workspaceIconMarginShrinked: -4
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % root.workspacesShown
property bool showNumbers: false property bool showNumbers: false
Timer { Timer {
@@ -56,8 +56,8 @@ Item {
// Function to update workspaceOccupied // Function to update workspaceOccupied
function updateWorkspaceOccupied() { function updateWorkspaceOccupied() {
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => { workspaceOccupied = Array.from({ length: root.workspacesShown }, (_, i) => {
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1); return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * root.workspacesShown + i + 1);
}) })
} }
@@ -79,8 +79,8 @@ Item {
updateWorkspaceOccupied(); updateWorkspaceOccupied();
} }
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : backgroundLayout.implicitWidth implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown)
implicitHeight: root.vertical ? backgroundLayout.implicitHeight : Appearance.sizes.barHeight implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown) : Appearance.sizes.barHeight
// Scroll to switch workspaces // Scroll to switch workspaces
WheelHandler { WheelHandler {
@@ -104,24 +104,20 @@ Item {
} }
// Workspaces - background // Workspaces - background
GridLayout { Grid {
id: backgroundLayout
z: 1 z: 1
anchors.fill: parent anchors.centerIn: parent
implicitHeight: root.vertical ? root.workspaceButtonWidth : Appearance.sizes.barHeight
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : root.workspaceButtonWidth
rowSpacing: 0 rowSpacing: 0
columnSpacing: 0 columnSpacing: 0
columns: root.vertical ? 1 : -1 columns: root.vertical ? 1 : root.workspacesShown
rows: root.vertical ? root.workspacesShown : 1
Repeater { Repeater {
model: Config.options.bar.workspaces.shown model: root.workspacesShown
Rectangle { Rectangle {
z: 1 z: 1
Layout.alignment: root.vertical ? Qt.AlignHCenter : Qt.AlignVCenter
implicitWidth: workspaceButtonWidth implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth implicitHeight: workspaceButtonWidth
radius: (width / 2) radius: (width / 2)
@@ -195,26 +191,24 @@ Item {
} }
// Workspaces - numbers // Workspaces - numbers
GridLayout { Grid {
id: rowLayoutNumbers
z: 3 z: 3
columns: vertical ? 1 : -1 columns: root.vertical ? 1 : root.workspacesShown
rows: root.vertical ? root.workspacesShown : 1
columnSpacing: 0 columnSpacing: 0
rowSpacing: 0 rowSpacing: 0
anchors.fill: parent anchors.fill: parent
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
Repeater { Repeater {
model: Config.options.bar.workspaces.shown model: root.workspacesShown
Button { Button {
id: button id: button
property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1 property int workspaceValue: workspaceGroup * root.workspacesShown + index + 1
Layout.fillHeight: !root.vertical implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
Layout.fillWidth: root.vertical implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`) onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
width: vertical ? undefined : workspaceButtonWidth width: vertical ? undefined : workspaceButtonWidth
height: vertical ? workspaceButtonWidth : undefined height: vertical ? workspaceButtonWidth : undefined
@@ -1,3 +1,4 @@
pragma ComponentBehavior: Bound
import qs import qs
import qs.services import qs.services
import qs.modules.common import qs.modules.common
@@ -11,8 +12,9 @@ Item {
readonly property var keybinds: HyprlandKeybinds.keybinds readonly property var keybinds: HyprlandKeybinds.keybinds
property real spacing: 20 property real spacing: 20
property real titleSpacing: 7 property real titleSpacing: 7
implicitWidth: rowLayout.implicitWidth property real padding: 4
implicitHeight: rowLayout.implicitHeight implicitWidth: row.implicitWidth + padding * 2
implicitHeight: row.implicitHeight + padding * 2
property var keyBlacklist: ["Super_L"] property var keyBlacklist: ["Super_L"]
property var keySubstitutions: ({ property var keySubstitutions: ({
@@ -28,43 +30,51 @@ Item {
// "Shift": "", // "Shift": "",
}) })
RowLayout { // Keybind columns Row { // Keybind columns
id: rowLayout id: row
spacing: root.spacing spacing: root.spacing
Repeater { Repeater {
model: keybinds.children model: keybinds.children
delegate: ColumnLayout { // Keybind sections delegate: Column { // Keybind sections
spacing: root.spacing spacing: root.spacing
required property var modelData required property var modelData
Layout.alignment: Qt.AlignTop anchors.top: row.top
Repeater { Repeater {
model: modelData.children model: modelData.children
delegate: Item { // Section with real keybinds delegate: Item { // Section with real keybinds
id: keybindSection
required property var modelData required property var modelData
implicitWidth: sectionColumnLayout.implicitWidth implicitWidth: sectionColumn.implicitWidth
implicitHeight: sectionColumnLayout.implicitHeight implicitHeight: sectionColumn.implicitHeight
ColumnLayout {
id: sectionColumnLayout Column {
id: sectionColumn
anchors.centerIn: parent anchors.centerIn: parent
spacing: root.titleSpacing spacing: root.titleSpacing
StyledText { StyledText {
id: sectionTitle id: sectionTitle
font.family: Appearance.font.family.title font.family: Appearance.font.family.title
font.pixelSize: Appearance.font.pixelSize.huge font.pixelSize: Appearance.font.pixelSize.huge
color: Appearance.colors.colOnLayer0 color: Appearance.colors.colOnLayer0
text: modelData.name text: keybindSection.modelData.name
} }
GridLayout { Grid {
id: keybindGrid id: keybindGrid
columns: 2 columns: 2
columnSpacing: 4
rowSpacing: 4
Repeater { Repeater {
model: { model: {
var result = []; var result = [];
for (var i = 0; i < modelData.keybinds.length; i++) { for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
const keybind = modelData.keybinds[i]; const keybind = keybindSection.modelData.keybinds[i];
result.push({ result.push({
"type": "keys", "type": "keys",
"mods": keybind.mods, "mods": keybind.mods,
@@ -89,7 +99,7 @@ Item {
Component { Component {
id: keysComponent id: keysComponent
RowLayout { Row {
spacing: 4 spacing: 4
Repeater { Repeater {
model: modelData.mods model: modelData.mods
@@ -101,7 +111,6 @@ Item {
StyledText { StyledText {
id: keybindPlus id: keybindPlus
visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0 visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
Layout.alignment: Qt.AlignVCenter
text: "+" text: "+"
} }
KeyboardKey { KeyboardKey {
@@ -15,14 +15,14 @@ Item {
implicitWidth: mainLayout.implicitWidth implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight implicitHeight: mainLayout.implicitHeight
ColumnLayout { Column {
id: mainLayout id: mainLayout
spacing: root.spacing spacing: root.spacing
Repeater { // Main table rows Repeater { // Main table rows
model: root.elements model: root.elements
delegate: RowLayout { // Table cells delegate: Row { // Table cells
id: tableRow id: tableRow
spacing: root.spacing spacing: root.spacing
required property var modelData required property var modelData
@@ -47,7 +47,7 @@ Item {
Repeater { // Main table rows Repeater { // Main table rows
model: root.series model: root.series
delegate: RowLayout { // Table cells delegate: Row { // Table cells
id: seriesTableRow id: seriesTableRow
spacing: root.spacing spacing: root.spacing
required property var modelData required property var modelData
@@ -1,4 +1,5 @@
import qs.modules.common import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets import qs.modules.common.widgets
import QtQuick import QtQuick
@@ -18,7 +19,7 @@ RippleButton {
topMargin: 4 topMargin: 4
leftMargin: 4 leftMargin: 4
} }
color: Appearance.colors.colLayer2 color: ColorUtils.transparentize(Appearance.colors.colLayer2)
radius: Appearance.rounding.full radius: Appearance.rounding.full
implicitWidth: Math.max(20, elementNumber.implicitWidth) implicitWidth: Math.max(20, elementNumber.implicitWidth)
implicitHeight: Math.max(20, elementNumber.implicitHeight) implicitHeight: Math.max(20, elementNumber.implicitHeight)
@@ -26,13 +27,35 @@ RippleButton {
StyledText { StyledText {
id: elementNumber id: elementNumber
anchors.centerIn: parent anchors.left: parent.left
color: Appearance.colors.colOnLayer2 color: Appearance.colors.colOnLayer2
text: root.element.number text: root.element.number
font.pixelSize: Appearance.font.pixelSize.smallest font.pixelSize: Appearance.font.pixelSize.smallest
} }
} }
Rectangle {
anchors {
top: parent.top
right: parent.right
topMargin: 4
rightMargin: 4
}
color: ColorUtils.transparentize(Appearance.colors.colLayer2)
radius: Appearance.rounding.full
implicitWidth: Math.max(20, elementWeight.implicitWidth)
implicitHeight: Math.max(20, elementWeight.implicitHeight)
width: height
StyledText {
id: elementWeight
anchors.right: parent.right
color: Appearance.colors.colOnLayer2
text: root.element.weight
font.pixelSize: Appearance.font.pixelSize.smallest
}
}
StyledText { StyledText {
id: elementSymbol id: elementSymbol
anchors.centerIn: parent anchors.centerIn: parent
@@ -356,7 +356,7 @@ Singleton {
property real mediaControlsWidth: 440 property real mediaControlsWidth: 440
property real mediaControlsHeight: 160 property real mediaControlsHeight: 160
property real notificationPopupWidth: 410 property real notificationPopupWidth: 410
property real osdWidth: 200 property real osdWidth: 180
property real searchWidthCollapsed: 260 property real searchWidthCollapsed: 260
property real searchWidth: 450 property real searchWidth: 450
property real sidebarWidth: 460 property real sidebarWidth: 460
@@ -260,6 +260,7 @@ Singleton {
} }
property JsonObject lock: JsonObject { property JsonObject lock: JsonObject {
property bool launchOnStartup: false
property JsonObject blur: JsonObject { property JsonObject blur: JsonObject {
property bool enable: false property bool enable: false
property real radius: 100 property real radius: 100
@@ -267,6 +268,7 @@ Singleton {
} }
property bool centerClock: true property bool centerClock: true
property bool showLockedText: true property bool showLockedText: true
property bool unlockKeyring: true
} }
property JsonObject media: JsonObject { property JsonObject media: JsonObject {
@@ -11,6 +11,15 @@ Singleton {
property string fileName: "states.json" property string fileName: "states.json"
property string filePath: `${root.fileDir}/${root.fileName}` property string filePath: `${root.fileDir}/${root.fileName}`
property bool ready: false
property string previousHyprlandInstanceSignature: ""
property bool isNewHyprlandInstance: previousHyprlandInstanceSignature !== states.hyprlandInstanceSignature
onReadyChanged: {
root.previousHyprlandInstanceSignature = root.states.hyprlandInstanceSignature
root.states.hyprlandInstanceSignature = Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE") || ""
}
Timer { Timer {
id: fileReloadTimer id: fileReloadTimer
interval: 100 interval: 100
@@ -36,6 +45,7 @@ Singleton {
watchChanges: true watchChanges: true
onFileChanged: fileReloadTimer.restart() onFileChanged: fileReloadTimer.restart()
onAdapterUpdated: fileWriteTimer.restart() onAdapterUpdated: fileWriteTimer.restart()
onLoaded: root.ready = true
onLoadFailed: error => { onLoadFailed: error => {
console.log("Failed to load persistent states file:", error); console.log("Failed to load persistent states file:", error);
if (error == FileViewError.FileNotFound) { if (error == FileViewError.FileNotFound) {
@@ -45,6 +55,9 @@ Singleton {
adapter: JsonAdapter { adapter: JsonAdapter {
id: persistentStatesJsonAdapter id: persistentStatesJsonAdapter
property string hyprlandInstanceSignature: ""
property JsonObject ai: JsonObject { property JsonObject ai: JsonObject {
property string model property string model
property real temperature: 0.5 property real temperature: 0.5
@@ -77,7 +77,7 @@ Rectangle {
} }
} }
Image { StyledImage {
id: image id: image
anchors.fill: parent anchors.fill: parent
@@ -72,8 +72,8 @@ RippleButton {
Layout.bottomMargin: 5 Layout.bottomMargin: 5
Layout.fillWidth: true Layout.fillWidth: true
value: 0.7 value: 0.7
sperm: true wavy: true
animateSperm: lightDarkButtonRoot.toggled animateWave: lightDarkButtonRoot.toggled
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5) trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
} }
@@ -173,7 +173,7 @@ Item { // Notification item area
implicitHeight: summaryText.implicitHeight implicitHeight: summaryText.implicitHeight
StyledText { StyledText {
id: summaryText id: summaryText
Layout.fillWidth: summaryTextMetrics.width >= summaryRow.width * root.summaryElideRatio Layout.fillWidth: summaryTextMetrics.width >= summaryRow.implicitWidth * root.summaryElideRatio
visible: !root.onlyNotification visible: !root.onlyNotification
font.pixelSize: root.fontSize font.pixelSize: root.fontSize
color: Appearance.colors.colOnLayer3 color: Appearance.colors.colOnLayer3
@@ -1,12 +1,9 @@
import qs.services pragma ComponentBehavior: Bound
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
/** /**
* Material 3 progress bar. See https://m3.material.io/components/progress-indicators/overview * Material 3 progress bar. See https://m3.material.io/components/progress-indicators/overview
@@ -18,13 +15,13 @@ ProgressBar {
property real valueBarGap: 4 property real valueBarGap: 4
property color highlightColor: Appearance?.colors.colPrimary ?? "#685496" property color highlightColor: Appearance?.colors.colPrimary ?? "#685496"
property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9" property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9"
property bool sperm: false // If true, the progress bar will have a wavy fill effect property bool wavy: false // If true, the progress bar will have a wavy fill effect
property bool animateSperm: true property bool animateWave: true
property real spermAmplitudeMultiplier: sperm ? 0.5 : 0 property real waveAmplitudeMultiplier: wavy ? 0.5 : 0
property real spermFrequency: 6 property real waveFrequency: 6
property real spermFps: 60 property real waveFps: 60
Behavior on spermAmplitudeMultiplier { Behavior on waveAmplitudeMultiplier {
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
} }
@@ -38,64 +35,62 @@ ProgressBar {
} }
contentItem: Item { contentItem: Item {
id: contentItem
anchors.fill: parent anchors.fill: parent
Canvas { Loader {
id: wavyFill
anchors { anchors {
left: parent.left left: parent.left
right: parent.right
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
height: parent.height * 6 active: root.wavy
onPaint: { sourceComponent: WavyLine {
var ctx = getContext("2d"); id: wavyFill
ctx.clearRect(0, 0, width, height); frequency: root.waveFrequency
color: root.highlightColor
var progress = root.visualPosition; amplitudeMultiplier: root.wavy ? 0.5 : 0
var fillWidth = progress * width; height: contentItem.height * 6
var amplitude = parent.height * root.spermAmplitudeMultiplier; width: contentItem.width * root.visualPosition
var frequency = root.spermFrequency; lineWidth: contentItem.height
var phase = Date.now() / 400.0; fullLength: root.width
var centerY = height / 2; Connections {
target: root
ctx.strokeStyle = root.highlightColor; function onValueChanged() { wavyFill.requestPaint(); }
ctx.lineWidth = parent.height; function onHighlightColorChanged() { wavyFill.requestPaint(); }
ctx.lineCap = "round"; }
ctx.beginPath(); FrameAnimation {
for (var x = ctx.lineWidth / 2; x <= fillWidth; x += 1) { running: root.animateWave
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / width + phase); onTriggered: {
if (x === 0) wavyFill.requestPaint()
ctx.moveTo(x, waveY); }
else
ctx.lineTo(x, waveY);
} }
ctx.stroke();
}
Connections {
target: root
function onValueChanged() { wavyFill.requestPaint(); }
function onHighlightColorChanged() { wavyFill.requestPaint(); }
}
Timer {
interval: 1000 / root.spermFps
running: root.animateSperm
repeat: root.sperm
onTriggered: wavyFill.requestPaint()
} }
} }
Loader {
active: !root.wavy
sourceComponent: Rectangle {
anchors.left: parent.left
width: contentItem.width * root.visualPosition
height: contentItem.height
radius: height / 2
color: root.highlightColor
}
}
Rectangle { // Right remaining part fill Rectangle { // Right remaining part fill
anchors.right: parent.right anchors.right: parent.right
width: (1 - root.visualPosition) * parent.width - valueBarGap width: (1 - root.visualPosition) * parent.width - valueBarGap
height: parent.height height: parent.height
radius: Appearance?.rounding.full ?? 9999 radius: height / 2
color: root.trackColor color: root.trackColor
} }
Rectangle { // Stop point Rectangle { // Stop point
anchors.right: parent.right anchors.right: parent.right
width: valueBarGap width: valueBarGap
height: valueBarGap height: valueBarGap
radius: Appearance?.rounding.full ?? 9999 radius: height / 2
color: root.highlightColor color: root.highlightColor
} }
} }
@@ -5,7 +5,7 @@ import qs.modules.common
RectangularShadow { RectangularShadow {
required property var target required property var target
anchors.fill: target anchors.fill: target
radius: 20 radius: target.radius
blur: 0.9 * Appearance.sizes.elevationMargin blur: 0.9 * Appearance.sizes.elevationMargin
offset: Qt.vector2d(0.0, 1.0) offset: Qt.vector2d(0.0, 1.0)
spread: 1 spread: 1
@@ -1,3 +1,4 @@
pragma ComponentBehavior: Bound
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import qs.services import qs.services
@@ -17,6 +18,7 @@ Slider {
property list<real> stopIndicatorValues: [1] property list<real> stopIndicatorValues: [1]
enum Configuration { enum Configuration {
Wavy = 4,
XS = 12, XS = 12,
S = 18, S = 18,
M = 30, M = 30,
@@ -28,10 +30,9 @@ Slider {
property real handleDefaultWidth: 3 property real handleDefaultWidth: 3
property real handlePressedWidth: 1.5 property real handlePressedWidth: 1.5
property color highlightColor: Appearance.colors.colPrimary property color highlightColor: Appearance.colors.colPrimary
property color trackColor: Appearance.colors.colSecondaryContainer property color trackColor: Appearance.colors.colSecondaryContainer
property color handleColor: Appearance.m3colors.m3onSecondaryContainer property color handleColor: Appearance.colors.colPrimary
property color dotColor: Appearance.m3colors.m3onSecondaryContainer property color dotColor: Appearance.m3colors.m3onSecondaryContainer
property color dotColorHighlighted: Appearance.m3colors.m3onPrimary property color dotColorHighlighted: Appearance.m3colors.m3onPrimary
property real unsharpenRadius: Appearance.rounding.unsharpen property real unsharpenRadius: Appearance.rounding.unsharpen
@@ -39,15 +40,18 @@ Slider {
property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21 property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21
: trackWidth >= StyledSlider.Configuration.L ? 12 : trackWidth >= StyledSlider.Configuration.L ? 12
: trackWidth >= StyledSlider.Configuration.M ? 9 : trackWidth >= StyledSlider.Configuration.M ? 9
: 6 : trackWidth >= StyledSlider.Configuration.S ? 6
property real handleHeight: Math.max(33, trackWidth + 9) : height / 2
property real handleHeight: (configuration === StyledSlider.Configuration.Wavy) ? 24 : Math.max(33, trackWidth + 9)
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
property real handleMargins: 4 property real handleMargins: 4
onHandleMarginsChanged: {
console.log("Handle margins changed to", handleMargins);
}
property real trackDotSize: 3 property real trackDotSize: 3
property string tooltipContent: `${Math.round(value * 100)}%` property string tooltipContent: `${Math.round(value * 100)}%`
property bool wavy: configuration === StyledSlider.Configuration.Wavy // If true, the progress bar will have a wavy fill effect
property bool animateWave: true
property real waveAmplitudeMultiplier: wavy ? 0.5 : 0
property real waveFrequency: 6
property real waveFps: 60
leftPadding: handleMargins leftPadding: handleMargins
rightPadding: handleMargins rightPadding: handleMargins
@@ -93,18 +97,51 @@ Slider {
implicitHeight: trackWidth implicitHeight: trackWidth
// Fill left // Fill left
Rectangle { Loader {
anchors { anchors {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left: parent.left left: parent.left
} }
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins) width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
height: trackWidth height: root.trackWidth
color: root.highlightColor active: !root.wavy
topLeftRadius: root.trackRadius sourceComponent: Rectangle {
bottomLeftRadius: root.trackRadius color: root.highlightColor
topRightRadius: root.unsharpenRadius topLeftRadius: root.trackRadius
bottomRightRadius: root.unsharpenRadius bottomLeftRadius: root.trackRadius
topRightRadius: root.unsharpenRadius
bottomRightRadius: root.unsharpenRadius
}
}
Loader {
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
height: root.height
active: root.wavy
sourceComponent: WavyLine {
id: wavyFill
frequency: root.waveFrequency
fullLength: root.width
color: root.highlightColor
amplitudeMultiplier: root.wavy ? 0.5 : 0
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
height: root.trackWidth
Connections {
target: root
function onValueChanged() { wavyFill.requestPaint(); }
function onHighlightColorChanged() { wavyFill.requestPaint(); }
}
FrameAnimation {
running: root.animateWave
onTriggered: {
wavyFill.requestPaint()
}
}
}
} }
// Fill right // Fill right
@@ -16,6 +16,7 @@ Item {
default property alias data: toolbarLayout.data default property alias data: toolbarLayout.data
implicitWidth: background.implicitWidth implicitWidth: background.implicitWidth
implicitHeight: background.implicitHeight implicitHeight: background.implicitHeight
property alias radius: background.radius
StyledRectangularShadow { StyledRectangularShadow {
target: background target: background
@@ -23,11 +24,11 @@ Item {
Rectangle { Rectangle {
id: background id: background
anchors.centerIn: parent anchors.fill: parent
color: Appearance.m3colors.m3surfaceContainer // Needs to be opaque color: Appearance.m3colors.m3surfaceContainer // Needs to be opaque
implicitHeight: Math.max(toolbarLayout.implicitHeight + root.padding * 2, 56) implicitHeight: Math.max(toolbarLayout.implicitHeight + root.padding * 2, 56)
implicitWidth: toolbarLayout.implicitWidth + root.padding * 2 implicitWidth: toolbarLayout.implicitWidth + root.padding * 2
radius: Appearance.rounding.full radius: height / 2
RowLayout { RowLayout {
id: toolbarLayout id: toolbarLayout
@@ -0,0 +1,34 @@
import qs.modules.common
import QtQuick
Canvas {
id: root
property real amplitudeMultiplier: 0.5
property real frequency: 6
property color color: Appearance?.colors.colPrimary ?? "#685496"
property real lineWidth: 4
property real fullLength: width
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);
var amplitude = root.lineWidth * root.amplitudeMultiplier;
var frequency = root.frequency;
var phase = Date.now() / 400.0;
var centerY = height / 2;
ctx.strokeStyle = root.color;
ctx.lineWidth = root.lineWidth;
ctx.lineCap = "round";
ctx.beginPath();
for (var x = ctx.lineWidth / 2; x <= root.width - ctx.lineWidth / 2; x += 1) {
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / root.fullLength + phase);
if (x === 0)
ctx.moveTo(x, waveY);
else
ctx.lineTo(x, waveY);
}
ctx.stroke();
}
}
@@ -1,4 +1,5 @@
import qs import qs
import qs.modules.common
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.Pam import Quickshell.Services.Pam
@@ -55,6 +56,7 @@ Scope {
onCompleted: result => { onCompleted: result => {
if (result == PamResult.Success) { if (result == PamResult.Success) {
root.unlocked(); root.unlocked();
if (Config.options.lock.unlockKeyring) root.unlockKeyring();
} else { } else {
root.showFailure = true; root.showFailure = true;
GlobalStates.screenUnlockFailed = true; GlobalStates.screenUnlockFailed = true;
@@ -64,4 +66,13 @@ Scope {
root.unlockInProgress = false; root.unlockInProgress = false;
} }
} }
function unlockKeyring() {
Quickshell.execDetached({
environment: ({
UNLOCK_PASSWORD: root.currentText
}),
command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")]
})
}
} }
@@ -155,21 +155,21 @@ MouseArea {
opacity: root.toolbarOpacity opacity: root.toolbarOpacity
// Username // Username
RowLayout { Row {
spacing: 6 spacing: 6
Layout.leftMargin: 8 Layout.leftMargin: 8
Layout.fillHeight: true Layout.fillHeight: true
MaterialSymbol { MaterialSymbol {
id: userIcon id: userIcon
Layout.alignment: Qt.AlignVCenter anchors.verticalCenter: parent.verticalCenter
fill: 1 fill: 1
text: "account_circle" text: "account_circle"
iconSize: Appearance.font.pixelSize.huge iconSize: Appearance.font.pixelSize.huge
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
} }
StyledText { StyledText {
Layout.alignment: Qt.AlignVCenter anchors.verticalCenter: parent.verticalCenter
text: SystemInfo.username text: SystemInfo.username
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
} }
@@ -184,18 +184,19 @@ MouseArea {
active: true active: true
visible: active visible: active
sourceComponent: RowLayout { sourceComponent: Row {
spacing: 8 spacing: 8
MaterialSymbol { MaterialSymbol {
id: keyboardIcon id: keyboardIcon
Layout.alignment: Qt.AlignVCenter anchors.verticalCenter: parent.verticalCenter
fill: 1 fill: 1
text: "keyboard_alt" text: "keyboard_alt"
iconSize: Appearance.font.pixelSize.huge iconSize: Appearance.font.pixelSize.huge
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
} }
Loader { Loader {
anchors.verticalCenter: parent.verticalCenter
sourceComponent: StyledText { sourceComponent: StyledText {
text: HyprlandXkb.currentLayoutCode text: HyprlandXkb.currentLayoutCode
color: Appearance.colors.colOnSurfaceVariant color: Appearance.colors.colOnSurfaceVariant
@@ -229,18 +230,18 @@ MouseArea {
scale: root.toolbarScale scale: root.toolbarScale
opacity: root.toolbarOpacity opacity: root.toolbarOpacity
RowLayout { Row {
visible: UPower.displayDevice.isLaptopBattery visible: UPower.displayDevice.isLaptopBattery
spacing: 6 spacing: 4
Layout.fillHeight: true Layout.fillHeight: true
Layout.leftMargin: 10 Layout.leftMargin: 10
Layout.rightMargin: 10 Layout.rightMargin: 10
MaterialSymbol { MaterialSymbol {
id: boltIcon id: boltIcon
Layout.alignment: Qt.AlignVCenter anchors {
Layout.leftMargin: -2 verticalCenter: parent.verticalCenter
Layout.rightMargin: -2 }
fill: 1 fill: 1
text: Battery.isCharging ? "bolt" : "battery_android_full" text: Battery.isCharging ? "bolt" : "battery_android_full"
iconSize: Appearance.font.pixelSize.huge iconSize: Appearance.font.pixelSize.huge
@@ -248,7 +249,7 @@ MouseArea {
color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant
} }
StyledText { StyledText {
Layout.alignment: Qt.AlignVCenter anchors.verticalCenter: parent.verticalCenter
text: Math.round(Battery.percentage * 100) text: Math.round(Battery.percentage * 100)
color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant
} }
@@ -1,3 +1,4 @@
pragma ComponentBehavior: Bound
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import qs.services import qs.services
@@ -153,8 +154,8 @@ Scope {
required property MprisPlayer modelData required property MprisPlayer modelData
player: modelData player: modelData
visualizerPoints: root.visualizerPoints visualizerPoints: root.visualizerPoints
implicitWidth: widgetWidth implicitWidth: root.widgetWidth
implicitHeight: widgetHeight implicitHeight: root.widgetHeight
radius: root.popupRounding radius: root.popupRounding
} }
} }
@@ -1,3 +1,4 @@
pragma ComponentBehavior: Bound
import qs.modules.common import qs.modules.common
import qs.modules.common.models import qs.modules.common.models
import qs.modules.common.widgets import qs.modules.common.widgets
@@ -160,7 +161,7 @@ Item { // Player instance
} }
} }
Image { // Art image StyledImage { // Art image
id: mediaArt id: mediaArt
property int size: parent.height property int size: parent.height
anchors.fill: parent anchors.fill: parent
@@ -169,7 +170,6 @@ Item { // Player instance
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
cache: false cache: false
antialiasing: true antialiasing: true
asynchronous: true
width: size width: size
height: size height: size
@@ -233,16 +233,41 @@ Item { // Player instance
Item { Item {
id: progressBarContainer id: progressBarContainer
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: progressBar.implicitHeight implicitHeight: Math.max(sliderLoader.implicitHeight, progressBarLoader.implicitHeight)
StyledProgressBar { Loader {
id: progressBar id: sliderLoader
anchors.fill: parent anchors.fill: parent
highlightColor: blendedColors.colPrimary active: playerController.player?.canSeek ?? false
trackColor: blendedColors.colSecondaryContainer sourceComponent: StyledSlider {
value: playerController.player?.position / playerController.player?.length configuration: StyledSlider.Configuration.Wavy
sperm: playerController.player?.isPlaying highlightColor: blendedColors.colPrimary
trackColor: blendedColors.colSecondaryContainer
handleColor: blendedColors.colPrimary
value: playerController.player?.position / playerController.player?.length
onMoved: {
playerController.player.position = value * playerController.player.length;
}
}
} }
Loader {
id: progressBarLoader
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
right: parent.right
}
active: !(playerController.player?.canSeek ?? false)
sourceComponent: StyledProgressBar {
wavy: playerController.player?.isPlaying
highlightColor: blendedColors.colPrimary
trackColor: blendedColors.colSecondaryContainer
value: playerController.player?.position / playerController.player?.length
}
}
} }
TrackChangeButton { TrackChangeButton {
iconName: "skip_next" iconName: "skip_next"
@@ -15,9 +15,21 @@ Scope {
property string protectionMessage: "" property string protectionMessage: ""
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
property string currentIndicator: "volume"
property var indicators: [
{
id: "volume",
sourceUrl: "indicators/VolumeIndicator.qml"
},
{
id: "brightness",
sourceUrl: "indicators/BrightnessIndicator.qml"
},
]
function triggerOsd() { function triggerOsd() {
GlobalStates.osdVolumeOpen = true GlobalStates.osdVolumeOpen = true;
osdTimeout.restart() osdTimeout.restart();
} }
Timer { Timer {
@@ -26,35 +38,44 @@ Scope {
repeat: false repeat: false
running: false running: false
onTriggered: { onTriggered: {
GlobalStates.osdVolumeOpen = false GlobalStates.osdVolumeOpen = false;
root.protectionMessage = "" root.protectionMessage = "";
} }
} }
Connections { Connections {
target: Brightness target: Brightness
function onBrightnessChanged() { function onBrightnessChanged() {
GlobalStates.osdVolumeOpen = false root.protectionMessage = "";
root.currentIndicator = "brightness";
root.triggerOsd();
} }
} }
Connections { // Listen to volume changes Connections {
// Listen to volume changes
target: Audio.sink?.audio ?? null target: Audio.sink?.audio ?? null
function onVolumeChanged() { function onVolumeChanged() {
if (!Audio.ready) return if (!Audio.ready)
root.triggerOsd() return;
root.currentIndicator = "volume";
root.triggerOsd();
} }
function onMutedChanged() { function onMutedChanged() {
if (!Audio.ready) return if (!Audio.ready)
root.triggerOsd() return;
root.currentIndicator = "volume";
root.triggerOsd();
} }
} }
Connections { // Listen to protection triggers Connections {
// Listen to protection triggers
target: Audio target: Audio
function onSinkProtectionTriggered(reason) { function onSinkProtectionTriggered(reason) {
root.protectionMessage = reason; root.protectionMessage = reason;
root.triggerOsd() root.currentIndicator = "volume";
root.triggerOsd();
} }
} }
@@ -69,7 +90,7 @@ Scope {
Connections { Connections {
target: root target: root
function onFocusedScreenChanged() { function onFocusedScreenChanged() {
osdRoot.screen = root.focusedScreen osdRoot.screen = root.focusedScreen;
} }
} }
@@ -97,10 +118,11 @@ Scope {
ColumnLayout { ColumnLayout {
id: columnLayout id: columnLayout
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
Item { Item {
id: osdValuesWrapper id: osdValuesWrapper
// Extra space for shadow // Extra space for shadow
implicitHeight: contentColumnLayout.implicitHeight + Appearance.sizes.elevationMargin * 2 implicitHeight: contentColumnLayout.implicitHeight
implicitWidth: contentColumnLayout.implicitWidth implicitWidth: contentColumnLayout.implicitWidth
clip: true clip: true
@@ -110,30 +132,25 @@ Scope {
onEntered: GlobalStates.osdVolumeOpen = false onEntered: GlobalStates.osdVolumeOpen = false
} }
ColumnLayout { Column {
id: contentColumnLayout id: contentColumnLayout
anchors { anchors {
top: parent.top top: parent.top
left: parent.left left: parent.left
right: parent.right right: parent.right
leftMargin: Appearance.sizes.elevationMargin
rightMargin: Appearance.sizes.elevationMargin
} }
spacing: 0 spacing: 0
OsdValueIndicator { Loader {
id: osdValues id: osdIndicatorLoader
Layout.fillWidth: true source: root.indicators.find(i => i.id === root.currentIndicator)?.sourceUrl
value: Audio.sink?.audio.volume ?? 0
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
name: Translation.tr("Volume")
} }
Item { Item {
id: protectionMessageWrapper id: protectionMessageWrapper
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: protectionMessageBackground.implicitHeight implicitHeight: protectionMessageBackground.implicitHeight
implicitWidth: protectionMessageBackground.implicitWidth implicitWidth: protectionMessageBackground.implicitWidth
Layout.alignment: Qt.AlignHCenter
opacity: root.protectionMessage !== "" ? 1 : 0 opacity: root.protectionMessage !== "" ? 1 : 0
StyledRectangularShadow { StyledRectangularShadow {
@@ -174,26 +191,26 @@ Scope {
} }
IpcHandler { IpcHandler {
target: "osdVolume" target: "osdVolume"
function trigger() { function trigger() {
root.triggerOsd() root.triggerOsd();
} }
function hide() { function hide() {
GlobalStates.osdVolumeOpen = false GlobalStates.osdVolumeOpen = false;
} }
function toggle() { function toggle() {
GlobalStates.osdVolumeOpen = !GlobalStates.osdVolumeOpen GlobalStates.osdVolumeOpen = !GlobalStates.osdVolumeOpen;
} }
} }
GlobalShortcut { GlobalShortcut {
name: "osdVolumeTrigger" name: "osdVolumeTrigger"
description: "Triggers volume OSD on press" description: "Triggers volume OSD on press"
onPressed: { onPressed: {
root.triggerOsd() root.triggerOsd();
} }
} }
GlobalShortcut { GlobalShortcut {
@@ -201,8 +218,7 @@ Scope {
description: "Hides volume OSD on press" description: "Hides volume OSD on press"
onPressed: { onPressed: {
GlobalStates.osdVolumeOpen = false GlobalStates.osdVolumeOpen = false;
} }
} }
} }
@@ -1,157 +0,0 @@
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.Wayland
Scope {
id: root
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
function triggerOsd() {
GlobalStates.osdBrightnessOpen = true
osdTimeout.restart()
}
Timer {
id: osdTimeout
interval: Config.options.osd.timeout
repeat: false
running: false
onTriggered: {
GlobalStates.osdBrightnessOpen = false
}
}
Connections {
target: Audio.sink?.audio ?? null
function onVolumeChanged() {
if (!Audio.ready) return
GlobalStates.osdBrightnessOpen = false
}
}
Connections {
target: Brightness
function onBrightnessChanged() {
if (!root.brightnessMonitor.ready) return
root.triggerOsd()
}
}
Loader {
id: osdLoader
active: GlobalStates.osdBrightnessOpen
sourceComponent: PanelWindow {
id: osdRoot
color: "transparent"
Connections {
target: root
function onFocusedScreenChanged() {
osdRoot.screen = root.focusedScreen
}
}
WlrLayershell.namespace: "quickshell:onScreenDisplay"
WlrLayershell.layer: WlrLayer.Overlay
anchors {
top: !Config.options.bar.bottom
bottom: Config.options.bar.bottom
}
mask: Region {
item: osdValuesWrapper
}
exclusionMode: ExclusionMode.Ignore
exclusiveZone: 0
margins {
top: Appearance.sizes.barHeight
bottom: Appearance.sizes.barHeight
}
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight
visible: osdLoader.active
ColumnLayout {
id: columnLayout
anchors.horizontalCenter: parent.horizontalCenter
Item {
id: osdValuesWrapper
// Extra space for shadow
implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2
implicitWidth: osdValues.implicitWidth
clip: true
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: GlobalStates.osdBrightnessOpen = false
}
Behavior on implicitHeight {
NumberAnimation {
duration: Appearance.animation.menuDecel.duration
easing.type: Appearance.animation.menuDecel.type
}
}
OsdValueIndicator {
id: osdValues
anchors.fill: parent
anchors.margins: Appearance.sizes.elevationMargin
value: root.brightnessMonitor?.brightness ?? 50
icon: "light_mode"
rotateIcon: true
scaleIcon: true
name: Translation.tr("Brightness")
}
}
}
}
}
IpcHandler {
target: "osdBrightness"
function trigger() {
root.triggerOsd()
}
function hide() {
GlobalStates.osdBrightnessOpen = false
}
function toggle() {
GlobalStates.osdBrightnessOpen = !GlobalStates.osdBrightnessOpen
}
}
GlobalShortcut {
name: "osdBrightnessTrigger"
description: "Triggers brightness OSD on press"
onPressed: {
root.triggerOsd()
}
}
GlobalShortcut {
name: "osdBrightnessHide"
description: "Hides brightness OSD on press"
onPressed: {
GlobalStates.osdBrightnessOpen = false
}
}
}
@@ -1,13 +1,8 @@
import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
// import Qt5Compat.GraphicalEffects
Item { Item {
id: root id: root
@@ -21,19 +16,23 @@ Item {
property real valueIndicatorLeftPadding: 10 property real valueIndicatorLeftPadding: 10
property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding
Layout.margins: Appearance.sizes.elevationMargin implicitWidth: Appearance.sizes.osdWidth + 2 * Appearance.sizes.elevationMargin
implicitWidth: Appearance.sizes.osdWidth implicitHeight: valueIndicator.implicitHeight + 2 * Appearance.sizes.elevationMargin
implicitHeight: valueIndicator.implicitHeight
StyledRectangularShadow { StyledRectangularShadow {
target: valueIndicator target: valueIndicator
} }
WrapperRectangle { Rectangle {
id: valueIndicator id: valueIndicator
anchors.fill: parent anchors {
fill: parent
margins: Appearance.sizes.elevationMargin
}
radius: Appearance.rounding.full radius: Appearance.rounding.full
color: Appearance.colors.colLayer0 color: Appearance.colors.colLayer0
implicitWidth: valueRow.implicitWidth implicitWidth: valueRow.implicitWidth
implicitHeight: valueRow.implicitHeight
RowLayout { // Icon on the left, stuff on the right RowLayout { // Icon on the left, stuff on the right
id: valueRow id: valueRow
@@ -48,6 +47,7 @@ Item {
Layout.leftMargin: valueIndicatorLeftPadding Layout.leftMargin: valueIndicatorLeftPadding
Layout.topMargin: valueIndicatorVerticalPadding Layout.topMargin: valueIndicatorVerticalPadding
Layout.bottomMargin: valueIndicatorVerticalPadding Layout.bottomMargin: valueIndicatorVerticalPadding
MaterialSymbol { // Icon MaterialSymbol { // Icon
anchors { anchors {
centerIn: parent centerIn: parent
@@ -0,0 +1,18 @@
import qs
import qs.services
import QtQuick
import Quickshell
import Quickshell.Hyprland
import "../"
OsdValueIndicator {
id: root
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
value: root.brightnessMonitor?.brightness ?? 50
icon: "light_mode"
rotateIcon: true
scaleIcon: true
name: Translation.tr("Brightness")
}
@@ -0,0 +1,11 @@
import qs
import qs.services
import QtQuick
import "../"
OsdValueIndicator {
id: osdValues
value: Audio.sink?.audio.volume ?? 0
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
name: Translation.tr("Volume")
}
@@ -63,7 +63,7 @@ Item {
border.width: 1 border.width: 1
border.color: Appearance.colors.colLayer0Border border.color: Appearance.colors.colLayer0Border
ColumnLayout { // Workspaces Column { // Workspaces
id: workspaceColumnLayout id: workspaceColumnLayout
z: root.workspaceZ z: root.workspaceZ
@@ -71,7 +71,7 @@ Item {
spacing: workspaceSpacing spacing: workspaceSpacing
Repeater { Repeater {
model: Config.options.overview.rows model: Config.options.overview.rows
delegate: RowLayout { delegate: Row {
id: row id: row
property int rowIndex: index property int rowIndex: index
spacing: workspaceSpacing spacing: workspaceSpacing
@@ -148,27 +148,27 @@ Item {
model: ScriptModel { model: ScriptModel {
values: { values: {
// console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2)) // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2))
return ToplevelManager.toplevels.values.filter((toplevel) => { return [...ToplevelManager.toplevels.values.filter((toplevel) => {
const address = `0x${toplevel.HyprlandToplevel?.address}` const address = `0x${toplevel.HyprlandToplevel?.address}`
var win = windowByAddress[address] var win = windowByAddress[address]
const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
return inWorkspaceGroup; return inWorkspaceGroup;
}) })].reverse()
} }
} }
delegate: OverviewWindow { delegate: OverviewWindow {
id: window id: window
required property var modelData required property var modelData
property int monitorId: windowData?.monitor property int monitorId: windowData?.monitor
property var monitor: HyprlandData.monitors[monitorId] property var monitor: HyprlandData.monitors.find(m => m.id == monitorId)
property var address: `0x${modelData.HyprlandToplevel.address}` property var address: `0x${modelData.HyprlandToplevel.address}`
windowData: windowByAddress[address]
toplevel: modelData toplevel: modelData
monitorData: HyprlandData.monitors[monitorId] monitorData: this.monitor
scale: root.scale scale: root.scale
availableWorkspaceWidth: root.workspaceImplicitWidth availableWorkspaceWidth: root.workspaceImplicitWidth
availableWorkspaceHeight: root.workspaceImplicitHeight availableWorkspaceHeight: root.workspaceImplicitHeight
widgetMonitorId: root.monitor.id widgetMonitorId: root.monitor.id
windowData: windowByAddress[address]
property bool atInitPosition: (initX == x && initY == y) property bool atInitPosition: (initX == x && initY == y)
@@ -188,7 +188,7 @@ Item {
} }
} }
z: atInitPosition ? root.windowZ : root.windowDraggingZ z: atInitPosition ? (root.windowZ + windowData?.floating) : root.windowDraggingZ
Drag.hotSpot.x: targetWindowWidth / 2 Drag.hotSpot.x: targetWindowWidth / 2
Drag.hotSpot.y: targetWindowHeight / 2 Drag.hotSpot.y: targetWindowHeight / 2
MouseArea { MouseArea {
@@ -218,8 +218,13 @@ Item {
updateWindowPosition.restart() updateWindowPosition.restart()
} }
else { else {
window.x = window.initX if (!window.windowData.floating) {
window.y = window.initY updateWindowPosition.restart()
return
}
const percentageX = Math.round((window.x - xOffset) / root.workspaceImplicitWidth * 100)
const percentageY = Math.round((window.y - yOffset) / root.workspaceImplicitHeight * 100)
Hyprland.dispatch(`movewindowpixel exact ${percentageX}% ${percentageY}%, address:${window.windowData?.address}`)
} }
} }
onClicked: (event) => { onClicked: (event) => {
@@ -81,35 +81,29 @@ Item { // Window
border.width : 1 border.width : 1
} }
ColumnLayout { Image {
anchors.verticalCenter: parent.verticalCenter id: windowIcon
anchors.left: parent.left anchors.centerIn: parent
anchors.right: parent.right property var iconSize: {
spacing: Appearance.font.pixelSize.smaller * 0.5 // console.log("-=-=-", root.toplevel.title, "-=-=-")
// console.log("Target window size:", targetWindowWidth, targetWindowHeight)
// console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
// console.log("Scale:", root.monitorData.scale)
// console.log("Final:", Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale)
return Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale;
}
// mipmap: true
Layout.alignment: Qt.AlignHCenter
source: root.iconPath
width: iconSize
height: iconSize
sourceSize: Qt.size(iconSize, iconSize)
Image { Behavior on width {
id: windowIcon animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
property var iconSize: { }
// console.log("-=-=-", root.toplevel.title, "-=-=-") Behavior on height {
// console.log("Target window size:", targetWindowWidth, targetWindowHeight) animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
// console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
// console.log("Scale:", root.monitorData.scale)
// console.log("Final:", Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale)
return Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale;
}
// mipmap: true
Layout.alignment: Qt.AlignHCenter
source: root.iconPath
width: iconSize
height: iconSize
sourceSize: Qt.size(iconSize, iconSize)
Behavior on width {
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
}
Behavior on height {
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
}
} }
} }
} }
@@ -42,7 +42,7 @@ Item { // Wrapper
{ {
action: "konachanwallpaper", action: "konachanwallpaper",
execute: () => { execute: () => {
Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random_konachan_wall.sh")]); Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]);
} }
}, },
{ {
@@ -81,6 +81,12 @@ Item { // Wrapper
GlobalStates.wallpaperSelectorOpen = true; GlobalStates.wallpaperSelectorOpen = true;
} }
}, },
{
action: "wipeclipboard",
execute: () => {
Cliphist.wipe();
}
},
] ]
function focusFirstItem() { function focusFirstItem() {
@@ -167,6 +167,14 @@ ContentPage {
icon: "lock" icon: "lock"
title: Translation.tr("Lock screen") title: Translation.tr("Lock screen")
ConfigSwitch {
text: Translation.tr('Launch on startup')
checked: Config.options.lock.launchOnStartup
onCheckedChanged: {
Config.options.lock.launchOnStartup = checked;
}
}
ContentSubsection { ContentSubsection {
title: Translation.tr("Blurred style") title: Translation.tr("Blurred style")
@@ -14,12 +14,13 @@ ContentPage {
forceWidth: true forceWidth: true
Process { Process {
id: konachanWallProc id: randomWallProc
property string status: "" property string status: ""
command: ["bash", "-c", FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/random_konachan_wall.sh`)] property string scriptPath: `${Directories.scriptPath}/colors/random/random_konachan_wall.sh`
command: ["bash", "-c", FileUtils.trimFileProtocol(randomWallProc.scriptPath)]
stdout: SplitParser { stdout: SplitParser {
onRead: data => { onRead: data => {
konachanWallProc.status = data.trim(); randomWallProc.status = data.trim();
} }
} }
} }
@@ -90,19 +91,35 @@ ContentPage {
ColumnLayout { ColumnLayout {
RippleButtonWithIcon { RippleButtonWithIcon {
id: rndWallBtn enabled: !randomWallProc.running
visible: Config.options.policies.weeb === 1 visible: Config.options.policies.weeb === 1
Layout.fillWidth: true Layout.fillWidth: true
buttonRadius: Appearance.rounding.small buttonRadius: Appearance.rounding.small
materialIcon: "ifl" materialIcon: "ifl"
mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") mainText: randomWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan")
onClicked: { onClicked: {
konachanWallProc.running = true; randomWallProc.scriptPath = `${Directories.scriptPath}/colors/random/random_konachan_wall.sh`;
randomWallProc.running = true;
} }
StyledToolTip { StyledToolTip {
text: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") text: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers")
} }
} }
RippleButtonWithIcon {
enabled: !randomWallProc.running
visible: Config.options.policies.weeb === 1
Layout.fillWidth: true
buttonRadius: Appearance.rounding.small
materialIcon: "ifl"
mainText: randomWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: osu! seasonal")
onClicked: {
randomWallProc.scriptPath = `${Directories.scriptPath}/colors/random/random_osu_wall.sh`;
randomWallProc.running = true;
}
StyledToolTip {
text: Translation.tr("Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers")
}
}
RippleButtonWithIcon { RippleButtonWithIcon {
Layout.fillWidth: true Layout.fillWidth: true
materialIcon: "wallpaper" materialIcon: "wallpaper"
@@ -154,17 +171,6 @@ ContentPage {
dark: true dark: true
} }
} }
ConfigSwitch {
text: Translation.tr("Transparency")
checked: Config.options.appearance.transparency.enable
onCheckedChanged: {
Config.options.appearance.transparency.enable = checked;
}
StyledToolTip {
text: Translation.tr("Might look ass. Unsupported.")
}
}
} }
} }
@@ -213,6 +219,17 @@ ContentPage {
} }
] ]
} }
ConfigSwitch {
text: Translation.tr("Transparency")
checked: Config.options.appearance.transparency.enable
onCheckedChanged: {
Config.options.appearance.transparency.enable = checked;
}
StyledToolTip {
text: Translation.tr("Might look ass. Unsupported.")
}
}
} }
ContentSection { ContentSection {
@@ -36,6 +36,9 @@ Item {
event.accepted = true event.accepted = true
} }
} }
if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) {
Ai.clearMessages();
}
} }
property var allCommands: [ property var allCommands: [
@@ -332,14 +335,11 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4 mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
property int lastResponseLength: 0 property int lastResponseLength: 0
property bool shouldAutoScroll: true
onContentYChanged: shouldAutoScroll = atYEnd
onContentHeightChanged: { onContentHeightChanged: {
if (shouldAutoScroll) positionViewAtEnd(); if (atYEnd) positionViewAtEnd();
} }
onCountChanged: { // Auto-scroll when new messages are added onCountChanged: { // Auto-scroll when new messages are added
if (shouldAutoScroll) positionViewAtEnd(); if (atYEnd) positionViewAtEnd();
} }
add: null // Prevent function calls from being janky add: null // Prevent function calls from being janky
@@ -647,13 +647,24 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
root.handleInput(inputText) root.handleInput(inputText)
event.accepted = true event.accepted = true
} }
} else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) { // Intercept Ctrl+V to handle image pasting } else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) { // Intercept Ctrl+V to handle image/file pasting
if (event.modifiers & Qt.ShiftModifier) { // Let Shift+Ctrl+V = plain paste
messageInputField.text += Quickshell.clipboardText
event.accepted = true;
return;
}
// Try image paste first // Try image paste first
const currentClipboardEntry = Cliphist.entries[0] const currentClipboardEntry = Cliphist.entries[0]
const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry)
if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) { // First entry = currently copied entry = image? if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) { // First entry = currently copied entry = image?
decodeImageAndAttachProc.handleEntry(currentClipboardEntry) decodeImageAndAttachProc.handleEntry(currentClipboardEntry)
event.accepted = true; event.accepted = true;
return; return;
} else if (cleanCliphistEntry.startsWith("file://")) { // First entry = currently copied entry = image?
const fileName = decodeURIComponent(cleanCliphistEntry)
Ai.attachFile(fileName);
event.accepted = true;
return;
} }
event.accepted = false; // No image, let text pasting proceed event.accepted = false; // No image, let text pasting proceed
} else if (event.key === Qt.Key_Escape) { // Esc to detach file } else if (event.key === Qt.Key_Escape) { // Esc to detach file
@@ -127,9 +127,13 @@ Scope { // Scope
sourceComponent: FloatingWindow { sourceComponent: FloatingWindow {
id: detachedSidebarRoot id: detachedSidebarRoot
visible: GlobalStates.sidebarLeftOpen
property var contentParent: detachedSidebarBackground property var contentParent: detachedSidebarBackground
visible: GlobalStates.sidebarLeftOpen
onVisibleChanged: {
if (!visible) GlobalStates.sidebarLeftOpen = false;
}
Rectangle { Rectangle {
id: detachedSidebarBackground id: detachedSidebarBackground
anchors.fill: parent anchors.fill: parent
@@ -125,13 +125,12 @@ Rectangle {
sourceComponent: Item { sourceComponent: Item {
implicitHeight: root.imageHeight * root.scale implicitHeight: root.imageHeight * root.scale
implicitWidth: imagePreview.implicitWidth implicitWidth: imagePreview.implicitWidth
Image { StyledImage {
id: imagePreview id: imagePreview
anchors.fill: parent anchors.fill: parent
source: Qt.resolvedUrl(root.filePath) source: Qt.resolvedUrl(root.filePath)
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
antialiasing: true antialiasing: true
asynchronous: true
width: root.imageWidth * root.scale width: root.imageWidth * root.scale
height: root.imageHeight * root.scale height: root.imageHeight * root.scale
sourceSize.width: root.imageWidth * root.scale sourceSize.width: root.imageWidth * root.scale
@@ -58,7 +58,7 @@ Button {
contentItem: Item { contentItem: Item {
anchors.fill: parent anchors.fill: parent
Image { StyledImage {
id: imageObject id: imageObject
anchors.fill: parent anchors.fill: parent
width: root.rowHeight * modelData.aspect_ratio width: root.rowHeight * modelData.aspect_ratio
@@ -68,12 +68,6 @@ Button {
sourceSize.width: root.rowHeight * modelData.aspect_ratio sourceSize.width: root.rowHeight * modelData.aspect_ratio
sourceSize.height: root.rowHeight sourceSize.height: root.rowHeight
visible: opacity > 0
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
}
layer.enabled: true layer.enabled: true
layer.effect: OpacityMask { layer.effect: OpacityMask {
maskSource: Rectangle { maskSource: Rectangle {
@@ -56,7 +56,7 @@ Item {
StyledSlider { StyledSlider {
id: slider id: slider
value: root.node.audio.volume value: root.node.audio.volume
onValueChanged: root.node.audio.volume = value onMoved: root.node.audio.volume = value
} }
} }
} }
@@ -80,7 +80,7 @@ Item { // Bar content region
} }
} }
ColumnLayout { // Middle section Column { // Middle section
id: middleSection id: middleSection
anchors.centerIn: parent anchors.centerIn: parent
spacing: 4 spacing: 4
@@ -157,8 +157,6 @@ Item { // Bar content region
Layout.fillHeight: false Layout.fillHeight: false
} }
} }
} }
@@ -70,12 +70,15 @@ MouseArea {
hoverTarget: root hoverTarget: root
active: GlobalStates.mediaControlsOpen ? false : root.containsMouse active: GlobalStates.mediaControlsOpen ? false : root.containsMouse
ColumnLayout { Column {
anchors.centerIn: parent anchors.centerIn: parent
RowLayout { spacing: 4
spacing: 5
Row {
spacing: 4
MaterialSymbol { MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
fill: 0 fill: 0
font.weight: Font.Medium font.weight: Font.Medium
text: "music_note" text: "music_note"
@@ -84,6 +87,7 @@ MouseArea {
} }
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter
text: "Media" text: "Media"
font { font {
weight: Font.Medium weight: Font.Medium
+46 -22
View File
@@ -15,6 +15,7 @@ import qs.modules.common.functions
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Widgets import Quickshell.Widgets
@@ -35,7 +36,11 @@ ShellRoot {
property color imageFillColor: "#33f1d1ff" property color imageFillColor: "#33f1d1ff"
property color onBorderColor: "#ff000000" property color onBorderColor: "#ff000000"
property real standardRounding: 4 property real standardRounding: 4
readonly property var windows: HyprlandData.windowList readonly property var windows: [...HyprlandData.windowList].sort((a, b) => {
// Sort floating=true windows before others
if (a.floating === b.floating) return 0;
return a.floating ? -1 : 1;
})
readonly property var layers: HyprlandData.layers readonly property var layers: HyprlandData.layers
readonly property real falsePositivePreventionRatio: 0.5 readonly property real falsePositivePreventionRatio: 0.5
@@ -74,10 +79,10 @@ ShellRoot {
} }
implicitWidth: regionInfoRow.implicitWidth + horizontalPadding * 2 implicitWidth: regionInfoRow.implicitWidth + horizontalPadding * 2
implicitHeight: regionInfoRow.implicitHeight + verticalPadding * 2 implicitHeight: regionInfoRow.implicitHeight + verticalPadding * 2
RowLayout { Row {
id: regionInfoRow id: regionInfoRow
anchors.centerIn: parent anchors.centerIn: parent
spacing: 8 spacing: 4
Loader { Loader {
id: regionIconLoader id: regionIconLoader
@@ -133,7 +138,8 @@ ShellRoot {
}) })
readonly property list<var> layerRegions: { readonly property list<var> layerRegions: {
const layersOfThisMonitor = root.layers[panelWindow.hyprlandMonitor.name] const layersOfThisMonitor = root.layers[panelWindow.hyprlandMonitor.name]
const topLayers = layersOfThisMonitor.levels["2"] const topLayers = layersOfThisMonitor?.levels["2"]
if (!topLayers) return [];
const nonBarTopLayers = topLayers const nonBarTopLayers = topLayers
.filter(layer => !(layer.namespace.includes(":bar") || layer.namespace.includes(":verticalBar") || layer.namespace.includes(":dock"))) .filter(layer => !(layer.namespace.includes(":bar") || layer.namespace.includes(":verticalBar") || layer.namespace.includes(":dock")))
.map(layer => { .map(layer => {
@@ -386,14 +392,24 @@ ShellRoot {
// Overlay to darken screen // Overlay to darken screen
Rectangle { // Base Rectangle { // Base
id: overlayRect id: darkenOverlay
z: 0 z: 1
anchors.fill: parent anchors {
color: root.overlayColor left: parent.left
layer.enabled: true top: parent.top
leftMargin: panelWindow.regionX - darkenOverlay.border.width
topMargin: panelWindow.regionY - darkenOverlay.border.width
}
width: panelWindow.regionWidth + darkenOverlay.border.width * 2
height: panelWindow.regionHeight + darkenOverlay.border.width * 2
color: "transparent"
// border.color: root.selectionBorderColor
border.color: root.overlayColor
border.width: Math.max(panelWindow.width, panelWindow.height)
radius: root.standardRounding
} }
Rectangle { Rectangle {
// TODO: Make this mask the base instead of just overlaying a border id: selectionBorder
z: 1 z: 1
anchors { anchors {
left: parent.left left: parent.left
@@ -406,11 +422,21 @@ ShellRoot {
color: "transparent" color: "transparent"
border.color: root.selectionBorderColor border.color: root.selectionBorderColor
border.width: 2 border.width: 2
radius: root.standardRounding // radius: root.standardRounding
radius: 0 // TODO: figure out how to make the overlay thing work with rounding
}
StyledText {
anchors {
bottom: selectionBorder.bottom
right: selectionBorder.right
margins: 8
}
text: `${Math.round(panelWindow.regionWidth)} x ${Math.round(panelWindow.regionHeight)}`
} }
// Instructions // Instructions
Rectangle { Rectangle {
z: 9999
anchors { anchors {
top: parent.top top: parent.top
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
@@ -430,21 +456,19 @@ ShellRoot {
implicitWidth: instructionsRow.implicitWidth + 10 * 2 implicitWidth: instructionsRow.implicitWidth + 10 * 2
implicitHeight: instructionsRow.implicitHeight + 5 * 2 implicitHeight: instructionsRow.implicitHeight + 5 * 2
RowLayout { Row {
id: instructionsRow id: instructionsRow
anchors.centerIn: parent anchors.centerIn: parent
Item { spacing: 4
Layout.fillHeight: true MaterialSymbol {
implicitWidth: screenshotRegionIcon.implicitWidth id: screenshotRegionIcon
MaterialSymbol { // anchors.centerIn: parent
id: screenshotRegionIcon iconSize: Appearance.font.pixelSize.larger
anchors.centerIn: parent text: "screenshot_region"
iconSize: Appearance.font.pixelSize.larger color: root.genericContentForeground
text: "screenshot_region"
color: root.genericContentForeground
}
} }
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter
text: Translation.tr("Drag or click a region • LMB: Copy • RMB: Edit") text: Translation.tr("Drag or click a region • LMB: Copy • RMB: Edit")
color: root.genericContentForeground color: root.genericContentForeground
} }
@@ -32,11 +32,11 @@ page=$((1 + RANDOM % 1000));
response=$(curl "https://konachan.net/post.json?tags=rating%3Asafe&limit=1&page=$page") response=$(curl "https://konachan.net/post.json?tags=rating%3Asafe&limit=1&page=$page")
link=$(echo "$response" | jq '.[0].file_url' -r); link=$(echo "$response" | jq '.[0].file_url' -r);
ext=$(echo "$link" | awk -F. '{print $NF}') ext=$(echo "$link" | awk -F. '{print $NF}')
downloadPath="$PICTURES_DIR/Wallpapers/konachan_random_image.$ext" downloadPath="$PICTURES_DIR/Wallpapers/random_wallpaper.$ext"
illogicalImpulseConfigPath="$HOME/.config/illogical-impulse/config.json" illogicalImpulseConfigPath="$HOME/.config/illogical-impulse/config.json"
currentWallpaperPath=$(jq -r '.background.wallpaperPath' $illogicalImpulseConfigPath) currentWallpaperPath=$(jq -r '.background.wallpaperPath' $illogicalImpulseConfigPath)
if [ "$downloadPath" == "$currentWallpaperPath" ]; then if [ "$downloadPath" == "$currentWallpaperPath" ]; then
downloadPath="$PICTURES_DIR/Wallpapers/konachan_random_image-1.$ext" downloadPath="$PICTURES_DIR/Wallpapers/random_wallpaper-1.$ext"
fi fi
curl "$link" -o "$downloadPath" curl "$link" -o "$downloadPath"
"$SCRIPT_DIR/switchwall.sh" --image "$downloadPath" "$SCRIPT_DIR/../switchwall.sh" --image "$downloadPath"
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
get_pictures_dir() {
if command -v xdg-user-dir &> /dev/null; then
xdg-user-dir PICTURES
return
fi
local config_file="${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
if [ -f "$config_file" ]; then
local pictures_path
pictures_path=$(source "$config_file" >/dev/null 2>&1; echo "$XDG_PICTURES_DIR")
echo "${pictures_path/#\$HOME/$HOME}"
return
fi
echo "$HOME/Pictures"
}
QUICKSHELL_CONFIG_NAME="ii"
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
PICTURES_DIR=$(get_pictures_dir)
CONFIG_DIR="$XDG_CONFIG_HOME/quickshell/$QUICKSHELL_CONFIG_NAME"
CACHE_DIR="$XDG_CACHE_HOME/quickshell"
STATE_DIR="$XDG_STATE_HOME/quickshell"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
mkdir -p "$PICTURES_DIR/Wallpapers"
response=$(curl "https://osu.ppy.sh/api/v2/seasonal-backgrounds")
images=$(echo "$response" | jq '.backgrounds | length' -r);
randomIndex=$((RANDOM % images));
link=$(echo "$response" | jq ".backgrounds[$randomIndex].url" -r)
ext=$(echo "$link" | awk -F. '{print $NF}')
downloadPath="$PICTURES_DIR/Wallpapers/random_wallpaper.$ext"
illogicalImpulseConfigPath="$HOME/.config/illogical-impulse/config.json"
currentWallpaperPath=$(jq -r '.background.wallpaperPath' $illogicalImpulseConfigPath)
if [ "$downloadPath" == "$currentWallpaperPath" ]; then
downloadPath="$PICTURES_DIR/Wallpapers/random_wallpaper-1.$ext"
fi
curl "$link" -o "$downloadPath"
"$SCRIPT_DIR/../switchwall.sh" --image "$downloadPath"
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Based on https://unix.stackexchange.com/a/602935
# Skip if already unlocked
locked_state=$(busctl --user get-property org.freedesktop.secrets \
/org/freedesktop/secrets/collection/login \
org.freedesktop.Secret.Collection Locked)
if [[ "${locked_state}" == "b false" ]]; then
echo 'Keyring is already unlocked.' >&2
exit 1
fi
# Prompt for password if not provided
if [[ -z "${UNLOCK_PASSWORD}" ]]; then
echo -n 'Login password: ' >&2
read -s UNLOCK_PASSWORD || return
fi
# Unlock
killall -q -u "$(whoami)" gnome-keyring-daemon
eval $(echo -n "${UNLOCK_PASSWORD}" \
| gnome-keyring-daemon --daemonize --login \
| sed -e 's/^/export /')
unset UNLOCK_PASSWORD
echo '' >&2
@@ -74,8 +74,14 @@ Singleton {
id: monitor id: monitor
required property ShellScreen screen required property ShellScreen screen
readonly property bool isDdc: root.ddcMonitors.some(m => m.model === screen.model) readonly property bool isDdc: {
readonly property string busNum: root.ddcMonitors.find(m => m.model === screen.model)?.busNum ?? "" const match = root.ddcMonitors.find(m => m.model === screen.model && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum));
return !!match;
}
readonly property string busNum: {
const match = root.ddcMonitors.find(m => m.model === screen.model && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum));
return match?.busNum ?? "";
}
property int rawMaxBrightness: 100 property int rawMaxBrightness: 100
property real brightness property real brightness
property bool ready: false property bool ready: false
@@ -95,6 +95,18 @@ Singleton {
deleteProc.deleteEntry(entry); deleteProc.deleteEntry(entry);
} }
Process {
id: wipeProc
command: [root.cliphistBinary, "wipe"]
onExited: (exitCode, exitStatus) => {
root.refresh();
}
}
function wipe() {
wipeProc.running = true;
}
Connections { Connections {
target: Quickshell target: Quickshell
function onClipboardTextChanged() { function onClipboardTextChanged() {
+22 -8
View File
@@ -12,7 +12,6 @@ import Quickshell.Io
*/ */
Singleton { Singleton {
id: root id: root
property var manualActive
property string from: Config.options?.light?.night?.from ?? "19:00" property string from: Config.options?.light?.night?.from ?? "19:00"
property string to: Config.options?.light?.night?.to ?? "06:30" property string to: Config.options?.light?.night?.to ?? "06:30"
property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true) property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true)
@@ -29,6 +28,9 @@ Singleton {
property int clockHour: DateTime.clock.hours property int clockHour: DateTime.clock.hours
property int clockMinute: DateTime.clock.minutes property int clockMinute: DateTime.clock.minutes
property var manualActive
property int manualActiveHour
property int manualActiveMinute
onClockMinuteChanged: reEvaluate() onClockMinuteChanged: reEvaluate()
onAutomaticChanged: { onAutomaticChanged: {
@@ -36,17 +38,26 @@ Singleton {
root.firstEvaluation = true; root.firstEvaluation = true;
reEvaluate(); reEvaluate();
} }
function inBetween(t, from, to) {
if (from < to) {
return (t >= from && t <= to);
} else {
// Wrapped around midnight
return (t >= from || t <= to);
}
}
function reEvaluate() { function reEvaluate() {
const t = clockHour * 60 + clockMinute; const t = clockHour * 60 + clockMinute;
const from = fromHour * 60 + fromMinute; const from = fromHour * 60 + fromMinute;
const to = toHour * 60 + toMinute; const to = toHour * 60 + toMinute;
const manualActive = manualActiveHour * 60 + manualActiveMinute;
if (from < to) { if (root.manualActive !== undefined && (inBetween(from, manualActive, t) || inBetween(to, manualActive, t))) {
root.shouldBeOn = t >= from && t <= to; root.manualActive = undefined;
} else {
// Wrapped around midnight
root.shouldBeOn = t >= from || t <= to;
} }
root.shouldBeOn = inBetween(t, from, to);
if (firstEvaluation) { if (firstEvaluation) {
firstEvaluation = false; firstEvaluation = false;
root.ensureState(); root.ensureState();
@@ -94,15 +105,18 @@ Singleton {
if (output.length == 0 || output.startsWith("Couldn't")) if (output.length == 0 || output.startsWith("Couldn't"))
root.active = false; root.active = false;
else else
root.active = (output != "6500"); root.active = (output != "6500"); // 6500 is the default when off
// console.log("[Hyprsunset] Fetched state:", output, "->", root.active); // console.log("[Hyprsunset] Fetched state:", output, "->", root.active);
} }
} }
} }
function toggle() { function toggle() {
if (root.manualActive === undefined) if (root.manualActive === undefined) {
root.manualActive = root.active; root.manualActive = root.active;
root.manualActiveHour = root.clockHour;
root.manualActiveMinute = root.clockMinute;
}
root.manualActive = !root.manualActive; root.manualActive = !root.manualActive;
if (root.manualActive) { if (root.manualActive) {
+14 -2
View File
@@ -12,10 +12,22 @@ Singleton {
id: root id: root
property alias inhibit: idleInhibitor.enabled property alias inhibit: idleInhibitor.enabled
inhibit: Persistent.states.idle.inhibit inhibit: false
Connections {
target: Persistent
function onReadyChanged() {
if (!Persistent.isNewHyprlandInstance) {
root.inhibit = Persistent.states.idle.inhibit
} else {
Persistent.states.idle.inhibit = root.inhibit
}
}
}
function toggleInhibit() { function toggleInhibit() {
Persistent.states.idle.inhibit = !Persistent.states.idle.inhibit root.inhibit = !root.inhibit
Persistent.states.idle.inhibit = root.inhibit
} }
IdleInhibitor { IdleInhibitor {
+2 -4
View File
@@ -224,10 +224,8 @@ ApplicationWindow {
Connections { Connections {
target: root target: root
function onCurrentPageChanged() { function onCurrentPageChanged() {
if (pageLoader.sourceComponent !== root.pages[root.currentPage].component) { switchAnim.complete();
switchAnim.complete(); switchAnim.start();
switchAnim.start();
}
} }
} }
+2 -4
View File
@@ -42,8 +42,7 @@ ShellRoot {
property bool enableLock: true property bool enableLock: true
property bool enableMediaControls: true property bool enableMediaControls: true
property bool enableNotificationPopup: true property bool enableNotificationPopup: true
property bool enableOnScreenDisplayBrightness: true property bool enableOnScreenDisplay: true
property bool enableOnScreenDisplayVolume: true
property bool enableOnScreenKeyboard: true property bool enableOnScreenKeyboard: true
property bool enableOverview: true property bool enableOverview: true
property bool enableReloadPopup: true property bool enableReloadPopup: true
@@ -72,8 +71,7 @@ ShellRoot {
LazyLoader { active: enableLock; component: Lock {} } LazyLoader { active: enableLock; component: Lock {} }
LazyLoader { active: enableMediaControls; component: MediaControls {} } LazyLoader { active: enableMediaControls; component: MediaControls {} }
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} } LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
LazyLoader { active: enableOnScreenDisplayBrightness; component: OnScreenDisplayBrightness {} } LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
LazyLoader { active: enableOnScreenDisplayVolume; component: OnScreenDisplayVolume {} }
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} } LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
LazyLoader { active: enableOverview; component: Overview {} } LazyLoader { active: enableOverview; component: Overview {} }
LazyLoader { active: enableReloadPopup; component: ReloadPopup {} } LazyLoader { active: enableReloadPopup; component: ReloadPopup {} }
+1 -1
View File
@@ -44,7 +44,7 @@ ApplicationWindow {
Process { Process {
id: konachanWallProc id: konachanWallProc
property string status: "" property string status: ""
command: ["bash", "-c", Quickshell.shellPath("scripts/colors/random_konachan_wall.sh")] command: ["bash", "-c", Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]
stdout: SplitParser { stdout: SplitParser {
onRead: data => { onRead: data => {
console.log(`Konachan wall proc output: ${data}`); console.log(`Konachan wall proc output: ${data}`);