mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 12:13:31 -04:00
feat(settings): Added Settings Tab Autostart App (XDG Autostart) (#2535)
* feat(Autostart): add Autostart tab and application selection popup * fix(AutoStartTab): update systemdUserDir property to use XDG_CONFIG_HOME * fix(AutoStartTab): update autostartDir and systemdUserDir to use StandardPaths for config home * refactor(AutoStartTab): use FileView & FolderListModel * refactor(AutoStartTab): implement systemd override generation for autostart applications using FileView * feat(AutoStartTab): add systemd check to determine environment and update tray icon visibility * feat(SettingsSidebar, AutoStartTab, DesktopService): add autostart functionality and systemd checks * feat(AutoStartTab): add hidden property support for desktop entries and toggle functionality * feat(AutoStartTab): add initialize autostart directory and add toast if writer failed * add(AutoStartTab): logging for scoped log tracking ---------
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
FloatingWindow {
|
||||
id: root
|
||||
|
||||
property bool disablePopupTransparency: true
|
||||
property string searchQuery: ""
|
||||
property var filteredApps: []
|
||||
property int selectedIndex: -1
|
||||
property bool keyboardNavigationActive: false
|
||||
property var appsModel: []
|
||||
property var parentModal: null
|
||||
parentWindow: parentModal
|
||||
|
||||
signal appSelected(string appId)
|
||||
|
||||
objectName: "appBrowserPopup"
|
||||
title: I18n.tr("Select Application")
|
||||
minimumSize: Qt.size(400, 350)
|
||||
implicitWidth: 500
|
||||
implicitHeight: 550
|
||||
color: "transparent"
|
||||
visible: false
|
||||
|
||||
WindowBlur {
|
||||
targetWindow: root
|
||||
blurX: 0
|
||||
blurY: 0
|
||||
blurWidth: root.visible ? root.width : 0
|
||||
blurHeight: root.visible ? root.height : 0
|
||||
blurRadius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, 0.95)
|
||||
border.color: Theme.outlineMedium
|
||||
border.width: Theme.layerOutlineWidth
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
root.hide();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Down:
|
||||
root.selectNext();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Up:
|
||||
root.selectPrevious();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
if (root.keyboardNavigationActive) {
|
||||
root.selectApp();
|
||||
} else if (root.filteredApps.length > 0) {
|
||||
root.selectAppByIndex(0);
|
||||
}
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: windowControls.tryStartMove()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, 0.5)
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "add_circle"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Select Application")
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankActionButton {
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
onClicked: root.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height - 48
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankTextField {
|
||||
id: searchField
|
||||
width: parent.width
|
||||
height: 48
|
||||
cornerRadius: Theme.cornerRadius
|
||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainerHigh, 0.8)
|
||||
normalBorderColor: Theme.outlineMedium
|
||||
focusedBorderColor: Theme.primary
|
||||
leftIconName: "search"
|
||||
leftIconSize: Theme.iconSize
|
||||
leftIconColor: Theme.surfaceVariantText
|
||||
leftIconFocusedColor: Theme.primary
|
||||
showClearButton: true
|
||||
textColor: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
placeholderText: I18n.tr("Search applications...")
|
||||
text: root.searchQuery
|
||||
onTextEdited: {
|
||||
root.searchQuery = text;
|
||||
root.updateFilteredApps();
|
||||
}
|
||||
}
|
||||
|
||||
DankListView {
|
||||
id: appList
|
||||
width: parent.width
|
||||
height: parent.height - searchField.height - Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
model: root.filteredApps
|
||||
clip: true
|
||||
|
||||
delegate: Rectangle {
|
||||
width: appList.width
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
readonly property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex
|
||||
|
||||
color: isSelected ? Theme.withAlpha(Theme.primary, 0.16) : appArea.containsMouse ? Theme.withAlpha(Theme.primary, 0.08) : Theme.withAlpha(Theme.surfaceVariant, 0.3)
|
||||
border.color: isSelected ? Theme.primary : Theme.outlineMedium
|
||||
border.width: isSelected ? 2 : Theme.layerOutlineWidth
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Image {
|
||||
width: 28
|
||||
height: 28
|
||||
source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
|
||||
sourceSize.width: 28
|
||||
sourceSize.height: 28
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onStatusChanged: {
|
||||
if (status === Image.Error)
|
||||
source = "image://icon/application-x-executable";
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
width: parent.width - 28 - Theme.spacingM * 3 - 24
|
||||
|
||||
StyledText {
|
||||
text: modelData.name || modelData.id || ""
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.comment || modelData.genericName || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outline
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "add"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: appArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const appId = modelData.id || modelData.execString || "";
|
||||
root.appSelected(appId);
|
||||
root.hide();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FloatingWindowControls {
|
||||
id: windowControls
|
||||
targetWindow: root
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilteredApps() {
|
||||
const allApps = root.appsModel || [];
|
||||
var filtered = [];
|
||||
if (!searchQuery || searchQuery.length === 0) {
|
||||
filtered = allApps.slice();
|
||||
} else {
|
||||
var query = searchQuery.toLowerCase();
|
||||
for (var i = 0; i < allApps.length; i++) {
|
||||
var app = allApps[i];
|
||||
var name = (app.name || "").toLowerCase();
|
||||
var id = (app.id || "").toLowerCase();
|
||||
var comment = (app.comment || app.genericName || "").toLowerCase();
|
||||
if (name.indexOf(query) !== -1 || id.indexOf(query) !== -1 || comment.indexOf(query) !== -1)
|
||||
filtered.push(app);
|
||||
}
|
||||
}
|
||||
filteredApps = filtered;
|
||||
selectedIndex = -1;
|
||||
keyboardNavigationActive = false;
|
||||
}
|
||||
|
||||
function selectNext() {
|
||||
if (filteredApps.length === 0) return;
|
||||
keyboardNavigationActive = true;
|
||||
selectedIndex = Math.min(selectedIndex + 1, filteredApps.length - 1);
|
||||
}
|
||||
|
||||
function selectPrevious() {
|
||||
if (filteredApps.length === 0) return;
|
||||
keyboardNavigationActive = true;
|
||||
selectedIndex = Math.max(selectedIndex - 1, -1);
|
||||
if (selectedIndex === -1) keyboardNavigationActive = false;
|
||||
}
|
||||
|
||||
function selectApp() {
|
||||
if (selectedIndex < 0 || selectedIndex >= filteredApps.length) return;
|
||||
selectAppByIndex(selectedIndex);
|
||||
}
|
||||
|
||||
function selectAppByIndex(idx) {
|
||||
const app = filteredApps[idx];
|
||||
if (!app) return;
|
||||
root.appSelected(app.id || app.execString || "");
|
||||
hide();
|
||||
}
|
||||
|
||||
function show() {
|
||||
updateFilteredApps();
|
||||
visible = true;
|
||||
Qt.callLater(() => searchField.forceActiveFocus());
|
||||
}
|
||||
|
||||
function hide() {
|
||||
visible = false;
|
||||
searchQuery = "";
|
||||
filteredApps = [];
|
||||
selectedIndex = -1;
|
||||
keyboardNavigationActive = false;
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
searchQuery = "";
|
||||
filteredApps = [];
|
||||
selectedIndex = -1;
|
||||
keyboardNavigationActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user