Merge pull request #23 from ferrreo/pretty-major-refactors

feat: settings is now json, refactor panels to be able to dismiss by …
This commit is contained in:
ferrreo 2025-07-18 17:12:22 +01:00 committed by GitHub
commit 247426ce31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 1577 additions and 1523 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.qmlls.ini

View file

@ -83,8 +83,7 @@ Scope {
anchors.rightMargin: 18 anchors.rightMargin: 18
spacing: 12 spacing: 12
NotificationHistory { NotificationIcon {
id: notificationHistoryWin
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@ -132,20 +131,22 @@ Scope {
} }
PanelWindow { PanelWindow {
id: topCornerPanel id: topLeftPanel
anchors.top: true anchors.top: true
anchors.left: true anchors.left: true
anchors.right: true
color: "transparent" color: "transparent"
screen: modelData screen: modelData
margins.top: 36 margins.top: 36
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
visible: true visible: true
WlrLayershell.layer: WlrLayer.Background
aboveWindows: false
WlrLayershell.namespace: "swww-daemon"
implicitHeight: 24 implicitHeight: 24
Corners { Corners {
id: topleftCorner id: topLeftCorner
position: "bottomleft" position: "bottomleft"
size: 1.3 size: 1.3
fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222"
@ -153,9 +154,25 @@ Scope {
offsetY: 0 offsetY: 0
anchors.top: parent.top anchors.top: parent.top
} }
}
PanelWindow {
id: topRightPanel
anchors.top: true
anchors.right: true
color: "transparent"
screen: modelData
margins.top: 36
WlrLayershell.exclusionMode: ExclusionMode.Ignore
visible: true
WlrLayershell.layer: WlrLayer.Background
aboveWindows: false
WlrLayershell.namespace: "swww-daemon"
implicitHeight: 24
Corners { Corners {
id: toprightCorner id: topRightCorner
position: "bottomright" position: "bottomright"
size: 1.3 size: 1.3
fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222"
@ -173,6 +190,9 @@ Scope {
screen: modelData screen: modelData
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
visible: true visible: true
WlrLayershell.layer: WlrLayer.Background
aboveWindows: false
WlrLayershell.namespace: "swww-daemon"
implicitHeight: 24 implicitHeight: 24
@ -188,13 +208,16 @@ Scope {
} }
PanelWindow { PanelWindow {
id: bottomRightCornerPanel id: bottomRightPanel
anchors.bottom: true anchors.bottom: true
anchors.right: true anchors.right: true
color: "transparent" color: "transparent"
screen: modelData screen: modelData
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
visible: true visible: true
WlrLayershell.layer: WlrLayer.Background
aboveWindows: false
WlrLayershell.namespace: "swww-daemon"
implicitHeight: 24 implicitHeight: 24
@ -208,15 +231,10 @@ Scope {
anchors.top: parent.top anchors.top: parent.top
} }
} }
Loader {
id: tabViewerLoader
} }
} }
} }
}
// This alias exposes the visual bar's visibility to the outside world // This alias exposes the visual bar's visibility to the outside world
property alias visible: barRootItem.visible property alias visible: barRootItem.visible
} }

View file

@ -12,10 +12,11 @@ PanelWindow {
anchors.top: true anchors.top: true
anchors.left: true anchors.left: true
anchors.right: true anchors.right: true
focusable: false
margins.top: barHeight margins.top: barHeight
visible: !activeWindowWrapper.finallyHidden visible: !activeWindowWrapper.finallyHidden
implicitHeight: activeWindowTitleContainer.height implicitHeight: activeWindowTitleContainer.height
implicitWidth: activeWindowTitleContainer.x implicitWidth: 0
property int barHeight: 36 property int barHeight: 36
color: "transparent" color: "transparent"
@ -104,7 +105,7 @@ PanelWindow {
bottomLeftRadius: Math.max(0, width / 2) bottomLeftRadius: Math.max(0, width / 2)
bottomRightRadius: Math.max(0, width / 2) bottomRightRadius: Math.max(0, width / 2)
width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.showActiveWindowIcon ? 28 : 22)) width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.settings.showActiveWindowIcon ? 28 : 22))
height: activeWindowTitle.implicitHeight + 12 height: activeWindowTitle.implicitHeight + 12
anchors.top: parent.top anchors.top: parent.top
@ -118,7 +119,7 @@ PanelWindow {
anchors.leftMargin: 6 anchors.leftMargin: 6
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
source: ToplevelManager?.activeToplevel ? getIcon() : "" source: ToplevelManager?.activeToplevel ? getIcon() : ""
visible: Settings.showActiveWindowIcon visible: Settings.settings.showActiveWindowIcon
anchors.verticalCenterOffset: -3 anchors.verticalCenterOffset: -3
} }
@ -129,12 +130,12 @@ PanelWindow {
color: Theme.textSecondary color: Theme.textSecondary
elide: Text.ElideRight elide: Text.ElideRight
anchors.left: icon.right anchors.left: icon.right
anchors.leftMargin: Settings.showActiveWindowIcon ? 4 : 6 anchors.leftMargin: Settings.settings.showActiveWindowIcon ? 4 : 6
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 6 anchors.rightMargin: 6
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -3 anchors.verticalCenterOffset: -3
horizontalAlignment: Settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter horizontalAlignment: Settings.settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
maximumLineCount: 1 maximumLineCount: 1
} }

View file

