diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d1affa686..d95366fb4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,22 +2,33 @@ - Please, please, please, make multiple PRs if you have many features/fixes, and don't shove your personal changes along with the PR, including changed defaults - We can accept features that we do not personally want, but in that case we will ask you to make it configurable/optionally loaded. -- If you want to start working on something big to contribute, it might be a good idea to ask first to not waste your effort (but if you've already done it for yourself, it doesn't hurt to submit). +- If you want to start working on something _big_ to contribute, it might be a good idea to ask first to not waste your effort (but if you've already done it for yourself, it doesn't hurt to submit). -# Code details +# Translations -## Contributing to i18n +See `dots/.config/quickshell/ii/translations/tools` -For contributing in translation (i18n) for Quickshell, see also `dots/.config/quickshell/ii/translations/tools`. +# Code ## Dynamic loading -- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`. One tip with `Loader`s is sometimes you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`. +- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader` + - Note that you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent` + - When something that's to be dynamically loaded doesn't affect its parent layout, you can have a fading animation by using FadeLoader and set the `shown` prop instead of `active` and `visible` ## Practical concerns - Make sure what you add does not require significant resources for a minor purpose or harm usability just for the sake of looking nice. The dotfiles must remain practical for daily driving. -- If there is something really fancy and impractical anyway, add a config option for it and make sure it's disabled by default. +- If there is something really fancy and impractical anyway, add a config option for it and make sure it's disabled by default (example: constantly rotating background clock) + +## Style + +- Spaces + - Space properties and children data into meaningful groups. (but of course, don't use 2+ blanks in a row) + - Put spaces between text and operators: `if (condition) { ... } else { ... }` instead of `if(condition){ ... }else{ ... }` +- As you can see, it's pretty easy to use lots of nesting. There's no hard limit, end-4 himself nests a lot too, but avoid/mitigate that: + - Prefer early return: Use something like `if (!condition) return; doStuff();` instead of `if (condition) { doStuff() }` + - If you feel it's a bother to refractor something into a new file, remember there's `component` to declare reusable components in the same file. # Setting up diff --git a/.github/README.md b/.github/README.md index de4c9c790..a8134a426 100644 --- a/.github/README.md +++ b/.github/README.md @@ -81,7 +81,7 @@ Widget system: Quickshell | Support: Yes |:---|:---------------| | image | image | | Window management | Weeb power | -| image | image | +| image | image | ### Other styles: Available at the end of the readme. diff --git a/.gitignore b/.gitignore index b59d26bf8..9e298d049 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,17 @@ /diagnose.result /cache + # Ignore Python cache files __pycache__/ *.py[cod] -dots/.config/quickshell/ii/.qmlls.ini -.update-lock + +/dots/.config/quickshell/ii/.qmlls.ini + +# exp-update +/.update-lock + +# custom os-release /os-release -#emacs backup + +# Emacs auto backup file *~ diff --git a/diagnose b/diagnose index db4a239e0..3001a62a2 100755 --- a/diagnose +++ b/diagnose @@ -70,6 +70,7 @@ x git submodule status --recursive e "Checking distro" x _check_distro_id +x cat os-release #x _check_distro e "Checking variables" diff --git a/dots/.config/hypr/hyprland/execs.conf b/dots/.config/hypr/hyprland/execs.conf index 4bb9c86e5..bb5dd7225 100644 --- a/dots/.config/hypr/hyprland/execs.conf +++ b/dots/.config/hypr/hyprland/execs.conf @@ -14,7 +14,7 @@ exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISP exec-once = hyprpm reload # Audio -exec-once = easyeffects --gapplication-service +exec-once = easyeffects --hide-window --service-mode # Clipboard: history # exec-once = wl-paste --watch cliphist store & diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index fdda9a714..020fe582d 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -24,8 +24,8 @@ bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden] bindit = ,Super_R, global, quickshell:workspaceNumber # [hidden] bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard -bindd = Super, Tab, Toggle overview, global, quickshell:overviewToggle # [hidden] Toggle overview/launcher (alt) -bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # Toggle left sidebar +bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # [hidden] Toggle overview/launcher (alt) +bind = Super, A, global, quickshell:sidebarLeftToggle # Toggle left sidebar bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden] bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden] bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden] diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index 01ac1056d..ea9e6fd72 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -133,12 +133,14 @@ layerrule = blurpopups, quickshell:.* layerrule = blur, quickshell:.* layerrule = ignorealpha 0.79, quickshell:.* layerrule = animation slide, quickshell:bar +layerrule = noanim, quickshell:actionCenter layerrule = animation slide bottom, quickshell:cheatsheet layerrule = animation slide bottom, quickshell:dock layerrule = animation popin 120%, quickshell:screenCorners layerrule = noanim, quickshell:lockWindowPusher layerrule = animation fade, quickshell:notificationPopup layerrule = noanim, quickshell:overlay +layerrule = ignorealpha 1, quickshell:overlay layerrule = noanim, quickshell:overview layerrule = animation slide bottom, quickshell:osk layerrule = noanim, quickshell:polkit diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/README.md b/dots/.config/quickshell/ii/assets/icons/fluent/README.md index 5f4d3eaa2..c8fbd90e8 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/README.md +++ b/dots/.config/quickshell/ii/assets/icons/fluent/README.md @@ -1,5 +1,5 @@ -The "search" and "task view" icons are from here, with modifications +The "start-here", "search" and "task view" icons are from here, with modifications -https://www.figma.com/community/file/1123040825921884189/windows-11 +[Windows 11 by Joshua Oghenekaro Okwe - Figma](https://www.figma.com/community/file/1123040825921884189/windows-11) -License: CC BY 4.0 +License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.en) diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-sync.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-sync.svg new file mode 100644 index 000000000..73df6838e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-sync.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/chevron-down.svg b/dots/.config/quickshell/ii/assets/icons/fluent/chevron-down.svg new file mode 100644 index 000000000..604bf9887 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/pin-off.svg b/dots/.config/quickshell/ii/assets/icons/fluent/pin-off.svg new file mode 100644 index 000000000..046a27cea --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/pin-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/pin.svg b/dots/.config/quickshell/ii/assets/icons/fluent/pin.svg new file mode 100644 index 000000000..b31790338 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/pin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/settings.svg b/dots/.config/quickshell/ii/assets/icons/fluent/settings.svg new file mode 100644 index 000000000..f1e568829 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg new file mode 100644 index 000000000..e6b950eca --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg new file mode 100644 index 000000000..708d5a71b --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg new file mode 100644 index 000000000..af58d933f --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg new file mode 100644 index 000000000..8d0e69fce --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-dark.svg b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-dark.svg index 2d972a7b4..8eb990cc4 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-dark.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-dark.svg @@ -7,10 +7,11 @@ version="1.1" viewBox="0 0 16.933 16.933" id="svg2" - sodipodi:docname="system-search.svg" + sodipodi:docname="system-search-dark.svg" inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> + + + + + + style="fill:url(#linearGradient4);fill-opacity:1;stroke:url(#linearGradient919)" /> diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/task-view-pressed-dark.svg b/dots/.config/quickshell/ii/assets/icons/fluent/task-view-pressed-dark.svg new file mode 100644 index 000000000..a7e14009c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/task-view-pressed-dark.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/task-view-pressed-light.svg b/dots/.config/quickshell/ii/assets/icons/fluent/task-view-pressed-light.svg new file mode 100644 index 000000000..b1785dbd0 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/task-view-pressed-light.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index a724c043d..ec47c514b 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound import QtQuick import Quickshell import Quickshell.Io +import qs.modules.common.functions Singleton { id: root @@ -147,6 +148,7 @@ Singleton { property string networkEthernet: "kcmshell6 kcm_networkmanagement" property string taskManager: "plasma-systemmonitor --page-name Processes" property string terminal: "kitty -1" // This is only for shell actions + property string update: "kitty -1 --hold=yes fish -i -c 'sudo pacman -Syu'" property string volumeMixer: `~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol"` } @@ -158,7 +160,8 @@ Singleton { property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy" property real x: 100 property real y: 100 - property string style: "cookie" // Options: "cookie", "digital" + property string style: "cookie" // Options: "cookie", "digital" + property string styleLocked: "cookie" // Options: "cookie", "digital" property JsonObject cookie: JsonObject { property bool aiStyling: false property int sides: 14 @@ -236,13 +239,6 @@ Singleton { property bool showPerformanceProfileToggle: false property bool showScreenRecord: false } - property JsonObject tray: JsonObject { - property bool monochromeIcons: true - property bool showItemId: false - property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area - property list pinnedItems: [ ] - property bool filterPassive: true - } property JsonObject workspaces: JsonObject { property bool monochromeIcons: true property int shown: 10 @@ -264,6 +260,9 @@ Singleton { property bool showUnreadCount: false } } + property JsonObject tooltips: JsonObject { + property bool clickToShow: false + } } property JsonObject battery: JsonObject { @@ -426,6 +425,14 @@ Singleton { property int historyLength: 60 } + property JsonObject tray: JsonObject { + property bool monochromeIcons: true + property bool showItemId: false + property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area + property list pinnedItems: [ "Fcitx" ] + property bool filterPassive: true + } + property JsonObject musicRecognition: JsonObject { property int timeout: 16 property int interval: 4 @@ -532,6 +539,12 @@ Singleton { } property bool secondPrecision: false } + + property JsonObject updates: JsonObject { + property int checkInterval: 120 // minutes + property int adviseUpdateThreshold: 75 // packages + property int stronglyAdviseUpdateThreshold: 200 // packages + } property JsonObject wallpaperSelector: JsonObject { property bool useSystemFileDialog: false @@ -559,6 +572,10 @@ Singleton { } property JsonObject waffles: JsonObject { + // Animations on Windoes are kinda janky. Set the following to + // false will make (some) stuff also be like that for accuracy. + // Example: the right-click menu of the Start button + property bool smootherAnimations: true property JsonObject bar: JsonObject { property bool bottom: true property bool leftAlignApps: false diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index 8e70506d3..56f647684 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -32,7 +32,9 @@ Singleton { property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`) property string shellConfigName: "config.json" property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}` - property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`) + property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`) + property string notesPath: FileUtils.trimFileProtocol(`${Directories.state}/user/notes.txt`) + property string conflictCachePath: FileUtils.trimFileProtocol(`${Directories.cache}/conflict-killer`) property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`) property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`) property string generatedWallpaperCategoryPath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/wallpaper/category.txt`) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 212fc34b9..478cadd10 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -131,6 +131,14 @@ Singleton { property real height: 600 property int tabIndex: 0 } + property JsonObject notes: JsonObject { + property bool pinned: false + property bool clickthrough: true + property real x: 1400 + property real y: 42 + property real width: 460 + property real height: 330 + } } property JsonObject timer: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/common/functions/StringUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/StringUtils.qml index 88ab95b2d..0839b5386 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/StringUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/StringUtils.qml @@ -285,4 +285,14 @@ Singleton { } return str; } + + function toTitleCase(str) { + // Replace "-" and "_" with space, then capitalize each word + return str.replace(/[-_]/g, " ").replace( + /\w\S*/g, + function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + } + ); + } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml b/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml index aaa2753f8..f5064939b 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/FadeLoader.qml @@ -6,11 +6,13 @@ import qs.modules.common.widgets Loader { id: root property bool shown: true + property alias fade: opacityBehavior.enabled opacity: shown ? 1 : 0 visible: opacity > 0 active: opacity > 0 Behavior on opacity { + id: opacityBehavior animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/NotificationListView.qml b/dots/.config/quickshell/ii/modules/common/widgets/NotificationListView.qml index 1b68059d0..ec76349bb 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/NotificationListView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/NotificationListView.qml @@ -18,10 +18,9 @@ StyledListView { // Scrollable window required property int index required property var modelData popup: root.popup - anchors.left: parent?.left - anchors.right: parent?.right + width: ListView.view.width // https://doc.qt.io/qt-6/qml-qtquick-listview.html notificationGroup: popup ? Notifications.popupGroupsByAppName[modelData] : Notifications.groupsByAppName[modelData] } -} \ No newline at end of file +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml b/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml index bc72ee416..afc9957a4 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml @@ -14,6 +14,10 @@ Item { property real horizontalPadding: 10 property real verticalPadding: 5 + function updateAnchor() { + tooltipLoader.item?.anchor.updateAnchor(); + } + readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition property var anchorEdges: Edges.Top property var anchorGravity: anchorEdges diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledTextArea.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledTextArea.qml index b1f4579d2..13702e1f9 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledTextArea.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledTextArea.qml @@ -10,6 +10,7 @@ TextArea { selectedTextColor: Appearance.m3colors.m3onSecondaryContainer selectionColor: Appearance.colors.colSecondaryContainer placeholderTextColor: Appearance.m3colors.m3outline + color: Appearance.colors.colOnLayer0 font { family: Appearance.font.family.main pixelSize: Appearance?.font.pixelSize.small ?? 15 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml index 4d4d56335..1bca18463 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml @@ -5,7 +5,6 @@ import qs.services import QtQuick import QtQuick.Controls import QtQuick.Layouts -import Qt.labs.synchronizer Item { id: root diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml index 6d636be89..97e1e468a 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml @@ -16,7 +16,7 @@ AbstractBackgroundWidget { implicitHeight: contentColumn.implicitHeight implicitWidth: contentColumn.implicitWidth - readonly property string clockStyle: Config.options.background.widgets.clock.style + readonly property string clockStyle: GlobalStates.screenLocked ? Config.options.background.widgets.clock.styleLocked : Config.options.background.widgets.clock.style readonly property bool forceCenter: (GlobalStates.screenLocked && Config.options.lock.centerClock) readonly property bool shouldShow: (!Config.options.background.widgets.clock.showOnlyWhenLocked || GlobalStates.screenLocked) property bool wallpaperSafetyTriggered: false @@ -44,6 +44,7 @@ AbstractBackgroundWidget { id: cookieClockLoader anchors.horizontalCenter: parent.horizontalCenter shown: root.clockStyle === "cookie" && (root.shouldShow) + fade: false sourceComponent: Column { spacing: 10 CookieClock { @@ -61,6 +62,7 @@ AbstractBackgroundWidget { id: digitalClockLoader anchors.horizontalCenter: parent.horizontalCenter shown: root.clockStyle === "digital" && (root.shouldShow) + fade: false sourceComponent: ColumnLayout { id: clockColumn spacing: 6 diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml b/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml index 3cabf9f36..36799fefd 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml @@ -16,7 +16,7 @@ MouseArea { implicitWidth: batteryProgress.implicitWidth implicitHeight: Appearance.sizes.barHeight - hoverEnabled: true + hoverEnabled: !Config.options.bar.tooltips.clickToShow ClippedProgressBar { id: batteryProgress diff --git a/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml index 958695a86..1d4b9f865 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/ClockWidget.qml @@ -40,8 +40,7 @@ Item { MouseArea { id: mouseArea anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton + hoverEnabled: !Config.options.bar.tooltips.clickToShow ClockWidgetPopup { hoverTarget: mouseArea diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Resources.qml b/dots/.config/quickshell/ii/modules/ii/bar/Resources.qml index 6c25ab507..2d9e936ac 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Resources.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Resources.qml @@ -9,7 +9,7 @@ MouseArea { property bool alwaysShowAllResources: false implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin implicitHeight: Appearance.sizes.barHeight - hoverEnabled: true + hoverEnabled: !Config.options.bar.tooltips.clickToShow RowLayout { id: rowLayout diff --git a/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml b/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml index 0233b2405..56d33e0d4 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/SysTray.qml @@ -1,10 +1,11 @@ -import qs.modules.common -import qs.modules.common.widgets import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Hyprland import Quickshell.Services.SystemTray +import qs.services +import qs.modules.common +import qs.modules.common.widgets Item { id: root @@ -17,13 +18,8 @@ Item { property bool showOverflowMenu: true property var activeMenu: null - property bool smartTray: Config.options.bar.tray.filterPassive - property list itemsInUserList: SystemTray.items.values.filter(i => (Config.options.bar.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive))) - property list itemsNotInUserList: SystemTray.items.values.filter(i => (!Config.options.bar.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive))) - - property bool invertPins: Config.options.bar.tray.invertPinnedItems - property list pinnedItems: invertPins ? itemsNotInUserList : itemsInUserList - property list unpinnedItems: invertPins ? itemsInUserList : itemsNotInUserList + property list pinnedItems: TrayService.pinnedItems + property list unpinnedItems: TrayService.unpinnedItems onUnpinnedItemsChanged: { if (unpinnedItems.length == 0) root.closeOverflowMenu(); } diff --git a/dots/.config/quickshell/ii/modules/ii/bar/SysTrayItem.qml b/dots/.config/quickshell/ii/modules/ii/bar/SysTrayItem.qml index 1fe5df5f3..6230de0f8 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/SysTrayItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/SysTrayItem.qml @@ -1,11 +1,12 @@ -import qs.modules.common -import qs.modules.common.widgets -import qs.modules.common.functions import QtQuick import Quickshell import Quickshell.Services.SystemTray import Quickshell.Widgets import Qt5Compat.GraphicalEffects +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions MouseArea { id: root @@ -31,10 +32,7 @@ MouseArea { event.accepted = true; } onEntered: { - tooltip.text = item.tooltipTitle.length > 0 ? item.tooltipTitle - : (item.title.length > 0 ? item.title : item.id); - if (item.tooltipDescription.length > 0) tooltip.text += " • " + item.tooltipDescription; - if (Config.options.bar.tray.showItemId) tooltip.text += "\n[" + item.id + "]"; + tooltip.text = TrayService.getTooltipForItem(root.item); } Loader { @@ -65,7 +63,7 @@ MouseArea { IconImage { id: trayIcon - visible: !Config.options.bar.tray.monochromeIcons + visible: !Config.options.tray.monochromeIcons source: root.item.icon anchors.centerIn: parent width: parent.width @@ -73,7 +71,7 @@ MouseArea { } Loader { - active: Config.options.bar.tray.monochromeIcons + active: Config.options.tray.monochromeIcons anchors.fill: trayIcon sourceComponent: Item { Desaturate { diff --git a/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherBar.qml b/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherBar.qml index 1fcde7176..e82f41dab 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherBar.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherBar.qml @@ -13,15 +13,19 @@ MouseArea { implicitWidth: rowLayout.implicitWidth + 10 * 2 implicitHeight: Appearance.sizes.barHeight - hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: !Config.options.bar.tooltips.clickToShow onPressed: { - Weather.getData(); - Quickshell.execDetached(["notify-send", - Translation.tr("Weather"), - Translation.tr("Refreshing (manually triggered)") - , "-a", "Shell" - ]) + if (mouse.button === Qt.RightButton) { + Weather.getData(); + Quickshell.execDetached(["notify-send", + Translation.tr("Weather"), + Translation.tr("Refreshing (manually triggered)") + , "-a", "Shell" + ]) + mouse.accepted = false + } } RowLayout { diff --git a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml index f44efcb3e..d133fa654 100644 --- a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml +++ b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml @@ -5,7 +5,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Qt5Compat.GraphicalEffects -import Qt.labs.synchronizer import Quickshell.Io import Quickshell import Quickshell.Wayland diff --git a/dots/.config/quickshell/ii/modules/ii/dock/DockAppButton.qml b/dots/.config/quickshell/ii/modules/ii/dock/DockAppButton.qml index 41ed8cd07..8d2d270ae 100644 --- a/dots/.config/quickshell/ii/modules/ii/dock/DockAppButton.qml +++ b/dots/.config/quickshell/ii/modules/ii/dock/DockAppButton.qml @@ -67,11 +67,7 @@ DockButton { } altAction: () => { - if (Config.options.dock.pinnedApps.indexOf(appToplevel.appId) !== -1) { - Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.filter(id => id !== appToplevel.appId) - } else { - Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.concat([appToplevel.appId]) - } + TaskbarApps.togglePin(appToplevel.appId); } contentItem: Loader { diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/ii/overlay/Overlay.qml index 71ba510c6..65901ea13 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/Overlay.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/Overlay.qml @@ -25,7 +25,8 @@ Scope { exclusionMode: ExclusionMode.Ignore WlrLayershell.namespace: "quickshell:overlay" WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + // Use OnDemand for pinned widgets to allow focus switching with mouse clicks + WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : (OverlayContext.clickableWidgets.length > 0 ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None) visible: true color: "transparent" diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayContext.qml b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayContext.qml index 15e1ef91b..bb68cefc3 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayContext.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayContext.qml @@ -11,6 +11,7 @@ Singleton { { identifier: "floatingImage", materialSymbol: "imagesmode" }, { identifier: "recorder", materialSymbol: "screen_record" }, { identifier: "resources", materialSymbol: "browse_activity" }, + { identifier: "notes", materialSymbol: "note_stack" }, { identifier: "volumeMixer", materialSymbol: "volume_up" }, ] diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayWidgetDelegateChooser.qml b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayWidgetDelegateChooser.qml index 6e4bc6522..e004be7dd 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayWidgetDelegateChooser.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayWidgetDelegateChooser.qml @@ -8,10 +8,11 @@ import Quickshell import Quickshell.Bluetooth import qs.modules.ii.overlay.crosshair import qs.modules.ii.overlay.volumeMixer +import qs.modules.ii.overlay.floatingImage import qs.modules.ii.overlay.fpsLimiter import qs.modules.ii.overlay.recorder import qs.modules.ii.overlay.resources -import qs.modules.ii.overlay.floatingImage +import qs.modules.ii.overlay.notes DelegateChooser { id: root @@ -22,5 +23,6 @@ DelegateChooser { DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} } DelegateChoice { roleValue: "recorder"; Recorder {} } DelegateChoice { roleValue: "resources"; Resources {} } + DelegateChoice { roleValue: "notes"; Notes {} } DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} } } diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/notes/Notes.qml b/dots/.config/quickshell/ii/modules/ii/overlay/notes/Notes.qml new file mode 100644 index 000000000..64aa11d05 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/overlay/notes/Notes.qml @@ -0,0 +1,17 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.ii.overlay + +StyledOverlayWidget { + id: root + title: Translation.tr("Notes") + showCenterButton: true + + contentItem: NotesContent { + radius: root.contentRadius + isClickthrough: root.clickthrough + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/notes/NotesContent.qml b/dots/.config/quickshell/ii/modules/ii/overlay/notes/NotesContent.qml new file mode 100644 index 000000000..e52c226b1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/overlay/notes/NotesContent.qml @@ -0,0 +1,292 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.ii.overlay + +OverlayBackground { + id: root + + property alias content: textInput.text + property bool pendingReload: false + property var copyListEntries: [] + property string lastParsedCopylistText: "" + property var parsedCopylistLines: [] + property bool isClickthrough: false + property real maxCopyButtonSize: 20 + + Component.onCompleted: { + noteFile.reload(); + updateCopyListEntries(); + } + + function saveContent() { + if (!textInput) + return; + noteFile.setText(root.content); + } + + function focusAtEnd() { + if (!textInput) + return; + textInput.forceActiveFocus(); + const endPos = root.content.length; + applySelection(endPos, endPos); + } + + function applySelection(cursorPos, anchorPos) { + if (!textInput) + return; + const textLength = root.content.length; + const cursor = Math.max(0, Math.min(cursorPos, textLength)); + const anchor = Math.max(0, Math.min(anchorPos, textLength)); + textInput.select(anchor, cursor); + if (cursor === anchor) + textInput.deselect(); + } + + function scheduleCopylistUpdate(immediate = false) { + if (!textInput) + return; + if (immediate) { + copyListDebounce?.stop(); + updateCopyListEntries(); + } else { + copyListDebounce.restart(); + } + } + + function updateCopyListEntries() { + if (!textInput) + return; + const textValue = root.content; + if (!textValue || textValue.length === 0) { + lastParsedCopylistText = ""; + parsedCopylistLines = []; + root.copyListEntries = []; + return; + } + + if (textValue !== lastParsedCopylistText) { + const lineRegex = /(.*?)(\r?\n|$)/g; + let match = null; + const parsed = []; + while ((match = lineRegex.exec(textValue)) !== null) { + const lineText = match[1]; + const newlineText = match[2]; + const lineStart = match.index; + const lineEnd = lineStart + lineText.length; + const bulletMatch = lineText.match(/^\s*-\s+(.*\S)\s*$/); + if (bulletMatch) { + parsed.push({ + content: bulletMatch[1].trim(), + start: lineStart, + end: lineEnd + }); + } + if (newlineText === "") + break; + } + lastParsedCopylistText = textValue; + parsedCopylistLines = parsed; + if (parsed.length === 0) { + root.copyListEntries = []; + return; + } + } + + updateCopylistPositions(); + } + + function updateCopylistPositions() { + if (!textInput || parsedCopylistLines.length === 0) + return; + const rawSelectionStart = textInput.selectionStart; + const rawSelectionEnd = textInput.selectionEnd; + const selectionStart = rawSelectionStart === -1 ? textInput.cursorPosition : rawSelectionStart; + const selectionEnd = rawSelectionEnd === -1 ? textInput.cursorPosition : rawSelectionEnd; + const rangeStart = Math.min(selectionStart, selectionEnd); + const rangeEnd = Math.max(selectionStart, selectionEnd); + + const entries = parsedCopylistLines.map(line => { + // Don't show copy button if line is (partially) selected + const caretIntersects = rangeEnd > line.start && rangeStart <= line.end; + if (caretIntersects) + return null; + const startRect = textInput.positionToRectangle(line.start); + let endRect = textInput.positionToRectangle(line.end); + if (!isFinite(startRect.y)) + return null; + if (!isFinite(endRect.y)) + endRect = startRect; + const lineBottom = endRect.y + endRect.height; + const rectHeight = Math.max(lineBottom - startRect.y, textInput.font.pixelSize + 8); + return { + content: line.content, + y: startRect.y, + height: rectHeight + }; + }).filter(entry => entry !== null); + + root.copyListEntries = entries; + } + + implicitWidth: 300 + implicitHeight: 200 + + ColumnLayout { + id: contentItem + anchors.fill: parent + spacing: -16 + + ScrollView { + id: editorScrollView + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + ScrollBar.vertical.policy: ScrollBar.AsNeeded + onWidthChanged: root.scheduleCopylistUpdate(true) + + StyledTextArea { // This has to be a direct child of ScrollView for proper scrolling + id: textInput + anchors { + left: parent.left + right: parent.right + } + wrapMode: TextEdit.Wrap + placeholderText: Translation.tr("Write something here...\nUse '-' to create copyable bullet points, like this:\n\nSheep fricker\n- 4x Slab\n- 1x Boat\n- 4x Redstone Dust\n- 1x Sticky Piston\n- 1x End Rod\n- 4x Redstone Repeater\n- 1x Redstone Torch\n- 1x Sheep") + selectByMouse: true + persistentSelection: true + textFormat: TextEdit.PlainText + background: null + padding: 24 + + onTextChanged: { + if (textInput.activeFocus) { + saveDebounce.restart(); + } + root.scheduleCopylistUpdate(true); + } + + onHeightChanged: root.scheduleCopylistUpdate(true) + onContentHeightChanged: root.scheduleCopylistUpdate(true) + onCursorPositionChanged: root.scheduleCopylistUpdate() + onSelectionStartChanged: root.scheduleCopylistUpdate() + onSelectionEndChanged: root.scheduleCopylistUpdate() + } + + Item { + anchors.fill: parent + visible: root.copyListEntries.length > 0 + clip: true + + Repeater { + model: ScriptModel { + values: root.copyListEntries + } + delegate: RippleButton { + id: copyButton + required property var modelData + readonly property real lineHeight: Math.min(Math.max(modelData.height, Appearance.font.pixelSize.normal + 6), root.maxCopyButtonSize) + readonly property real iconSizeLocal: Appearance.font.pixelSize.normal + readonly property real hitPadding: 6 + property bool justCopied: false + + implicitHeight: lineHeight + implicitWidth: lineHeight + buttonRadius: height / 2 + y: modelData.y + anchors.right: parent.right + anchors.rightMargin: 10 + z: 5 + + Timer { + id: resetState + interval: 700 + onTriggered: { + copyButton.justCopied = false; + } + } + + onClicked: { + Quickshell.clipboardText = copyButton.modelData.content; + justCopied = true; + resetState.start(); + } + + contentItem: Item { + anchors.centerIn: parent + MaterialSymbol { + id: iconItem + anchors.centerIn: parent + text: copyButton.justCopied ? "check" : "content_copy" + iconSize: copyButton.iconSizeLocal + color: Appearance.colors.colOnLayer1 + } + } + } + } + } + } + + StyledText { + id: statusLabel + Layout.fillWidth: true + Layout.margins: 16 + horizontalAlignment: Text.AlignRight + text: saveDebounce.running ? Translation.tr("Saving...") : Translation.tr("Saved ") + color: Appearance.colors.colSubtext + } + } + + Timer { + id: saveDebounce + interval: 500 + repeat: false + onTriggered: saveContent() + } + + Timer { + id: copyListDebounce + interval: 100 + repeat: false + onTriggered: updateCopylistPositions() + } + + FileView { + id: noteFile + path: Qt.resolvedUrl(Directories.notesPath) + onLoaded: { + root.content = noteFile.text(); + if (root.content !== root.content) { + const previousCursor = textInput.cursorPosition; + const previousAnchor = textInput.selectionStart; + root.content = root.content; + applySelection(previousCursor, previousAnchor); + } + if (pendingReload) { + pendingReload = false; + Qt.callLater(root.focusAtEnd); + } + Qt.callLater(root.updateCopyListEntries); + } + onLoadFailed: error => { + if (error === FileViewError.FileNotFound) { + root.content = ""; + noteFile.setText(root.content); + if (pendingReload) { + pendingReload = false; + Qt.callLater(root.focusAtEnd); + } + Qt.callLater(root.updateCopyListEntries); + } else { + console.log("[Overlay Notes] Error loading file: " + error); + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/resources/Resources.qml b/dots/.config/quickshell/ii/modules/ii/overlay/resources/Resources.qml index 8bec779bd..512307fc1 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/resources/Resources.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/resources/Resources.qml @@ -5,7 +5,6 @@ import QtQuick.Layouts import Quickshell import Quickshell.Hyprland import Qt5Compat.GraphicalEffects -import Qt.labs.synchronizer import qs import qs.services import qs.modules.common diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index 6385b2485..248b46b56 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -167,6 +167,9 @@ Scope { function toggle() { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; } + function workspacesToggle() { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } function close() { GlobalStates.overviewOpen = false; } @@ -189,6 +192,14 @@ Scope { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; } } + GlobalShortcut { + name: "overviewWorkspacesToggle" + description: "Toggles overview on press" + + onPressed: { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + } GlobalShortcut { name: "overviewClose" description: "Closes overview" diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AiMessage.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AiMessage.qml index 74d5c5f6a..487665851 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AiMessage.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/AiMessage.qml @@ -285,48 +285,36 @@ Rectangle { } } } - Repeater { model: ScriptModel { - values: Array.from({ length: root.messageBlocks.length }, (msg, i) => { - return ({ - type: root.messageBlocks[i].type - }) - }); + values: root.messageBlocks } - delegate: DelegateChooser { id: messageDelegate role: "type" DelegateChoice { roleValue: "code"; MessageCodeBlock { - required property int index - property var thisBlock: root.messageBlocks[index] editing: root.editing renderMarkdown: root.renderMarkdown enableMouseSelection: root.enableMouseSelection - segmentContent: thisBlock.content - segmentLang: thisBlock.lang + segmentContent: modelData.content + segmentLang: modelData.lang messageData: root.messageData } } DelegateChoice { roleValue: "think"; MessageThinkBlock { - required property int index - property var thisBlock: root.messageBlocks[index] editing: root.editing renderMarkdown: root.renderMarkdown enableMouseSelection: root.enableMouseSelection - segmentContent: thisBlock.content + segmentContent: modelData.content messageData: root.messageData done: root.messageData?.done ?? false - completed: thisBlock.completed ?? false + completed: modelData.completed ?? false } } DelegateChoice { roleValue: "text"; MessageTextBlock { - required property int index - property var thisBlock: root.messageBlocks[index] editing: root.editing renderMarkdown: root.renderMarkdown enableMouseSelection: root.enableMouseSelection - segmentContent: thisBlock.content + segmentContent: modelData.content messageData: root.messageData done: root.messageData?.done ?? false forceDisableChunkSplitting: root.messageData?.content.includes("```") ?? true diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/MessageThinkBlock.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/MessageThinkBlock.qml index 1463c6e60..ee00ffdff 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/MessageThinkBlock.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/aiChat/MessageThinkBlock.qml @@ -156,7 +156,6 @@ Item { property bool editing: root.editing property bool renderMarkdown: root.renderMarkdown property bool enableMouseSelection: root.enableMouseSelection - property string segmentContent: root.segmentContent property var messageData: root.messageData property bool done: root.done @@ -165,8 +164,9 @@ Item { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom + segmentContent: root.segmentContent } } } } -} \ No newline at end of file +} diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml index ae76d5751..d592de63b 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml @@ -12,6 +12,7 @@ AndroidQuickToggleButton { name: Translation.tr("Bluetooth") statusText: BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("No device") + available: BluetoothStatus.available toggled: BluetoothStatus.enabled buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" mainAction: () => { @@ -27,4 +28,3 @@ AndroidQuickToggleButton { ) } } - diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml index bfd14335c..0ce6b7ab4 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml @@ -9,6 +9,7 @@ AndroidQuickToggleButton { name: Translation.tr("EasyEffects") + available: EasyEffects.available toggled: EasyEffects.active buttonIcon: "graphic_eq" diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml index 96401a6c0..31ad2f262 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml @@ -15,6 +15,7 @@ GroupButton { required property string name required property var mainAction property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive") + property bool available: true required property real baseCellWidth required property real baseCellHeight @@ -42,6 +43,7 @@ GroupButton { signal openMenu() + enabled: available || editMode padding: 6 horizontalPadding: padding verticalPadding: padding @@ -52,8 +54,8 @@ GroupButton { colBackgroundToggledActive: (altAction && expandedSize) ? Appearance.colors.colLayer2Active : Appearance.colors.colPrimaryActive buttonRadius: toggled ? Appearance.rounding.large : height / 2 buttonRadiusPressed: Appearance.rounding.normal - property color colText: (toggled && !(altAction && expandedSize)) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer2 - property color colIcon: expandedSize ? (root.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer3) : colText + property color colText: (toggled && !(altAction && expandedSize) && enabled) ? Appearance.colors.colOnPrimary : ColorUtils.transparentize(Appearance.colors.colOnLayer2, enabled ? 0 : 0.7) + property color colIcon: expandedSize ? ((root.toggled) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer3) : colText onClicked: { if (root.expandedSize && root.altAction) root.altAction(); diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml index d2b729244..3f20cbd2c 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/BatteryIndicator.qml @@ -15,7 +15,7 @@ MouseArea { readonly property bool isLow: percentage <= Config.options.battery.low / 100 implicitHeight: batteryProgress.implicitHeight - hoverEnabled: true + hoverEnabled: !Config.options.bar.tooltips.clickToShow ClippedProgressBar { id: batteryProgress diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/Resources.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/Resources.qml index 353874723..1c3815721 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/Resources.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/Resources.qml @@ -9,7 +9,7 @@ MouseArea { property bool alwaysShowAllResources: false implicitHeight: columnLayout.implicitHeight implicitWidth: columnLayout.implicitWidth - hoverEnabled: true + hoverEnabled: !Config.options.bar.tooltips.clickToShow ColumnLayout { id: columnLayout diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalClockWidget.qml index 15a49c5a5..3f9ca818a 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalClockWidget.qml @@ -33,8 +33,7 @@ Item { MouseArea { id: mouseArea anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton + hoverEnabled: !Config.options.bar.tooltips.clickToShow Bar.ClockWidgetPopup { hoverTarget: mouseArea diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalMedia.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalMedia.qml index 8d38caa76..4db432b26 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalMedia.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalMedia.qml @@ -28,7 +28,7 @@ MouseArea { } acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton | Qt.LeftButton - hoverEnabled: true + hoverEnabled: !Config.options.bar.tooltips.clickToShow onPressed: (event) => { if (event.button === Qt.MiddleButton) { activePlayer.togglePlaying(); diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index ec9dc320d..66630cc24 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -55,6 +55,20 @@ ContentPage { ContentSection { icon: "clock_loader_40" title: Translation.tr("Widget: Clock") + id: settingsClock + + function stylePresent(styleName) { + if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) { + return true; + } + if (Config.options.background.widgets.clock.styleLocked === styleName) { + return true; + } + return false; + } + + readonly property bool digitalPresent: stylePresent("digital") + readonly property bool cookiePresent: stylePresent("cookie") ConfigRow { Layout.fillWidth: true @@ -107,6 +121,7 @@ ContentPage { } ContentSubsection { + visible: !Config.options.background.widgets.clock.showOnlyWhenLocked title: Translation.tr("Clock style") ConfigSelectionArray { currentValue: Config.options.background.widgets.clock.style @@ -129,7 +144,29 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "digital" + title: Translation.tr("Clock style (locked)") + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.styleLocked + onSelected: newValue => { + Config.options.background.widgets.clock.styleLocked = newValue; + } + options: [ + { + displayName: Translation.tr("Digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Cookie"), + icon: "cookie", + value: "cookie" + } + ] + } + } + + ContentSubsection { + visible: settingsClock.digitalPresent title: Translation.tr("Digital clock settings") ConfigSwitch { @@ -143,7 +180,7 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" + visible: settingsClock.cookiePresent title: Translation.tr("Cookie clock settings") ConfigSwitch { @@ -197,7 +234,7 @@ ContentPage { ConfigRow { ConfigSwitch { - enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full" + enabled: Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full" buttonIcon: "brightness_7" text: Translation.tr("Hour marks") checked: Config.options.background.widgets.clock.cookie.hourMarks @@ -213,7 +250,7 @@ ContentPage { } ConfigSwitch { - enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers" + enabled: Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers" buttonIcon: "timer_10" text: Translation.tr("Digits in the middle") checked: Config.options.background.widgets.clock.cookie.timeIndicators @@ -231,7 +268,7 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" + visible: settingsClock.cookiePresent title: Translation.tr("Dial style") ConfigSelectionArray { currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle @@ -270,7 +307,7 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" + visible: settingsClock.cookiePresent title: Translation.tr("Hour hand") ConfigSelectionArray { currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle @@ -303,7 +340,7 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" + visible: settingsClock.cookiePresent title: Translation.tr("Minute hand") ConfigSelectionArray { @@ -342,7 +379,7 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" + visible: settingsClock.cookiePresent title: Translation.tr("Second hand") ConfigSelectionArray { @@ -376,7 +413,7 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" + visible: settingsClock.cookiePresent title: Translation.tr("Date style") ConfigSelectionArray { diff --git a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml index a53d30644..bfe4c256e 100644 --- a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml @@ -148,18 +148,18 @@ ContentPage { ConfigSwitch { buttonIcon: "keep" text: Translation.tr('Make icons pinned by default') - checked: Config.options.bar.tray.invertPinnedItems + checked: Config.options.tray.invertPinnedItems onCheckedChanged: { - Config.options.bar.tray.invertPinnedItems = checked; + Config.options.tray.invertPinnedItems = checked; } } ConfigSwitch { buttonIcon: "colors" text: Translation.tr('Tint icons') - checked: Config.options.bar.tray.monochromeIcons + checked: Config.options.tray.monochromeIcons onCheckedChanged: { - Config.options.bar.tray.monochromeIcons = checked; + Config.options.tray.monochromeIcons = checked; } } } @@ -334,4 +334,17 @@ ContentPage { } } } + + ContentSection { + icon: "tooltip" + title: Translation.tr("Tooltips") + ConfigSwitch { + buttonIcon: "ads_click" + text: Translation.tr("Click to show") + checked: Config.options.bar.tooltips.clickToShow + onCheckedChanged: { + Config.options.bar.tooltips.clickToShow = checked; + } + } + } } diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 738578585..1614ec47f 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -22,7 +22,7 @@ ContentPage { // Use a nerdfont to see the icons options: ([ "󰖳", "", "󰨡", "", "󰌽", "󰣇", "", "", "", - "", "", "󱄛", "", "", "⌘", "󰀲", "󰟍", "" + "", "", "󱄛", "", "", "", "⌘", "󰀲", "󰟍", "" ]).map(icon => { return { displayName: icon, value: icon diff --git a/dots/.config/quickshell/ii/modules/waffle/README.md b/dots/.config/quickshell/ii/modules/waffle/README.md new file mode 100644 index 000000000..299635be9 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/README.md @@ -0,0 +1,37 @@ +## Waffle + +A recreation of Windoes. It's WIP! + +- If you install illogical-impulse fully, you can press Super+Alt+W to switch to this style. +- If you're just copying the Quickshell config, run the config as usual (`qs -c ii`) then run `qs -c ii ipc call panelFamily cycle` + +## From EWW version to Quickshell + +Just a reflection, in case anyone's interested. My blog is probably a better place for this, but it does not exist. Besides, this is going to change as I do more stuff. Currently there's just the bar. + +### Improvements + +- QtQuick's `Button` has the `{top/bottom/left/right}Inset` properties, so we can have clickable regions expanding beyond the button background for free. With EWW it was annoying to wrap the button content with an `eventbox` that has some padding, then somehow use CSS selectors to make sure hovering effects work. I have to admit, (a large) part of that annoyance was with how bad my copy-pasting coding practice was at the time, but still... + +- Fancy effects: Gtk3 CSS does not support transformations. In QtQuick we can smack `rotation` and `scale` almost everywhere, so it's simple to make bouncy icons and rotating chevrons + +- Quickshell provides a system tray service (EWW does now but didn't at the time I created the EWW Windoes version), so now there's no Waybar needed for the tray. + +- QtQuick has `Loader`s, so we can have this live-switchable from the main style without killing the widget system, moving styles to the correct folder, and relaunching. + +- This time my computer is powerful enough to run a VM, so I don't have to occasionally reboot to take quick screenshots for reference. I try to make everything pixel-perfect so this is necessary. Speaking about pixel-perfectness, in the EWW version I hardcoded sizes, but this time I'm still doing that (lol), BUT that's normal and not a problem because Qt has the `QT_SCALE_FACTOR` env var for scaling. (Please feel free to prove me wrong in saying Gtk3 doesn't have that magic) + +### Challenges + +- Qt is not Gtk and definitely not React + - We don't get directional border on QtQuick `Rectangle`s like in CSS. I was able to get around this with manual drawing, but it was a bit more work + +- Fluent Icons is difficult to use, compared to Material Symbols + - No React, so no clean use via a library. + - If we use the font, there's no proper, searchable **codepoint** cheatsheet like Nerd Fonts, and there's no ligatures + - I resorted to downloading individual SVGs. Not that nice, but it's better than scanning the whole table of icons every time I want one. For this we have fluenticon.com and fluenticons.co, but icons are awkwardly named and there's no alias. Why is the reload/refresh icon called "arrow-sync"? Well, the name is not misleading, but arguably reload/refresh are more common actions. From Fluent Design's [page on Iconography](https://fluent2.microsoft.design/iconography): + + > Fluent system icons are literal metaphors and are named for the shape or object they represent, not the functionality they provide + + "sync" is functionality. + diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml new file mode 100644 index 000000000..6d2e528db --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml @@ -0,0 +1,77 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +WBarAttachedPanelContent { + id: root + + contentItem: ColumnLayout { + anchors.centerIn: parent + spacing: 0 + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + topLeftRadius: root.border.radius - root.border.border.width + topRightRadius: topLeftRadius + color: Looks.colors.bgPanelBody + + implicitWidth: 360 + implicitHeight: 380 + } + + Rectangle { + Layout.fillHeight: false + Layout.fillWidth: true + color: Looks.colors.bgPanelSeparator + implicitHeight: 1 + } + + Rectangle { + Layout.fillHeight: false + Layout.fillWidth: true + bottomLeftRadius: root.border.radius - root.border.border.width + bottomRightRadius: bottomLeftRadius + color: Looks.colors.bgPanelFooter + + implicitWidth: 360 + implicitHeight: 47 + + // Battery button + WPanelFooterButton { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 12 + + contentItem: Row { + spacing: 4 + + FluentIcon { + anchors.verticalCenter: parent.verticalCenter + icon: WIcons.batteryIcon + } + WText { + anchors.verticalCenter: parent.verticalCenter + text: `${Math.round(Battery.percentage * 100) || 0}%` + } + } + } + + // Settings button + WPanelFooterButton { + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 12 + + contentItem: FluentIcon { + icon: "settings" + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml new file mode 100644 index 000000000..86e610238 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml @@ -0,0 +1,85 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets + +Scope { + id: root + + Connections { + target: GlobalStates + + function onSidebarLeftOpenChanged() { + if (GlobalStates.sidebarLeftOpen) barLoader.active = true; + } + } + + Loader { + id: barLoader + active: GlobalStates.sidebarLeftOpen + sourceComponent: PanelWindow { + id: panelWindow + exclusiveZone: 0 + WlrLayershell.namespace: "quickshell:actionCenter" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + anchors { + bottom: Config.options.waffles.bar.bottom + top: !Config.options.waffles.bar.bottom + right: true + } + + implicitWidth: content.implicitWidth + content.visualMargin * 2 + implicitHeight: content.implicitHeight + content.visualMargin * 2 + + HyprlandFocusGrab { + id: focusGrab + active: true + windows: [panelWindow] + onCleared: content.close(); + } + + Connections { + target: GlobalStates + function onSidebarLeftOpenChanged() { + if (!GlobalStates.sidebarLeftOpen) content.close(); + } + } + + ActionCenterContent { + id: content + anchors.centerIn: parent + + onClosed: { + barLoader.active = false; + GlobalStates.sidebarLeftOpen = false; + } + } + } + } + + function toggleOpen() { + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + } + + IpcHandler { + target: "sidebarLeft" + + function toggle() { + root.toggleOpen(); + } + } + + GlobalShortcut { + name: "sidebarLeftToggle" + description: "Toggles left sidebar on press" + + onPressed: root.toggleOpen(); + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml index a35e12a2e..7f527c97d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml @@ -17,10 +17,12 @@ BarButton { rightInset: 2 implicitWidth: height - topInset - bottomInset + leftInset + rightInset + property real pressedScale: 5/6 + onDownChanged: { scaleAnim.duration = root.down ? 150 : 200 scaleAnim.easing.bezierCurve = root.down ? Looks.transition.easing.bezierCurve.easeIn : Looks.transition.easing.bezierCurve.easeOut - contentItem.scale = root.down ? 5/6 : 1 // If/When we do dragging, the scale is 1.25 + contentItem.scale = root.down ? root.pressedScale : 1 // If/When we do dragging, the scale is 1.25 } background: Item { @@ -45,9 +47,7 @@ BarButton { anchors.fill: parent anchors.rightMargin: 5 active: root.multiple - sourceComponent: BackgroundAcrylicRectangle { - - } + sourceComponent: BackgroundAcrylicRectangle {} } } @@ -75,7 +75,7 @@ BarButton { component BackgroundAcrylicRectangle: AcrylicRectangle { shiny: ((root.hovered && !root.down) || root.checked) - color: root.colBackground + color: root.color border.width: 1 border.color: root.colBackgroundBorder diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml b/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml index f70a80603..48ff26104 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml @@ -13,6 +13,8 @@ Kirigami.Icon { property real implicitSize: 26 implicitWidth: implicitSize implicitHeight: implicitSize + + animated: true roundToIconSize: false fallback: root.iconName source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml index ce1a4cdc7..f8192389a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml @@ -8,11 +8,14 @@ import qs.modules.waffle.looks Button { id: root - signal altAction() - signal middleClickAction() + property var altAction: () => {} + property var middleClickAction: () => {} - property color colBackground + property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) + property color colBackgroundHover: Looks.colors.bg1Hover + property color colBackgroundActive: Looks.colors.bg1Active property color colBackgroundBorder + property color color Layout.fillHeight: true topInset: 4 bottomInset: 4 @@ -37,16 +40,16 @@ Button { } } - colBackground: { + colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.contentTransparency : 1) + color: { if (root.down) { - return Looks.colors.bg1Active + return root.colBackgroundActive } else if ((root.hovered && !root.down) || root.checked) { - return Looks.colors.bg1Hover + return root.colBackgroundHover } else { - return ColorUtils.transparentize(Looks.colors.bg1) + return root.colBackground } } - colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, root.checked ? Looks.contentTransparency : 1) MouseArea { anchors.fill: parent @@ -66,7 +69,8 @@ Button { background: AcrylicRectangle { shiny: ((root.hovered && !root.down) || root.checked) - color: root.colBackground + color: root.color + radius: Looks.radius.medium border.width: 1 border.color: root.colBackgroundBorder diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarIconButton.qml new file mode 100644 index 000000000..311e5e4c9 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarIconButton.qml @@ -0,0 +1,41 @@ +import QtQuick +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.waffle.looks +import qs.modules.waffle.bar + +BarButton { + id: root + + property alias iconName: iconContent.icon + property alias iconSource: iconContent.source + property alias iconSize: iconContent.implicitSize + property alias iconRotation: iconContent.rotation + property alias iconMonochrome: iconContent.monochrome + property alias iconScale: iconContent.scale + property alias tooltipText: tooltip.text + property alias overlayingItems: iconContent.data + + implicitWidth: 32 + + contentItem: Item { + anchors.centerIn: parent + implicitWidth: iconContent.implicitWidth + implicitHeight: iconContent.implicitHeight + + FluentIcon { + id: iconContent + anchors.centerIn: parent + implicitSize: 16 + icon: root.iconName + monochrome: false + } + } + + BarToolTip { + id: tooltip + extraVisibleCondition: root.shouldShowTooltip && text !== "" + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarMenu.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarMenu.qml new file mode 100644 index 000000000..871abc23f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarMenu.qml @@ -0,0 +1,59 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +BarPopup { + id: root + default property var menuData + property var model: [ + { iconName: "start-here", text: "Start", action: () => {print("hello")} }, + { type : "separator" }, + ] + readonly property bool hasIcons: model.some(item => item.iconName !== undefined && item.iconName !== "") + padding: 2 + + contentItem: ColumnLayout { + anchors.centerIn: parent + spacing: 0 + + Repeater { + model: root.model + delegate: DelegateChooser { + role: "type" + DelegateChoice { + roleValue: "separator" + Rectangle { + Layout.topMargin: 2 + Layout.bottomMargin: 2 + Layout.fillWidth: true + implicitHeight: 1 + color: Looks.colors.bg0Border + } + } + DelegateChoice { + roleValue: undefined + WButton { + id: btn + Layout.fillWidth: true + + required property var modelData + forceShowIcon: root.hasIcons + icon.name: modelData.iconName ? modelData.iconName : "" + monochromeIcon: modelData.monochromeIcon ?? true + text: modelData.text ? modelData.text : "" + + onClicked: { + if (modelData.action) modelData.action(); + root.close(); + } + } + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarPopup.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarPopup.qml new file mode 100644 index 000000000..a46811b82 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarPopup.qml @@ -0,0 +1,131 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +Loader { + id: root + + required property var contentItem + property real padding: Looks.radius.large - Looks.radius.medium + property bool noSmoothClosing: !Config.options.waffles.smootherAnimations + property bool closeOnFocusLost: true + signal focusCleared() + + property Item anchorItem: parent + property real visualMargin: 12 + readonly property bool barAtBottom: Config.options.waffles.bar.bottom + property real ambientShadowWidth: 1 + + onFocusCleared: { + if (!root.closeOnFocusLost) return; + root.close() + } + + function grabFocus() { // Doesn't work + item.grabFocus(); + } + + function close() { + item.close(); + } + + function updateAnchor() { + item?.anchor.updateAnchor(); + } + + active: false + visible: active + sourceComponent: PopupWindow { + id: popupWindow + visible: true + Component.onCompleted: { + openAnim.start(); + } + + anchor { + adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX + item: root.anchorItem + gravity: root.barAtBottom ? Edges.Top : Edges.Bottom + edges: root.barAtBottom ? Edges.Top : Edges.Bottom + } + + HyprlandFocusGrab { + id: focusGrab + active: true + windows: [popupWindow] + onCleared: root.focusCleared(); + } + + function close() { + if (root.noSmoothClosing) root.active = false; + else closeAnim.start(); + } + + function grabFocus() { + focusGrab.active = true; // Doesn't work + } + + implicitWidth: realContent.implicitWidth + (root.ambientShadowWidth * 2) + (root.visualMargin * 2) + implicitHeight: realContent.implicitHeight + (root.ambientShadowWidth * 2) + (root.visualMargin * 2) + + property real sourceEdgeMargin: -implicitHeight + PropertyAnimation { + id: openAnim + target: popupWindow + property: "sourceEdgeMargin" + to: (root.ambientShadowWidth + root.visualMargin) + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + SequentialAnimation { + id: closeAnim + PropertyAnimation { + target: popupWindow + property: "sourceEdgeMargin" + to: -implicitHeight + duration: 150 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + ScriptAction { + script: { + root.active = false; + } + } + } + + color: "transparent" + WAmbientShadow { + target: realContent + } + + Rectangle { + id: realContent + z: 1 + anchors { + left: parent.left + right: parent.right + top: root.barAtBottom ? undefined : parent.top + bottom: root.barAtBottom ? parent.bottom : undefined + margins: root.ambientShadowWidth + root.visualMargin + // Opening anim + bottomMargin: root.barAtBottom ? popupWindow.sourceEdgeMargin : (root.ambientShadowWidth + root.visualMargin) + topMargin: root.barAtBottom ? (root.ambientShadowWidth + root.visualMargin) : popupWindow.sourceEdgeMargin + } + color: Looks.colors.bg1 + radius: Looks.radius.large + + // test + implicitWidth: root.contentItem.implicitWidth + (root.padding * 2) + implicitHeight: root.contentItem.implicitHeight + (root.padding * 2) + + children: [root.contentItem] + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml index a86faaece..3e8dd6282 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml @@ -9,9 +9,10 @@ import qs.modules.waffle.looks AppButton { id: root - iconName: "system-search" + iconName: checked ? "system-search-checked" : "system-search" separateLightDark: true + checked: GlobalStates.overviewOpen onClicked: { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now... } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml index 4595802ee..f4a15cc00 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml @@ -1,6 +1,7 @@ import QtQuick +import QtQuick.Controls import QtQuick.Layouts -import org.kde.kirigami as Kirigami +import Quickshell import qs import qs.services import qs.modules.common @@ -10,7 +11,7 @@ AppButton { id: root leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0 - iconName: "start-here" + iconName: down ? "start-here-pressed" : "start-here" onClicked: { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now... @@ -21,4 +22,45 @@ AppButton { text: Translation.tr("Start") extraVisibleCondition: root.shouldShowTooltip } + + altAction: () => { + contextMenu.active = true; + } + + BarMenu { + id: contextMenu + + model: [ + { + text: Translation.tr("Terminal"), + action: () => { + Quickshell.execDetached(["bash", "-c", Config.options.apps.terminal]); + } + }, + { + text: Translation.tr("Task Manager"), + action: () => { + Quickshell.execDetached(["bash", "-c", Config.options.apps.taskManager]); + } + }, + { + text: Translation.tr("Settings"), + action: () => { + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("settings.qml")]); + } + }, + { + text: Translation.tr("File Explorer"), + action: () => { + Qt.openUrlExternally(Directories.home); + } + }, + { + text: Translation.tr("Search"), + action: () => { + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "overview", "toggle"]); + } + }, + ] + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/SystemButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/SystemButton.qml index 1de9654d5..2ce1cd862 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/SystemButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/SystemButton.qml @@ -8,9 +8,9 @@ import qs.modules.waffle.looks BarButton { id: root - checked: GlobalStates.sidebarRightOpen + checked: GlobalStates.sidebarLeftOpen onClicked: { - GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; // For now... + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; } contentItem: Item { @@ -85,11 +85,13 @@ BarButton { BarToolTip { extraVisibleCondition: root.shouldShowTooltip && volumeHoverArea.containsMouse text: Translation.tr("Speakers (%1): %2") // - .arg(Audio.sink?.nickname || Audio.sink?.description || Translation.tr("Unknown")) // - .arg(`${Math.round(Audio.sink?.audio.volume * 100) || 0}%`) // + .arg(Audio.sink?.nickname || Audio.sink?.description || Translation.tr("Unknown")) // + .arg(Audio.sink?.audio.muted ? Translation.tr("Muted") : `${Math.round(Audio.sink?.audio.volume * 100) || 0}%`) // } BarToolTip { extraVisibleCondition: root.shouldShowTooltip && batteryHoverArea.containsMouse - text: Translation.tr("Battery: %1").arg(`${Math.round(Battery.percentage * 100) || 0}%`) + text: Translation.tr("Battery: %1%2") // + .arg(`${Math.round(Battery.percentage * 100) || 0}%`) // + .arg(Battery.isPluggedIn ? (" " + Translation.tr("(Plugged in)")) : "") } } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/TaskViewButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/TaskViewButton.qml index cc35c8b41..760c00608 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/TaskViewButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/TaskViewButton.qml @@ -9,7 +9,8 @@ import qs.modules.waffle.looks AppButton { id: root - iconName: "task-view" + iconName: (down && !checked) ? "task-view-pressed" : "task-view" + pressedScale: checked ? 5/6 : 1 separateLightDark: true checked: GlobalStates.overviewOpen diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/UpdatesButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/UpdatesButton.qml new file mode 100644 index 000000000..54286ac88 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/UpdatesButton.qml @@ -0,0 +1,33 @@ +import QtQuick +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.waffle.looks +import qs.modules.waffle.bar.tray + +BarIconButton { + id: root + + visible: Updates.updateAdvised || Updates.updateStronglyAdvised + padding: 4 + iconName: "arrow-sync" + iconSize: 20 // Needed because the icon appears to have some padding + tooltipText: Translation.tr("Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages").arg(Updates.count) + + onClicked: { + Quickshell.execDetached(["bash", "-c", Config.options.apps.update]); + } + + overlayingItems: Rectangle { + anchors { + right: parent.right + bottom: parent.bottom + margins: 1 + } + implicitWidth: 8 + implicitHeight: implicitWidth + radius: height / 2 + color: Updates.updateStronglyAdvised ? Looks.colors.warning : Looks.colors.accent + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml index aa5f51f68..2326a47f7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml @@ -9,9 +9,8 @@ import qs.modules.common import qs.modules.common.widgets Scope { - id: bar - property bool showBarBackground: Config.options.bar.showBackground - + id: root + LazyLoader { id: barLoader active: GlobalStates.barOpen && !GlobalStates.screenLocked diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBarContent.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBarContent.qml index aa064118a..cfbc52779 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBarContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBarContent.qml @@ -4,6 +4,7 @@ import qs.modules.common import qs.modules.common.widgets import qs.modules.waffle.looks import qs.modules.waffle.bar.tasks +import qs.modules.waffle.bar.tray Rectangle { id: root @@ -68,6 +69,8 @@ Rectangle { shown: Config.options.waffles.bar.leftAlignApps sourceComponent: WidgetsButton {} } + Tray {} + UpdatesButton {} SystemButton {} TimeButton {} } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml index 1c6c11859..51a3175bc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml @@ -18,6 +18,11 @@ AppButton { onClicked: { GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen } + onDownChanged: { + scaleAnim.duration = root.down ? 150 : 200 + scaleAnim.easing.bezierCurve = root.down ? Looks.transition.easing.bezierCurve.easeIn : Looks.transition.easing.bezierCurve.easeOut + iconWidget.scale = root.down ? 5/6 : 1 // If/When we do dragging, the scale is 1.25 + } contentItem: Item { anchors { @@ -41,6 +46,13 @@ AppButton { id: iconWidget anchors.verticalCenter: parent.verticalCenter iconName: root.iconName + + Behavior on scale { + NumberAnimation { + id: scaleAnim + easing.type: Easing.BezierSpline + } + } } Column { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml index 53b68b82c..a5915e83e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Layouts import qs.services import qs.modules.common +import qs.modules.common.functions import qs.modules.waffle.looks import qs.modules.waffle.bar import Quickshell @@ -16,6 +17,7 @@ AppButton { property bool hasWindows: appEntry.toplevels.length > 0 signal hoverPreviewRequested() + signal hoverPreviewDismissed() multiple: appEntry.toplevels.length > 1 checked: active @@ -37,12 +39,18 @@ AppButton { } } - onMiddleClickAction: { + middleClickAction: () => { if (root.desktopEntry) { desktopEntry.execute() } } + altAction: () => { + root.hoverPreviewDismissed() + root.hoverTimer.stop() + contextMenu.active = true; + } + // Active indicator Rectangle { id: activeIndicator @@ -74,4 +82,44 @@ AppButton { extraVisibleCondition: root.shouldShowTooltip && !root.hasWindows text: desktopEntry ? desktopEntry.name : appEntry.appId } + + BarMenu { + id: contextMenu + + model: [ + ...((root.desktopEntry?.actions.length > 0) ? root.desktopEntry.actions.map(action =>({ + iconName: action.icon, + text: action.name, + action: () => { + action.execute() + } + })).concat({ type: "separator" }) : []), + { + iconName: root.iconName, + text: root.desktopEntry ? root.desktopEntry.name : StringUtils.toTitleCase(appEntry.appId), + monochromeIcon: false, + action: () => { + if (root.desktopEntry) { + root.desktopEntry.execute() + } + } + }, + { + iconName: root.appEntry.pinned ? "pin-off" : "pin", + text: root.appEntry.pinned ? qsTr("Unpin from taskbar") : qsTr("Pin to taskbar"), + action: () => { + TaskbarApps.togglePin(root.appEntry.appId); + } + }, + ...(root.appEntry.toplevels.length > 0 ? [{ + iconName: "dismiss", + text: root.multiple ? qsTr("Close all windows") : qsTr("Close window"), + action: () => { + for (let toplevel of root.appEntry.toplevels) { + toplevel.close(); + } + } + }] : []), + ] + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskPreview.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskPreview.qml index 6d8aeba1a..18b11caee 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskPreview.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskPreview.qml @@ -16,7 +16,7 @@ PopupWindow { property Item anchorItem //////////////////// Functions //////////////////// - function close() { + function close() { // Closing doesn't animate, not sure if they're just lazy or it's intentional marginBehavior.enabled = false; root.visible = false; } @@ -36,7 +36,7 @@ PopupWindow { ///////////////////// Internals ///////////////////// readonly property bool bottom: Config.options.waffles.bar.bottom property real visualMargin: 12 - property alias ambientShadowWidth: ambientShadow.border.width + property real ambientShadowWidth: 1 visible: false color: "transparent" @@ -64,16 +64,8 @@ PopupWindow { hoverEnabled: true // Shadow - Rectangle { - id: ambientShadow - anchors { - fill: contentItem - margins: -border.width - } - border.color: ColorUtils.transparentize(Looks.colors.bg0Border, Looks.shadowTransparency) - border.width: 1 - color: "transparent" - radius: Looks.radius.large + border.width + WAmbientShadow { + target: contentItem } Rectangle { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml index 895b1353f..f33c6e12a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml @@ -17,10 +17,17 @@ MouseArea { previewPopup.show(appEntry, button); } + Behavior on implicitWidth { + animation: Looks.transition.move.createObject(this) + } + // Apps row RowLayout { id: row - anchors.fill: parent + anchors { + top: parent.top + bottom: parent.bottom + } spacing: 0 Repeater { @@ -36,6 +43,9 @@ MouseArea { onHoverPreviewRequested: { root.showPreviewPopup(appEntry, this) } + onHoverPreviewDismissed: { + previewPopup.close() + } } } } @@ -46,4 +56,5 @@ MouseArea { tasksHovered: root.containsMouse anchor.window: root.QsWindow.window } + } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml new file mode 100644 index 000000000..f05f2593e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tray/Tray.qml @@ -0,0 +1,118 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Qt.labs.synchronizer +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.waffle.looks +import qs.modules.waffle.bar + +RowLayout { + id: root + + property bool overflowOpen: false + property bool dragging: false + + Layout.fillHeight: true + spacing: 0 + + BarIconButton { + id: overflowButton + + visible: (TrayService.unpinnedItems.length > 0 || root.dragging) + checked: root.overflowOpen + + iconName: "chevron-down" + iconMonochrome: true + iconRotation: (Config.options.waffles.bar.bottom ? 180 : 0) + (root.overflowOpen ? 180 : 0) + Behavior on iconRotation { + animation: Looks.transition.rotate.createObject(this) + } + + onClicked: { + root.overflowOpen = !root.overflowOpen; + } + + TrayOverflowMenu { + id: trayOverflowLayout + Synchronizer on active { + property alias source: root.overflowOpen + } + } + + BarToolTip { + extraVisibleCondition: overflowButton.shouldShowTooltip + text: qsTr("Show hidden icons") + } + + DropArea { + id: pinDropArea + anchors.fill: parent + property bool willPin: false + onEntered: willPin = true + onExited: willPin = false + } + } + + Repeater { + model: ScriptModel { + values: TrayService.pinnedItems + } + delegate: TrayButton { + id: trayButton + required property var modelData + item: modelData + + property real initialX + property real initialY + + MouseArea { + id: dragArea + anchors.fill: parent + drag.target: parent + drag.axis: Drag.XAxis + drag.threshold: 2 + + onPressed: event => { + trayButton.Drag.hotSpot.x = event.x; + trayButton.initialX = trayButton.x; + root.dragging = true; + trayButton.Drag.active = true; + } + onPositionChanged: { + pinTooltip.updateAnchor(); + } + onReleased: { + if (!dragArea.drag.active) { + trayButton.click(); + } else { + if (pinDropArea.containsDrag && pinDropArea.willPin) { + // Quickshell would crash if we don't hide this item first. Took me fucking 3 hours to figure out... + trayButton.visible = false; + TrayService.togglePin(trayButton.item.id); + pinDropArea.willPin = false; + } else { + trayButton.x = trayButton.initialX; + } + } + trayButton.Drag.active = false; + root.dragging = false; + } + } + + BarToolTip { + id: pinTooltip + extraVisibleCondition: trayButton.Drag.active && pinDropArea.containsDrag && pinDropArea.willPin + realContentHorizontalPadding: 6 + realContentVerticalPadding: 6 + realContentItem: FluentIcon { + anchors.centerIn: parent + icon: "pin-off" + implicitSize: 18 + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tray/TrayButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tray/TrayButton.qml new file mode 100644 index 000000000..e3c85e290 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tray/TrayButton.qml @@ -0,0 +1,52 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.SystemTray +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.waffle.looks +import qs.modules.waffle.bar + +BarIconButton { + id: root + + required property SystemTrayItem item + property alias menuOpen: menu.visible + readonly property bool barAtBottom: Config.options.waffles.bar.bottom + iconSource: item.icon + iconScale: 0 + Component.onCompleted: { + root.iconScale = 1 + } + Behavior on iconScale { + animation: Looks.transition.enter.createObject(this) + } + + onClicked: { + item.activate(); + } + + altAction: () => { + if (item.hasMenu) menu.open() + } + + // This is lazy, but it's not like tray menus on Windoes are consistent... + // TODO: Figure out how to do cascading menus then use a custom menu + QsMenuAnchor { + id: menu + menu: root.item.menu + anchor { + adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX + item: root + gravity: root.barAtBottom ? Edges.Top : Edges.Bottom + edges: root.barAtBottom ? Edges.Top : Edges.Bottom + } + } + + BarToolTip { + extraVisibleCondition: root.shouldShowTooltip && !root.Drag.active + text: TrayService.getTooltipForItem(root.item) + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tray/TrayOverflowMenu.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tray/TrayOverflowMenu.qml new file mode 100644 index 000000000..2b3044643 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tray/TrayOverflowMenu.qml @@ -0,0 +1,124 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks +import qs.modules.waffle.bar + +BarPopup { + id: root + + closeOnFocusLost: false + onFocusCleared: { + const hasMenuOpen = contentItem.children.some(c => (c.menuOpen)); + if (!hasMenuOpen) + root.close(); + else + root.grabFocus(); + } + + contentItem: Item { + id: contentItem + anchors.centerIn: parent + implicitWidth: contentGrid.implicitWidth + implicitHeight: contentGrid.implicitHeight + GridLayout { + id: contentGrid + anchors.centerIn: parent + rows: Math.floor(Math.sqrt(TrayService.unpinnedItems.length)) + columns: Math.ceil(TrayService.unpinnedItems.length / rows) + columnSpacing: 0 + rowSpacing: 0 + + Repeater { + model: ScriptModel { + values: TrayService.unpinnedItems + onValuesChanged: { + root.updateAnchor(); + if (values.length === 0) { + root.close(); + } + } + } + delegate: TrayButton { + id: trayButton + required property var modelData + item: modelData + + topInset: 0 + bottomInset: 0 + implicitWidth: 40 + implicitHeight: 40 + + colBackground: ColorUtils.transparentize(Looks.colors.bg2) + colBackgroundHover: Looks.colors.bg2Hover + colBackgroundActive: Looks.colors.bg2Active + + onMenuOpenChanged: { + // The overflow menu should only be closed when the user clicks outside + // However the focus grab refuses to reactivate, so we can't have that + // But most of the time the user dismisses the menu by clicking outside anyway, + // so this is acceptable. + if (!menuOpen) { + root.close(); + } + } + + property real initialX + property real initialY + + Behavior on x { + animation: Looks.transition.move.createObject(this) + } + Behavior on y { + animation: Looks.transition.move.createObject(this) + } + + MouseArea { + id: dragArea + anchors.fill: parent + drag.target: parent + drag.threshold: 2 + + onPressed: event => { + trayButton.Drag.hotSpot.x = event.x; + trayButton.Drag.hotSpot.y = event.y; + trayButton.initialX = trayButton.x; + trayButton.initialY = trayButton.y; + trayButton.Drag.active = true; + } + onReleased: { + if (!dragArea.drag.active) { + trayButton.click(); + } else { + if (!unpinDropArea.containsDrag && unpinDropArea.willUnpin) { + // Quickshell would crash if we don't hide this item first. Took me fucking 3 hours to figure out... + trayButton.visible = false; + TrayService.togglePin(trayButton.item.id); + unpinDropArea.willUnpin = false; + } else { + trayButton.x = trayButton.initialX; + trayButton.y = trayButton.initialY; + } + } + trayButton.Drag.active = false; + } + } + } + } + } + + DropArea { + id: unpinDropArea + anchors.fill: parent + property bool willUnpin: false + onEntered: willUnpin = false + onExited: willUnpin = true + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/FluentIcon.qml b/dots/.config/quickshell/ii/modules/waffle/looks/FluentIcon.qml index 81b6ebf08..5a7f15902 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/FluentIcon.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/FluentIcon.qml @@ -6,6 +6,7 @@ import qs.modules.waffle.looks Kirigami.Icon { id: root required property string icon + property alias monochrome: root.isMask // Should be 16, but it appears the icons have some padding, // Unlike the Windows-only Segoe UI icons, the open source FluentUI ones are hella small property int implicitSize: 20 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 09176830f..a01628d4a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -17,9 +17,12 @@ Singleton { property real backgroundTransparency: 0.17 property real contentTransparency: 0.25 - property real shadowTransparency: 0.6 colors: QtObject { id: colors + property color ambientShadow: ColorUtils.transparentize("#000000", 0.75) + property color bgPanelFooter: root.dark ? "#1C1C1C" : "#EEEEEE" + property color bgPanelBody: root.dark ? "#242424" : "#F2F2F2" + property color bgPanelSeparator: root.dark ? "#191919" : "#E0E0E0" property color bg0: root.dark ? "#1C1C1C" : "#EEEEEE" property color bg0Border: root.dark ? "#404040" : "#BEBEBE" property color bg1: root.dark ? "#2C2C2C" : "#F7F7F7" @@ -34,6 +37,7 @@ Singleton { property color fg1: root.dark ? "#D1D1D1" : "#626262" property color danger: "#C42B1C" property color dangerActive: "#B62D1F" + property color warning: "#FF9900" // property color accent: root.dark ? "#A5C6D8" : "#5377A3" property color accent: Appearance.m3colors.m3primary property color accentUnfocused: root.dark ? "#989898" : "#848484" @@ -61,7 +65,7 @@ Singleton { } property QtObject pixelSize: QtObject { property real normal: 11 - property real large: 15 + property real large: 14 } } @@ -115,6 +119,14 @@ Singleton { } } + property Component rotate: Component { + NumberAnimation { + duration: 170 + easing.type: Easing.BezierSpline + easing.bezierCurve: transition.easing.bezierCurve.easeInOut + } + } + property Component anchor: Component { AnchorAnimation { duration: 160 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WAmbientShadow.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WAmbientShadow.qml new file mode 100644 index 000000000..79a3ca857 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WAmbientShadow.qml @@ -0,0 +1,25 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +Rectangle { + id: root + + required property var target + z: 0 + + anchors { + fill: target + margins: -border.width + } + + border.color: Looks.colors.ambientShadow + border.width: 1 + color: "transparent" + radius: target.radius + border.width +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml new file mode 100644 index 000000000..3350c5887 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml @@ -0,0 +1,79 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.waffle.looks + +Item { + id: root + + signal closed() + + property alias border: borderRect + required default property Item contentItem + property real visualMargin: 12 + + function close() { + closeAnim.start(); + } + + readonly property bool barAtBottom: Config.options.waffles.bar.bottom + + implicitHeight: borderRect.implicitHeight + implicitWidth: borderRect.implicitWidth + + Rectangle { + id: borderRect + + color: "transparent" + radius: Looks.radius.large + border.color: Looks.colors.bg2Border + border.width: 1 + implicitWidth: contentItem.implicitWidth + border.width * 2 + implicitHeight: contentItem.implicitHeight + border.width * 2 + children: [root.contentItem] + + anchors { + left: parent.left + right: parent.right + top: root.barAtBottom ? undefined : parent.top + bottom: root.barAtBottom ? parent.bottom : undefined + // Opening anim + bottomMargin: root.barAtBottom ? sourceEdgeMargin : 0 + topMargin: root.barAtBottom ? 0 : sourceEdgeMargin + } + + Component.onCompleted: { + openAnim.start(); + } + + property real sourceEdgeMargin: -(implicitHeight + root.visualMargin) + PropertyAnimation { + id: openAnim + target: borderRect + property: "sourceEdgeMargin" + to: 0 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + SequentialAnimation { + id: closeAnim + PropertyAnimation { + target: borderRect + property: "sourceEdgeMargin" + to: -(implicitHeight + root.visualMargin) + duration: 150 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + ScriptAction { + script: { + root.closed(); + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml new file mode 100644 index 000000000..3b48a79e3 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -0,0 +1,95 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +// Generic button with background +Button { + id: root + + property color colBackgroundHover: Looks.colors.bg2Hover + property color colBackgroundActive: Looks.colors.bg2Active + property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) + + property alias monochromeIcon: buttonIcon.monochrome + property bool forceShowIcon: false + + property var altAction: () => {} + property var middleClickAction: () => {} + + property real inset: 2 + topInset: inset + bottomInset: inset + leftInset: inset + rightInset: inset + horizontalPadding: 10 + verticalPadding: 6 + implicitHeight: contentItem.implicitHeight + verticalPadding * 2 + implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 + + background: Rectangle { + radius: Looks.radius.medium + color: { + if (root.down) { + return root.colBackgroundActive; + } else if ((root.hovered && !root.down) || root.checked) { + return root.colBackgroundHover; + } else { + return root.colBackground; + } + } + Behavior on color { + animation: Looks.transition.color.createObject(this) + } + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.MiddleButton + onClicked: (event) => { + if (event.button === Qt.LeftButton) root.clicked(); + if (event.button === Qt.RightButton) root.altAction(); + if (event.button === Qt.MiddleButton) root.middleClickAction(); + } + } + + contentItem: Item { + anchors { + fill: parent + margins: root.inset + } + implicitWidth: contentLayout.implicitWidth + implicitHeight: contentLayout.implicitHeight + RowLayout { + id: contentLayout + anchors { + fill: parent + leftMargin: root.horizontalPadding + rightMargin: root.horizontalPadding + } + spacing: 12 + FluentIcon { + id: buttonIcon + visible: root.icon.name !== "" || root.forceShowIcon + monochrome: true + implicitSize: 16 + Layout.leftMargin: 6 + Layout.fillWidth: false + Layout.alignment: Qt.AlignVCenter + icon: root.icon.name + } + WText { + Layout.rightMargin: 12 + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + text: root.text + horizontalAlignment: Text.AlignLeft + font { + pixelSize: Looks.font.pixelSize.large + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPanelFooterButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelFooterButton.qml new file mode 100644 index 000000000..9b017d75f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelFooterButton.qml @@ -0,0 +1,32 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +Button { + id: root + + implicitHeight: 36 + + property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) + property color colBackgroundHover: Looks.colors.bg1Hover + property color colBackgroundActive: Looks.colors.bg1Active + property color color + property color colForeground: Looks.colors.fg + color: { + if (root.down) { + return root.colBackgroundActive + } else if ((root.hovered && !root.down) || root.checked) { + return root.colBackgroundHover + } else { + return root.colBackground + } + } + + background: Rectangle { + radius: Looks.radius.medium + color: root.color + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml index b35258204..feddc3793 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPopupToolTip.qml @@ -10,42 +10,37 @@ import qs.modules.waffle.looks PopupToolTip { id: root - property real padding: 2 - verticalPadding: padding - horizontalPadding: padding + property Item realContentItem + realContentItem: WText { + text: root.text + anchors.centerIn: parent + } + + property real visualMargin: 11 + verticalPadding: visualMargin + horizontalPadding: visualMargin + property real realContentVerticalPadding: 8 + property real realContentHorizontalPadding: 10 contentItem: Item { anchors.centerIn: parent - implicitWidth: realContent.implicitWidth + root.verticalPadding * 2 - implicitHeight: realContent.implicitHeight + root.horizontalPadding * 2 + implicitWidth: realContent.implicitWidth + 2 * 2 + implicitHeight: realContent.implicitHeight + 2 * 2 - Rectangle { - id: ambientShadow - z: 0 - anchors { - fill: realContent - margins: -border.width - } - border.color: ColorUtils.transparentize(Looks.colors.bg0Border, Looks.shadowTransparency) - border.width: 1 - color: "transparent" - radius: realContent.radius + border.width + WAmbientShadow { + target: realContent } Rectangle { id: realContent z: 1 anchors.centerIn: parent - implicitWidth: tooltipText.implicitWidth + 10 * 2 - implicitHeight: tooltipText.implicitHeight + 8 * 2 + implicitWidth: root.realContentItem.implicitWidth + root.realContentHorizontalPadding * 2 + implicitHeight: root.realContentItem.implicitHeight + root.realContentVerticalPadding * 2 color: Looks.colors.bg1 radius: Looks.radius.medium - WText { - id: tooltipText - text: root.text - anchors.centerIn: parent - } + children: [root.realContentItem] } } } diff --git a/dots/.config/quickshell/ii/services/Brightness.qml b/dots/.config/quickshell/ii/services/Brightness.qml index f3cec016c..465d8729c 100644 --- a/dots/.config/quickshell/ii/services/Brightness.qml +++ b/dots/.config/quickshell/ii/services/Brightness.qml @@ -135,10 +135,18 @@ Singleton { } function syncBrightness() { - const brightnessValue = Math.max(monitor.multipliedBrightness, 0) - const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1); - setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded] : ["brightnessctl", "--class", "backlight", "s", rawValueRounded, "--quiet"]; - setProc.startDetached(); + const brightnessValue = Math.max(monitor.multipliedBrightness, 0); + if (isDdc) { + const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1); + setProc.command = ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded]; + setProc.startDetached(); + } else { + const valuePercentNumber = Math.floor(brightnessValue * 100); + let valuePercent = `${valuePercentNumber}%`; + if (valuePercentNumber == 0) valuePercent = "1"; // Prevent fully black + setProc.command = ["brightnessctl", "--class", "backlight", "s", valuePercent, "--quiet"]; + setProc.startDetached(); + } } function setBrightness(value: real): void { diff --git a/dots/.config/quickshell/ii/services/EasyEffects.qml b/dots/.config/quickshell/ii/services/EasyEffects.qml index 4117e50c4..e21a0a67f 100644 --- a/dots/.config/quickshell/ii/services/EasyEffects.qml +++ b/dots/.config/quickshell/ii/services/EasyEffects.qml @@ -30,7 +30,7 @@ Singleton { function enable() { root.active = true - Quickshell.execDetached(["bash", "-c", "easyeffects --gapplication-service || flatpak run com.github.wwmm.easyeffects --gapplication-service"]) + Quickshell.execDetached(["bash", "-c", "easyeffects --hide-window --service-mode || flatpak run com.github.wwmm.easyeffects --hide-window --service-mode"]) } function toggle() { diff --git a/dots/.config/quickshell/ii/services/SystemInfo.qml b/dots/.config/quickshell/ii/services/SystemInfo.qml index 1d5c0bf6e..2b519a229 100644 --- a/dots/.config/quickshell/ii/services/SystemInfo.qml +++ b/dots/.config/quickshell/ii/services/SystemInfo.qml @@ -58,6 +58,7 @@ Singleton { // Update the distroIcon property based on distroId switch (distroId) { + case "artix": case "arch": distroIcon = "arch-symbolic"; break; case "endeavouros": distroIcon = "endeavouros-symbolic"; break; case "cachyos": distroIcon = "cachyos-symbolic"; break; diff --git a/dots/.config/quickshell/ii/services/TaskbarApps.qml b/dots/.config/quickshell/ii/services/TaskbarApps.qml index 0b5818b71..052abcaec 100644 --- a/dots/.config/quickshell/ii/services/TaskbarApps.qml +++ b/dots/.config/quickshell/ii/services/TaskbarApps.qml @@ -8,6 +8,14 @@ import Quickshell.Wayland Singleton { id: root + function togglePin(appId) { + if (Config.options.dock.pinnedApps.indexOf(appId) !== -1) { + Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.filter(id => id !== appId) + } else { + Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.concat([appId]) + } + } + property list apps: { var map = new Map(); diff --git a/dots/.config/quickshell/ii/services/TrayService.qml b/dots/.config/quickshell/ii/services/TrayService.qml new file mode 100644 index 000000000..a874c6c11 --- /dev/null +++ b/dots/.config/quickshell/ii/services/TrayService.qml @@ -0,0 +1,45 @@ +pragma Singleton + +import qs.modules.common +import QtQuick +import Quickshell +import Quickshell.Services.SystemTray + +Singleton { + id: root + + property bool smartTray: Config.options.tray.filterPassive + property list itemsInUserList: SystemTray.items.values.filter(i => (Config.options.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive))) + property list itemsNotInUserList: SystemTray.items.values.filter(i => (!Config.options.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive))) + + property bool invertPins: Config.options.tray.invertPinnedItems + property list pinnedItems: invertPins ? itemsNotInUserList : itemsInUserList + property list unpinnedItems: invertPins ? itemsInUserList : itemsNotInUserList + + function getTooltipForItem(item) { + var result = item.tooltipTitle.length > 0 ? item.tooltipTitle + : (item.title.length > 0 ? item.title : item.id); + if (item.tooltipDescription.length > 0) result += " • " + item.tooltipDescription; + if (Config.options.tray.showItemId) result += "\n[" + item.id + "]"; + return result; + } + + // Pinning + function pin(itemId) { + var pins = Config.options.tray.pinnedItems; + if (pins.includes(itemId)) return; + Config.options.tray.pinnedItems.push(itemId); + } + function unpin(itemId) { + Config.options.tray.pinnedItems = Config.options.tray.pinnedItems.filter(id => id !== itemId); + } + function togglePin(itemId) { + var pins = Config.options.tray.pinnedItems; + if (pins.includes(itemId)) { + unpin(itemId) + } else { + pin(itemId) + } + } + +} diff --git a/dots/.config/quickshell/ii/services/Updates.qml b/dots/.config/quickshell/ii/services/Updates.qml new file mode 100644 index 000000000..58b8be892 --- /dev/null +++ b/dots/.config/quickshell/ii/services/Updates.qml @@ -0,0 +1,57 @@ +pragma Singleton + +import qs.modules.common +import qs.modules.common.functions +import QtQuick +import Quickshell +import Quickshell.Io + +/* + * System updates service. Currently only supports Arch. + */ +Singleton { + id: root + + property bool available: false + property int count: 0 + + readonly property bool updateAdvised: available && count > Config.options.updates.adviseUpdateThreshold + readonly property bool updateStronglyAdvised: available && count > Config.options.updates.stronglyAdviseUpdateThreshold + + function load() {} + function refresh() { + if (!available) return; + print("[Updates] Checking for system updates") + checkUpdatesProc.running = true; + } + + Timer { + interval: Config.options.updates.checkInterval * 60 * 1000 + repeat: true + running: Config.ready + onTriggered: { + print("[Updates] Periodic update check due") + root.refresh(); + } + } + + Process { + id: checkAvailabilityProc + running: true + command: ["which", "checkupdates"] + onExited: (exitCode, exitStatus) => { + root.available = (exitCode === 0); + root.refresh(); + } + } + + Process { + id: checkUpdatesProc + command: ["bash", "-c", "checkupdates | wc -l"] + stdout: StdioCollector { + onStreamFinished: { + root.count = parseInt(text.trim()); + } + } + } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index ea100fdda..766728cf5 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -28,6 +28,7 @@ import qs.modules.ii.overlay import qs.modules.ii.verticalBar import qs.modules.ii.wallpaperSelector +import qs.modules.waffle.actionCenter import qs.modules.waffle.background import qs.modules.waffle.bar @@ -49,6 +50,7 @@ ShellRoot { ConflictKiller.load() Cliphist.refresh() Wallpapers.load() + Updates.load() } // Load enabled stuff @@ -74,6 +76,7 @@ ShellRoot { PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} } PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} } PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} } + PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } PanelLoader { identifier: "wBar"; component: WaffleBar {} } PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } @@ -87,7 +90,7 @@ ShellRoot { property list families: ["ii", "waffle"] property var panelFamilies: ({ "ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"], - "waffle": ["wBar", "wBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiWallpaperSelector"], + "waffle": ["wBar", "wBackground", "wActionCenter", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiSessionScreen", "iiSidebarRight", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) diff --git a/sdata/deps-info.md b/sdata/deps-info.md index 475639b6e..a7e0f9f14 100644 --- a/sdata/deps-info.md +++ b/sdata/deps-info.md @@ -211,6 +211,7 @@ Extra dependencies. - `qt6-translations` - `qt6-virtualkeyboard` - `qt6-wayland` +- `kirigami` - `kdialog` - `syntax-highlighting` diff --git a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD index 806f2272e..690b73123 100644 --- a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD @@ -9,7 +9,7 @@ conflicts=("quickshell-git") _pkgname=quickshell pkgname="$_prefix-$_pkgname-git" pkgver=0.1.0.r1 -pkgrel=5 +pkgrel=6 pkgdesc="$_pkgname-git pinned commit and extra deps for $_prefix" arch=(x86_64 aarch64) url='https://git.outfoxxed.me/quickshell/quickshell' @@ -39,6 +39,7 @@ depends=( qt6-translations qt6-virtualkeyboard qt6-wayland + kirigami kdialog syntax-highlighting ) diff --git a/sdata/dist-fedora/SPECS/Bibata-Modern-Classic.spec b/sdata/dist-fedora/SPECS/Bibata-Modern-Classic.spec deleted file mode 100644 index 1522ae851..000000000 --- a/sdata/dist-fedora/SPECS/Bibata-Modern-Classic.spec +++ /dev/null @@ -1,36 +0,0 @@ -Name: Bibata-Modern-Classic -Version: 2.0.7 -Release: %autorelease -Summary: Open source, compact, and material designed cursor set. - -License: GPL-3.0 -URL: https://github.com/ful1e5/Bibata_Cursor -Source0: %{name}.tar.xz - -BuildArch: noarch - -%description -Open source, compact, and material designed cursor set. - -%prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/releases/download/v%{version}/Bibata-Modern-Classic.tar.xz -wget -q -O %{_buildrootdir}/LICENSE %{url}/raw/refs/heads/main/LICENSE -%setup -q -n %{name} - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_iconsdir}/Bibata-Modern-Classic -cp -r * %{buildroot}%{_iconsdir}/Bibata-Modern-Classic - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 %{_buildrootdir}/LICENSE %{buildroot}%{_licensedir}/%{name}/ - -%files -%{_iconsdir}/Bibata-Modern-Classic -%license %{_licensedir}/%{name}/LICENSE - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/JetBrainsMonoNerdFont-Regular.spec b/sdata/dist-fedora/SPECS/JetBrainsMonoNerdFont-Regular.spec deleted file mode 100644 index 01f1e0e08..000000000 --- a/sdata/dist-fedora/SPECS/JetBrainsMonoNerdFont-Regular.spec +++ /dev/null @@ -1,42 +0,0 @@ -Name: JetBrainsMonoNerdFont-Regular -Version: 1.2 -Release: %autorelease -Summary: JetBrainsMonoNerdFonts (TrueType Outlines) (Regulear) - -License: MIT -URL: https://github.com/Zhaopudark/JetBrainsMonoNerdFonts -Source0: %{name} - -BuildRequires: fonts-rpm-macros -BuildArch: noarch - -%description -An auto-updated compiling version of JetBrains Mono that has been patched with Nerd Fonts. - -%prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/releases/download/v%{version}/JetBrainsMonoNerdFont-Regular-v%{version}.ttf -wget -q -P %{_sourcedir} %{url}/raw/refs/heads/main/LICENSE - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_fontdir} -install -m 0644 %{_sourcedir}/JetBrainsMonoNerdFont*ttf %{buildroot}%{_fontdir} - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 %{_sourcedir}/LICENSE %{buildroot}%{_licensedir}/%{name}/ - -%post -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%postun -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%files -%{_fontdir}/JetBrainsMonoNerdFont*ttf -%license %{_licensedir}/%{name}/LICENSE - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/MicroTeX.spec b/sdata/dist-fedora/SPECS/MicroTeX.spec deleted file mode 100644 index ec8ae436f..000000000 --- a/sdata/dist-fedora/SPECS/MicroTeX.spec +++ /dev/null @@ -1,50 +0,0 @@ -%global commit0 0e3707f6dafebb121d98b53c64364d16fefe481d -%global shortcommit0 %(c=%{commit0}; echo ${c:0:7}) -%global bumpver 100 - -Name: MicroTeX -Version: 0.0.1%{?bumpver:^%{bumpver}.git%{shortcommit0}} -Release: %autorelease -Summary: A dynamic, cross-platform, and embeddable LaTeX rendering library - -License: MIT -URL: https://github.com/NanoMichael/MicroTeX -Source0: %{name}-%{shortcommit0}.tar.gz - -BuildRequires: gcc-c++ cmake -BuildRequires: pkgconfig(tinyxml2) -BuildRequires: gtkmm3.0-devel gtksourceviewmm3-devel cairomm-devel - -%description -MicroTeX is a library for rendering LaTeX mathematical formulas, supporting multiple backends -such as GTK+, Qt, and Skia. It provides both library components and demo applications for -testing LaTeX rendering. - -%prep -curl -fsSL --retry 3 \ - https://codeload.github.com/NanoMichael/MicroTeX/tar.gz/%{shortcommit0} \ - -o %{_sourcedir}/%{name}-%{shortcommit0}.tar.gz -%setup -q -n %{name}-%{shortcommit0} - -%build -mkdir -p build -cd build -cmake .. -make -j$(nproc) - -%install -mkdir -p %{buildroot}/opt/MicroTeX -cp build/LaTeX %{buildroot}/opt/MicroTeX/ -cp -r build/res %{buildroot}/opt/MicroTeX/ - -install -Dpm 0644 LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE -install -Dpm 0644 res/greek/LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE-greek -install -Dpm 0644 res/cyrillic/LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE-cyrillic - -%files -/opt/MicroTeX/ -%license %{_licensedir}/%{name}/ - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/breeze-plus.spec b/sdata/dist-fedora/SPECS/breeze-plus.spec deleted file mode 100644 index d37ff8e41..000000000 --- a/sdata/dist-fedora/SPECS/breeze-plus.spec +++ /dev/null @@ -1,38 +0,0 @@ -Name: breeze-plus -Version: 6.19.0 -Release: %autorelease -Summary: Breeze theme with additional icons - -License: LGPL-2.1 -URL: https://github.com/mjkim0727/breeze-plus -Source0: %{name}-%{version}.tar.gz - -BuildArch: noarch - -%description -Breeze icon theme with additional icons for applications not covered by the -official Breeze theme. Includes icons for Wine, third-party apps, and more. - -%prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/archive/refs/tags/%{version}.tar.gz -%setup -q - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_iconsdir}/breeze-plus -cp -r src/breeze-plus %{buildroot}%{_iconsdir}/ -cp -r src/breeze-plus-dark %{buildroot}%{_iconsdir}/ - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 LICENSE %{buildroot}%{_licensedir}/%{name}/ - -%files -%{_iconsdir}/breeze-plus/ -%{_iconsdir}/breeze-plus-dark/ -%license %{_licensedir}/%{name}/LICENSE - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/hyprland-qt-support.spec b/sdata/dist-fedora/SPECS/hyprland-qt-support.spec index c4f8bf057..4cae243ae 100644 --- a/sdata/dist-fedora/SPECS/hyprland-qt-support.spec +++ b/sdata/dist-fedora/SPECS/hyprland-qt-support.spec @@ -25,7 +25,6 @@ BuildRequires: pkgconfig(hyprlang) %{summary}. %prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/archive/v%{version}/%{name}-%{version}.tar.gz %autosetup -p1 %build diff --git a/sdata/dist-fedora/SPECS/otf-SpaceGrotesk.spec b/sdata/dist-fedora/SPECS/otf-SpaceGrotesk.spec deleted file mode 100644 index e42e4bc8e..000000000 --- a/sdata/dist-fedora/SPECS/otf-SpaceGrotesk.spec +++ /dev/null @@ -1,44 +0,0 @@ -Name: otf-SpaceGrotesk -Version: 2.0.0 -Release: %autorelease -Summary: Space Grotesk: a proportional variant of the original fixed-width Space Mono family - -License: OFL-1.1 -URL: https://github.com/floriankarsten/space-grotesk -Source0: %{name}-%{version} - -BuildRequires: fonts-rpm-macros -BuildArch: noarch - -%description -Space Grotesk is a proportional sans-serif typeface variant based on Colophon Foundry's fixed-width Space Mono family (2016). -Originally designed by Florian Karsten in 2018, -Space Grotesk retains the monospace's idiosyncratic details while optimizing for improved readability at non-display sizes. - -%prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/releases/download/%{version}/SpaceGrotesk-%{version}.zip -unzip %{_sourcedir}/SpaceGrotesk-%{version}.zip - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_fontdir} -install -m 0644 %{_buildrootdir}/SpaceGrotesk-%{version}/otf/SpaceGrotesk*otf %{buildroot}%{_fontdir} - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 %{_buildrootdir}/SpaceGrotesk-%{version}/OFL.txt %{buildroot}%{_licensedir}/%{name}/ - -%post -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%postun -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%files -%{_fontdir}/SpaceGrotesk*otf -%license %{_licensedir}/%{name}/OFL.txt - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/quickshell-git.spec b/sdata/dist-fedora/SPECS/quickshell-git.spec index a0b23bfd8..e90bdebe2 100644 --- a/sdata/dist-fedora/SPECS/quickshell-git.spec +++ b/sdata/dist-fedora/SPECS/quickshell-git.spec @@ -3,18 +3,19 @@ %bcond_with asan %global commit db1777c20b936a86528c1095cbcb1ebd92801402 +%global shortcommit %(c=%{commit}; echo ${c:0:7}) %global commits 699 %global snapdate 20251030 %global tag 0.2.1 Name: quickshell-git -Version: %{tag}^%{commits}.git%(c=%{commit}; echo ${c:0:7}) +Version: %{tag}^%{commits}.git%{shortcommit} Release: 0%{?dist} Summary: Flexible QtQuick based desktop shell toolkit License: LGPL-3.0-only AND GPL-3.0-only URL: https://github.com/quickshell-mirror/quickshell -Source0: %{url}/archive/%{commit}/quickshell-%{commit}.tar.gz +Source0: %{url}/archive/%{commit}/quickshell-%{shortcommit}.tar.gz Conflicts: quickshell <= %{tag} @@ -53,7 +54,6 @@ Flexible toolkit for making desktop shells with QtQuick, targeting Wayland and X11. %prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/archive/%{commit}/quickshell-%{commit}.tar.gz %autosetup -n quickshell-%{commit} -p1 %build diff --git a/sdata/dist-fedora/SPECS/ttf-gabarito.spec b/sdata/dist-fedora/SPECS/ttf-gabarito.spec deleted file mode 100644 index a46341bf9..000000000 --- a/sdata/dist-fedora/SPECS/ttf-gabarito.spec +++ /dev/null @@ -1,46 +0,0 @@ -%global commit0 1f3fb39d6449eefa880543f109f33ede0cd4064f -%global shortcommit0 %(c=%{commit0}; echo ${c:0:7}) -%global bumpver 100 - -Name: ttf-gabarito -Version: 1.000%{?bumpver:^%{bumpver}.git%{shortcommit0}} -Release: %autorelease -Summary: Gabarito Font - -License: OFL-1.1 -URL: https://github.com/naipefoundry/gabarito -Source0: gabarito-%{shortcommit0}.tar.gz - -BuildRequires: fonts-rpm-macros -BuildArch: noarch - -%description -Gabarito is a light-hearted geometric sans typeface with 6 weights ranging from Regular to Black originally designed for an online learning platform in Brazil. - -%prep -wget --content-disposition -q -N -P %{_sourcedir} https://codeload.github.com/naipefoundry/gabarito/tar.gz/%{shortcommit0} -%setup -q -n gabarito-%{shortcommit0} - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_fontdir} -install -m 0644 fonts/ttf/Gabarito*.ttf %{buildroot}%{_fontdir} - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 OFL.txt %{buildroot}%{_licensedir}/%{name}/ - -%post -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%postun -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%files -%{_fontdir}/Gabarito*ttf -%license %{_licensedir}/%{name}/OFL.txt - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/ttf-material-symbols-variable.spec b/sdata/dist-fedora/SPECS/ttf-material-symbols-variable.spec deleted file mode 100644 index 989540ec3..000000000 --- a/sdata/dist-fedora/SPECS/ttf-material-symbols-variable.spec +++ /dev/null @@ -1,42 +0,0 @@ -Name: ttf-material-symbols-variable -Version: 4.0.0 -Release: %autorelease -Summary: Material Design icons by Google (Material Symbols) - -License: Apache-2.0 -URL: https://github.com/google/material-design-icons -Source0: %{name}-%{version} - -BuildRequires: fonts-rpm-macros -BuildArch: noarch - -%description -Google Material Symbols Rounded - -%prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/raw/refs/heads/master/variablefont/MaterialSymbolsRounded%5BFILL,GRAD,opsz,wght%5D.ttf -wget -q -N -P %{_sourcedir} %{url}/raw/refs/heads/master/LICENSE - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_fontdir}/variable-fonts -install -m 0644 %{_sourcedir}/MaterialSymbolsRounded*ttf %{buildroot}%{_fontdir}/variable-fonts/ - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 %{_sourcedir}/LICENSE %{buildroot}%{_licensedir}/%{name}/ - -%post -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%postun -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%files -%{_fontdir}/variable-fonts/MaterialSymbolsRounded*ttf -%license %{_licensedir}/%{name}/LICENSE - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/ttf-roboto-flex.spec b/sdata/dist-fedora/SPECS/ttf-roboto-flex.spec deleted file mode 100644 index e95917bf1..000000000 --- a/sdata/dist-fedora/SPECS/ttf-roboto-flex.spec +++ /dev/null @@ -1,42 +0,0 @@ -Name: ttf-roboto-flex -Version: 3.200 -Release: %autorelease -Summary: Roboto Flex - -License: OFL-1.1 -URL: https://github.com/googlefonts/roboto-flex -Source0: %{name} - -BuildRequires: fonts-rpm-macros -BuildArch: noarch - -%description -: - -%prep -wget --content-disposition -q -N -P %{_sourcedir} %{url}/raw/refs/heads/main/fonts/RobotoFlex%5BGRAD,XOPQ,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght%5D.ttf -wget -q -O %{_sourcedir}/OFL.txt %{url}/raw/refs/heads/main/OFL.txt - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_fontdir} -install -m 0644 %{_sourcedir}/RobotoFlex*ttf %{buildroot}%{_fontdir} - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 %{_sourcedir}/OFL.txt %{buildroot}%{_licensedir}/%{name}/ - -%post -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%postun -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%files -%{_fontdir}/RobotoFlex*ttf -%license %{_licensedir}/%{name}/OFL.txt - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/SPECS/ttf-rubik-variable.spec b/sdata/dist-fedora/SPECS/ttf-rubik-variable.spec deleted file mode 100644 index ec276be74..000000000 --- a/sdata/dist-fedora/SPECS/ttf-rubik-variable.spec +++ /dev/null @@ -1,46 +0,0 @@ -%global commit0 e337a5f69a9bea30e58d05bd40184d79cc099628 -%global shortcommit0 %(c=%{commit0}; echo ${c:0:7}) -%global bumpver 100 - -Name: ttf-rubik-variable -Version: 1.0%{?bumpver:^%{bumpver}.git%{shortcommit0}} -Release: %autorelease -Summary: Rubik fonts variable - -License: OFL-1.1 -URL: https://github.com/googlefonts/rubik -Source0: rubik-%{shortcommit0}.tar.gz - -BuildRequires: fonts-rpm-macros -BuildArch: noarch - -%description -: - -%prep -wget --content-disposition -q -N -P %{_sourcedir} https://codeload.github.com/googlefonts/rubik/tar.gz/%{shortcommit0} -%setup -q -n rubik-%{shortcommit0} - -%build -: - -%install -install -d -m 0755 %{buildroot}%{_fontdir}/variable-fonts -install -m 0644 fonts/variable/Rubik*ttf %{buildroot}%{_fontdir}/variable-fonts/ - -install -d -m 0755 %{buildroot}%{_licensedir}/%{name} -install -m 0644 OFL.txt %{buildroot}%{_licensedir}/%{name}/ - -%post -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%postun -/usr/bin/fc-cache -fv >/dev/null 2>&1 || : - -%files -%{_fontdir}/variable-fonts/Rubik*ttf -%license %{_licensedir}/%{name}/OFL.txt - -%changelog -%autochangelog - diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh index 6374d5e7e..624fba6ca 100644 --- a/sdata/dist-fedora/install-deps.sh +++ b/sdata/dist-fedora/install-deps.sh @@ -8,8 +8,8 @@ fi # Update System case $SKIP_SYSUPDATE in - true) sleep 0;; - *) v sudo dnf upgrade --refresh -y;; + true) sleep 0 ;; + *) v sudo dnf upgrade --refresh -y ;; esac # Remove version lock @@ -19,9 +19,8 @@ v sudo dnf versionlock delete quickshell-git 2>/dev/null v sudo dnf install @development-tools fedora-packager rpmdevtools fonts-rpm-macros qt6-rpm-macros -y # COPR repositories +v sudo dnf copr enable ririko66z/dots-hyprland -y v sudo dnf copr enable solopasha/hyprland -y -v sudo dnf copr enable errornointernet/quickshell -y -v sudo dnf copr enable errornointernet/packages -y v sudo dnf copr enable deltacopy/darkly -y v sudo dnf copr enable alternateved/eza -y v sudo dnf copr enable atim/starship -y @@ -35,15 +34,19 @@ v sudo dnf install geoclue2 brightnessctl ddcutil -y # Basic v sudo dnf install bc coreutils cliphist cmake curl wget2 ripgrep jq xdg-utils rsync yq -y +# Cursor themes +v sudo dnf install bibata-cursor-theme -y + # Fonts & Themes themes_deps=( - adw-gtk3-theme breeze-cursor-theme grub2-breeze-theme breeze-icon-theme{,-fedora} - kf6-breeze-icons sddm-breeze darkly eza fish fontconfig kitty matugen starship - jetbrains-mono-nl-fonts material-icons-fonts twitter-twemoji-fonts + adw-gtk3-theme breeze-cursor-theme grub2-breeze-theme breeze-icon-theme{,-fedora} kf6-breeze-icons + sddm-breeze breeze-plus-icon-theme darkly eza fish fontconfig kitty matugen florian-karsten-space-grotesk-fonts + starship gabarito-fonts jetbrains-mono-nerd-fonts google-material-symbols-vf-rounded-fonts material-icons-fonts + readex-pro-fonts-all google-roboto-flex-fonts google-rubik-vf-fonts twitter-twemoji-fonts ) v sudo dnf install ${themes_deps[@]} -y -# Hyprland +# Hyprland hyprland_deps=( hyprland hyprsunset @@ -56,8 +59,8 @@ v sudo dnf install hyprlang-devel -y # KDE v sudo dnf install bluedevil gnome-keyring NetworkManager plasma-nm polkit-kde dolphin plasma-systemsettings -y -# Microtex-git -v sudo dnf install --setopt="install_weak_deps=False" tinyxml2-devel gtkmm3.0-devel gtksourceviewmm3-devel cairomm-devel -y +# MicroTeX-git +v sudo dnf install microtex -y # Portal v sudo dnf install xdg-desktop-portal{,-gtk,-kde,-hyprland} -y @@ -69,14 +72,13 @@ v sudo dnf install python3{,.12}{,-devel} -y # Quickshell-git quickshell_deps=( - qt6-qtdeclarative qt6-qtbase jemalloc qt6-qtsvg pipewire-libs - libxcb wayland-devel qt6-qtwayland qt5-qtwayland libdrm breakpad + qt6-qtdeclarative qt6-qtbase jemalloc qt6-qtsvg pipewire-libs libxcb wayland-devel qt6-qtwayland + qt5-qtwayland libdrm breakpad ) # NOTE: Below are custom dependencies of illogical-impulse quickshell_custom_deps=( - qt6-qt5compat qt6-qtimageformats qt6-qtpositioning - qt6-qtquicktimeline qt6-qtsensors qt6-qttools qt6-qttranslations - qt6-qtvirtualkeyboard qt6-qtwayland kdialog kf6-syntax-highlighting + qt6-qt5compat qt6-qtimageformats qt6-qtpositioning qt6-qtquicktimeline qt6-qtsensors qt6-qttools + qt6-qttranslations qt6-qtvirtualkeyboard qt6-qtwayland kdialog kf6-syntax-highlighting kf6-kirigami ) quickshell_build_deps=( breakpad-static breakpad-devel gcc-c++ ninja-build mesa-libgbm-devel cli11-devel glib2-devel @@ -102,24 +104,24 @@ v sudo dnf install --setopt="install_weak_deps=False" mpvpaper plasma-systemmoni # Start building the missing RPM package locally. install_RPMS() { - rpmbuildroot=${REPO_ROOT}/cache/rpmbuild - x mkdir -p $rpmbuildroot/{BUILD,RPMS,SOURCES} - x cp -r ${REPO_ROOT}/sdata/dist-fedora/SPECS $rpmbuildroot/ - x cd $rpmbuildroot/SPECS - mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0) - for spec_file in ${local_specs[@]}; do - x rpmbuild -bb --define "_topdir $rpmbuildroot" $spec_file - done - mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) - echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" - x sudo dnf install "${local_rpms[@]}" -y - x cd ${REPO_ROOT} + rpmbuildroot=${REPO_ROOT}/cache/rpmbuild + x mkdir -p $rpmbuildroot/{BUILD,RPMS,SOURCES} + x cp -r ${REPO_ROOT}/sdata/dist-fedora/SPECS $rpmbuildroot/ + x cd $rpmbuildroot/SPECS + mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0) + for spec_file in ${local_specs[@]}; do + x spectool -g -C "$rpmbuildroot/SOURCES" $spec_file + x rpmbuild -bb --define "_topdir $rpmbuildroot" $spec_file + done + mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) + echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" + x sudo dnf install "${local_rpms[@]}" -y + x cd ${REPO_ROOT} } showfun install_RPMS v install_RPMS - # hyprland-qtutils depends on hyprland-qt-support v sudo dnf install hyprland-qtutils -y diff --git a/sdata/dist-nix/home-manager/quickshell.nix b/sdata/dist-nix/home-manager/quickshell.nix index 823797082..0fb7b57fe 100644 --- a/sdata/dist-nix/home-manager/quickshell.nix +++ b/sdata/dist-nix/home-manager/quickshell.nix @@ -40,6 +40,7 @@ in pkgs.stdenv.mkDerivation { qt6.qttranslations #qt6-translations qt6.qtvirtualkeyboard #qt6-virtualkeyboard qt6.qtwayland #qt6-wayland + kdePackages.kirigami #kirigami kdePackages.kdialog #kdialog kdePackages.syntax-highlighting #syntax-highlighting ]; diff --git a/sdata/lib/dist-determine.sh b/sdata/lib/dist-determine.sh index 46f9afdd8..78f548ac1 100644 --- a/sdata/lib/dist-determine.sh +++ b/sdata/lib/dist-determine.sh @@ -25,7 +25,9 @@ function print_os_group_id_unofficial(){ printf "The support for your distro is provided by the community.\n" printf "It is not officially supported by github:end-4/dots-hyprland .\n" printf "${STY_BOLD}" - printf "If you find out any problems about it, PR is welcomed if you are able to address it. Or, create a discussion about it, but please do not submit issue, because the developers do not use this distro, therefore they cannot help.${STY_RST}\n" + printf "If you find out any problems about it, PR is welcomed if you are able to address it.\n" + printf "Or, create a discussion about it, but please do not submit issue, \n" + printf "because the developers do not use this distro, therefore they cannot help.${STY_RST}\n" printf "${STY_PURPLE}" printf "Proceed only at your own risk.\n" printf "============\n\n" diff --git a/sdata/subcmd-install/1.deps-router.sh b/sdata/subcmd-install/1.deps-router.sh index 1287e4c6f..dd6712518 100644 --- a/sdata/subcmd-install/1.deps-router.sh +++ b/sdata/subcmd-install/1.deps-router.sh @@ -64,26 +64,42 @@ elif [[ "$OS_GROUP_ID" =~ ^(arch|gentoo|fedora)$ ]]; then if [[ "${tmp_update_status}" =~ ^(OUTDATED|EMPTY_TARGET|EMPTY_SOURCE|FORCE_OUTDATED|WIP)$ ]]; then printf "${STY_RED}${STY_BOLD}===URGENT===${STY_RST}\n" printf "${STY_RED}" - printf "The community provided ./sdata/dist-${TARGET_ID}/ is outdated (status: ${tmp_update_status}),\n" + printf "Status code: ${tmp_update_status}\n" + printf "The community provided ./sdata/dist-${TARGET_ID}/ seems to be outdated,\n" printf "which means it probably does not reflect all latest changes of ./sdata/dist-arch/ .\n" + printf "In such case it may work unexpectedly.${STY_RST}\n" printf "\n" - printf "According to the actual changes, it may still works, but it can also work unexpectedly.\n" - printf "It's highly recommended to check the following links before continue:${STY_RST}\n" - printf "${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/discussions/2140${STY_RST}\n" - printf "${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/commits/main/sdata/dist-arch${STY_RST}\n" - printf "${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/commits/main/sdata/dist-${TARGET_ID}${STY_RST}\n" + printf "${STY_RED}It's highly recommended to check the following links before continue.${STY_RST}\n" + printf "${STY_RED}1. Normally just check discussion#2140 to see if there's any valid update notice.${STY_RST}\n" + printf " ${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/discussions/2140${STY_RST}\n" + printf " ${STY_RED}Note that the timeliness relies on manual maintenance.${STY_RST}\n" + printf "${STY_RED}2. For details please compare the two lists of commit history:${STY_RST}\n" + printf " ${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/commits/main/sdata/dist-arch${STY_RST}\n" + printf " ${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/commits/main/sdata/dist-${TARGET_ID}${STY_RST}\n" printf "\n" - printf "${STY_PURPLE}${STY_INVERT}PR on ./sdata/dist-${TARGET_ID}/ to properly reflect the latest changes of ./sdata/dist-arch is welcomed.${STY_RST}\n" + printf "${STY_PURPLE}PR on ./sdata/dist-${TARGET_ID}/ to properly reflect the latest changes of ./sdata/dist-arch is welcomed.${STY_RST}\n" + printf "${STY_PURPLE}${STY_BOLD}Again, do not create any issue,${STY_RST}\n" + printf "${STY_PURPLE}but you can create a discussion under \"Extra Distros\" category: ${STY_RST}\n" + printf "${STY_PURPLE}${STY_UNDERLINE}https://github.com/end-4/dots-hyprland/discussions/new?category=extra-distros${STY_RST}\n" printf "\n" - if [ "$ask" = "false" ]; then - echo "Urgent problem encountered, aborting...";exit 1 - else - printf "${STY_RED}Still proceed?${STY_RST}\n" - read -p "[y/N]: " p - case "$p" in - [yY])sleep 0;; - *)echo "Aborting...";exit 1;; - esac + if [[ "${tmp_update_status}" = "OUTDATED" ]]; then + printf "${STY_RED}NOTE: The conclusion above is determined automatically by comparing latest Git commit time,\n" + printf "however sometimes the changes on \"dist-arch\" are actually not needed for \"dist-${TARGET_ID}\",\n" + printf "in such case you should just ignore it and continue.\n" + printf "${STY_RST}\n" + fi + printf "\n" + if ! [[ "$IGNORE_OUTDATE_CHECK" = "true" ]]; then + if [ "$ask" = "false" ]; then + printf "${STY_RED}Urgent problem encountered, aborting...${STY_RST}\n";exit 1 + else + printf "${STY_RED}Still proceed?${STY_RST}\n" + read -p "[y/N]: " p + case "$p" in + [yY])sleep 0;; + *)echo "Aborting...";exit 1;; + esac + fi fi fi fi diff --git a/sdata/subcmd-install/2.setups.sh b/sdata/subcmd-install/2.setups.sh index 2e585832c..c321bd752 100644 --- a/sdata/subcmd-install/2.setups.sh +++ b/sdata/subcmd-install/2.setups.sh @@ -2,19 +2,22 @@ # It's not for directly running. function prepare_systemd_user_service(){ - if [[ ! -d "${XDG_CONFIG_HOME}/systemd/user" ]]; then - x mkdir -p "${XDG_CONFIG_HOME}/systemd/user" - fi - if [[ ! -e "${XDG_CONFIG_HOME}/systemd/user/ydotool.service" ]]; then - x ln -s /usr/lib/systemd/system/ydotool.service "${XDG_CONFIG_HOME}/systemd/user/ydotool.service" + if [[ ! -e "/usr/lib/systemd/user/ydotool.service" ]]; then + x sudo ln -s /usr/lib/systemd/{system,user}/ydotool.service fi } function setup_user_group(){ - if [[ -z $(getent group i2c) ]]; then + if [[ -z $(getent group i2c) ]] && [[ "$OS_GROUP_ID" != "fedora" ]]; then + # On Fedora this is not needed. Tested with desktop computer with NVIDIA video card. x sudo groupadd i2c fi - x sudo usermod -aG video,i2c,input "$(whoami)" + + if [[ "$OS_GROUP_ID" == "fedora" ]]; then + x sudo usermod -aG video,input "$(whoami)" + else + x sudo usermod -aG video,i2c,input "$(whoami)" + fi } ##################################################################################### # These python packages are installed using uv into the venv (virtual environment). Once the folder of the venv gets deleted, they are all gone cleanly. So it's considered as setups, not dependencies. @@ -25,8 +28,7 @@ showfun setup_user_group v setup_user_group if [[ ! -z $(systemctl --version) ]]; then - # TODO: Why fedora does not add i2c-dev? - # TODO: Why fedora add uinput and udev rules? + # For Fedora, uinput is required for the virtual keyboard to function, and udev rules enable input group users to utilize it. if [[ "$OS_GROUP_ID" == "fedora" ]]; then v bash -c "echo uinput | sudo tee /etc/modules-load.d/uinput.conf" v bash -c 'echo SUBSYSTEM==\"misc\", KERNEL==\"uinput\", MODE=\"0660\", GROUP=\"input\" | sudo tee /etc/udev/rules.d/99-uinput.rules' diff --git a/sdata/subcmd-install/options.sh b/sdata/subcmd-install/options.sh index 992010cfb..727a3c13b 100644 --- a/sdata/subcmd-install/options.sh +++ b/sdata/subcmd-install/options.sh @@ -14,6 +14,7 @@ Options for install: --skip-alldeps Skip the whole process installing dependency --skip-allsetups Skip the whole process setting up permissions/services etc --skip-allfiles Skip the whole process copying configuration files + --ignore-outdate Ignore outdate checking for community supported \"dist-*\". -s, --skip-sysupdate Skip system package upgrade e.g. \"sudo pacman -Syu\" --skip-plasmaintg Skip installing plasma-browser-integration --skip-backup Skip backup conflicting files @@ -45,7 +46,7 @@ cleancache(){ # `man getopt` to see more para=$(getopt \ -o hfFk:cs \ - -l help,force,firstrun,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-plasmaintg,skip-backup,skip-quickshell,skip-fish,skip-hyprland,skip-fontconfig,skip-miscconf,core,exp-files,via-nix \ + -l help,force,firstrun,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,ignore-outdate,skip-sysupdate,skip-plasmaintg,skip-backup,skip-quickshell,skip-fish,skip-hyprland,skip-fontconfig,skip-miscconf,core,exp-files,via-nix \ -n "$0" -- "$@") [ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 ##################################################################################### @@ -76,6 +77,7 @@ while true ; do --skip-allsetups) SKIP_ALLSETUPS=true;shift;; --skip-allfiles) SKIP_ALLFILES=true;shift;; -s|--skip-sysupdate) SKIP_SYSUPDATE=true;shift;; + --ignore-outdate) IGNORE_OUTDATE_CHECK=true;shift;; --skip-plasmaintg) SKIP_PLASMAINTG=true;shift;; --skip-backup) SKIP_BACKUP=true;shift;; --skip-hyprland) SKIP_HYPRLAND=true;shift;;