Watch
1
0
Fork
You've already forked souveraine
0

quickshell: dock fullscreen fallback via HyprlandData IPC, wallpaper folder browsing

This commit is contained in:
Fimeg 2026-07-21 15:52:32 -04:00
commit 0d06cbb983
2 changed files with 106 additions and 11 deletions

View file

@ -61,7 +61,15 @@ Scope { // Scope
ws => ws.monitor && ws.monitor.name === Hyprland.focusedMonitor?.name)
readonly property var activeFullscreenWorkspace: root.focusedWorkspaces.filter(
ws => ws.active && ws.toplevels.values.filter(w => w.wayland?.fullscreen)[0] !== undefined)[0]
// The workspace scan above walks ext-foreign-toplevel-list, which never
// sees standalone `qs -p` windows (souveraine-settings): Hyprland has them
// fullscreen but the workspace reports zero toplevels. HyprlandData polls
// `hyprctl activewindow -j` Hyprland's own IPC, the source of truth so
// fall back to it. `fullscreen === 2` is Hyprland's real (not maximized)
// fullscreen mode. Primary check stays first: no IPC round-trip when it
// already answers.
readonly property bool activeMonitorHasFullscreen: root.activeFullscreenWorkspace !== undefined
|| HyprlandData.activeWindow?.fullscreen === 2
function computeDockState() {
if (root.activeMonitorHasFullscreen)

View file

@ -14,7 +14,14 @@ import qs.modules.common.widgets
ContentPage {
forceWidth: true
readonly property string wallpaperDir: Quickshell.env("HOME") + "/Pictures/Wallpapers"
readonly property string homeDir: Quickshell.env("HOME")
property string currentDir: homeDir + "/Pictures/Wallpapers"
property var quickDirs: [
{ name: "Wallpapers", path: homeDir + "/Pictures/Wallpapers", icon: "wallpaper" },
{ name: "Pictures", path: homeDir + "/Pictures", icon: "image" },
{ name: "Downloads", path: homeDir + "/Downloads", icon: "download" },
{ name: "Home", path: homeDir, icon: "home" }
]
ContentSection {
icon: "wallpaper"
@ -29,6 +36,54 @@ ContentPage {
elide: Text.ElideMiddle
}
// Quick directory buttons
Flow {
Layout.fillWidth: true
spacing: 6
Repeater {
model: quickDirs
RippleButton {
required property var modelData
property bool isCurrent: currentDir === modelData.path
padding: 8
buttonRadius: Appearance.rounding.small
toggled: isCurrent
colBackgroundToggled: Appearance.colors.colSecondaryContainer
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
colRippleToggled: Appearance.colors.colSecondaryContainerActive
onClicked: {
currentDir = modelData.path;
}
contentItem: RowLayout {
spacing: 4
MaterialSymbol {
iconSize: 16
text: modelData.icon
color: isCurrent ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1
}
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: modelData.name
color: isCurrent ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1
}
}
}
}
}
// Current path display
StyledText {
Layout.fillWidth: true
text: currentDir.replace(homeDir, "~")
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.smaller
elide: Text.ElideMiddle
}
GridView {
id: grid
Layout.fillWidth: true
@ -42,14 +97,18 @@ ContentPage {
model: FolderListModel {
id: folderModel
folder: "file://" + wallpaperDir
nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.webp"]
showDirs: false
folder: "file://" + currentDir
nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.webp", "*.avif"]
showDirs: true
showDotAndDotDot: false
showOnlyReadable: true
sortField: FolderListModel.Name
}
delegate: Item {
required property string filePath
required property string fileName
required property bool fileIsDir
width: grid.cellWidth
height: grid.cellHeight
@ -58,26 +117,54 @@ ContentPage {
anchors.margins: 5
radius: Appearance.rounding.small
color: Appearance.colors.colLayer1
border.width: Config.options.background.wallpaperPath === filePath ? 3 : 0
border.width: (!fileIsDir && Config.options.background.wallpaperPath === filePath) ? 3 : 0
border.color: Appearance.colors.colPrimary
clip: true
Image {
anchors.fill: parent
anchors.margins: 3
source: "file://" + filePath
source: fileIsDir ? "" : "file://" + filePath
fillMode: Image.PreserveAspectCrop
asynchronous: true
sourceSize.width: 240
visible: !fileIsDir
}
// Directory indicator
Column {
anchors.centerIn: parent
visible: fileIsDir
spacing: 4
MaterialSymbol {
anchors.horizontalCenter: parent.horizontalCenter
iconSize: 32
text: "folder"
color: Appearance.colors.colPrimary
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Appearance.font.pixelSize.smaller
text: fileName
color: Appearance.colors.colOnLayer1
elide: Text.ElideRight
width: grid.cellWidth - 16
horizontalAlignment: Text.AlignHCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: {
// Shell process owns selection + theming.
Quickshell.execDetached(["qs", "-c", "souveraine",
"ipc", "call", "wallpapers", "apply", filePath]);
Config.options.background.wallpaperPath = filePath;
if (fileIsDir) {
currentDir = filePath;
} else {
// Shell process owns selection + theming.
Quickshell.execDetached(["qs", "-c", "souveraine",
"ipc", "call", "wallpapers", "apply", filePath]);
Config.options.background.wallpaperPath = filePath;
}
}
}
}
@ -87,7 +174,7 @@ ContentPage {
StyledText {
visible: folderModel.count === 0
Layout.fillWidth: true
text: Translation.tr("No images in %1").arg(wallpaperDir)
text: Translation.tr("No images in %1").arg(currentDir.replace(homeDir, "~"))
color: Appearance.colors.colSubtext
wrapMode: Text.WordWrap
}