Add desktop app service and VM dev session tooling

Introduces a new `core/applications` QML/C++ module (`Nebula.Applications`) to discover `.desktop` entries, expose filterable application models, and launch installed apps safely from the shell. The desktop launcher now uses real installed apps with themed icon loading and fallback glyphs, adds output-aware surface sizing via `OutputTracker`, and wires in image/icon providers. It also adds VMware-focused Sway/shell wrapper scripts, updates Sway config/session env defaults, and refreshes README docs to document the new development flow and Desktop 0.1 behavior.
This commit is contained in:
2026-08-26 17:19:30 +12:00
parent a7d204f241
commit b0c95b9e78
31 changed files with 1673 additions and 72 deletions
@@ -1,5 +1,6 @@
import QtQuick
import Nebula.UI
import Nebula.Applications
Item {
id: root
@@ -13,8 +14,9 @@ Item {
Accessible.role: Accessible.Dialog
Accessible.name: qsTr("Launcher")
readonly property bool usingInstalledApps: ApplicationService.count > 0
readonly property bool searching: searchField.text.trim().length > 0
readonly property var filteredApps: {
readonly property var mockMatches: {
const query = searchField.text.trim().toLowerCase()
const apps = MockApps.apps
if (!query)
@@ -24,6 +26,9 @@ Item {
return haystack.indexOf(query) !== -1
})
}
readonly property int resultCount: usingInstalledApps
? ApplicationService.filteredModel.count
: mockMatches.length
Keys.onEscapePressed: {
event.accepted = true
@@ -62,6 +67,7 @@ Item {
id: searchField
width: parent.width
placeholderText: qsTr("Search applications")
onTextChanged: ApplicationService.filter = text
}
Item {
@@ -87,7 +93,7 @@ Item {
Column {
width: parent.width
spacing: 8
visible: !root.searching && MockApps.recents.length > 0
visible: !root.searching && !root.usingInstalledApps && MockApps.recents.length > 0
Text {
text: qsTr("Recent")
@@ -133,14 +139,33 @@ Item {
Grid {
id: appGrid
visible: root.filteredApps.length > 0
visible: root.resultCount > 0
width: parent.width
columns: 4
columnSpacing: 6
rowSpacing: 6
Repeater {
model: root.filteredApps
model: root.usingInstalledApps ? ApplicationService.filteredModel : []
delegate: AppItem {
required property string desktopId
required property string name
required property string comment
required property string iconName
width: (appGrid.width - 18) / 4
variant: "tile"
item: ({
desktopId: desktopId,
name: name,
comment: comment,
iconName: iconName
})
onSelected: root.launchApp(item)
}
}
Repeater {
model: root.usingInstalledApps ? [] : root.mockMatches
delegate: AppItem {
required property var modelData
width: (appGrid.width - 18) / 4
@@ -152,7 +177,7 @@ Item {
}
Text {
visible: root.filteredApps.length === 0
visible: root.resultCount === 0
width: parent.width
topPadding: 8
text: qsTr("No matching applications")
@@ -165,7 +190,7 @@ Item {
Column {
width: parent.width
spacing: 8
visible: !root.searching && MockApps.projects.length > 0
visible: !root.searching && !root.usingInstalledApps && MockApps.projects.length > 0
Text {
text: qsTr("Recent projects")
@@ -286,10 +311,12 @@ Item {
}
function launchApp(app) {
ShellState.launch(app.name)
if (app && app.desktopId)
ApplicationService.launch(app.desktopId)
ShellState.closeLauncher()
}
function openProject(project) {
ShellState.launch(project.name)
ShellState.closeLauncher()
}
}