diff --git a/doc/json-schema/os-list-schema.json b/doc/json-schema/os-list-schema.json index 4bd017a..20cfa2a 100644 --- a/doc/json-schema/os-list-schema.json +++ b/doc/json-schema/os-list-schema.json @@ -194,7 +194,8 @@ "extract_sha256": "ceb7d7489847ed811e7746fa779837f78fc06d43663148a696280e6a1cfe00e3", "image_download_size": 1306588543, "release_date": "2022-01-28", - "init_format": "systemd" + "init_format": "systemd", + "devices": ["pi1a"] } ], "required": [ @@ -205,7 +206,8 @@ "extract_size", "extract_sha256", "image_download_size", - "release_date" + "release_date", + "devices" ], "properties": { "name": { @@ -298,6 +300,18 @@ "systemd" ] }, + "devices": { + "$id": "#/properties/os_list/items/anyOf/0/properties/compat_with", + "type": "array", + "title": "The compat_with schema", + "description": "Provides a JSON-format list of strings representing Raspberry Pi devices that are supported with this image", + "default": "", + "examples": [ + "[\"1a\", \"1b\"]", + "[\"4\", \"5\"]", + "[\"cm3\", \"cm4\"]" + ] + }, "website": { "$id": "#/properties/os_list/items/anyOf/1/properties/website", "type": "string", diff --git a/src/i18n/rpi-imager_en.ts b/src/i18n/rpi-imager_en.ts index 909e619..d80bbdf 100644 --- a/src/i18n/rpi-imager_en.ts +++ b/src/i18n/rpi-imager_en.ts @@ -467,18 +467,28 @@ - + + CHOOSE DEVICE + + + + + Select this button to choose your target Raspberry Pi + + + + Operating System - + CHOOSE OS - + Select this button to change the operating system diff --git a/src/main.qml b/src/main.qml index ce65977..dec7d56 100644 --- a/src/main.qml +++ b/src/main.qml @@ -14,9 +14,9 @@ ApplicationWindow { id: window visible: true - width: imageWriter.isEmbeddedMode() ? -1 : 680 + width: imageWriter.isEmbeddedMode() ? -1 : 800 height: imageWriter.isEmbeddedMode() ? -1 : 450 - minimumWidth: imageWriter.isEmbeddedMode() ? -1 : 680 + minimumWidth: imageWriter.isEmbeddedMode() ? -1 : 800 minimumHeight: imageWriter.isEmbeddedMode() ? -1 : 420 title: qsTr("Raspberry Pi Imager v%1").arg(imageWriter.constantVersion()) @@ -25,6 +25,8 @@ ApplicationWindow { FontLoader {id: robotoLight; source: "fonts/Roboto-Light.ttf"} FontLoader {id: robotoBold; source: "fonts/Roboto-Bold.ttf"} + property string hwTags + onClosing: { if (progressBar.visible) { close.accepted = false @@ -83,11 +85,47 @@ ApplicationWindow { anchors.leftMargin: 50 rows: 6 - columns: 3 + columns: 4 columnSpacing: 25 ColumnLayout { - id: columnLayout + id: columnLayout0 + spacing: 0 + Layout.fillWidth: true + + Text { + id: text0 + color: "#ffffff" + text: qsTr("Select your device") + Layout.fillWidth: true + Layout.preferredHeight: 17 + Layout.preferredWidth: 100 + font.pixelSize: 12 + font.family: robotoBold.name + font.bold: true + horizontalAlignment: Text.AlignHCenter + } + + ImButton { + id: hwbutton + text: qsTr("CHOOSE DEVICE") + spacing: 0 + padding: 0 + bottomPadding: 0 + topPadding: 0 + Layout.minimumHeight: 40 + Layout.fillWidth: true + onClicked: { + hwpopup.open() + hwswipeview.currentItem.forceActiveFocus() + } + Accessible.ignored: ospopup.visible || dstpopup.visible + Accessible.description: qsTr("Select this button to choose your target Raspberry Pi") + } + } + + ColumnLayout { + id: columnLayout1 spacing: 0 Layout.fillWidth: true @@ -359,6 +397,107 @@ ApplicationWindow { } } + Popup { + id: hwpopup + x: 50 + y: 25 + width: parent.width-100 + height: parent.height-50 + padding: 0 + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + property string hwselected: "" + + // background of title + Rectangle { + color: "#f5f5f5" + anchors.right: parent.right + anchors.top: parent.top + height: 35 + width: parent.width + } + // line under title + Rectangle { + color: "#afafaf" + width: parent.width + y: 35 + implicitHeight: 1 + } + + Text { + text: "X" + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: roboto.name + font.bold: true + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + hwpopup.close() + } + } + } + + ColumnLayout { + spacing: 10 + + Text { + text: qsTr("Raspberry Pi Device") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + Layout.topMargin: 10 + font.family: roboto.name + font.bold: true + } + + Item { + clip: true + Layout.preferredWidth: hwlist.width + Layout.preferredHeight: hwlist.height + + SwipeView { + id: hwswipeview + interactive: false + + ListView { + id: hwlist + model: ListModel { + id: deviceModel + ListElement { + name: qsTr("[ All ]") + tags: "[]" + } + } + currentIndex: -1 + delegate: hwdelegate + width: window.width-100 + height: window.height-100 + boundsBehavior: Flickable.StopAtBounds + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + ScrollBar.vertical: ScrollBar { + width: 10 + policy: hwlist.contentHeight > hwlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + } + Keys.onSpacePressed: { + if (currentIndex != -1) + selectHWitem(model.get(currentIndex)) + } + Accessible.onPressAction: { + if (currentIndex != -1) + selectHWitem(model.get(currentIndex)) + } + Keys.onEnterPressed: Keys.onSpacePressed(event) + Keys.onReturnPressed: Keys.onSpacePressed(event) + } + } + } + } + } + /* Popup for OS selection */ @@ -419,59 +558,6 @@ ApplicationWindow { font.bold: true } - Rectangle { - id: modelRowRect - color: "#ffffe3" - Layout.fillWidth: true - implicitHeight: modelRow.implicitHeight - visible: osswipeview.currentIndex == 0 - Layout.bottomMargin: -10 - - Row { - id: modelRow - spacing: 15 - leftPadding: 15 - - Text { - id: modelText - text: qsTr("Pi model:") - font.family: roboto.name - verticalAlignment: Qt.AlignVCenter - height: parent.height - } - - ComboBox { - id: deviceModelCombo - model: ListModel { - id: deviceModel - ListElement { - name: qsTr("[ All ]") - tags: "[]" - } - } - width: 300 - textRole: "name" - font.family: roboto.name - font.pixelSize: 12 - currentIndex: 0 - onCurrentIndexChanged: { - /* Reload list */ - httpRequest(imageWriter.constantOsListUrl(), function (x) { - var o = JSON.parse(x.responseText) - var oslist = oslistFromJson(o) - if (oslist === false) - return - - osmodel.remove(0, osmodel.count-2) - for (var i in oslist) { - osmodel.insert(osmodel.count-2, oslist[i]) - } - }) - } - } - } - } - Item { clip: true Layout.preferredWidth: oslist.width @@ -487,7 +573,7 @@ ApplicationWindow { currentIndex: -1 delegate: osdelegate width: window.width-100 - height: modelRowRect.visible ? window.height-100-modelRowRect.height : window.height-100 + height: window.height-100 boundsBehavior: Flickable.StopAtBounds highlight: Rectangle { color: "lightsteelblue"; radius: 5 } ScrollBar.vertical: ScrollBar { @@ -559,30 +645,6 @@ ApplicationWindow { ListModel { id: osmodel - ListElement { - url: "internal://format" - icon: "icons/erase.png" - extract_size: 0 - image_download_size: 0 - extract_sha256: "" - contains_multiple_files: false - release_date: "" - subitems_url: "" - subitems_json: "" - name: qsTr("Erase") - description: qsTr("Format card as FAT32") - tooltip: "" - website: "" - init_format: "" - } - - ListElement { - url: "" - icon: "icons/use_custom.png" - name: qsTr("Use custom") - description: qsTr("Select a custom .img from your computer") - } - Component.onCompleted: { if (imageWriter.isOnline()) { fetchOSlist(); @@ -590,6 +652,97 @@ ApplicationWindow { } } + Component { + id: hwdelegate + + Item { + width: window.width-100 + height: contentLayout.implicitHeight + 24 + Accessible.name: name+".\n"+description + + MouseArea { + id: hwMouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + bgrect.mouseOver = true + } + + onExited: { + bgrect.mouseOver = false + } + + onClicked: { + selectHWitem(model) + } + } + + Rectangle { + id: bgrect + anchors.fill: parent + color: "#f5f5f5" + visible: mouseOver && parent.ListView.view.currentIndex !== index + property bool mouseOver: false + } + Rectangle { + id: borderrect + implicitHeight: 1 + implicitWidth: parent.width + color: "#dcdcdc" + y: parent.height + } + + RowLayout { + id: contentLayout + anchors { + left: parent.left + top: parent.top + right: parent.right + margins: 12 + } + spacing: 12 + + Image { + source: icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png": icon + Layout.preferredHeight: 40 + Layout.preferredWidth: 40 + sourceSize.width: 40 + sourceSize.height: 40 + fillMode: Image.PreserveAspectFit + verticalAlignment: Image.AlignVCenter + Layout.alignment: Qt.AlignVCenter + } + ColumnLayout { + Layout.fillWidth: true + + Text { + text: name + elide: Text.ElideRight + font.family: roboto.name + font.bold: true + } + + Text { + Layout.fillWidth: true + font.family: roboto.name + text: description + wrapMode: Text.WordWrap + color: "#1a1a1a" + } + + ToolTip { + visible: hwMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" + delay: 1000 + text: typeof(tooltip) == "string" ? tooltip : "" + clip: false + } + } + } + } + } + Component { id: osdelegate @@ -1206,7 +1359,9 @@ ApplicationWindow { oslist = o["os_list"] } - filterItems(oslist, JSON.parse(deviceModel.get(deviceModelCombo.currentIndex).tags)) + if (hwTags != "") { + filterItems(oslist, JSON.parse(hwTags)) + } checkForRandom(oslist) /* Flatten subitems to subitems_json */ @@ -1242,8 +1397,9 @@ ApplicationWindow { var oslist = oslistFromJson(o) if (oslist === false) return + osmodel.clear() for (var i in oslist) { - osmodel.insert(osmodel.count-2, oslist[i]) + osmodel.append(oslist[i]) } if ("imager" in o) { @@ -1251,6 +1407,7 @@ ApplicationWindow { if ("devices" in imager) { + deviceModel.clear() var devices = imager["devices"] for (var j in devices) { @@ -1258,7 +1415,7 @@ ApplicationWindow { deviceModel.append(devices[j]) if ("default" in devices[j] && devices[j]["default"]) { - deviceModelCombo.currentIndex = deviceModel.count-1 + hwlist.currentIndex = deviceModel.count-1 } } } @@ -1283,6 +1440,31 @@ ApplicationWindow { } } } + + /* Add in our 'special' items. */ + osmodel.append({ + url: "internal://format", + icon: "icons/erase.png", + extract_size: 0, + image_download_size: 0, + extract_sha256: "", + contains_multiple_files: false, + release_date: "", + subitems_url: "", + subitems_json: "", + name: qsTr("Erase"), + description: qsTr("Format card as FAT32"), + tooltip: "", + website: "", + init_format: "" + }) + + osmodel.append({ + url: "", + icon: "icons/use_custom.png", + name: qsTr("Use custom"), + description: qsTr("Select a custom .img from your computer") + }) }) } @@ -1326,6 +1508,25 @@ ApplicationWindow { return m } + function selectHWitem(hwmodel) { + hwTags = hwmodel.tags + /* Reload list */ + httpRequest(imageWriter.constantOsListUrl(), function (x) { + var o = JSON.parse(x.responseText) + var oslist = oslistFromJson(o) + if (oslist === false) + return + + osmodel.remove(0, osmodel.count-2) + for (var i in oslist) { + osmodel.insert(osmodel.count-2, oslist[i]) + } + }) + + hwbutton.text = hwmodel.name + hwpopup.close() + } + function selectOSitem(d, selectFirstSubitem) { if (typeof(d.subitems_json) == "string" && d.subitems_json !== "") {