forked from Shinonome/dots-hyprland
Merge branch 'end-4:main' into main
This commit is contained in:
@@ -27,6 +27,23 @@ Singleton {
|
||||
property bool superReleaseMightTrigger: true
|
||||
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
|
||||
onScreenZoomChanged: {
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]);
|
||||
|
||||
@@ -175,16 +175,10 @@ Variants {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
Image {
|
||||
StyledImage {
|
||||
id: wallpaper
|
||||
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
|
||||
asynchronous: true
|
||||
retainWhileLoading: true
|
||||
smooth: false
|
||||
// Range = groups that workspaces span on
|
||||
property int chunkSize: Config?.options.bar.workspaces.shown ?? 10
|
||||
@@ -300,9 +294,7 @@ Variants {
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: 8
|
||||
|
||||
sourceComponent: Column {
|
||||
Loader {
|
||||
id: digitalClockLoader
|
||||
visible: root.clockStyle === "digital"
|
||||
@@ -340,7 +332,6 @@ Variants {
|
||||
|
||||
Loader {
|
||||
id: cookieClockLoader
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: root.clockStyle === "cookie"
|
||||
active: visible
|
||||
sourceComponent: CookieClock {}
|
||||
@@ -432,7 +423,7 @@ Variants {
|
||||
styleColor: Appearance.colors.colShadow
|
||||
animateChange: true
|
||||
}
|
||||
component ClockStatusText: RowLayout {
|
||||
component ClockStatusText: Row {
|
||||
id: statusTextRow
|
||||
property alias statusIcon: statusIconWidget.text
|
||||
property alias statusText: statusTextWidget.text
|
||||
@@ -443,10 +434,10 @@ Variants {
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Layout.fillWidth: false
|
||||
spacing: 4
|
||||
MaterialSymbol {
|
||||
id: statusIconWidget
|
||||
Layout.fillWidth: false
|
||||
anchors.verticalCenter: statusTextRow.verticalCenter
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: statusTextRow.textColor
|
||||
style: Text.Raised
|
||||
@@ -454,8 +445,8 @@ Variants {
|
||||
}
|
||||
ClockText {
|
||||
id: statusTextWidget
|
||||
Layout.fillWidth: false
|
||||
color: statusTextRow.textColor
|
||||
anchors.verticalCenter: statusTextRow.verticalCenter
|
||||
font {
|
||||
family: Appearance.font.family.main
|
||||
pixelSize: Appearance.font.pixelSize.large
|
||||
|
||||
@@ -97,7 +97,7 @@ Item { // Bar content region
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout { // Middle section
|
||||
Row { // Middle section
|
||||
id: middleSection
|
||||
anchors {
|
||||
top: parent.top
|
||||
@@ -108,8 +108,8 @@ Item { // Bar content region
|
||||
|
||||
BarGroup {
|
||||
id: leftCenterGroup
|
||||
Layout.preferredWidth: root.centerSideModuleWidth
|
||||
Layout.fillHeight: false
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitWidth: root.centerSideModuleWidth
|
||||
|
||||
Resources {
|
||||
alwaysShowAllResources: root.useShortenedForm === 2
|
||||
@@ -128,6 +128,7 @@ Item { // Bar content region
|
||||
|
||||
BarGroup {
|
||||
id: middleCenterGroup
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
padding: workspacesWidget.widgetPadding
|
||||
|
||||
Workspaces {
|
||||
@@ -153,9 +154,9 @@ Item { // Bar content region
|
||||
|
||||
MouseArea {
|
||||
id: rightCenterGroup
|
||||
implicitWidth: rightCenterGroupContent.implicitWidth
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitWidth: root.centerSideModuleWidth
|
||||
implicitHeight: rightCenterGroupContent.implicitHeight
|
||||
Layout.preferredWidth: root.centerSideModuleWidth
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
||||
|
||||
@@ -14,11 +14,12 @@ StyledPopup {
|
||||
spacing: 4
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
Row {
|
||||
id: header
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: "battery_android_full"
|
||||
@@ -27,6 +28,7 @@ StyledPopup {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Battery"
|
||||
font {
|
||||
weight: Font.Medium
|
||||
|
||||
@@ -39,10 +39,11 @@ StyledPopup {
|
||||
spacing: 4
|
||||
|
||||
// Date + Time row
|
||||
RowLayout {
|
||||
Row {
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: "calendar_month"
|
||||
@@ -50,6 +51,7 @@ StyledPopup {
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
text: `${root.formattedDate}`
|
||||
@@ -79,26 +81,26 @@ StyledPopup {
|
||||
}
|
||||
|
||||
// Tasks
|
||||
ColumnLayout {
|
||||
Column {
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
Row {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "checklist"
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
font.pixelSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Translation.tr("To Do:")
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
|
||||
@@ -6,10 +6,16 @@ import qs.modules.common.widgets
|
||||
Loader {
|
||||
id: root
|
||||
property bool vertical: false
|
||||
|
||||
active: HyprlandXkb.layoutCodes.length > 1
|
||||
visible: active
|
||||
|
||||
function abbreviateLayoutCode(fullCode) {
|
||||
return fullCode.split(':').map(layout => {
|
||||
const baseLayout = layout.split('-')[0];
|
||||
return baseLayout.slice(0, 4);
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitWidth: root.vertical ? null : layoutCodeText.implicitWidth
|
||||
implicitHeight: root.vertical ? layoutCodeText.implicitHeight : null
|
||||
@@ -18,7 +24,7 @@ Loader {
|
||||
id: layoutCodeText
|
||||
anchors.centerIn: parent
|
||||
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
|
||||
color: rightSidebarButton.colText
|
||||
animateChange: true
|
||||
|
||||
@@ -20,7 +20,6 @@ StyledPopup {
|
||||
required property string label
|
||||
required property string value
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
MaterialSymbol {
|
||||
text: resourceItem.icon
|
||||
@@ -40,13 +39,14 @@ StyledPopup {
|
||||
}
|
||||
}
|
||||
|
||||
component ResourceHeaderItem: RowLayout {
|
||||
component ResourceHeaderItem: Row {
|
||||
id: headerItem
|
||||
required property var icon
|
||||
required property var label
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: headerItem.icon
|
||||
@@ -55,6 +55,7 @@ StyledPopup {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: headerItem.label
|
||||
font {
|
||||
weight: Font.Medium
|
||||
@@ -64,19 +65,20 @@ StyledPopup {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 12
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Column {
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
ResourceHeaderItem {
|
||||
icon: "memory"
|
||||
label: "RAM"
|
||||
}
|
||||
ColumnLayout {
|
||||
Column {
|
||||
spacing: 4
|
||||
ResourceItem {
|
||||
icon: "clock_loader_60"
|
||||
label: Translation.tr("Used:")
|
||||
@@ -95,16 +97,17 @@ StyledPopup {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
visible: ResourceUsage.swapTotal > 0
|
||||
Layout.alignment: Qt.AlignTop
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
ResourceHeaderItem {
|
||||
icon: "swap_horiz"
|
||||
label: "Swap"
|
||||
}
|
||||
ColumnLayout {
|
||||
Column {
|
||||
spacing: 4
|
||||
ResourceItem {
|
||||
icon: "clock_loader_60"
|
||||
label: Translation.tr("Used:")
|
||||
@@ -123,15 +126,16 @@ StyledPopup {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Column {
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
ResourceHeaderItem {
|
||||
icon: "planner_review"
|
||||
label: "CPU"
|
||||
}
|
||||
ColumnLayout {
|
||||
Column {
|
||||
spacing: 4
|
||||
ResourceItem {
|
||||
icon: "bolt"
|
||||
label: Translation.tr("Load:")
|
||||
|
||||
@@ -2,7 +2,6 @@ import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Revealer { // Scroll hint
|
||||
id: root
|
||||
@@ -11,10 +10,11 @@ Revealer { // Scroll hint
|
||||
property string tooltipText: ""
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.right: root.side === "left" ? parent.right : undefined
|
||||
anchors.left: root.side === "right" ? parent.left : undefined
|
||||
implicitWidth: contentColumnLayout.implicitWidth
|
||||
implicitHeight: contentColumnLayout.implicitHeight
|
||||
implicitWidth: contentColumn.implicitWidth
|
||||
implicitHeight: contentColumn.implicitHeight
|
||||
property bool hovered: false
|
||||
|
||||
hoverEnabled: true
|
||||
@@ -22,32 +22,36 @@ Revealer { // Scroll hint
|
||||
onExited: hovered = false
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
// StyledToolTip {
|
||||
// extraVisibleCondition: tooltipText.length > 0
|
||||
// text: tooltipText
|
||||
// }
|
||||
property bool showHintTimedOut: false
|
||||
onHoveredChanged: showHintTimedOut = false
|
||||
Timer {
|
||||
running: mouseArea.hovered
|
||||
interval: 500
|
||||
onTriggered: mouseArea.showHintTimedOut = true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumnLayout
|
||||
anchors.centerIn: parent
|
||||
PopupToolTip {
|
||||
extraVisibleCondition: (tooltipText.length > 0 && mouseArea.showHintTimedOut)
|
||||
text: tooltipText
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
anchors {
|
||||
fill: parent
|
||||
}
|
||||
spacing: -5
|
||||
MaterialSymbol {
|
||||
Layout.leftMargin: 5
|
||||
Layout.rightMargin: 5
|
||||
text: "keyboard_arrow_up"
|
||||
iconSize: 14
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
MaterialSymbol {
|
||||
Layout.leftMargin: 5
|
||||
Layout.rightMargin: 5
|
||||
text: root.icon
|
||||
iconSize: 14
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
MaterialSymbol {
|
||||
Layout.leftMargin: 5
|
||||
Layout.rightMargin: 5
|
||||
text: "keyboard_arrow_down"
|
||||
iconSize: 14
|
||||
color: Appearance.colors.colSubtext
|
||||
|
||||
@@ -24,8 +24,8 @@ LazyLoader {
|
||||
anchors.top: Config.options.bar.vertical || (!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
|
||||
implicitHeight: popupBackground.implicitHeight + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin
|
||||
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 + root.popupBackgroundMargin
|
||||
implicitHeight: popupBackground.implicitHeight + Appearance.sizes.elevationMargin * 2 + root.popupBackgroundMargin
|
||||
|
||||
mask: Region {
|
||||
item: popupBackground
|
||||
@@ -63,10 +63,10 @@ LazyLoader {
|
||||
readonly property real margin: 10
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.left)
|
||||
rightMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.right)
|
||||
topMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.top)
|
||||
bottomMargin: Appearance.sizes.hyprlandGapsOut + root.popupBackgroundMargin * (!popupWindow.anchors.bottom)
|
||||
leftMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.left)
|
||||
rightMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.right)
|
||||
topMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.top)
|
||||
bottomMargin: Appearance.sizes.elevationMargin + root.popupBackgroundMargin * (!popupWindow.anchors.bottom)
|
||||
}
|
||||
implicitWidth: root.contentItem.implicitWidth + margin * 2
|
||||
implicitHeight: root.contentItem.implicitHeight + margin * 2
|
||||
|
||||
@@ -5,7 +5,6 @@ import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
@@ -19,7 +18,8 @@ Item {
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||
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 int widgetPadding: 4
|
||||
property int workspaceButtonWidth: 26
|
||||
@@ -28,7 +28,7 @@ Item {
|
||||
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||
property real workspaceIconOpacityShrinked: 1
|
||||
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
|
||||
Timer {
|
||||
@@ -56,8 +56,8 @@ Item {
|
||||
|
||||
// Function to update workspaceOccupied
|
||||
function updateWorkspaceOccupied() {
|
||||
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => {
|
||||
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1);
|
||||
workspaceOccupied = Array.from({ length: root.workspacesShown }, (_, i) => {
|
||||
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * root.workspacesShown + i + 1);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,8 +79,8 @@ Item {
|
||||
updateWorkspaceOccupied();
|
||||
}
|
||||
|
||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : backgroundLayout.implicitWidth
|
||||
implicitHeight: root.vertical ? backgroundLayout.implicitHeight : Appearance.sizes.barHeight
|
||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown)
|
||||
implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown) : Appearance.sizes.barHeight
|
||||
|
||||
// Scroll to switch workspaces
|
||||
WheelHandler {
|
||||
@@ -104,24 +104,20 @@ Item {
|
||||
}
|
||||
|
||||
// Workspaces - background
|
||||
GridLayout {
|
||||
id: backgroundLayout
|
||||
Grid {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
implicitHeight: root.vertical ? root.workspaceButtonWidth : Appearance.sizes.barHeight
|
||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : root.workspaceButtonWidth
|
||||
anchors.centerIn: parent
|
||||
|
||||
rowSpacing: 0
|
||||
columnSpacing: 0
|
||||
columns: root.vertical ? 1 : -1
|
||||
columns: root.vertical ? 1 : root.workspacesShown
|
||||
rows: root.vertical ? root.workspacesShown : 1
|
||||
|
||||
Repeater {
|
||||
model: Config.options.bar.workspaces.shown
|
||||
model: root.workspacesShown
|
||||
|
||||
Rectangle {
|
||||
z: 1
|
||||
Layout.alignment: root.vertical ? Qt.AlignHCenter : Qt.AlignVCenter
|
||||
|
||||
implicitWidth: workspaceButtonWidth
|
||||
implicitHeight: workspaceButtonWidth
|
||||
radius: (width / 2)
|
||||
@@ -195,26 +191,24 @@ Item {
|
||||
}
|
||||
|
||||
// Workspaces - numbers
|
||||
GridLayout {
|
||||
id: rowLayoutNumbers
|
||||
Grid {
|
||||
z: 3
|
||||
|
||||
columns: vertical ? 1 : -1
|
||||
columns: root.vertical ? 1 : root.workspacesShown
|
||||
rows: root.vertical ? root.workspacesShown : 1
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
anchors.fill: parent
|
||||
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
||||
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
|
||||
|
||||
Repeater {
|
||||
model: Config.options.bar.workspaces.shown
|
||||
model: root.workspacesShown
|
||||
|
||||
Button {
|
||||
id: button
|
||||
property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1
|
||||
Layout.fillHeight: !root.vertical
|
||||
Layout.fillWidth: root.vertical
|
||||
property int workspaceValue: workspaceGroup * root.workspacesShown + index + 1
|
||||
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
||||
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
|
||||
onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
|
||||
width: vertical ? undefined : workspaceButtonWidth
|
||||
height: vertical ? workspaceButtonWidth : undefined
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
@@ -11,8 +12,9 @@ Item {
|
||||
readonly property var keybinds: HyprlandKeybinds.keybinds
|
||||
property real spacing: 20
|
||||
property real titleSpacing: 7
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
property real padding: 4
|
||||
implicitWidth: row.implicitWidth + padding * 2
|
||||
implicitHeight: row.implicitHeight + padding * 2
|
||||
|
||||
property var keyBlacklist: ["Super_L"]
|
||||
property var keySubstitutions: ({
|
||||
@@ -28,43 +30,51 @@ Item {
|
||||
// "Shift": "",
|
||||
})
|
||||
|
||||
RowLayout { // Keybind columns
|
||||
id: rowLayout
|
||||
Row { // Keybind columns
|
||||
id: row
|
||||
spacing: root.spacing
|
||||
|
||||
Repeater {
|
||||
model: keybinds.children
|
||||
|
||||
delegate: ColumnLayout { // Keybind sections
|
||||
delegate: Column { // Keybind sections
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
Layout.alignment: Qt.AlignTop
|
||||
anchors.top: row.top
|
||||
|
||||
Repeater {
|
||||
model: modelData.children
|
||||
|
||||
delegate: Item { // Section with real keybinds
|
||||
id: keybindSection
|
||||
required property var modelData
|
||||
implicitWidth: sectionColumnLayout.implicitWidth
|
||||
implicitHeight: sectionColumnLayout.implicitHeight
|
||||
ColumnLayout {
|
||||
id: sectionColumnLayout
|
||||
implicitWidth: sectionColumn.implicitWidth
|
||||
implicitHeight: sectionColumn.implicitHeight
|
||||
|
||||
Column {
|
||||
id: sectionColumn
|
||||
anchors.centerIn: parent
|
||||
spacing: root.titleSpacing
|
||||
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
font.family: Appearance.font.family.title
|
||||
font.pixelSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.colors.colOnLayer0
|
||||
text: modelData.name
|
||||
text: keybindSection.modelData.name
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Grid {
|
||||
id: keybindGrid
|
||||
columns: 2
|
||||
columnSpacing: 4
|
||||
rowSpacing: 4
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
var result = [];
|
||||
for (var i = 0; i < modelData.keybinds.length; i++) {
|
||||
const keybind = modelData.keybinds[i];
|
||||
for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
|
||||
const keybind = keybindSection.modelData.keybinds[i];
|
||||
result.push({
|
||||
"type": "keys",
|
||||
"mods": keybind.mods,
|
||||
@@ -89,7 +99,7 @@ Item {
|
||||
|
||||
Component {
|
||||
id: keysComponent
|
||||
RowLayout {
|
||||
Row {
|
||||
spacing: 4
|
||||
Repeater {
|
||||
model: modelData.mods
|
||||
@@ -101,7 +111,6 @@ Item {
|
||||
StyledText {
|
||||
id: keybindPlus
|
||||
visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "+"
|
||||
}
|
||||
KeyboardKey {
|
||||
|
||||
@@ -15,14 +15,14 @@ Item {
|
||||
implicitWidth: mainLayout.implicitWidth
|
||||
implicitHeight: mainLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
id: mainLayout
|
||||
spacing: root.spacing
|
||||
|
||||
Repeater { // Main table rows
|
||||
model: root.elements
|
||||
|
||||
delegate: RowLayout { // Table cells
|
||||
delegate: Row { // Table cells
|
||||
id: tableRow
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
@@ -47,7 +47,7 @@ Item {
|
||||
Repeater { // Main table rows
|
||||
model: root.series
|
||||
|
||||
delegate: RowLayout { // Table cells
|
||||
delegate: Row { // Table cells
|
||||
id: seriesTableRow
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
|
||||
@@ -18,7 +19,7 @@ RippleButton {
|
||||
topMargin: 4
|
||||
leftMargin: 4
|
||||
}
|
||||
color: Appearance.colors.colLayer2
|
||||
color: ColorUtils.transparentize(Appearance.colors.colLayer2)
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: Math.max(20, elementNumber.implicitWidth)
|
||||
implicitHeight: Math.max(20, elementNumber.implicitHeight)
|
||||
@@ -26,13 +27,35 @@ RippleButton {
|
||||
|
||||
StyledText {
|
||||
id: elementNumber
|
||||
anchors.centerIn: parent
|
||||
anchors.left: parent.left
|
||||
color: Appearance.colors.colOnLayer2
|
||||
text: root.element.number
|
||||
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 {
|
||||
id: elementSymbol
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -356,7 +356,7 @@ Singleton {
|
||||
property real mediaControlsWidth: 440
|
||||
property real mediaControlsHeight: 160
|
||||
property real notificationPopupWidth: 410
|
||||
property real osdWidth: 200
|
||||
property real osdWidth: 180
|
||||
property real searchWidthCollapsed: 260
|
||||
property real searchWidth: 450
|
||||
property real sidebarWidth: 460
|
||||
|
||||
@@ -260,6 +260,7 @@ Singleton {
|
||||
}
|
||||
|
||||
property JsonObject lock: JsonObject {
|
||||
property bool launchOnStartup: false
|
||||
property JsonObject blur: JsonObject {
|
||||
property bool enable: false
|
||||
property real radius: 100
|
||||
@@ -267,6 +268,7 @@ Singleton {
|
||||
}
|
||||
property bool centerClock: true
|
||||
property bool showLockedText: true
|
||||
property bool unlockKeyring: true
|
||||
}
|
||||
|
||||
property JsonObject media: JsonObject {
|
||||
|
||||
@@ -11,6 +11,15 @@ Singleton {
|
||||
property string fileName: "states.json"
|
||||
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 {
|
||||
id: fileReloadTimer
|
||||
interval: 100
|
||||
@@ -36,6 +45,7 @@ Singleton {
|
||||
watchChanges: true
|
||||
onFileChanged: fileReloadTimer.restart()
|
||||
onAdapterUpdated: fileWriteTimer.restart()
|
||||
onLoaded: root.ready = true
|
||||
onLoadFailed: error => {
|
||||
console.log("Failed to load persistent states file:", error);
|
||||
if (error == FileViewError.FileNotFound) {
|
||||
@@ -45,6 +55,9 @@ Singleton {
|
||||
|
||||
adapter: JsonAdapter {
|
||||
id: persistentStatesJsonAdapter
|
||||
|
||||
property string hyprlandInstanceSignature: ""
|
||||
|
||||
property JsonObject ai: JsonObject {
|
||||
property string model
|
||||
property real temperature: 0.5
|
||||
|
||||
@@ -77,7 +77,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
StyledImage {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ RippleButton {
|
||||
Layout.bottomMargin: 5
|
||||
Layout.fillWidth: true
|
||||
value: 0.7
|
||||
sperm: true
|
||||
animateSperm: lightDarkButtonRoot.toggled
|
||||
wavy: true
|
||||
animateWave: lightDarkButtonRoot.toggled
|
||||
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
|
||||
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ Item { // Notification item area
|
||||
implicitHeight: summaryText.implicitHeight
|
||||
StyledText {
|
||||
id: summaryText
|
||||
Layout.fillWidth: summaryTextMetrics.width >= summaryRow.width * root.summaryElideRatio
|
||||
Layout.fillWidth: summaryTextMetrics.width >= summaryRow.implicitWidth * root.summaryElideRatio
|
||||
visible: !root.onlyNotification
|
||||
font.pixelSize: root.fontSize
|
||||
color: Appearance.colors.colOnLayer3
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import qs.services
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
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
|
||||
@@ -18,13 +15,13 @@ ProgressBar {
|
||||
property real valueBarGap: 4
|
||||
property color highlightColor: Appearance?.colors.colPrimary ?? "#685496"
|
||||
property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9"
|
||||
property bool sperm: false // If true, the progress bar will have a wavy fill effect
|
||||
property bool animateSperm: true
|
||||
property real spermAmplitudeMultiplier: sperm ? 0.5 : 0
|
||||
property real spermFrequency: 6
|
||||
property real spermFps: 60
|
||||
property bool wavy: false // 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
|
||||
|
||||
Behavior on spermAmplitudeMultiplier {
|
||||
Behavior on waveAmplitudeMultiplier {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
@@ -38,64 +35,62 @@ ProgressBar {
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
|
||||
Canvas {
|
||||
id: wavyFill
|
||||
Loader {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
height: parent.height * 6
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
var progress = root.visualPosition;
|
||||
var fillWidth = progress * width;
|
||||
var amplitude = parent.height * root.spermAmplitudeMultiplier;
|
||||
var frequency = root.spermFrequency;
|
||||
var phase = Date.now() / 400.0;
|
||||
var centerY = height / 2;
|
||||
|
||||
ctx.strokeStyle = root.highlightColor;
|
||||
ctx.lineWidth = parent.height;
|
||||
ctx.lineCap = "round";
|
||||
ctx.beginPath();
|
||||
for (var x = ctx.lineWidth / 2; x <= fillWidth; x += 1) {
|
||||
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / width + phase);
|
||||
if (x === 0)
|
||||
ctx.moveTo(x, waveY);
|
||||
else
|
||||
ctx.lineTo(x, waveY);
|
||||
active: root.wavy
|
||||
sourceComponent: WavyLine {
|
||||
id: wavyFill
|
||||
frequency: root.waveFrequency
|
||||
color: root.highlightColor
|
||||
amplitudeMultiplier: root.wavy ? 0.5 : 0
|
||||
height: contentItem.height * 6
|
||||
width: contentItem.width * root.visualPosition
|
||||
lineWidth: contentItem.height
|
||||
fullLength: root.width
|
||||
Connections {
|
||||
target: root
|
||||
function onValueChanged() { wavyFill.requestPaint(); }
|
||||
function onHighlightColorChanged() { wavyFill.requestPaint(); }
|
||||
}
|
||||
FrameAnimation {
|
||||
running: root.animateWave
|
||||
onTriggered: {
|
||||
wavyFill.requestPaint()
|
||||
}
|
||||
}
|
||||
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
|
||||
anchors.right: parent.right
|
||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
||||
height: parent.height
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
radius: height / 2
|
||||
color: root.trackColor
|
||||
}
|
||||
|
||||
Rectangle { // Stop point
|
||||
anchors.right: parent.right
|
||||
width: valueBarGap
|
||||
height: valueBarGap
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
radius: height / 2
|
||||
color: root.highlightColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import qs.modules.common
|
||||
RectangularShadow {
|
||||
required property var target
|
||||
anchors.fill: target
|
||||
radius: 20
|
||||
radius: target.radius
|
||||
blur: 0.9 * Appearance.sizes.elevationMargin
|
||||
offset: Qt.vector2d(0.0, 1.0)
|
||||
spread: 1
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
@@ -17,6 +18,7 @@ Slider {
|
||||
|
||||
property list<real> stopIndicatorValues: [1]
|
||||
enum Configuration {
|
||||
Wavy = 4,
|
||||
XS = 12,
|
||||
S = 18,
|
||||
M = 30,
|
||||
@@ -28,10 +30,9 @@ Slider {
|
||||
|
||||
property real handleDefaultWidth: 3
|
||||
property real handlePressedWidth: 1.5
|
||||
|
||||
property color highlightColor: Appearance.colors.colPrimary
|
||||
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 dotColorHighlighted: Appearance.m3colors.m3onPrimary
|
||||
property real unsharpenRadius: Appearance.rounding.unsharpen
|
||||
@@ -39,15 +40,18 @@ Slider {
|
||||
property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21
|
||||
: trackWidth >= StyledSlider.Configuration.L ? 12
|
||||
: trackWidth >= StyledSlider.Configuration.M ? 9
|
||||
: 6
|
||||
property real handleHeight: Math.max(33, trackWidth + 9)
|
||||
: trackWidth >= StyledSlider.Configuration.S ? 6
|
||||
: height / 2
|
||||
property real handleHeight: (configuration === StyledSlider.Configuration.Wavy) ? 24 : Math.max(33, trackWidth + 9)
|
||||
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
|
||||
property real handleMargins: 4
|
||||
onHandleMarginsChanged: {
|
||||
console.log("Handle margins changed to", handleMargins);
|
||||
}
|
||||
property real trackDotSize: 3
|
||||
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
|
||||
rightPadding: handleMargins
|
||||
@@ -93,18 +97,51 @@ Slider {
|
||||
implicitHeight: trackWidth
|
||||
|
||||
// Fill left
|
||||
Rectangle {
|
||||
Loader {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||
height: trackWidth
|
||||
color: root.highlightColor
|
||||
topLeftRadius: root.trackRadius
|
||||
bottomLeftRadius: root.trackRadius
|
||||
topRightRadius: root.unsharpenRadius
|
||||
bottomRightRadius: root.unsharpenRadius
|
||||
height: root.trackWidth
|
||||
active: !root.wavy
|
||||
sourceComponent: Rectangle {
|
||||
color: root.highlightColor
|
||||
topLeftRadius: root.trackRadius
|
||||
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
|
||||
|
||||
@@ -16,6 +16,7 @@ Item {
|
||||
default property alias data: toolbarLayout.data
|
||||
implicitWidth: background.implicitWidth
|
||||
implicitHeight: background.implicitHeight
|
||||
property alias radius: background.radius
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: background
|
||||
@@ -23,11 +24,11 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.centerIn: parent
|
||||
anchors.fill: parent
|
||||
color: Appearance.m3colors.m3surfaceContainer // Needs to be opaque
|
||||
implicitHeight: Math.max(toolbarLayout.implicitHeight + root.padding * 2, 56)
|
||||
implicitWidth: toolbarLayout.implicitWidth + root.padding * 2
|
||||
radius: Appearance.rounding.full
|
||||
radius: height / 2
|
||||
|
||||
RowLayout {
|
||||
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.modules.common
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pam
|
||||
@@ -55,6 +56,7 @@ Scope {
|
||||
onCompleted: result => {
|
||||
if (result == PamResult.Success) {
|
||||
root.unlocked();
|
||||
if (Config.options.lock.unlockKeyring) root.unlockKeyring();
|
||||
} else {
|
||||
root.showFailure = true;
|
||||
GlobalStates.screenUnlockFailed = true;
|
||||
@@ -64,4 +66,13 @@ Scope {
|
||||
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
|
||||
|
||||
// Username
|
||||
RowLayout {
|
||||
Row {
|
||||
spacing: 6
|
||||
Layout.leftMargin: 8
|
||||
Layout.fillHeight: true
|
||||
|
||||
MaterialSymbol {
|
||||
id: userIcon
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 1
|
||||
text: "account_circle"
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: SystemInfo.username
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
@@ -184,18 +184,19 @@ MouseArea {
|
||||
active: true
|
||||
visible: active
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
sourceComponent: Row {
|
||||
spacing: 8
|
||||
|
||||
MaterialSymbol {
|
||||
id: keyboardIcon
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 1
|
||||
text: "keyboard_alt"
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
Loader {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
sourceComponent: StyledText {
|
||||
text: HyprlandXkb.currentLayoutCode
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
@@ -229,18 +230,18 @@ MouseArea {
|
||||
scale: root.toolbarScale
|
||||
opacity: root.toolbarOpacity
|
||||
|
||||
RowLayout {
|
||||
Row {
|
||||
visible: UPower.displayDevice.isLaptopBattery
|
||||
spacing: 6
|
||||
spacing: 4
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
|
||||
MaterialSymbol {
|
||||
id: boltIcon
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: -2
|
||||
Layout.rightMargin: -2
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
fill: 1
|
||||
text: Battery.isCharging ? "bolt" : "battery_android_full"
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
@@ -248,7 +249,7 @@ MouseArea {
|
||||
color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Math.round(Battery.percentage * 100)
|
||||
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.widgets
|
||||
import qs.services
|
||||
@@ -153,8 +154,8 @@ Scope {
|
||||
required property MprisPlayer modelData
|
||||
player: modelData
|
||||
visualizerPoints: root.visualizerPoints
|
||||
implicitWidth: widgetWidth
|
||||
implicitHeight: widgetHeight
|
||||
implicitWidth: root.widgetWidth
|
||||
implicitHeight: root.widgetHeight
|
||||
radius: root.popupRounding
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models
|
||||
import qs.modules.common.widgets
|
||||
@@ -160,7 +161,7 @@ Item { // Player instance
|
||||
}
|
||||
}
|
||||
|
||||
Image { // Art image
|
||||
StyledImage { // Art image
|
||||
id: mediaArt
|
||||
property int size: parent.height
|
||||
anchors.fill: parent
|
||||
@@ -169,7 +170,6 @@ Item { // Player instance
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
cache: false
|
||||
antialiasing: true
|
||||
asynchronous: true
|
||||
|
||||
width: size
|
||||
height: size
|
||||
@@ -233,16 +233,41 @@ Item { // Player instance
|
||||
Item {
|
||||
id: progressBarContainer
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: progressBar.implicitHeight
|
||||
implicitHeight: Math.max(sliderLoader.implicitHeight, progressBarLoader.implicitHeight)
|
||||
|
||||
StyledProgressBar {
|
||||
id: progressBar
|
||||
Loader {
|
||||
id: sliderLoader
|
||||
anchors.fill: parent
|
||||
highlightColor: blendedColors.colPrimary
|
||||
trackColor: blendedColors.colSecondaryContainer
|
||||
value: playerController.player?.position / playerController.player?.length
|
||||
sperm: playerController.player?.isPlaying
|
||||
active: playerController.player?.canSeek ?? false
|
||||
sourceComponent: StyledSlider {
|
||||
configuration: StyledSlider.Configuration.Wavy
|
||||
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 {
|
||||
iconName: "skip_next"
|
||||
|
||||
+49
-33
@@ -15,9 +15,21 @@ Scope {
|
||||
property string protectionMessage: ""
|
||||
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() {
|
||||
GlobalStates.osdVolumeOpen = true
|
||||
osdTimeout.restart()
|
||||
GlobalStates.osdVolumeOpen = true;
|
||||
osdTimeout.restart();
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -26,35 +38,44 @@ Scope {
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
GlobalStates.osdVolumeOpen = false
|
||||
root.protectionMessage = ""
|
||||
GlobalStates.osdVolumeOpen = false;
|
||||
root.protectionMessage = "";
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Brightness
|
||||
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
|
||||
function onVolumeChanged() {
|
||||
if (!Audio.ready) return
|
||||
root.triggerOsd()
|
||||
if (!Audio.ready)
|
||||
return;
|
||||
root.currentIndicator = "volume";
|
||||
root.triggerOsd();
|
||||
}
|
||||
function onMutedChanged() {
|
||||
if (!Audio.ready) return
|
||||
root.triggerOsd()
|
||||
if (!Audio.ready)
|
||||
return;
|
||||
root.currentIndicator = "volume";
|
||||
root.triggerOsd();
|
||||
}
|
||||
}
|
||||
|
||||
Connections { // Listen to protection triggers
|
||||
Connections {
|
||||
// Listen to protection triggers
|
||||
target: Audio
|
||||
function onSinkProtectionTriggered(reason) {
|
||||
root.protectionMessage = reason;
|
||||
root.triggerOsd()
|
||||
root.currentIndicator = "volume";
|
||||
root.triggerOsd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +90,7 @@ Scope {
|
||||
Connections {
|
||||
target: root
|
||||
function onFocusedScreenChanged() {
|
||||
osdRoot.screen = root.focusedScreen
|
||||
osdRoot.screen = root.focusedScreen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +118,11 @@ Scope {
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Item {
|
||||
id: osdValuesWrapper
|
||||
// Extra space for shadow
|
||||
implicitHeight: contentColumnLayout.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: contentColumnLayout.implicitHeight
|
||||
implicitWidth: contentColumnLayout.implicitWidth
|
||||
clip: true
|
||||
|
||||
@@ -110,30 +132,25 @@ Scope {
|
||||
onEntered: GlobalStates.osdVolumeOpen = false
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
id: contentColumnLayout
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: Appearance.sizes.elevationMargin
|
||||
rightMargin: Appearance.sizes.elevationMargin
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
OsdValueIndicator {
|
||||
id: osdValues
|
||||
Layout.fillWidth: true
|
||||
value: Audio.sink?.audio.volume ?? 0
|
||||
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
|
||||
name: Translation.tr("Volume")
|
||||
Loader {
|
||||
id: osdIndicatorLoader
|
||||
source: root.indicators.find(i => i.id === root.currentIndicator)?.sourceUrl
|
||||
}
|
||||
|
||||
Item {
|
||||
id: protectionMessageWrapper
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitHeight: protectionMessageBackground.implicitHeight
|
||||
implicitWidth: protectionMessageBackground.implicitWidth
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
opacity: root.protectionMessage !== "" ? 1 : 0
|
||||
|
||||
StyledRectangularShadow {
|
||||
@@ -174,26 +191,26 @@ Scope {
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "osdVolume"
|
||||
target: "osdVolume"
|
||||
|
||||
function trigger() {
|
||||
root.triggerOsd()
|
||||
function trigger() {
|
||||
root.triggerOsd();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
GlobalStates.osdVolumeOpen = false
|
||||
GlobalStates.osdVolumeOpen = false;
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
GlobalStates.osdVolumeOpen = !GlobalStates.osdVolumeOpen
|
||||
GlobalStates.osdVolumeOpen = !GlobalStates.osdVolumeOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "osdVolumeTrigger"
|
||||
description: "Triggers volume OSD on press"
|
||||
|
||||
onPressed: {
|
||||
root.triggerOsd()
|
||||
root.triggerOsd();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
@@ -201,8 +218,7 @@ Scope {
|
||||
description: "Hides volume OSD on press"
|
||||
|
||||
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.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
// import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -21,19 +16,23 @@ Item {
|
||||
property real valueIndicatorLeftPadding: 10
|
||||
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
|
||||
implicitHeight: valueIndicator.implicitHeight
|
||||
implicitWidth: Appearance.sizes.osdWidth + 2 * Appearance.sizes.elevationMargin
|
||||
implicitHeight: valueIndicator.implicitHeight + 2 * Appearance.sizes.elevationMargin
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: valueIndicator
|
||||
}
|
||||
WrapperRectangle {
|
||||
Rectangle {
|
||||
id: valueIndicator
|
||||
anchors.fill: parent
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: Appearance.sizes.elevationMargin
|
||||
}
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.colors.colLayer0
|
||||
|
||||
implicitWidth: valueRow.implicitWidth
|
||||
implicitHeight: valueRow.implicitHeight
|
||||
|
||||
RowLayout { // Icon on the left, stuff on the right
|
||||
id: valueRow
|
||||
@@ -48,6 +47,7 @@ Item {
|
||||
Layout.leftMargin: valueIndicatorLeftPadding
|
||||
Layout.topMargin: valueIndicatorVerticalPadding
|
||||
Layout.bottomMargin: valueIndicatorVerticalPadding
|
||||
|
||||
MaterialSymbol { // Icon
|
||||
anchors {
|
||||
centerIn: parent
|
||||
@@ -101,4 +101,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.color: Appearance.colors.colLayer0Border
|
||||
|
||||
ColumnLayout { // Workspaces
|
||||
Column { // Workspaces
|
||||
id: workspaceColumnLayout
|
||||
|
||||
z: root.workspaceZ
|
||||
@@ -71,7 +71,7 @@ Item {
|
||||
spacing: workspaceSpacing
|
||||
Repeater {
|
||||
model: Config.options.overview.rows
|
||||
delegate: RowLayout {
|
||||
delegate: Row {
|
||||
id: row
|
||||
property int rowIndex: index
|
||||
spacing: workspaceSpacing
|
||||
@@ -148,27 +148,27 @@ Item {
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
// 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}`
|
||||
var win = windowByAddress[address]
|
||||
const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
|
||||
return inWorkspaceGroup;
|
||||
})
|
||||
})].reverse()
|
||||
}
|
||||
}
|
||||
delegate: OverviewWindow {
|
||||
id: window
|
||||
required property var modelData
|
||||
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}`
|
||||
windowData: windowByAddress[address]
|
||||
toplevel: modelData
|
||||
monitorData: HyprlandData.monitors[monitorId]
|
||||
monitorData: this.monitor
|
||||
scale: root.scale
|
||||
availableWorkspaceWidth: root.workspaceImplicitWidth
|
||||
availableWorkspaceHeight: root.workspaceImplicitHeight
|
||||
widgetMonitorId: root.monitor.id
|
||||
windowData: windowByAddress[address]
|
||||
|
||||
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.y: targetWindowHeight / 2
|
||||
MouseArea {
|
||||
@@ -218,8 +218,13 @@ Item {
|
||||
updateWindowPosition.restart()
|
||||
}
|
||||
else {
|
||||
window.x = window.initX
|
||||
window.y = window.initY
|
||||
if (!window.windowData.floating) {
|
||||
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) => {
|
||||
|
||||
@@ -81,35 +81,29 @@ Item { // Window
|
||||
border.width : 1
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.font.pixelSize.smaller * 0.5
|
||||
Image {
|
||||
id: windowIcon
|
||||
anchors.centerIn: parent
|
||||
property var iconSize: {
|
||||
// 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 {
|
||||
id: windowIcon
|
||||
property var iconSize: {
|
||||
// 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)
|
||||
|
||||
Behavior on width {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
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",
|
||||
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;
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "wipeclipboard",
|
||||
execute: () => {
|
||||
Cliphist.wipe();
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
function focusFirstItem() {
|
||||
|
||||
@@ -167,6 +167,14 @@ ContentPage {
|
||||
icon: "lock"
|
||||
title: Translation.tr("Lock screen")
|
||||
|
||||
ConfigSwitch {
|
||||
text: Translation.tr('Launch on startup')
|
||||
checked: Config.options.lock.launchOnStartup
|
||||
onCheckedChanged: {
|
||||
Config.options.lock.launchOnStartup = checked;
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Blurred style")
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ ContentPage {
|
||||
forceWidth: true
|
||||
|
||||
Process {
|
||||
id: konachanWallProc
|
||||
id: randomWallProc
|
||||
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 {
|
||||
onRead: data => {
|
||||
konachanWallProc.status = data.trim();
|
||||
randomWallProc.status = data.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,19 +91,35 @@ ContentPage {
|
||||
|
||||
ColumnLayout {
|
||||
RippleButtonWithIcon {
|
||||
id: rndWallBtn
|
||||
enabled: !randomWallProc.running
|
||||
visible: Config.options.policies.weeb === 1
|
||||
Layout.fillWidth: true
|
||||
buttonRadius: Appearance.rounding.small
|
||||
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: {
|
||||
konachanWallProc.running = true;
|
||||
randomWallProc.scriptPath = `${Directories.scriptPath}/colors/random/random_konachan_wall.sh`;
|
||||
randomWallProc.running = true;
|
||||
}
|
||||
StyledToolTip {
|
||||
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 {
|
||||
Layout.fillWidth: true
|
||||
materialIcon: "wallpaper"
|
||||
@@ -154,17 +171,6 @@ ContentPage {
|
||||
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 {
|
||||
|
||||
@@ -36,6 +36,9 @@ Item {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) {
|
||||
Ai.clearMessages();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
property int lastResponseLength: 0
|
||||
property bool shouldAutoScroll: true
|
||||
|
||||
onContentYChanged: shouldAutoScroll = atYEnd
|
||||
onContentHeightChanged: {
|
||||
if (shouldAutoScroll) positionViewAtEnd();
|
||||
if (atYEnd) positionViewAtEnd();
|
||||
}
|
||||
onCountChanged: { // Auto-scroll when new messages are added
|
||||
if (shouldAutoScroll) positionViewAtEnd();
|
||||
if (atYEnd) positionViewAtEnd();
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
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?
|
||||
decodeImageAndAttachProc.handleEntry(currentClipboardEntry)
|
||||
event.accepted = true;
|
||||
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
|
||||
} else if (event.key === Qt.Key_Escape) { // Esc to detach file
|
||||
|
||||
@@ -127,8 +127,12 @@ Scope { // Scope
|
||||
|
||||
sourceComponent: FloatingWindow {
|
||||
id: detachedSidebarRoot
|
||||
visible: GlobalStates.sidebarLeftOpen
|
||||
property var contentParent: detachedSidebarBackground
|
||||
|
||||
visible: GlobalStates.sidebarLeftOpen
|
||||
onVisibleChanged: {
|
||||
if (!visible) GlobalStates.sidebarLeftOpen = false;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: detachedSidebarBackground
|
||||
|
||||
@@ -125,13 +125,12 @@ Rectangle {
|
||||
sourceComponent: Item {
|
||||
implicitHeight: root.imageHeight * root.scale
|
||||
implicitWidth: imagePreview.implicitWidth
|
||||
Image {
|
||||
StyledImage {
|
||||
id: imagePreview
|
||||
anchors.fill: parent
|
||||
source: Qt.resolvedUrl(root.filePath)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
antialiasing: true
|
||||
asynchronous: true
|
||||
width: root.imageWidth * root.scale
|
||||
height: root.imageHeight * root.scale
|
||||
sourceSize.width: root.imageWidth * root.scale
|
||||
|
||||
@@ -58,7 +58,7 @@ Button {
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
StyledImage {
|
||||
id: imageObject
|
||||
anchors.fill: parent
|
||||
width: root.rowHeight * modelData.aspect_ratio
|
||||
@@ -68,12 +68,6 @@ Button {
|
||||
sourceSize.width: root.rowHeight * modelData.aspect_ratio
|
||||
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.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
|
||||
@@ -56,7 +56,7 @@ Item {
|
||||
StyledSlider {
|
||||
id: slider
|
||||
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
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
@@ -156,8 +156,6 @@ Item { // Bar content region
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +70,15 @@ MouseArea {
|
||||
hoverTarget: root
|
||||
active: GlobalStates.mediaControlsOpen ? false : root.containsMouse
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
spacing: 4
|
||||
|
||||
Row {
|
||||
spacing: 4
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fill: 0
|
||||
font.weight: Font.Medium
|
||||
text: "music_note"
|
||||
@@ -84,6 +87,7 @@ MouseArea {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Media"
|
||||
font {
|
||||
weight: Font.Medium
|
||||
|
||||
@@ -15,6 +15,7 @@ import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
@@ -35,7 +36,11 @@ ShellRoot {
|
||||
property color imageFillColor: "#33f1d1ff"
|
||||
property color onBorderColor: "#ff000000"
|
||||
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 real falsePositivePreventionRatio: 0.5
|
||||
|
||||
@@ -74,10 +79,10 @@ ShellRoot {
|
||||
}
|
||||
implicitWidth: regionInfoRow.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: regionInfoRow.implicitHeight + verticalPadding * 2
|
||||
RowLayout {
|
||||
Row {
|
||||
id: regionInfoRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
spacing: 4
|
||||
|
||||
Loader {
|
||||
id: regionIconLoader
|
||||
@@ -133,7 +138,8 @@ ShellRoot {
|
||||
})
|
||||
readonly property list<var> layerRegions: {
|
||||
const layersOfThisMonitor = root.layers[panelWindow.hyprlandMonitor.name]
|
||||
const topLayers = layersOfThisMonitor.levels["2"]
|
||||
const topLayers = layersOfThisMonitor?.levels["2"]
|
||||
if (!topLayers) return [];
|
||||
const nonBarTopLayers = topLayers
|
||||
.filter(layer => !(layer.namespace.includes(":bar") || layer.namespace.includes(":verticalBar") || layer.namespace.includes(":dock")))
|
||||
.map(layer => {
|
||||
@@ -386,14 +392,24 @@ ShellRoot {
|
||||
|
||||
// Overlay to darken screen
|
||||
Rectangle { // Base
|
||||
id: overlayRect
|
||||
z: 0
|
||||
anchors.fill: parent
|
||||
color: root.overlayColor
|
||||
layer.enabled: true
|
||||
id: darkenOverlay
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
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 {
|
||||
// TODO: Make this mask the base instead of just overlaying a border
|
||||
id: selectionBorder
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
@@ -406,11 +422,21 @@ ShellRoot {
|
||||
color: "transparent"
|
||||
border.color: root.selectionBorderColor
|
||||
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
|
||||
Rectangle {
|
||||
z: 9999
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
@@ -430,21 +456,19 @@ ShellRoot {
|
||||
implicitWidth: instructionsRow.implicitWidth + 10 * 2
|
||||
implicitHeight: instructionsRow.implicitHeight + 5 * 2
|
||||
|
||||
RowLayout {
|
||||
Row {
|
||||
id: instructionsRow
|
||||
anchors.centerIn: parent
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: screenshotRegionIcon.implicitWidth
|
||||
MaterialSymbol {
|
||||
id: screenshotRegionIcon
|
||||
anchors.centerIn: parent
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "screenshot_region"
|
||||
color: root.genericContentForeground
|
||||
}
|
||||
spacing: 4
|
||||
MaterialSymbol {
|
||||
id: screenshotRegionIcon
|
||||
// anchors.centerIn: parent
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "screenshot_region"
|
||||
color: root.genericContentForeground
|
||||
}
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Translation.tr("Drag or click a region • LMB: Copy • RMB: Edit")
|
||||
color: root.genericContentForeground
|
||||
}
|
||||
|
||||
+3
-3
@@ -32,11 +32,11 @@ page=$((1 + RANDOM % 1000));
|
||||
response=$(curl "https://konachan.net/post.json?tags=rating%3Asafe&limit=1&page=$page")
|
||||
link=$(echo "$response" | jq '.[0].file_url' -r);
|
||||
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"
|
||||
currentWallpaperPath=$(jq -r '.background.wallpaperPath' $illogicalImpulseConfigPath)
|
||||
if [ "$downloadPath" == "$currentWallpaperPath" ]; then
|
||||
downloadPath="$PICTURES_DIR/Wallpapers/konachan_random_image-1.$ext"
|
||||
downloadPath="$PICTURES_DIR/Wallpapers/random_wallpaper-1.$ext"
|
||||
fi
|
||||
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
@@ -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
|
||||
|
||||
required property ShellScreen screen
|
||||
readonly property bool isDdc: root.ddcMonitors.some(m => m.model === screen.model)
|
||||
readonly property string busNum: root.ddcMonitors.find(m => m.model === screen.model)?.busNum ?? ""
|
||||
readonly property bool isDdc: {
|
||||
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 real brightness
|
||||
property bool ready: false
|
||||
|
||||
@@ -95,6 +95,18 @@ Singleton {
|
||||
deleteProc.deleteEntry(entry);
|
||||
}
|
||||
|
||||
Process {
|
||||
id: wipeProc
|
||||
command: [root.cliphistBinary, "wipe"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
function wipe() {
|
||||
wipeProc.running = true;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Quickshell
|
||||
function onClipboardTextChanged() {
|
||||
|
||||
@@ -12,7 +12,6 @@ import Quickshell.Io
|
||||
*/
|
||||
Singleton {
|
||||
id: root
|
||||
property var manualActive
|
||||
property string from: Config.options?.light?.night?.from ?? "19:00"
|
||||
property string to: Config.options?.light?.night?.to ?? "06:30"
|
||||
property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true)
|
||||
@@ -29,6 +28,9 @@ Singleton {
|
||||
property int clockHour: DateTime.clock.hours
|
||||
property int clockMinute: DateTime.clock.minutes
|
||||
|
||||
property var manualActive
|
||||
property int manualActiveHour
|
||||
property int manualActiveMinute
|
||||
|
||||
onClockMinuteChanged: reEvaluate()
|
||||
onAutomaticChanged: {
|
||||
@@ -36,17 +38,26 @@ Singleton {
|
||||
root.firstEvaluation = true;
|
||||
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() {
|
||||
const t = clockHour * 60 + clockMinute;
|
||||
const from = fromHour * 60 + fromMinute;
|
||||
const to = toHour * 60 + toMinute;
|
||||
const manualActive = manualActiveHour * 60 + manualActiveMinute;
|
||||
|
||||
if (from < to) {
|
||||
root.shouldBeOn = t >= from && t <= to;
|
||||
} else {
|
||||
// Wrapped around midnight
|
||||
root.shouldBeOn = t >= from || t <= to;
|
||||
if (root.manualActive !== undefined && (inBetween(from, manualActive, t) || inBetween(to, manualActive, t))) {
|
||||
root.manualActive = undefined;
|
||||
}
|
||||
root.shouldBeOn = inBetween(t, from, to);
|
||||
if (firstEvaluation) {
|
||||
firstEvaluation = false;
|
||||
root.ensureState();
|
||||
@@ -94,15 +105,18 @@ Singleton {
|
||||
if (output.length == 0 || output.startsWith("Couldn't"))
|
||||
root.active = false;
|
||||
else
|
||||
root.active = (output != "6500");
|
||||
root.active = (output != "6500"); // 6500 is the default when off
|
||||
// console.log("[Hyprsunset] Fetched state:", output, "->", root.active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (root.manualActive === undefined)
|
||||
if (root.manualActive === undefined) {
|
||||
root.manualActive = root.active;
|
||||
root.manualActiveHour = root.clockHour;
|
||||
root.manualActiveMinute = root.clockMinute;
|
||||
}
|
||||
|
||||
root.manualActive = !root.manualActive;
|
||||
if (root.manualActive) {
|
||||
|
||||
@@ -12,10 +12,22 @@ Singleton {
|
||||
id: root
|
||||
|
||||
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() {
|
||||
Persistent.states.idle.inhibit = !Persistent.states.idle.inhibit
|
||||
root.inhibit = !root.inhibit
|
||||
Persistent.states.idle.inhibit = root.inhibit
|
||||
}
|
||||
|
||||
IdleInhibitor {
|
||||
|
||||
@@ -224,10 +224,8 @@ ApplicationWindow {
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentPageChanged() {
|
||||
if (pageLoader.sourceComponent !== root.pages[root.currentPage].component) {
|
||||
switchAnim.complete();
|
||||
switchAnim.start();
|
||||
}
|
||||
switchAnim.complete();
|
||||
switchAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,7 @@ ShellRoot {
|
||||
property bool enableLock: true
|
||||
property bool enableMediaControls: true
|
||||
property bool enableNotificationPopup: true
|
||||
property bool enableOnScreenDisplayBrightness: true
|
||||
property bool enableOnScreenDisplayVolume: true
|
||||
property bool enableOnScreenDisplay: true
|
||||
property bool enableOnScreenKeyboard: true
|
||||
property bool enableOverview: true
|
||||
property bool enableReloadPopup: true
|
||||
@@ -72,8 +71,7 @@ ShellRoot {
|
||||
LazyLoader { active: enableLock; component: Lock {} }
|
||||
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
||||
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
||||
LazyLoader { active: enableOnScreenDisplayBrightness; component: OnScreenDisplayBrightness {} }
|
||||
LazyLoader { active: enableOnScreenDisplayVolume; component: OnScreenDisplayVolume {} }
|
||||
LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
|
||||
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
||||
LazyLoader { active: enableOverview; component: Overview {} }
|
||||
LazyLoader { active: enableReloadPopup; component: ReloadPopup {} }
|
||||
|
||||
@@ -44,7 +44,7 @@ ApplicationWindow {
|
||||
Process {
|
||||
id: konachanWallProc
|
||||
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 {
|
||||
onRead: data => {
|
||||
console.log(`Konachan wall proc output: ${data}`);
|
||||
|
||||
Reference in New Issue
Block a user