diff --git a/dots/.config/hypr/custom/env.conf b/dots/.config/hypr/custom/env.conf index fcd59ec86..3215877be 100644 --- a/dots/.config/hypr/custom/env.conf +++ b/dots/.config/hypr/custom/env.conf @@ -1,2 +1,10 @@ # You can put extra environment variables here # https://wiki.hyprland.org/Configuring/Environment-variables/ + +# ######### Input method ########## +# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland +#env = QT_IM_MODULE, fcitx +#env = XMODIFIERS, @im=fcitx +#env = SDL_IM_MODULE, fcitx +#env = GLFW_IM_MODULE, ibus +#env = INPUT_METHOD, fcitx diff --git a/dots/.config/hypr/custom/execs.conf b/dots/.config/hypr/custom/execs.conf index cae4ef6c5..66bb2b8b5 100644 --- a/dots/.config/hypr/custom/execs.conf +++ b/dots/.config/hypr/custom/execs.conf @@ -1,2 +1,6 @@ # You can make apps auto-start here # Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing + +# Input method +# exec-once = fcitx5 + diff --git a/dots/.config/hypr/hyprland/env.conf b/dots/.config/hypr/hyprland/env.conf index bd45f9f7e..acc363cc6 100644 --- a/dots/.config/hypr/hyprland/env.conf +++ b/dots/.config/hypr/hyprland/env.conf @@ -1,11 +1,3 @@ -# ######### Input method ########## -# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland -#env = QT_IM_MODULE, fcitx -#env = XMODIFIERS, @im=fcitx -#env = SDL_IM_MODULE, fcitx -#env = GLFW_IM_MODULE, ibus -#env = INPUT_METHOD, fcitx - # ############ Wayland ############# env = ELECTRON_OZONE_PLATFORM_HINT,auto diff --git a/dots/.config/hypr/hyprland/execs.conf b/dots/.config/hypr/hyprland/execs.conf index bb5dd7225..2cad1308e 100644 --- a/dots/.config/hypr/hyprland/execs.conf +++ b/dots/.config/hypr/hyprland/execs.conf @@ -3,9 +3,6 @@ exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh exec-once = qs -c $qsConfig & exec-once = ~/.config/hypr/custom/scripts/__restore_video_wallpaper.sh -# Input method -# exec-once = fcitx5 - # Core components (authentication, lock screen, notification daemon) exec-once = gnome-keyring-daemon --start --components=secrets exec-once = hypridle diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index 020fe582d..6032ab65a 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -4,27 +4,27 @@ #! ##! Shell # These absolutely need to be on top, or they won't work consistently -bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher -bindid = Super, Super_R, Toggle overview, global, quickshell:overviewToggleRelease # [hidden] Toggle overview/launcher +bindid = Super, Super_L, Toggle search, global, quickshell:searchToggleRelease # Toggle search +bindid = Super, Super_R, Toggle search, global, quickshell:searchToggleRelease # [hidden] Toggle search bind = Super, Super_L, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback) bind = Super, Super_R, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback) -binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Ctrl, Super_R, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden] +binditn = Super, catchall, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Ctrl, Super_L, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Ctrl, Super_R, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:272, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:273, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:274, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:275, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:276, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:277, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse_up, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse_down,global, quickshell:searchToggleReleaseInterrupt # [hidden] bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden] bindit = ,Super_R, global, quickshell:workspaceNumber # [hidden] +bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # Toggle overview bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard -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] @@ -218,8 +218,8 @@ submap = global #! # Testing -bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account image and Discord icon. Oh and here is a random image in your Pictures folder: \"Testing" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden] -bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your Pictures folder and Discord icon.\nFlick right to dismiss!" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Useless button" -A "action3=Cry more"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden] +bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account image and Discord icon. Oh and here is a random image in your Pictures folder: \"Testing" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden] +bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your Pictures folder and Discord icon.\nFlick right to dismiss!" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Profile image" -A "action2=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden] bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden] ##! Session diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index 17a4d5af9..cfb1e3f7b 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -159,7 +159,9 @@ layerrule = animation slide right, quickshell:sidebarRight layerrule = animation slide left, quickshell:sidebarLeft layerrule = animation slide, quickshell:verticalBar layerrule = animation slide top, quickshell:wallpaperSelector +layerrule = noanim, quickshell:wNotificationCenter layerrule = noanim, quickshell:wOnScreenDisplay +layerrule = noanim, quickshell:wStartMenu # Launchers need to be FAST layerrule = noanim, gtk4-layer-shell diff --git a/dots/.config/quickshell/ii/GlobalStates.qml b/dots/.config/quickshell/ii/GlobalStates.qml index 972495c64..85a0414d6 100644 --- a/dots/.config/quickshell/ii/GlobalStates.qml +++ b/dots/.config/quickshell/ii/GlobalStates.qml @@ -20,6 +20,7 @@ Singleton { property bool overlayOpen: false property bool overviewOpen: false property bool regionSelectorOpen: false + property bool searchOpen: false property bool screenLocked: false property bool screenLockContainsCharacters: false property bool screenUnlockFailed: false diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg b/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg new file mode 100644 index 000000000..0ed1a0319 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg new file mode 100644 index 000000000..2cfa6dba7 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/power.svg b/dots/.config/quickshell/ii/assets/icons/fluent/power.svg new file mode 100644 index 000000000..5c28fe986 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/power.svg @@ -0,0 +1,4 @@ + + + + \ 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 index e6b950eca..2efc81c45 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg @@ -1,24 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg index 708d5a71b..9f7b4a177 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg @@ -1,24 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 index af58d933f..84f42c500 100644 --- 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 @@ -26,8 +26,8 @@ inkscape:zoom="4.65625" inkscape:cx="32" inkscape:cy="32" - inkscape:window-width="1197" - inkscape:window-height="1020" + inkscape:window-width="1595" + inkscape:window-height="664" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -48,10 +48,10 @@ 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 index 8d0e69fce..76af86e67 100644 --- 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 @@ -23,10 +23,10 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:zoom="6.5849319" - inkscape:cx="26.95548" + inkscape:cx="27.031411" inkscape:cy="26.423963" - inkscape:window-width="1257" - inkscape:window-height="1020" + inkscape:window-width="1621" + inkscape:window-height="820" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -35,10 +35,10 @@ id="defs2"> diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 0cf28653a..d26f04d65 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -154,6 +154,7 @@ Singleton { property JsonObject apps: JsonObject { property string bluetooth: "kcmshell6 kcm_bluetooth" property string network: "kcmshell6 kcm_networkmanagement" + property string manageUser: "kcmshell6 kcm_users" 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 diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index 56f647684..9afbed44b 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -1,6 +1,7 @@ pragma Singleton pragma ComponentBehavior: Bound +import qs.services import qs.modules.common.functions import QtCore import QtQuick @@ -46,6 +47,9 @@ Singleton { property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`) property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`) property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`) + property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`) + property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`) + property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`) // Cleanup on init Component.onCompleted: { Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) diff --git a/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml index 8a336e8ad..d6adcc0f6 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml @@ -84,4 +84,28 @@ Singleton { // Older dates return Qt.formatDateTime(messageTime, "MMMM dd"); } + + function processNotificationBody(body, appName) { + let processedBody = body + + // Clean Chromium-based browsers notifications - remove first line + if (appName) { + const lowerApp = appName.toLowerCase() + const chromiumBrowsers = [ + "brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge" + ] + + if (chromiumBrowsers.some(name => lowerApp.includes(name))) { + const lines = body.split('\n\n') + + if (lines.length > 1 && lines[0].startsWith(' lowerApp.includes(name))) { - const lines = body.split('\n\n') - - if (lines.length > 1 && lines[0].startsWith('") + return NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
") } } } ColumnLayout { // Expanded content + id: expandedContentColumn Layout.fillWidth: true opacity: root.expanded ? 1 : 0 visible: opacity > 0 @@ -218,8 +197,8 @@ Item { // Notification item area elide: Text.ElideRight textFormat: Text.RichText text: { - return `` + - `${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` + return `` + + `${NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` } onLinkActivated: (link) => { @@ -293,6 +272,8 @@ Item { // Notification item area id: actionRepeater model: notificationObject.actions NotificationActionButton { + id: notifAction + required property var modelData Layout.fillWidth: true buttonText: modelData.text urgency: notificationObject.urgency diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml index c360b536c..17dfc56c4 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml @@ -12,4 +12,14 @@ Image { Behavior on opacity { animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } + + property list fallbacks: [] + property int currentFallbackIndex: 0 + + onStatusChanged: { + if (status === Image.Error && currentFallbackIndex < fallbacks.length) { + source = fallbacks[currentFallbackIndex]; + currentFallbackIndex += 1; + } + } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml index 53797fb66..4688b29be 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml @@ -20,6 +20,7 @@ ToolTip { hintingPreference: Font.PreferNoHinting // Prevent shaky text } + delay: 0 visible: internalVisibleCondition contentItem: StyledToolTipContent { diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index 248b46b56..c435f7f8a 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -162,7 +162,7 @@ Scope { } IpcHandler { - target: "overview" + target: "search" function toggle() { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; @@ -185,8 +185,8 @@ Scope { } GlobalShortcut { - name: "overviewToggle" - description: "Toggles overview on press" + name: "searchToggle" + description: "Toggles search on press" onPressed: { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; @@ -201,16 +201,8 @@ Scope { } } GlobalShortcut { - name: "overviewClose" - description: "Closes overview" - - onPressed: { - GlobalStates.overviewOpen = false; - } - } - GlobalShortcut { - name: "overviewToggleRelease" - description: "Toggles overview on release" + name: "searchToggleRelease" + description: "Toggles search on release" onPressed: { GlobalStates.superReleaseMightTrigger = true; @@ -225,8 +217,8 @@ Scope { } } GlobalShortcut { - name: "overviewToggleReleaseInterrupt" - description: "Interrupts possibility of overview being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + name: "searchToggleReleaseInterrupt" + description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." onPressed: { GlobalStates.superReleaseMightTrigger = false; diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml index 5cd7b9ac5..6a5de6a7e 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml @@ -81,7 +81,7 @@ RowLayout { } } - onTextChanged: root.searchingText = text + onTextChanged: LauncherSearch.query = text onAccepted: { if (appResults.count > 0) { diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml index 03ebfce1b..c0ca28d62 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml @@ -14,81 +14,11 @@ import Quickshell.Io Item { // Wrapper id: root readonly property string xdgConfigHome: Directories.config - property string searchingText: "" + property string searchingText: LauncherSearch.query property bool showResults: searchingText != "" implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitHeight: searchBar.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2 - property string mathResult: "" - property bool clipboardWorkSafetyActive: { - const enabled = Config.options.workSafety.enable.clipboard; - const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) - return enabled && sensitiveNetwork; - } - - property var searchActions: [ - { - action: "accentcolor", - execute: args => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]); - } - }, - { - action: "dark", - execute: () => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]); - } - }, - { - action: "konachanwallpaper", - execute: () => { - Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]); - } - }, - { - action: "light", - execute: () => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]); - } - }, - { - action: "superpaste", - execute: args => { - if (!/^(\d+)/.test(args.trim())) { // Invalid if doesn't start with numbers - Quickshell.execDetached([ - "notify-send", - Translation.tr("Superpaste"), - Translation.tr("Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries").arg(Config.options.search.prefix.action), - "-a", "Shell" - ]); - return; - } - const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim()); - const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1; - const isImage = !!syntaxMatch[2]; - Cliphist.superpaste(count, isImage); - } - }, - { - action: "todo", - execute: args => { - Todo.addTask(args); - } - }, - { - action: "wallpaper", - execute: () => { - GlobalStates.wallpaperSelectorOpen = true; - } - }, - { - action: "wipeclipboard", - execute: () => { - Cliphist.wipe(); - } - }, - ] - function focusFirstItem() { appResults.currentIndex = 0; } @@ -103,13 +33,13 @@ Item { // Wrapper function cancelSearch() { searchBar.searchInput.selectAll(); - root.searchingText = ""; + LauncherSearch.query = ""; searchBar.animateWidth = true; } function setSearchingText(text) { searchBar.searchInput.text = text; - root.searchingText = text; + LauncherSearch.query = text; } function containsUnsafeLink(entry) { @@ -118,34 +48,6 @@ Item { // Wrapper return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); } - Timer { - id: nonAppResultsTimer - interval: Config.options.search.nonAppResultDelay - onTriggered: { - let expr = root.searchingText; - if (expr.startsWith(Config.options.search.prefix.math)) { - expr = expr.slice(Config.options.search.prefix.math.length); - } - mathProcess.calculateExpression(expr); - } - } - - Process { - id: mathProcess - property list baseCommand: ["qalc", "-t"] - function calculateExpression(expression) { - mathProcess.running = false; - mathProcess.command = baseCommand.concat(expression); - mathProcess.running = true; - } - stdout: SplitParser { - onRead: data => { - root.mathResult = data; - root.focusFirstItem(); - } - } - } - Keys.onPressed: event => { // Prevent Esc and Backspace from registering if (event.key === Qt.Key_Escape) @@ -285,167 +187,9 @@ Item { // Wrapper model: ScriptModel { id: model objectProp: "key" - values: { - // Search results are handled here - ////////////////// Skip? ////////////////// - if (root.searchingText == "") - return []; - - ///////////// Special cases /////////////// - if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) { - // Clipboard - const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.clipboard); - return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => { - const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive; - let shouldBlurImage = mightBlurImage; - if (mightBlurImage) { - shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1])); - } - const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}` - return { - key: type, - cliphistRawString: entry, - name: StringUtils.cleanCliphistEntry(entry), - clickActionName: "", - type: type, - execute: () => { - Cliphist.copy(entry) - }, - actions: [ - { - name: "Copy", - materialIcon: "content_copy", - execute: () => { - Cliphist.copy(entry); - } - }, - { - name: "Delete", - materialIcon: "delete", - execute: () => { - Cliphist.deleteEntry(entry); - } - } - ], - blurImage: shouldBlurImage, - blurImageText: Translation.tr("Work safety") - }; - }).filter(Boolean); - } - else if (root.searchingText.startsWith(Config.options.search.prefix.emojis)) { - // Clipboard - const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.emojis); - return Emojis.fuzzyQuery(searchString).map(entry => { - const emoji = entry.match(/^\s*(\S+)/)?.[1] || "" - return { - key: emoji, - cliphistRawString: entry, - bigText: emoji, - name: entry.replace(/^\s*\S+\s+/, ""), - clickActionName: "", - type: "Emoji", - execute: () => { - Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]; - } - }; - }).filter(Boolean); - } - - ////////////////// Init /////////////////// - nonAppResultsTimer.restart(); - const mathResultObject = { - key: `Math result: ${root.mathResult}`, - name: root.mathResult, - clickActionName: Translation.tr("Copy"), - type: Translation.tr("Math result"), - fontType: "monospace", - materialSymbol: 'calculate', - execute: () => { - Quickshell.clipboardText = root.mathResult; - } - }; - const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.app)).map(entry => { - entry.clickActionName = Translation.tr("Launch"); - entry.type = Translation.tr("App"); - entry.key = entry.execute - return entry; - }) - const commandResultObject = { - key: `cmd ${root.searchingText}`, - name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.shellCommand).replace("file://", ""), - clickActionName: Translation.tr("Run"), - type: Translation.tr("Run command"), - fontType: "monospace", - materialSymbol: 'terminal', - execute: () => { - let cleanedCommand = root.searchingText.replace("file://", ""); - cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand); - if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) { - cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length); - } - Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]); - } - }; - const webSearchResultObject = { - key: `website ${root.searchingText}`, - name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch), - clickActionName: Translation.tr("Search"), - type: Translation.tr("Search the web"), - materialSymbol: 'travel_explore', - execute: () => { - let query = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch); - let url = Config.options.search.engineBaseUrl + query; - for (let site of Config.options.search.excludedSites) { - url += ` -site:${site}`; - } - Qt.openUrlExternally(url); - } - } - const launcherActionObjects = root.searchActions.map(action => { - const actionString = `${Config.options.search.prefix.action}${action.action}`; - if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) { - return { - key: `Action ${actionString}`, - name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString, - clickActionName: Translation.tr("Run"), - type: Translation.tr("Action"), - materialSymbol: 'settings_suggest', - execute: () => { - action.execute(root.searchingText.split(" ").slice(1).join(" ")); - } - }; - } - return null; - }).filter(Boolean); - - //////// Prioritized by prefix ///////// - let result = []; - const startsWithNumber = /^\d/.test(root.searchingText); - const startsWithMathPrefix = root.searchingText.startsWith(Config.options.search.prefix.math); - const startsWithShellCommandPrefix = root.searchingText.startsWith(Config.options.search.prefix.shellCommand); - const startsWithWebSearchPrefix = root.searchingText.startsWith(Config.options.search.prefix.webSearch); - if (startsWithNumber || startsWithMathPrefix) { - result.push(mathResultObject); - } else if (startsWithShellCommandPrefix) { - result.push(commandResultObject); - } else if (startsWithWebSearchPrefix) { - result.push(webSearchResultObject); - } - - //////////////// Apps ////////////////// - result = result.concat(appResultObjects); - - ////////// Launcher actions //////////// - result = result.concat(launcherActionObjects); - - /// Math result, command, web search /// - if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) { - if (!startsWithShellCommandPrefix) result.push(commandResultObject); - if (!startsWithNumber && !startsWithMathPrefix) result.push(mathResultObject); - if (!startsWithWebSearchPrefix) result.push(webSearchResultObject); - } - - return result; + values: LauncherSearch.results + onValuesChanged: { + root.focusFirstItem(); } } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml index 20af517fe..440695a2e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml @@ -66,7 +66,7 @@ BarButton { } } - AppIcon { + WAppIcon { id: iconWidget anchors.centerIn: parent iconName: root.iconName diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml index 53a37cba0..4502b22f7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml @@ -5,17 +5,12 @@ import qs.modules.common import qs.modules.common.functions import qs.modules.waffle.looks -WButton { +AcrylicButton { id: root property var altAction: () => {} property var middleClickAction: () => {} - colBackground: ColorUtils.transparentize(Looks.colors.bg1) - colBackgroundHover: Looks.colors.bg1Hover - colBackgroundActive: Looks.colors.bg1Active - property color colBackgroundBorder - property color color Layout.fillHeight: true topInset: 4 bottomInset: 4 @@ -23,16 +18,7 @@ WButton { rightInset: 0 horizontalPadding: 8 - colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.backgroundTransparency : 1) - color: { - if (root.down) { - return root.colBackgroundActive - } else if ((root.hovered && !root.down) || root.checked) { - return root.colBackgroundHover - } else { - return root.colBackground - } - } + colBackground: ColorUtils.transparentize(Looks.colors.bg1) MouseArea { anchors.fill: parent @@ -50,15 +36,4 @@ WButton { } } - background: AcrylicRectangle { - shiny: ((root.hovered && !root.down) || root.checked) - color: root.color - radius: Looks.radius.medium - border.width: 1 - border.color: root.colBackgroundBorder - - Behavior on border.color { - animation: Looks.transition.color.createObject(this) - } - } } diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml index f4a15cc00..a92a85578 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml @@ -7,14 +7,16 @@ import qs.services import qs.modules.common import qs.modules.waffle.looks +// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation AppButton { id: root leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0 iconName: down ? "start-here-pressed" : "start-here" + checked: GlobalStates.searchOpen onClicked: { - GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now... + GlobalStates.searchOpen = !GlobalStates.searchOpen; } BarToolTip { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml index 51a3175bc..c1c16096b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml @@ -42,7 +42,7 @@ AppButton { } spacing: 6 - AppIcon { + WAppIcon { id: iconWidget anchors.verticalCenter: parent.verticalCenter iconName: root.iconName diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml index 2839a6747..9f114609f 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml @@ -43,7 +43,7 @@ Button { Layout.fillHeight: false spacing: 8 - AppIcon { + WAppIcon { id: appIcon Layout.leftMargin: Looks.radius.large - root.padding + 2 Layout.alignment: Qt.AlignVCenter diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml new file mode 100644 index 000000000..ef5c0747a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml @@ -0,0 +1,42 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +WButton { + id: root + + colBackground: Looks.colors.bg1 + colBackgroundHover: Looks.colors.bg1Hover + colBackgroundActive: Looks.colors.bg1Active + property color colBackgroundBorder + property color color + property alias border: background.border + property alias shinyColor: background.borderColor + + colBackgroundBorder: ColorUtils.transparentize(color, (root.checked || root.hovered) ? Looks.backgroundTransparency : 0) + color: { + if (root.down) { + return root.colBackgroundActive + } else if ((root.hovered && !root.down) || root.checked) { + return root.colBackgroundHover + } else { + return root.colBackground + } + } + + background: AcrylicRectangle { + id: background + shiny: ((root.hovered && !root.down) || root.checked) + color: root.color + radius: Looks.radius.medium + border.width: 1 + border.color: root.colBackgroundBorder + + Behavior on border.color { + animation: Looks.transition.color.createObject(this) + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml index 7e041d111..7fecaa068 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml @@ -9,16 +9,17 @@ Rectangle { id: root property bool shiny: true // Top border - property color borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, shiny ? 0.5 : 1) + property color borderColor: ColorUtils.transparentize(Looks.colors.bg1Hover, 0.7) + property color internalBorderColor: ColorUtils.transparentize(borderColor, shiny ? 0.0 : 1) color: Looks.colors.bg1Hover radius: Looks.radius.medium Behavior on color { animation: Looks.transition.color.createObject(this) } - Behavior on borderColor { + Behavior on internalBorderColor { animation: Looks.transition.color.createObject(this) } - onBorderColorChanged: { + onInternalBorderColorChanged: { borderCanvas.requestPaint(); } @@ -32,7 +33,7 @@ Rectangle { var ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); - var borderColor = root.borderColor; + var borderColor = root.internalBorderColor; var r = root.radius; var fadeLength = Math.max(1, r); diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index e55f840cb..609c9877b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -53,6 +53,8 @@ Singleton { property color controlBgHover: '#57575B' property color controlFg: "#FFFFFF" property color accentUnfocused: "#848484" + property color link: "#235CCF" + property color inputBg: ColorUtils.transparentize(bg0, 0.4) } darkColors: QtObject { id: darkColors @@ -70,7 +72,7 @@ Singleton { property color bg2: '#8a8a8a' property color bg2Hover: '#b1b1b1' property color bg2Active: '#919191' - property color bg2Border: '#c4c4c4' + property color bg2Border: '#bdbdbd' property color subfg: "#CED1D7" property color fg: "#FFFFFF" property color fg1: "#D1D1D1" @@ -80,6 +82,8 @@ Singleton { property color controlBgHover: "#CFCED1" property color controlFg: "#454545" property color accentUnfocused: "#989898" + property color link: "#A7C9FC" + property color inputBg: ColorUtils.transparentize(darkColors.bg0, 0.5) } colors: QtObject { id: colors @@ -110,6 +114,8 @@ Singleton { property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg + property color inputBg: root.dark ? root.darkColors.inputBg : root.lightColors.inputBg + property color link: root.dark ? root.darkColors.link : root.lightColors.link property color danger: "#C42B1C" property color dangerActive: "#B62D1F" property color warning: "#FF9900" @@ -118,6 +124,7 @@ Singleton { property color accentActive: Appearance.colors.colPrimaryActive property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000" + property color selection: Appearance.colors.colPrimaryContainer } radius: QtObject { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml similarity index 94% rename from dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml index 48ff26104..6f71c65bb 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml @@ -2,7 +2,6 @@ import QtQuick import org.kde.kirigami as Kirigami import qs.services import qs.modules.common -import qs.modules.waffle.looks Kirigami.Icon { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml index 6b2bc4ecb..ceed470ba 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -53,10 +53,11 @@ Button { // Hover stuff signal hoverTimedOut property bool shouldShowTooltip: false + ToolTip.delay: 400 property Timer hoverTimer: Timer { id: hoverTimer running: root.hovered - interval: 400 + interval: root.ToolTip.delay onTriggered: { root.hoverTimedOut(); } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml index 1d24170bc..7fe121165 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml @@ -7,6 +7,10 @@ import qs.services Singleton { id: root + function pathForName(iconName) { + return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`); + } + function wifiIconForStrength(strength) { if (strength > 75) return "wifi-1"; diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml new file mode 100644 index 000000000..a208f355b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -0,0 +1,83 @@ +pragma ComponentBehavior: Bound +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 + +Menu { + id: root + + property bool downDirection: false + property bool hasIcons: false // TODO: implement + + implicitWidth: background.implicitWidth + root.padding * 2 + implicitHeight: background.implicitHeight + root.padding * 2 + padding: 3 + property real sourceEdgeMargin: -implicitHeight + clip: true + + enter: Transition { + NumberAnimation { + property: "sourceEdgeMargin" + from: -root.implicitHeight + to: root.padding + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + exit: Transition { + NumberAnimation { + property: "sourceEdgeMargin" + from: root.padding + to: -root.implicitHeight + duration: 150 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + } + + background: WPane { + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.padding + topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding + bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin + } + contentItem: Rectangle { + color: Looks.colors.bg1Base + implicitWidth: menuListView.implicitWidth + root.padding * 2 + implicitHeight: root.contentItem.implicitHeight + root.padding * 2 + } + } + + contentItem: ListView { + id: menuListView + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.padding * 2 + topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding + bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin + } + implicitHeight: contentHeight + implicitWidth: Array.from({ + length: count + }, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b) + + model: root.contentModel + } + + delegate: WMenuItem { + id: menuItemDelegate + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml new file mode 100644 index 000000000..3030bc122 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml @@ -0,0 +1,93 @@ +pragma ComponentBehavior: Bound +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 + +MenuItem { + id: root + + property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) + property color colBackgroundHover: Looks.colors.bg2Hover + property color colBackgroundActive: Looks.colors.bg2Active + property color colBackgroundToggled: Looks.colors.accent + property color colBackgroundToggledHover: Looks.colors.accentHover + property color colBackgroundToggledActive: Looks.colors.accentActive + property color colForeground: Looks.colors.fg + property color colForegroundToggled: Looks.colors.accentFg + property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4) + property color color: { + if (!root.enabled) + return colBackground; + if (root.checked) { + if (root.down) { + return root.colBackgroundToggledActive; + } else if (root.hovered) { + return root.colBackgroundToggledHover; + } else { + return root.colBackgroundToggled; + } + } + if (root.down) { + return root.colBackgroundActive; + } else if (root.hovered) { + return root.colBackgroundHover; + } else { + return root.colBackground; + } + } + property color fgColor: { + if (root.checked) + return root.colForegroundToggled; + if (root.enabled) + return root.colForeground; + return root.colForegroundDisabled; + } + + property real inset: 2 + topInset: inset + bottomInset: inset + leftInset: inset + rightInset: inset + horizontalPadding: 11 + + background: Rectangle { + id: backgroundRect + radius: Looks.radius.medium + color: root.color + Behavior on color { + animation: Looks.transition.color.createObject(this) + } + } + + implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset + implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding + + contentItem: RowLayout { + id: contentLayout + spacing: 12 + FluentIcon { + id: buttonIcon + monochrome: true + implicitSize: 20 + Layout.fillWidth: false + Layout.alignment: Qt.AlignVCenter + color: root.fgColor + visible: root.icon.name !== ""; + icon: root.icon.name + } + WText { + id: buttonText + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + text: root.text + horizontalAlignment: Text.AlignLeft + font.pixelSize: Looks.font.pixelSize.large + color: root.fgColor + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index be75cc30a..f281a8b7c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -10,7 +10,7 @@ import qs.modules.waffle.looks Item { id: root - required property Item contentItem + property Item contentItem property real radius: Looks.radius.large property alias border: borderRect property alias borderColor: borderRect.border.color diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml index 240fd63be..0da156893 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml @@ -8,8 +8,11 @@ Text { color: Looks.colors.fg font { + hintingPreference: Font.PreferFullHinting family: Looks.font.family.ui pixelSize: Looks.font.pixelSize.normal weight: Looks.font.weight.regular } + + linkColor: Looks.colors.link } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml new file mode 100644 index 000000000..a3f3e8a40 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Controls + +TextInput { + id: root + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: Looks.colors.fg + + font { + hintingPreference: Font.PreferFullHinting + family: Looks.font.family.ui + pixelSize: Looks.font.pixelSize.large + weight: Looks.font.weight.regular + } + + selectionColor: Looks.colors.selection +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml index 3c8d20d26..7368836cc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml @@ -25,6 +25,8 @@ StyledToolTip { verticalPadding: 8 horizontalPadding: 10 + delay: 400 + contentItem: WToolTipContent { id: tooltipContent realContentItem: root.realContentItem diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml index 13c78fc7e..b0a400df0 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml @@ -6,6 +6,7 @@ Item { id: root anchors.centerIn: parent required property Item realContentItem + property alias radius: realContent.radius property real verticalPadding: 8 property real horizontalPadding: 10 implicitWidth: realContent.implicitWidth + 2 * 2 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml new file mode 100644 index 000000000..07499d51b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml @@ -0,0 +1,27 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +StyledImage { + id: avatar + Layout.alignment: Qt.AlignTop + sourceSize: Qt.size(32, 32) + source: Directories.userAvatarPathAccountsService + fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2] + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Circle { + diameter: avatar.height + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml index 797cee28a..f49477988 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -20,6 +20,7 @@ WBarAttachedPanelContent { property bool collapsed: false contentItem: ColumnLayout { + id: contentLayout anchors { horizontalCenter: parent.horizontalCenter top: parent.top @@ -41,9 +42,24 @@ WBarAttachedPanelContent { } contentItem: NotificationPaneContent { implicitWidth: calendarColumnLayout.implicitWidth - implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230 + implicitHeight: { + if (Notifications.list.length > 0) { + return ((contentLayout.height - calendarPane.height - contentLayout.spacing) - notificationPane.borderWidth * 2) + } + return 230; + } + Timer { + id: enableTimer + interval: Config.options.hacks.arbitraryRaceConditionDelay + onTriggered: heightBehavior.enabled = true; + } Behavior on implicitHeight { + id: heightBehavior + enabled: false + Component.onCompleted: { + enableTimer.restart(); + } animation: Looks.transition.enter.createObject(this) } } @@ -51,9 +67,9 @@ WBarAttachedPanelContent { } WPane { - contentItem: ColumnLayout { + id: calendarPane + contentItem: WPanelPageColumn { id: calendarColumnLayout - spacing: 0 DateHeader { Layout.fillWidth: true Synchronizer on collapsed { diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml index 860451fc3..9aa20a690 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml @@ -8,18 +8,24 @@ import qs.modules.common.functions import qs.modules.waffle.looks WBorderlessButton { - id: headerButton + id: root Layout.fillWidth: false - implicitWidth: 16 - implicitHeight: 16 + property real implicitSize: 16 + implicitWidth: implicitSize + implicitHeight: implicitSize color: "transparent" + colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1 + + Behavior on colForeground { + animation: Looks.transition.color.createObject(this) + } contentItem: Item { FluentIcon { anchors.centerIn: parent - implicitSize: 16 - icon: headerButton.icon.name - color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1 + implicitSize: root.implicitSize + icon: root.icon.name + color: root.colForeground } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml index c4331a7dc..faab4d90d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml @@ -2,18 +2,27 @@ import QtQuick import qs import qs.services import qs.modules.common +import qs.modules.common.functions import qs.modules.waffle.looks -SmallBorderedIconButton { +AcrylicButton { id: root property bool iconVisible: true property string iconName: "" property bool iconFilled: true + colBackground: Looks.colors.bg2 + colBackgroundHover: Looks.colors.bg2Hover + colBackgroundActive: Looks.colors.bg2Active + property color colBorder: Looks.colors.bg2Border + property color colBorderToggled: Looks.colors.accent + border.color: checked ? colBorderToggled : colBorder + leftPadding: 12 rightPadding: 12 implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding + implicitHeight: 24 contentItem: Row { id: focusButtonContent diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml index 837c7c016..658bc03ae 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -7,11 +7,13 @@ import qs.modules.common.widgets import qs.modules.common.functions import qs.modules.waffle.looks +// TODO: Swipe to dismiss MouseArea { id: root required property var notificationGroup readonly property var notifications: notificationGroup?.notifications ?? [] + property bool expanded: false implicitWidth: contentLayout.implicitWidth implicitHeight: contentLayout.implicitHeight @@ -34,12 +36,23 @@ MouseArea { interactive: false spacing: 4 model: ScriptModel { - values: root.notifications.slice().reverse() + values: root.expanded ? root.notifications.slice().reverse() : root.notifications.slice(-1) + objectProp: "notificationId" } delegate: WSingleNotification { + required property int index required property var modelData width: ListView.view.width notification: modelData + groupExpandControlMessage: { + if (root.notifications.length <= 1) return ""; + if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1); + if (index === root.notifications.length - 1) return Translation.tr("See fewer"); + return ""; + } + onGroupExpandToggle: { + root.expanded = !root.expanded; + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 42a832adb..895ab6892 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell @@ -12,11 +13,18 @@ MouseArea { id: root required property var notification - property bool expanded: false + property bool expanded: notification.actions.length > 0 + property string groupExpandControlMessage: "" + signal groupExpandToggle + hoverEnabled: true implicitHeight: contentItem.implicitHeight implicitWidth: contentItem.implicitWidth + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } + Rectangle { id: contentItem anchors.fill: parent @@ -26,34 +34,205 @@ MouseArea { implicitHeight: notificationContent.implicitHeight + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2 border.width: 1 - border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow) + border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1) ColumnLayout { id: notificationContent anchors.fill: parent anchors.margins: contentItem.padding + spacing: 19 - RowLayout { + // Header + SingleNotificationHeader { Layout.fillWidth: true - WText { - text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time) + } + + // Content + Item { + id: actualContent + Layout.fillWidth: true + Layout.fillHeight: true + property real spacing: 16 + implicitHeight: Math.max(contentColumn.implicitHeight, imageLoader.height) + implicitWidth: contentColumn.implicitWidth + + Loader { + id: imageLoader + anchors { + top: parent.top + left: parent.left + } + active: root.notification.image != "" + sourceComponent: StyledImage { + readonly property int size: 48 + width: size + height: size + sourceSize.width: size + sourceSize.height: size + source: root.notification.image + fillMode: Image.PreserveAspectFit + } + } + + ColumnLayout { + id: contentColumn + anchors { + top: parent.top + left: parent.left + right: parent.right + } + spacing: 3 + + SummaryText { + id: summaryText + Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0 + } + BodyText { + Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0 + // onLineLaidOut: (line) => { + // if (!imageLoader.active) return; + // const dodgeDistance = imageLoader.width + actualContent.spacing; + // // print(line.y, dodgeDistance) + // if (summaryText.height + line.y > dodgeDistance) { + // line.x -= dodgeDistance; + // line.width += dodgeDistance; + // } + // } + } } } - ColumnLayout { + // Actions + ActionsRow { Layout.fillWidth: true - WText { - Layout.fillWidth: true - elide: Text.ElideRight - text: root.notification.summary - } - WText { - Layout.fillWidth: true - elide: Text.ElideRight + } + + // "+1 notifications" button + GroupExpandButton { + Layout.bottomMargin: 2 + } + } + } + + component SingleNotificationHeader: RowLayout { + ExpandButton { + Layout.topMargin: -2 + } + + Item { + Layout.fillWidth: true + } + + NotificationHeaderButton { + Layout.rightMargin: 4 + opacity: root.containsMouse ? 1 : 0 + icon.name: "dismiss" + implicitSize: 12 + onClicked: { + Qt.callLater(() => { + Notifications.discardNotification(root.notification?.notificationId); + }); + } + } + } + + component ActionsRow: RowLayout { + visible: root.expanded && root.notification.actions.length > 0 + uniformCellSizes: true + Repeater { + id: actionRepeater + model: root.notification.actions + delegate: WBorderedButton { + id: actionButton + Layout.fillHeight: true + required property var modelData + Layout.fillWidth: true + verticalPadding: 16 + horizontalPadding: 12 + text: modelData.text + implicitHeight: actionButtonText.implicitHeight + verticalPadding * 2 + contentItem: WText { + id: actionButtonText + text: actionButton.text + font.pixelSize: Looks.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap - maximumLineCount: root.expanded ? 100 : 1 } } } } + + component SummaryText: WText { + Layout.fillWidth: true + elide: Text.ElideRight + text: root.notification?.summary + font.pixelSize: Looks.font.pixelSize.large + } + + component BodyText: WText { + Layout.fillWidth: true + Layout.fillHeight: true + elide: Text.ElideRight + verticalAlignment: Text.AlignTop + wrapMode: Text.Wrap + maximumLineCount: root.expanded ? 100 : 1 + text: { + if (root.expanded) + return `` + `${NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "
")}`; + return NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "
"); + } + color: Looks.colors.subfg + textFormat: root.expanded ? Text.RichText : Text.StyledText + onLinkActivated: link => { + Qt.openUrlExternally(link); + GlobalStates.sidebarRightOpen = false; + } + } + + component ExpandButton: NotificationHeaderButton { + id: expandButton + implicitWidth: expandButtonContent.implicitWidth + onClicked: root.expanded = !root.expanded + + contentItem: Item { + id: expandButtonContent + implicitWidth: expandButtonRow.implicitWidth + implicitHeight: expandButtonRow.implicitHeight + RowLayout { + id: expandButtonRow + anchors.centerIn: parent + spacing: 8 + WText { + color: expandButton.colForeground + text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time) + } + FluentIcon { + Layout.rightMargin: 12 + icon: "chevron-down" + implicitSize: 18 + rotation: root.expanded ? -180 : 0 + color: expandButton.colForeground + Behavior on rotation { + animation: Looks.transition.rotate.createObject(this) + } + } + } + } + } + + component GroupExpandButton: AcrylicButton { + id: groupExpandButton + visible: root.groupExpandControlMessage !== "" + horizontalPadding: 10 + implicitHeight: 24 + implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2 + onClicked: root.groupExpandToggle() + contentItem: Item { + WText { + id: expandButtonText + anchors.centerIn: parent + text: root.groupExpandControlMessage + } + } + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml new file mode 100644 index 000000000..6bd23ae9b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml @@ -0,0 +1,84 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +FooterRectangle { + id: root + + property bool searching: text.length > 0 + property alias text: searchInput.text + + Component.onCompleted: searchInput.forceActiveFocus() + + focus: true + color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter + + implicitWidth: 832 // TODO: Make sizes naturally inferred + implicitHeight: 63 + + Rectangle { + id: outline + anchors { + fill: parent + leftMargin: 32 + rightMargin: 32 + topMargin: 16 + bottomMargin: 15 + } + color: "transparent" + radius: height / 2 + border.width: 1 + border.color: Looks.colors.bg2Border + } + + Rectangle { + id: searchInputBg + anchors.fill: outline + anchors.margins: 1 + radius: height / 2 + color: Looks.colors.inputBg + + RowLayout { + anchors.fill: parent + spacing: 11 + + WAppIcon { + Layout.leftMargin: 14 + iconName: "system-search-checked" + separateLightDark: true + implicitSize: 18 + } + + WTextInput { + id: searchInput + focus: true + Layout.fillWidth: true + + WText { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + color: Looks.colors.accentUnfocused + text: Translation.tr("Search for apps") // should also have "", settings, and documents" but we don't have those + visible: searchInput.text.length === 0 + font.pixelSize: Looks.font.pixelSize.large + } + } + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml new file mode 100644 index 000000000..cdbb7d3b8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml @@ -0,0 +1,16 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +BodyRectangle { + id: root + + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml new file mode 100644 index 000000000..6e97f88be --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -0,0 +1,39 @@ +pragma ComponentBehavior: Bound +import Qt.labs.synchronizer +import QtQuick +import QtQuick.Controls +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 + + property bool searching: false + property string searchText: "" + + contentItem: WPane { + contentItem: WPanelPageColumn { + SearchBar { + focus: true + Layout.fillWidth: true + Synchronizer on searching { + property alias target: root.searching + } + Synchronizer on text { + property alias source: root.searchText + } + } + Loader { + id: pageContentLoader + Layout.fillWidth: true + source: root.searching ? "SearchPageContent.qml" : "StartPageContent.qml" + } + } + } + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml new file mode 100644 index 000000000..eb3c96a80 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml @@ -0,0 +1,227 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +WPanelPageColumn { + id: root + + WPanelSeparator {} + + BodyRectangle { + implicitHeight: 736 // TODO: Make sizes naturally inferred + } + + WPanelSeparator {} + + StartFooter { + Layout.fillWidth: true + } + + component StartFooter: FooterRectangle { + implicitHeight: 63 + + UserButton { + anchors { + left: parent.left + leftMargin: 52 + bottom: parent.bottom + bottomMargin: 12 + } + } + + PowerButton { + anchors { + right: parent.right + rightMargin: 52 + bottom: parent.bottom + bottomMargin: 12 + } + } + } + + component UserButton: WBorderlessButton { + id: userButton + implicitWidth: userButtonRow.implicitWidth + 12 * 2 + implicitHeight: 40 + + contentItem: Item { + RowLayout { + id: userButtonRow + anchors.centerIn: parent + spacing: 12 + + WUserAvatar { + sourceSize: Qt.size(32, 32) + } + WText { + Layout.alignment: Qt.AlignVCenter + text: SystemInfo.username + } + } + } + + onClicked: { + userMenu.open(); + } + + WToolTip { + text: SystemInfo.username + } + + Popup { + id: userMenu + x: -51 + y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10 + + background: null + + WToolTipContent { + id: popupContent + horizontalPadding: 10 + verticalPadding: 7 + radius: Looks.radius.large + realContentItem: Item { + implicitWidth: userMenuContentLayout.implicitWidth + implicitHeight: userMenuContentLayout.implicitHeight + + ColumnLayout { + id: userMenuContentLayout + anchors { + fill: parent + leftMargin: popupContent.horizontalPadding + rightMargin: popupContent.horizontalPadding + topMargin: popupContent.verticalPadding + bottomMargin: popupContent.verticalPadding + } + spacing: 5 + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 6 + FluentIcon { + Layout.alignment: Qt.AlignVCenter + implicitSize: 22 + icon: "corporation" + monochrome: false + } + WText { + Layout.alignment: Qt.AlignVCenter + text: "Megahard" + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.strong + } + Item { Layout.fillWidth: true } + WBorderlessButton { + Layout.alignment: Qt.AlignVCenter + implicitHeight: 36 + implicitWidth: textItem.implicitWidth + 10 * 2 + contentItem: WText { + id: textItem + text: Translation.tr("Sign out") + font.pixelSize: Looks.font.pixelSize.large + } + onClicked: Session.logout() + } + } + Item { // Force min width 360 (using min on the item somehow doesn't work) + implicitWidth: 334 + } + RowLayout { + Layout.fillWidth: true + Layout.bottomMargin: 7 + Layout.leftMargin: 6 + spacing: 12 + WUserAvatar { + sourceSize: Qt.size(58, 58) + } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 2 + WText { + text: SystemInfo.username + font.pixelSize: Looks.font.pixelSize.larger + font.weight: Looks.font.weight.strong + } + WText { + color: Looks.colors.fg1 + text: Translation.tr("Local account") + } + WText { + color: Looks.colors.accent + text: Translation.tr("Manage my account") + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser]) + GlobalStates.searchOpen = false; + } + } + } + } + } + } + } + } + } + } + + component PowerButton: WBorderlessButton { + id: powerButton + implicitWidth: 40 + implicitHeight: 40 + + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: "power" + implicitSize: 20 + } + } + + WToolTip { + extraVisibleCondition: !powerMenu.visible + text: qsTr("Power") + } + + onClicked: { + powerMenu.open() + } + + WMenu { + id: powerMenu + x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2 + y: -powerMenu.implicitHeight - 4 + Action { + icon.name: "lock-closed" + text: Translation.tr("Lock") + onTriggered: Session.lock() + } + Action { + icon.name: "weather-moon" + text: Translation.tr("Sleep") + onTriggered: Session.suspend() + } + Action { + icon.name: "power" + text: Translation.tr("Shut down") + onTriggered: Session.poweroff() + } + Action { + icon.name: "arrow-counterclockwise" + text: Translation.tr("Restart") + onTriggered: Session.reboot() + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml new file mode 100644 index 000000000..9b59fb0c8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml @@ -0,0 +1,119 @@ +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 onSearchOpenChanged() { + if (GlobalStates.searchOpen) + panelLoader.active = true; + } + } + + Loader { + id: panelLoader + active: GlobalStates.searchOpen + sourceComponent: PanelWindow { + id: panelWindow + exclusiveZone: 0 + WlrLayershell.namespace: "quickshell:wStartMenu" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + anchors { + bottom: Config.options.waffles.bar.bottom + top: !Config.options.waffles.bar.bottom + left: Config.options.waffles.bar.leftAlignApps + } + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + HyprlandFocusGrab { + id: focusGrab + active: true + windows: [panelWindow] + onCleared: content.close() + } + + Connections { + target: GlobalStates + function onSearchOpenChanged() { + if (!GlobalStates.searchOpen) + content.close(); + } + } + + StartMenuContent { + id: content + anchors.fill: parent + focus: true + + onClosed: { + GlobalStates.searchOpen = false; + panelLoader.active = false; + } + } + } + } + + IpcHandler { + target: "search" + + function toggle() { + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + function close() { + GlobalStates.searchOpen = false; + } + function open() { + GlobalStates.searchOpen = true; + } + function toggleReleaseInterrupt() { + GlobalStates.superReleaseMightTrigger = false; + } + } + + GlobalShortcut { + name: "searchToggle" + description: "Toggles search on press" + + onPressed: { + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + } + GlobalShortcut { + name: "searchToggleRelease" + description: "Toggles search on release" + + onPressed: { + GlobalStates.superReleaseMightTrigger = true; + } + + onReleased: { + if (!GlobalStates.superReleaseMightTrigger) { + GlobalStates.superReleaseMightTrigger = true; + return; + } + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + } + GlobalShortcut { + name: "searchToggleReleaseInterrupt" + description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + + onPressed: { + GlobalStates.superReleaseMightTrigger = false; + } + } +} diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml new file mode 100644 index 000000000..bf23c1c48 --- /dev/null +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -0,0 +1,278 @@ +pragma Singleton + +import qs.modules.common +import qs.modules.common.models +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + property string query: "" + property var searchActions: [ + { + action: "accentcolor", + execute: args => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]); + } + }, + { + action: "dark", + execute: () => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]); + } + }, + { + action: "konachanwallpaper", + execute: () => { + Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]); + } + }, + { + action: "light", + execute: () => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]); + } + }, + { + action: "superpaste", + execute: args => { + if (!/^(\d+)/.test(args.trim())) { + // Invalid if doesn't start with numbers + Quickshell.execDetached(["notify-send", Translation.tr("Superpaste"), Translation.tr("Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries").arg(Config.options.search.prefix.action), "-a", "Shell"]); + return; + } + const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim()); + const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1; + const isImage = !!syntaxMatch[2]; + Cliphist.superpaste(count, isImage); + } + }, + { + action: "todo", + execute: args => { + Todo.addTask(args); + } + }, + { + action: "wallpaper", + execute: () => { + GlobalStates.wallpaperSelectorOpen = true; + } + }, + { + action: "wipeclipboard", + execute: () => { + Cliphist.wipe(); + } + }, + ] + + property string mathResult: "" + property bool clipboardWorkSafetyActive: { + const enabled = Config.options.workSafety.enable.clipboard; + const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + return enabled && sensitiveNetwork; + } + + function containsUnsafeLink(entry) { + if (entry == undefined) return false; + const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords; + return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); + } + + Timer { + id: nonAppResultsTimer + interval: Config.options.search.nonAppResultDelay + onTriggered: { + let expr = root.query; + if (expr.startsWith(Config.options.search.prefix.math)) { + expr = expr.slice(Config.options.search.prefix.math.length); + } + mathProc.calculateExpression(expr); + } + } + + Process { + id: mathProc + property list baseCommand: ["qalc", "-t"] + function calculateExpression(expression) { + mathProc.running = false; + mathProc.command = baseCommand.concat(expression); + mathProc.running = true; + } + stdout: SplitParser { + onRead: data => { + root.mathResult = data; + } + } + } + + property list results: { + // Search results are handled here + ////////////////// Skip? ////////////////// + if (root.query == "") + return []; + + ///////////// Special cases /////////////// + if (root.query.startsWith(Config.options.search.prefix.clipboard)) { + // Clipboard + const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.clipboard); + return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => { + const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive; + let shouldBlurImage = mightBlurImage; + if (mightBlurImage) { + shouldBlurImage = shouldBlurImage && (root.containsUnsafeLink(array[index - 1]) || root.containsUnsafeLink(array[index + 1])); + } + const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`; + return { + key: type, + cliphistRawString: entry, + name: StringUtils.cleanCliphistEntry(entry), + clickActionName: "", + type: type, + execute: () => { + Cliphist.copy(entry); + }, + actions: [ + { + name: "Copy", + materialIcon: "content_copy", + execute: () => { + Cliphist.copy(entry); + } + }, + { + name: "Delete", + materialIcon: "delete", + execute: () => { + Cliphist.deleteEntry(entry); + } + } + ], + blurImage: shouldBlurImage, + blurImageText: Translation.tr("Work safety") + }; + }).filter(Boolean); + } else if (root.query.startsWith(Config.options.search.prefix.emojis)) { + // Clipboard + const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.emojis); + return Emojis.fuzzyQuery(searchString).map(entry => { + const emoji = entry.match(/^\s*(\S+)/)?.[1] || ""; + return { + key: emoji, + cliphistRawString: entry, + bigText: emoji, + name: entry.replace(/^\s*\S+\s+/, ""), + clickActionName: "", + type: "Emoji", + execute: () => { + Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]; + } + }; + }).filter(Boolean); + } + + ////////////////// Init /////////////////// + nonAppResultsTimer.restart(); + const mathResultObject = { + key: `Math result: ${root.mathResult}`, + name: root.mathResult, + clickActionName: Translation.tr("Copy"), + type: Translation.tr("Math result"), + fontType: "monospace", + materialSymbol: 'calculate', + execute: () => { + Quickshell.clipboardText = root.mathResult; + } + }; + const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => { + entry.clickActionName = Translation.tr("Launch"); + entry.type = Translation.tr("App"); + entry.key = entry.execute; + return entry; + }); + const commandResultObject = { + key: `cmd ${root.query}`, + name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.shellCommand).replace("file://", ""), + clickActionName: Translation.tr("Run"), + type: Translation.tr("Run command"), + fontType: "monospace", + materialSymbol: 'terminal', + execute: () => { + let cleanedCommand = root.query.replace("file://", ""); + cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand); + if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) { + cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length); + } + Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]); + } + }; + const webSearchResultObject = { + key: `website ${root.query}`, + name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch), + clickActionName: Translation.tr("Search"), + type: Translation.tr("Search the web"), + materialSymbol: 'travel_explore', + execute: () => { + let query = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch); + let url = Config.options.search.engineBaseUrl + query; + for (let site of Config.options.search.excludedSites) { + url += ` -site:${site}`; + } + Qt.openUrlExternally(url); + } + }; + const launcherActionObjects = root.searchActions.map(action => { + const actionString = `${Config.options.search.prefix.action}${action.action}`; + if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) { + return { + key: `Action ${actionString}`, + name: root.query.startsWith(actionString) ? root.query : actionString, + clickActionName: Translation.tr("Run"), + type: Translation.tr("Action"), + materialSymbol: 'settings_suggest', + execute: () => { + action.execute(root.query.split(" ").slice(1).join(" ")); + } + }; + } + return null; + }).filter(Boolean); + + //////// Prioritized by prefix ///////// + let result = []; + const startsWithNumber = /^\d/.test(root.query); + const startsWithMathPrefix = root.query.startsWith(Config.options.search.prefix.math); + const startsWithShellCommandPrefix = root.query.startsWith(Config.options.search.prefix.shellCommand); + const startsWithWebSearchPrefix = root.query.startsWith(Config.options.search.prefix.webSearch); + if (startsWithNumber || startsWithMathPrefix) { + result.push(mathResultObject); + } else if (startsWithShellCommandPrefix) { + result.push(commandResultObject); + } else if (startsWithWebSearchPrefix) { + result.push(webSearchResultObject); + } + + //////////////// Apps ////////////////// + result = result.concat(appResultObjects); + + ////////// Launcher actions //////////// + result = result.concat(launcherActionObjects); + + /// Math result, command, web search /// + if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) { + if (!startsWithShellCommandPrefix) + result.push(commandResultObject); + if (!startsWithNumber && !startsWithMathPrefix) + result.push(mathResultObject); + if (!startsWithWebSearchPrefix) + result.push(webSearchResultObject); + } + + return result; + } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 3f57c0c28..7bb565d11 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -33,6 +33,7 @@ import qs.modules.waffle.background import qs.modules.waffle.bar import qs.modules.waffle.notificationCenter import qs.modules.waffle.onScreenDisplay +import qs.modules.waffle.startMenu import QtQuick import QtQuick.Window @@ -77,11 +78,13 @@ 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 {} } PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } + PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } ReloadPopup {} component PanelLoader: LazyLoader { @@ -94,7 +97,7 @@ ShellRoot { property list families: ["ii", "waffle"] property var panelFamilies: ({ "ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"], - "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) diff --git a/sdata/deps-info.md b/sdata/deps-info.md index fc6f315a3..9a1e4e826 100644 --- a/sdata/deps-info.md +++ b/sdata/deps-info.md @@ -191,6 +191,9 @@ Tips: - Used in Quickshell config. - `wlogout` - Used in Hyprland config. +- `libqalculate` + - Used in Quickshell config, providing math ability in searchbar. + - Note that `qalc` is the needed executable. In Arch Linux [libqalculate](https://archlinux.org/packages/extra/x86_64/libqalculate) provides it, but in Fedora [qalculate](https://packages.fedoraproject.org/pkgs/libqalculate/qalculate/fedora-43.html#files) does and [libqalculate](https://packages.fedoraproject.org/pkgs/libqalculate/libqalculate/fedora-43.html#files) does not. # Actual packages diff --git a/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD b/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD index 76f71d43f..fa9d4df02 100644 --- a/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD @@ -1,6 +1,6 @@ pkgname=illogical-impulse-widgets pkgver=1.0 -pkgrel=5 +pkgrel=6 pkgdesc='Illogical Impulse Widget Dependencies' arch=(any) license=(None) @@ -14,4 +14,5 @@ depends=( songrec translate-shell wlogout + libqalculate ) diff --git a/sdata/dist-fedora/feddeps.toml b/sdata/dist-fedora/feddeps.toml index f21169a1b..367626ced 100644 --- a/sdata/dist-fedora/feddeps.toml +++ b/sdata/dist-fedora/feddeps.toml @@ -181,6 +181,7 @@ packages = [ "hyprpicker", "songrec", "translate-shell", + "qalculate", "wlogout" ] @@ -191,4 +192,5 @@ packages = [ "plasma-systemmonitor", "unzip" ] -install_opts = ["--setopt=install_weak_deps=False"] \ No newline at end of file +install_opts = ["--setopt=install_weak_deps=False"] + diff --git a/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r3.ebuild b/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r4.ebuild similarity index 95% rename from sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r3.ebuild rename to sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r4.ebuild index 95ffeffba..9ffceea09 100644 --- a/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r3.ebuild +++ b/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r4.ebuild @@ -21,6 +21,7 @@ RDEPEND=" app-misc/songrec app-i18n/translate-shell gui-apps/wlogout + sci-libs/libqalculate " ##### CUSTOM EBUILDS # app-misc/songrec diff --git a/sdata/dist-nix/README.md b/sdata/dist-nix/README.md index 3a8120e04..9aca1d13f 100644 --- a/sdata/dist-nix/README.md +++ b/sdata/dist-nix/README.md @@ -37,8 +37,38 @@ As [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3 See also [caelestia-dots/shell#668](https://github.com/caelestia-dots/shell/issues/668). -### NixGL -On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem. +### GPU +On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. + +~~`nixGL` should be used to address the problem.~~ + +Since home-manager 25.11, for non-NixOS just set the following: +```nix +targets.genericLinux.enable = true; +``` +Then during building, home-manager will show a message to tell you running a command manually to configure GPU, like: +```bash +sudo /nix/store/-non-nixos-gpu/bin/non-nixos-gpu-setup +``` +It runs a bash script with following content: +``` +#!/nix/store/-bash-/bin/bash + +set -e + +# Install the systemd service file and ensure that the store path won't be +# garbage-collected as long as it's installed. +unit_path=/etc/systemd/system/non-nixos-gpu.service +ln -sf /nix/store/-non-nixos-gpu/resources/non-nixos-gpu.service "$unit_path" +ln -sf "$unit_path" "/nix/var/nix"/gcroots/non-nixos-gpu.service + +systemctl daemon-reload +systemctl enable non-nixos-gpu.service +systemctl restart non-nixos-gpu.service +``` +_Note: it uses `systemctl`, maybe won't work for OpenRC..._ + +See [gpu-non-nixos](https://nix-community.github.io/home-manager/index.xhtml#sec-usage-gpu-non-nixos). # Handling dot files ## Status diff --git a/sdata/dist-nix/home-manager/flake.nix b/sdata/dist-nix/home-manager/flake.nix index d88daad6e..97a50c466 100644 --- a/sdata/dist-nix/home-manager/flake.nix +++ b/sdata/dist-nix/home-manager/flake.nix @@ -3,23 +3,24 @@ description = "illogical-impulse"; inputs = { - # Qt 6.10 is not yet available from released version of nixpkgs. - #nixpkgs.url = "nixpkgs/nixos-25.05"; - nixpkgs.url = "nixpkgs/nixos-unstable"; + nixpkgs.url = "nixpkgs/nixos-25.11"; + #nixpkgs.url = "nixpkgs/nixos-unstable"; home-manager = { - #url = "github:nix-community/home-manager/release-25.05"; - url = "github:nix-community/home-manager/master"; + url = "github:nix-community/home-manager/release-25.11"; + #url = "github:nix-community/home-manager/master"; inputs.nixpkgs.follows = "nixpkgs"; }; - nixgl.url = "github:nix-community/nixGL"; + #nixgl.url = "github:nix-community/nixGL"; quickshell = { url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { nixpkgs, home-manager, nixgl, quickshell, ... }: + outputs = { nixpkgs, home-manager, + #nixgl, + quickshell, ... }: let home_attrs = rec { username = import ./username.nix; @@ -36,7 +37,9 @@ homeConfigurations = { illogical_impulse = home-manager.lib.homeManagerConfiguration { inherit pkgs; - extraSpecialArgs = { inherit home_attrs nixgl quickshell; }; + extraSpecialArgs = { inherit home_attrs + #nixgl + quickshell; }; modules = [ ./home.nix ]; diff --git a/sdata/dist-nix/home-manager/home.nix b/sdata/dist-nix/home-manager/home.nix index 596430395..b1381481d 100644 --- a/sdata/dist-nix/home-manager/home.nix +++ b/sdata/dist-nix/home-manager/home.nix @@ -1,8 +1,13 @@ -{ config, lib, pkgs, nixgl, quickshell, home_attrs, ... }: +{ config, lib, pkgs, +#nixgl, +quickshell, home_attrs, ... }: { programs.home-manager.enable = true; - nixGL.packages = nixgl.packages; - nixGL.defaultWrapper = "mesa"; + + # Necessary for non-NixOS to handle GPU (since home-manager version 25.11) + targets.genericLinux.enable = true; + #nixGL.packages = nixgl.packages; + #nixGL.defaultWrapper = "mesa"; xdg.portal = { enable = true; @@ -27,7 +32,8 @@ systemd.enable = false; plugins = []; settings = {}; extraConfig = ""; enable = true; ## Use NixGL - package = config.lib.nixGL.wrap pkgs.hyprland; + #package = config.lib.nixGL.wrap pkgs.hyprland; + package = pkgs.hyprland; }; home = { @@ -167,6 +173,7 @@ songrec #songrec translate-shell #translate-shell wlogout #wlogout + libqalculate #libqalculate ] ++ [ @@ -174,7 +181,9 @@ ### illogical-impulse-quickshell-git #(config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default) - (import ./quickshell.nix { inherit pkgs quickshell; nixGLWrap = config.lib.nixGL.wrap; }) + (import ./quickshell.nix { inherit pkgs quickshell; + #nixGLWrap = config.lib.nixGL.wrap; + }) ]; }//home_attrs; } diff --git a/sdata/dist-nix/home-manager/quickshell.nix b/sdata/dist-nix/home-manager/quickshell.nix index 0fb7b57fe..fb81af848 100644 --- a/sdata/dist-nix/home-manager/quickshell.nix +++ b/sdata/dist-nix/home-manager/quickshell.nix @@ -1,10 +1,14 @@ -{ pkgs, quickshell, nixGLWrap, ... }: +{ pkgs, quickshell, +#nixGLWrap, +... }: let - qs = nixGLWrap quickshell.packages.x86_64-linux.default; + #qs = nixGLWrap quickshell.packages.x86_64-linux.default; + qs = quickshell.packages.x86_64-linux.default; in pkgs.stdenv.mkDerivation { name = "illogical-impulse-quickshell-wrapper"; meta = with pkgs.lib; { - description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage"; + #description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage"; + description = "Quickshell bundled Qt deps for home-manager usage"; license = licenses.gpl3Only; }; diff --git a/sdata/dist-nix/install-deps.sh b/sdata/dist-nix/install-deps.sh index 357bb498f..2049efb82 100644 --- a/sdata/dist-nix/install-deps.sh +++ b/sdata/dist-nix/install-deps.sh @@ -32,8 +32,8 @@ function install_home-manager(){ try source $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh command -v $cmd && return - x nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs-home - x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz home-manager + x nix-channel --add https://nixos.org/channels/nixos-25.11 nixpkgs-home + x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz home-manager x nix-channel --update x env NIX_PATH="nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs-home" nix-shell '' -A install @@ -56,6 +56,7 @@ function hm_deps(){ x home-manager switch --flake .#illogical_impulse \ --extra-experimental-features nix-command \ --extra-experimental-features flakes + x sudo /nix/store/*-non-nixos-gpu/bin/non-nixos-gpu-setup cd $REPO_ROOT x git rm -f "${SETUP_USERNAME_NIXFILE}" }