mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-29 14:37:47 -04:00
* fix(frame): make connected mode honor the settings it overrides Several frame-mode behaviors contradicted what the settings UI shows: - Frame -> Blur gated blur for every shell surface in connected mode, including the Settings window, polkit, context menus, OSDs and toasts. WindowBlur now defaults to Background blur; only surfaces painted with the frame color (connected popouts, slideouts, Control Center) follow Frame -> Blur, and frame-owned surfaces are blurred by the frame as before. - The notepad slideout painted its own fill at the notepad transparency. On screens where the frame owns connected chrome it now claims a frame surface (new connected_arc slot 5), so the frame paints it flush with the cutout, with fillets, and follows the slide. - Notification popups painted a card surface on top of the frame body, stacking fills. That layer is transparent when the frame paints it. - Connected mode stripped shadow, border and corner styling from every bar, including bars on displays without a frame, and hid the rows. Styling is now stripped and restored per bar based on whether it covers a frame display, re-checked on screen, frame display and display-name-mode changes. - Auto popup gaps and Gap size were hidden in separate mode although they still apply there; they now hide only in connected mode. - The launcher style picker was hidden in connected mode while Island still overrode the connected launcher. Connected mode now always uses the connected launcher; the picker shows Full, disabled, with a note linking to the Bar layout picker. - The frame shadow ignored Shadows -> Apply to -> Bars. * fix(frame): address review feedback on connected mode - Skip bar style reconciliation while the screen list is empty, so a hotplug doesn't restore and re-strip every bar. - Replace the 16 copies of the connector blur anchor and cutout in FrameWindow with a ConnectorBlurRegion inline component. - Let connected mode use the island launcher again now that the island can sit in any bar. The picker offers Full and Island there and hides Spotlight, which opens the full launcher in that mode anyway; the note and the SettingsControlledBy bar target are dropped. * frame: address review findings on connected mode --------- Co-authored-by: purian23 <[email protected]>
854 lines
37 KiB
QML
854 lines
37 KiB
QML
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property var parentModal: null
|
|
readonly property string defaultLauncherAction: "spawn dms ipc call spotlight toggle"
|
|
readonly property string spotlightBarAction: "spawn dms ipc call spotlight-bar toggle"
|
|
readonly property int keybindDataVersion: KeybindsService._dataVersion
|
|
readonly property bool keybindsAvailable: KeybindsService.available
|
|
readonly property string defaultLauncherKeybindSearch: "spotlight toggle"
|
|
readonly property string spotlightBarKeybindSearch: "spotlight-bar"
|
|
readonly property var builtInPluginIds: ["dms_settings", "dms_notepad", "dms_sysmon", "dms_colorpicker", "dms_settings_search", "dms_clipboard_search", "dms_power", "dms_vpn", "dms_qr_generator"]
|
|
|
|
function openKeybindsSearch(query) {
|
|
if (!root.parentModal)
|
|
return;
|
|
if (typeof root.parentModal.showKeybindsSearch === "function") {
|
|
root.parentModal.showKeybindsSearch(query);
|
|
} else {
|
|
root.parentModal.navigateTo("keybinds");
|
|
}
|
|
}
|
|
|
|
function keysLabel(actionId) {
|
|
void (keybindDataVersion);
|
|
if (!keybindsAvailable)
|
|
return I18n.tr("Manual config");
|
|
const keys = KeybindsService.keysForAction(actionId);
|
|
if (!keys || keys.length === 0)
|
|
return I18n.tr("Not bound");
|
|
return keys.join(", ");
|
|
}
|
|
|
|
function lastLaunchedText(lastUsed) {
|
|
if (!lastUsed)
|
|
return I18n.tr("Never used");
|
|
const date = new Date(lastUsed);
|
|
const diffMs = Date.now() - date.getTime();
|
|
const diffMins = Math.floor(diffMs / (1000 * 60));
|
|
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
if (diffMins < 1)
|
|
return I18n.tr("Last launched just now");
|
|
if (diffMins < 60)
|
|
return diffMins === 1 ? I18n.tr("Last launched %1 minute ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffMins) : I18n.tr("Last launched %1 minutes ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffMins);
|
|
if (diffHours < 24)
|
|
return diffHours === 1 ? I18n.tr("Last launched %1 hour ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffHours) : I18n.tr("Last launched %1 hours ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffHours);
|
|
if (diffDays < 7)
|
|
return diffDays === 1 ? I18n.tr("Last launched %1 day ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffDays) : I18n.tr("Last launched %1 days ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffDays);
|
|
return I18n.tr("Last launched %1", "app usage list in launcher settings, %1 is a localized date").arg(date.toLocaleDateString());
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
KeybindsService.addRef();
|
|
if (KeybindsService.available)
|
|
KeybindsService.loadBinds(false);
|
|
}
|
|
|
|
Component.onDestruction: KeybindsService.removeRef()
|
|
|
|
SettingsPage {
|
|
id: mainColumn
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "search"
|
|
title: I18n.tr("Default action")
|
|
settingKey: "launcherStyle"
|
|
|
|
SettingsButtonGroupRow {
|
|
readonly property bool islandOffered: SettingsData.dankIslandEnabled || SettingsData.launcherStyle === "island"
|
|
readonly property bool spotlightOffered: !SettingsData.connectedFrameModeActive
|
|
|
|
settingKey: "launcherStyleSelector"
|
|
tags: ["launcher", "style", "default", "spotlight", "full", "minimal", "island", "dankisland"]
|
|
resetKeys: ["launcherStyle"]
|
|
text: I18n.tr("Opens", "verb, row label, which launcher style the shortcut opens")
|
|
model: [I18n.tr("Full", "adjective, full size launcher style option")].concat(spotlightOffered ? [I18n.tr("Spotlight", "launcher style option, small centered search bar")] : []).concat(islandOffered ? [I18n.tr("Island")] : [])
|
|
values: ["full"].concat(spotlightOffered ? ["spotlight"] : []).concat(islandOffered ? ["island"] : [])
|
|
value: SettingsData.launcherStyle
|
|
fallbackValue: "full"
|
|
onValueSelected: value => SettingsData.set("launcherStyle", value)
|
|
}
|
|
|
|
SettingsRow {
|
|
iconName: "keyboard"
|
|
title: I18n.tr("Shortcut", "noun, keyboard shortcut row in launcher settings")
|
|
subtitle: root.keybindsAvailable ? "" : I18n.tr("Bind the %1 IPC action in your compositor config.", "launcher shortcut hint, %1 is the ipc target name").arg("spotlight")
|
|
trailingBadge: root.keysLabel(root.defaultLauncherAction)
|
|
trailingBadgeColor: Theme.primary
|
|
showChevron: root.keybindsAvailable
|
|
clickable: root.keybindsAvailable
|
|
onClicked: root.openKeybindsSearch(root.defaultLauncherKeybindSearch)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "overview_key"
|
|
title: I18n.tr("Overview")
|
|
settingKey: "launcherNiriOverview"
|
|
tags: ["launcher", "niri", "overview", "overlay"]
|
|
visible: CompositorService.isNiri
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "niriOverviewOverlayEnabled"
|
|
tags: ["launcher", "niri", "overview", "overlay", "enable"]
|
|
text: I18n.tr("Overlay")
|
|
description: I18n.tr("Typing in the overview opens the launcher")
|
|
checked: SettingsData.niriOverviewOverlayEnabled
|
|
onToggled: checked => SettingsData.set("niriOverviewOverlayEnabled", checked)
|
|
}
|
|
|
|
SettingsButtonGroupRow {
|
|
visible: SettingsData.niriOverviewOverlayEnabled
|
|
settingKey: "niriOverviewLauncherStyle"
|
|
tags: ["launcher", "niri", "overview", "overlay", "style", "spotlight", "full"]
|
|
text: I18n.tr("Opens")
|
|
model: [I18n.tr("Full"), I18n.tr("Spotlight")]
|
|
currentIndex: SettingsData.niriOverviewLauncherStyle === "spotlight" ? 1 : 0
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected)
|
|
return;
|
|
SettingsData.set("niriOverviewLauncherStyle", index === 1 ? "spotlight" : "full");
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "tune"
|
|
title: I18n.tr("Appearance")
|
|
settingKey: "dankLauncherV2Appearance"
|
|
tags: ["launcher", "size", "grid", "columns", "footer", "badges", "darken"]
|
|
|
|
SettingsButtonGroupRow {
|
|
readonly property var sizes: ["micro", "compact", "medium", "large"]
|
|
|
|
settingKey: "dankLauncherV2Size"
|
|
tags: ["launcher", "size", "micro", "compact", "medium", "large"]
|
|
text: I18n.tr("Size")
|
|
model: ["1", "2", "3", "4"]
|
|
currentIndex: {
|
|
const index = sizes.indexOf(SettingsData.dankLauncherV2Size);
|
|
return index >= 0 ? index : 2;
|
|
}
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected)
|
|
return;
|
|
SettingsData.set("dankLauncherV2Size", sizes[index]);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
settingKey: "appLauncherGridColumns"
|
|
tags: ["launcher", "grid", "columns", "layout"]
|
|
text: I18n.tr("Grid columns")
|
|
unit: ""
|
|
minimum: 2
|
|
maximum: 8
|
|
value: SettingsData.appLauncherGridColumns
|
|
onSliderValueChanged: newValue => SettingsData.set("appLauncherGridColumns", newValue)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "dankLauncherV2ShowFooter"
|
|
tags: ["launcher", "footer", "hints", "shortcuts", "modes", "filters"]
|
|
text: I18n.tr("Show footer")
|
|
checked: SettingsData.dankLauncherV2ShowFooter
|
|
enabled: SettingsData.dankLauncherV2Size !== "micro"
|
|
onToggled: checked => SettingsData.set("dankLauncherV2ShowFooter", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "dankLauncherV2ShowSourceBadges"
|
|
tags: ["launcher", "appearance", "badge", "source", "flatpak", "snap", "appimage", "nix"]
|
|
text: I18n.tr("Show package source badges")
|
|
checked: SettingsData.dankLauncherV2ShowSourceBadges
|
|
onToggled: checked => SettingsData.set("dankLauncherV2ShowSourceBadges", checked)
|
|
}
|
|
|
|
SettingsControlledBy {
|
|
visible: SettingsData.frameEnabled
|
|
parentModal: root.parentModal
|
|
section: "frameBorder"
|
|
settingLabel: I18n.tr("Darken modal background")
|
|
reason: I18n.tr("Disabled by Frame Mode")
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
enabled: !SettingsData.frameEnabled
|
|
settingKey: "modalDarkenBackground"
|
|
tags: ["modal", "darken", "background", "overlay", "launcher"]
|
|
text: I18n.tr("Darken modal background")
|
|
checked: SettingsData.modalDarkenBackground
|
|
onToggled: checked => SettingsData.set("modalDarkenBackground", checked)
|
|
}
|
|
}
|
|
|
|
SettingsToggleCard {
|
|
width: parent.width
|
|
iconName: "border_style"
|
|
title: I18n.tr("Border")
|
|
settingKey: "dankLauncherV2BorderEnabled"
|
|
tags: ["launcher", "border", "outline"]
|
|
checked: SettingsData.dankLauncherV2BorderEnabled
|
|
onToggled: checked => SettingsData.set("dankLauncherV2BorderEnabled", checked)
|
|
|
|
SettingsSliderRow {
|
|
settingKey: "dankLauncherV2BorderThickness"
|
|
tags: ["launcher", "border", "thickness"]
|
|
text: I18n.tr("Thickness")
|
|
minimum: 1
|
|
maximum: 6
|
|
value: SettingsData.dankLauncherV2BorderThickness
|
|
unit: "px"
|
|
onSliderValueChanged: newValue => SettingsData.set("dankLauncherV2BorderThickness", newValue)
|
|
}
|
|
|
|
SettingsButtonGroupRow {
|
|
readonly property var colors: ["primary", "secondary", "outline", "surfaceText"]
|
|
|
|
settingKey: "dankLauncherV2BorderColor"
|
|
tags: ["launcher", "border", "color"]
|
|
text: I18n.tr("Color")
|
|
model: [I18n.tr("Primary", "primary color"), I18n.tr("Secondary", "secondary color"), I18n.tr("Outline", "outline color"), I18n.tr("Text", "text color")]
|
|
currentIndex: Math.max(0, colors.indexOf(SettingsData.dankLauncherV2BorderColor))
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected)
|
|
return;
|
|
SettingsData.set("dankLauncherV2BorderColor", colors[index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "search"
|
|
title: I18n.tr("Search", "launcher settings section title, noun")
|
|
settingKey: "searchOptions"
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "searchAppActions"
|
|
tags: ["launcher", "search", "actions", "shortcuts", "desktop"]
|
|
text: I18n.tr("App actions")
|
|
description: I18n.tr("Search also matches actions apps provide, like New Window", "launcher app actions search toggle description")
|
|
checked: SessionData.searchAppActions
|
|
onToggled: checked => SessionData.setSearchAppActions(checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "rememberLastMode"
|
|
tags: ["launcher", "remember", "last", "mode", "tab"]
|
|
text: I18n.tr("Remember last mode")
|
|
checked: SettingsData.rememberLastMode
|
|
onToggled: checked => SettingsData.set("rememberLastMode", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "launcherHistoryEnabled"
|
|
tags: ["launcher", "history", "privacy", "usage", "recent", "persist", "save"]
|
|
text: I18n.tr("History")
|
|
description: I18n.tr("Record app usage and past queries. Turning this off clears what is stored.", "launcher history toggle description")
|
|
checked: SettingsData.launcherHistoryEnabled
|
|
onToggled: checked => SettingsData.set("launcherHistoryEnabled", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "rememberLastQuery"
|
|
tags: ["launcher", "remember", "last", "search", "query"]
|
|
text: I18n.tr("Remember last query")
|
|
checked: SettingsData.rememberLastQuery
|
|
onToggled: checked => SettingsData.set("rememberLastQuery", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "sortAppsAlphabetically"
|
|
tags: ["launcher", "sort", "alphabetically", "apps", "order", "usage", "frequency"]
|
|
text: I18n.tr("Sort alphabetically")
|
|
checked: SettingsData.sortAppsAlphabetically
|
|
onToggled: checked => SettingsData.set("sortAppsAlphabetically", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "dankLauncherV2IncludeFilesInAll"
|
|
tags: ["launcher", "files", "dsearch", "all", "results", "indexed"]
|
|
text: I18n.tr("Include files in All tab")
|
|
checked: SettingsData.dankLauncherV2IncludeFilesInAll
|
|
onToggled: checked => SettingsData.set("dankLauncherV2IncludeFilesInAll", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "dankLauncherV2IncludeFoldersInAll"
|
|
tags: ["launcher", "folders", "dirs", "dsearch", "all", "results", "indexed"]
|
|
text: I18n.tr("Include folders in All tab")
|
|
checked: SettingsData.dankLauncherV2IncludeFoldersInAll
|
|
onToggled: checked => SettingsData.set("dankLauncherV2IncludeFoldersInAll", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
id: hiddenAppsCard
|
|
width: parent.width
|
|
iconName: "visibility_off"
|
|
title: I18n.tr("Hidden apps")
|
|
settingKey: "hiddenApps"
|
|
|
|
property var hiddenAppsModel: {
|
|
SessionData.hiddenApps;
|
|
const apps = [];
|
|
const allApps = AppSearchService.applications || [];
|
|
for (const hiddenId of SessionData.hiddenApps) {
|
|
const app = allApps.find(a => (a.id || a.execString || a.exec) === hiddenId);
|
|
if (app) {
|
|
apps.push({
|
|
id: hiddenId,
|
|
name: app.name || hiddenId,
|
|
icon: app.icon || "",
|
|
comment: app.comment || ""
|
|
});
|
|
} else {
|
|
apps.push({
|
|
id: hiddenId,
|
|
name: hiddenId,
|
|
icon: "",
|
|
comment: ""
|
|
});
|
|
}
|
|
}
|
|
return apps.sort((a, b) => a.name.localeCompare(b.name));
|
|
}
|
|
|
|
SettingsRow {
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("Right-click an app in the launcher and choose 'Hide App'")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: hiddenAppsCard.hiddenAppsModel
|
|
|
|
SettingsRow {
|
|
id: hiddenAppRow
|
|
|
|
required property var modelData
|
|
|
|
title: modelData.name
|
|
subtitle: modelData.comment || modelData.id
|
|
leading: AppIconRenderer {
|
|
width: Theme.iconSize
|
|
height: Theme.iconSize
|
|
iconValue: hiddenAppRow.modelData.icon || "application-x-executable"
|
|
iconSize: Theme.iconSize
|
|
fallbackText: (hiddenAppRow.modelData.name || "?").charAt(0).toUpperCase()
|
|
}
|
|
|
|
DActionButton {
|
|
iconName: "visibility"
|
|
Accessible.name: I18n.tr("Show")
|
|
iconColor: Theme.primary
|
|
onClicked: SessionData.showApp(hiddenAppRow.modelData.id)
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: hiddenAppsCard.hiddenAppsModel.length === 0
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("No hidden apps.")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceVariantText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
id: appOverridesCard
|
|
width: parent.width
|
|
iconName: "edit"
|
|
title: I18n.tr("App customizations")
|
|
settingKey: "appOverrides"
|
|
|
|
property var overridesModel: {
|
|
SessionData.appOverrides;
|
|
const items = [];
|
|
const allApps = AppSearchService.applications || [];
|
|
for (const appId in SessionData.appOverrides) {
|
|
const override = SessionData.appOverrides[appId];
|
|
const app = allApps.find(a => (a.id || a.execString || a.exec) === appId);
|
|
items.push({
|
|
id: appId,
|
|
name: override.name || app?.name || appId,
|
|
originalName: app?.name || appId,
|
|
icon: override.icon || app?.icon || "",
|
|
hasOverride: true
|
|
});
|
|
}
|
|
return items.sort((a, b) => a.name.localeCompare(b.name));
|
|
}
|
|
|
|
SettingsRow {
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("Right-click an app in the launcher and choose 'Edit App'")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: appOverridesCard.overridesModel
|
|
|
|
SettingsRow {
|
|
id: overrideRow
|
|
|
|
required property var modelData
|
|
|
|
title: modelData.name
|
|
subtitle: modelData.originalName !== modelData.name ? modelData.originalName : modelData.id
|
|
leading: AppIconRenderer {
|
|
width: Theme.iconSize
|
|
height: Theme.iconSize
|
|
iconValue: overrideRow.modelData.icon || "application-x-executable"
|
|
iconSize: Theme.iconSize
|
|
fallbackText: (overrideRow.modelData.name || "?").charAt(0).toUpperCase()
|
|
}
|
|
|
|
DActionButton {
|
|
iconName: "delete"
|
|
tooltipText: I18n.tr("Reset to default")
|
|
iconColor: Theme.error
|
|
onClicked: SessionData.clearAppOverride(overrideRow.modelData.id)
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: appOverridesCard.overridesModel.length === 0
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("No app customizations.")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceVariantText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "apps"
|
|
title: I18n.tr("Built-in apps", "launcher settings card, DMS tools listed with the installed apps")
|
|
settingKey: "launcherBuiltInApps"
|
|
tags: ["launcher", "apps", "builtin", "settings", "notepad", "system monitor", "color picker"]
|
|
|
|
Repeater {
|
|
model: root.builtInPluginIds.filter(id => AppSearchService.builtInPlugins[id]?.isLauncher !== true)
|
|
|
|
SettingsToggleRow {
|
|
required property string modelData
|
|
readonly property var plugin: AppSearchService.builtInPlugins[modelData]
|
|
|
|
iconName: plugin?.cornerIcon ?? "extension"
|
|
text: plugin?.name ?? modelData
|
|
checked: SettingsData.getBuiltInPluginSetting(modelData, "enabled", true)
|
|
onToggled: checked => SettingsData.setBuiltInPluginSetting(modelData, "enabled", checked)
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
id: pluginVisibilityCard
|
|
width: parent.width
|
|
iconName: "filter_list"
|
|
title: I18n.tr("Plugin visibility")
|
|
settingKey: "pluginVisibility"
|
|
|
|
property var allLauncherPlugins: {
|
|
SettingsData.launcherPluginOrder;
|
|
SettingsData.dankLauncherV2IncludeFilesInAll;
|
|
SettingsData.dankLauncherV2IncludeFoldersInAll;
|
|
var plugins = [];
|
|
var builtIn = AppSearchService.getBuiltInLauncherPlugins() || {};
|
|
for (var pluginId in builtIn) {
|
|
var plugin = builtIn[pluginId];
|
|
plugins.push({
|
|
id: pluginId,
|
|
name: plugin.name || pluginId,
|
|
icon: plugin.cornerIcon || "extension",
|
|
iconType: "material",
|
|
isBuiltIn: true,
|
|
isVirtual: false,
|
|
trigger: AppSearchService.getBuiltInPluginTrigger(pluginId) || ""
|
|
});
|
|
}
|
|
var thirdParty = PluginService.getLauncherPlugins() || {};
|
|
for (var pluginId in thirdParty) {
|
|
var plugin = thirdParty[pluginId];
|
|
var rawIcon = plugin.icon || "extension";
|
|
plugins.push({
|
|
id: pluginId,
|
|
name: plugin.name || pluginId,
|
|
icon: rawIcon.startsWith("material:") ? rawIcon.substring(9) : rawIcon.startsWith("unicode:") ? rawIcon.substring(8) : rawIcon,
|
|
iconType: rawIcon.startsWith("unicode:") ? "unicode" : "material",
|
|
isBuiltIn: false,
|
|
isVirtual: false,
|
|
trigger: PluginService.getPluginTrigger(pluginId) || ""
|
|
});
|
|
}
|
|
if (SettingsData.dankLauncherV2IncludeFilesInAll) {
|
|
plugins.push({
|
|
id: "__files",
|
|
name: I18n.tr("Files"),
|
|
icon: "insert_drive_file",
|
|
iconType: "material",
|
|
isBuiltIn: false,
|
|
isVirtual: true,
|
|
trigger: "/"
|
|
});
|
|
}
|
|
if (SettingsData.dankLauncherV2IncludeFoldersInAll) {
|
|
plugins.push({
|
|
id: "__folders",
|
|
name: I18n.tr("Folders"),
|
|
icon: "folder",
|
|
iconType: "material",
|
|
isBuiltIn: false,
|
|
isVirtual: true,
|
|
trigger: "/"
|
|
});
|
|
}
|
|
return SettingsData.getOrderedLauncherPlugins(plugins);
|
|
}
|
|
|
|
SettingsRow {
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("Plugins shown in All mode without a trigger. Drag to reorder")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
}
|
|
|
|
SettingsReorderList {
|
|
id: pluginList
|
|
|
|
model: pluginVisibilityCard.allLauncherPlugins
|
|
onReordered: indices => SettingsData.setLauncherPluginOrder(indices.map(i => pluginVisibilityCard.allLauncherPlugins[i].id))
|
|
|
|
delegate: SettingsReorderRow {
|
|
id: pluginRow
|
|
|
|
required property var modelData
|
|
|
|
reorderList: pluginList
|
|
title: modelData.name
|
|
subtitle: modelData.trigger ? I18n.tr("Trigger: %1", "launcher plugin subtitle, %1 is the trigger prefix text").arg(modelData.trigger) : I18n.tr("No trigger")
|
|
iconName: modelData.iconType !== "unicode" ? modelData.icon : ""
|
|
textIcon: modelData.iconType === "unicode" ? modelData.icon : ""
|
|
|
|
DBadge {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: pluginRow.modelData.isBuiltIn
|
|
text: I18n.tr("Built-in", "badge on launcher plugins that ship with DMS")
|
|
color: Theme.primaryHover
|
|
textColor: Theme.primary
|
|
}
|
|
|
|
DToggle {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
hideText: true
|
|
checked: {
|
|
switch (pluginRow.modelData.id) {
|
|
case "__files":
|
|
return SettingsData.dankLauncherV2IncludeFilesInAll;
|
|
case "__folders":
|
|
return SettingsData.dankLauncherV2IncludeFoldersInAll;
|
|
default:
|
|
return SettingsData.getPluginAllowWithoutTrigger(pluginRow.modelData.id);
|
|
}
|
|
}
|
|
onToggled: isChecked => {
|
|
switch (pluginRow.modelData.id) {
|
|
case "__files":
|
|
SettingsData.set("dankLauncherV2IncludeFilesInAll", isChecked);
|
|
return;
|
|
case "__folders":
|
|
SettingsData.set("dankLauncherV2IncludeFoldersInAll", isChecked);
|
|
return;
|
|
default:
|
|
SettingsData.setPluginAllowWithoutTrigger(pluginRow.modelData.id, isChecked);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: pluginVisibilityCard.allLauncherPlugins.length === 0
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("No launcher plugins installed.")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceVariantText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "extension"
|
|
title: I18n.tr("Built-in plugins")
|
|
settingKey: "launcherBuiltInPlugins"
|
|
tags: ["launcher", "plugins", "trigger", "builtin", "power", "vpn", "qr", "clipboard", "search"]
|
|
|
|
Repeater {
|
|
model: root.builtInPluginIds.filter(id => AppSearchService.builtInPlugins[id]?.isLauncher === true)
|
|
|
|
SettingsRow {
|
|
id: builtInRow
|
|
|
|
required property string modelData
|
|
readonly property var plugin: AppSearchService.builtInPlugins[modelData]
|
|
|
|
iconName: plugin?.cornerIcon ?? "extension"
|
|
title: plugin?.name ?? modelData
|
|
|
|
DTextField {
|
|
outlined: true
|
|
leftIconName: "keyboard"
|
|
labelText: I18n.tr("Trigger", "noun, launcher plugin trigger prefix text field label")
|
|
width: Theme.fontSizeMedium * 6 + Theme.iconButtonSize
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
onTextEdited: SettingsData.setBuiltInPluginSetting(builtInRow.modelData, "trigger", text)
|
|
Component.onCompleted: text = SettingsData.getBuiltInPluginSetting(builtInRow.modelData, "trigger", builtInRow.plugin?.defaultTrigger ?? "")
|
|
}
|
|
|
|
DToggle {
|
|
hideText: true
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
checked: SettingsData.getBuiltInPluginSetting(builtInRow.modelData, "enabled", true)
|
|
onToggled: checked => SettingsData.setBuiltInPluginSetting(builtInRow.modelData, "enabled", checked)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "horizontal_split"
|
|
title: I18n.tr("Spotlight bar")
|
|
settingKey: "spotlightBar"
|
|
tags: ["launcher", "spotlight", "bar", "shortcut", "chips"]
|
|
|
|
SettingsRow {
|
|
iconName: "keyboard"
|
|
title: I18n.tr("Shortcut")
|
|
subtitle: root.keybindsAvailable ? "" : I18n.tr("Bind the %1 IPC action in your compositor config.").arg("spotlight-bar")
|
|
trailingBadge: root.keysLabel(root.spotlightBarAction)
|
|
trailingBadgeColor: Theme.primary
|
|
showChevron: root.keybindsAvailable
|
|
clickable: root.keybindsAvailable
|
|
onClicked: root.openKeybindsSearch(root.spotlightBarKeybindSearch)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "spotlightBarShowModeChips"
|
|
tags: ["launcher", "spotlight", "bar", "chips", "tabs", "modes", "filters"]
|
|
text: I18n.tr("Show mode chips")
|
|
checked: SettingsData.spotlightBarShowModeChips
|
|
onToggled: checked => SettingsData.set("spotlightBarShowModeChips", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "rocket_launch"
|
|
title: I18n.tr("Launching", "launcher settings card about how apps are launched")
|
|
settingKey: "launcherLaunching"
|
|
tags: ["launcher", "prefix", "terminal", "uwsm"]
|
|
|
|
SettingsTextFieldRow {
|
|
settingKey: "launchPrefix"
|
|
tags: ["launcher", "prefix", "uwsm", "command", "launch"]
|
|
text: I18n.tr("Launch prefix")
|
|
description: I18n.tr("Added in front of every app launch command", "launcher launch prefix field description")
|
|
leftIconName: "terminal"
|
|
placeholderText: I18n.tr("Enter launch prefix (e.g., 'uwsm-app')")
|
|
value: SettingsData.launchPrefix
|
|
onValueEdited: value => SettingsData.set("launchPrefix", value)
|
|
}
|
|
|
|
TerminalPickerRow {}
|
|
}
|
|
|
|
SettingsCard {
|
|
id: recentAppsCard
|
|
width: parent.width
|
|
iconName: "history"
|
|
title: I18n.tr("Recently used apps")
|
|
settingKey: "recentApps"
|
|
visible: SettingsData.launcherHistoryEnabled
|
|
collapsible: true
|
|
expanded: false
|
|
|
|
property var rankedAppsModel: {
|
|
var ranking = AppUsageHistoryData.appUsageRanking;
|
|
if (!ranking)
|
|
return [];
|
|
var apps = [];
|
|
for (var appId in ranking) {
|
|
var appData = ranking[appId];
|
|
apps.push({
|
|
"id": appId,
|
|
"name": appData.name,
|
|
"exec": appData.exec,
|
|
"icon": appData.icon,
|
|
"comment": appData.comment,
|
|
"usageCount": appData.usageCount,
|
|
"lastUsed": appData.lastUsed
|
|
});
|
|
}
|
|
apps.sort(function (a, b) {
|
|
if (a.usageCount !== b.usageCount)
|
|
return b.usageCount - a.usageCount;
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
return apps.slice(0, 20);
|
|
}
|
|
|
|
SettingsRow {
|
|
body: Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
StyledText {
|
|
width: parent.width - clearAllButton.width - Theme.spacingM
|
|
text: I18n.tr("Apps are ordered by usage frequency, then last used, then alphabetically.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
DActionButton {
|
|
id: clearAllButton
|
|
iconName: "delete_sweep"
|
|
tooltipText: I18n.tr("Clear All")
|
|
iconSize: Theme.iconSizeMedium
|
|
iconColor: Theme.error
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
onClicked: AppUsageHistoryData.clear()
|
|
}
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: recentAppsCard.rankedAppsModel
|
|
|
|
SettingsRow {
|
|
id: rankedAppRow
|
|
|
|
required property var modelData
|
|
required property int index
|
|
|
|
title: modelData.name || I18n.tr("Unknown App")
|
|
subtitle: root.lastLaunchedText(modelData.lastUsed)
|
|
leading: [
|
|
StyledText {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: Theme.iconSize
|
|
text: (rankedAppRow.index + 1).toString()
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.primary
|
|
},
|
|
AppIconRenderer {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: Theme.iconSize
|
|
height: Theme.iconSize
|
|
iconValue: rankedAppRow.modelData.icon || "application-x-executable"
|
|
iconSize: Theme.iconSize
|
|
fallbackText: (rankedAppRow.modelData.name || "?").charAt(0).toUpperCase()
|
|
}
|
|
]
|
|
|
|
DActionButton {
|
|
iconName: "close"
|
|
Accessible.name: I18n.tr("Remove")
|
|
iconColor: Theme.error
|
|
onClicked: {
|
|
const currentRanking = Object.assign({}, AppUsageHistoryData.appUsageRanking || {});
|
|
delete currentRanking[rankedAppRow.modelData.id];
|
|
AppUsageHistoryData.appUsageRanking = currentRanking;
|
|
AppUsageHistoryData.saveSettings();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsRow {
|
|
visible: recentAppsCard.rankedAppsModel.length === 0
|
|
body: StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("No apps have been launched yet.")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceVariantText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
title: I18n.tr("Advanced")
|
|
settingKey: "launcherAdvanced"
|
|
tags: ["launcher", "advanced", "overlay", "layer", "unload", "memory"]
|
|
collapsible: true
|
|
expanded: false
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "launcherUseOverlayLayer"
|
|
tags: ["launcher", "fullscreen", "overlay", "layer"]
|
|
text: I18n.tr("Use overlay layer")
|
|
checked: SettingsData.launcherUseOverlayLayer
|
|
onToggled: checked => SettingsData.set("launcherUseOverlayLayer", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "dankLauncherV2UnloadOnClose"
|
|
tags: ["launcher", "unload", "close", "memory", "vram"]
|
|
text: I18n.tr("Unload on close")
|
|
description: I18n.tr("Frees the launcher when closed and rebuilds it on open", "launcher unload on close toggle description")
|
|
checked: SettingsData.dankLauncherV2UnloadOnClose
|
|
onToggled: checked => SettingsData.set("dankLauncherV2UnloadOnClose", checked)
|
|
}
|
|
}
|
|
}
|
|
}
|