From f5f7eae4a539a87fa5e7c9f86d087c2930da32a7 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Tue, 25 Jun 2024 04:27:32 -0500 Subject: [PATCH 01/22] Initial Commit. Signed-off-by: Armored Dragon --- applications/metadata.js | 9 + applications/more/img/icon_black.png | Bin 0 -> 521 bytes applications/more/img/icon_white.png | Bin 0 -> 511 bytes applications/more/menu.svg | 38 ++ applications/more/more.js | 231 +++++++++ applications/more/more.qml | 713 +++++++++++++++++++++++++++ 6 files changed, 991 insertions(+) create mode 100644 applications/more/img/icon_black.png create mode 100644 applications/more/img/icon_white.png create mode 100644 applications/more/menu.svg create mode 100644 applications/more/more.js create mode 100644 applications/more/more.qml diff --git a/applications/metadata.js b/applications/metadata.js index 6507bad..4ad5b5e 100644 --- a/applications/metadata.js +++ b/applications/metadata.js @@ -333,6 +333,15 @@ var metadata = { "applications": "icon": "domainMapper/icon_inactive_white.png", "caption": "DOMAP" }, + { + "isActive": true, + "directory": "more", + "name": "MoreNG", + "description": "More app rewrite in QML. Please see documentation https://github.com/overte-org/community-apps/tree/master/applications/more for more information", + "jsfile": "more/more.js", + "icon": "more/img/icon_white.png", + "caption": "MORE-NG" + }, { "isActive": true, "directory": "hmd3rdPerson", diff --git a/applications/more/img/icon_black.png b/applications/more/img/icon_black.png new file mode 100644 index 0000000000000000000000000000000000000000..b62d59774686a43d1f07bf0d0f208aa5cd8fdf98 GIT binary patch literal 521 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?3oVGw3ym^DWND9DoT z=pfi@Ln>}1{rUgj{%`?M;0TkFgrsEWM_y$y!>ZSZxf~`k3OM-i9A;r)xv=3Q zhXVtzbU)F|jT3IkC~TK>RIRmO1nQhE!N{>7@v&e7Lz3izus8Kt>h=u`Qf7us0uG0d zDKjvddZx*)#8?*GBY&lrIGBWR8Zc|H9V zgF-{Zi#d&%VFnB=0=izhZdS9|zo|GdTwMIsTbx0_Vapo#f0wN+6^lUzOo@V7iDWxE z)8%wk%L=KPQMxQJr(VhX`)_%fhwyDD1`dU3j??Cx_+|t0+!9t4Z$J#1@Hi^uttS&O v>^gzomggTe~DWM4fUFo6; literal 0 HcmV?d00001 diff --git a/applications/more/img/icon_white.png b/applications/more/img/icon_white.png new file mode 100644 index 0000000000000000000000000000000000000000..e765410b50e836051e64a1064681bbc9504db3a9 GIT binary patch literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?3oVGw3ym^DWND9DoT z=}1{rUgj{%`?M;0V*w&X2sxVun?(4|6gwb#x}qU}#{FGLvKESdjQw zkXWXISkWxzCuYhk7VxVx0Cfs0GccOu*f0q=9A5U78%V0TF)(G^s9;fOXzO(c+S7XO z+h6ry-GhICN_3b7DtfkjuyJ7EoxOw=#LxI%pLI+I#Fs#IM`PCCAXx?$0q(iR24}h0 z8W77m4~Ep;!MfM!qodZIo`T|I>lWIzx%)Ji1VF_;;V{Q{xN!K+&t zpib?$wf6r1D}0$r(Hy`qGCDCy_~txzpuZ+>LGcE}Ahx2I?p3y)ps+FodWTt1+2o}h kGmF6K06)JI3mdKI;Vst04I=|C;$Ke literal 0 HcmV?d00001 diff --git a/applications/more/menu.svg b/applications/more/menu.svg new file mode 100644 index 0000000..8d3273d --- /dev/null +++ b/applications/more/menu.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/applications/more/more.js b/applications/more/more.js new file mode 100644 index 0000000..250fa8e --- /dev/null +++ b/applications/more/more.js @@ -0,0 +1,231 @@ +// +// more.js +// +// Easily install additional functionality from repositories online +// +// Created by Armored Dragon, 2024. +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +(() => { + "use strict"; + + // TODO: Preinstall Overte community apps by default + var installed_scripts = Settings.getValue("ArmoredMore-InstalledScripts", []) || []; // All scripts installed though more.js + var installed_repositories = Settings.getValue("ArmoredMore-InstalledRepositories", []) || []; // All repositories installed though more.js + + // Global vars + var tablet; + var app_button; + var active = false; + + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + + app_button = tablet.addButton({ + icon: Script.resolvePath("./img/icon_white.png"), + activeIcon: Script.resolvePath("./img/icon_black.png"), + text: "MORE", + isActive: active, + }); + // When script ends, remove itself from tablet + Script.scriptEnding.connect(function () { + console.log("Shutting Down"); + tablet.removeButton(app_button); + }); + + // Overlay button toggle + app_button.clicked.connect(toolbarButtonClicked); + + tablet.fromQml.connect(fromQML); + + function toolbarButtonClicked() { + if (active) { + tablet.gotoHomeScreen(); + active = !active; + app_button.editProperties({ + isActive: active, + }); + } else { + getLists(); + tablet.loadQMLSource(Script.resolvePath("./more.qml")); + active = !active; + app_button.editProperties({ + isActive: active, + }); + } + } + + function installApp({ title, repository, url, icon, description }) { + // Add script to saved list + installed_scripts.push({ + title: title, + repository: repository, + url: url, + icon: icon, + description: description, + }); + + // Save new list as setting + Settings.setValue("ArmoredMore-InstalledScripts", installed_scripts); + + // Install the script + ScriptDiscoveryService.loadScript(url, true); // Force reload the script, do not use cache. + + // Send updated app list + getLists(); + } + + function uninstallApp(url) { + // Find app in saved list + var entry = installed_scripts.filter((app) => app.url == url); + const index = installed_scripts.indexOf(entry); + + // Remove it from list + installed_scripts.splice(index, 1); + + // Save new list as setting + Settings.setValue("ArmoredMore-InstalledScripts", installed_scripts); + + // Uninstall the script + ScriptDiscoveryService.stopScript(url, false); + + // Send updated app list + getLists(); + } + + // TODO: Duplication check + async function installRepo(url) { + // Test repository + const repo = await request(url); + if (!repo) return; // Failure + + // Add repo to saved list + installed_repositories.push({ + title: repo.title || "Unnamed repository", + url: url, + }); + + // Save new list as setting + Settings.setValue("ArmoredMore-InstalledRepositories", installed_repositories); + + // Send updated repository list + getLists(); + } + function uninstallRepo(url) { + // Find app in saved list + var entry = installed_repositories.filter((repo) => repo.url == url); + const index = installed_repositories.indexOf(entry); + + // Remove it from list + installed_repositories.splice(index, 1); + + // Save new list as setting + Settings.setValue("ArmoredMore-InstalledRepositories", installed_repositories); + + // Send updated app list + getLists(); + } + + // Startup populate lists + async function getLists() { + let application_list = []; + let installed_apps_by_url = installed_scripts.map((app) => app.url); + + for (let i = 0; installed_repositories.length > i; i++) { + let repo = installed_repositories[i]; + let apps = await request(repo.url); + if (!apps) continue; // Failure + + apps = apps.application_list || []; + + // Filter to non-installed ones + apps = apps.filter((app) => { + let app_root = repo.url.replace(/\/metadata.json/g, "") + `/${app.directory}`; + + let script_url = app_root + `/${app.script}`; + + return installed_apps_by_url.indexOf(script_url) == -1; + }); + + apps = apps.map((app) => { + let app_root = repo.url.replace(/\/metadata.json/g, "") + `/${app.directory}`; + + let script_url = app_root + `/${app.script}`; + let script_icon = app_root + `/${app.icon}`; + + return { + title: app.name, + description: app.description, + icon: script_icon, + repository: repo.title, + url: script_url, + }; + }); + + // Add all apps from repo to list + application_list.push(...apps); + } + + _emitEvent({ + type: "installed_apps", + app_list: [ + ...installed_scripts.map((app) => { + return { ...app, installed: true }; + }), + ...application_list, + ], + }); + + _emitEvent({ + type: "installed_repositories", + repository_list: installed_repositories, + }); + } + + async function request(url) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", url, false); + xmlHttp.send(null); + + // Any request we make is intended to be a JSON response. + // If it can not be parsed into JSON then fail. + try { + return JSON.parse(xmlHttp.responseText); + } catch { + return false; + } + } + + function fromQML(event) { + console.log(`New QML event:\n${JSON.stringify(event)}`); + + switch (event.type) { + case "initialized": + getLists(); + break; + case "install_application": + installApp(event); + break; + case "remove_application": + uninstallApp(event.url); + break; + case "install_repo": + installRepo(event.url); + break; + case "remove_repo": + uninstallRepo(event.url); + break; + } + } + + /** + * Emit a packet to the HTML front end. Easy communication! + * @param {Object} packet - The Object packet to emit to the HTML + * @param {("show_message"|"clear_messages"|"notification"|"initial_settings")} packet.type - The type of packet it is + */ + function _emitEvent(packet = { type: "" }) { + tablet.sendToQml(packet); + } +})(); diff --git a/applications/more/more.qml b/applications/more/more.qml new file mode 100644 index 0000000..6b98b42 --- /dev/null +++ b/applications/more/more.qml @@ -0,0 +1,713 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import controlsUit 1.0 as HifiControlsUit + +Rectangle { + color: Qt.rgba(0.1,0.1,0.1,1) + signal sendToScript(var message); + width: 200 + height: 700 + id: root + + property string current_page: "app_list" + property string last_message_user: "" + property date last_message_time: new Date() + + Timer { + interval: 10 + running: true + repeat: false + onTriggered: { + toScript({type: "initialized"}); + } + } + + // User view + Item { + anchors.fill: parent + + // Navigation Bar + Rectangle { + id: navigation_bar + width: parent.width + height: 60 + color: Qt.rgba(0,0,0,1) + visible: ["app_list", "repos"].includes(current_page) ? true : false + + Item { + anchors.centerIn: parent + width: parent.width - 10 + height: parent.height - 25 + + Rectangle { + color: "white" + width: parent.width - 100 + anchors.verticalCenter: parent.verticalCenter + height: parent.height + radius: 5 + + TextInput { + width: parent.width - 10 + color: "black" + font.pointSize: 12 + anchors.centerIn: parent + id: search_query + onAccepted: { + if (current_page == "app_list"){ + searchList(search_query.text, installed_apps); + return; + } + if (current_page == "repos"){ + searchList(search_query.text, repo_list); + return; + } + } + } + + Text { + color: "Gray" + font.pointSize: 10 + anchors.verticalCenter: parent.verticalCenter + x: 5 + text: "Search..." + font.italic: true + visible: parent.children[0].text == "" + } + } + + Rectangle { + color: "#296992" + width: parent.width - parent.children[0].width - 10 + anchors.verticalCenter: parent.verticalCenter + height: parent.height + radius: 5 + anchors.right: parent.right + + Image { + source: "menu.svg" + anchors.centerIn: parent + sourceSize.width: 20 + sourceSize.height: 20 + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (root.current_page == "app_list") { + root.current_page = "repos" + return; + } + + if (root.current_page == "repos") { + root.current_page = "app_list" + return; + } + } + } + } + } + } + + // Pages ---- + + // Installed Apps + Item { + width: parent.width + height: parent.height - 40 + anchors.top: navigation_bar.bottom + visible: current_page == "app_list" + + // Installed Apps + ListView { + property int index_selected: -1 + width: parent.width + height: parent.height - 60 + clip: true + interactive: true + spacing: 5 + id: installed_apps_list + model: installed_apps + + delegate: Loader { + property int delegateIndex: index + property string delegateTitle: model.title + property string delegateRepository: model.repository + property string delegateDescription: model.description + property string delegateIcon: model.icon + property string delegateURL: model.url + property bool delegateInstalled: model.installed + property bool delegateIsVisible: model.is_visible + width: installed_apps_list.width + + sourceComponent: app_listing + } + } + + ListModel { + id: installed_apps + } + } + + // Repository Manager + Item { + width: parent.width + height: parent.height - 40 + anchors.top: navigation_bar.bottom + visible: current_page == "repos" + + Rectangle { + height: 70 + width: parent.width + color: "#111111" + + Item { + width: parent.width - 10 + height: parent.height + anchors.horizontalCenter: parent.horizontalCenter + + + Text{ + text: "Add a new repository" + color: "White" + font.pointSize: 12 + wrapMode: Text.WordWrap + height: 30 + } + + Rectangle{ + width: parent.width - 70 + height: 30 + radius: 5 + anchors.top: parent.children[0].bottom + + TextInput { + width: parent.width - 10 + color: "black" + font.pointSize: 12 + anchors.centerIn: parent + id: repo_url + } + + Text { + color: "Gray" + font.pointSize: 10 + anchors.verticalCenter: parent.verticalCenter + x: 5 + text: "Add a manifest.json url" + font.italic: true + visible: parent.children[0].text == "" + } + } + + Rectangle { + anchors.top: parent.children[0].bottom + width: parent.width - parent.children[1].width - 10 + anchors.right: parent.right + height: 30 + color: "green" + radius: 5 + + Text { + text: "+" + color: "White" + font.pointSize: 14 + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + + onClicked: { + installNewRepository(repo_url.text); + repo_url.text = ""; + } + } + } + } + + } + + ListView { + property int index_selected: -1 + width: parent.width + height: parent.height - 60 + clip: true + interactive: true + spacing: 5 + id: registered_repo_list + model: repo_list + anchors.top: parent.children[0].bottom + delegate: Loader { + property int delegateIndex: index + property string delegateTitle: model.title + property string delegateURL: model.url + property bool selected: false + property bool delegateIsVisible: model.is_visible + + width: registered_repo_list.width + + sourceComponent: repo_listing + } + } + ListModel { + id: repo_list + } + } + + + // Go back button from app details + Rectangle { + id: go_back_button + width: parent.width + height: 60 + color: Qt.rgba(0,0,0,1) + visible: current_page == "details" + + Rectangle { + width: parent.width - 20 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + height: 35 + radius: 5 + color: "#771d1d" + + Text { + color: "white" + font.pointSize: 12 + anchors.centerIn: parent + text: "Back" + } + + MouseArea { + anchors.fill: parent + + onClicked: { + current_page = "app_list" + } + } + } + } + + // App Details + Item { + width: parent.width - 20 + height: parent.height - 40 + anchors.top: navigation_bar.bottom + visible: current_page == "details" + anchors.horizontalCenter: parent.horizontalCenter + + Item { + width: parent.width + height: 100 + y: 10 + + + Rectangle{ + width: 100 + height: 100 + radius: 5 + + Rectangle { + color: "black" + width: 96 + height: 96 + radius: 5 + anchors.centerIn: parent + + Image { + id: details_icon + width: 90 + height: 90 + anchors.centerIn: parent + } + } + } + + Text { + x: parent.children[0].width + 10 + text: "" + color:"white"; + font.pointSize: 14 + id: details_title + } + + Text { + x: parent.children[0].width + 10 + y: parent.children[1].height + 5 + text: "" + color: "gray"; + font.pointSize: 10 + id: details_repo_url + } + } + + Item { + width: parent.width + anchors.top: parent.children[0].bottom + + Text{ + text: "" + color: "white"; + font.pointSize: 12 + y: 20 + id: details_description + } + } + + } + } + + // Templates + Component { + id: app_listing + + Rectangle { + property int index: delegateIndex + property string title: delegateTitle + property string repo: delegateRepository + property string description: delegateDescription + property string icon: delegateIcon + property string url: delegateURL + property bool installed: delegateInstalled + property bool is_visible: delegateIsVisible + + property bool selected: (installed_apps_list.index_selected == index) + + visible: is_visible + height: is_visible ? selected ? 100 : 60 : 0 + width: parent.width + + color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + + Behavior on height { + NumberAnimation { + duration: 100 + } + } + + Item { + width: parent.width - 10 + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height + clip: true + + // Icon + Rectangle { + width: 50 + height: 50 + radius: 5 + color: installed ? "#505186" : "white" + y: 5 + + Rectangle{ + anchors.centerIn: parent + width: 46 + height: 46 + radius: 5 + color: "black" + + Image { + source: icon + anchors.centerIn: parent + sourceSize.width: 40 + sourceSize.height: 40 + } + } + } + + // App info + Item { + width: parent.width - parent.children[0].width - 50 + x: parent.children[0].width + 10 + height: 20 + + Text { + width: parent.width + height: 20 + text: title + color: "white" + font.pointSize: 12 + wrapMode: Text.NoWrap + elide: Text.ElideRight + } + Text { + width: parent.width + height: 20 + text: repo + color: "gray" + font.pointSize: 10 + anchors.top: parent.children[0].bottom + } + } + + // Action Buttons + Item { + width: parent.width + height: 30 + + y: 65 + visible: selected ? true : false + + + Rectangle { + width: 120 + height: parent.height + radius: 5 + color: "#771d1d" + visible: installed + + Text{ + text: "Uninstall" + anchors.centerIn: parent + color:"white" + } + + MouseArea { + anchors.fill: parent + + onClicked: { + removeApp(url); + } + } + + } + + Rectangle { + width: 120 + height: parent.height + radius: 5 + color: "#00930f" + visible: !installed + + Text{ + text: "Install" + anchors.centerIn: parent + color:"white" + } + + MouseArea { + anchors.fill: parent + + onClicked: { + installNewApp(title, url, repo, description, icon); + } + } + } + + Rectangle { + width: 120 + height: parent.height + radius: 5 + color: "#505186" + x: parent.children[0].width + 5 + + Text { + text: "Details" + anchors.centerIn: parent + color:"white" + } + + MouseArea { + anchors.fill: parent + + onClicked: { + openAppDetails(title, url, repo, description, icon); + } + } + } + } + + MouseArea { + width: parent.width + height: 60 + + onClicked: { + if (installed_apps_list.index_selected == index){ + installed_apps_list.index_selected = -1; + return; + } + + installed_apps_list.index_selected = index + } + } + + } + } + } + + Component { + id: repo_listing + + Rectangle { + property int index: delegateIndex + property string title: delegateTitle + property string url: delegateURL + property bool is_visible: delegateIsVisible + + property bool selected: (registered_repo_list.index_selected == index) + + height: selected ? 70 : 40 + width: parent.width + visible: is_visible + color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + clip: true + + Behavior on height { + NumberAnimation { + duration: 100 + easing.type: Easing.OutQuad + } + } + + Item { + width: parent.width - 10 + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height + + // Repo Info + Text { + width: parent.width + height: 20 + text: title + color: "white" + font.pointSize: 12 + wrapMode: Text.noWrap + elide: Text.ElideRight + } + Text { + width: parent.width + height: 20 + anchors.top: parent.children[0].bottom + text: url + color: "gray" + font.pointSize: 10 + wrapMode: Text.noWrap + elide: Text.ElideRight + } + + // Action Buttons + Item { + height: selected ? 30 : 0 + width: parent.width + anchors.top: parent.children[1].bottom + visible: selected ? true : false + + Rectangle { + width: 120 + height: parent.height + radius: 5 + color: "#771d1d" + + Text{ + text: "Remove" + anchors.centerIn: parent + color:"white" + } + + MouseArea { + anchors.fill: parent + + onClicked: { + removeRepository(url); + } + } + } + } + + } + + MouseArea { + width: parent.width + height: 40 + onClicked: { + if (registered_repo_list.index_selected == index){ + registered_repo_list.index_selected = -1; + return; + } + + registered_repo_list.index_selected = index + } + } + + } + } + + // List population and management + function addApplicationToList(name, repo_name, description, installed, url){ + + } + function clearApplicationList(){ + installed_apps.clear() + } + function addRepositoryToList(repo_name, url){ + + } + function clearRepositoryList(){ + repo_list.clear() + } + + // Funcionality + function installNewRepository(url){ + toScript({type: "install_repo", url: url}); + } + function removeRepository(url){ + toScript({type: "remove_repo", url: url}); + } + function installNewApp(title, url, repository, description, icon){ + toScript({type: "install_application", title: title, url: url, repository: repository, description: description, icon: icon}); + } + function removeApp(url){ + toScript({type: "remove_application", url: url}); + } + + // Searching + function searchList(text, element){ + + for (var i = 0; i < element.count; i++) { + var app = element.get(i); + + var is_found = app.title.toLowerCase().includes(text.toLowerCase()) || app.description.toLowerCase().includes(text.toLowerCase()) || app.url.toLowerCase().includes(text.toLowerCase()) + + if (!app.title.toLowerCase().includes(text.toLowerCase())){ + app.is_visible = false; + } + else { + app.is_visible = true + } + + } + } + + // App Details page + function openAppDetails(title, url, repo, description, icon){ + current_page = "details"; + details_title.text = title; + details_repo_url.text = repo; + details_description.text = description; + details_icon.source = icon; + } + + // Messages from script + function fromScript(message) { + switch (message.type){ + case "installed_apps": + clearApplicationList(); + message.app_list.forEach((app) => installed_apps.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: app.installed || false, is_visible: true })) + break; + case "installed_repositories": + clearRepositoryList(); + message.repository_list.forEach((repo) => repo_list.append({ title: repo.title, url: repo.url, is_visible: true })) + break; + case "clear_messages": + break; + case "initial_settings": + break; + } + } + + // Send message to script + function toScript(packet){ + sendToScript(packet) + } +} From 26be09eb20ffde1b010181df8408e317d8167374 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Wed, 26 Jun 2024 04:36:16 -0500 Subject: [PATCH 02/22] Hover effects. Removed HiFi controlsUit. Signed-off-by: Armored Dragon --- applications/more/more.qml | 42 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 6b98b42..8c0a894 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -1,7 +1,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 -import controlsUit 1.0 as HifiControlsUit Rectangle { color: Qt.rgba(0.1,0.1,0.1,1) @@ -63,6 +62,17 @@ Rectangle { return; } } + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: parent.parent.children[1].x = 15 + onExited: parent.parent.children[1].x = 5 + + onClicked: (mouse) => { + mouse.accepted = false + parent.forceActiveFocus() // Hack? Maybe see if this can be better done another way + } + } } Text { @@ -73,7 +83,15 @@ Rectangle { text: "Search..." font.italic: true visible: parent.children[0].text == "" + + Behavior on x { + NumberAnimation { + duration: 100 + } + } + } + } Rectangle { @@ -93,6 +111,10 @@ Rectangle { MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: parent.color = "#122F41" + onExited: parent.color = "#296992" + onClicked: { if (root.current_page == "app_list") { root.current_page = "repos" @@ -125,7 +147,6 @@ Rectangle { height: parent.height - 60 clip: true interactive: true - spacing: 5 id: installed_apps_list model: installed_apps @@ -522,6 +543,14 @@ Rectangle { width: parent.width height: 60 + hoverEnabled: true + onEntered: { + parent.parent.color = "#111111" + } + onExited: { + parent.parent.color = parent.parent.index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + } + onClicked: { if (installed_apps_list.index_selected == index){ installed_apps_list.index_selected = -1; @@ -620,6 +649,15 @@ Rectangle { MouseArea { width: parent.width height: 40 + + hoverEnabled: true + onEntered: { + parent.color = "#111111" + } + onExited: { + parent.color = parent.index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + } + onClicked: { if (registered_repo_list.index_selected == index){ registered_repo_list.index_selected = -1; From a616785558d99ef5c97917fa494749859b0316f8 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Wed, 26 Jun 2024 05:58:54 -0500 Subject: [PATCH 03/22] More hover effects. Signed-off-by: Armored Dragon --- applications/more/more.qml | 46 ++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 8c0a894..82f656a 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -69,7 +69,6 @@ Rectangle { onExited: parent.parent.children[1].x = 5 onClicked: (mouse) => { - mouse.accepted = false parent.forceActiveFocus() // Hack? Maybe see if this can be better done another way } } @@ -208,6 +207,17 @@ Rectangle { font.pointSize: 12 anchors.centerIn: parent id: repo_url + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: parent.parent.children[1].x = 15 + onExited: parent.parent.children[1].x = 5 + + onClicked: (mouse) => { + parent.forceActiveFocus() // Hack? Maybe see if this can be better done another way + } + } } Text { @@ -218,6 +228,11 @@ Rectangle { text: "Add a manifest.json url" font.italic: true visible: parent.children[0].text == "" + Behavior on x { + NumberAnimation { + duration: 100 + } + } } } @@ -238,7 +253,10 @@ Rectangle { MouseArea { anchors.fill: parent - + hoverEnabled: true + onEntered: parent.color = "#004D00" + onExited: parent.color = "green" + onClicked: { installNewRepository(repo_url.text); repo_url.text = ""; @@ -303,6 +321,10 @@ Rectangle { MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: parent.color = "#471111" + onExited: parent.color = "#771d1d" + onClicked: { current_page = "app_list" } @@ -487,6 +509,10 @@ Rectangle { MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: parent.color = "#471111" + onExited: parent.color = "#771d1d" + onClicked: { removeApp(url); } @@ -510,6 +536,10 @@ Rectangle { MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: parent.color = "#005809" + onExited: parent.color = "#00930f" + onClicked: { installNewApp(title, url, repo, description, icon); } @@ -532,6 +562,10 @@ Rectangle { MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: parent.color = "#303150" + onExited: parent.color = "#505186" + onClicked: { openAppDetails(title, url, repo, description, icon); } @@ -628,7 +662,7 @@ Rectangle { radius: 5 color: "#771d1d" - Text{ + Text { text: "Remove" anchors.centerIn: parent color:"white" @@ -637,6 +671,10 @@ Rectangle { MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: parent.color = "#471111" + onExited: parent.color = "#771d1d" + onClicked: { removeRepository(url); } @@ -664,7 +702,7 @@ Rectangle { return; } - registered_repo_list.index_selected = index + registered_repo_list.index_selected = index; } } From 63594a46965f1843a7879ca1eaf6d10d772650a1 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 27 Jun 2024 12:58:25 -0500 Subject: [PATCH 04/22] Added navigation page. This removed the direct link to the app repository from the app list, and moved it to the new navigation page. Signed-off-by: Armored Dragon --- applications/more/more.qml | 207 +++++++++++++++++++++++++++++-------- 1 file changed, 162 insertions(+), 45 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 82f656a..5a32990 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -32,7 +32,7 @@ Rectangle { width: parent.width height: 60 color: Qt.rgba(0,0,0,1) - visible: ["app_list", "repos"].includes(current_page) ? true : false + visible: current_page == "app_list" Item { anchors.centerIn: parent @@ -115,21 +115,51 @@ Rectangle { onExited: parent.color = "#296992" onClicked: { - if (root.current_page == "app_list") { - root.current_page = "repos" - return; - } - - if (root.current_page == "repos") { - root.current_page = "app_list" - return; - } + root.current_page = "page_selection" } } } } } + // Go back button from app details + Rectangle { + id: go_back_button + width: parent.width + height: 60 + color: Qt.rgba(0,0,0,1) + visible: current_page != "app_list" + + Rectangle { + width: parent.width - 20 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + height: 35 + radius: 5 + color: "#771d1d" + + Text { + color: "white" + font.pointSize: 12 + anchors.centerIn: parent + text: "Back" + } + + MouseArea { + anchors.fill: parent + + hoverEnabled: true + onEntered: parent.color = "#471111" + onExited: parent.color = "#771d1d" + + onClicked: { + if (current_page == "page_selection") return current_page = "app_list"; + current_page = "page_selection" + } + } + } + } + // Pages ---- // Installed Apps @@ -169,6 +199,128 @@ Rectangle { } } + // Page selection + Item { + width: parent.width + height: parent.height - 40 + anchors.top: navigation_bar.bottom + visible: current_page == "page_selection" + + // Installed Apps + ListView { + property int index_selected: -1 + width: parent.width + height: parent.height - 60 + clip: true + interactive: true + model: ListModel { + + // TODO: + // ListElement { + // name: "Installed Apps" + // description: "View a list of applications installed" + // page_name: "installed_apps" + // } + ListElement { + name: "Repository Manager" + description: "Manage your list of repositories" + page_name: "repos" + } + } + + delegate: Component { + Rectangle { + width: parent.width + height: 60 + color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + + Item { + height: parent.height + width: parent.width - 40 + + Behavior on x { + NumberAnimation { + duration: 150 + } + } + + Text { + width: parent.width - 50 + y: 5 + height: 30 + text: name + font.pixelSize: 16 + color: "white" + anchors.horizontalCenter: parent.horizontalCenter + font.italic: true + } + Text { + width: parent.width - 50 + height: 15 + anchors.top: parent.children[0].bottom + text: description + font.pixelSize: 12 + color: "white" + anchors.horizontalCenter: parent.horizontalCenter + font.italic: true + } + } + + Text { + width: 50 + height: parent.height + text: ">" + color: "transparent" + x: parent.width - 150 + font.pixelSize: 40 + anchors.verticalCenter: parent.verticalCenter + + Behavior on x { + NumberAnimation { + duration: 150 + } + } + Behavior on color { + ColorAnimation { + duration: 150 + } + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onEntered: { + parent.color = "#111111" + + parent.children[0].x = parent.children[0].x + 20 + + // Arrow + parent.children[1].x = parent.width - 50 + parent.children[1].color = "white" + } + onExited: { + parent.color = index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + + parent.children[0].x = 0 + + // Arrow + parent.children[1].x = parent.width - 150 + parent.children[1].color = "transparent" + } + + onClicked: (mouse) => { + current_page = page_name + } + } + } + } + } + + + } + // Repository Manager Item { width: parent.width @@ -295,42 +447,7 @@ Rectangle { } - // Go back button from app details - Rectangle { - id: go_back_button - width: parent.width - height: 60 - color: Qt.rgba(0,0,0,1) - visible: current_page == "details" - Rectangle { - width: parent.width - 20 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - height: 35 - radius: 5 - color: "#771d1d" - - Text { - color: "white" - font.pointSize: 12 - anchors.centerIn: parent - text: "Back" - } - - MouseArea { - anchors.fill: parent - - hoverEnabled: true - onEntered: parent.color = "#471111" - onExited: parent.color = "#771d1d" - - onClicked: { - current_page = "app_list" - } - } - } - } // App Details Item { From 367b0c21289e34aba5db1a1caea5c685ae70c7b5 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 27 Jun 2024 14:03:32 -0500 Subject: [PATCH 05/22] Added "Installed Apps" view. The same thing as the default view, but only lists applications that were installed. Signed-off-by: Armored Dragon --- applications/more/more.qml | 86 ++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 5a32990..d24f00a 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -154,6 +154,7 @@ Rectangle { onClicked: { if (current_page == "page_selection") return current_page = "app_list"; + if (current_page == "details") return current_page = "app_list"; current_page = "page_selection" } } @@ -162,13 +163,50 @@ Rectangle { // Pages ---- - // Installed Apps + // Apps Listing Item { width: parent.width height: parent.height - 40 anchors.top: navigation_bar.bottom visible: current_page == "app_list" + // Installed Apps + ListView { + property int index_selected: -1 + width: parent.width + height: parent.height - 60 + clip: true + interactive: true + id: app_listing_list + model: app_listings + + delegate: Loader { + property int delegateIndex: index + property string delegateTitle: model.title + property string delegateRepository: model.repository + property string delegateDescription: model.description + property string delegateIcon: model.icon + property string delegateURL: model.url + property bool delegateInstalled: model.installed + property bool delegateIsVisible: model.is_visible + width: app_listing_list.width + + sourceComponent: app_listing + } + } + + ListModel { + id: app_listings + } + } + + // Installed Apps + Item { + width: parent.width + height: parent.height - 40 + anchors.top: navigation_bar.bottom + visible: current_page == "installed_app_list" + // Installed Apps ListView { property int index_selected: -1 @@ -215,18 +253,20 @@ Rectangle { interactive: true model: ListModel { - // TODO: - // ListElement { - // name: "Installed Apps" - // description: "View a list of applications installed" - // page_name: "installed_apps" - // } - ListElement { - name: "Repository Manager" - description: "Manage your list of repositories" - page_name: "repos" + // TODO: + ListElement { + name: "Installed Apps" + description: "View a list of applications installed" + page_name: "installed_app_list" + } + ListElement { + name: "Repository Manager" + description: "Manage your list of repositories" + page_name: "repos" + } + + } - } delegate: Component { Rectangle { @@ -317,8 +357,6 @@ Rectangle { } } } - - } // Repository Manager @@ -446,9 +484,6 @@ Rectangle { } } - - - // App Details Item { width: parent.width - 20 @@ -532,7 +567,7 @@ Rectangle { property bool installed: delegateInstalled property bool is_visible: delegateIsVisible - property bool selected: (installed_apps_list.index_selected == index) + property bool selected: (app_listing_list.index_selected == index) visible: is_visible height: is_visible ? selected ? 100 : 60 : 0 @@ -703,12 +738,12 @@ Rectangle { } onClicked: { - if (installed_apps_list.index_selected == index){ - installed_apps_list.index_selected = -1; + if (app_listing_list.index_selected == index){ + app_listing_list.index_selected = -1; return; } - installed_apps_list.index_selected = index + app_listing_list.index_selected = index } } @@ -831,6 +866,7 @@ Rectangle { } function clearApplicationList(){ + app_listings.clear() installed_apps.clear() } function addRepositoryToList(repo_name, url){ @@ -886,7 +922,13 @@ Rectangle { switch (message.type){ case "installed_apps": clearApplicationList(); - message.app_list.forEach((app) => installed_apps.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: app.installed || false, is_visible: true })) + message.app_list.forEach((app) => { + app_listings.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: app.installed || false, is_visible: true }); + + if (app.installed){ + installed_apps.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: true, is_visible: true }); + } + }) break; case "installed_repositories": clearRepositoryList(); From 9be42b0b493986b27105187c120aceb175d24618 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 28 Jun 2024 23:58:12 -0500 Subject: [PATCH 06/22] Fix missing onScreenChanged event listener. Signed-off-by: Armored Dragon --- applications/more/more.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/applications/more/more.js b/applications/more/more.js index 250fa8e..e30e869 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -22,6 +22,7 @@ var active = false; tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + tablet.screenChanged.connect(onScreenChanged); app_button = tablet.addButton({ icon: Script.resolvePath("./img/icon_white.png"), @@ -184,6 +185,15 @@ }); } + function onScreenChanged(type, url) { + if (url != Script.resolvePath("./more.qml")) { + active = false; + app_button.editProperties({ + isActive: active, + }); + } + } + async function request(url) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open("GET", url, false); From 71819cc61e9b458500443ba61a480f79c26adc41 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 18 Jul 2024 11:36:13 -0500 Subject: [PATCH 07/22] Community-apps support. Adds hardcoded support for the current community-apps metadata.js file. Signed-off-by: Armored Dragon --- applications/more/more.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/applications/more/more.js b/applications/more/more.js index e30e869..92bf04b 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -136,10 +136,10 @@ for (let i = 0; installed_repositories.length > i; i++) { let repo = installed_repositories[i]; - let apps = await request(repo.url); - if (!apps) continue; // Failure + let repo_content = await request(repo.url); + if (!repo_content) continue; // Failure - apps = apps.application_list || []; + let apps = repo_content.application_list || []; // Filter to non-installed ones apps = apps.filter((app) => { @@ -151,7 +151,7 @@ }); apps = apps.map((app) => { - let app_root = repo.url.replace(/\/metadata.json/g, "") + `/${app.directory}`; + let app_root = repo_content.base_url + `/${app.directory}`; let script_url = app_root + `/${app.script}`; let script_icon = app_root + `/${app.icon}`; @@ -199,6 +199,30 @@ xmlHttp.open("GET", url, false); xmlHttp.send(null); + // Hardcode support for Overte Community-Apps metadata.js + // This can be safely removed at some point in the far future. 7/18/2024 + if (url === "https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js") { + // Scary text formatting to get the metadata.js response object into a JSON object. + var formatted_response = xmlHttp.responseText.replace("var metadata = ", "").slice(0, -1).trim(); + + // Extract the application list. + var application_list = JSON.parse(formatted_response).applications; + + // Convert each entry into a value we expect it to be. + application_list = application_list.map((app_entry) => { + return { + name: app_entry.name, + directory: app_entry.directory, + script: app_entry.jsfile.replace(`${app_entry.directory}/`, ""), + icon: app_entry.icon.replace(`${app_entry.directory}/`, ""), + description: app_entry.description, + }; + }); + + // Return the formatted list along with extra repository information. + return { title: "Overte", base_url: "https://raw.githubusercontent.com/overte-org/community-apps/master/applications", application_list: application_list }; + } + // Any request we make is intended to be a JSON response. // If it can not be parsed into JSON then fail. try { From ad2b9c08519782ba5624b33e19653660649f560c Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 18 Jul 2024 11:40:16 -0500 Subject: [PATCH 08/22] Fix app description overflow on details page. Signed-off-by: Armored Dragon --- applications/more/more.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/more/more.qml b/applications/more/more.qml index d24f00a..348bb2e 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -544,6 +544,8 @@ Rectangle { Text{ text: "" color: "white"; + wrapMode: Text.WordWrap + width: parent.width font.pointSize: 12 y: 20 id: details_description From 9d62db785fe14b1bcf034b3d8b7e8c975c66ec26 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 18 Jul 2024 13:48:23 -0500 Subject: [PATCH 09/22] Documentation. Signed-off-by: Armored Dragon --- applications/more/README.md | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 applications/more/README.md diff --git a/applications/more/README.md b/applications/more/README.md new file mode 100644 index 0000000..0e70c89 --- /dev/null +++ b/applications/more/README.md @@ -0,0 +1,69 @@ +# More +1. What is More +2. User manual + - Installation + - Adding Repositories + - Usability tips +3. Development + - How to create your own repository + +## What is More +More is an application that allows users to quickly install additional applications hosted from online repositories. + +## User Manual +### Installation +**Recommended method**: + +To install this application, visit the pre-installed More application provided inside of Overte. + +Inside of the pre installed More application, search for the app that is also called "More". +Click the "Install" button and the More application will be installed. +You will also need to add the url Add `https://raw.githubusercontent.com/overte-org/community-apps/master/applications/more/more.qml` to the QML whitelist manually. + +**Alternative**: + +You can install More manually by following these instructions: +1. In Interface, go to Edit > Running Scripts. +2. Load the script url: `https://raw.githubusercontent.com/overte-org/community-apps/master/applications/more/more.js` +3. Add `https://raw.githubusercontent.com/overte-org/community-apps/master/applications/more/more.qml` to the QML whitelist. + +### Adding Repositories +To add a repository, navigate to the application menu by pressing the top right most button with the hamburger icon. +From there, click the section labeled "Repository Manager". In the text field labeled "Add a manifest.json url", paste in a url that provides a manifest json object. +As an example, if you wanted to provide a url to a manifest file hosted on GitHub, you would paste in something along the lines of `https://raw.githubusercontent.com/user/respository/manifest.json`. +After the url is in the text field, press the green plus button to add the repository to the list. Do note that this app does attempt to verify repositories when they are added. If you find that your repository can not be added successfully, ensure the repository is of the correct format. Also make sure that the url you are providing is correct. +If you are a repository host, ensure that your repository is set up correctly. + + +### Usability Tips +TODO + +## Development +### How to create your own repository +#### Github +To turn a GitHub repository into a Overte repository provider, you need to make your GitHub repository a "[GitHub Pages](https://pages.github.com/)" repository. +The defaults provided by GitHub will be sufficient for this use case. Select the root of the repository as the GitHub Pages root. +After you have ensured that your repository is set up correctly, you can add the `manifest.json` file to the root of your repository. + +This is the format to use: +```json +{ + "title": "My GitHub Repository", // This is the name that will show up in the repository manager + "base_url": "https://raw.githubusercontent.com/myuser/myrepository", // This is what the More app uses as a base to search for applications provided by the 'applications' key just below this entry. + + // This is the list of all applications this repository will provide to the More app. + "applications": [ + { + "name": "My App", // The name of the application to display. + "directory": "myapp", // The directory of the application relative to the 'base_url'. This will be interpreted as 'https://raw.githubusercontent.com/myuser/myrepository/myapp' internally. + "script": "myapp.js", // The entry script of the application. + "icon": "icon.png", // The icon of the application to show in the list. + "description": "This is my first application! Download this please." // The description of the application to display in the "details" page. + }, + /// ...and other applications + ] +} +``` + +#### HTTPS Servers +To provide the applications from a standard HTTPS server, the procedure is largely the same as with GitHub. You simply need to provide a JSON response that is in the same format as the above "GitHub" format. \ No newline at end of file From 947787f38f690b6d12f8704af1d71040890c0645 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 19 Jul 2024 00:53:41 -0500 Subject: [PATCH 10/22] Install Overte Community Apps by default. Signed-off-by: Armored Dragon --- applications/more/more.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/applications/more/more.js b/applications/more/more.js index 92bf04b..e25bdb1 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -10,11 +10,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html (() => { - "use strict"; + ("use strict"); // TODO: Preinstall Overte community apps by default var installed_scripts = Settings.getValue("ArmoredMore-InstalledScripts", []) || []; // All scripts installed though more.js var installed_repositories = Settings.getValue("ArmoredMore-InstalledRepositories", []) || []; // All repositories installed though more.js + var is_first_run = Settings.getValue("ArmoredMore-FirstRun", true); // Check if this app has ran before // Global vars var tablet; @@ -41,6 +42,12 @@ tablet.fromQml.connect(fromQML); + if (is_first_run) { + installRepo("https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js"); + Settings.setValue("ArmoredMore-FirstRun", false); + is_first_run = false; + } + function toolbarButtonClicked() { if (active) { tablet.gotoHomeScreen(); From dec7739a050d9baadd1d9acf31f0c89360c26628 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 19 Jul 2024 00:59:35 -0500 Subject: [PATCH 11/22] Changed variables to Camel Case. Signed-off-by: Armored Dragon --- applications/more/more.js | 105 +++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/applications/more/more.js b/applications/more/more.js index e25bdb1..73347e3 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -12,20 +12,19 @@ (() => { ("use strict"); - // TODO: Preinstall Overte community apps by default - var installed_scripts = Settings.getValue("ArmoredMore-InstalledScripts", []) || []; // All scripts installed though more.js - var installed_repositories = Settings.getValue("ArmoredMore-InstalledRepositories", []) || []; // All repositories installed though more.js - var is_first_run = Settings.getValue("ArmoredMore-FirstRun", true); // Check if this app has ran before + var installedScripts = Settings.getValue("ArmoredMore-InstalledScripts", []) || []; // All scripts installed though more.js + var installedRepositories = Settings.getValue("ArmoredMore-InstalledRepositories", []) || []; // All repositories installed though more.js + var isFirstRun = Settings.getValue("ArmoredMore-FirstRun", true); // Check if this app has ran before // Global vars var tablet; - var app_button; + var appButton; var active = false; tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.screenChanged.connect(onScreenChanged); - app_button = tablet.addButton({ + appButton = tablet.addButton({ icon: Script.resolvePath("./img/icon_white.png"), activeIcon: Script.resolvePath("./img/icon_black.png"), text: "MORE", @@ -34,32 +33,32 @@ // When script ends, remove itself from tablet Script.scriptEnding.connect(function () { console.log("Shutting Down"); - tablet.removeButton(app_button); + tablet.removeButton(appButton); }); // Overlay button toggle - app_button.clicked.connect(toolbarButtonClicked); + appButton.clicked.connect(toolbarButtonClicked); tablet.fromQml.connect(fromQML); - if (is_first_run) { + if (isFirstRun) { installRepo("https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js"); Settings.setValue("ArmoredMore-FirstRun", false); - is_first_run = false; + isFirstRun = false; } function toolbarButtonClicked() { if (active) { tablet.gotoHomeScreen(); active = !active; - app_button.editProperties({ + appButton.editProperties({ isActive: active, }); } else { getLists(); tablet.loadQMLSource(Script.resolvePath("./more.qml")); active = !active; - app_button.editProperties({ + appButton.editProperties({ isActive: active, }); } @@ -67,7 +66,7 @@ function installApp({ title, repository, url, icon, description }) { // Add script to saved list - installed_scripts.push({ + installedScripts.push({ title: title, repository: repository, url: url, @@ -76,7 +75,7 @@ }); // Save new list as setting - Settings.setValue("ArmoredMore-InstalledScripts", installed_scripts); + Settings.setValue("ArmoredMore-InstalledScripts", installedScripts); // Install the script ScriptDiscoveryService.loadScript(url, true); // Force reload the script, do not use cache. @@ -87,14 +86,14 @@ function uninstallApp(url) { // Find app in saved list - var entry = installed_scripts.filter((app) => app.url == url); - const index = installed_scripts.indexOf(entry); + var entry = installedScripts.filter((app) => app.url == url); + const index = installedScripts.indexOf(entry); // Remove it from list - installed_scripts.splice(index, 1); + installedScripts.splice(index, 1); // Save new list as setting - Settings.setValue("ArmoredMore-InstalledScripts", installed_scripts); + Settings.setValue("ArmoredMore-InstalledScripts", installedScripts); // Uninstall the script ScriptDiscoveryService.stopScript(url, false); @@ -110,27 +109,27 @@ if (!repo) return; // Failure // Add repo to saved list - installed_repositories.push({ + installedRepositories.push({ title: repo.title || "Unnamed repository", url: url, }); // Save new list as setting - Settings.setValue("ArmoredMore-InstalledRepositories", installed_repositories); + Settings.setValue("ArmoredMore-InstalledRepositories", installedRepositories); // Send updated repository list getLists(); } function uninstallRepo(url) { // Find app in saved list - var entry = installed_repositories.filter((repo) => repo.url == url); - const index = installed_repositories.indexOf(entry); + var entry = installedRepositories.filter((repo) => repo.url == url); + const index = installedRepositories.indexOf(entry); // Remove it from list - installed_repositories.splice(index, 1); + installedRepositories.splice(index, 1); // Save new list as setting - Settings.setValue("ArmoredMore-InstalledRepositories", installed_repositories); + Settings.setValue("ArmoredMore-InstalledRepositories", installedRepositories); // Send updated app list getLists(); @@ -138,64 +137,64 @@ // Startup populate lists async function getLists() { - let application_list = []; - let installed_apps_by_url = installed_scripts.map((app) => app.url); + let applicationList = []; + let installedAppsByUrl = installedScripts.map((app) => app.url); - for (let i = 0; installed_repositories.length > i; i++) { - let repo = installed_repositories[i]; - let repo_content = await request(repo.url); - if (!repo_content) continue; // Failure + for (let i = 0; installedRepositories.length > i; i++) { + let repo = installedRepositories[i]; + let repoContent = await request(repo.url); + if (!repoContent) continue; // Failure - let apps = repo_content.application_list || []; + let apps = repoContent.application_list || []; // Filter to non-installed ones apps = apps.filter((app) => { - let app_root = repo.url.replace(/\/metadata.json/g, "") + `/${app.directory}`; + let appRoot = repo.url.replace(/\/metadata.json/g, "") + `/${app.directory}`; - let script_url = app_root + `/${app.script}`; + let scriptUrl = appRoot + `/${app.script}`; - return installed_apps_by_url.indexOf(script_url) == -1; + return installedAppsByUrl.indexOf(scriptUrl) == -1; }); apps = apps.map((app) => { - let app_root = repo_content.base_url + `/${app.directory}`; + let appRoot = repoContent.base_url + `/${app.directory}`; - let script_url = app_root + `/${app.script}`; - let script_icon = app_root + `/${app.icon}`; + let scriptUrl = appRoot + `/${app.script}`; + let scriptIcon = appRoot + `/${app.icon}`; return { title: app.name, description: app.description, - icon: script_icon, + icon: scriptIcon, repository: repo.title, - url: script_url, + url: scriptUrl, }; }); // Add all apps from repo to list - application_list.push(...apps); + applicationList.push(...apps); } _emitEvent({ type: "installed_apps", app_list: [ - ...installed_scripts.map((app) => { + ...installedScripts.map((app) => { return { ...app, installed: true }; }), - ...application_list, + ...applicationList, ], }); _emitEvent({ type: "installed_repositories", - repository_list: installed_repositories, + repository_list: installedRepositories, }); } function onScreenChanged(type, url) { if (url != Script.resolvePath("./more.qml")) { active = false; - app_button.editProperties({ + appButton.editProperties({ isActive: active, }); } @@ -210,24 +209,24 @@ // This can be safely removed at some point in the far future. 7/18/2024 if (url === "https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js") { // Scary text formatting to get the metadata.js response object into a JSON object. - var formatted_response = xmlHttp.responseText.replace("var metadata = ", "").slice(0, -1).trim(); + var formattedResponse = xmlHttp.responseText.replace("var metadata = ", "").slice(0, -1).trim(); // Extract the application list. - var application_list = JSON.parse(formatted_response).applications; + var applicationList = JSON.parse(formattedResponse).applications; // Convert each entry into a value we expect it to be. - application_list = application_list.map((app_entry) => { + applicationList = applicationList.map((appEntry) => { return { - name: app_entry.name, - directory: app_entry.directory, - script: app_entry.jsfile.replace(`${app_entry.directory}/`, ""), - icon: app_entry.icon.replace(`${app_entry.directory}/`, ""), - description: app_entry.description, + name: appEntry.name, + directory: appEntry.directory, + script: appEntry.jsfile.replace(`${appEntry.directory}/`, ""), + icon: appEntry.icon.replace(`${appEntry.directory}/`, ""), + description: appEntry.description, }; }); // Return the formatted list along with extra repository information. - return { title: "Overte", base_url: "https://raw.githubusercontent.com/overte-org/community-apps/master/applications", application_list: application_list }; + return { title: "Overte", base_url: "https://raw.githubusercontent.com/overte-org/community-apps/master/applications", application_list: applicationList }; } // Any request we make is intended to be a JSON response. From 33b71d6a56ca9cc8967b1f21015e580d5827bfc6 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 19 Jul 2024 01:02:44 -0500 Subject: [PATCH 12/22] Fix TextWrapping causing undefined error. Signed-off-by: Armored Dragon --- applications/more/more.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 348bb2e..caf7542 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -789,7 +789,7 @@ Rectangle { text: title color: "white" font.pointSize: 12 - wrapMode: Text.noWrap + wrapMode: Text.NoWrap elide: Text.ElideRight } Text { @@ -799,7 +799,7 @@ Rectangle { text: url color: "gray" font.pointSize: 10 - wrapMode: Text.noWrap + wrapMode: Text.NoWrap elide: Text.ElideRight } From f2c276e47641d4873da510392dca18af19f12cd1 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 19 Jul 2024 01:11:09 -0500 Subject: [PATCH 13/22] Removed bottom padding. Signed-off-by: Armored Dragon --- applications/more/more.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index caf7542..9b3d32a 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -166,7 +166,7 @@ Rectangle { // Apps Listing Item { width: parent.width - height: parent.height - 40 + height: parent.height anchors.top: navigation_bar.bottom visible: current_page == "app_list" From fb96779343e79fb97f51f9fefc3287250c7fd37b Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 19 Jul 2024 10:58:50 -0500 Subject: [PATCH 14/22] Update base_url of Overte Community Apps. Signed-off-by: Armored Dragon --- applications/more/more.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/more/more.js b/applications/more/more.js index 73347e3..d2f6e81 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -226,7 +226,7 @@ }); // Return the formatted list along with extra repository information. - return { title: "Overte", base_url: "https://raw.githubusercontent.com/overte-org/community-apps/master/applications", application_list: applicationList }; + return { title: "Overte", base_url: "https://more.overte.org/applications", application_list: applicationList }; } // Any request we make is intended to be a JSON response. From a65f112657541e15f50b2fa095e95a39650e7e2c Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Fri, 19 Jul 2024 12:24:32 -0500 Subject: [PATCH 15/22] Duplication check on installing repositories. Signed-off-by: Armored Dragon --- applications/more/more.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/more/more.js b/applications/more/more.js index d2f6e81..059e4d8 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -102,8 +102,10 @@ getLists(); } - // TODO: Duplication check async function installRepo(url) { + var repoIsInstalled = installedRepositories.find((repo) => repo.url === url) ? true : false; + if (repoIsInstalled) return; // Repository URL already in the list, don't add it again. + // Test repository const repo = await request(url); if (!repo) return; // Failure From 709b55d40ac30df6e6b206eefbac882d92174848 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 22:13:30 -0500 Subject: [PATCH 16/22] Improved legacy repo support. --- applications/more/more.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/applications/more/more.js b/applications/more/more.js index 059e4d8..65be984 100644 --- a/applications/more/more.js +++ b/applications/more/more.js @@ -42,7 +42,7 @@ tablet.fromQml.connect(fromQML); if (isFirstRun) { - installRepo("https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js"); + installRepo("https://more.overte.org/applications/metadata.js"); Settings.setValue("ArmoredMore-FirstRun", false); isFirstRun = false; } @@ -103,6 +103,10 @@ } async function installRepo(url) { + // Hardcode support for Overte + if (url === "https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js") + url = "https://more.overte.org/applications/metadata.js" + var repoIsInstalled = installedRepositories.find((repo) => repo.url === url) ? true : false; if (repoIsInstalled) return; // Repository URL already in the list, don't add it again. @@ -151,7 +155,7 @@ // Filter to non-installed ones apps = apps.filter((app) => { - let appRoot = repo.url.replace(/\/metadata.json/g, "") + `/${app.directory}`; + let appRoot = repo.url.replace(/\/metadata.js(?:on)?/g, "") + `/${app.directory}`; let scriptUrl = appRoot + `/${app.script}`; @@ -209,7 +213,7 @@ // Hardcode support for Overte Community-Apps metadata.js // This can be safely removed at some point in the far future. 7/18/2024 - if (url === "https://raw.githubusercontent.com/overte-org/community-apps/master/applications/metadata.js") { + if (url === "https://more.overte.org/applications/metadata.js") { // Scary text formatting to get the metadata.js response object into a JSON object. var formattedResponse = xmlHttp.responseText.replace("var metadata = ", "").slice(0, -1).trim(); From 11611ba14c2cd21164f845f1fd290e1e7ac8a4d2 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 22:15:23 -0500 Subject: [PATCH 17/22] Unfocus selected index on list clear. --- applications/more/more.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/more/more.qml b/applications/more/more.qml index 9b3d32a..3fd9a93 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -870,12 +870,14 @@ Rectangle { function clearApplicationList(){ app_listings.clear() installed_apps.clear() + app_listing_list.index_selected = -1; } function addRepositoryToList(repo_name, url){ } function clearRepositoryList(){ repo_list.clear() + registered_repo_list.index_selected = -1; } // Funcionality From 574f6b6a70e285f4d38b3719a0054475f97b04e6 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 22:18:07 -0500 Subject: [PATCH 18/22] Fixed search width issue. --- applications/more/more.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 3fd9a93..cff7213 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -36,7 +36,7 @@ Rectangle { Item { anchors.centerIn: parent - width: parent.width - 10 + width: parent.width - 20 height: parent.height - 25 Rectangle { From 77bdd9d5cbacf399e009c5bceb3f5c07b4ebbc8e Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 22:40:06 -0500 Subject: [PATCH 19/22] Fix app search. --- applications/more/more.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index cff7213..9299821 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -54,13 +54,13 @@ Rectangle { id: search_query onAccepted: { if (current_page == "app_list"){ - searchList(search_query.text, installed_apps); - return; - } - if (current_page == "repos"){ - searchList(search_query.text, repo_list); + searchList(search_query.text, app_listings); return; } + // if (current_page == "repos"){ + // searchList(search_query.text, repo_list); + // return; + // } } MouseArea { anchors.fill: parent @@ -896,7 +896,7 @@ Rectangle { // Searching function searchList(text, element){ - + for (var i = 0; i < element.count; i++) { var app = element.get(i); From 2d8e00de64fcb762ff7ddd629f07c5ea5dac640e Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 22:41:45 -0500 Subject: [PATCH 20/22] Removed dead code. --- applications/more/more.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 9299821..59f4536 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -10,8 +10,6 @@ Rectangle { id: root property string current_page: "app_list" - property string last_message_user: "" - property date last_message_time: new Date() Timer { interval: 10 @@ -896,7 +894,7 @@ Rectangle { // Searching function searchList(text, element){ - + for (var i = 0; i < element.count; i++) { var app = element.get(i); From 7864f771ad5db386c6f2532ad9c5dfdc3038cd0b Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 22:45:01 -0500 Subject: [PATCH 21/22] Refactor message handler. --- applications/more/more.qml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/applications/more/more.qml b/applications/more/more.qml index 59f4536..c6e074b 100644 --- a/applications/more/more.qml +++ b/applications/more/more.qml @@ -862,16 +862,22 @@ Rectangle { } // List population and management - function addApplicationToList(name, repo_name, description, installed, url){ + function addApplicationsToList(message){ + message.app_list.forEach((app) => { + app_listings.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: app.installed || false, is_visible: true }); + if (app.installed){ + installed_apps.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: true, is_visible: true }); + } + }) } function clearApplicationList(){ app_listings.clear() installed_apps.clear() app_listing_list.index_selected = -1; } - function addRepositoryToList(repo_name, url){ - + function addRepositoriesToList(message){ + message.repository_list.forEach((repo) => repo_list.append({ title: repo.title, url: repo.url, is_visible: true })) } function clearRepositoryList(){ repo_list.clear() @@ -924,17 +930,11 @@ Rectangle { switch (message.type){ case "installed_apps": clearApplicationList(); - message.app_list.forEach((app) => { - app_listings.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: app.installed || false, is_visible: true }); - - if (app.installed){ - installed_apps.append({title: app.title, repository: app.repository, description: app.description, icon: app.icon, url: app.url, installed: true, is_visible: true }); - } - }) + addApplicationsToList(message); break; case "installed_repositories": clearRepositoryList(); - message.repository_list.forEach((repo) => repo_list.append({ title: repo.title, url: repo.url, is_visible: true })) + addRepositoriesToList(message) break; case "clear_messages": break; From 1b8223ca3440ae9c2e4e55c3f463576e35368f36 Mon Sep 17 00:00:00 2001 From: armored-dragon Date: Tue, 27 Aug 2024 23:02:43 -0500 Subject: [PATCH 22/22] Changed icon. --- applications/more/img/icon_black.png | Bin 521 -> 327 bytes applications/more/img/icon_white.png | Bin 511 -> 336 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/applications/more/img/icon_black.png b/applications/more/img/icon_black.png index b62d59774686a43d1f07bf0d0f208aa5cd8fdf98..47c4f633562a6496802f27bccc300fc5f2181dfc 100644 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3TAX~PbvH$7ERG^?_ ziEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0vlJY5_^G8*4bcjRj_5OGQ0 z(_K*Wgh9?ztf1ljGBvTSj9Pyuob&$IwJ=r8SKh>U&dl!b>`xXk-CNOMuJz#TA|{pS zNwz07&-A9ep4xA)`3XY?BM(E48FRz3l7El?va^iSkI+4^b4V>LUYQjrNj_7f-jiQKs-j-LRsDz6j@%Y)Rj?y;pfi@Ln>}1{rUgj{%`?M;0TkFgrsEWM_y$y!>ZSZxf~`k3OM-i9A;r)xv=3Q zhXVtzbU)F|jT3IkC~TK>RIRmO1nQhE!N{>7@v&e7Lz3izus8Kt>h=u`Qf7us0uG0d zDKjvddZx*)#8?*GBY&lrIGBWR8Zc|H9V zgF-{Zi#d&%VFnB=0=izhZdS9|zo|GdTwMIsTbx0_Vapo#f0wN+6^lUzOo@V7iDWxE z)8%wk%L=KPQMxQJr(VhX`)_%fhwyDD1`dU3j??Cx_+|t0+!9t4Z$J#1@Hi^uttS&O v>^gzomggTe~DWM4fUFo6; diff --git a/applications/more/img/icon_white.png b/applications/more/img/icon_white.png index e765410b50e836051e64a1064681bbc9504db3a9..8764e6a6388cf2eda683ba7e1714ab685115ca6b 100644 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3TAX~PbvH$7ERG^?_ ziEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0v_JzX3_G8*4b^W|$Y5NVNL z*SkHSFW$1jeNwATLQF(*m;IK^LNBZMPi9}Z6UF-d*bi(w)AyHgrv`&GYrDGz4AYje8$6mTc%btEyRdM=j}M>J`yQ}gPTc9`leX=DVcCz(@)LF+ zS?RiJhrIBENfq0+$qQ{{zCY~^*8`6alc(SK@I%^y!Q4LY;nVEjp58I0XYU-jddY{O zbYauJT^IiN#A+N57gzf6$|>~X8Q&P)vn&DPS}#^}pFMx2wWy|4v1z?th$u+QB8D81 ehO^I@?=aTMJ^wxR60;%DqYR#|elF{r5}E)`0E)N( literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?3oVGw3ym^DWND9DoT z=}1{rUgj{%`?M;0V*w&X2sxVun?(4|6gwb#x}qU}#{FGLvKESdjQw zkXWXISkWxzCuYhk7VxVx0Cfs0GccOu*f0q=9A5U78%V0TF)(G^s9;fOXzO(c+S7XO z+h6ry-GhICN_3b7DtfkjuyJ7EoxOw=#LxI%pLI+I#Fs#IM`PCCAXx?$0q(iR24}h0 z8W77m4~Ep;!MfM!qodZIo`T|I>lWIzx%)Ji1VF_;;V{Q{xN!K+&t zpib?$wf6r1D}0$r(Hy`qGCDCy_~txzpuZ+>LGcE}Ahx2I?p3y)ps+FodWTt1+2o}h kGmF6K06)JI3mdKI;Vst04I=|C;$Ke