mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 14:05:38 -05:00
Recently used apps
This commit is contained in:
@@ -120,24 +120,52 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addRecentApp(app) {
|
function addRecentApp(app) {
|
||||||
|
if (!app) return
|
||||||
|
|
||||||
|
var execProp = app.execString || app.exec || ""
|
||||||
|
if (!execProp) return
|
||||||
|
|
||||||
var existingIndex = -1
|
var existingIndex = -1
|
||||||
for (var i = 0; i < recentlyUsedApps.length; i++) {
|
for (var i = 0; i < recentlyUsedApps.length; i++) {
|
||||||
if (recentlyUsedApps[i].exec === app.exec) {
|
if (recentlyUsedApps[i].exec === execProp) {
|
||||||
existingIndex = i
|
existingIndex = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0) {
|
||||||
recentlyUsedApps.splice(existingIndex, 1)
|
// App exists, increment usage count
|
||||||
|
recentlyUsedApps[existingIndex].usageCount = (recentlyUsedApps[existingIndex].usageCount || 1) + 1
|
||||||
|
recentlyUsedApps[existingIndex].lastUsed = Date.now()
|
||||||
|
} else {
|
||||||
|
// New app, create entry
|
||||||
|
var appData = {
|
||||||
|
name: app.name || "",
|
||||||
|
exec: execProp,
|
||||||
|
icon: app.icon || "application-x-executable",
|
||||||
|
comment: app.comment || "",
|
||||||
|
usageCount: 1,
|
||||||
|
lastUsed: Date.now()
|
||||||
|
}
|
||||||
|
recentlyUsedApps.push(appData)
|
||||||
}
|
}
|
||||||
|
|
||||||
recentlyUsedApps.unshift(app)
|
// Sort by usage count (descending), then alphabetically by name
|
||||||
|
var sortedApps = recentlyUsedApps.sort(function(a, b) {
|
||||||
|
if (a.usageCount !== b.usageCount) {
|
||||||
|
return b.usageCount - a.usageCount // Higher usage count first
|
||||||
|
}
|
||||||
|
return a.name.localeCompare(b.name) // Alphabetical tiebreaker
|
||||||
|
})
|
||||||
|
|
||||||
if (recentlyUsedApps.length > 10) {
|
// Limit to 5 apps
|
||||||
recentlyUsedApps = recentlyUsedApps.slice(0, 10)
|
if (sortedApps.length > 5) {
|
||||||
|
sortedApps = sortedApps.slice(0, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reassign to trigger property change signal
|
||||||
|
recentlyUsedApps = sortedApps
|
||||||
|
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,20 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveRecentApps() {
|
function saveRecentApps() {
|
||||||
recentAppsFileView.text = JSON.stringify(recentApps, null, 2)
|
var jsonData = JSON.stringify(recentApps, null, 2)
|
||||||
|
var process = Qt.createQmlObject('
|
||||||
|
import Quickshell.Io
|
||||||
|
Process {
|
||||||
|
command: ["sh", "-c", "echo \'' + jsonData.replace(/'/g, "'\"'\"'") + '\' > \'' + root.recentAppsFile + '\'"]
|
||||||
|
running: true
|
||||||
|
onExited: {
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
console.warn("Failed to save recent apps:", exitCode)
|
||||||
|
}
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
', root)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRecentApp(app) {
|
function addRecentApp(app) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ PanelWindow {
|
|||||||
// App management
|
// App management
|
||||||
property var categories: AppSearchService.getAllCategories()
|
property var categories: AppSearchService.getAllCategories()
|
||||||
property string selectedCategory: "All"
|
property string selectedCategory: "All"
|
||||||
property var recentApps: []
|
property var recentApps: Prefs.getRecentApps()
|
||||||
property var pinnedApps: ["firefox", "code", "terminal", "file-manager"]
|
property var pinnedApps: ["firefox", "code", "terminal", "file-manager"]
|
||||||
property bool showCategories: false
|
property bool showCategories: false
|
||||||
property string viewMode: "list" // "list" or "grid"
|
property string viewMode: "list" // "list" or "grid"
|
||||||
@@ -84,6 +84,13 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Prefs
|
||||||
|
function onRecentlyUsedAppsChanged() {
|
||||||
|
recentApps = Prefs.getRecentApps()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateFilteredModel() {
|
function updateFilteredModel() {
|
||||||
filteredModel.clear()
|
filteredModel.clear()
|
||||||
|
|
||||||
@@ -383,6 +390,7 @@ PanelWindow {
|
|||||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && filteredModel.count) {
|
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && filteredModel.count) {
|
||||||
var firstApp = filteredModel.get(0)
|
var firstApp = filteredModel.get(0)
|
||||||
if (firstApp.desktopEntry) {
|
if (firstApp.desktopEntry) {
|
||||||
|
Prefs.addRecentApp(firstApp.desktopEntry)
|
||||||
AppSearchService.launchApp(firstApp.desktopEntry)
|
AppSearchService.launchApp(firstApp.desktopEntry)
|
||||||
} else {
|
} else {
|
||||||
launcher.launchApp(firstApp.exec)
|
launcher.launchApp(firstApp.exec)
|
||||||
@@ -398,6 +406,69 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recent apps section
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: recentApps.length > 0 && searchField.text.length === 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Recently Used"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Math.min(recentApps.length, 5)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 56
|
||||||
|
height: 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: recentAppMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||||
|
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 8
|
||||||
|
source: recentApps[index] ? Quickshell.iconPath(recentApps[index].icon, "") : ""
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: recentAppMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (recentApps[index]) {
|
||||||
|
var recentApp = recentApps[index]
|
||||||
|
// Find the desktop entry for this recent app
|
||||||
|
var foundApp = AppSearchService.getAppByExec(recentApp.exec)
|
||||||
|
if (foundApp) {
|
||||||
|
Prefs.addRecentApp(foundApp)
|
||||||
|
AppSearchService.launchApp(foundApp)
|
||||||
|
} else {
|
||||||
|
// Fallback to direct execution
|
||||||
|
var cleanExec = recentApp.exec.replace(/%[fFuU]/g, "").trim()
|
||||||
|
Quickshell.execDetached(["sh", "-c", cleanExec])
|
||||||
|
}
|
||||||
|
launcher.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Category filter and view mode controls
|
// Category filter and view mode controls
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -521,6 +592,7 @@ PanelWindow {
|
|||||||
// Calculate more precise remaining height
|
// Calculate more precise remaining height
|
||||||
let usedHeight = 40 + Theme.spacingL // Header
|
let usedHeight = 40 + Theme.spacingL // Header
|
||||||
usedHeight += 52 + Theme.spacingL // Search container
|
usedHeight += 52 + Theme.spacingL // Search container
|
||||||
|
usedHeight += (recentApps.length > 0 && searchField.text.length === 0 ? 56 + Theme.spacingS + Theme.spacingL : 0) // Recent apps when visible
|
||||||
usedHeight += (searchField.text.length === 0 ? 40 + Theme.spacingL : 0) // Category/controls when visible
|
usedHeight += (searchField.text.length === 0 ? 40 + Theme.spacingL : 0) // Category/controls when visible
|
||||||
return parent.height - usedHeight
|
return parent.height - usedHeight
|
||||||
}
|
}
|
||||||
@@ -784,6 +856,7 @@ PanelWindow {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.desktopEntry) {
|
if (model.desktopEntry) {
|
||||||
|
Prefs.addRecentApp(model.desktopEntry)
|
||||||
AppSearchService.launchApp(model.desktopEntry)
|
AppSearchService.launchApp(model.desktopEntry)
|
||||||
} else {
|
} else {
|
||||||
launcher.launchApp(model.exec)
|
launcher.launchApp(model.exec)
|
||||||
@@ -845,6 +918,7 @@ PanelWindow {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.desktopEntry) {
|
if (model.desktopEntry) {
|
||||||
|
Prefs.addRecentApp(model.desktopEntry)
|
||||||
AppSearchService.launchApp(model.desktopEntry)
|
AppSearchService.launchApp(model.desktopEntry)
|
||||||
} else {
|
} else {
|
||||||
launcher.launchApp(model.exec)
|
launcher.launchApp(model.exec)
|
||||||
@@ -870,6 +944,7 @@ PanelWindow {
|
|||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
launcher.isVisible = true
|
launcher.isVisible = true
|
||||||
|
recentApps = Prefs.getRecentApps() // Refresh recent apps
|
||||||
Qt.callLater(function() {
|
Qt.callLater(function() {
|
||||||
searchField.forceActiveFocus()
|
searchField.forceActiveFocus()
|
||||||
})
|
})
|
||||||
@@ -894,5 +969,6 @@ PanelWindow {
|
|||||||
categories = AppSearchService.getAllCategories()
|
categories = AppSearchService.getAllCategories()
|
||||||
updateFilteredModel()
|
updateFilteredModel()
|
||||||
}
|
}
|
||||||
|
recentApps = Prefs.getRecentApps() // Load recent apps on startup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadRecentApps() {
|
function loadRecentApps() {
|
||||||
recentApps = PreferencesService.getRecentApps()
|
recentApps = Prefs.getRecentApps()
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFilteredApps() {
|
function updateFilteredApps() {
|
||||||
@@ -154,7 +154,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function launchApp(app) {
|
function launchApp(app) {
|
||||||
PreferencesService.addRecentApp(app)
|
Prefs.addRecentApp(app)
|
||||||
if (app.desktopEntry) {
|
if (app.desktopEntry) {
|
||||||
AppSearchService.launchApp(app.desktopEntry)
|
AppSearchService.launchApp(app.desktopEntry)
|
||||||
} else {
|
} else {
|
||||||
@@ -221,6 +221,13 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Prefs
|
||||||
|
function onRecentlyUsedAppsChanged() {
|
||||||
|
recentApps = Prefs.getRecentApps()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dimmed overlay background
|
// Dimmed overlay background
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -236,13 +243,18 @@ PanelWindow {
|
|||||||
width: 600
|
width: 600
|
||||||
height: {
|
height: {
|
||||||
// Calculate dynamic height based on content
|
// Calculate dynamic height based on content
|
||||||
let baseHeight = Theme.spacingXL * 2 + Theme.spacingL * 3 // Margins and spacing
|
let baseHeight = Theme.spacingXL * 2 + Theme.spacingL * 4 // Margins and spacing
|
||||||
|
|
||||||
// Add category section height if visible
|
// Add category section height if visible
|
||||||
if (categories.length > 1 || filteredModel.count > 0) {
|
if (categories.length > 1 || filteredModel.count > 0) {
|
||||||
baseHeight += 36 * 2 + Theme.spacingS + Theme.spacingM // Categories (2 rows)
|
baseHeight += 36 * 2 + Theme.spacingS + Theme.spacingM // Categories (2 rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add recent apps section height if visible
|
||||||
|
if (recentApps.length > 0 && searchField.text.length === 0) {
|
||||||
|
baseHeight += 56 + Theme.spacingS + Theme.fontSizeMedium + Theme.spacingL // Recent apps
|
||||||
|
}
|
||||||
|
|
||||||
// Add search field height
|
// Add search field height
|
||||||
baseHeight += 56
|
baseHeight += 56
|
||||||
|
|
||||||
@@ -367,6 +379,73 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recent apps section
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: recentApps.length > 0 && searchField.text.length === 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Recently Used"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Math.min(recentApps.length, 5)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 56
|
||||||
|
height: 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: recentSpotlightMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||||
|
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 8
|
||||||
|
source: recentApps[index] ? Quickshell.iconPath(recentApps[index].icon, "") : ""
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: recentSpotlightMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (recentApps[index]) {
|
||||||
|
var recentApp = recentApps[index]
|
||||||
|
// Find the desktop entry for this recent app
|
||||||
|
var foundApp = AppSearchService.getAppByExec(recentApp.exec)
|
||||||
|
if (foundApp) {
|
||||||
|
launchApp({
|
||||||
|
name: foundApp.name,
|
||||||
|
exec: foundApp.execString,
|
||||||
|
icon: foundApp.icon,
|
||||||
|
comment: foundApp.comment,
|
||||||
|
desktopEntry: foundApp
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Fallback to direct execution
|
||||||
|
launchApp(recentApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Search field with view toggle buttons
|
// Search field with view toggle buttons
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -531,7 +610,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
width: parent.width
|
width: resultsList.width
|
||||||
height: 60
|
height: 60
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
||||||
@@ -730,5 +809,6 @@ PanelWindow {
|
|||||||
if (AppSearchService.ready) {
|
if (AppSearchService.ready) {
|
||||||
categories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
|
categories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
|
||||||
}
|
}
|
||||||
|
loadRecentApps() // Load recent apps on startup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user