Files
logos-basecamp/src/Basecamp/AppManager/AppContextMenu.qml
T
romanzac 36f4cf4a1b test(qml): add spec §4.1 automation objectNames to QML sources
Add the stable automation handles enumerated in spec §4.1, following
the existing sidebar.app.<name> / confirmationDialog.<mode>.<button>
convention: welcomePage.installNow, appManager.{searchField,
category.<name>,emptyView}, appContextMenu root,
addApplicationDialog.{primaryButton,closeButton,errorText,
resolutionBanner}, settings.searchField,
pluginInterface.{call.<method>,result,back}, repositories.{urlField,
addButton,refreshButton,errorBanner,errorDismiss,row.<url>,
removeConfirm.{confirm,cancel}}, and sidebar.buildLabel.

workspace.dock.<name> is deliberately not applied: the dock root's
objectName is pinned to the bare module name by
workspace_area_test.cpp and needs a coordinated rename (recorded as
a finding).
2026-08-21 04:30:04 +00:00

105 lines
3.6 KiB
QML

import QtQuick
import QtQuick.Controls
import Logos.Controls
import Logos.Theme
import Basecamp.Backend 1.0
LogosMenu {
id: root
objectName: "appContextMenu"
// Plain object snapshot of the row: name, displayName, repositoryUrl,
// isInstalled, installStage, installStatus, installType. Set via
// openFor() — never bound to a delegate's `model`, which would dangle
// once the row scrolls out from under an open menu.
property var appData: ({})
signal openRequested(string name, string repositoryUrl)
signal detailsRequested(string name, string repositoryUrl)
signal installRequested(string name, string repositoryUrl)
signal uninstallRequested(string name, string repositoryUrl)
function openFor(appData_) {
root.appData = appData_ || ({});
root.popup();
}
QtObject {
id: d
readonly property string rowName:
(root.appData && root.appData.name) ? root.appData.name : ""
readonly property string rowRepositoryUrl:
(root.appData && root.appData.repositoryUrl) ? root.appData.repositoryUrl : ""
readonly property bool rowInstalled:
!!root.appData && root.appData.isInstalled === true
readonly property int rowStage:
(root.appData && root.appData.installStage !== undefined)
? root.appData.installStage
: InstallStage.None
// Install/upgrade in flight — the module's pending slot is global,
// so a racing uninstall would just be rejected. Disable rather than
// hide so the item doesn't jump around mid-install.
readonly property bool rowBusy:
d.rowStage === InstallStage.Queued
|| d.rowStage === InstallStage.Downloading
|| d.rowStage === InstallStage.Installing
readonly property bool rowEmbedded:
!!root.appData && root.appData.installType === "embedded"
readonly property bool rowProtected:
d.rowName === "main_ui"
readonly property bool canUninstall:
d.rowInstalled && !d.rowEmbedded && !d.rowProtected
}
LogosMenuItem {
objectName: "appContextMenu.open"
text: qsTr("Open")
visible: d.rowInstalled
height: visible ? implicitHeight : 0
onTriggered: root.openRequested(d.rowName, d.rowRepositoryUrl)
}
LogosMenuItem {
objectName: "appContextMenu.details"
text: qsTr("Details")
visible: d.rowInstalled
height: visible ? implicitHeight : 0
onTriggered: root.detailsRequested(d.rowName, d.rowRepositoryUrl)
}
LogosMenuItem {
objectName: "appContextMenu.install"
text: qsTr("Install…")
visible: !d.rowInstalled
height: visible ? implicitHeight : 0
onTriggered: root.installRequested(d.rowName, d.rowRepositoryUrl)
}
LogosMenuSeparator {
visible: d.canUninstall
height: visible ? implicitHeight : 0
}
LogosMenuItem {
id: uninstallItem
objectName: "appContextMenu.uninstall"
text: qsTr("Uninstall")
visible: d.canUninstall
height: visible ? implicitHeight : 0
enabled: !d.rowBusy
onTriggered: root.uninstallRequested(d.rowName, d.rowRepositoryUrl)
contentItem: LogosText {
leftPadding: Theme.spacing.medium
rightPadding: Theme.spacing.medium
verticalAlignment: Text.AlignVCenter
text: uninstallItem.text
color: uninstallItem.enabled ? Theme.palette.error
: Theme.palette.textMuted
}
}
}