aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/vm/VmPage.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/modules/vm/VmPage.qml')
-rw-r--r--desktop/modules/vm/VmPage.qml92
1 files changed, 82 insertions, 10 deletions
diff --git a/desktop/modules/vm/VmPage.qml b/desktop/modules/vm/VmPage.qml
index 9c731f5..2ac57f0 100644
--- a/desktop/modules/vm/VmPage.qml
+++ b/desktop/modules/vm/VmPage.qml
@@ -189,7 +189,8 @@ Column {
//
// One card per compose project, shaped like a VM row: a dot, the
// aggregate stats, and the containers with their verbs when selected.
- // Nothing here is destructive enough to confirm: no rm, no prune.
+ // Nothing per container is destructive enough to confirm: no rm. Image
+ // prune is, since a re-pull can be gigabytes, so it asks first.
property string selectedProject: ""
@@ -199,11 +200,54 @@ Column {
return [["start", "Start"]];
}
- Text {
+ property bool pruneArmed: false
+
+ Row {
visible: Docker.projects.length > 0
- text: "Containers"
- font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true }
- color: Theme.subtext
+ spacing: 10
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: "Containers"
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true }
+ color: Theme.subtext
+ }
+ Button {
+ visible: !page.pruneArmed && (Docker.unused.length > 0 || Docker.pruning)
+ enabled: !Docker.pruning
+ text: Docker.pruning ? "Pruning…" : "Prune images"
+ onClicked: page.pruneArmed = true
+ }
+ Button {
+ visible: page.pruneArmed
+ danger: true
+ text: "Delete unused images"
+ onClicked: { page.pruneArmed = false; Docker.prune(); }
+ }
+ Button {
+ visible: page.pruneArmed
+ text: "Cancel"
+ onClicked: page.pruneArmed = false
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ visible: !page.pruneArmed && Docker.pruned !== ""
+ text: Docker.pruned
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ color: Theme.overlay
+ }
+ }
+
+ // What prune would delete: images no container uses.
+ Repeater {
+ model: Docker.unused
+ Text {
+ required property var modelData
+ width: page.width
+ elide: Text.ElideMiddle
+ text: `${modelData.name} ${modelData.size}`
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ color: Theme.overlay
+ }
}
Repeater {
@@ -284,23 +328,30 @@ Column {
Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.08) }
// Whole-project verbs, by container id, so no compose
- // file is needed. Pointless for a group of one.
+ // file is needed; pointless for a group of one. Update
+ // is the exception: it needs the compose files.
Flow {
- visible: cs.length > 1
+ visible: cs.length > 1 || !!cs[0]?.files
width: parent.width
spacing: 8
Button {
- visible: up < cs.length
+ visible: !!cs[0]?.files
+ enabled: Docker.updating === ""
+ text: Docker.updating === modelData ? "Updating…" : "Update"
+ onClicked: Docker.update(modelData, cs[0].files)
+ }
+ Button {
+ visible: cs.length > 1 && up < cs.length
text: "Start all"
onClicked: Docker.act(cs.map(c => c.id), modelData, "start")
}
Button {
- visible: up > 0
+ visible: cs.length > 1 && up > 0
text: "Stop all"
onClicked: Docker.act(cs.map(c => c.id), modelData, "stop")
}
Button {
- visible: up > 0
+ visible: cs.length > 1 && up > 0
text: "Restart all"
onClicked: Docker.act(cs.map(c => c.id), modelData, "restart")
}
@@ -352,6 +403,27 @@ Column {
}
}
}
+
+ // Published ports, opened by the default
+ // browser (a tab in a running one).
+ // ponytail: docker cannot say which port is a
+ // web UI, so every published port gets a
+ // button, and a port ending in 443 is guessed
+ // https.
+ Flow {
+ visible: modelData.ports.length > 0
+ width: parent.width
+ spacing: 8
+ Repeater {
+ model: ctr.modelData.ports
+ Button {
+ required property int modelData
+ text: `localhost:${modelData}`
+ onClicked: Qt.openUrlExternally(
+ `${modelData % 1000 === 443 ? "https" : "http"}://localhost:${modelData}`)
+ }
+ }
+ }
}
}
}