/* Copyright 2018 WobLight * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick import GitAddonsManager.engine import QtQuick.Controls import QtQuick.Layouts Page { title: "Addons" header: ToolBar { RowLayout { anchors.fill: parent ToolButton { hoverEnabled: true action: addAction ToolTip.visible: hovered ToolTip.text: "Download a new addon from a git repository" //Material.foreground: Material.background } ToolButton { hoverEnabled: true action: exportList ToolTip.visible: hovered ToolTip.text: "Export the list of installed addons" //Material.foreground: Material.background } ToolButton { hoverEnabled: true action: updateAllAction icon.name: availableUpdates > 0 ? "update-high" : "update-none" ToolTip.visible: hovered ToolTip.text: "Upgrade all addons" //Material.foreground: Material.background } Item { Layout.fillWidth: true } ToolButton { hoverEnabled: true Layout.alignment: Qt.AlignRight action: refreshAction ToolTip.visible: hovered ToolTip.text: "Refresh addons list and check for updates" //Material.foreground: Material.background } } } RowLayout { anchors.fill: parent anchors.leftMargin: 4 spacing: 0 ListView { ScrollBar.vertical: scrollBar clip: true Layout.fillHeight: true Layout.fillWidth: true model: Engine.addons id: listView section.property: "path" section.criteria: ViewSection.FullString section.delegate: Button { text: section icon.name: "go-parent-folder" width: Math.min(listView.width, implicitWidth) onClicked: Qt.openUrlExternally((Qt.platform.os == "windows" ? "file:///" : "file://")+section) display: AbstractButton.TextBesideIcon } delegate: MouseArea { property Addon addon: Engine.addons[index] implicitWidth: listView.width implicitHeight: row.height hoverEnabled: true id: ma Rectangle { anchors.fill: row color: "#ff8000" visible: addon.status === Addon.Status.Error || addon.gitStatus === Addon.Error } RowLayout { width: parent.width id: row Rectangle { visible: ma.containsMouse || addonMenu.visible || branchSelector.popup.visible Layout.fillHeight: true width: 4 color: deleteButton.hovered ? "red" : "lightgreen" } Label { id: addonName text: addon.name } Item { Layout.fillWidth: true Layout.fillHeight: true ProgressBar { anchors.fill: parent anchors.margins: 4 id: pBar from: 0 to: addon.total value: addon.progress visible: addon.status === Addon.Status.Busy indeterminate: to == 0 } } ComboBox { id: branchSelector model: addon.branches onActivated: addon.currentBranch = currentText implicitContentWidthPolicy: ComboBox.WidestText Component.onCompleted: branchSelector.currentIndex = branchSelector.find(addon.currentBranch) Connections { target: addon function onCurrentBranchChanged() { branchSelector.currentIndex = branchSelector.find(addon.currentBranch) } function onBranchesChanged() { branchSelector.currentIndex = branchSelector.find(addon.currentBranch) } } } RowLayout { spacing: 0 AddonUpdateButton { } ToolButton { icon.name: "overflow-menu" onClicked: if (!addonMenu.visible) addonMenu.visible = true checked: addonMenu.visible Menu { x: -width id: addonMenu visible: false MenuItem { id: readmeButton text: qsTr("readme") enabled: addon.readme !== "" icon.name: "help-about" onTriggered: { readmeDialog.addon = addon readmeDialog.visible = true } hoverEnabled: true } MenuItem { id: websiteButton text: qsTr("website") icon.name: "internet-services-symbolic" onTriggered: { Qt.openUrlExternally(addon.getUrl()) } hoverEnabled: true } MenuItem { id: changeUrlButton text: qsTr("change url") icon.name: "edit-link" enabled: addon.status == Addon.Status.Ready || addon.status == Addon.Status.Error hoverEnabled: true onTriggered: { urlDialog.addon = addon urlDialog.newUrlText = addon.getUrl().toString() urlDialog.visible = true } } MenuItem { id: repairButton hoverEnabled: true state: addon.gitStatus === Addon.Error ? "reclone" : "reset" states: [ State { name: "reset" PropertyChanges { enabled: addon.status == Addon.Status.Ready target: repairButton text: qsTr("repair") icon.name: "document-edit-decrypt" ToolTip.visible: hovered ToolTip.text: "Restore addon files" onTriggered: addon.reset() } }, State { name: "reclone" PropertyChanges { enabled: true target: repairButton text: qsTr("repair repository") icon.name: "view-refresh" ToolTip.visible: hovered ToolTip.text: "Attempts to re-download the repository" onTriggered: addon.reclone() } } ] } MenuItem { enabled: addon.status == Addon.Status.Ready id: deleteButton text: qsTr("delete") icon.name: "delete" onTriggered: addon.uninstall() hoverEnabled: true } } } } } Dialog { parent: window.overlay x: (parent.width - implicitWidth) / 2 y: (parent.height - implicitHeight) / 2 title: qsTr("Following files will be deleted:") standardButtons: Dialog.Ok | Dialog.Cancel onAccepted: addon.confirmFileRemove(true) onRejected: addon.confirmFileRemove(false) modal: true visible: addon.filesToRemove != "" closePolicy: Popup.NoAutoClose ScrollView{ anchors.fill: parent TextArea { text: addon.filesToRemove readOnly: true anchors.fill: parent wrapMode: Text.NoWrap } } } } } ScrollBar {id: scrollBar; Layout.fillHeight: true; visible: listView.contentHeight > listView.header} } Dialog { property Addon addon id: readmeDialog parent: window.overlay x: (parent.width - width) / 2 y: (parent.height - height) / 2 width: Math.min(800, parent.width * 0.75) height: Math.min(600, parent.height * 0.75) title: addon?qsTr("%1 Readme").arg(addon.name):"" standardButtons: Dialog.Close ScrollView { anchors.fill: parent contentWidth: parent.width ColumnLayout { width: parent.width Label { id: readmeLabel Layout.fillWidth: true width: parent.width wrapMode: Text.WordWrap textFormat: Text.MarkdownText Layout.maximumWidth: parent.width font.pixelSize: 14 topPadding: 15 bottomPadding: 15 leftPadding: 10 rightPadding: 10 text: { let rawText = readmeDialog.addon ? readmeDialog.addon.readme : ""; return rawText.replace(/<[^>]*>/g, ''); let listText = cleanText.replace(/\|(.*)\|(.*)\|/g, (match, p1, p2) => { if (p1.includes("---")) return ""; return "• **" + p1.trim() + "**: " + p2.trim(); }); return listText; } baseUrl: { if (readmeDialog.addon) { // 1. Remove the ".git" from the end of the repository URL let cleanUrl = readmeDialog.addon.getUrl().toString().replace(".git", ""); // 2. Get the current branch (e.g., "master" or "main") let branch = readmeDialog.addon.currentBranch; if (branch === "") branch = "master"; // Fallback // 3. Construct the GitHub raw content link // Result: https://github.com/user/repo/raw/master/ return cleanUrl + "/raw/" + branch + "/"; } return ""; } onLinkActivated: function(link) { Qt.openUrlExternally(link) } } } } } Dialog { property Addon addon property alias newUrlText: urlInput.text id: urlDialog parent: window.overlay x: (parent.width - width) / 2 y: (parent.height - height) / 2 width: Math.min(500, parent.width * 0.75) title: addon ? qsTr("Change URL for %1").arg(addon.name) : "" standardButtons: Dialog.Save | Dialog.Cancel onAccepted: { if (addon && urlInput.text !== "") { addon.setRemoteUrl(urlInput.text) } } ColumnLayout { anchors.fill: parent Label { text: "New Git Repository URL:" } TextField { id: urlInput Layout.fillWidth: true selectByMouse: true placeholderText: "https://github.com/user/repo.git" } } } }