diff options
Diffstat (limited to 'desktop/modules/vm/VmPage.qml')
| -rw-r--r-- | desktop/modules/vm/VmPage.qml | 92 |
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}`) + } + } + } } } } |
