diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-17 10:16:40 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-17 10:16:40 +0200 |
| commit | 70bbc970c75221c902c515efd124c47fd702e3b7 (patch) | |
| tree | d35f6b1ffbb506b65430af7fc1eca2c432879a22 /desktop/modules/appearance/IconsTab.qml | |
| parent | e5399f3e8d8572f1db7c4391a7c571d4845500f1 (diff) | |
| download | quickshell-70bbc970c75221c902c515efd124c47fd702e3b7.tar.gz quickshell-70bbc970c75221c902c515efd124c47fd702e3b7.zip | |
appearance moves into the settings window
The appearance shell existed because a wallpaper picker needed more room
than the 600px drawer, so it ran as its own process with a full-screen
overlay. The settings window now gives it that room, and its five tabs
become five settings pages in the desktop process: Wallpaper, Theme,
Icons & cursor, Sunset and Idle. appearance/ is gone, and with it a
process, a keepalive window and a layer blur rule.
The files move to desktop/modules/appearance/ unchanged in what they do.
Each tab gets a small *Module.qml; the window's refresh-on-open calls the
singletons' refresh(), which the panel's show() used to. The two tabs that
were inline Components in AppearancePanel become ThemeTab and
WallpaperTab, owning the notices the panel printed. Sunset, Idle and
Icons drop their own Flickable so the page scrolls them and search can
reveal a row; the wallpaper list virtualises its rows and scrolls itself,
so it takes the page's new viewHeight instead.
Search finds a colour scheme and an icon or cursor theme by name.
The appearance IPC target moves to desktop/shell.qml with the same verbs,
so udt-steps and any keybind change only the path. Git now follows the
appearance pages and autostart in the sidebar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/appearance/IconsTab.qml')
| -rw-r--r-- | desktop/modules/appearance/IconsTab.qml | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/desktop/modules/appearance/IconsTab.qml b/desktop/modules/appearance/IconsTab.qml new file mode 100644 index 0000000..777ea51 --- /dev/null +++ b/desktop/modules/appearance/IconsTab.qml @@ -0,0 +1,228 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import Quickshell +import Quickshell.Widgets +import QtQuick +import "../.." + +Item { + implicitHeight: col.implicitHeight + + // Clicking a cursor card stages it; Apply commits. There is deliberately no + // live hover preview: hyprctl setcursor is not a read-only switch, Hyprland + // persists the theme to org.gnome.desktop.interface cursor-theme, so + // hovering wrote the setting and a close left the last hovered theme set. + // The four rendered shapes on each card are the preview. + property string pendingCursor: "" + + Column { + id: col + width: parent.width + spacing: 12 + + Text { + text: Icons.scanning ? "scanning…" : `${Icons.iconThemes.length} icon themes · ${Icons.cursorThemes.length} cursor themes` + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + + Text { + text: "Icon theme" + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true } + } + + // Flow, not a horizontal strip: the cards wrap onto further rows rather + // than running off the right edge, and the tab scrolls vertically. + Flow { + width: parent.width + spacing: 10 + Repeater { + model: Icons.iconThemes + Rectangle { + id: iconCard + required property string modelData + // What a search hit scrolls to (IconsModule's searchItems key). + objectName: "icon:" + modelData + readonly property bool current: Icons.currentIcon === modelData + width: 230; height: 150; radius: 10 + color: current ? Qt.alpha(Theme.accent, 0.2) : Qt.alpha(Theme.surface, 0.35) + border.width: current ? 2 : 1 + border.color: current ? Theme.accent : "transparent" + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: Icons.applyIcon(modelData) + } + + Column { + width: parent.width - 16 + anchors.centerIn: parent + spacing: 10 + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: 8 + Repeater { + model: ["folder", "text-x-generic", "image-x-generic", "network-wireless"] + IconImage { + required property string modelData + readonly property string glyph: (Icons.iconPreview[iconCard.modelData] ?? {})[modelData] ?? "" + // Absolute paths must carry file://, or they + // resolve against the config's qrc base and + // fail; Gtk's builtin icons come back as + // /org/gtk gresource paths Qt cannot open. + source: glyph.indexOf("/") === 0 && glyph.indexOf("/org/gtk/") !== 0 + ? "file://" + glyph : "" + implicitSize: 44 + } + } + } + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + text: iconCard.current ? modelData + " current" : modelData + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + wrapMode: Text.WrapAnywhere + maximumLineCount: 2 + elide: Text.ElideRight + } + } + } + } + } + + Row { + spacing: 10 + Text { + anchors.verticalCenter: parent.verticalCenter + text: "Cursor theme" + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true } + } + Item { width: 12; height: 1 } + Text { + anchors.verticalCenter: parent.verticalCenter + text: "Size" + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } + Repeater { + model: [16, 24, 32, 48, 64] + Tab { + required property var modelData + anchors.verticalCenter: parent.verticalCenter + text: String(modelData) + selected: Icons.cursorSize === modelData + onClicked: Icons.setCursorSize(modelData) + } + } + } + + Flow { + width: parent.width + spacing: 10 + Repeater { + model: Icons.cursorThemes + Rectangle { + id: cursorCard + required property string modelData + objectName: "cursor:" + modelData + readonly property bool current: Icons.currentCursor === modelData + readonly property bool staged: pendingCursor === modelData + width: 230; height: 150; radius: 10 + color: current || staged ? Qt.alpha(Theme.accent, 0.2) : Qt.alpha(Theme.surface, 0.35) + border.width: current || staged ? 2 : 1 + border.color: staged ? Theme.accent + : current ? Qt.alpha(Theme.accent, 0.5) + : "transparent" + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: pendingCursor = modelData + } + + Column { + width: parent.width - 16 + anchors.centerIn: parent + spacing: 10 + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: 8 + Repeater { + // The cases that tell themes apart: arrow, hand, + // text and horizontal resize. + model: ["left_ptr", "hand2", "xterm", "resize"] + Image { + required property string modelData + readonly property string p: (Icons.cursorPreview[cursorCard.modelData] ?? {})[modelData] ?? "" + width: 44; height: 44 + asynchronous: true + source: p !== "" ? "file://" + p : "" + fillMode: Image.PreserveAspectFit + } + } + } + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + text: cursorCard.staged ? modelData + " staged" + : cursorCard.current ? modelData + " current" + : modelData + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + wrapMode: Text.WrapAnywhere + maximumLineCount: 2 + elide: Text.ElideRight + } + } + } + } + } + + Row { + spacing: 8 + Text { + anchors.verticalCenter: parent.verticalCenter + text: pendingCursor !== "" ? "click Apply to set" : "click a cursor theme to stage it" + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + Tab { + text: "Apply" + selected: pendingCursor !== "" + enabled: pendingCursor !== "" + onClicked: { + Icons.applyCursor(pendingCursor); + pendingCursor = ""; + } + } + Tab { + text: "Reset" + enabled: pendingCursor !== "" + onClicked: pendingCursor = "" + } + } + + Text { + width: parent.width + wrapMode: Text.Wrap + visible: Icons.notice !== "" + text: Icons.notice + color: Theme.green + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } + } + +} |
