diff --git a/CMakeLists.txt b/CMakeLists.txt index e52d6f37..44203b9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,15 +25,9 @@ add_subdirectory (thirdparty/qml-box2d) add_subdirectory (thirdparty/simple-svg-writer) add_subdirectory (plugins) +# Qt has problems with multiple executables when building for Android +# TODO Re-check if support for Android got better in Qt6 if(NOT ANDROID) - add_subdirectory (liveloader) -endif() - -if(NOT ANDROID) - add_subdirectory (sandboxes/gui) - add_subdirectory (sandboxes/visualfx) - add_subdirectory (sandboxes/platformer) - add_subdirectory (sandboxes/pluginlive) - add_subdirectory (sandboxes/topdown) - add_subdirectory (sandboxes/void) + add_subdirectory (tools) + add_subdirectory (examples) endif() diff --git a/README.md b/README.md index cabf7306..265d5560 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,32 @@ -## Flow, form, create +## Overview: Flow, Form, Create When participating at [Ludum Dare 44](https://ldjam.com) I started to develop -utilities and a LiveLoading app to ease game creation. As I really enjoyed -using these components, I decided to create separate project and make it +utilities and a live loading app to ease game creation. As I really enjoyed +using these components, I decided to create a separate project and to make it open source. Clayground is a [Qt](https://www.qt.io) based toolset that allows using C++, JavaScript -and [QML](https://doc.qt.io/qt-5/qtqml-index.html) to rapidly develop apps in a sandbox environment with live loading capabilities. -It provides tools that try both facilitating learning and allowing more -focused and faster development by reducing typical build and restart times -significantly. +and [QML](https://doc.qt.io/qt-5/qtqml-index.html) to rapidly develop apps in a sandbox +environment with live loading capabilities. It provides tools that try both, facilitating +learning and allowing more focused and faster development by reducing typical build and restart times significantly. ![Platformer Screenshot](res/screenshot_platformer.png) Goals/Basic Design Decisions: -- Use Qt (Quick): Don't write everything from scratch, but think how to re-combine Qt's capabilities -- Code > Graphical Tools: Focus on minimizing time needed to write and debug applications -- Don't write graphical tools, but support formats which can be used with popular free tools -- Add dedicated APIs but still allow bypassing them (full power of Qt/Qml, C++, JavaScript) -- Target (rapid) prototyping and small/medium app development - -### Available Plugins +- Target Scope: Optimize for (rapid) prototyping and small/medium app development +- Focus on written source code and keyboard actions not for usage of graphical tools +- Full control: Add dedicated APIs but allow bypassing them easily (full power of C++, Qt/Qml, JavaScript) +- Don't provide (graphical) tools: Go for approaches that allow usage of freely available, popular tools like Qt Creator, Git and Inkscape +- Qt as Foundation: Don't write everything from scratch, but think how to re-combine Qt's capabilities + +Main components: +- **Dojo**: Sandbox environment which is designed to be used for rapid dev, it is typically put next to a code editor/IDE, code changes are automatically applied +- **Plugins**: Different packages that can be used to build (interactive) apps +- **Examples**: Demonstrate the usage of Clayground, all examples can be either used with Dojo or standalone + +### Plugins: Clayground's basic building blocks Clayground comes with a set of plugins that extend Qt capabilities in order to build interactive experiences: - Physics/Box2D: Adds 2D Physics capabilities @@ -34,32 +38,30 @@ build interactive experiences: - Storage: Get persistent storage with a very simple API - World: Uses Canvas, Physics and SVG to provide a foundation for small games and simulations -### Available Examples +### Examples: See how problems can be solved with Clayground A bunch of example application allow you to try it out easily: - GUI: Visual training app for keyboard shortcuts, comes with a configuration for Inkscape. - VisualFX: Implementation of different visual effects, includes usage of the [Qt Particle System](https://doc.qt.io/qt-5/qtquick-effects-particles.html). - Platformer: Starting point for a platformer game. -- TopDown: Starting point for a topdown game (for example a classical RPG). -- PluginLive: Demonstrates how to use the Clayground LiveLoader to develop a C++ plugin. +- TopDown: Starting point for a topdown game (for example a classical RPG), comes with network support +- PluginLive: Demonstrates how to use the Clayground LiveLoader to develop a C++ plugin ### How to work with a sandbox? 1. Clone this repo and build it (Qt 5.15.x, Qt Creator) -2. Start the qml_live_loader app `clayrestarter --dynimportdir=/sandboxes/void` +2. Start the dojo app `claydojo --sbx=/sandboxes/void/Sandbox.qml` 3. Move the window to a location that allows you to keep it visible even when your are editing code. -4. Make changes to `/sandboxes/void/Sandbox.qml` -> see the changes live applied -5. Check out the other sandboxes in the same way :) +4. Make changes to `/sandboxes/void/Sandbox.qml` -> see the changes applied automatically +5. Press `G` in the app window to show the Guide/Help overlay. +6. Check out the other sandboxes in the same way :) + #### Using the Logging Overlay -The sandbox comes with a logging overlay that shows all -`console.log(...)` messages in a continous log view and -watched properties/expressions in a sticky section. You can -easily show/hide the overlay by using the `log` button of the -restarter or by pressing lower-case L when the Sbx Window has the focus. +The sandbox comes with a logging overlay that shows all `console.log(...)` messages in a continous log view and watched properties/expressions in a sticky section. You can +easily show/hide the logging overlay by pressing `L` when the Sandbox Window has the focus. LogOverlay Screenshot -Have a look at the `onMapLoaded` signal handler of the Platformer -Sandbox to see how you can use them. +Have a look at the `onMapLoaded` signal handler of the Platformer Sandbox to see how you can use them. ### How to create a deployable app? Each of the demo sandboxes also comes with a CMake application configuration which @@ -67,7 +69,9 @@ allows to build a standalone app. So you can just use one as a template to build ### Is there any recommended development setup? - OS: Linux (fastest and easy to use) - I have also used it on macOS and Windows 10, still good but use Linux if you can -- IDE/Editor: Qt Creator as it also allows you to easily debug and profile resulting apps - additionally I use Vim for various text processing tasks +- IDE/Editor: Qt Creator as it also allows you to easily debug and profile resulting apps - additionally I use Vim and VS Code for various text processing tasks +Feel free to contact me, create issues or to contribute :) -I hope you enjoy using clayground, feel free to create issues or to contribute :) +Enjoy life,
+`mgc` \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..c55b24fd --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) +project (examples) + +add_subdirectory (gui) +add_subdirectory (visualfx) +add_subdirectory (platformer) +add_subdirectory (pluginlive) +add_subdirectory (topdown) +add_subdirectory (void) diff --git a/sandboxes/gui/CMakeLists.txt b/examples/gui/CMakeLists.txt similarity index 100% rename from sandboxes/gui/CMakeLists.txt rename to examples/gui/CMakeLists.txt diff --git a/sandboxes/gui/Sandbox.qml b/examples/gui/Sandbox.qml similarity index 100% rename from sandboxes/gui/Sandbox.qml rename to examples/gui/Sandbox.qml diff --git a/sandboxes/gui/Scoreboard.qml b/examples/gui/Scoreboard.qml similarity index 100% rename from sandboxes/gui/Scoreboard.qml rename to examples/gui/Scoreboard.qml diff --git a/sandboxes/gui/ShortcutChecker.qml b/examples/gui/ShortcutChecker.qml similarity index 100% rename from sandboxes/gui/ShortcutChecker.qml rename to examples/gui/ShortcutChecker.qml diff --git a/sandboxes/gui/TrainingDb.qml b/examples/gui/TrainingDb.qml similarity index 100% rename from sandboxes/gui/TrainingDb.qml rename to examples/gui/TrainingDb.qml diff --git a/sandboxes/gui/inkscape.xml b/examples/gui/inkscape.xml similarity index 100% rename from sandboxes/gui/inkscape.xml rename to examples/gui/inkscape.xml diff --git a/sandboxes/gui/main.cpp b/examples/gui/main.cpp similarity index 100% rename from sandboxes/gui/main.cpp rename to examples/gui/main.cpp diff --git a/sandboxes/gui/main.qml b/examples/gui/main.qml similarity index 100% rename from sandboxes/gui/main.qml rename to examples/gui/main.qml diff --git a/sandboxes/gui/res.qrc b/examples/gui/res.qrc similarity index 100% rename from sandboxes/gui/res.qrc rename to examples/gui/res.qrc diff --git a/sandboxes/gui/visuals.svg b/examples/gui/visuals.svg similarity index 100% rename from sandboxes/gui/visuals.svg rename to examples/gui/visuals.svg diff --git a/sandboxes/platformer/CMakeLists.txt b/examples/platformer/CMakeLists.txt similarity index 100% rename from sandboxes/platformer/CMakeLists.txt rename to examples/platformer/CMakeLists.txt diff --git a/sandboxes/platformer/JnRPlayer.qml b/examples/platformer/JnRPlayer.qml similarity index 100% rename from sandboxes/platformer/JnRPlayer.qml rename to examples/platformer/JnRPlayer.qml diff --git a/sandboxes/platformer/Player.qml b/examples/platformer/Player.qml similarity index 100% rename from sandboxes/platformer/Player.qml rename to examples/platformer/Player.qml diff --git a/sandboxes/platformer/Sandbox.qml b/examples/platformer/Sandbox.qml similarity index 100% rename from sandboxes/platformer/Sandbox.qml rename to examples/platformer/Sandbox.qml diff --git a/sandboxes/platformer/Wall.qml b/examples/platformer/Wall.qml similarity index 100% rename from sandboxes/platformer/Wall.qml rename to examples/platformer/Wall.qml diff --git a/sandboxes/platformer/WoodenBox.qml b/examples/platformer/WoodenBox.qml similarity index 100% rename from sandboxes/platformer/WoodenBox.qml rename to examples/platformer/WoodenBox.qml diff --git a/sandboxes/platformer/main.cpp b/examples/platformer/main.cpp similarity index 100% rename from sandboxes/platformer/main.cpp rename to examples/platformer/main.cpp diff --git a/sandboxes/platformer/main.qml b/examples/platformer/main.qml similarity index 100% rename from sandboxes/platformer/main.qml rename to examples/platformer/main.qml diff --git a/sandboxes/platformer/map.svg b/examples/platformer/map.svg similarity index 100% rename from sandboxes/platformer/map.svg rename to examples/platformer/map.svg diff --git a/sandboxes/platformer/res.qrc b/examples/platformer/res.qrc similarity index 100% rename from sandboxes/platformer/res.qrc rename to examples/platformer/res.qrc diff --git a/sandboxes/platformer/visuals.svg b/examples/platformer/visuals.svg similarity index 100% rename from sandboxes/platformer/visuals.svg rename to examples/platformer/visuals.svg diff --git a/sandboxes/pluginlive/CMakeLists.txt b/examples/pluginlive/CMakeLists.txt similarity index 100% rename from sandboxes/pluginlive/CMakeLists.txt rename to examples/pluginlive/CMakeLists.txt diff --git a/sandboxes/pluginlive/Sandbox.qml b/examples/pluginlive/Sandbox.qml similarity index 100% rename from sandboxes/pluginlive/Sandbox.qml rename to examples/pluginlive/Sandbox.qml diff --git a/sandboxes/pluginlive/main.cpp b/examples/pluginlive/main.cpp similarity index 100% rename from sandboxes/pluginlive/main.cpp rename to examples/pluginlive/main.cpp diff --git a/sandboxes/pluginlive/main.qml b/examples/pluginlive/main.qml similarity index 100% rename from sandboxes/pluginlive/main.qml rename to examples/pluginlive/main.qml diff --git a/sandboxes/pluginlive/plugin/CMakeLists.txt b/examples/pluginlive/plugin/CMakeLists.txt similarity index 100% rename from sandboxes/pluginlive/plugin/CMakeLists.txt rename to examples/pluginlive/plugin/CMakeLists.txt diff --git a/sandboxes/pluginlive/plugin/mycomponent.cpp b/examples/pluginlive/plugin/mycomponent.cpp similarity index 100% rename from sandboxes/pluginlive/plugin/mycomponent.cpp rename to examples/pluginlive/plugin/mycomponent.cpp diff --git a/sandboxes/pluginlive/plugin/mycomponent.h b/examples/pluginlive/plugin/mycomponent.h similarity index 100% rename from sandboxes/pluginlive/plugin/mycomponent.h rename to examples/pluginlive/plugin/mycomponent.h diff --git a/sandboxes/pluginlive/plugin/myplugin.cpp b/examples/pluginlive/plugin/myplugin.cpp similarity index 100% rename from sandboxes/pluginlive/plugin/myplugin.cpp rename to examples/pluginlive/plugin/myplugin.cpp diff --git a/sandboxes/pluginlive/plugin/myplugin.h b/examples/pluginlive/plugin/myplugin.h similarity index 100% rename from sandboxes/pluginlive/plugin/myplugin.h rename to examples/pluginlive/plugin/myplugin.h diff --git a/sandboxes/pluginlive/plugin/qmldir.in b/examples/pluginlive/plugin/qmldir.in similarity index 100% rename from sandboxes/pluginlive/plugin/qmldir.in rename to examples/pluginlive/plugin/qmldir.in diff --git a/sandboxes/pluginlive/res.qrc b/examples/pluginlive/res.qrc similarity index 100% rename from sandboxes/pluginlive/res.qrc rename to examples/pluginlive/res.qrc diff --git a/sandboxes/topdown/CMakeLists.txt b/examples/topdown/CMakeLists.txt similarity index 100% rename from sandboxes/topdown/CMakeLists.txt rename to examples/topdown/CMakeLists.txt diff --git a/sandboxes/topdown/Player.qml b/examples/topdown/Player.qml similarity index 100% rename from sandboxes/topdown/Player.qml rename to examples/topdown/Player.qml diff --git a/sandboxes/topdown/Sandbox.qml b/examples/topdown/Sandbox.qml similarity index 100% rename from sandboxes/topdown/Sandbox.qml rename to examples/topdown/Sandbox.qml diff --git a/sandboxes/topdown/Wall.qml b/examples/topdown/Wall.qml similarity index 100% rename from sandboxes/topdown/Wall.qml rename to examples/topdown/Wall.qml diff --git a/sandboxes/topdown/main.cpp b/examples/topdown/main.cpp similarity index 100% rename from sandboxes/topdown/main.cpp rename to examples/topdown/main.cpp diff --git a/sandboxes/topdown/main.qml b/examples/topdown/main.qml similarity index 100% rename from sandboxes/topdown/main.qml rename to examples/topdown/main.qml diff --git a/sandboxes/topdown/map.svg b/examples/topdown/map.svg similarity index 100% rename from sandboxes/topdown/map.svg rename to examples/topdown/map.svg diff --git a/sandboxes/topdown/res.qrc b/examples/topdown/res.qrc similarity index 100% rename from sandboxes/topdown/res.qrc rename to examples/topdown/res.qrc diff --git a/sandboxes/visualfx/AbsorptionFx.qml b/examples/visualfx/AbsorptionFx.qml similarity index 100% rename from sandboxes/visualfx/AbsorptionFx.qml rename to examples/visualfx/AbsorptionFx.qml diff --git a/sandboxes/visualfx/CMakeLists.txt b/examples/visualfx/CMakeLists.txt similarity index 100% rename from sandboxes/visualfx/CMakeLists.txt rename to examples/visualfx/CMakeLists.txt diff --git a/sandboxes/visualfx/ExplosionFx.qml b/examples/visualfx/ExplosionFx.qml similarity index 100% rename from sandboxes/visualfx/ExplosionFx.qml rename to examples/visualfx/ExplosionFx.qml diff --git a/sandboxes/visualfx/Sandbox.qml b/examples/visualfx/Sandbox.qml similarity index 100% rename from sandboxes/visualfx/Sandbox.qml rename to examples/visualfx/Sandbox.qml diff --git a/sandboxes/visualfx/explosion.png b/examples/visualfx/explosion.png similarity index 100% rename from sandboxes/visualfx/explosion.png rename to examples/visualfx/explosion.png diff --git a/sandboxes/visualfx/main.cpp b/examples/visualfx/main.cpp similarity index 100% rename from sandboxes/visualfx/main.cpp rename to examples/visualfx/main.cpp diff --git a/sandboxes/visualfx/main.qml b/examples/visualfx/main.qml similarity index 100% rename from sandboxes/visualfx/main.qml rename to examples/visualfx/main.qml diff --git a/sandboxes/visualfx/res.qrc b/examples/visualfx/res.qrc similarity index 100% rename from sandboxes/visualfx/res.qrc rename to examples/visualfx/res.qrc diff --git a/sandboxes/void/CMakeLists.txt b/examples/void/CMakeLists.txt similarity index 100% rename from sandboxes/void/CMakeLists.txt rename to examples/void/CMakeLists.txt diff --git a/sandboxes/void/Sandbox.qml b/examples/void/Sandbox.qml similarity index 100% rename from sandboxes/void/Sandbox.qml rename to examples/void/Sandbox.qml diff --git a/sandboxes/void/main.cpp b/examples/void/main.cpp similarity index 100% rename from sandboxes/void/main.cpp rename to examples/void/main.cpp diff --git a/sandboxes/void/main.qml b/examples/void/main.qml similarity index 100% rename from sandboxes/void/main.qml rename to examples/void/main.qml diff --git a/sandboxes/void/res.qrc b/examples/void/res.qrc similarity index 100% rename from sandboxes/void/res.qrc rename to examples/void/res.qrc diff --git a/liveloader/CMakeLists.txt b/liveloader/CMakeLists.txt deleted file mode 100644 index 6b05198e..00000000 --- a/liveloader/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project (clay_live_loader_tools) - -add_subdirectory (utils) -add_subdirectory (loader) -add_subdirectory (restarter) diff --git a/liveloader/loader/main.qml b/liveloader/loader/main.qml deleted file mode 100644 index 413ce06c..00000000 --- a/liveloader/loader/main.qml +++ /dev/null @@ -1,95 +0,0 @@ -// (c) serein.pfeiffer@gmail.com - zlib license, see "LICENSE" file - -import QtQuick 2.12 -import QtQuick.Window 2.12 -import QtQuick.Controls 2.5 -import Clayground.Common 1.0 -import Clayground.Storage 1.0 - -Window { - visible: true - x: keyvalues.get("x",Screen.desktopAvailableWidth * .01) - y: keyvalues.get("y",Screen.desktopAvailableHeight * .35) - width: keyvalues.get("width",Screen.desktopAvailableWidth * .32) - height: keyvalues.get("height",width) - title: qsTr("Clay Live Loader") - - onXChanged: keyvalues.set("x",x) - onYChanged: keyvalues.set("y",y) - onWidthChanged: keyvalues.set("width",width) - onHeightChanged: keyvalues.set("height",height) - - MessageView { - id: claylog - Component.onCompleted: Clayground.watchView = claylog; - opacity: 0 - anchors.centerIn: parent - width: 0.9 * parent.width - height: 0.75 * parent.height - z: 999 - function toggle() { - let opac = opacity > .5 ? 0.0 : 1.0; - opacity = opac; - } - } - - Loader { - id: sbxLoader - anchors.fill: parent - source: ClayLiveLoader.sandboxUrl - } - - Rectangle { - id: messageShow - anchors.fill: parent - color: "black" - visible: !sbxLoader.source - ScrollView { - anchors.centerIn: parent - width: parent.width * .95 - TextArea { - enabled: false - textFormat: TextEdit.RichText - wrapMode: Text.Wrap - horizontalAlignment:Text.AlignHCenter - color: "white" - text: ClayLiveLoader.altMessage - font.pixelSize: messageShow.height * .04 - font.family: "Monospace" - } - } - - } - - KeyValueStore { id: keyvalues; name: "clayrtdb" } - Connections { - target: ClayLiveLoader - function onRestarted() { - let r = parseInt(keyvalues.get("nrRestarts", 0)) + 1; - keyvalues.set("nrRestarts", r); - claylog.clear(); - } - function onMessagePosted(message) { claylog.add(message); } - } - - Timer { - running: true - repeat: true - interval: 250 - onTriggered: { - let opt = keyvalues.get("options"); - if (opt === "log") claylog.toggle(); - keyvalues.set("options", ""); - } - } - - Shortcut { - sequence: "r" - onActivated: keyvalues.set("command", "restart"); - } - - Shortcut { - sequence: "l" - onActivated: claylog.toggle(); - } -} diff --git a/liveloader/restarter/graphics.svg b/liveloader/restarter/graphics.svg deleted file mode 100644 index e140a67f..00000000 --- a/liveloader/restarter/graphics.svg +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..3cf22b33 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.16) +project (clayground_tools) + +add_subdirectory (common) +add_subdirectory (dojo) +add_subdirectory (loader) diff --git a/liveloader/utils/CMakeLists.txt b/tools/common/CMakeLists.txt similarity index 93% rename from liveloader/utils/CMakeLists.txt rename to tools/common/CMakeLists.txt index 6fd3624d..82033f0d 100644 --- a/liveloader/utils/CMakeLists.txt +++ b/tools/common/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project (clayutilities) +project (claytoolscommon) import_qt_components(Core) diff --git a/liveloader/utils/clayfilesysobserver.cpp b/tools/common/clayfilesysobserver.cpp similarity index 100% rename from liveloader/utils/clayfilesysobserver.cpp rename to tools/common/clayfilesysobserver.cpp diff --git a/liveloader/utils/clayfilesysobserver.h b/tools/common/clayfilesysobserver.h similarity index 100% rename from liveloader/utils/clayfilesysobserver.h rename to tools/common/clayfilesysobserver.h diff --git a/liveloader/utils/utilityfunctions.cpp b/tools/common/utilityfunctions.cpp similarity index 69% rename from liveloader/utils/utilityfunctions.cpp rename to tools/common/utilityfunctions.cpp index 5f789b09..2ba3d143 100644 --- a/liveloader/utils/utilityfunctions.cpp +++ b/tools/common/utilityfunctions.cpp @@ -6,5 +6,7 @@ void addCommonArgs(QCommandLineParser &parser) { parser.addOption({DYN_PLUGIN_ARG, DYN_PLUGIN_ARG_DESCR, "directory-pair"}); parser.addOption({DYN_IMPORT_DIR_ARG, DYN_IMPORT_DIR_ARG_DESCR, "directory", ""}); + parser.addOption({SBX_ARG, SBX_ARG_DESCR, "file-path", ""}); + parser.addOption({SBX_INDEX_ARG, SBX_INDEX_ARG_DESCR, "index", QString::number(USE_FIRST_SBX_IDX)}); parser.addOption({MESSAGE_ARG, MESSAGE_ARG_DESCR, "N/A"}); } diff --git a/liveloader/utils/utilityfunctions.h b/tools/common/utilityfunctions.h similarity index 63% rename from liveloader/utils/utilityfunctions.h rename to tools/common/utilityfunctions.h index 4955981d..6808cd1d 100644 --- a/liveloader/utils/utilityfunctions.h +++ b/tools/common/utilityfunctions.h @@ -6,7 +6,7 @@ #include // HINT: -// The following arguments are processed by both the ClayRestarter +// The following arguments are processed by both the ClayDojo // as well as the ClayLiveLoader - have a look at the description // to better understand their purpose. @@ -16,12 +16,27 @@ static constexpr const char* DYN_PLUGIN_ARG_DESCR = "Format ,. needs to be the import directory " "otherwise the Sandbox won't be able to use the plugin."; -static constexpr const char* DYN_IMPORT_DIR_ARG = "dynimportdir"; +static constexpr const char* DYN_IMPORT_DIR_ARG = "import"; static constexpr const char* DYN_IMPORT_DIR_ARG_DESCR = - "Adds a directory that contains parts of a QML App that ." + "Adds a directory that contains parts of a QML App that " "may change while the app is running. This can be a part " "with used QML files as well as a dir containing a plugin."; +static constexpr const char* SBX_ARG = "sbx"; +static constexpr const char* SBX_ARG_DESCR = + "Adds the specified qml file as a loadable Sandbox. " + "It's directory get added as an import directory if the " + "sandbox is activated (see sbxindex)."; + +static constexpr const char* SBX_INDEX_ARG = "sbxindex"; +static constexpr const char* SBX_INDEX_ARG_DESCR = + "Specifies which of the sandboxes " + "should be used - if not specified, the first " + "import directory containing a Sandbox.qml wins." + "Index has to be within [0;numOfSbxs-1]."; +static constexpr int USE_FIRST_SBX_IDX = -1; +static constexpr int USE_NONE_SBX_IDX = -2; + static constexpr const char* MESSAGE_ARG = "message"; static constexpr const char* MESSAGE_ARG_DESCR = "When this arg is set, the specified message is shown instead of " diff --git a/liveloader/restarter/CMakeLists.txt b/tools/dojo/CMakeLists.txt similarity index 86% rename from liveloader/restarter/CMakeLists.txt rename to tools/dojo/CMakeLists.txt index 542cc726..5e314ef2 100644 --- a/liveloader/restarter/CMakeLists.txt +++ b/tools/dojo/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(clayrestarter LANGUAGES CXX) +project(claydojo LANGUAGES CXX) set(THREADS_PREFER_PTHREAD_FLAG ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -10,7 +10,7 @@ import_qt_components(Core Quick QuickWidgets) find_package(Threads REQUIRED) add_executable(${PROJECT_NAME} - clayrestarter.cpp + claydojo.cpp main.cpp main.qml res.qrc @@ -20,7 +20,7 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) target_link_libraries(${PROJECT_NAME} PRIVATE - clayutilities + claytoolscommon Qt::Core Qt::Quick Qt::QuickWidgets diff --git a/liveloader/restarter/PixelProgress.qml b/tools/dojo/PixelProgress.qml similarity index 100% rename from liveloader/restarter/PixelProgress.qml rename to tools/dojo/PixelProgress.qml diff --git a/liveloader/restarter/SciFiWatch.qml b/tools/dojo/SciFiWatch.qml similarity index 100% rename from liveloader/restarter/SciFiWatch.qml rename to tools/dojo/SciFiWatch.qml diff --git a/liveloader/restarter/clayrestarter.cpp b/tools/dojo/claydojo.cpp similarity index 85% rename from liveloader/restarter/clayrestarter.cpp rename to tools/dojo/claydojo.cpp index 16ea6688..33d433dd 100644 --- a/liveloader/restarter/clayrestarter.cpp +++ b/tools/dojo/claydojo.cpp @@ -1,6 +1,6 @@ // (c) serein.pfeiffer@gmail.com - zlib license, see "LICENSE" file -#include "clayrestarter.h" +#include "claydojo.h" #include #include @@ -11,29 +11,30 @@ #include #include -ClayRestarter::ClayRestarter(QObject *parent): +ClayDojo::ClayDojo(QObject *parent): QObject(parent), shallStop_(false), shallRestart_(false), + sbxIdx_(USE_FIRST_SBX_IDX), sbx_(nullptr), logCat_(LIVE_LOADER_CAT) { - connect(&fileObserver_, &ClayFileSysObserver::fileChanged, this, &ClayRestarter::onFileSysChange); - connect(&fileObserver_, &ClayFileSysObserver::fileAdded, this, &ClayRestarter::onFileSysChange); - connect(&fileObserver_, &ClayFileSysObserver::fileRemoved, this, &ClayRestarter::onFileSysChange); + connect(&fileObserver_, &ClayFileSysObserver::fileChanged, this, &ClayDojo::onFileSysChange); + connect(&fileObserver_, &ClayFileSysObserver::fileAdded, this, &ClayDojo::onFileSysChange); + connect(&fileObserver_, &ClayFileSysObserver::fileRemoved, this, &ClayDojo::onFileSysChange); restart_.setSingleShot(true); - connect(&restart_, &QTimer::timeout, this, &ClayRestarter::onTimeToRestart); + connect(&restart_, &QTimer::timeout, this, &ClayDojo::onTimeToRestart); } -ClayRestarter::~ClayRestarter() +ClayDojo::~ClayDojo() { std::unique_lock ul(mutex_); shallStop_ = true; restarterStopped_.wait(ul); } -void ClayRestarter::addDynPluginDepedency(const QString& srcPath, +void ClayDojo::addDynPluginDepedency(const QString& srcPath, const QString& binPath) { qCDebug(logCat_) << "New dyn plugin dir: " << srcPath << " " << binPath; @@ -48,7 +49,7 @@ void ClayRestarter::addDynPluginDepedency(const QString& srcPath, qCDebug(logCat_) << "Added plugin dependency " << srcPath << " , " << binPath; } -void ClayRestarter::run() +void ClayDojo::run() { std::thread t([this] { const auto loaderCmd = QString("%1/clayliveloader").arg(QCoreApplication::applicationDirPath()); @@ -58,15 +59,18 @@ void ClayRestarter::run() // Ensure that delete gets called after pending signal processing if (sbx_.get()) { auto& p = *sbx_.release(); - disconnect(&p, &QProcess::readyReadStandardError, this, &ClayRestarter::onSbxOutput); + disconnect(&p, &QProcess::readyReadStandardError, this, &ClayDojo::onSbxOutput); p.deleteLater(); } } sbx_.reset(new QProcess()); auto& p = *sbx_.get(); - connect(&p, &QProcess::readyReadStandardError, this, &ClayRestarter::onSbxOutput); - if (buildWaitList_.empty()) - p.start(loaderCmd, QCoreApplication::arguments()); + connect(&p, &QProcess::readyReadStandardError, this, &ClayDojo::onSbxOutput); + if (buildWaitList_.empty()){ + auto args = QCoreApplication::arguments(); + args << QString("--%1").arg(SBX_INDEX_ARG) << QString::number(sbxIdx_); + p.start(loaderCmd, args); + } else { auto msg = buildWaitList_.join(";"); p.start(loaderCmd, @@ -108,12 +112,13 @@ void ClayRestarter::run() t.detach(); } -void ClayRestarter::triggerRestart() +void ClayDojo::triggerRestart(int sbxIdx) { + sbxIdx_ = sbxIdx; shallRestart_ = true; } -void ClayRestarter::onSbxOutput() +void ClayDojo::onSbxOutput() { if (!mutex_.try_lock_for(std::chrono::milliseconds(250))) return; std::lock_guard l(mutex_, std::adopt_lock); @@ -128,7 +133,7 @@ void ClayRestarter::onSbxOutput() } } -void ClayRestarter::onFileSysChange(const QString &path) +void ClayDojo::onFileSysChange(const QString &path) { auto dir = QFileInfo(path).absoluteDir(); if (!dir.exists()) { @@ -167,7 +172,7 @@ void ClayRestarter::onFileSysChange(const QString &path) } } -void ClayRestarter::onTimeToRestart() +void ClayDojo::onTimeToRestart() { shallRestart_ = true; } diff --git a/liveloader/restarter/clayrestarter.h b/tools/dojo/claydojo.h similarity index 83% rename from liveloader/restarter/clayrestarter.h rename to tools/dojo/claydojo.h index ca6e1d99..76bf26b1 100644 --- a/liveloader/restarter/clayrestarter.h +++ b/tools/dojo/claydojo.h @@ -3,6 +3,7 @@ #ifndef CLAY_RESTARTER_H #define CLAY_RESTARTER_H #include +#include #include #include @@ -15,18 +16,18 @@ #include #include -class ClayRestarter: public QObject +class ClayDojo: public QObject { Q_OBJECT public: - ClayRestarter(QObject* parent = nullptr); - ~ClayRestarter(); + ClayDojo(QObject* parent = nullptr); + ~ClayDojo(); void addDynPluginDepedency(const QString &srcPath, const QString &binPath); public slots: void run(); - void triggerRestart(); + void triggerRestart(int sbxIdx = USE_FIRST_SBX_IDX); private slots: void onSbxOutput(); @@ -42,6 +43,7 @@ private slots: std::condition_variable_any restarterStopped_; std::atomic_bool shallStop_; std::atomic_bool shallRestart_; + std::atomic_int sbxIdx_; std::unique_ptr sbx_; ClayFileSysObserver fileObserver_; std::map sourceToBuildDir_; diff --git a/liveloader/restarter/main.cpp b/tools/dojo/main.cpp similarity index 70% rename from liveloader/restarter/main.cpp rename to tools/dojo/main.cpp index cd2663fe..80d99d4a 100644 --- a/liveloader/restarter/main.cpp +++ b/tools/dojo/main.cpp @@ -1,7 +1,7 @@ // (c) serein.pfeiffer@gmail.com - zlib license, see "LICENSE" file #include "utilityfunctions.h" -#include "clayrestarter.h" +#include "claydojo.h" #include #include #include @@ -10,7 +10,7 @@ #include #include -void processCmdLineArgs(const QGuiApplication& app, ClayRestarter& restarter) +void processCmdLineArgs(const QGuiApplication& app, ClayDojo& restarter) { QCommandLineParser parser; addCommonArgs(parser); @@ -29,19 +29,19 @@ void processCmdLineArgs(const QGuiApplication& app, ClayRestarter& restarter) int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); - QGuiApplication::setApplicationName("ClayRestarter"); - QGuiApplication::setApplicationVersion("0.1"); + QGuiApplication::setApplicationName("ClayDojo"); + QGuiApplication::setApplicationVersion("0.2"); QQmlApplicationEngine engine; engine.addImportPath("qml"); engine.setOfflineStoragePath(QDir::homePath() + "/.clayground"); - ClayRestarter restarter; - processCmdLineArgs(app, restarter); - engine.rootContext()->setContextProperty("ClayRestarter", &restarter); + ClayDojo dojo; + processCmdLineArgs(app, dojo); + engine.rootContext()->setContextProperty("ClayDojo", &dojo); engine.load(QUrl("qrc:/clayground/main.qml")); - QTimer::singleShot(0, &restarter, SLOT(run())); + QTimer::singleShot(0, &dojo, SLOT(run())); return app.exec(); } diff --git a/liveloader/restarter/main.qml b/tools/dojo/main.qml similarity index 65% rename from liveloader/restarter/main.qml rename to tools/dojo/main.qml index 6fdf824c..34009a4d 100644 --- a/liveloader/restarter/main.qml +++ b/tools/dojo/main.qml @@ -14,7 +14,7 @@ Window { y: Screen.desktopAvailableHeight * .01 width: Screen.desktopAvailableWidth * .32 height: Screen.desktopAvailableHeight * .2 - title: qsTr("Clay Dev Session") + title: qsTr("Clay Dojo") opacity: .95 property int nrRestarts: 0 @@ -23,19 +23,28 @@ Window { Component.onCompleted: { keyvalues.set("nrRestarts", 0); keyvalues.set("command", ""); - keyvalues.set("options", ""); + keyvalues.set("liveLoaderCtrl", ""); + _commandProc.start(); } Timer { - running: true + id: _commandProc + running: false repeat: true interval: 250 onTriggered: { - nrRestarts = keyvalues.get("nrRestarts", 0) - currError = keyvalues.get("lastErrorMsg", 0) - let cmd = keyvalues.get("command"); - if (cmd === "restart") ClayRestarter.triggerRestart(); - keyvalues.set("command", ""); + nrRestarts = keyvalues.get("nrRestarts", 0); + currError = keyvalues.get("lastErrorMsg", 0); + const cmd = keyvalues.get("command"); + if (cmd){ + const cmdParts = cmd.split(' '); + if (cmdParts[0] === "restart") { + let idx = -1; + if (cmdParts.length === 2) idx = parseInt(cmdParts[1]); + ClayDojo.triggerRestart(idx); + } + keyvalues.set("command", ""); + } } } @@ -68,40 +77,16 @@ Window { font.pixelSize: lbl.font.pixelSize * 1.8 } } - Button { - id: btnRestart - width: theWindow.width * .05 - height: width - anchors.verticalCenter: parent.verticalCenter - background: Image { source: theSvgSource.source("reload") } - onPressed: ClayRestarter.triggerRestart(); - ToolTip.visible: btnRestart.hovered - ToolTip.text: "Restart Sbx (Press 'r' in Sbx)" - ToolTip.delay: 500 - } - Button { - id: btnToggleLog - width: theWindow.width * .05 - height: width - anchors.verticalCenter: parent.verticalCenter - background: Image { source: theSvgSource.source("log") } - function toggleLog() {keyvalues.set("options", "log");} - onPressed: toggleLog() - ToolTip.visible: btnToggleLog.hovered - ToolTip.text: "Show/hide log overlay (Press 'l' in Sbx)" - ToolTip.delay: 500 - } } Text { id: briefStatus - anchors.horizontalCenter: parent.horizontalCenter color: blinkColor - horizontalAlignment: Text.AlignHCenter + anchors { left: parent.left; leftMargin: watch.width * .05} font.family: "Monospace" font.pixelSize: watch.width * 0.06 - text: errDetected ? "CRITICAL ERROR" : "All Systems up and running" + text: errDetected ? "> CRITICAL ERROR" : "> All Systems up and running..." property color blinkColor: errDetected ? "#D64545" : watch.secondsColor property bool errDetected: currError !== "" @@ -144,21 +129,23 @@ Window { } } + Button { + id: btnToggleHelp + text: "?"; font.family: "Monospace"; font.pixelSize: lbl.font.pixelSize * 1.8 + width: height; flat: true; highlighted: true + anchors.top: parent.top; anchors.topMargin: height * .1; + anchors.right: parent.right; anchors.rightMargin: width * .1; + ToolTip {visible: btnToggleHelp.hovered; text: "Show/hide help overlay"; delay: 500} + function toggleHelp() {keyvalues.set("liveLoaderCtrl", "toggleHelp");} + onPressed: toggleHelp() + } + KeyValueStore { id: keyvalues; name: "clayrtdb" } Connections { - target: ClayRestarter + target: ClayDojo function onRestarted() { let r = parseInt(keyvalues.get("nrRestarts", 0)) + 1; keyvalues.set("nrRestarts", r); - btnRestart.enabled = true; } - function onAboutToRestart() { btnRestart.enabled = false; } } - - SvgImageSource { - id: theSvgSource - svgPath: "clayground/graphics" - annotationRRGGBB:"000000" - } - } diff --git a/liveloader/restarter/res.qrc b/tools/dojo/res.qrc similarity index 85% rename from liveloader/restarter/res.qrc rename to tools/dojo/res.qrc index 946dcf90..be90946e 100644 --- a/liveloader/restarter/res.qrc +++ b/tools/dojo/res.qrc @@ -3,7 +3,6 @@ main.qml PixelProgress.qml SciFiWatch.qml - graphics.svg diff --git a/liveloader/loader/CMakeLists.txt b/tools/loader/CMakeLists.txt similarity index 97% rename from liveloader/loader/CMakeLists.txt rename to tools/loader/CMakeLists.txt index 88aa89eb..6285d7e8 100644 --- a/liveloader/loader/CMakeLists.txt +++ b/tools/loader/CMakeLists.txt @@ -18,7 +18,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$ don't add sbx."; } - if (!engine_.importPathList().contains(path)) - engine_.addImportPath(path); + if (allSbxs_.size() != cnt) emit sandboxesChanged(); + if (allSbxs_.isEmpty()) + qFatal("No sandbox specified or available -> cannot use any.'"); +} - fileObserver_.observeDir(path); +QStringList ClayLiveLoader::sandboxes() const +{ + QStringList lst; + for (const auto& sbx: allSbxs_) lst << sbx.toString(); + return lst; +} + + +void ClayLiveLoader::addDynImportDir(const QString &path) +{ + if (!engine_.importPathList().contains(path)){ + engine_.addImportPath(path); + fileObserver_.observeDir(path); + } } void ClayLiveLoader::addDynPluginDir(const QString &path) @@ -119,10 +146,10 @@ void ClayLiveLoader::show() void ClayLiveLoader::onTimeToRestart() { - const auto sbxUrl = sandboxUrl(); - setSandboxUrl(QUrl()); + auto idx = sbxIdx_; + setSbxIndex(USE_NONE_SBX_IDX); clearCache(); - setSandboxUrl(sbxUrl); + setSbxIndex(idx); numRestarts_++; emit restarted(); } @@ -162,19 +189,26 @@ void ClayLiveLoader::clearCache() QUrl ClayLiveLoader::sandboxUrl() const { - return sandboxUrl_; + return sbxIdx_ >= 0 ? allSbxs_[sbxIdx_] : QUrl(); } QString ClayLiveLoader::sandboxDir() const { - QFileInfo info(sandboxUrl_.toLocalFile()); + QFileInfo info(sandboxUrl().toLocalFile()); return info.absolutePath(); } -void ClayLiveLoader::setSandboxUrl(const QUrl& url) +void ClayLiveLoader::setSbxIndex(int sbxIdx) { - if (url != sandboxUrl_){ - sandboxUrl_ = url; + if (sbxIdx >= allSbxs_.size()){ + qCritical() << "Sbx Idx out of bounds " << sbxIdx << " len " << allSbxs_.size(); + return; + } + + if (sbxIdx_ != sbxIdx){ + sbxIdx_ = sbxIdx; + auto sbxDir = QFileInfo(sandboxDir()); + if (sbxDir.exists()) addDynImportDir(sandboxDir()); qputenv("CLAYGROUND_SBX_DIR", sandboxDir().toUtf8()); emit sandboxUrlChanged(); emit sandboxDirChanged(); diff --git a/liveloader/loader/clayliveloader.h b/tools/loader/clayliveloader.h similarity index 81% rename from liveloader/loader/clayliveloader.h rename to tools/loader/clayliveloader.h index 2f255b5f..7189c423 100644 --- a/liveloader/loader/clayliveloader.h +++ b/tools/loader/clayliveloader.h @@ -3,6 +3,7 @@ #ifndef QML_ENGINE_WRAPPER_H #define QML_ENGINE_WRAPPER_H #include +#include #include #include #include @@ -14,6 +15,7 @@ class ClayLiveLoader: public QObject Q_OBJECT Q_PROPERTY(QUrl sandboxUrl READ sandboxUrl NOTIFY sandboxUrlChanged) Q_PROPERTY(QString sandboxDir READ sandboxDir NOTIFY sandboxDirChanged) + Q_PROPERTY(QStringList sandboxes READ sandboxes NOTIFY sandboxesChanged) Q_PROPERTY(QString altMessage READ altMessage NOTIFY altMessageChanged) Q_PROPERTY(int numRestarts READ numRestarts NOTIFY restarted) @@ -22,7 +24,10 @@ class ClayLiveLoader: public QObject QUrl sandboxUrl() const; QString sandboxDir() const; - void addDynImportDir(const QString& path); + void setSbxIndex(int sbxIdx); + void addSandboxes(const QStringList &sbxFiles); + QStringList sandboxes() const; + void addDynImportDirs(const QStringList &dirs); void addDynPluginDir(const QString& path); void show(); QString altMessage() const; @@ -33,6 +38,7 @@ class ClayLiveLoader: public QObject signals: void sandboxUrlChanged(); void sandboxDirChanged(); + void sandboxesChanged(); void altMessageChanged(); void restarted(); void messagePosted(const QString& message); @@ -45,7 +51,8 @@ private slots: void onTimeToRestart(); private: - void setSandboxUrl(const QUrl &path); + void addDynImportDir(const QString& path); + void setSbxUrl(const QUrl &url); void clearCache(); bool isQmlPlugin(const QString &path) const; void storeValue(const QString& key, const QString& value); @@ -54,7 +61,8 @@ private slots: private: QQmlApplicationEngine engine_; ClayFileSysObserver fileObserver_; - QUrl sandboxUrl_; + QVector allSbxs_; + int sbxIdx_ = USE_NONE_SBX_IDX; QSqlDatabase statsDb_; QTimer reload_; QString altMessage_; diff --git a/liveloader/loader/main.cpp b/tools/loader/main.cpp similarity index 88% rename from liveloader/loader/main.cpp rename to tools/loader/main.cpp index 0a278571..c2c0648f 100644 --- a/liveloader/loader/main.cpp +++ b/tools/loader/main.cpp @@ -16,6 +16,7 @@ void processCmdLineArgs(const QGuiApplication& app, ClayLiveLoader& loader) auto isMessageMode = parser.isSet(MESSAGE_ARG); auto isSbxMode = parser.isSet(DYN_IMPORT_DIR_ARG) || + parser.isSet(SBX_ARG) || parser.isSet(DYN_PLUGIN_ARG); if (isMessageMode) { auto msg = parser.value(MESSAGE_ARG); @@ -23,14 +24,11 @@ void processCmdLineArgs(const QGuiApplication& app, ClayLiveLoader& loader) } else if (isSbxMode) { - if (parser.isSet(DYN_IMPORT_DIR_ARG)) { - for (auto& val: parser.values(DYN_IMPORT_DIR_ARG)) - { - QDir dir(val); - if (!dir.exists()) parser.showHelp(1); - loader.addDynImportDir(val); - } - } + if (parser.isSet(DYN_IMPORT_DIR_ARG)) + loader.addDynImportDirs(parser.values(DYN_IMPORT_DIR_ARG)); + + if (parser.isSet(SBX_ARG)) + loader.addSandboxes(parser.values(SBX_ARG)); if (parser.isSet(DYN_PLUGIN_ARG)) { for (auto& val: parser.values(DYN_PLUGIN_ARG)) @@ -41,6 +39,9 @@ void processCmdLineArgs(const QGuiApplication& app, ClayLiveLoader& loader) loader.addDynPluginDir(dynPlugDirs[1]); } } + + auto idx = parser.value(SBX_INDEX_ARG).toInt(); + loader.setSbxIndex(idx == USE_FIRST_SBX_IDX ? 0 : idx); } else parser.showHelp(1); diff --git a/tools/loader/main.qml b/tools/loader/main.qml new file mode 100644 index 00000000..71626703 --- /dev/null +++ b/tools/loader/main.qml @@ -0,0 +1,150 @@ +// (c) serein.pfeiffer@gmail.com - zlib license, see "LICENSE" file + +import QtQuick 2.12 +import QtQuick.Window 2.12 +import QtQuick.Controls 2.5 +import Clayground.Common 1.0 +import Clayground.Storage 1.0 + +Window { + id: _theWindow + + visible: true + x: keyvalues.get("x",Screen.desktopAvailableWidth * .01) + y: keyvalues.get("y",Screen.desktopAvailableHeight * .35) + width: keyvalues.get("width",Screen.desktopAvailableWidth * .32) + height: keyvalues.get("height",width) + title: qsTr("Clay Live Loader") + + onXChanged: keyvalues.set("x",x) + onYChanged: keyvalues.set("y",y) + onWidthChanged: keyvalues.set("width",width) + onHeightChanged: keyvalues.set("height",height) + + MessageView { + id: claylog + Component.onCompleted: Clayground.watchView = claylog; + anchors.centerIn: parent + width: 0.9 * parent.width + height: 0.75 * parent.height + z: 999 + opacity: 0 + Behavior on opacity {NumberAnimation{duration: 250}} + function toggle() { opacity = opacity > .5 ? 0.0 : 1.0; } + } + + Loader { + id: sbxLoader + anchors.fill: parent + source: ClayLiveLoader.sandboxUrl + onSourceChanged: showSbxSourceComp.createObject(parent) + } + + Component { + id: showSbxSourceComp + Text { + id: _lblSrc + + font.pixelSize: parent.height * .06 + font.bold: true; color: "white"; anchors.centerIn: parent + visible: sbxLoader.source !== ""; opacity: 1.0; + + Behavior on opacity {NumberAnimation{duration: _lblSrc.ttl}} + property int ttl: 750 + property var _sbxUrlEls: sbxLoader.source.toString().split('/') + text: _sbxUrlEls.length > 1 ? _sbxUrlEls[_sbxUrlEls.length-2] : "" + + Component.onCompleted: opacity = 0 + + Rectangle {color: "black"; anchors.centerIn: parent; z: -1; + opacity: 0.9; height: parent.height * 1.1; width: parent.width * 1.1} + Timer { running: true; interval: _lblSrc.ttl; onTriggered: _lblSrc.destroy() } + } + } + + Rectangle { + id: messageShow + anchors.fill: parent + color: "black" + visible: !sbxLoader.source + ScrollView { + anchors.centerIn: parent + width: parent.width * .95 + TextArea { + enabled: false + textFormat: TextEdit.RichText + wrapMode: Text.Wrap + horizontalAlignment:Text.AlignHCenter + color: "white" + text: ClayLiveLoader.altMessage + font.pixelSize: messageShow.height * .04 + font.family: "Monospace" + } + } + + } + + KeyValueStore { id: keyvalues; name: "clayrtdb" } + Connections { + target: ClayLiveLoader + function onRestarted() { + let r = parseInt(keyvalues.get("nrRestarts", 0)) + 1; + keyvalues.set("nrRestarts", r); + claylog.clear(); + } + function onMessagePosted(message) { claylog.add(message); } + } + + Timer { + running: true + repeat: true + interval: 250 + onTriggered: { + let opt = keyvalues.get("liveLoaderCtrl"); + if (opt === "toggleHelp") guideScreen.toggle(); + keyvalues.set("liveLoaderCtrl", "") + } + } + + // The guide screen and all (documented) shortcuts + readonly property string _SC_USED_MOD: "Ctrl+" + readonly property string _SC_TOGGLE_LOG: _SC_USED_MOD + "L" + readonly property string _SC_TOGGLE_GUIDE: _SC_USED_MOD + "G" + function _scRestartSbx(sbxIdx) {return _SC_USED_MOD + sbxIdx;} + function _restart(sbxIdx){ keyvalues.set("command", "restart " + sbxIdx); } + + Rectangle { + id: guideScreen + anchors.fill: parent + color: "black" + Column { + anchors.centerIn: parent + spacing: 5 + Text {font.bold: true; color: "#D69545"; text: "OVERLAYS"} + ShortcutDescr {keys: _SC_TOGGLE_LOG; descr: "Show/Hide log overlay"} + ShortcutDescr {keys: _SC_TOGGLE_GUIDE; descr: "Show/Hide this guide overlay."} + Text {font.bold: true; color: "#D69545"; text: "SANDBOXES"} + Repeater { + model: ClayLiveLoader.sandboxes + ShortcutDescr { + property var segs: modelData.split('/') + keys: _scRestartSbx(index+1) + descr: segs[segs.length-2] + "/" + segs[segs.length-1] + } + } + } + opacity: 0 + visible: opacity > .1 + Behavior on opacity {NumberAnimation{duration: 250}} + function toggle() { opacity = opacity > .5 ? 0.0 : .85; } + MouseArea {anchors.fill: parent; onClicked: guideScreen.toggle();} + } + + Shortcut {sequence: _SC_TOGGLE_LOG; onActivated: claylog.toggle(); } + Shortcut {sequence: _SC_TOGGLE_GUIDE; onActivated: guideScreen.toggle(); } + Shortcut {sequence: _scRestartSbx(1); onActivated: _restart(0)} + Shortcut {sequence: _scRestartSbx(2); onActivated: _restart(1)} + Shortcut {sequence: _scRestartSbx(3); onActivated: _restart(2)} + Shortcut {sequence: _scRestartSbx(4); onActivated: _restart(3)} + Shortcut {sequence: _scRestartSbx(5); onActivated: _restart(4)} +} diff --git a/liveloader/loader/qmlres.qrc b/tools/loader/qmlres.qrc similarity index 80% rename from liveloader/loader/qmlres.qrc rename to tools/loader/qmlres.qrc index c5ba18dd..9d5b2bec 100644 --- a/liveloader/loader/qmlres.qrc +++ b/tools/loader/qmlres.qrc @@ -2,5 +2,6 @@ main.qml MessageView.qml + ShortcutDescr.qml