Add wallust support

This commit is contained in:
Ly-sec 2025-08-12 19:39:28 +02:00
commit 48f54c0b82
32 changed files with 257 additions and 245 deletions

View file

@ -8,7 +8,8 @@ Variants {
delegate: PanelWindow {
required property ShellScreen modelData
property string wallpaperSource: Wallpapers.currentWallpaper !== "" && !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : ""
property string wallpaperSource: Wallpapers.currentWallpaper !== ""
&& !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : ""
visible: wallpaperSource !== "" && !Settings.data.wallpaper.swww.enabled

View file

@ -9,7 +9,8 @@ Variants {
delegate: PanelWindow {
required property ShellScreen modelData
property string wallpaperSource: Wallpapers.currentWallpaper !== "" && !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : ""
property string wallpaperSource: Wallpapers.currentWallpaper !== ""
&& !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : ""
visible: wallpaperSource !== "" && !Settings.data.wallpaper.swww.enabled
color: "transparent"

View file

@ -109,7 +109,7 @@ Variants {
sizeMultiplier: 0.8
showBorder: false
anchors.verticalCenter: parent.verticalCenter
onClicked: function () {
onClicked: {
demoPanel.isLoaded = !demoPanel.isLoaded
}
}
@ -121,7 +121,7 @@ Variants {
sizeMultiplier: 0.8
showBorder: false
anchors.verticalCenter: parent.verticalCenter
onClicked: function () {
onClicked: {
// Map this button's center to the screen and open the side panel below it
const localCenterX = width / 2
const localCenterY = height / 2

View file

@ -12,15 +12,15 @@ NClock {
target: root
}
onEntered: function () {
onEntered: {
if (!calendar.isLoaded) {
tooltip.show()
}
}
onExited: function () {
onExited: {
tooltip.hide()
}
onClicked: function () {
onClicked: {
tooltip.hide()
calendar.isLoaded = !calendar.isLoaded
}

View file

@ -70,7 +70,7 @@ Item {
Audio.volumeDecrement()
}
}
onClicked: function () {
onClicked: {
audioDeviceSelector.isLoaded = !audioDeviceSelector.isLoaded
}
}

View file

@ -24,7 +24,7 @@ NIconButton {
return connected ? network.signalIcon(parent.currentSignal) : "wifi_off"
}
tooltipText: "WiFi Networks"
onClicked: function () {
onClicked: {
if (!wifiMenuLoader.active) {
wifiMenuLoader.isLoaded = true
}

View file

@ -78,7 +78,7 @@ NLoader {
NIconButton {
icon: "refresh"
sizeMultiplier: 0.8
onClicked: function () {
onClicked: {
network.refreshNetworks()
}
}
@ -86,7 +86,7 @@ NLoader {
NIconButton {
icon: "close"
sizeMultiplier: 0.8
onClicked: function () {
onClicked: {
wifiPanel.visible = false
network.onMenuClosed()
}

View file

@ -47,7 +47,7 @@ NLoader {
NIconButton {
icon: "chevron_left"
onClicked: function () {
onClicked: {
let newDate = new Date(grid.year, grid.month - 1, 1)
grid.year = newDate.getFullYear()
grid.month = newDate.getMonth()
@ -65,7 +65,7 @@ NLoader {
NIconButton {
icon: "chevron_right"
onClicked: function () {
onClicked: {
let newDate = new Date(grid.year, grid.month + 1, 1)
grid.year = newDate.getFullYear()
grid.month = newDate.getMonth()

View file

@ -73,17 +73,17 @@ NLoader {
stepSize: 0.01
value: Scaling.overrideScale
implicitWidth: bgRect.width * 0.75
onMoved: function () {
onMoved: {
Scaling.overrideScale = value
}
onPressedChanged: function () {
onPressedChanged: {
Scaling.overrideEnabled = true
}
}
NIconButton {
icon: "refresh"
fontPointSize: Style.fontSizeXL * scaling
onClicked: function () {
onClicked: {
Scaling.overrideEnabled = false
Scaling.overrideScale = 1.0
}
@ -171,8 +171,9 @@ NLoader {
NTextInput {
text: "Type anything"
Layout.fillWidth: true
onEditingFinished: function () {}
onEditingFinished: {
}
NDivider {
Layout.fillWidth: true
}

View file

@ -191,7 +191,7 @@ PanelWindow {
anchors.right: parent.right
anchors.margins: Style.marginSmall * scaling
icon: "close"
onClicked: function () {
onClicked: {
animateOut()
}
}

View file

@ -46,13 +46,11 @@ NLoader {
"label": "Wallpaper",
"icon": "image",
"source": "Tabs/Wallpaper.qml"
},
{
}, {
"label": "Wallpaper Selector",
"icon": "wallpaper_slideshow",
"source": "Tabs/WallpaperSelector.qml"
},
{
}, {
"label": "Misc",
"icon": "more_horiz",
"source": "Tabs/Misc.qml"
@ -62,7 +60,7 @@ NLoader {
"source": "Tabs/About.qml"
}]
onVisibleChanged: function () {
onVisibleChanged: {
if (visible)
currentTabIndex = 0
}
@ -190,7 +188,7 @@ NLoader {
icon: "close"
tooltipText: "Close settings panel"
Layout.alignment: Qt.AlignVCenter
onClicked: function () {
onClicked: {
settingsWindow.isLoaded = !settingsWindow.isLoaded
}
}

View file

@ -84,7 +84,7 @@ ColumnLayout {
text: Settings.data.general.avatarImage
placeholderText: "/home/user/.face"
Layout.fillWidth: true
onEditingFinished: function () {
onEditingFinished: {
Settings.data.general.avatarImage = text
}
}

View file

@ -65,7 +65,7 @@ ColumnLayout {
NTextInput {
text: Settings.data.screenRecorder.directory
Layout.fillWidth: true
onEditingFinished: function () {
onEditingFinished: {
Settings.data.screenRecorder.directory = text
}
}

View file

@ -58,7 +58,7 @@ ColumnLayout {
text: Settings.data.location.name
placeholderText: "Enter city name"
Layout.fillWidth: true
onEditingFinished: function () {
onEditingFinished: {
Settings.data.location.name = text
}
}

View file

@ -70,7 +70,7 @@ ColumnLayout {
NTextInput {
text: Settings.data.wallpaper.directory
Layout.fillWidth: true
onEditingFinished: function () {
onEditingFinished: {
Settings.data.wallpaper.directory = text
}
}

View file

@ -83,9 +83,8 @@ Item {
}
NText {
text: Settings.data.wallpaper.swww.enabled ?
"Wallpapers will change with " + Settings.data.wallpaper.swww.transitionType + " transition" :
"Wallpapers will change instantly"
text: Settings.data.wallpaper.swww.enabled ? "Wallpapers will change with " + Settings.data.wallpaper.swww.transitionType
+ " transition" : "Wallpapers will change instantly"
color: Colors.textSecondary
font.pointSize: Style.fontSizeSmall * scaling
visible: Settings.data.wallpaper.swww.enabled
@ -193,7 +192,9 @@ Item {
radius: parent.radius
Behavior on opacity {
NumberAnimation { duration: 150 }
NumberAnimation {
duration: 150
}
}
}

View file

@ -21,17 +21,23 @@ NBox {
// Performance
NIconButton {
icon: "speed"
onClicked: function () {/* TODO: hook to power profile */ }
onClicked: {
/* TODO: hook to power profile */ }
}
// Balanced
NIconButton {
icon: "balance"
onClicked: function () {/* TODO: hook to power profile */ }
onClicked: {
/* TODO: hook to power profile */ }
}
// Eco
NIconButton {
icon: "eco"
onClicked: function () {/* TODO: hook to power profile */ }
onClicked: {
/* TODO: hook to power profile */ }
}
Item {
Layout.fillWidth: true

View file

@ -59,7 +59,7 @@ NBox {
}
NIconButton {
icon: "settings"
onClicked: function () {
onClicked: {
if (!root.settingsWindow) {
const comp = Qt.createComponent("../../Settings/SettingsWindow.qml")
if (comp.status === Component.Ready) {

View file

@ -23,10 +23,10 @@ Singleton {
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
Component.onCompleted: function () {
Component.onCompleted: {
reload()
}
onLoaded: function () {
onLoaded: {
loadFromCache()
}
onLoadFailed: function (error) {

View file

@ -16,7 +16,7 @@ Singleton {
FileView {
path: locationFile
onAdapterUpdated: writeAdapter()
onLoaded: function () {
onLoaded: {
updateWeather()
}
onLoadFailed: function (error) {

View file

@ -47,10 +47,10 @@ Singleton {
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
Component.onCompleted: function () {
Component.onCompleted: {
reload()
}
onLoaded: function () {
onLoaded: {
Qt.callLater(function () {
if (adapter.wallpaper.current !== "") {
@ -188,8 +188,14 @@ Singleton {
Connections {
target: adapter.wallpaper
function onIsRandomChanged() { Wallpapers.toggleRandomWallpaper() }
function onRandomIntervalChanged() { Wallpapers.restartRandomWallpaperTimer() }
function onDirectoryChanged() { Wallpapers.loadWallpapers() }
function onIsRandomChanged() {
Wallpapers.toggleRandomWallpaper()
}
function onRandomIntervalChanged() {
Wallpapers.restartRandomWallpaperTimer()
}
function onDirectoryChanged() {
Wallpapers.loadWallpapers()
}
}
}

View file

@ -143,8 +143,8 @@ Singleton {
}
onExited: function(exitCode, exitStatus) {
onExited: function (exitCode, exitStatus) {
console.log("SWWW: Process finished with exit code:", exitCode, "status:", exitStatus)
if (exitCode !== 0) {
console.log("SWWW: Process failed. Make sure swww-daemon is running with: swww-daemon")
console.log("SWWW: You can start it with: swww-daemon --format xrgb")
@ -168,7 +168,7 @@ Singleton {
console.log("SWWW: Daemon start process initiated")
}
onExited: function(exitCode, exitStatus) {
onExited: function (exitCode, exitStatus) {
console.log("SWWW: Daemon start process finished with exit code:", exitCode)
if (exitCode === 0) {
console.log("SWWW: Daemon started successfully")

View file

@ -1,5 +1,6 @@
pragma Singleton
pragma ComponentBehavior: Bound
pragma ComponentBehavior
import QtQuick
import Quickshell
@ -8,149 +9,149 @@ import Quickshell.Hyprland
import qs.Services
Singleton {
id: root
id: root
property ListModel workspaces: ListModel {}
property bool isHyprland: false
property bool isNiri: false
property var hlWorkspaces: Hyprland.workspaces.values
// Detect which compositor we're using
Component.onCompleted: {
console.log("WorkspaceManager initializing...");
detectCompositor();
}
property ListModel workspaces: ListModel {}
property bool isHyprland: false
property bool isNiri: false
property var hlWorkspaces: Hyprland.workspaces.values
// Detect which compositor we're using
Component.onCompleted: {
console.log("WorkspaceManager initializing...")
detectCompositor()
}
function detectCompositor() {
try {
try {
if (Hyprland.eventSocketPath) {
console.log("Detected Hyprland compositor");
isHyprland = true;
isNiri = false;
initHyprland();
return;
}
} catch (e) {
console.log("Hyprland not available:", e);
}
if (typeof Niri !== "undefined") {
console.log("Detected Niri service");
isHyprland = false;
isNiri = true;
initNiri();
return;
}
console.log("No supported compositor detected");
} catch (e) {
console.error("Error detecting compositor:", e);
function detectCompositor() {
try {
try {
if (Hyprland.eventSocketPath) {
console.log("Detected Hyprland compositor")
isHyprland = true
isNiri = false
initHyprland()
return
}
}
} catch (e) {
console.log("Hyprland not available:", e)
}
// Initialize Hyprland integration
function initHyprland() {
try {
// Fixes the odd workspace issue.
Hyprland.refreshWorkspaces();
// hlWorkspaces = Hyprland.workspaces.values;
// updateHyprlandWorkspaces();
return true;
} catch (e) {
console.error("Error initializing Hyprland:", e);
isHyprland = false;
return false;
if (typeof Niri !== "undefined") {
console.log("Detected Niri service")
isHyprland = false
isNiri = true
initNiri()
return
}
console.log("No supported compositor detected")
} catch (e) {
console.error("Error detecting compositor:", e)
}
}
// Initialize Hyprland integration
function initHyprland() {
try {
// Fixes the odd workspace issue.
Hyprland.refreshWorkspaces()
// hlWorkspaces = Hyprland.workspaces.values;
// updateHyprlandWorkspaces();
return true
} catch (e) {
console.error("Error initializing Hyprland:", e)
isHyprland = false
return false
}
}
onHlWorkspacesChanged: {
updateHyprlandWorkspaces()
}
Connections {
target: Hyprland.workspaces
function onValuesChanged() {
updateHyprlandWorkspaces()
}
}
Connections {
target: Hyprland
function onRawEvent(event) {
updateHyprlandWorkspaces()
}
}
function updateHyprlandWorkspaces() {
workspaces.clear()
try {
for (var i = 0; i < hlWorkspaces.length; i++) {
const ws = hlWorkspaces[i]
// Only append workspaces with id >= 1
if (ws.id >= 1) {
workspaces.append({
"id": i,
"idx": ws.id,
"name": ws.name || "",
"output": ws.monitor?.name || "",
"isActive": ws.active === true,
"isFocused": ws.focused === true,
"isUrgent": ws.urgent === true
})
}
}
workspacesChanged()
} catch (e) {
console.error("Error updating Hyprland workspaces:", e)
}
}
function initNiri() {
updateNiriWorkspaces()
}
Connections {
target: Niri
function onWorkspacesChanged() {
updateNiriWorkspaces()
}
}
function updateNiriWorkspaces() {
const niriWorkspaces = Niri.workspaces || []
workspaces.clear()
for (var i = 0; i < niriWorkspaces.length; i++) {
const ws = niriWorkspaces[i]
workspaces.append({
"id": ws.id,
"idx": ws.idx || 1,
"name": ws.name || "",
"output": ws.output || "",
"isFocused": ws.isFocused === true,
"isActive": ws.isActive === true,
"isUrgent": ws.isUrgent === true,
"isOccupied": ws.isOccupied === true
})
}
onHlWorkspacesChanged: {
updateHyprlandWorkspaces();
}
workspacesChanged()
}
Connections {
target: Hyprland.workspaces
function onValuesChanged() {
updateHyprlandWorkspaces();
}
}
Connections {
target: Hyprland
function onRawEvent(event) {
updateHyprlandWorkspaces();
}
}
function updateHyprlandWorkspaces() {
workspaces.clear();
try {
for (let i = 0; i < hlWorkspaces.length; i++) {
const ws = hlWorkspaces[i];
// Only append workspaces with id >= 1
if (ws.id >= 1) {
workspaces.append({
id: i,
idx: ws.id,
name: ws.name || "",
output: ws.monitor?.name || "",
isActive: ws.active === true,
isFocused: ws.focused === true,
isUrgent: ws.urgent === true
});
}
}
workspacesChanged();
} catch (e) {
console.error("Error updating Hyprland workspaces:", e);
}
}
function initNiri() {
updateNiriWorkspaces();
}
Connections {
target: Niri
function onWorkspacesChanged() {
updateNiriWorkspaces();
}
}
function updateNiriWorkspaces() {
const niriWorkspaces = Niri.workspaces || [];
workspaces.clear();
for (let i = 0; i < niriWorkspaces.length; i++) {
const ws = niriWorkspaces[i];
workspaces.append({
id: ws.id,
idx: ws.idx || 1,
name: ws.name || "",
output: ws.output || "",
isFocused: ws.isFocused === true,
isActive: ws.isActive === true,
isUrgent: ws.isUrgent === true,
isOccupied: ws.isOccupied === true,
});
}
workspacesChanged();
}
function switchToWorkspace(workspaceId) {
if (isHyprland) {
try {
Hyprland.dispatch(`workspace ${workspaceId}`);
} catch (e) {
console.error("Error switching Hyprland workspace:", e);
}
} else if (isNiri) {
try {
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", workspaceId.toString()]);
} catch (e) {
console.error("Error switching Niri workspace:", e);
}
} else {
console.warn("No supported compositor detected for workspace switching");
}
function switchToWorkspace(workspaceId) {
if (isHyprland) {
try {
Hyprland.dispatch(`workspace ${workspaceId}`)
} catch (e) {
console.error("Error switching Hyprland workspace:", e)
}
} else if (isNiri) {
try {
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", workspaceId.toString()])
} catch (e) {
console.error("Error switching Niri workspace:", e)
}
} else {
console.warn("No supported compositor detected for workspace switching")
}
}
}

View file

@ -6,9 +6,10 @@ Rectangle {
id: root
readonly property real scaling: Scaling.scale(screen)
property var onEntered: function () {}
property var onExited: function () {}
property var onClicked: function () {}
signal entered
signal exited
signal clicked
width: textItem.paintedWidth
height: textItem.paintedHeight
@ -26,8 +27,8 @@ Rectangle {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: root.onEntered()
onExited: root.onExited()
onClicked: root.onClicked()
onEntered: root.entered()
onExited: root.exited()
onClicked: root.clicked()
}
}

View file

@ -13,7 +13,8 @@ ComboBox {
property list<string> optionsKeys: []
property list<string> optionsLabels: []
property string currentKey: ''
property var onSelected: function (string) {}
signal selected(string key)
Layout.fillWidth: true
Layout.preferredHeight: height
@ -21,7 +22,7 @@ ComboBox {
model: optionsKeys
currentIndex: model.indexOf(currentKey)
onActivated: {
root.onSelected(model[currentIndex])
root.selected(model[currentIndex])
}
// Rounded background

View file

@ -15,12 +15,13 @@ Rectangle {
property bool showBorder: true
property bool enabled: true
property bool hovering: false
property var onEntered: function () {}
property var onExited: function () {}
property var onClicked: function () {}
property real fontPointSize: Style.fontSizeMedium
property string fontFamily: "Material Symbols Outlined"
signal entered
signal exited
signal clicked
implicitWidth: size
implicitHeight: size
@ -62,20 +63,20 @@ Rectangle {
if (tooltipText) {
tooltip.show()
}
root.onEntered()
root.entered()
}
onExited: {
hovering = false
if (tooltipText) {
tooltip.hide()
}
root.onExited()
root.exited()
}
onClicked: {
if (tooltipText) {
tooltip.hide()
}
root.onClicked()
root.clicked()
}
}
}

View file

@ -4,26 +4,27 @@ import Quickshell.Wayland
import qs.Services
PanelWindow {
id: outerPanel
id: root
readonly property real scaling: Scaling.scale(screen)
property bool showOverlay: Settings.data.general.dimDesktop
property int topMargin: Style.barHeight * scaling
property color overlayColor: showOverlay ? Colors.overlay : "transparent"
signal dismissed
function hide() {
//visible = false
dismissed()
root.dismissed()
}
function show() {
// Ensure only one panel is visible at a time using Settings as ephemeral store
try {
if (Settings.openPanel && Settings.openPanel !== outerPanel && Settings.openPanel.hide) {
if (Settings.openPanel && Settings.openPanel !== root && Settings.openPanel.hide) {
Settings.openPanel.hide()
}
Settings.openPanel = outerPanel
Settings.openPanel = root
} catch (e) {
// ignore
@ -45,7 +46,7 @@ PanelWindow {
MouseArea {
anchors.fill: parent
onClicked: outerPanel.hide()
onClicked: root.hide()
}
Behavior on color {
@ -57,16 +58,16 @@ PanelWindow {
Component.onDestruction: {
try {
if (visible && Settings.openPanel === outerPanel)
if (visible && Settings.openPanel === root)
Settings.openPanel = null
} catch (e) {
}
}
onVisibleChanged: function () {
onVisibleChanged: {
try {
if (!visible && Settings.openPanel === outerPanel)
if (!visible && Settings.openPanel === root)
Settings.openPanel = null
} catch (e) {

View file

@ -18,10 +18,12 @@ Item {
property real sizeMultiplier: 0.8
property bool autoHide: false
property var onEntered: function () {}
property var onExited: function () {}
property var onClicked: function () {}
property var onWheel: function (delta) {}
signal shown
signal hidden
signal entered
signal exited
signal clicked
signal wheel(int delta)
// Internal state
property bool showPill: false
@ -34,10 +36,6 @@ Item {
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap)
// TBC, do we use those ?
signal shown
signal hidden
width: iconSize + (showPill ? maxPillWidth - pillOverlap : 0)
height: pillHeight
@ -127,7 +125,7 @@ Item {
}
onStopped: {
delayedHideAnim.start()
shown()
root.shown()
}
}
@ -166,7 +164,7 @@ Item {
onStopped: {
showPill = false
shouldAnimateHide = false
hidden()
root.hidden()
}
}
@ -194,18 +192,18 @@ Item {
onEntered: {
showDelayed()
tooltip.show()
root.onEntered()
root.entered()
}
onExited: {
hide()
tooltip.hide()
root.onExited()
root.exited()
}
onClicked: {
root.onClicked()
root.clicked()
}
onWheel: wheel => {
root.onWheel(wheel.angleDelta.y)
root.wheel(wheel.angleDelta.y)
}
}

View file

@ -35,14 +35,6 @@ Slider {
height: parent.height
color: Colors.accentPrimary
radius: parent.radius
// Feels more responsive without animation
// Behavior on width {
// NumberAnimation {
// duration: 50
// easing.type: Easing.OutQuad
// }
// }
}
// Circular cutout
@ -84,6 +76,7 @@ Slider {
color: root.pressed ? Colors.surfaceVariant : Colors.surface
border.color: Colors.accentPrimary
border.width: Math.max(1, Style.borderThick * scaling)
// Press feedback halo (using accent color, low opacity)
Rectangle {
anchors.centerIn: parent

View file

@ -13,7 +13,8 @@ Item {
property alias placeholderText: input.placeholderText
property bool readOnly: false
property bool enabled: true
property var onEditingFinished: function () {}
signal editingFinished
// Sizing
implicitHeight: Style.baseWidgetSize * 1.25 * scaling
@ -55,7 +56,7 @@ Item {
placeholderTextColor: Colors.textSecondary
background: null
font.pointSize: Style.fontSizeSmall * scaling
onEditingFinished: root.onEditingFinished()
onEditingFinished: root.editingFinished()
// Text changes are observable via the aliased 'text' property (root.text) and its 'textChanged' signal.
// No additional callback is invoked here to avoid conflicts with QML's onTextChanged handler semantics.
}

View file

@ -12,7 +12,8 @@ RowLayout {
property bool value: false
property bool hovering: false
property int baseSize: Style.baseWidgetSize
property var onToggled: function (value) {}
signal toggled(bool balue)
Layout.fillWidth: true
@ -72,7 +73,7 @@ RowLayout {
onExited: hovering = false
onClicked: {
value = !value
root.onToggled(value)
root.toggled(value)
}
}
}