diff options
Diffstat (limited to 'crates/flasher-qt/qml/Main.qml')
| -rw-r--r-- | crates/flasher-qt/qml/Main.qml | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/crates/flasher-qt/qml/Main.qml b/crates/flasher-qt/qml/Main.qml new file mode 100644 index 0000000..a41064d --- /dev/null +++ b/crates/flasher-qt/qml/Main.qml @@ -0,0 +1,394 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import Qt.labs.platform 1.1 as Platform + +ApplicationWindow { + id: window + visible: true + width: 760 + height: 820 + minimumWidth: 620 + minimumHeight: 620 + title: qsTr("ALT Image Writer — Qt") + + property bool sourceIsImage: false + property bool outputIsFile: false + property string sourcePath: "" + property string outputPath: "" + property string selectedBoard: "" + property string selectedPlatform: "" + property var driveNames: availableDrivesText.length ? availableDrivesText.split("\n") : [] + + function localPath(url) { + var value = url.toString() + return value.indexOf("file://") === 0 ? decodeURIComponent(value.substring(7)) : value + } + + function fileName(path) { + var parts = path.split("/") + return parts[parts.length - 1] + } + + function ready() { + return sourcePath.length > 0 && selectedBoard.length > 0 + && (outputIsFile ? outputPath.length > 0 : driveBox.currentIndex >= 0) + } + + header: ToolBar { + RowLayout { + anchors.fill: parent + anchors.leftMargin: 16 + anchors.rightMargin: 8 + Label { + text: qsTr("ALT Image Writer") + font.pixelSize: 18 + font.bold: true + Layout.fillWidth: true + } + ToolButton { + text: "ⓘ" + font.pixelSize: 18 + Accessible.name: qsTr("About") + onClicked: aboutDialog.open() + } + } + } + + ScrollView { + id: scroll + anchors.fill: parent + contentWidth: availableWidth + clip: true + + ColumnLayout { + width: Math.min(scroll.availableWidth - 40, 700) + x: Math.max(20, (scroll.availableWidth - width) / 2) + spacing: 16 + + Item { Layout.preferredHeight: 8 } + Label { + text: qsTr("Create Installation Media") + font.pixelSize: 26 + font.bold: true + Layout.alignment: Qt.AlignHCenter + } + Label { + text: qsTr("Prepare an ALT Linux root filesystem or disk image for your board.") + color: palette.mid + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + } + + Frame { + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + spacing: 12 + Label { text: qsTr("Installation Source"); font.bold: true; font.pixelSize: 17 } + Label { text: qsTr("Choose a root filesystem archive or a ready-made disk image."); color: palette.mid } + ButtonGroup { id: sourceGroup } + RowLayout { + Label { text: qsTr("Source type"); Layout.fillWidth: true } + RadioButton { + text: qsTr("Root filesystem") + checked: true + ButtonGroup.group: sourceGroup + onCheckedChanged: if (checked) sourceIsImage = false + } + RadioButton { + text: qsTr("Disk image") + ButtonGroup.group: sourceGroup + onCheckedChanged: if (checked) { + sourceIsImage = true + outputIsFile = false + driveOutput.checked = true + } + } + } + RowLayout { + spacing: 12 + Label { + text: sourcePath.length ? fileName(sourcePath) : qsTr("No source selected") + font.bold: sourcePath.length > 0 + elide: Text.ElideMiddle + Layout.fillWidth: true + } + Button { text: qsTr("Choose…"); onClicked: sourceDialog.open() } + } + Label { + text: sourcePath.length ? sourcePath + : sourceIsImage ? qsTr("Supported: .img, .img.xz") + : qsTr("Supported: .tar, .tar.gz, .tar.xz") + color: palette.mid + elide: Text.ElideMiddle + Layout.fillWidth: true + } + } + } + + Frame { + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + spacing: 10 + Label { text: qsTr("Target Hardware"); font.bold: true; font.pixelSize: 17 } + Label { text: qsTr("Boards are grouped by platform, SoC family, and architecture."); color: palette.mid } + RowLayout { + ColumnLayout { + Layout.fillWidth: true + Label { + text: selectedBoard.length ? selectedBoard : qsTr("No board selected") + font.bold: selectedBoard.length > 0 + } + Label { + text: selectedBoard.length ? selectedPlatform : qsTr("Choose from the supported board list") + color: palette.mid + } + } + Button { text: qsTr("Select Board…"); onClicked: boardDialog.open() } + } + } + } + + Frame { + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + spacing: 12 + Label { text: qsTr("Destination"); font.bold: true; font.pixelSize: 17 } + Label { text: qsTr("Write to removable media or create an image file."); color: palette.mid } + ButtonGroup { id: outputGroup } + RowLayout { + Label { text: qsTr("Output type"); Layout.fillWidth: true } + RadioButton { + id: driveOutput + text: qsTr("Drive") + checked: true + ButtonGroup.group: outputGroup + onCheckedChanged: if (checked) outputIsFile = false + } + RadioButton { + text: qsTr("Image file") + enabled: !sourceIsImage + ButtonGroup.group: outputGroup + onCheckedChanged: if (checked) outputIsFile = true + } + } + RowLayout { + visible: !outputIsFile + Label { text: qsTr("Storage device"); Layout.preferredWidth: 150 } + ComboBox { + id: driveBox + Layout.fillWidth: true + model: driveNames + displayText: currentIndex < 0 ? qsTr("No removable drives detected") : currentText + } + } + RowLayout { + visible: outputIsFile + Label { + text: outputPath.length ? outputPath : qsTr("Choose where to save the image") + elide: Text.ElideMiddle + Layout.fillWidth: true + } + Button { text: qsTr("Choose…"); onClicked: outputDialog.open() } + } + } + } + + Frame { + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + spacing: 8 + Label { text: qsTr("Options"); font.bold: true; font.pixelSize: 17 } + RowLayout { + Label { text: qsTr("Root filesystem"); Layout.fillWidth: true } + ComboBox { model: ["ext4", "f2fs"] } + } + CheckBox { text: qsTr("Resize root partition to fill the destination"); checked: true } + CheckBox { text: qsTr("Create a separate 512 MiB boot partition") } + CheckBox { text: qsTr("Encrypt the root partition with LUKS") } + CheckBox { text: qsTr("Add the system serial console") } + RowLayout { + Label { text: qsTr("EFI system partition"); Layout.fillWidth: true } + ComboBox { model: [qsTr("None"), qsTr("EFI (GPT)"), qsTr("EFI (MBR)")] } + } + RowLayout { + Label { text: qsTr("First-boot VNC"); Layout.fillWidth: true } + ComboBox { model: [qsTr("Use image default"), qsTr("Enabled"), qsTr("Disabled")] } + } + } + } + Item { Layout.preferredHeight: 80 } + } + } + + footer: ToolBar { + RowLayout { + anchors.fill: parent + anchors.margins: 10 + Label { text: qsTr("Nothing will be written until you confirm."); color: palette.mid; Layout.fillWidth: true } + Button { + text: qsTr("Review and Write") + highlighted: true + enabled: ready() + onClicked: reviewDialog.open() + } + } + } + + Dialog { + id: boardDialog + parent: Overlay.overlay + anchors.centerIn: parent + width: Math.min(window.width - 48, 620) + height: Math.min(window.height - 80, 650) + modal: true + title: qsTr("Select Target Board") + standardButtons: Dialog.Cancel + + ListModel { id: allBoards } + ListModel { id: filteredBoards } + + function populate() { + if (allBoards.count > 0) return + var platform = "Other" + var lines = supportedBoardsText.split("\n") + for (var i = 0; i < lines.length; ++i) { + var line = lines[i].trim() + if (!line.length) continue + if (line.charAt(line.length - 1) === ":") { + platform = line.substring(0, line.length - 1) + } else { + var names = line.split(/\s+/) + for (var j = 0; j < names.length; ++j) + allBoards.append({ boardName: names[j], platformName: platform }) + } + } + filter("") + } + + function filter(query) { + filteredBoards.clear() + var needle = query.toLowerCase() + for (var i = 0; i < allBoards.count; ++i) { + var item = allBoards.get(i) + if (!needle.length || item.boardName.toLowerCase().indexOf(needle) >= 0 + || item.platformName.toLowerCase().indexOf(needle) >= 0) + filteredBoards.append({ boardName: item.boardName, platformName: item.platformName }) + } + } + + onOpened: { populate(); searchField.forceActiveFocus() } + + ColumnLayout { + anchors.fill: parent + TextField { + id: searchField + Layout.fillWidth: true + placeholderText: qsTr("Search boards or platforms…") + selectByMouse: true + onTextChanged: boardDialog.filter(text) + } + Label { + text: qsTr("%1 matching boards").arg(filteredBoards.count) + color: palette.mid + } + ListView { + id: boardList + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: filteredBoards + spacing: 2 + section.property: "platformName" + section.criteria: ViewSection.FullString + section.delegate: Rectangle { + width: boardList.width + height: 42 + color: window.palette.alternateBase + Label { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: section + font.bold: true + color: window.palette.highlight + } + } + delegate: ItemDelegate { + width: boardList.width + text: boardName + onClicked: { + selectedBoard = boardName + selectedPlatform = platformName + boardDialog.close() + } + } + ScrollBar.vertical: ScrollBar {} + } + } + } + + Dialog { + id: reviewDialog + parent: Overlay.overlay + anchors.centerIn: parent + modal: true + title: qsTr("Ready to write?") + standardButtons: Dialog.Cancel | Dialog.Ok + onAccepted: previewNotice.open() + ColumnLayout { + Label { text: qsTr("Board"); font.bold: true } + Label { text: selectedBoard + " — " + selectedPlatform } + Label { text: qsTr("Destination"); font.bold: true } + Label { text: outputIsFile ? outputPath : driveBox.currentText; elide: Text.ElideMiddle; Layout.maximumWidth: 450 } + Label { text: qsTr("This is an interface preview. No data will be written."); color: palette.mid } + } + } + + Dialog { + id: aboutDialog + parent: Overlay.overlay + anchors.centerIn: parent + modal: true + title: qsTr("About ALT Image Writer") + standardButtons: Dialog.Ok + Label { + text: qsTr("ALT Image Writer — Qt\nVersion 0.1.0\n\nPrepare ALT Linux installation media for supported SBCs.") + horizontalAlignment: Text.AlignHCenter + } + } + + Popup { + id: previewNotice + parent: Overlay.overlay + x: (parent.width - width) / 2 + y: parent.height - height - 24 + padding: 16 + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + Label { text: qsTr("Flashing is not implemented in this preview") } + Timer { interval: 3500; running: previewNotice.visible; onTriggered: previewNotice.close() } + } + + Platform.FileDialog { + id: sourceDialog + title: qsTr("Choose Installation Source") + fileMode: Platform.FileDialog.OpenFile + nameFilters: sourceIsImage ? [qsTr("Disk images (*.img *.img.xz)"), qsTr("All files (*)")] + : [qsTr("Root filesystem archives (*.tar *.tar.gz *.tar.xz)"), qsTr("All files (*)")] + onAccepted: sourcePath = localPath(file) + } + + Platform.FileDialog { + id: outputDialog + title: qsTr("Create Disk Image") + fileMode: Platform.FileDialog.SaveFile + defaultSuffix: "img" + nameFilters: [qsTr("Raw disk images (*.img)"), qsTr("All files (*)")] + onAccepted: outputPath = localPath(file) + } +} |