@ -5,28 +5,42 @@ import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Components import qs.Components
import qs.Settings import qs.Settings
import Qt5Compat.GraphicalEffects
import Quickshell.Wayland import Quickshell.Wayland
import "../../Helpers/Fuzzysort.js" as Fuzzysort import "../../Helpers/Fuzzysort.js" as Fuzzysort
PanelWindow { PanelWithOverlay {
id: appLauncherPanel id: appLauncherPanel
function showAt() {
appLauncherPanelRect.showAt();
}
function hidePanel() {
appLauncherPanelRect.hidePanel();
}
function show() {
appLauncherPanelRect.showAt();
}
function dismiss() {
appLauncherPanelRect.hidePanel();
}
Rectangle {
id: appLauncherPanelRect
implicitWidth: 460 implicitWidth: 460
implicitHeight: 640 implicitHeight: 640
color: "transparent" color: "transparent"
visible: false visible: parent.visible
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
screen: (typeof modelData !== 'undefined' ? modelData : null)
property bool shouldBeVisible: false property bool shouldBeVisible: false
anchors.top: parent.top
anchors.top: true anchors.horizontalCenter: parent.horizontalCenter
margins.top: 36
function showAt() { function showAt() {
visible = true; appLauncherPanel.visible = true;
shouldBeVisible = true; shouldBeVisible = true;
searchField.forceActiveFocus() searchField.forceActiveFocus();
root.selectedIndex = 0; root.selectedIndex = 0;
root.appModel = DesktopEntries.applications.values; root.appModel = DesktopEntries.applications.values;
root.updateFilter(); root.updateFilter();
@ -40,7 +54,7 @@ PanelWindow {
Rectangle { Rectangle {
id: root id: root
width: 400 width: 460
height: 640 height: 640
x: (parent.width - width) / 2 x: (parent.width - width) / 2
color: Theme.backgroundPrimary color: Theme.backgroundPrimary
@ -51,16 +65,22 @@ PanelWindow {
property var filteredApps: [] property var filteredApps: []
property int selectedIndex: 0 property int selectedIndex: 0
property int targetY: (parent.height - height) / 2 property int targetY: (parent.height - height) / 2
y: appLauncherPanel.shouldBeVisible ? targetY : -height y: appLauncherPanelRect.shouldBeVisible ? targetY : -height
Behavior on y { Behavior on y {
NumberAnimation { duration: 300; easing.type: Easing.OutCubic } NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
} }
scale: appLauncherPanel.shouldBeVisible ? 1 : 0 }
scale: appLauncherPanelRect.shouldBeVisible ? 1 : 0
Behavior on scale { Behavior on scale {
NumberAnimation { duration: 200; easing.type: Easing.InOutCubic } NumberAnimation {
duration: 200
easing.type: Easing.InOutCubic
}
} }
onScaleChanged: { onScaleChanged: {
if (scale === 0 && !appLauncherPanel.shouldBeVisible) { if (scale === 0 && !appLauncherPanelRect.shouldBeVisible) {
appLauncherPanel.visible = false; appLauncherPanel.visible = false;
} }
} }
@ -95,12 +115,16 @@ PanelWindow {
} }
} }
if (!query || query.startsWith("=")) { if (!query || query.startsWith("=")) {
results = results.concat(apps.sort(function(a, b) { results = results.concat(apps.sort(function (a, b) {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
})); }));
} else { } else {
var fuzzyResults = Fuzzysort.go(query, apps, { keys: ["name", "comment", "genericName"] }); var fuzzyResults = Fuzzysort.go(query, apps, {
results = results.concat(fuzzyResults.map(function(r) { return r.obj; })); keys: ["name", "comment", "genericName"]
});
results = results.concat(fuzzyResults.map(function (r) {
return r.obj;
}));
} }
root.filteredApps = results; root.filteredApps = results;
root.selectedIndex = 0; root.selectedIndex = 0;
@ -121,13 +145,9 @@ PanelWindow {
var modelData = filteredApps[selectedIndex]; var modelData = filteredApps[selectedIndex];
if (modelData.isCalculator) { if (modelData.isCalculator) {
Qt.callLater(function() { Qt.callLater(function () {
Quickshell.clipboardText = String(modelData.result); Quickshell.clipboardText = String(modelData.result);
Quickshell.execDetached([ Quickshell.execDetached(["notify-send", "Calculator Result", `${modelData.expr} = ${modelData.result} (copied to clipboard)`]);
"notify-send",
"Calculator Result",
`${modelData.expr} = ${modelData.result} (copied to clipboard)`
]);
}); });
} else if (modelData.execute) { } else if (modelData.execute) {
modelData.execute(); modelData.execute();
@ -208,8 +228,16 @@ PanelWindow {
Keys.onEscapePressed: appLauncherPanel.hidePanel() Keys.onEscapePressed: appLauncherPanel.hidePanel()
} }
} }
Behavior on border.color { ColorAnimation { duration: 120 } } Behavior on border.color {
Behavior on border.width { NumberAnimation { duration: 120 } } ColorAnimation {
duration: 120
}
}
Behavior on border.width {
NumberAnimation {
duration: 120
}
}
} }
// App List Card // App List Card
@ -219,7 +247,6 @@ PanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
clip: true clip: true
anchors.margins: 0
property int innerPadding: 16 property int innerPadding: 16
Item { Item {
@ -250,9 +277,21 @@ PanelWindow {
radius: 12 radius: 12
border.color: hovered || isSelected ? Theme.accentPrimary : "transparent" border.color: hovered || isSelected ? Theme.accentPrimary : "transparent"
border.width: hovered || isSelected ? 2 : 0 border.width: hovered || isSelected ? 2 : 0
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color {
Behavior on border.color { ColorAnimation { duration: 120 } } ColorAnimation {
Behavior on border.width { NumberAnimation { duration: 120 } } duration: 120
}
}
Behavior on border.color {
ColorAnimation {
duration: 120
}
}
Behavior on border.width {
NumberAnimation {
duration: 120
}
}
} }
RowLayout { RowLayout {
@ -261,7 +300,8 @@ PanelWindow {
anchors.rightMargin: 10 anchors.rightMargin: 10
spacing: 10 spacing: 10
Item { Item {
width: 28; height: 28 width: 28
height: 28
property bool iconLoaded: !modelData.isCalculator && iconImg.status === Image.Ready && iconImg.source !== "" && iconImg.status !== Image.Error property bool iconLoaded: !modelData.isCalculator && iconImg.status === Image.Ready && iconImg.source !== "" && iconImg.status !== Image.Error
Image { Image {
id: iconImg id: iconImg
@ -297,8 +337,7 @@ PanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
} }
Text { Text {
text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) : (modelData.comment || modelData.genericName || "No description available")
: (modelData.comment || modelData.genericName || "No description available")
color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeCaption font.pixelSize: Theme.fontSizeCaption
@ -309,7 +348,9 @@ PanelWindow {
} }
} }
Item { Layout.fillWidth: true } Item {
Layout.fillWidth: true
}
Text { Text {
text: modelData.isCalculator ? "content_copy" : "chevron_right" text: modelData.isCalculator ? "content_copy" : "chevron_right"
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
@ -331,10 +372,10 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
ripple.opacity = 0.18 ripple.opacity = 0.18;
rippleNumberAnimation.start() rippleNumberAnimation.start();
root.selectedIndex = index root.selectedIndex = index;
root.activateSelected() root.activateSelected();
} }
onPressed: ripple.opacity = 0.18 onPressed: ripple.opacity = 0.18
onReleased: ripple.opacity = 0.0 onReleased: ripple.opacity = 0.0
@ -368,7 +409,7 @@ PanelWindow {
size: 1.1 size: 1.1
fillColor: Theme.backgroundPrimary fillColor: Theme.backgroundPrimary
anchors.top: root.top anchors.top: root.top
offsetX: 396 offsetX: 416
offsetY: 0 offsetY: 0
} }
@ -378,7 +419,8 @@ PanelWindow {
size: 1.1 size: 1.1
fillColor: Theme.backgroundPrimary fillColor: Theme.backgroundPrimary
anchors.top: root.top anchors.top: root.top
offsetX: -396 offsetX: -416
offsetY: 0 offsetY: 0
} }
}
} }

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import qs.Settings import qs.Settings
import qs.Services import qs.Services
@ -10,7 +10,7 @@ Item {
id: mediaControl id: mediaControl
width: visible ? mediaRow.width : 0 width: visible ? mediaRow.width : 0
height: 36 height: 36
visible: Settings.showMediaInBar && MusicManager.currentPlayer visible: Settings.settings.showMediaInBar && MusicManager.currentPlayer
RowLayout { RowLayout {
id: mediaRow id: mediaRow

View file

@ -6,7 +6,7 @@ import qs.Services
Row { Row {
id: layout id: layout
spacing: 10 spacing: 10
visible: Settings.showSystemInfoInBar visible: Settings.settings.showSystemInfoInBar
Row { Row {
id: cpuUsageLayout id: cpuUsageLayout

View file

@ -16,7 +16,7 @@ Item {
height: usableOuter * 2 height: usableOuter * 2
onOuterRadiusChanged: () => { onOuterRadiusChanged: () => {
usableOuter = Settings.visualizerType === "fire" ? outerRadius * 0.85 : outerRadius; usableOuter = Settings.settings.visualizerType === "fire" ? outerRadius * 0.85 : outerRadius;
} }
Repeater { Repeater {
@ -25,25 +25,25 @@ Item {
property real value: root.values[index] property real value: root.values[index]
property real angle: (index / root.values.length) * 360 property real angle: (index / root.values.length) * 360
width: Math.max(2, (root.innerRadius * 2 * Math.PI) / root.values.length - 4) width: Math.max(2, (root.innerRadius * 2 * Math.PI) / root.values.length - 4)
height: Settings.visualizerType === "diamond" ? value * 2 * (usableOuter - root.innerRadius) : value * (usableOuter - root.innerRadius) height: Settings.settings.visualizerType === "diamond" ? value * 2 * (usableOuter - root.innerRadius) : value * (usableOuter - root.innerRadius)
radius: width / 2 radius: width / 2
color: root.fillColor color: root.fillColor
border.color: root.strokeColor border.color: root.strokeColor
border.width: root.strokeWidth border.width: root.strokeWidth
antialiasing: true antialiasing: true
x: Settings.visualizerType === "radial" ? root.width / 2 - width / 2 : root.width / 2 + root.innerRadius * Math.cos(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - width / 2 x: Settings.settings.visualizerType === "radial" ? root.width / 2 - width / 2 : root.width / 2 + root.innerRadius * Math.cos(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - width / 2
y: Settings.visualizerType === "radial" ? root.height / 2 - height : Settings.visualizerType === "diamond" ? root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height / 2 : root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height y: Settings.settings.visualizerType === "radial" ? root.height / 2 - height : Settings.settings.visualizerType === "diamond" ? root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height / 2 : root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height
transform: [ transform: [
Rotation { Rotation {
origin.x: width / 2 origin.x: width / 2
origin.y: Settings.visualizerType === "diamond" ? height / 2 : height origin.y: Settings.settings.visualizerType === "diamond" ? height / 2 : height
angle: Settings.visualizerType === "radial" ? (index / root.values.length) * 360 : Settings.visualizerType === "fire" ? 0 : (index / root.values.length) * 360 - 90 angle: Settings.settings.visualizerType === "radial" ? (index / root.values.length) * 360 : Settings.settings.visualizerType === "fire" ? 0 : (index / root.values.length) * 360 - 90
}, },
Translate { Translate {
x: Settings.visualizerType === "radial" ? root.innerRadius * Math.cos(2 * Math.PI * index / root.values.length) : 0 x: Settings.settings.visualizerType === "radial" ? root.innerRadius * Math.cos(2 * Math.PI * index / root.values.length) : 0
y: Settings.visualizerType === "radial" ? root.innerRadius * Math.sin(2 * Math.PI * index / root.values.length) : 0 y: Settings.settings.visualizerType === "radial" ? root.innerRadius * Math.sin(2 * Math.PI * index / root.values.length) : 0
} }
] ]

View file

@ -0,0 +1,44 @@
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Settings
PanelWindow {
id: outerPanel
property bool showOverlay: true
property int topMargin: 36
property color overlayColor: showOverlay ? Theme.overlay : "transparent"
function dismiss() {
visible = false;
}
function show() {
visible = true;
}
implicitWidth: screen.width
implicitHeight: screen.height
color: visible ? overlayColor : "transparent"
visible: false
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
screen: (typeof modelData !== 'undefined' ? modelData : null)
anchors.top: true
anchors.left: true
anchors.right: true
anchors.bottom: true
margins.top: topMargin
MouseArea {
anchors.fill: parent
onClicked: outerPanel.dismiss()
}
Behavior on color {
ColorAnimation {
duration: 350
easing.type: Easing.InOutCubic
}
}
}

View file

@ -1,5 +1,4 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import qs.Settings import qs.Settings
@ -12,8 +11,9 @@ Window {
flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
color: "transparent" color: "transparent"
visible: false visible: false
minimumWidth: Math.max(minimumWidth, tooltipText.implicitWidth + 24)
minimumHeight: Math.max(minimumHeight, tooltipText.implicitHeight + 16) minimumWidth: tooltipText.implicitWidth + 24
minimumHeight: tooltipText.implicitHeight + 16
property var _timerObj: null property var _timerObj: null
onTooltipVisibleChanged: { onTooltipVisibleChanged: {
if (tooltipVisible) { if (tooltipVisible) {
@ -33,26 +33,32 @@ Window {
x = pos.x - width / 2 + targetItem.width / 2; x = pos.x - width / 2 + targetItem.width / 2;
y = pos.y + 12; y = pos.y + 12;
visible = true; visible = true;
console.log("StyledTooltip _showNow called");
console.log("StyledTooltip Theme.textPrimary:", Theme.textPrimary);
} }
function _hideNow() { function _hideNow() {
visible = false; visible = false;
if (_timerObj) { _timerObj.destroy(); _timerObj = null; } if (_timerObj) { _timerObj.destroy(); _timerObj = null; }
} }
Connections { Connections {
target: targetItem target: tooltipWindow.targetItem
onXChanged: if (tooltipWindow.visible) tooltipWindow._showNow() function onXChanged() {
onYChanged: if (tooltipWindow.visible) tooltipWindow._showNow() if (tooltipWindow.visible) tooltipWindow._showNow()
onWidthChanged: if (tooltipWindow.visible) tooltipWindow._showNow()
onHeightChanged: if (tooltipWindow.visible) tooltipWindow._showNow()
} }
Component.onCompleted: console.log("Tooltip window loaded") function onYChanged() {
if (tooltipWindow.visible) tooltipWindow._showNow()
}
function onWidthChanged() {
if (tooltipWindow.visible) tooltipWindow._showNow()
}
function onHeightChanged() {
if (tooltipWindow.visible) tooltipWindow._showNow()
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 6 radius: 6
color: "#222" color: "#222"
border.color: Theme.border || "#444" border.color: Theme.backgroundTertiary || "#444"
border.width: 1 border.width: 1
opacity: 0.97 opacity: 0.97
z: 1 z: 1

View file

@ -1,11 +1,10 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Layouts
import QtQuick.Layouts 1.15
import qs.Settings import qs.Settings
Item { Item {
id: root id: root
property var tabsModel: [] // [{icon: "videocam", label: "Video"}, ...] property var tabsModel: []
property int currentIndex: 0 property int currentIndex: 0
signal tabChanged(int index) signal tabChanged(int index)
@ -16,17 +15,20 @@ Item {
Repeater { Repeater {
model: root.tabsModel model: root.tabsModel
delegate: Column { delegate: Rectangle {
width: 56 id: tabWrapper
spacing: 2 implicitHeight: tab.height
implicitWidth: 56
color: "transparent"
property bool hovered: false property bool hovered: false
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
if (root.currentIndex !== index) { if (currentIndex !== index) {
root.currentIndex = index; currentIndex = index;
root.tabChanged(index); tabChanged(index);
} }
} }
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -35,17 +37,20 @@ Item {
onExited: parent.hovered = false onExited: parent.hovered = false
} }
ColumnLayout {
id: tab
spacing: 2
anchors.centerIn: parent
Layout.fillWidth: true
Layout.fillHeight: true
// Icon // Icon
Text { Text {
text: modelData.icon text: modelData.icon
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 22 font.pixelSize: 22
color: index === root.currentIndex color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : tabWrapper.hovered ? (Theme ? Theme.accentPrimary : "#7C3AED") : (Theme ? Theme.textSecondary : "#444")
? (Theme ? Theme.accentPrimary : "#7C3AED") Layout.alignment: Qt.AlignCenter
: parent.hovered
? (Theme ? Theme.accentPrimary : "#7C3AED")
: (Theme ? Theme.textSecondary : "#444")
anchors.horizontalCenter: parent.horizontalCenter
} }
// Label // Label
@ -53,12 +58,8 @@ Item {
text: modelData.label text: modelData.label
font.pixelSize: 12 font.pixelSize: 12
font.bold: index === root.currentIndex font.bold: index === root.currentIndex
color: index === root.currentIndex color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : tabWrapper.hovered ? (Theme ? Theme.accentPrimary : "#7C3AED") : (Theme ? Theme.textSecondary : "#444")
? (Theme ? Theme.accentPrimary : "#7C3AED") Layout.alignment: Qt.AlignCenter
: parent.hovered
? (Theme ? Theme.accentPrimary : "#7C3AED")
: (Theme ? Theme.textSecondary : "#444")
anchors.horizontalCenter: parent.horizontalCenter
} }
// Underline for active tab // Underline for active tab
@ -66,10 +67,9 @@ Item {
width: 24 width: 24
height: 2 height: 2
radius: 1 radius: 1
color: index === root.currentIndex color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : "transparent"
? (Theme ? Theme.accentPrimary : "#7C3AED") Layout.alignment: Qt.AlignCenter
: "transparent" }
anchors.horizontalCenter: parent.horizontalCenter
} }
} }
} }

View file

@ -3,7 +3,6 @@ import Quickshell.Io
IpcHandler { IpcHandler {
property var appLauncherPanel property var appLauncherPanel
property var lockScreen property var lockScreen
property var tabViewer
target: "globalIPC" target: "globalIPC"

View file

@ -15,18 +15,18 @@ Singleton {
toggleRandomWallpaper(); toggleRandomWallpaper();
} }
} }
property string wallpaperDirectory: Settings.wallpaperFolder property string wallpaperDirectory: Settings.settings.wallpaperFolder
property var wallpaperList: [] property var wallpaperList: []
property string currentWallpaper: Settings.currentWallpaper property string currentWallpaper: Settings.settings.currentWallpaper
property bool scanning: false property bool scanning: false
property string transitionType: Settings.transitionType property string transitionType: Settings.settings.transitionType
property var randomChoices: ["fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer"] property var randomChoices: ["fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer"]
function loadWallpapers() { function loadWallpapers() {
scanning = true; scanning = true;
wallpaperList = []; wallpaperList = [];
folderModel.folder = ""; folderModel.folder = "";
folderModel.folder = "file://" + (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : ""); folderModel.folder = "file://" + (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "");
} }
function changeWallpaper(path) { function changeWallpaper(path) {
@ -36,14 +36,13 @@ Singleton {
function setCurrentWallpaper(path, isInitial) { function setCurrentWallpaper(path, isInitial) {
currentWallpaper = path; currentWallpaper = path;
if (!isInitial) { if (!isInitial) {
Settings.currentWallpaper = path; Settings.settings.currentWallpaper = path;
Settings.saveSettings();
} }
if (Settings.useSWWW) { if (Settings.settings.useSWWW) {
if (Settings.transitionType === "random") { if (Settings.settings.transitionType === "random") {
transitionType = randomChoices[Math.floor(Math.random() * randomChoices.length)]; transitionType = randomChoices[Math.floor(Math.random() * randomChoices.length)];
} else { } else {
transitionType = Settings.transitionType; transitionType = Settings.settings.transitionType;
} }
changeWallpaperProcess.running = true; changeWallpaperProcess.running = true;
} }
@ -60,31 +59,30 @@ Singleton {
} }
function toggleRandomWallpaper() { function toggleRandomWallpaper() {
if (Settings.randomWallpaper && !randomWallpaperTimer.running) { if (Settings.settings.randomWallpaper && !randomWallpaperTimer.running) {
randomWallpaperTimer.start(); randomWallpaperTimer.start();
setRandomWallpaper(); setRandomWallpaper();
} else if (!Settings.randomWallpaper && randomWallpaperTimer.running) { } else if (!Settings.settings.randomWallpaper && randomWallpaperTimer.running) {
randomWallpaperTimer.stop(); randomWallpaperTimer.stop();
} }
} }
function restartRandomWallpaperTimer() { function restartRandomWallpaperTimer() {
if (Settings.randomWallpaper) { if (Settings.settings.randomWallpaper) {
randomWallpaperTimer.stop(); randomWallpaperTimer.stop();
randomWallpaperTimer.start(); randomWallpaperTimer.start();
setRandomWallpaper();
} }
} }
function generateTheme() { function generateTheme() {
if (Settings.useWallpaperTheme) { if (Settings.settings.useWallpaperTheme) {
generateThemeProcess.running = true; generateThemeProcess.running = true;
} }
} }
Timer { Timer {
id: randomWallpaperTimer id: randomWallpaperTimer
interval: Settings.wallpaperInterval * 1000 interval: Settings.settings.wallpaperInterval * 1000
running: false running: false
repeat: true repeat: true
onTriggered: setRandomWallpaper() onTriggered: setRandomWallpaper()
@ -100,7 +98,7 @@ Singleton {
if (status === FolderListModel.Ready) { if (status === FolderListModel.Ready) {
var files = []; var files = [];
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
var fileph = (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + get(i, "fileName"); var fileph = (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "") + "/" + get(i, "fileName");
files.push(fileph); files.push(fileph);
} }
wallpaperList = files; wallpaperList = files;
@ -111,7 +109,7 @@ Singleton {
Process { Process {
id: changeWallpaperProcess id: changeWallpaperProcess
command: ["swww", "img", "--resize", Settings.wallpaperResize, "--transition-fps", Settings.transitionFps.toString(), "--transition-type", transitionType, "--transition-duration", Settings.transitionDuration.toString(), currentWallpaper] command: ["swww", "img", "--resize", Settings.settings.wallpaperResize, "--transition-fps", Settings.settings.transitionFps.toString(), "--transition-type", transitionType, "--transition-duration", Settings.settings.transitionDuration.toString(), currentWallpaper]
running: false running: false
} }

View file

@ -89,7 +89,7 @@ Singleton {
id: i, id: i,
idx: ws.id, idx: ws.id,
name: ws.name || "", name: ws.name || "",
output: ws.monitor.name || "", output: ws.monitor?.name || "",
isActive: ws.active === true, isActive: ws.active === true,
isFocused: ws.focused === true, isFocused: ws.focused === true,
isUrgent: ws.urgent === true isUrgent: ws.urgent === true

View file

@ -1,98 +1,66 @@
pragma Singleton pragma Singleton
import QtQuick import QtQuick
import QtCore
import Quickshell import Quickshell
import Quickshell.Io
import qs.Services import qs.Services
QtObject { Singleton {
property string shellName: "Noctalia"
property string settingsDir: Quickshell.env("HOME") + "/.config/" + shellName + "/"
property string settingsFile: settingsDir + "Settings.json"
property var settings: settingAdapter
Item {
Component.onCompleted: { Component.onCompleted: {
Qt.application.name = "quickshell" // ensure settings dir
Qt.application.organization = "quickshell" Quickshell.execDetached(["mkdir", "-p", settingsDir]);
Qt.application.domain = "quickshell.app"
loadSettings()
} }
}
FileView {
id: settingFileView
path: settingsFile
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
Component.onCompleted: function() {
reload()
}
onLoaded: function() {
WallpaperManager.setCurrentWallpaper(settings.currentWallpaper, true);
}
onLoadFailed: function(error) {
settingAdapter = {}
writeAdapter()
}
JsonAdapter {
id: settingAdapter
property string weatherCity: "Dinslaken" property string weatherCity: "Dinslaken"
property string profileImage: "/home/" + Quickshell.env("USER") + "/.face" property string profileImage: Quickshell.env("HOME") + "/.face"
property bool useFahrenheit: false property bool useFahrenheit: false
property string wallpaperFolder: "/usr/share/wallpapers" property string wallpaperFolder: "/usr/share/wallpapers"
property string currentWallpaper: "" property string currentWallpaper: ""
property string videoPath: "~/Videos/" property string videoPath: "~/Videos/"
property bool showActiveWindowIcon: false property bool showActiveWindowIcon: false
property bool showSystemInfoInBar: false
property bool showMediaInBar: false
property bool useSWWW: false property bool useSWWW: false
property bool randomWallpaper: false property bool randomWallpaper: false
property bool useWallpaperTheme: false property bool useWallpaperTheme: false
property bool showSystemInfoInBar: true
property bool showMediaInBar: false
property int wallpaperInterval: 300 property int wallpaperInterval: 300
property string wallpaperResize: "crop" property string wallpaperResize: "crop"
property int transitionFps: 60 property int transitionFps: 60
property string transitionType: "random" property string transitionType: "random"
property real transitionDuration: 1.1 property real transitionDuration: 1.1
property string visualizerType: "radial" // Options: "fire", "diamond", "radial" property string visualizerType: "radial"
}
// Settings persistence
property var settings: Settings {
category: "quickshell"
} }
function loadSettings() { Connections {
weatherCity = settings.value("weatherCity", weatherCity) target: settingAdapter
profileImage = settings.value("profileImage", profileImage) function onRandomWallpaperChanged() { WallpaperManager.toggleRandomWallpaper() }
let tempUnit = settings.value("weatherTempUnit", "celsius") function onWallpaperIntervalChanged() { WallpaperManager.restartRandomWallpaperTimer() }
useFahrenheit = (tempUnit === "fahrenheit") function onWallpaperFolderChanged() { WallpaperManager.loadWallpapers() }
wallpaperFolder = settings.value("wallpaperFolder", wallpaperFolder)
currentWallpaper = settings.value("currentWallpaper", currentWallpaper)
videoPath = settings.value("videoPath", videoPath)
let showActiveWindowIconFlag = settings.value("showActiveWindowIconFlag", "false")
showActiveWindowIcon = showActiveWindowIconFlag === "true"
let showSystemInfoInBarFlag = settings.value("showSystemInfoInBarFlag", "true")
showSystemInfoInBar = showSystemInfoInBarFlag === "true"
let showMediaInBarFlag = settings.value("showMediaInBarFlag", "true")
showMediaInBar = showMediaInBarFlag === "true"
let useSWWWFlag = settings.value("useSWWWFlag", "false")
useSWWW = useSWWWFlag === "true"
let randomWallpaperFlag = settings.value("randomWallpaperFlag", "false")
randomWallpaper = randomWallpaperFlag === "true"
let useWallpaperThemeFlag = settings.value("useWallpaperThemeFlag", "false")
useWallpaperTheme = useWallpaperThemeFlag === "true"
wallpaperInterval = settings.value("wallpaperInterval", wallpaperInterval)
wallpaperResize = settings.value("wallpaperResize", wallpaperResize)
transitionFps = settings.value("transitionFps", transitionFps)
transitionType = settings.value("transitionType", transitionType)
transitionDuration = settings.value("transitionDuration", transitionDuration)
visualizerType = settings.value("visualizerType", visualizerType)
WallpaperManager.setCurrentWallpaper(currentWallpaper, true);
} }
function saveSettings() {
settings.setValue("weatherCity", weatherCity)
settings.setValue("profileImage", profileImage)
settings.setValue("weatherTempUnit", useFahrenheit ? "fahrenheit" : "celsius")
settings.setValue("wallpaperFolder", wallpaperFolder)
settings.setValue("currentWallpaper", currentWallpaper)
settings.setValue("videoPath", videoPath)
settings.setValue("showActiveWindowIconFlag", showActiveWindowIcon ? "true" : "false")
settings.setValue("showSystemInfoInBarFlag", showSystemInfoInBar ? "true" : "false")
settings.setValue("showMediaInBarFlag", showMediaInBar ? "true" : "false")
settings.setValue("useSWWWFlag", useSWWW ? "true" : "false")
settings.setValue("randomWallpaperFlag", randomWallpaper ? "true" : "false")
settings.setValue("useWallpaperThemeFlag", useWallpaperTheme ? "true" : "false")
settings.setValue("wallpaperInterval", wallpaperInterval)
settings.setValue("wallpaperResize", wallpaperResize)
settings.setValue("transitionFps", transitionFps)
settings.setValue("transitionType", transitionType)
settings.setValue("transitionDuration", transitionDuration)
settings.setValue("visualizerType", visualizerType)
settings.sync()
}
// Property change handlers to auto-save (all commented out for explicit save only)
// onWeatherCityChanged: saveSettings()
// onProfileImageChanged: saveSettings()
// onUseFahrenheitChanged: saveSettings()
onRandomWallpaperChanged: WallpaperManager.toggleRandomWallpaper()
onWallpaperIntervalChanged: WallpaperManager.restartRandomWallpaperTimer()
onWallpaperFolderChanged: WallpaperManager.loadWallpapers()
} }

View file

@ -3,6 +3,7 @@ pragma Singleton
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Settings
Singleton { Singleton {
id: root id: root
@ -14,11 +15,16 @@ Singleton {
// FileView to load theme data from JSON file // FileView to load theme data from JSON file
FileView { FileView {
id: themeFile id: themeFile
path: Quickshell.configDir + "/Settings/Theme.json" path: Settings.settingsDir + "Theme.json"
watchChanges: true watchChanges: true
onFileChanged: reload() onFileChanged: reload()
onAdapterUpdated: writeAdapter() onAdapterUpdated: writeAdapter()
onLoadFailed: function(error) {
if (error.includes("No such file")) {
themeData = {}
writeAdapter()
}
}
JsonAdapter { JsonAdapter {
id: themeData id: themeData

View file

@ -44,4 +44,4 @@ check_contrast = true
# target: ABSOLUTE path in which to place a file with generated templated values. # target: ABSOLUTE path in which to place a file with generated templated values.
# ¡ If either one is a directory, then both SHOULD be one. ! # ¡ If either one is a directory, then both SHOULD be one. !
# zathura = { template = 'zathura', target = '~/.config/zathura/zathurarc' } # zathura = { template = 'zathura', target = '~/.config/zathura/zathurarc' }
Quickshell = { template = 'quickshell.json', target = 'Settings/Theme.json' } Quickshell = { template = 'quickshell.json', target = '~/.config/Noctalia/Theme.json' }

View file

@ -5,7 +5,7 @@ import qs.Services
import qs.Settings import qs.Settings
ShellRoot { ShellRoot {
property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.useSWWW ? WallpaperManager.currentWallpaper : "" property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.settings.useSWWW ? WallpaperManager.currentWallpaper : ""
PanelWindow { PanelWindow {
visible: wallpaperSource !== "" visible: wallpaperSource !== ""
anchors { anchors {

View file

@ -18,7 +18,7 @@ WlSessionLock {
property bool authenticating: false property bool authenticating: false
property string password: "" property string password: ""
property bool pamAvailable: typeof PamContext !== "undefined" property bool pamAvailable: typeof PamContext !== "undefined"
property string weatherCity: Settings.weatherCity property string weatherCity: Settings.settings.weatherCity
property var weatherData: null property var weatherData: null
property string weatherError: "" property string weatherError: ""
property string weatherInfo: "" property string weatherInfo: ""
@ -161,7 +161,7 @@ WlSessionLock {
id: avatarImage id: avatarImage
anchors.fill: parent anchors.fill: parent
anchors.margins: 4 anchors.margins: 4
source: Settings.profileImage source: Settings.settings.profileImage
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
visible: false // Only show the masked version visible: false // Only show the masked version
asynchronous: true asynchronous: true
@ -175,7 +175,7 @@ WlSessionLock {
radius: avatarImage.width / 2 radius: avatarImage.width / 2
visible: false visible: false
} }
visible: Settings.profileImage !== "" visible: Settings.settings.profileImage !== ""
} }
// Fallback icon // Fallback icon
Text { Text {
@ -184,7 +184,7 @@ WlSessionLock {
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 32 font.pixelSize: 32
color: Theme.onAccent color: Theme.onAccent
visible: Settings.profileImage === "" visible: Settings.settings.profileImage === ""
} }
// Glow effect // Glow effect
layer.enabled: true layer.enabled: true
@ -357,7 +357,7 @@ WlSessionLock {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
Text { Text {
text: weatherData && weatherData.current_weather ? (Settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.useFahrenheit ? "--°F" : "--°C") text: weatherData && weatherData.current_weather ? (Settings.settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.settings.useFahrenheit ? "--°F" : "--°C")
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 18 font.pixelSize: 18
color: Theme.textSecondary color: Theme.textSecondary

View file

@ -1,56 +1,30 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Components
import qs.Settings import qs.Settings
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import qs.Components
Item { // The popup window
id: root PanelWithOverlay {
property string configDir: Quickshell.configDir
property string historyFilePath: configDir + "/notification_history.json"
property bool hasUnread: notificationHistoryWin.hasUnread && !notificationHistoryWin.visible
function addToHistory(notification) { notificationHistoryWin.addToHistory(notification) }
width: 22; height: 22
// Bell icon/button
Item {
id: bell
width: 22; height: 22
Text {
anchors.centerIn: parent
text: root.hasUnread ? "notifications_unread" : "notifications"
font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 16
font.weight: root.hasUnread ? Font.Bold : Font.Normal
color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (root.hasUnread ? Theme.accentPrimary : Theme.textDisabled)
}
MouseArea {
id: mouseAreaBell
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: notificationHistoryWin.visible = !notificationHistoryWin.visible
}
}
// The popup window
PanelWindow {
id: notificationHistoryWin id: notificationHistoryWin
width: 400 property string historyFilePath: Settings.settingsDir + "notification_history.json"
property int maxPopupHeight: 500 property bool hasUnread: notificationHistoryWinRect.hasUnread && !notificationHistoryWinRect.visible
function addToHistory(notification) { notificationHistoryWinRect.addToHistory(notification) }
Rectangle {
id: notificationHistoryWinRect
implicitWidth: 400
property int maxPopupHeight: 800
property int minPopupHeight: 230 property int minPopupHeight: 230
property int contentHeight: headerRow.height + historyList.contentHeight + 56 property int contentHeight: headerRow.height + historyList.contentHeight + 56
height: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight) implicitHeight: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight)
color: "transparent" visible: parent.visible
visible: false anchors.top: parent.top
screen: Quickshell.primaryScreen anchors.right: parent.right
focusable: true anchors.topMargin: 4
anchors.top: true anchors.rightMargin: 4
anchors.right: true color: Theme.backgroundPrimary
margins.top: 4 radius: 20
margins.right: 4
property int maxHistory: 100 property int maxHistory: 100
property bool hasUnread: false property bool hasUnread: false
@ -62,7 +36,7 @@ Item {
FileView { FileView {
id: historyFileView id: historyFileView
path: root.historyFilePath path: notificationHistoryWin.historyFilePath
blockLoading: true blockLoading: true
printErrors: true printErrors: true
watchChanges: true watchChanges: true
@ -73,14 +47,13 @@ Item {
} }
onFileChanged: historyFileView.reload() onFileChanged: historyFileView.reload()
onLoaded: notificationHistoryWin.loadHistory() onLoaded: notificationHistoryWinRect.loadHistory()
onLoadFailed: function(error) { onLoadFailed: function (error) {
if (error.includes("No such file")) { historyAdapter.notifications = [];
historyAdapter.notifications = [] historyFileView.writeAdapter();
writeAdapter()
} }
} Component.onCompleted: if (path)
Component.onCompleted: if (path) reload() reload()
} }
function updateHasUnread() { function updateHasUnread() {
@ -99,16 +72,18 @@ Item {
function loadHistory() { function loadHistory() {
if (historyAdapter.notifications) { if (historyAdapter.notifications) {
historyModel.clear() historyModel.clear();
const notifications = historyAdapter.notifications const notifications = historyAdapter.notifications;
const count = Math.min(notifications.length, maxHistory) const count = Math.min(notifications.length, maxHistory);
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
let n = notifications[i] let n = notifications[i];
if (typeof n === 'object' && n !== null) { if (typeof n === 'object' && n !== null) {
if (n.read === undefined) n.read = false; if (n.read === undefined)
n.read = false;
// Mark as read if window is open // Mark as read if window is open
if (visible) n.read = true; if (notificationHistoryWinRect.visible)
historyModel.append(n) n.read = true;
historyModel.append(n);
} }
} }
updateHasUnread(); updateHasUnread();
@ -116,10 +91,10 @@ Item {
} }
function saveHistory() { function saveHistory() {
const historyArray = [] const historyArray = [];
const count = Math.min(historyModel.count, maxHistory) const count = Math.min(historyModel.count, maxHistory);
for (let i = 0; i < count; ++i) { for (let i = 0; i < count; ++i) {
let obj = historyModel.get(i) let obj = historyModel.get(i);
if (typeof obj === 'object' && obj !== null) { if (typeof obj === 'object' && obj !== null) {
historyArray.push({ historyArray.push({
id: obj.id, id: obj.id,
@ -128,51 +103,55 @@ Item {
body: obj.body, body: obj.body,
timestamp: obj.timestamp, timestamp: obj.timestamp,
read: obj.read === undefined ? false : obj.read read: obj.read === undefined ? false : obj.read
}) });
} }
} }
historyAdapter.notifications = historyArray historyAdapter.notifications = historyArray;
Qt.callLater(function() { Qt.callLater(function () {
historyFileView.writeAdapter() historyFileView.writeAdapter();
}) });
updateHasUnread(); updateHasUnread();
} }
function addToHistory(notification) { function addToHistory(notification) {
if (!notification.id) notification.id = Date.now() if (!notification.id)
if (!notification.timestamp) notification.timestamp = new Date().toISOString() notification.id = Date.now();
if (!notification.timestamp)
notification.timestamp = new Date().toISOString();
// Mark as read if window is open // Mark as read if window is open
notification.read = visible notification.read = visible;
// Remove duplicate by id // Remove duplicate by id
for (let i = 0; i < historyModel.count; ++i) { for (let i = 0; i < historyModel.count; ++i) {
if (historyModel.get(i).id === notification.id) { if (historyModel.get(i).id === notification.id) {
historyModel.remove(i) historyModel.remove(i);
break break;
} }
} }
historyModel.insert(0, notification) historyModel.insert(0, notification);
if (historyModel.count > maxHistory) historyModel.remove(maxHistory) if (historyModel.count > maxHistory)
saveHistory() historyModel.remove(maxHistory);
saveHistory();
} }
function clearHistory() { function clearHistory() {
historyModel.clear() historyModel.clear();
historyAdapter.notifications = [] historyAdapter.notifications = [];
historyFileView.writeAdapter() historyFileView.writeAdapter();
} }
function formatTimestamp(ts) { function formatTimestamp(ts) {
if (!ts) return ""; if (!ts)
return "";
var date = typeof ts === "number" ? new Date(ts) : new Date(Date.parse(ts)); var date = typeof ts === "number" ? new Date(ts) : new Date(Date.parse(ts));
var y = date.getFullYear(); var y = date.getFullYear();
var m = (date.getMonth()+1).toString().padStart(2,'0'); var m = (date.getMonth() + 1).toString().padStart(2, '0');
var d = date.getDate().toString().padStart(2,'0'); var d = date.getDate().toString().padStart(2, '0');
var h = date.getHours().toString().padStart(2,'0'); var h = date.getHours().toString().padStart(2, '0');
var min = date.getMinutes().toString().padStart(2,'0'); var min = date.getMinutes().toString().padStart(2, '0');
return `${y}-${m}-${d} ${h}:${min}`; return `${y}-${m}-${d} ${h}:${min}`;
} }
@ -186,13 +165,14 @@ Item {
changed = true; changed = true;
} }
} }
if (changed) saveHistory(); if (changed)
saveHistory();
} }
} }
Rectangle { Rectangle {
width: notificationHistoryWin.width width: notificationHistoryWinRect.width
height: notificationHistoryWin.height height: notificationHistoryWinRect.height
anchors.fill: parent anchors.fill: parent
color: Theme.backgroundPrimary color: Theme.backgroundPrimary
radius: 20 radius: 20
@ -205,10 +185,12 @@ Item {
RowLayout { RowLayout {
id: headerRow id: headerRow
spacing: 4 spacing: 4
anchors.top: parent.top
anchors.topMargin: 4 anchors.topMargin: 4
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 52
anchors.leftMargin: 16 anchors.leftMargin: 16
anchors.rightMargin: 16 anchors.rightMargin: 16
Text { Text {
@ -218,7 +200,9 @@ Item {
color: Theme.textPrimary color: Theme.textPrimary
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
} }
Item { Layout.fillWidth: true } Item {
Layout.fillWidth: true
}
Rectangle { Rectangle {
id: clearAllButton id: clearAllButton
width: 90 width: 90
@ -251,7 +235,7 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: notificationHistoryWin.clearHistory() onClicked: notificationHistoryWinRect.clearHistory()
} }
} }
} }
@ -264,11 +248,10 @@ Item {
} }
Rectangle { Rectangle {
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 56 anchors.topMargin: 56
anchors.bottom: parent.bottom height: notificationHistoryWinRect.height - 56 - 12
color: Theme.surfaceVariant color: Theme.surfaceVariant
radius: 20 radius: 20
@ -288,7 +271,10 @@ Item {
Column { Column {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
Item { id: topSpacer; height: (parent.height - historyList.height) / 2 } Item {
id: topSpacer
height: (parent.height - historyList.height) / 2
}
ListView { ListView {
id: historyList id: historyList
width: parent.width width: parent.width
@ -315,7 +301,7 @@ Item {
anchors.margins: 14 anchors.margins: 14
spacing: 6 spacing: 6
RowLayout { RowLayout {
id: headerRow id: headerRow2
spacing: 8 spacing: 8
Rectangle { Rectangle {
id: iconBackground id: iconBackground
@ -349,14 +335,16 @@ Item {
} }
Text { Text {
visible: !model.isPlaceholder visible: !model.isPlaceholder
text: model.timestamp ? notificationHistoryWin.formatTimestamp(model.timestamp) : "" text: model.timestamp ? notificationHistoryWinRect.formatTimestamp(model.timestamp) : ""
color: Theme.textDisabled color: Theme.textDisabled
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeCaption font.pixelSize: Theme.fontSizeCaption
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
} }
Item { Layout.fillWidth: true } Item {
Layout.fillWidth: true
}
} }
Text { Text {
text: model.summary || (model.isPlaceholder ? "You're all caught up!" : "") text: model.summary || (model.isPlaceholder ? "You're all caught up!" : "")
@ -382,7 +370,11 @@ Item {
} }
} }
Rectangle { width: 1; height: 24; color: "transparent" } Rectangle {
width: 1
height: 24
color: "transparent"
}
ListModel { ListModel {
id: placeholderModel id: placeholderModel

View file

@ -0,0 +1,30 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Settings
Item {
id: root
width: 22; height: 22
// Bell icon/button
Item {
id: bell
width: 22; height: 22
Text {
anchors.centerIn: parent
text: notificationHistoryWin.hasUnread ? "notifications_unread" : "notifications"
font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 16
font.weight: notificationHistoryWin.hasUnread ? Font.Bold : Font.Normal
color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (notificationHistoryWin.hasUnread ? Theme.accentPrimary : Theme.textDisabled)
}
MouseArea {
id: mouseAreaBell
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: notificationHistoryWin.visible = !notificationHistoryWin.visible
}
}
}

View file

@ -111,11 +111,7 @@ PanelWindow {
border.width: 1.5 border.width: 1.5
// Get all possible icon sources from notification // Get all possible icon sources from notification
property var iconSources: [ property var iconSources: [rawNotification?.image || "", rawNotification?.appIcon || "", rawNotification?.icon || ""]
rawNotification?.image || "",
rawNotification?.appIcon || "",
rawNotification?.icon || ""
]
// Try to load notification icon // Try to load notification icon
Image { Image {
@ -131,7 +127,8 @@ PanelWindow {
source: { source: {
for (var i = 0; i < iconBackground.iconSources.length; i++) { for (var i = 0; i < iconBackground.iconSources.length; i++) {
var icon = iconBackground.iconSources[i]; var icon = iconBackground.iconSources[i];
if (!icon) continue; if (!icon)
continue;
if (icon.includes("?path=")) { if (icon.includes("?path=")) {
const [name, path] = icon.split("?path="); const [name, path] = icon.split("?path=");
@ -202,7 +199,8 @@ PanelWindow {
repeat: false repeat: false
onTriggered: { onTriggered: {
dismissAnimation.start(); dismissAnimation.start();
if (rawNotification) rawNotification.expire(); if (rawNotification)
rawNotification.expire();
} }
} }
@ -210,28 +208,63 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
dismissAnimation.start(); dismissAnimation.start();
if (rawNotification) rawNotification.dismiss(); if (rawNotification)
rawNotification.dismiss();
} }
} }
ParallelAnimation { ParallelAnimation {
id: dismissAnimation id: dismissAnimation
NumberAnimation { target: notificationDelegate; property: "opacity"; to: 0; duration: 150 } NumberAnimation {
NumberAnimation { target: notificationDelegate; property: "height"; to: 0; duration: 150 } target: notificationDelegate
NumberAnimation { target: notificationDelegate; property: "x"; to: width; duration: 150; easing.type: Easing.InQuad } property: "opacity"
to: 0
duration: 150
}
NumberAnimation {
target: notificationDelegate
property: "height"
to: 0
duration: 150
}
NumberAnimation {
target: notificationDelegate
property: "x"
to: width
duration: 150
easing.type: Easing.InQuad
}
onFinished: { onFinished: {
var idx = notificationRepeater.indexOf(notificationDelegate); for (let i = 0; i < notificationModel.count; i++) {
if (idx !== -1) { if (notificationModel.get(i).id === notificationDelegate.id) {
notificationModel.remove(idx); notificationModel.remove(i);
break;
}
} }
} }
} }
ParallelAnimation { ParallelAnimation {
id: appearAnimation id: appearAnimation
NumberAnimation { target: notificationDelegate; property: "opacity"; to: 1; duration: 150 } NumberAnimation {
NumberAnimation { target: notificationDelegate; property: "height"; to: contentRow.height + 20; duration: 150 } target: notificationDelegate
NumberAnimation { target: notificationDelegate; property: "x"; to: 0; duration: 150; easing.type: Easing.OutQuad } property: "opacity"
to: 1
duration: 150
}
NumberAnimation {
target: notificationDelegate
property: "height"
to: contentRow.height + 20
duration: 150
}
NumberAnimation {
target: notificationDelegate
property: "x"
to: 0
duration: 150
easing.type: Easing.OutQuad
}
} }
Component.onCompleted: { Component.onCompleted: {
@ -240,18 +273,21 @@ PanelWindow {
height = 0; height = 0;
x = width; x = width;
appearAnimation.start(); appearAnimation.start();
var idx = notificationRepeater.indexOf(notificationDelegate); for (let i = 0; i < notificationModel.count; i++) {
if (idx !== -1) { if (notificationModel.get(i).id === notificationDelegate.id) {
var oldItem = notificationModel.get(idx); var oldItem = notificationModel.get(i);
notificationModel.set(idx, { notificationModel.set(i, {
id: oldItem.id, id: oldItem.id,
appName: oldItem.appName, appName: oldItem.appName,
summary: oldItem.summary, summary: oldItem.summary,
body: oldItem.body, body: oldItem.body,
rawNotification: oldItem.rawNotification, rawNotification: oldItem.rawNotification,
appeared: true, appeared: true,
read: oldItem.read,
dismissed: oldItem.dismissed dismissed: oldItem.dismissed
}); });
break;
}
} }
} }
} }
@ -263,7 +299,7 @@ PanelWindow {
target: Quickshell target: Quickshell
function onScreensChanged() { function onScreensChanged() {
if (window.screen) { if (window.screen) {
x = window.screen.width - width - 20 x = window.screen.width - width - 20;
} }
} }
} }

View file

@ -6,7 +6,7 @@ import qs.Services
import qs.Settings import qs.Settings
ShellRoot { ShellRoot {
property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.useSWWW ? WallpaperManager.currentWallpaper : "" property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.settings.useSWWW ? WallpaperManager.currentWallpaper : ""
PanelWindow { PanelWindow {
visible: wallpaperSource !== "" visible: wallpaperSource !== ""
anchors { anchors {

View file

@ -22,19 +22,6 @@ Item {
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
if (sidebarPopup.visible) { if (sidebarPopup.visible) {
// Close all modals if open
if (sidebarPopup.settingsModal && sidebarPopup.settingsModal.visible) {
sidebarPopup.settingsModal.visible = false;
}
if (sidebarPopup.wallpaperPanelModal && sidebarPopup.wallpaperPanelModal.visible) {
sidebarPopup.wallpaperPanelModal.visible = false;
}
if (sidebarPopup.wifiPanelModal && sidebarPopup.wifiPanelModal.visible) {
sidebarPopup.wifiPanelModal.visible = false;
}
if (sidebarPopup.bluetoothPanelModal && sidebarPopup.bluetoothPanelModal.visible) {
sidebarPopup.bluetoothPanelModal.visible = false;
}
sidebarPopup.hidePopup(); sidebarPopup.hidePopup();
} else { } else {
sidebarPopup.showAt(); sidebarPopup.showAt();

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import qs.Settings import qs.Settings
ColumnLayout { ColumnLayout {

View file

@ -1,47 +1,38 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import qs.Settings import qs.Settings
Rectangle { Rectangle {
id: profileSettingsCard id: profileSettingsCard
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 340 Layout.preferredHeight: 500
color: Theme.surface color: Theme.surface
Layout.topMargin: 12
radius: 18 radius: 18
border.color: "transparent" border.color: "transparent"
border.width: 0 border.width: 0
Layout.bottomMargin: 16 Layout.bottomMargin: 16
property bool showActiveWindowIcon: false
signal showAWIconChanged(bool showActiveWindowIcon)
property bool showSystemInfoInBar: true
signal showSystemInfoChanged(bool showSystemInfoInBar)
property bool showMediaInBar: false
signal showMediaChanged(bool showMediaInBar)
property string visualizerType: Settings.visualizerType
signal visualizerTypeUpdated(string type)
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 18 anchors.margins: 18
spacing: 12 spacing: 12
// Profile Image Header
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: 12 spacing: 12
Text { Text {
text: "person" text: "settings"
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 20 font.pixelSize: 20
color: Theme.accentPrimary color: Theme.accentPrimary
} }
Text { Text {
text: "Profile Image" text: "System Settings"
font.family: Theme.fontFamily
font.pixelSize: 16 font.pixelSize: 16
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
@ -49,15 +40,27 @@ Rectangle {
} }
} }
// Profile Image Input Row // Profile Image Input Section
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Text {
text: "Profile Image"
font.family: Theme.fontFamily
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
RowLayout { RowLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Rectangle { Rectangle {
width: 36 width: 40
height: 36 height: 40
radius: 18 radius: 20
color: Theme.surfaceVariant color: Theme.surfaceVariant
border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline
border.width: 1 border.width: 1
@ -66,7 +69,7 @@ Rectangle {
id: avatarImage id: avatarImage
anchors.fill: parent anchors.fill: parent
anchors.margins: 2 anchors.margins: 2
source: Settings.profileImage source: Settings.settings.profileImage
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
visible: false visible: false
asynchronous: true asynchronous: true
@ -83,7 +86,7 @@ Rectangle {
radius: avatarImage.width / 2 radius: avatarImage.width / 2
visible: false visible: false
} }
visible: Settings.profileImage !== "" visible: Settings.settings.profileImage !== ""
} }
// Fallback icon // Fallback icon
@ -91,16 +94,15 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
text: "person" text: "person"
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 18 font.pixelSize: 20
color: Theme.accentPrimary color: Theme.accentPrimary
visible: Settings.profileImage === "" visible: Settings.settings.profileImage === ""
} }
} }
// Text input styled exactly like weather city
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 36 Layout.preferredHeight: 40
radius: 8 radius: 8
color: Theme.surfaceVariant color: Theme.surfaceVariant
border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline
@ -108,9 +110,16 @@ Rectangle {
TextInput { TextInput {
id: profileImageInput id: profileImageInput
anchors.fill: parent anchors.left: parent.left
anchors.margins: 12 anchors.right: parent.right
text: Settings.profileImage anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.leftMargin: 12
anchors.rightMargin: 12
anchors.topMargin: 6
anchors.bottomMargin: 6
text: Settings.settings.profileImage
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
@ -120,7 +129,7 @@ Rectangle {
activeFocusOnTab: true activeFocusOnTab: true
inputMethodHints: Qt.ImhNone inputMethodHints: Qt.ImhNone
onTextChanged: { onTextChanged: {
Settings.profileImage = text Settings.settings.profileImage = text
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@ -131,14 +140,17 @@ Rectangle {
} }
} }
} }
}
// Show Active Window Icon Setting // Show Active Window Icon Setting
RowLayout { RowLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
Text { Text {
text: "Show Active Window Icon" text: "Show Active Window Icon"
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
@ -154,8 +166,8 @@ Rectangle {
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
color: showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant
border.color: showActiveWindowIcon ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -167,7 +179,7 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: showActiveWindowIcon ? customSwitch.width - width - 2 : 2 x: Settings.settings.showActiveWindowIcon ? customSwitch.width - width - 2 : 2
Behavior on x { Behavior on x {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic } NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
@ -177,7 +189,7 @@ Rectangle {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
showAWIconChanged(!showActiveWindowIcon) Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon
} }
} }
} }
@ -187,9 +199,11 @@ Rectangle {
RowLayout { RowLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
Text { Text {
text: "Show System Info In Bar" text: "Show System Info In Bar"
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
@ -206,8 +220,8 @@ Rectangle {
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
color: showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant
border.color: showSystemInfoInBar ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -219,7 +233,7 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: showSystemInfoInBar ? customSwitch2.width - width - 2 : 2 x: Settings.settings.showSystemInfoInBar ? customSwitch2.width - width - 2 : 2
Behavior on x { Behavior on x {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic } NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
@ -229,7 +243,7 @@ Rectangle {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
showSystemInfoChanged(!showSystemInfoInBar) Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar
} }
} }
} }
@ -239,9 +253,11 @@ Rectangle {
RowLayout { RowLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
Text { Text {
text: "Show Media In Bar" text: "Show Media In Bar"
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
@ -258,8 +274,8 @@ Rectangle {
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
color: showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant
border.color: showMediaInBar ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -271,7 +287,7 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: showMediaInBar ? customSwitch3.width - width - 2 : 2 x: Settings.settings.showMediaInBar ? customSwitch3.width - width - 2 : 2
Behavior on x { Behavior on x {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic } NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
@ -281,110 +297,125 @@ Rectangle {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
showMediaChanged(!showMediaInBar) Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar
} }
} }
} }
} }
// Visualizer Type Selection // Visualizer Type Selection
RowLayout { ColumnLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Text { Text {
text: "Visualizer Type" text: "Visualizer Type"
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
Layout.alignment: Qt.AlignVCenter
} }
Item { ComboBox {
id: visualizerTypeComboBox
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 40
model: ["radial", "fire", "diamond"]
currentIndex: model.indexOf(Settings.settings.visualizerType)
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
color: Theme.surfaceVariant
border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
border.width: 1
radius: 8
} }
// Dropdown for visualizer type contentItem: Text {
Rectangle { leftPadding: 12
width: 120 rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing
height: 36 text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1)
radius: 8 font.family: Theme.fontFamily
font.pixelSize: 13
color: Theme.textPrimary
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
indicator: Text {
x: visualizerTypeComboBox.width - width - 12
y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2
text: "arrow_drop_down"
font.family: "Material Symbols Outlined"
font.pixelSize: 24
color: Theme.textPrimary
}
popup: Popup {
y: visualizerTypeComboBox.height
width: visualizerTypeComboBox.width
implicitHeight: contentItem.implicitHeight
padding: 1
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null
currentIndex: visualizerTypeComboBox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator {}
}
background: Rectangle {
color: Theme.surfaceVariant color: Theme.surfaceVariant
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
radius: 8
}
}
Text { delegate: ItemDelegate {
id: visualizerTypeText width: visualizerTypeComboBox.width
anchors.left: parent.left contentItem: Text {
anchors.leftMargin: 12 text: modelData.charAt(0).toUpperCase() + modelData.slice(1)
anchors.verticalCenter: parent.verticalCenter font.family: Theme.fontFamily
text: visualizerType === "fire" ? "Fire" :
visualizerType === "diamond" ? "Diamond" :
visualizerType === "radial" ? "Radial" : "Radial"
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
} }
highlighted: visualizerTypeComboBox.highlightedIndex === index
Text { background: Rectangle {
text: "arrow_drop_down" color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
font.family: "Material Symbols Outlined"
font.pixelSize: 20
color: Theme.textPrimary
anchors.right: parent.right
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
visualizerTypeMenu.open()
} }
} }
Menu { onActivated: {
id: visualizerTypeMenu Settings.settings.visualizerType = model[index];
width: 120
y: parent.height
MenuItem {
text: "Fire"
onTriggered: {
visualizerTypeUpdated("fire")
}
}
MenuItem {
text: "Diamond"
onTriggered: {
visualizerTypeUpdated("diamond")
}
}
MenuItem {
text: "Radial"
onTriggered: {
visualizerTypeUpdated("radial")
}
}
} }
} }
} }
// Video Path Input Row // Video Path Input Section
RowLayout { ColumnLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
Text { Text {
text: "Video Path" text: "Video Path"
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
Layout.alignment: Qt.AlignVCenter
} }
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 36 Layout.preferredHeight: 40
radius: 8 radius: 8
color: Theme.surfaceVariant color: Theme.surfaceVariant
border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline
@ -392,9 +423,16 @@ Rectangle {
TextInput { TextInput {
id: videoPathInput id: videoPathInput
anchors.fill: parent anchors.left: parent.left
anchors.margins: 12 anchors.right: parent.right
text: Settings.videoPath !== undefined ? Settings.videoPath : "" anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.leftMargin: 12
anchors.rightMargin: 12
anchors.topMargin: 6
anchors.bottomMargin: 6
text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : ""
font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
@ -403,7 +441,7 @@ Rectangle {
activeFocusOnTab: true activeFocusOnTab: true
inputMethodHints: Qt.ImhUrlCharactersOnly inputMethodHints: Qt.ImhUrlCharactersOnly
onTextChanged: { onTextChanged: {
Settings.videoPath = text Settings.settings.videoPath = text
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import qs.Settings import qs.Settings
@ -20,25 +20,7 @@ PanelWindow {
//z: 100 //z: 100
//border.color: Theme.outline //border.color: Theme.outline
//border.width: 1 //border.width: 1
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
// Local properties for editing (not saved until apply)
property string tempWeatherCity: (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : ""
property bool tempUseFahrenheit: Settings.useFahrenheit
property string tempProfileImage: (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : ""
property string tempWallpaperFolder: (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : ""
property bool tempShowActiveWindowIcon: Settings.showActiveWindowIcon
property bool tempUseSWWW: Settings.useSWWW
property bool tempRandomWallpaper: Settings.randomWallpaper
property bool tempUseWallpaperTheme: Settings.useWallpaperTheme
property int tempWallpaperInterval: Settings.wallpaperInterval
property string tempWallpaperResize: Settings.wallpaperResize
property int tempTransitionFps: Settings.transitionFps
property string tempTransitionType: Settings.transitionType
property real tempTransitionDuration: Settings.transitionDuration
property bool tempShowSystemInfoInBar: Settings.showSystemInfoInBar
property bool tempShowMediaInBar: Settings.showMediaInBar
property string tempVisualizerType: Settings.visualizerType
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
@ -49,12 +31,17 @@ PanelWindow {
z: 0 z: 0
ColumnLayout { ColumnLayout {
id: content
anchors.fill: parent anchors.fill: parent
anchors.margins: 32 anchors.leftMargin: 32
anchors.rightMargin: 32
anchors.topMargin: 32
spacing: 24 spacing: 24
// Header // Header
ColumnLayout { ColumnLayout {
id: header
Layout.fillWidth: true Layout.fillWidth: true
spacing: 4 spacing: 4
RowLayout { RowLayout {
@ -117,7 +104,7 @@ PanelWindow {
// Scrollable settings area // Scrollable settings area
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 520 Layout.preferredHeight: content.height - settingsTabs.height - header.height - 128
color: "transparent" color: "transparent"
border.width: 0 border.width: 0
radius: 20 radius: 20
@ -145,14 +132,6 @@ PanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
anchors.margins: 16 anchors.margins: 16
weatherCity: (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : ""
useFahrenheit: tempUseFahrenheit
onCityChanged: function (city) {
tempWeatherCity = city;
}
onTemperatureUnitChanged: function (useFahrenheit) {
tempUseFahrenheit = useFahrenheit;
}
} }
} }
} }
@ -164,22 +143,6 @@ PanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
anchors.margins: 16 anchors.margins: 16
showActiveWindowIcon: tempShowActiveWindowIcon
onShowAWIconChanged: function (showActiveWindowIcon) {
tempShowActiveWindowIcon = showActiveWindowIcon;
}
showSystemInfoInBar: tempShowSystemInfoInBar
onShowSystemInfoChanged: function (showSystemInfoInBar) {
tempShowSystemInfoInBar = showSystemInfoInBar;
}
showMediaInBar: tempShowMediaInBar
onShowMediaChanged: function (showMediaInBar) {
tempShowMediaInBar = showMediaInBar;
}
visualizerType: tempVisualizerType
onVisualizerTypeUpdated: function (type) {
tempVisualizerType = type;
}
} }
} }
} }
@ -192,89 +155,7 @@ PanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
anchors.margins: 16 anchors.margins: 16
wallpaperFolder: (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : ""
useSWWW: tempUseSWWW
randomWallpaper: tempRandomWallpaper
useWallpaperTheme: tempUseWallpaperTheme
wallpaperInterval: tempWallpaperInterval
wallpaperResize: tempWallpaperResize
transitionFps: tempTransitionFps
transitionType: tempTransitionType
transitionDuration: tempTransitionDuration
onWallpaperFolderEdited: function (folder) {
tempWallpaperFolder = folder;
} }
onUseSWWWChangedUpdated: function(useSWWW) {
tempUseSWWW = useSWWW;
}
onRandomWallpaperChangedUpdated: function(randomWallpaper) {
tempRandomWallpaper = randomWallpaper;
}
onUseWallpaperThemeChangedUpdated: function(useWallpaperTheme) {
tempUseWallpaperTheme = useWallpaperTheme;
}
onWallpaperIntervalChangedUpdated: function(wallpaperInterval) {
tempWallpaperInterval = wallpaperInterval;
}
onWallpaperResizeChangedUpdated: function(resize) {
tempWallpaperResize = resize;
}
onTransitionFpsChangedUpdated: function(fps) {
tempTransitionFps = fps;
}
onTransitionTypeChangedUpdated: function(type) {
tempTransitionType = type;
}
onTransitionDurationChangedUpdated: function(duration) {
tempTransitionDuration = duration;
}
}
}
}
}
// Apply Button
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 52
radius: 16
color: applyButtonArea.containsMouse ? Theme.accentPrimary : Theme.accentPrimary
border.color: "transparent"
border.width: 0
opacity: 1.0
Text {
anchors.centerIn: parent
text: "Apply Changes"
font.pixelSize: 17
font.bold: true
color: applyButtonArea.containsMouse ? Theme.onAccent : Theme.onAccent
}
MouseArea {
id: applyButtonArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
Settings.weatherCity = (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : "";
Settings.useFahrenheit = tempUseFahrenheit;
Settings.profileImage = (typeof tempProfileImage !== 'undefined' && tempProfileImage !== null) ? tempProfileImage : "";
Settings.wallpaperFolder = (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : "";
Settings.showActiveWindowIcon = tempShowActiveWindowIcon;
Settings.useSWWW = tempUseSWWW;
Settings.randomWallpaper = tempRandomWallpaper;
Settings.useWallpaperTheme = tempUseWallpaperTheme;
Settings.wallpaperInterval = tempWallpaperInterval;
Settings.wallpaperResize = tempWallpaperResize;
Settings.transitionFps = tempTransitionFps;
Settings.transitionType = tempTransitionType;
Settings.transitionDuration = tempTransitionDuration;
Settings.showSystemInfoInBar = tempShowSystemInfoInBar;
Settings.showMediaInBar = tempShowMediaInBar;
Settings.visualizerType = tempVisualizerType;
Settings.saveSettings();
if (typeof weather !== 'undefined' && weather) {
weather.fetchCityWeather();
}
settingsModal.closeSettings();
} }
} }
} }
@ -283,27 +164,6 @@ PanelWindow {
// Function to open the modal and initialize temp values // Function to open the modal and initialize temp values
function openSettings() { function openSettings() {
tempWeatherCity = (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : "";
tempUseFahrenheit = Settings.useFahrenheit;
tempShowActiveWindowIcon = Settings.showActiveWindowIcon;
tempProfileImage = (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : "";
tempWallpaperFolder = (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : "";
if (tempWallpaperFolder === undefined || tempWallpaperFolder === null)
tempWallpaperFolder = "";
// Initialize wallpaper settings
tempUseSWWW = Settings.useSWWW;
tempRandomWallpaper = Settings.randomWallpaper;
tempUseWallpaperTheme = Settings.useWallpaperTheme;
tempWallpaperInterval = Settings.wallpaperInterval;
tempWallpaperResize = Settings.wallpaperResize;
tempTransitionFps = Settings.transitionFps;
tempTransitionType = Settings.transitionType;
tempTransitionDuration = Settings.transitionDuration;
tempShowSystemInfoInBar = Settings.showSystemInfoInBar;
tempShowMediaInBar = Settings.showMediaInBar;
tempVisualizerType = Settings.visualizerType;
visible = true; visible = true;
// Force focus on the text input after a short delay // Force focus on the text input after a short delay
focusTimer.start(); focusTimer.start();

View file

@ -1,36 +1,15 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import qs.Settings import qs.Settings
Rectangle { Rectangle {
id: wallpaperSettingsCard id: wallpaperSettingsCard
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 680 Layout.preferredHeight: 720
Layout.topMargin: 12
color: Theme.surface color: Theme.surface
radius: 18 radius: 18
// Property for binding
property string wallpaperFolder: ""
signal wallpaperFolderEdited(string folder)
property bool useSWWW: false
signal useSWWWChangedUpdated(bool useSWWW)
property bool randomWallpaper: false
signal randomWallpaperChangedUpdated(bool randomWallpaper)
property bool useWallpaperTheme: false
signal useWallpaperThemeChangedUpdated(bool useWallpaperTheme)
property int wallpaperInterval: 300
signal wallpaperIntervalChangedUpdated(int wallpaperInterval)
property string wallpaperResize: "crop"
signal wallpaperResizeChangedUpdated(string resize)
property int transitionFps: 60
signal transitionFpsChangedUpdated(int fps)
property string transitionType: "random"
signal transitionTypeChangedUpdated(string type)
property real transitionDuration: 1.1
signal transitionDurationChangedUpdated(real duration)
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 18 anchors.margins: 18
@ -47,7 +26,7 @@ Rectangle {
color: Theme.accentPrimary color: Theme.accentPrimary
} }
Text { Text {
text: "Wallpaper Folder" text: "Wallpaper Settings"
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 16 font.pixelSize: 16
font.bold: true font.bold: true
@ -56,6 +35,18 @@ Rectangle {
} }
} }
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Text {
text: "Wallpaper Path"
font.family: Theme.fontFamily
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
// Folder Path Input // Folder Path Input
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
@ -74,7 +65,7 @@ Rectangle {
anchors.rightMargin: 12 anchors.rightMargin: 12
anchors.topMargin: 6 anchors.topMargin: 6
anchors.bottomMargin: 6 anchors.bottomMargin: 6
text: wallpaperFolder text: Settings.settings.wallpaperFolder
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
@ -84,7 +75,7 @@ Rectangle {
activeFocusOnTab: true activeFocusOnTab: true
inputMethodHints: Qt.ImhUrlCharactersOnly inputMethodHints: Qt.ImhUrlCharactersOnly
onTextChanged: { onTextChanged: {
wallpaperFolderEdited(text) Settings.settings.wallpaperFolder = text;
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@ -92,6 +83,7 @@ Rectangle {
} }
} }
} }
}
// Use SWWW Setting // Use SWWW Setting
RowLayout { RowLayout {
@ -116,8 +108,8 @@ Rectangle {
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
color: useSWWW ? Theme.accentPrimary : Theme.surfaceVariant color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant
border.color: useSWWW ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -129,17 +121,20 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: useSWWW ? swwwSwitch.width - width - 2 : 2 x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2
Behavior on x { Behavior on x {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic } NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
} }
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
useSWWWChangedUpdated(!useSWWW) Settings.settings.useSWWW = !Settings.settings.useSWWW;
} }
} }
} }
@ -168,8 +163,8 @@ Rectangle {
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
color: randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant
border.color: randomWallpaper ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -181,17 +176,20 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2
Behavior on x { Behavior on x {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic } NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
} }
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
randomWallpaperChangedUpdated(!randomWallpaper) Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper;
} }
} }
} }
@ -220,8 +218,8 @@ Rectangle {
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
color: useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant
border.color: useWallpaperTheme ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -233,17 +231,20 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2
Behavior on x { Behavior on x {
NumberAnimation { duration: 200; easing.type: Easing.OutCubic } NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
} }
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
useWallpaperThemeChangedUpdated(!useWallpaperTheme) Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme;
} }
} }
} }
@ -269,7 +270,7 @@ Rectangle {
} }
Text { Text {
text: wallpaperInterval text: Settings.settings.wallpaperInterval
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
} }
@ -281,7 +282,7 @@ Rectangle {
from: 10 from: 10
to: 900 to: 900
stepSize: 10 stepSize: 10
value: wallpaperInterval value: Settings.settings.wallpaperInterval
snapMode: Slider.SnapAlways snapMode: Slider.SnapAlways
background: Rectangle { background: Rectangle {
@ -314,7 +315,7 @@ Rectangle {
} }
onMoved: { onMoved: {
wallpaperIntervalChangedUpdated(Math.round(value)) Settings.settings.wallpaperInterval = Math.round(value);
} }
} }
} }
@ -337,7 +338,7 @@ Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 40 Layout.preferredHeight: 40
model: ["no", "crop", "fit", "stretch"] model: ["no", "crop", "fit", "stretch"]
currentIndex: model.indexOf(wallpaperResize) currentIndex: model.indexOf(Settings.settings.wallpaperResize)
background: Rectangle { background: Rectangle {
implicitWidth: 120 implicitWidth: 120
@ -380,7 +381,7 @@ Rectangle {
model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null
currentIndex: resizeComboBox.highlightedIndex currentIndex: resizeComboBox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { } ScrollIndicator.vertical: ScrollIndicator {}
} }
background: Rectangle { background: Rectangle {
@ -409,7 +410,7 @@ Rectangle {
} }
onActivated: { onActivated: {
wallpaperResizeChangedUpdated(model[index]) Settings.settings.wallpaperResize = model[index];
} }
} }
} }
@ -432,7 +433,7 @@ Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 40 Layout.preferredHeight: 40
model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"]
currentIndex: model.indexOf(transitionType) currentIndex: model.indexOf(Settings.settings.transitionType)
background: Rectangle { background: Rectangle {
implicitWidth: 120 implicitWidth: 120
@ -475,7 +476,7 @@ Rectangle {
model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null
currentIndex: transitionTypeComboBox.highlightedIndex currentIndex: transitionTypeComboBox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { } ScrollIndicator.vertical: ScrollIndicator {}
} }
background: Rectangle { background: Rectangle {
@ -504,7 +505,7 @@ Rectangle {
} }
onActivated: { onActivated: {
transitionTypeChangedUpdated(model[index]) Settings.settings.transitionType = model[index];
} }
} }
} }
@ -529,7 +530,7 @@ Rectangle {
} }
Text { Text {
text: transitionFps text: Settings.settings.transitionFps
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
} }
@ -541,7 +542,7 @@ Rectangle {
from: 30 from: 30
to: 500 to: 500
stepSize: 5 stepSize: 5
value: transitionFps value: Settings.settings.transitionFps
snapMode: Slider.SnapAlways snapMode: Slider.SnapAlways
background: Rectangle { background: Rectangle {
@ -574,7 +575,7 @@ Rectangle {
} }
onMoved: { onMoved: {
transitionFpsChangedUpdated(Math.round(value)) Settings.settings.transitionFps = Math.round(value);
} }
} }
} }
@ -599,7 +600,7 @@ Rectangle {
} }
Text { Text {
text: transitionDuration.toFixed(3) text: Settings.settings.transitionDuration.toFixed(3)
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
} }
@ -611,7 +612,7 @@ Rectangle {
from: 0.250 from: 0.250
to: 10.0 to: 10.0
stepSize: 0.050 stepSize: 0.050
value: transitionDuration value: Settings.settings.transitionDuration
snapMode: Slider.SnapAlways snapMode: Slider.SnapAlways
background: Rectangle { background: Rectangle {
@ -644,7 +645,7 @@ Rectangle {
} }
onMoved: { onMoved: {
transitionDurationChangedUpdated(value) Settings.settings.transitionDuration = value;
} }
} }
} }

View file

@ -1,23 +1,14 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15
import qs.Settings import qs.Settings
Rectangle { Rectangle {
id: weatherSettingsCard id: weatherSettingsCard
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 180 Layout.preferredHeight: 180
Layout.topMargin: 12
color: Theme.surface color: Theme.surface
radius: 18 radius: 18
// Properties for binding
property string weatherCity: ""
property bool useFahrenheit: false
signal cityChanged(string city)
signal temperatureUnitChanged(bool useFahrenheit)
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 18 anchors.margins: 18
@ -76,7 +67,7 @@ Rectangle {
anchors.rightMargin: 12 anchors.rightMargin: 12
anchors.topMargin: 6 anchors.topMargin: 6
anchors.bottomMargin: 6 anchors.bottomMargin: 6
text: weatherCity text: Settings.settings.weatherCity
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 13 font.pixelSize: 13
color: Theme.textPrimary color: Theme.textPrimary
@ -88,7 +79,7 @@ Rectangle {
inputMethodHints: Qt.ImhNone inputMethodHints: Qt.ImhNone
onTextChanged: { onTextChanged: {
cityChanged(text) Settings.settings.weatherCity = text
} }
MouseArea { MouseArea {
@ -137,11 +128,11 @@ Rectangle {
border.color: Theme.outline border.color: Theme.outline
border.width: 1 border.width: 1
y: 2 y: 2
x: useFahrenheit ? customSwitch.width - width - 2 : 2 x: Settings.settings.useFahrenheit ? customSwitch.width - width - 2 : 2
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: useFahrenheit ? "\u00b0F" : "\u00b0C" text: Settings.settings.useFahrenheit ? "\u00b0F" : "\u00b0C"
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 12 font.pixelSize: 12
font.bold: true font.bold: true
@ -156,7 +147,7 @@ Rectangle {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
temperatureUnitChanged(!useFahrenheit) Settings.settings.useFahrenheit = !Settings.settings.useFahrenheit
} }
} }
} }

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell import Quickshell
import Quickshell.Bluetooth import Quickshell.Bluetooth
@ -80,7 +80,7 @@ Item {
anchors.right: true anchors.right: true
margins.right: 0 margins.right: 0
margins.top: 0 margins.top: 0
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
onVisibleChanged: { onVisibleChanged: {
if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering)

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import qs.Settings import qs.Settings
import qs.Components import qs.Components

View file

@ -7,37 +7,68 @@ import qs.Settings
import qs.Widgets.Sidebar.Config import qs.Widgets.Sidebar.Config
import qs.Components import qs.Components
PanelWindow { PanelWithOverlay {
id: panelPopup id: sidebarPopup
function showAt() {
sidebarPopupRect.showAt();
}
function hidePopup() {
sidebarPopupRect.hidePopup();
}
function show() {
sidebarPopupRect.showAt();
}
function dismiss() {
sidebarPopupRect.hidePopup();
}
Rectangle {
id: sidebarPopupRect
implicitWidth: 500 implicitWidth: 500
implicitHeight: 800 implicitHeight: 800
visible: false visible: parent.visible
color: "transparent" color: "transparent"
screen: modelData anchors.top: parent.top
anchors.top: true anchors.right: parent.right
anchors.right: true
margins.top: 0
WlrLayershell.keyboardFocus: (settingsModal.visible && mouseArea.containsMouse) ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
// Animation properties // Animation properties
property real slideOffset: width property real slideOffset: width
property bool isAnimating: false property bool isAnimating: false
function showAt() { function showAt() {
if (!visible) { if (!sidebarPopup.visible) {
visible = true; sidebarPopup.visible = true;
forceActiveFocus(); forceActiveFocus();
slideAnim.from = width; slideAnim.from = width;
slideAnim.to = 0; slideAnim.to = 0;
slideAnim.running = true; slideAnim.running = true;
if (weather) weather.startWeatherFetch(); if (weather)
if (systemWidget) systemWidget.panelVisible = true; weather.startWeatherFetch();
if (quickAccessWidget) quickAccessWidget.panelVisible = true; if (systemWidget)
systemWidget.panelVisible = true;
if (quickAccessWidget)
quickAccessWidget.panelVisible = true;
} }
} }
function hidePopup() { function hidePopup() {
if (visible) { if (sidebarPopupRect.settingsModal && sidebarPopupRect.settingsModal.visible) {
sidebarPopupRect.settingsModal.visible = false;
}
if (sidebarPopupRect.wallpaperPanelModal && sidebarPopupRect.wallpaperPanelModal.visible) {
sidebarPopupRect.wallpaperPanelModal.visible = false;
}
if (sidebarPopupRect.wifiPanelModal && sidebarPopupRect.wifiPanelModal.visible) {
sidebarPopupRect.wifiPanelModal.visible = false;
}
if (sidebarPopupRect.bluetoothPanelModal && sidebarPopupRect.bluetoothPanelModal.visible) {
sidebarPopupRect.bluetoothPanelModal.visible = false;
}
if (sidebarPopup.visible) {
slideAnim.from = 0; slideAnim.from = 0;
slideAnim.to = width; slideAnim.to = width;
slideAnim.running = true; slideAnim.running = true;
@ -46,24 +77,27 @@ PanelWindow {
NumberAnimation { NumberAnimation {
id: slideAnim id: slideAnim
target: panelPopup target: sidebarPopupRect
property: "slideOffset" property: "slideOffset"
duration: 300 duration: 300
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
onStopped: { onStopped: {
if (panelPopup.slideOffset === panelPopup.width) { if (sidebarPopupRect.slideOffset === sidebarPopupRect.width) {
panelPopup.visible = false; sidebarPopup.visible = false;
// Stop monitoring and background tasks when hidden // Stop monitoring and background tasks when hidden
if (weather) weather.stopWeatherFetch(); if (weather)
if (systemWidget) systemWidget.panelVisible = false; weather.stopWeatherFetch();
if (quickAccessWidget) quickAccessWidget.panelVisible = false; if (systemWidget)
systemWidget.panelVisible = false;
if (quickAccessWidget)
quickAccessWidget.panelVisible = false;
} }
panelPopup.isAnimating = false; sidebarPopupRect.isAnimating = false;
} }
onStarted: { onStarted: {
panelPopup.isAnimating = true; sidebarPopupRect.isAnimating = true;
} }
} }
@ -72,18 +106,21 @@ PanelWindow {
Rectangle { Rectangle {
id: mainRectangle id: mainRectangle
width: parent.width - leftPadding width: sidebarPopupRect.width - sidebarPopupRect.leftPadding
height: parent.height - bottomPadding height: sidebarPopupRect.height - sidebarPopupRect.bottomPadding
anchors.top: parent.top anchors.top: sidebarPopupRect.top
x: leftPadding + slideOffset x: sidebarPopupRect.leftPadding + sidebarPopupRect.slideOffset
y: 0 y: 0
color: Theme.backgroundPrimary color: Theme.backgroundPrimary
bottomLeftRadius: 20 bottomLeftRadius: 20
z: 0 z: 0
Behavior on x { Behavior on x {
enabled: !panelPopup.isAnimating enabled: !sidebarPopupRect.isAnimating
NumberAnimation { duration: 300; easing.type: Easing.OutCubic } NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
} }
} }
@ -97,11 +134,14 @@ PanelWindow {
Item { Item {
anchors.fill: mainRectangle anchors.fill: mainRectangle
x: slideOffset x: sidebarPopupRect.slideOffset
Behavior on x { Behavior on x {
enabled: !panelPopup.isAnimating enabled: !sidebarPopupRect.isAnimating
NumberAnimation { duration: 300; easing.type: Easing.OutCubic } NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
} }
MouseArea { MouseArea {
@ -171,7 +211,8 @@ PanelWindow {
// Wifi button // Wifi button
Rectangle { Rectangle {
id: wifiButton id: wifiButton
width: 36; height: 36 width: 36
height: 36
radius: 18 radius: 18
border.color: Theme.accentPrimary border.color: Theme.accentPrimary
border.width: 1 border.width: 1
@ -182,9 +223,7 @@ PanelWindow {
text: "wifi" text: "wifi"
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 22 font.pixelSize: 22
color: wifiButtonArea.containsMouse color: wifiButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
? Theme.backgroundPrimary
: Theme.accentPrimary
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
@ -201,7 +240,8 @@ PanelWindow {
// Bluetooth button // Bluetooth button
Rectangle { Rectangle {
id: bluetoothButton id: bluetoothButton
width: 36; height: 36 width: 36
height: 36
radius: 18 radius: 18
border.color: Theme.accentPrimary border.color: Theme.accentPrimary
border.width: 1 border.width: 1
@ -212,9 +252,7 @@ PanelWindow {
text: "bluetooth" text: "bluetooth"
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 22 font.pixelSize: 22
color: bluetoothButtonArea.containsMouse color: bluetoothButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
? Theme.backgroundPrimary
: Theme.accentPrimary
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
@ -251,39 +289,30 @@ PanelWindow {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -16 Layout.topMargin: -16
z: 2 z: 2
isRecording: panelPopup.isRecording isRecording: sidebarPopupRect.isRecording
onRecordingRequested: { onRecordingRequested: {
startRecording() startRecording();
} }
onStopRecordingRequested: { onStopRecordingRequested: {
stopRecording() stopRecording();
} }
onRecordingStateMismatch: function(actualState) { onRecordingStateMismatch: function (actualState) {
isRecording = actualState isRecording = actualState;
quickAccessWidget.isRecording = actualState quickAccessWidget.isRecording = actualState;
} }
onSettingsRequested: { onSettingsRequested: {
settingsModal.visible = true settingsModal.visible = true;
} }
onWallpaperRequested: { onWallpaperRequested: {
wallpaperPanelModal.visible = true wallpaperPanelModal.visible = true;
} }
} }
} }
Keys.onEscapePressed: panelPopup.hidePopup() Keys.onEscapePressed: sidebarPopupRect.hidePopup()
}
onVisibleChanged: if (!visible) {/* cleanup if needed */}
// Update height when screen changes
onScreenChanged: {
if (screen) {
// Height is now hardcoded to 720, no need to update
}
} }
// Recording properties // Recording properties
@ -293,59 +322,59 @@ PanelWindow {
// Start screen recording // Start screen recording
function startRecording() { function startRecording() {
var currentDate = new Date() var currentDate = new Date();
var hours = String(currentDate.getHours()).padStart(2, '0') var hours = String(currentDate.getHours()).padStart(2, '0');
var minutes = String(currentDate.getMinutes()).padStart(2, '0') var minutes = String(currentDate.getMinutes()).padStart(2, '0');
var day = String(currentDate.getDate()).padStart(2, '0') var day = String(currentDate.getDate()).padStart(2, '0');
var month = String(currentDate.getMonth() + 1).padStart(2, '0') var month = String(currentDate.getMonth() + 1).padStart(2, '0');
var year = currentDate.getFullYear() var year = currentDate.getFullYear();
var filename = hours + "-" + minutes + "-" + day + "-" + month + "-" + year + ".mp4" var filename = hours + "-" + minutes + "-" + day + "-" + month + "-" + year + ".mp4";
var outputPath = Settings.videoPath + filename var outputPath = Settings.settings.videoPath + filename;
var command = "gpu-screen-recorder -w portal -f 60 -a default_output -o " + outputPath var command = "gpu-screen-recorder -w portal -f 60 -a default_output -o " + outputPath;
var qmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "' + command + '"]; running: true }' var qmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "' + command + '"]; running: true }';
recordingProcess = Qt.createQmlObject(qmlString, panelPopup) recordingProcess = Qt.createQmlObject(qmlString, sidebarPopup);
isRecording = true isRecording = true;
quickAccessWidget.isRecording = true quickAccessWidget.isRecording = true;
} }
// Stop recording with cleanup // Stop recording with cleanup
function stopRecording() { function stopRecording() {
if (recordingProcess && isRecording) { if (recordingProcess && isRecording) {
var stopQmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -SIGINT -f \'gpu-screen-recorder.*portal\'"]; running: true; onExited: function() { destroy() } }' var stopQmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -SIGINT -f \'gpu-screen-recorder.*portal\'"]; running: true; onExited: function() { destroy() } }';
var stopProcess = Qt.createQmlObject(stopQmlString, panelPopup) var stopProcess = Qt.createQmlObject(stopQmlString, sidebarPopup);
var cleanupTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; running: true; repeat: false }', panelPopup) var cleanupTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; running: true; repeat: false }', sidebarPopup);
cleanupTimer.triggered.connect(function() { cleanupTimer.triggered.connect(function () {
if (recordingProcess) { if (recordingProcess) {
recordingProcess.running = false recordingProcess.running = false;
recordingProcess.destroy() recordingProcess.destroy();
recordingProcess = null recordingProcess = null;
} }
var forceKillQml = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -9 -f \'gpu-screen-recorder.*portal\' 2>/dev/null || true"]; running: true; onExited: function() { destroy() } }' var forceKillQml = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -9 -f \'gpu-screen-recorder.*portal\' 2>/dev/null || true"]; running: true; onExited: function() { destroy() } }';
var forceKillProcess = Qt.createQmlObject(forceKillQml, panelPopup) var forceKillProcess = Qt.createQmlObject(forceKillQml, sidebarPopup);
cleanupTimer.destroy() cleanupTimer.destroy();
}) });
} }
isRecording = false isRecording = false;
quickAccessWidget.isRecording = false quickAccessWidget.isRecording = false;
recordingPid = null recordingPid = null;
} }
// Clean up processes on destruction // Clean up processes on destruction
Component.onDestruction: { Component.onDestruction: {
if (isRecording) { if (isRecording) {
stopRecording() stopRecording();
} }
if (recordingProcess) { if (recordingProcess) {
recordingProcess.running = false recordingProcess.running = false;
recordingProcess.destroy() recordingProcess.destroy();
recordingProcess = null recordingProcess = null;
} }
} }
@ -355,12 +384,15 @@ PanelWindow {
size: 1.1 size: 1.1
fillColor: Theme.backgroundPrimary fillColor: Theme.backgroundPrimary
anchors.top: mainRectangle.top anchors.top: mainRectangle.top
offsetX: -447 + panelPopup.slideOffset offsetX: -447 + sidebarPopupRect.slideOffset
offsetY: 0 offsetY: 0
Behavior on offsetX { Behavior on offsetX {
enabled: !panelPopup.isAnimating enabled: !sidebarPopupRect.isAnimating
NumberAnimation { duration: 300; easing.type: Easing.OutCubic } NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
} }
} }
@ -369,12 +401,15 @@ PanelWindow {
position: "bottomright" position: "bottomright"
size: 1.1 size: 1.1
fillColor: Theme.backgroundPrimary fillColor: Theme.backgroundPrimary
offsetX: 33 + panelPopup.slideOffset offsetX: 33 + sidebarPopupRect.slideOffset
offsetY: 46 offsetY: 46
Behavior on offsetX { Behavior on offsetX {
enabled: !panelPopup.isAnimating enabled: !sidebarPopupRect.isAnimating
NumberAnimation { duration: 300; easing.type: Easing.OutCubic } NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
} }
} }
@ -389,4 +424,5 @@ PanelWindow {
} }
// Add a close button inside WallpaperPanel.qml for user to close the modal // Add a close button inside WallpaperPanel.qml for user to close the modal
} }
}
} }

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import Quickshell.Services.UPower import Quickshell.Services.UPower
import qs.Settings import qs.Settings
import qs.Components import qs.Components

View file

@ -53,7 +53,7 @@ Rectangle {
source: Image { source: Image {
id: avatarImage id: avatarImage
anchors.fill: parent anchors.fill: parent
source: Settings.profileImage !== undefined ? Settings.profileImage : "" source: Settings.settings.profileImage !== undefined ? Settings.settings.profileImage : ""
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
asynchronous: true asynchronous: true
cache: false cache: false
@ -66,7 +66,7 @@ Rectangle {
radius: 22 radius: 22
visible: false visible: false
} }
visible: Settings.profileImage !== undefined && Settings.profileImage !== "" visible: Settings.settings.profileImage !== undefined && Settings.settings.profileImage !== ""
z: 1 z: 1
} }
@ -77,7 +77,7 @@ Rectangle {
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 24 font.pixelSize: 24
color: Theme.onAccent color: Theme.onAccent
visible: Settings.profileImage === undefined || Settings.profileImage === "" visible: Settings.settings.profileImage === undefined || Settings.settings.profileImage === ""
z: 0 z: 0
} }
} }

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import Quickshell.Io import Quickshell.Io
import qs.Components import qs.Components
import qs.Services import qs.Services

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Widgets import Quickshell.Widgets
@ -126,8 +126,8 @@ PanelWindow {
anchors.margins: 4 anchors.margins: 4
color: Qt.darker(Theme.backgroundPrimary, 1.1) color: Qt.darker(Theme.backgroundPrimary, 1.1)
radius: 12 radius: 12
border.color: Settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline
border.width: Settings.currentWallpaper === modelData ? 3 : 1 border.width: Settings.settings.currentWallpaper === modelData ? 3 : 1
Image { Image {
id: wallpaperImage id: wallpaperImage
anchors.fill: parent anchors.fill: parent

View file

@ -1,6 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Layouts
import QtQuick.Controls 2.15 import QtQuick.Controls
import qs.Settings import qs.Settings
import "../../../Helpers/Weather.js" as WeatherHelper import "../../../Helpers/Weather.js" as WeatherHelper
@ -11,7 +11,7 @@ Rectangle {
color: "transparent" color: "transparent"
anchors.horizontalCenterOffset: -2 anchors.horizontalCenterOffset: -2
property string city: Settings.weatherCity !== undefined ? Settings.weatherCity : "" property string city: Settings.settings.weatherCity !== undefined ? Settings.settings.weatherCity : ""
property var weatherData: null property var weatherData: null
property string errorString: "" property string errorString: ""
property bool isVisible: false property bool isVisible: false
@ -95,7 +95,7 @@ Rectangle {
} }
} }
Text { Text {
text: weatherData && weatherData.current_weather ? ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? `${Math.round(weatherData.current_weather.temperature * 9/5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? "--°F" : "--°C") text: weatherData && weatherData.current_weather ? ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? `${Math.round(weatherData.current_weather.temperature * 9/5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? "--°F" : "--°C")
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 24 font.pixelSize: 24
font.bold: true font.bold: true
@ -151,7 +151,7 @@ Rectangle {
} }
Text { Text {
// High/low temp // High/low temp
text: weatherData && weatherData.daily ? ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? `${Math.round(weatherData.daily.temperature_2m_max[index] * 9/5 + 32)}° / ${Math.round(weatherData.daily.temperature_2m_min[index] * 9/5 + 32)}°` : `${Math.round(weatherData.daily.temperature_2m_max[index])}° / ${Math.round(weatherData.daily.temperature_2m_min[index])}°`) : ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? "--° / --°" : "--° / --°") text: weatherData && weatherData.daily ? ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? `${Math.round(weatherData.daily.temperature_2m_max[index] * 9/5 + 32)}° / ${Math.round(weatherData.daily.temperature_2m_min[index] * 9/5 + 32)}°` : `${Math.round(weatherData.daily.temperature_2m_max[index])}° / ${Math.round(weatherData.daily.temperature_2m_min[index])}°`) : ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? "--° / --°" : "--° / --°")
font.family: Theme.fontFamily font.family: Theme.fontFamily
font.pixelSize: 12 font.pixelSize: 12
color: Theme.textPrimary color: Theme.textPrimary

View file

@ -342,7 +342,7 @@ Item {
anchors.right: true anchors.right: true
margins.right: 0 margins.right: 0
margins.top: 0 margins.top: 0
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
Component.onCompleted: { Component.onCompleted: {
wifiLogic.refreshNetworks() wifiLogic.refreshNetworks()
} }