summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorBora Gülerman <49169566+eratoriele@users.noreply.github.com>2026-02-19 13:26:10 +0300
committerGitHub <noreply@github.com>2026-02-19 21:26:10 +1100
commit40a255283083301b9503e1cbb9f0ea7db83e069a (patch)
treeb98f42e7dd34fffb87fb6f81c82fc93b091163b5 /modules
parent[CI] chore: update flake (diff)
downloadcaelestia-shell-40a255283083301b9503e1cbb9f0ea7db83e069a.tar.gz
caelestia-shell-40a255283083301b9503e1cbb9f0ea7db83e069a.tar.bz2
caelestia-shell-40a255283083301b9503e1cbb9f0ea7db83e069a.zip
launcher: add favorite apps (#946)
* launcher: add favorite apps Favorite apps always appear above non-favorite apps Accepts regex, same logic as #920 Added the same regex logic to hidden apps Added util file may need to be relocated * addressed requested changes * fix: Renamed newly added util singleton Also added a null check to favorite icon loader in AppItem.qml * controlCenter/launcherPane: added favorite apps added icons to the app list to indicate if they are favorited/hidden marking as favorite/hidden is desabled if the other is selected * favouriteApps: renamed from favorite to favourite Also disabled favorite/hidden switch for entries added as regex * appDb: added notify and emit to favoriteApps * controlCentre/Launcher: Fixed bug with favourite switch not enabling itself when no hiddenApps exist Added a comment to explain the enabled state of the switches icon loader is now a single loader rather than two, hidden icon has priority * spelling mistakes * fixed warning * formatting fixes
Diffstat (limited to 'modules')
-rw-r--r--modules/controlcenter/launcher/LauncherPane.qml92
-rw-r--r--modules/drawers/Drawers.qml16
-rw-r--r--modules/launcher/items/AppItem.qml19
-rw-r--r--modules/launcher/services/Apps.qml3
4 files changed, 99 insertions, 31 deletions
diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml
index 0dd464f..b236cf9 100644
--- a/modules/controlcenter/launcher/LauncherPane.qml
+++ b/modules/controlcenter/launcher/LauncherPane.qml
@@ -24,6 +24,7 @@ Item {
property var selectedApp: root.session.launcher.active
property bool hideFromLauncherChecked: false
+ property bool favouriteChecked: false
anchors.fill: parent
@@ -43,16 +44,14 @@ Item {
function updateToggleState() {
if (!root.selectedApp) {
root.hideFromLauncherChecked = false;
+ root.favouriteChecked = false;
return;
}
const appId = root.selectedApp.id || root.selectedApp.entry?.id;
- if (Config.launcher.hiddenApps && Config.launcher.hiddenApps.length > 0) {
- root.hideFromLauncherChecked = Config.launcher.hiddenApps.includes(appId);
- } else {
- root.hideFromLauncherChecked = false;
- }
+ root.hideFromLauncherChecked = Config.launcher.hiddenApps && Config.launcher.hiddenApps.length > 0 && Strings.testRegexList(Config.launcher.hiddenApps, appId);
+ root.favouriteChecked = Config.launcher.favouriteApps && Config.launcher.favouriteApps.length > 0 && Strings.testRegexList(Config.launcher.favouriteApps, appId);
}
function saveHiddenApps(isHidden) {
@@ -83,6 +82,7 @@ Item {
id: allAppsDb
path: `${Paths.state}/apps.sqlite`
+ favouriteApps: Config.launcher.favouriteApps
entries: DesktopEntries.applications.values
}
@@ -286,6 +286,7 @@ Item {
id: appsListLoader
Layout.fillWidth: true
Layout.fillHeight: true
+ asynchronous: true
active: true
sourceComponent: StyledListView {
@@ -305,7 +306,8 @@ Item {
delegate: StyledRect {
required property var modelData
- width: parent ? parent.width : 0
+ width: parent ? parent.width : 0
+ implicitHeight: 40
readonly property bool isSelected: root.selectedApp === modelData
@@ -353,9 +355,34 @@ Item {
text: modelData.name || modelData.entry?.name || qsTr("Unknown")
font.pointSize: Appearance.font.size.normal
}
- }
- implicitHeight: 40
+ Loader {
+ Layout.alignment: Qt.AlignVCenter
+ readonly property bool isHidden: modelData ? Strings.testRegexList(Config.launcher.hiddenApps, modelData.id) : false
+ readonly property bool isFav: modelData ? Strings.testRegexList(Config.launcher.favouriteApps, modelData.id) : false
+ active: isHidden || isFav
+
+ sourceComponent: isHidden ? hiddenIcon : (isFav ? favouriteIcon : null)
+ }
+
+ Component {
+ id: hiddenIcon
+ MaterialIcon {
+ text: "visibility_off"
+ fill: 1
+ color: Colours.palette.m3primary
+ }
+ }
+
+ Component {
+ id: favouriteIcon
+ MaterialIcon {
+ text: "favorite"
+ fill: 1
+ color: Colours.palette.m3primary
+ }
+ }
+ }
}
}
}
@@ -440,13 +467,11 @@ Item {
onDisplayedAppChanged: {
if (displayedApp) {
const appId = displayedApp.id || displayedApp.entry?.id;
- if (Config.launcher.hiddenApps && Config.launcher.hiddenApps.length > 0) {
- root.hideFromLauncherChecked = Config.launcher.hiddenApps.includes(appId);
- } else {
- root.hideFromLauncherChecked = false;
- }
+ root.hideFromLauncherChecked = Config.launcher.hiddenApps && Config.launcher.hiddenApps.length > 0 && Strings.testRegexList(Config.launcher.hiddenApps, appId);
+ root.favouriteChecked = Config.launcher.favouriteApps && Config.launcher.favouriteApps.length > 0 && Strings.testRegexList(Config.launcher.favouriteApps, appId);
} else {
root.hideFromLauncherChecked = false;
+ root.favouriteChecked = false;
}
}
}
@@ -562,9 +587,48 @@ Item {
SwitchRow {
Layout.topMargin: Appearance.spacing.normal
visible: appDetailsLayout.displayedApp !== null
+ label: qsTr("Mark as favourite")
+ checked: root.favouriteChecked
+ // disabled if:
+ // * app is hidden
+ // * app isn't in favouriteApps array but marked as favourite anyway
+ // ^^^ This means that this app is favourited because of a regex check
+ // this button can not toggle regexed apps
+ enabled: appDetailsLayout.displayedApp !== null && !root.hideFromLauncherChecked && (Config.launcher.favouriteApps.indexOf(appDetailsLayout.displayedApp.id || appDetailsLayout.displayedApp.entry?.id) !== -1 || !root.favouriteChecked)
+ opacity: enabled ? 1 : 0.6
+ onToggled: checked => {
+ root.favouriteChecked = checked;
+ const app = appDetailsLayout.displayedApp;
+ if (app) {
+ const appId = app.id || app.entry?.id;
+ const favouriteApps = Config.launcher.favouriteApps ? [...Config.launcher.favouriteApps] : [];
+ if (checked) {
+ if (!favouriteApps.includes(appId)) {
+ favouriteApps.push(appId);
+ }
+ } else {
+ const index = favouriteApps.indexOf(appId);
+ if (index !== -1) {
+ favouriteApps.splice(index, 1);
+ }
+ }
+ Config.launcher.favouriteApps = favouriteApps;
+ Config.save();
+ }
+ }
+ }
+ SwitchRow {
+ Layout.topMargin: Appearance.spacing.normal
+ visible: appDetailsLayout.displayedApp !== null
label: qsTr("Hide from launcher")
checked: root.hideFromLauncherChecked
- enabled: appDetailsLayout.displayedApp !== null
+ // disabled if:
+ // * app is favourited
+ // * app isn't in hiddenApps array but marked as hidden anyway
+ // ^^^ This means that this app is hidden because of a regex check
+ // this button can not toggle regexed apps
+ enabled: appDetailsLayout.displayedApp !== null && !root.favouriteChecked && (Config.launcher.hiddenApps.indexOf(appDetailsLayout.displayedApp.id || appDetailsLayout.displayedApp.entry?.id) !== -1 || !root.hideFromLauncherChecked)
+ opacity: enabled ? 1 : 0.6
onToggled: checked => {
root.hideFromLauncherChecked = checked;
const app = appDetailsLayout.displayedApp;
diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml
index 00f9596..93534ec 100644
--- a/modules/drawers/Drawers.qml
+++ b/modules/drawers/Drawers.qml
@@ -4,6 +4,7 @@ import qs.components
import qs.components.containers
import qs.services
import qs.config
+import qs.utils
import qs.modules.bar
import Quickshell
import Quickshell.Wayland
@@ -18,20 +19,7 @@ Variants {
id: scope
required property ShellScreen modelData
- readonly property bool barDisabled: {
- const regexChecker = /^\^.*\$$/;
- for (const filter of Config.bar.excludedScreens) {
- // If filter is a regex
- if (regexChecker.test(filter)) {
- if ((new RegExp(filter)).test(modelData.name))
- return true;
- } else {
- if (filter === modelData.name)
- return true;
- }
- }
- return false;
- }
+ readonly property bool barDisabled: Strings.testRegexList(Config.bar.excludedScreens, modelData.name)
Exclusions {
screen: scope.modelData
diff --git a/modules/launcher/items/AppItem.qml b/modules/launcher/items/AppItem.qml
index 48aace7..2bd818d 100644
--- a/modules/launcher/items/AppItem.qml
+++ b/modules/launcher/items/AppItem.qml
@@ -2,6 +2,7 @@ import "../services"
import qs.components
import qs.services
import qs.config
+import qs.utils
import Quickshell
import Quickshell.Widgets
import QtQuick
@@ -46,7 +47,7 @@ Item {
anchors.leftMargin: Appearance.spacing.normal
anchors.verticalCenter: icon.verticalCenter
- implicitWidth: parent.width - icon.width
+ implicitWidth: parent.width - icon.width - favouriteIcon.width
implicitHeight: name.implicitHeight + comment.implicitHeight
StyledText {
@@ -64,10 +65,24 @@ Item {
color: Colours.palette.m3outline
elide: Text.ElideRight
- width: root.width - icon.width - Appearance.rounding.normal * 2
+ width: root.width - icon.width - favouriteIcon.width - Appearance.rounding.normal * 2
anchors.top: name.bottom
}
}
+
+ Loader {
+ id: favouriteIcon
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ active: modelData && Strings.testRegexList(Config.launcher.favouriteApps, modelData.id)
+
+ sourceComponent: MaterialIcon {
+ text: "favorite"
+ fill: 1
+ color: Colours.palette.m3primary
+ }
+ }
}
}
diff --git a/modules/launcher/services/Apps.qml b/modules/launcher/services/Apps.qml
index c409a7b..7f2d645 100644
--- a/modules/launcher/services/Apps.qml
+++ b/modules/launcher/services/Apps.qml
@@ -72,6 +72,7 @@ Searcher {
id: appDb
path: `${Paths.state}/apps.sqlite`
- entries: DesktopEntries.applications.values.filter(a => !Config.launcher.hiddenApps.includes(a.id))
+ favouriteApps: Config.launcher.favouriteApps
+ entries: DesktopEntries.applications.values.filter(a => !Strings.testRegexList(Config.launcher.hiddenApps, a.id))
}
}