Merge remote-tracking branch 'origin/qds/dev'
Change-Id: Ic852bc9977d0292fb6cd93a319f4bfdebb22a1b0
28
README.md
@@ -934,6 +934,34 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### QrCodeGenerator
|
||||
|
||||
The QML Designer plugin uses QR Code Generator for Design Viewer integration.
|
||||
|
||||
https://github.com/alex-spataru/Qt-QrCodeGenerator
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Alex Spataru
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### cmake
|
||||
|
||||
The CMake project manager uses the CMake lexer code for parsing CMake files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(IDE_VERSION "4.3.0") # The IDE version.
|
||||
set(IDE_VERSION_COMPAT "4.3.0") # The IDE Compatibility version.
|
||||
set(IDE_VERSION_DISPLAY "4.3.0") # The IDE display version.
|
||||
set(IDE_VERSION "4.4.0") # The IDE version.
|
||||
set(IDE_VERSION_COMPAT "4.4.0") # The IDE Compatibility version.
|
||||
set(IDE_VERSION_DISPLAY "4.4.0") # The IDE display version.
|
||||
set(IDE_COPYRIGHT_YEAR "2023") # The IDE current copyright year.
|
||||
|
||||
set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation.
|
||||
|
||||
BIN
doc/qtcreator/images/extraimages/images/KDxnMQzgmIY.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -2,7 +2,6 @@
|
||||
images/commercial.png \
|
||||
images/SsFWyUeAA_4.jpg \
|
||||
images/9ihYeC0YJ0M.jpg \
|
||||
images/RfEYO-5Mw6s.jpg \
|
||||
images/yOUdg1o2KJM.jpg \
|
||||
images/DVWd_xMMgvg.jpg \
|
||||
images/Ed8WS03C-Vk.jpg \
|
||||
@@ -11,6 +10,5 @@
|
||||
images/w1yhDl93YI0.jpg \
|
||||
images/pEETxSxYazg.jpg \
|
||||
images/V3Po15bNErw.jpg \
|
||||
images/bMXeeQw6BYs.jpg \
|
||||
images/u3kZJjlk3CY.jpg \
|
||||
images/9MqUCP6JLCQ.jpg
|
||||
images/9MqUCP6JLCQ.jpg \
|
||||
images/KDxnMQzgmIY.jpg
|
||||
|
||||
@@ -897,6 +897,18 @@
|
||||
|
||||
\include license-mit.qdocinc
|
||||
|
||||
\li \b QrCodeGenerator
|
||||
|
||||
The QML Designer plugin uses QR Code Generator for Design Viewer integration.
|
||||
|
||||
\list
|
||||
\li \l https://github.com/alex-spataru/Qt-QrCodeGenerator
|
||||
\endlist
|
||||
|
||||
Distributed under the MIT license.
|
||||
|
||||
\include license-mit.qdocinc
|
||||
|
||||
\li \b cmake
|
||||
|
||||
The CMake project manager uses the CMake lexer code for parsing CMake files.
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
Besides the 3D model, the 3D scene also has the default camera and the default directional
|
||||
light.
|
||||
|
||||
\include run-tutorial-project.qdocinc
|
||||
|
||||
\section1 Adding Materials to the 3D Models
|
||||
|
||||
First, use materials from \uicontrol {Content Library} on the ball bearing.
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
All assets you need for this tutorial are included in the Car Demo project.
|
||||
|
||||
\include run-tutorial-project.qdocinc
|
||||
|
||||
\section1 Creating States
|
||||
|
||||
First, you create the different states. In this tutorial, you create four
|
||||
@@ -121,16 +123,19 @@
|
||||
\list 1
|
||||
\li Go to the \uicontrol Connections view.
|
||||
\li In \uicontrol{Navigator}, select \e button_side and in
|
||||
\uicontrol {Connections}, select \inlineimage icons/plus.png
|
||||
.
|
||||
This creates a new connection with \e button_side as the target.
|
||||
\li Set \uicontrol{Signal Handler} to \uicontrol onClicked.
|
||||
\li Set \uicontrol Actions to \e {Change state to side}.
|
||||
\uicontrol {Connections}, select the \inlineimage icons/plus.png
|
||||
button to open the connection setup options.
|
||||
\li Set \uicontrol Signal to \c clicked, \uicontrol Action to
|
||||
\c {Change State}, \uicontrol {State Group} to \c rectangle and
|
||||
\uicontrol State to \c side in the respective
|
||||
drop-down menus.
|
||||
\li Select the \inlineimage icons/close.png
|
||||
button to close the connection setup options.
|
||||
\li Repeat steps 2 to 4 for the next three buttons and set them to go to
|
||||
their corresponding states.
|
||||
\endlist
|
||||
|
||||
\image state-transition-connections.png
|
||||
\image state-transition-connections.webp
|
||||
|
||||
Now you can preview and try the transitions to see how the UI moves between
|
||||
the states when you select the buttons.
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
This tutorial requires that you know the basics of \QDS, see
|
||||
\l{Getting Started}.
|
||||
|
||||
\include run-tutorial-project.qdocinc
|
||||
|
||||
\section1 Creating a Timeline Animation
|
||||
|
||||
First, you create an animation where the ball bearing continuously rotates
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
doc/qtdesignstudio/examples/doc/images/loginui3-connections.webp
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -252,7 +252,7 @@
|
||||
|
||||
\image loginui1-entry-field-styled.jpg "Modified button in the 2D view"
|
||||
|
||||
\note Do not edit the the value of \uicontrol Text in the \uicontrol Character
|
||||
\note Do not edit the value of \uicontrol Text in the \uicontrol Character
|
||||
property, because this will break the connection, and later you won't be able
|
||||
to change the text in \uicontrol {Button Content} > \uicontrol Text.
|
||||
|
||||
|
||||
@@ -135,17 +135,22 @@
|
||||
\uicontrol {Connections} to open the \uicontrol Connections view.
|
||||
\li Select \e createAccount in \uicontrol Navigator.
|
||||
\li In the \uicontrol Connections tab, select the \inlineimage icons/plus.png
|
||||
button to add the action that the \c onClicked signal handler of
|
||||
\e createAccount should apply.
|
||||
\li Double-click the value \uicontrol Action column and select
|
||||
\uicontrol {Change state to createAccount} in the drop-down menu.
|
||||
\note Or, you can right-click the \e createAccount button in \l Navigator.
|
||||
Then select \uicontrol {Connections} > \uicontrol {Add signal handler} >
|
||||
\uicontrol {clicked} > \uicontrol {Change State to createAccount}.
|
||||
\image loginui3-connections.png "Connections tab"
|
||||
button to open the connection setup options.
|
||||
\li Set \uicontrol Signal to \c clicked, \uicontrol Action to
|
||||
\c {Change State}, \uicontrol {State Group} to \c rectangle and
|
||||
\uicontrol State to \c createAccount in the respective
|
||||
drop-down menus.
|
||||
\li Select the \inlineimage icons/close.png
|
||||
button to close the connection setup options.
|
||||
|
||||
\image loginui3-connections.webp "Connections tab"
|
||||
|
||||
\li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S}
|
||||
to save your changes.
|
||||
|
||||
\note Or, you can right-click the \e createAccount button in \l Navigator.
|
||||
Then select \uicontrol {Connections} > \uicontrol {Add signal handler} >
|
||||
\uicontrol {clicked} > \uicontrol {Change State to createAccount}.
|
||||
\endlist
|
||||
|
||||
In the live preview, you can now click the \uicontrol {Create Account}
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
\l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/multi-language%20tutorial/Loginui2}{here}
|
||||
before you start.
|
||||
|
||||
Download the project and open the \e loginui2.qmlproject file in \QDS
|
||||
to get started.
|
||||
|
||||
This project consists of a login page with a couple of text elements.
|
||||
|
||||
Additionally, you will use a JSON translation file in this tutorial.
|
||||
Download it from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/multi-language}{here}.
|
||||
|
||||
\include run-tutorial-project.qdocinc
|
||||
|
||||
\section1 JSON Translation File
|
||||
|
||||
The JSON translation file you are using in this project has the following
|
||||
|
||||
@@ -40,10 +40,23 @@
|
||||
|
||||
We use the \uicontrol {\QMCU Application} project template to create
|
||||
an application for MCUs, which support only a subset of the preset
|
||||
\l{glossary-component}{components}. We select \uicontrol File >
|
||||
\uicontrol {New Project} > \uicontrol {\QMCU Application} >
|
||||
\uicontrol Choose, and follow the instructions of the wizard to create our
|
||||
project.
|
||||
\l{glossary-component}{components}.
|
||||
|
||||
To create an MCU project:
|
||||
|
||||
\list 1
|
||||
\li Select \uicontrol {File} > \uicontrol {New Project}.
|
||||
\li In the \uicontrol {Presets} tab, select the \uicontrol {\QMCU} preset.
|
||||
\li In the \uicontrol {Details} tab:
|
||||
\list
|
||||
\li Select the path for the project files. You can move the project
|
||||
folders later.
|
||||
\li Set the screen size to match the device screen, which also enables
|
||||
previewing on the desktop. You can change the screen size later in
|
||||
\l {Properties}.
|
||||
\endlist
|
||||
\li Select \uicontrol {Create} to create the project.
|
||||
\endlist
|
||||
|
||||
This way, only the components and properties supported on MCUs are visible
|
||||
in \l Components and \l Properties, and we won't accidentally
|
||||
@@ -193,10 +206,12 @@
|
||||
Then, we select the mouse area for the start button, \e startMA,
|
||||
in \uicontrol Navigator. On the \uicontrol Connections tab in the
|
||||
\l {Connections} view, we select the \inlineimage icons/plus.png
|
||||
(\uicontrol Add) button to connect the \c onClicked() signal handler
|
||||
of the button to the \c startClicked() signal.
|
||||
(\uicontrol Add) button. We set \uicontrol Signal to \c clicked,
|
||||
\uicontrol Action to \c {Call Function} and \uicontrol Item to
|
||||
\c startClicked. Next, we select the \inlineimage icons/close.png
|
||||
button to close the connection setup options.
|
||||
|
||||
\image washingmachineui-connections.png "Connections view"
|
||||
\image washingmachineui-connections.webp "Connections view"
|
||||
|
||||
Then, in \e ApplicationView.qml, we specify that the \c startClicked()
|
||||
signal changes the application state to \e presets:
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
BIN
doc/qtdesignstudio/images/qtquick-component-signal.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/qtdesignstudio/images/studio-project-export-advanced.webp
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
doc/qtdesignstudio/images/studio-project-export.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 50 KiB |
@@ -28,19 +28,19 @@
|
||||
in ways that are not supported in \QDS by default, you can define
|
||||
custom properties on the \uicontrol {Properties} tab in the
|
||||
\l {Connections} view.
|
||||
\image qmldesigner-dynamicprops.png "Connections View Properties tab"
|
||||
\image add-updated-local-custom-property.webp "Connections View Properties tab"
|
||||
For more information, see \l{Specifying Custom Properties}.
|
||||
\li To enable users to interact with the component instances, connect
|
||||
the instances to signals on the \uicontrol Connections tab in the
|
||||
\uicontrol {Connections} view. For example, you can specify what
|
||||
happens when a component instance is clicked. For more information,
|
||||
see \l{Connecting Components to Signals}.
|
||||
\image qmldesigner-connections.png "Connections View Connections tab"
|
||||
\image qmldesigner-connections.webp "Connections View Connections tab"
|
||||
\li To dynamically change the behavior of a component instance when
|
||||
another component instance changes, create bindings between them on
|
||||
the \uicontrol Bindings tab in the \uicontrol {Connections} view.
|
||||
For more information, see \l{Adding Bindings Between Properties}.
|
||||
\image qmldesigner-bindings.png "Connections view Bindings tab"
|
||||
\image qmldesigner-bindings.webp "Connections view Bindings tab"
|
||||
\li Add states to apply sets of changes to the property values of one
|
||||
or several component instances in the \uicontrol States view.
|
||||
For more information, see \l{Working with States}.
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
the \l{UI Files}{UI files} (.ui.qml), while developers should work
|
||||
on the corresponding implementation files (.qml) to define their
|
||||
programmatic behaviors or JavaScript. This enables iteration from
|
||||
both the design and development side of the process without the the
|
||||
both the design and development side of the process without the
|
||||
risk of overwriting each other's work.
|
||||
\endlist
|
||||
*/
|
||||
|
||||
@@ -421,7 +421,7 @@
|
||||
The child components of grid layout components are arranged according to the
|
||||
\uicontrol Flow property. When the direction of a flow is set to
|
||||
\uicontrol LeftToRight, child components are positioned next to to each
|
||||
other until the the number of columns specified in the
|
||||
other until the number of columns specified in the
|
||||
\uicontrol {Columns & Rows} field is reached. Then,
|
||||
the auto-positioning wraps back to the beginning of the next row.
|
||||
|
||||
|
||||
@@ -73,19 +73,6 @@
|
||||
\note Using 3D components will affect the performance of your UI. Do not
|
||||
use 3D components if the same results can be achieved using 2D components.
|
||||
|
||||
\section2 Videos About 3D Components
|
||||
|
||||
The following video shows you how to add the components included in the
|
||||
\uicontrol {Qt Quick 3D} module, such as 3D models, cameras, and lights,
|
||||
to your scene:
|
||||
|
||||
\youtube u3kZJjlk3CY
|
||||
|
||||
The following video shows you how to use the custom shader utilities, 3D
|
||||
effects, and materials:
|
||||
|
||||
\youtube bMXeeQw6BYs
|
||||
|
||||
The following video shows you how to combine 2D and 3D components:
|
||||
|
||||
\youtube w1yhDl93YI0
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
|
||||
Text can be either in plain text or rich text format, depending on the
|
||||
value you set in the \uicontrol Format field. If you select
|
||||
\uicontrol AutoText and the the first line of text contains an HTML tag,
|
||||
\uicontrol AutoText and the first line of text contains an HTML tag,
|
||||
the text is treated as rich text. Rich text supports a subset of HTML 4
|
||||
described on the \l {Supported HTML Subset}. Note that plain text offers
|
||||
better performance than rich text.
|
||||
|
||||
@@ -31,76 +31,58 @@
|
||||
\e CMakeLists.txt file as the project file. This enables you to share
|
||||
your project as a fully working C++ application with developers.
|
||||
|
||||
If you add or remove QML files in \QDS, you have to regenerate the
|
||||
\e CMakeLists.txt project configuration file by selecting \uicontrol File
|
||||
> \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}.
|
||||
|
||||
If you use Git, you can clone an example project
|
||||
\l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/playground/AuroraCluster0}
|
||||
{here}.
|
||||
|
||||
The following image shows the example project structure and contents in the
|
||||
\l Projects and \l {File System} views in \QDS and Qt Creator:
|
||||
\section1 Exporting a \QDS Project
|
||||
|
||||
\image studio-project-structure.png "\QDS project in \QDS and Qt Creator views"
|
||||
|
||||
\section1 Converting Project Structure for CMake
|
||||
|
||||
\QDS can generate \e CMakeLists.txt and other related files to use with
|
||||
Qt Creator and to compile into an executable application but only if the
|
||||
project has a certain folder structure. If you have a \QDS QML project that
|
||||
doesn't have the CMake configuration, follow these steps to convert its
|
||||
file structure to the correct format.
|
||||
\QDS uses a different project format than Qt Creator. \QDS does not build the project,
|
||||
it uses a pre-compiled \l{QML runtime} to run the project. To export a \QDS project for the
|
||||
Qt Creator, follow the process:
|
||||
|
||||
\list 1
|
||||
\li Create a folder named \e content in the project's folder. This folder contains the
|
||||
application's main module.
|
||||
\li Move all QML files of the project's main module to the \e content folder. If your project
|
||||
has multiple modules, place the other modules in the \e imports or
|
||||
\e asset_imports folder.
|
||||
\li If your project's main module has resource folders such as \e fonts or \e {images}, move
|
||||
them to the \e content folder.
|
||||
\li Create a folder named \e src in the project's folder. This folder contains C++ code for
|
||||
compiling the project.
|
||||
\li If your project doesn't have an \e imports folder for other QML modules, create it
|
||||
now even if you do not have other modules. The CMake file generator expects it.
|
||||
\li In the project's \e .qmlproject file:
|
||||
\list
|
||||
\li Add \e "." in importPaths. For example:
|
||||
\code
|
||||
importPaths: [ "imports", "asset_imports", "." ]
|
||||
\endcode
|
||||
\li Change mainFile to \e "content/App.qml":
|
||||
\code
|
||||
mainFile: "content/App.qml"
|
||||
\endcode
|
||||
\li Open the project you want to export in \QDS.
|
||||
\li Select \uicontrol {File} > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}.
|
||||
\image studio-project-export.webp "Export the \QDS project for Qt Creator"
|
||||
|
||||
\li Select \uicontrol {Details} to access the \l {Advanced Options}.
|
||||
\image studio-project-export-advanced.webp "Access Advanced Options in the project exporter"
|
||||
|
||||
\note The project exporter has default settings selected. This works better if the project
|
||||
is combined with an existing Qt project.
|
||||
|
||||
\li Select all the options here. This allows to export the
|
||||
complete project. So, it can be compiled as a stand-alone application.
|
||||
\image studio-project-export-advanced-options.webp "Select all the options in the project exporter"
|
||||
|
||||
\note If you copy this export on top of the existing Qt Creator project
|
||||
it overwrites the existing project. Hence, the default selected options in
|
||||
the exporter only exports the QML-specific items. You get a list of
|
||||
warnings at the bottom part of the exporter that denotes exactly which parts
|
||||
of the project gets overwritten.
|
||||
\endlist
|
||||
\li In the \e content folder, create a file named \e App.qml and add the following content:
|
||||
|
||||
\qml
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import YourImportModuleHere
|
||||
Window {
|
||||
width: Constants.width
|
||||
height: Constants.height
|
||||
visible: true
|
||||
title: "YourWindowTitleHere"
|
||||
<YourMainQmlClassHere> {
|
||||
}
|
||||
}
|
||||
\endqml
|
||||
\section1 Using the Exported Project in Qt Creator
|
||||
|
||||
\li In \e{App.qml}, modify imported modules, window dimensions, window title, and main QML
|
||||
class appropriately.
|
||||
After exporting the project from the \QDS, you have to open it from Qt Creator.
|
||||
|
||||
\note This template assumes that your project has a module named \e YourImportModuleHere in
|
||||
the \a imports folder containing a singleton class named \a Constants.
|
||||
This isn't mandatory.
|
||||
If you have used any version before \QDS 4.0 to create the project, manually include this code
|
||||
in the \l {CMakeLists.txt} file so the exported project works in Qt Creator.
|
||||
|
||||
\li Generate CMake files and C++ source files that are used to compile the application into
|
||||
an executable file by selecting \uicontrol File > \uicontrol {Export Project} >
|
||||
\uicontrol {Generate CMake Build Files}.
|
||||
\code
|
||||
set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components")
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
if (${BUILD_QDS_COMPONENTS})
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents)
|
||||
endif ()
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
|
||||
\endcode
|
||||
|
||||
\note If you have created the project with the \QDS version 4.0 or above, you already have this code in
|
||||
\l {CMakeLists.txt} by default.
|
||||
|
||||
\endlist
|
||||
*/
|
||||
|
||||
@@ -16,8 +16,14 @@
|
||||
\li \QDS Version
|
||||
\li \QMCU SDK Version
|
||||
\row
|
||||
\li 4.0 or later
|
||||
\li 2.4 or later
|
||||
\li 4.3 or later
|
||||
\li 2.6 or later
|
||||
\row
|
||||
\li 4.2 or later
|
||||
\li 2.5
|
||||
\row
|
||||
\li 4.0 up to 4.1
|
||||
\li 2.4
|
||||
\row
|
||||
\li 3.8 up to 3.9
|
||||
\li 2.3
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
\li \b -
|
||||
\li A scene in the \uicontrol 2D view is rendered by the regular Qt Quick
|
||||
and QML, and not as \QUL and \QMCU, so some imperfections or inaccuracies
|
||||
can occur.
|
||||
can occur. Note that the default font used in \QDS preview and \QUL are
|
||||
different, and the developer must confirm both fonts are the same.
|
||||
\row
|
||||
\li \l 3D
|
||||
\li \b -
|
||||
@@ -71,15 +72,19 @@
|
||||
\li \b -
|
||||
\li \b -
|
||||
\li The \uicontrol Connections view displays all signal handlers in the
|
||||
current file but it doesn't filter available signals, so you can still
|
||||
see and select signals that are available in Qt Quick, but not in \QUL.
|
||||
current file, but it doesn't filter available signals, so you can still
|
||||
see and select signals available in Qt Quick, but not in \QUL.
|
||||
The same also applies if \uicontrol Action is set to \uicontrol{Call Function}
|
||||
and \uicontrol Item is set to \uicontrol Qt. See the component documentation
|
||||
to filter available signal/function.
|
||||
\row
|
||||
\li \l {States}
|
||||
\li \b X
|
||||
\li \b -
|
||||
\li \b -
|
||||
\li The feature is fully supported as such, but there are some
|
||||
limitations listed in \l {\QMCU Known Issues or Limitations}.
|
||||
limitations such as StateGroup and the ones listed in
|
||||
\l {\QMCU Known Issues or Limitations}.
|
||||
\row
|
||||
\li \l {Transitions}
|
||||
\li \b X
|
||||
@@ -89,9 +94,11 @@
|
||||
\row
|
||||
\li \l {Translations}
|
||||
\li \b -
|
||||
\li \b -
|
||||
\li \b X
|
||||
\li \b -
|
||||
\li The \uicontrol Translations view previews with regular Qt Quick instead
|
||||
of \QUL, and it can be inaccurate in calculating the text overflow in some translations.
|
||||
Also, the developer needs to configure the \QUL project to use \QDS translations (.ts) files.
|
||||
\row
|
||||
\li \l {Timeline}
|
||||
\li \b X
|
||||
@@ -100,11 +107,11 @@
|
||||
\li \b -
|
||||
\row
|
||||
\li \l {Curves}
|
||||
\li \b -
|
||||
\li \b X
|
||||
\li \b -
|
||||
\li Linear interpolation works, but \QMCU does not support the
|
||||
\c easing.bezierCurve property of a keyframe.
|
||||
\li \b -
|
||||
\li Linear interpolation works, and \QMCU supports the \c easing.bezierCurve property
|
||||
of a keyframe in \QMCU 2.6 or higher.
|
||||
\row
|
||||
\li \l Code
|
||||
\li \b X
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
\image qtquick-annotation-editor.png "Annotation Editor"
|
||||
\li The \uicontrol {Selected Item} field displays the ID of the
|
||||
component.
|
||||
\li In the the \uicontrol Name field, enter a free-form text that
|
||||
\li In the \uicontrol Name field, enter a free-form text that
|
||||
describes the component.
|
||||
\li In the \uicontrol Title field, enter the text to display in
|
||||
the tab for this comment.
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
\section2 Set the AVD as the Device in the Android Kit
|
||||
|
||||
Next, you need to set the AVD as the Android device kit. You do this under the the
|
||||
Next, you need to set the AVD as the Android device kit. You do this under the
|
||||
\uicontrol Kits tab. If the \uicontrol Kits list is empty, restart \QDS.
|
||||
|
||||
\image qtds-options-kits.png
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
component height is adjusted automatically. Similarly, the opacity of a
|
||||
component can be bound to the opacity of its parent component.
|
||||
|
||||
\image qtquick-connection-editor-assignment.png "Binding Editor"
|
||||
\image qtquick-connection-editor-assignment.webp "Binding Editor"
|
||||
|
||||
Property bindings are created implicitly whenever a property is assigned a
|
||||
JavaScript expression.
|
||||
@@ -115,7 +115,7 @@
|
||||
is to create \l{glossary-binding}{bindings} between the values of their
|
||||
\l{glossary-property}{properties}.
|
||||
|
||||
\image qmldesigner-connections.png "The Connections view"
|
||||
\image qmldesigner-connections.webp "The Connections view"
|
||||
|
||||
Read more about connections:
|
||||
|
||||
@@ -211,9 +211,9 @@
|
||||
the application. For example, the \l {Mouse Area} component has a \c clicked
|
||||
signal that is emitted whenever the mouse is clicked within the area. Since
|
||||
the signal name is \c clicked, the signal handler for receiving this signal
|
||||
is named \c onClicked.
|
||||
is named \c onClicked. Then it performs the defined \uicontrol {Action}.
|
||||
|
||||
\image washingmachineui-connections.png "Connections view, Connections tab"
|
||||
\image qtquick-component-signal.webp "Component signal"
|
||||
|
||||
Further, a signal is automatically emitted when the value of a
|
||||
\l{glossary-property}{property} changes.
|
||||
|
||||
@@ -792,7 +792,7 @@
|
||||
in particle size, specify values for \uicontrol {Particle scale variation}
|
||||
and \uicontrol {Particle end scale variation}.
|
||||
|
||||
\uicontrol {Depth bias} specifies the the depth bias of the emitter. Depth
|
||||
\uicontrol {Depth bias} specifies the depth bias of the emitter. Depth
|
||||
bias is added to the object's distance from camera when sorting objects.
|
||||
This can be used to force the rendering order of objects that are located
|
||||
close to each other if it might otherwise change between frames. Negative
|
||||
@@ -1046,7 +1046,7 @@
|
||||
\uicontrol {Maximum Size} defines the maximum size that the affector can
|
||||
scale particles to.
|
||||
|
||||
\uicontrol Duration defines the the duration of the scaling cycle in
|
||||
\uicontrol Duration defines the duration of the scaling cycle in
|
||||
milliseconds.
|
||||
|
||||
\uicontrol {Easing Curve} defines the
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
\uicontrol {Input 01} field to the value of the \uicontrol {Below min}
|
||||
field of the minimum-maximum mapper for the bad value range. For the
|
||||
\e overValueAnd operator, we bind it to the value of the
|
||||
\uicontrol {Above max} field of the the same mapper.
|
||||
\uicontrol {Above max} field of the same mapper.
|
||||
|
||||
\image studio-logic-helper-combining-example-ao2.png "Under value minimum-maximum mapper Input 01"
|
||||
|
||||
|
||||
4
doc/qtdesignstudio/src/run-tutorial-project.qdocinc
Normal file
@@ -0,0 +1,4 @@
|
||||
\section1 Running the Tutorial Project
|
||||
|
||||
To open the tutorial project in \QDS, open the \e{.qmlproject} file located
|
||||
in the root folder of the downloaded project.
|
||||
@@ -112,6 +112,13 @@
|
||||
\li Open the \uicontrol {Manual Code Edit} window from the
|
||||
\uicontrol {Connections} view and write JavaScript expressions with components
|
||||
and logical expressions manually.
|
||||
|
||||
\note If you create a conditional expression by selecting options from the
|
||||
drop-down menus in the \uicontrol {Connection} view, you can only create a single
|
||||
level \e {if-else} expression. For nested level \e {if-else} expressions,
|
||||
use the \uicontrol {Manual Code Edit}.
|
||||
|
||||
\image qmldesigner-connections-ConditionalAction-Manual.webp
|
||||
\endlist
|
||||
|
||||
\section2 Action Properties
|
||||
@@ -152,11 +159,7 @@
|
||||
\li N/A
|
||||
\endtable
|
||||
|
||||
\note If you create a conditional expression by selecting options from drop-down menus in
|
||||
the \uicontrol {Connection} view, you can only create a single
|
||||
level {if-else} expression. For nested level \e {if-elseif-else} expressions,
|
||||
you have to use the \uicontrol {Manual Code Edit}.
|
||||
|
||||
\image qmldesigner-connections-ConditionalAction-Manual.webp
|
||||
Watch this video for practical examples of the \uicontrol {Connection} view workflow:
|
||||
\youtube KDxnMQzgmIY
|
||||
|
||||
*/
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
You can move the views anywhere on the screen and save them as
|
||||
\e workspaces, as instructed in \l {Managing Workspaces}.
|
||||
|
||||
To learn more about using the design views, see the following video:
|
||||
|
||||
\youtube RfEYO-5Mw6s
|
||||
|
||||
\section1 Summary of Design Views
|
||||
|
||||
In addition to the summary of design views, the table below includes an MCU
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
\li \l{Previewing Component Size}
|
||||
\row
|
||||
\li \inlineimage icons/canvas-color.png
|
||||
\li Sets the color of the the \uicontrol {2D} view working area.
|
||||
\li Sets the color of the \uicontrol {2D} view working area.
|
||||
\li \l{Setting Canvas Color}
|
||||
\row
|
||||
\li \inlineimage icons/zoomIn.png
|
||||
|
||||
@@ -32,12 +32,7 @@ TreeViewDelegate {
|
||||
readonly property int __dirItemHeight: 21
|
||||
|
||||
implicitHeight: root.__isDirectory ? root.__dirItemHeight : root.__fileItemHeight
|
||||
implicitWidth: {
|
||||
if (root.assetsView.verticalScrollBar.scrollBarVisible)
|
||||
return root.assetsView.width - root.indentation - root.assetsView.verticalScrollBar.width
|
||||
else
|
||||
return root.assetsView.width - root.indentation
|
||||
}
|
||||
implicitWidth: root.assetsView.width
|
||||
|
||||
leftMargin: root.__isDirectory ? 0 : thumbnailImage.width
|
||||
|
||||
@@ -88,7 +83,7 @@ TreeViewDelegate {
|
||||
background: Rectangle {
|
||||
id: bg
|
||||
|
||||
x: root.indentation * root.depth
|
||||
x: root.indentation * (root.depth - 1)
|
||||
width: root.implicitWidth - bg.x
|
||||
|
||||
color: {
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import CollectionDetails 1.0 as CollectionDetails
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioHelpers as StudioHelpers
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import QtQuick.Templates as T
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property var columnType
|
||||
|
||||
property var __modifier : textEditor
|
||||
property bool __changesAccepted: true
|
||||
|
||||
TableView.onCommit: {
|
||||
if (root.__changesAccepted)
|
||||
edit = __modifier.editor.editValue
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
__changesAccepted = true
|
||||
if (edit && edit !== "")
|
||||
root.__modifier.editor.editValue = edit
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (root.activeFocus)
|
||||
root.__modifier.editor.forceActiveFocus()
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: modifierFocusConnection
|
||||
|
||||
target: root.__modifier.editor
|
||||
|
||||
function onActiveFocusChanged() {
|
||||
if (!modifierFocusConnection.target.activeFocus)
|
||||
root.TableView.commit()
|
||||
}
|
||||
}
|
||||
|
||||
EditorPopup {
|
||||
id: textEditor
|
||||
|
||||
editor: textField
|
||||
|
||||
StudioControls.TextField {
|
||||
id: textField
|
||||
|
||||
property alias editValue: textField.text
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicatorVisible: false
|
||||
|
||||
onRejected: root.__changesAccepted = false
|
||||
}
|
||||
}
|
||||
|
||||
EditorPopup {
|
||||
id: numberEditor
|
||||
|
||||
editor: numberField
|
||||
|
||||
StudioControls.RealSpinBox {
|
||||
id: numberField
|
||||
|
||||
property alias editValue: numberField.realValue
|
||||
|
||||
actionIndicator.visible: false
|
||||
realFrom: -9e9
|
||||
realTo: 9e9
|
||||
realStepSize: 1.0
|
||||
decimals: 6
|
||||
}
|
||||
}
|
||||
|
||||
EditorPopup {
|
||||
id: boolEditor
|
||||
|
||||
editor: boolField
|
||||
|
||||
StudioControls.CheckBox {
|
||||
id: boolField
|
||||
|
||||
property alias editValue: boolField.checked
|
||||
|
||||
actionIndicatorVisible: false
|
||||
}
|
||||
}
|
||||
|
||||
EditorPopup {
|
||||
id: colorEditor
|
||||
|
||||
editor: colorPicker
|
||||
|
||||
implicitHeight: colorPicker.height + topPadding + bottomPadding
|
||||
implicitWidth: colorPicker.width + leftPadding + rightPadding
|
||||
padding: 8
|
||||
|
||||
StudioHelpers.ColorBackend {
|
||||
id: colorBackend
|
||||
}
|
||||
|
||||
StudioControls.ColorEditorPopup {
|
||||
id: colorPicker
|
||||
|
||||
property alias editValue: colorBackend.color
|
||||
color: colorBackend.color
|
||||
|
||||
width: 200
|
||||
|
||||
Keys.onEnterPressed: colorPicker.focus = false
|
||||
|
||||
onActivateColor: function(color) {
|
||||
colorBackend.activateColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
border.color: StudioTheme.Values.themeInteraction
|
||||
border.width: StudioTheme.Values.border
|
||||
}
|
||||
}
|
||||
|
||||
component EditorPopup: T.Popup {
|
||||
id: editorPopup
|
||||
|
||||
required property Item editor
|
||||
|
||||
implicitHeight: contentHeight
|
||||
implicitWidth: contentWidth
|
||||
|
||||
enabled: visible
|
||||
visible: false
|
||||
|
||||
Connections {
|
||||
target: editorPopup.editor
|
||||
|
||||
function onActiveFocusChanged() {
|
||||
if (!editorPopup.editor.activeFocus)
|
||||
editorPopup.close()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: editorPopup.editor.Keys
|
||||
|
||||
function onEscapePressed() {
|
||||
root.__changesAccepted = false
|
||||
editorPopup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
when: columnType !== CollectionDetails.DataType.Boolean
|
||||
&& columnType !== CollectionDetails.DataType.Color
|
||||
&& columnType !== CollectionDetails.DataType.Number
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
__modifier: textEditor
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: textEditor
|
||||
visible: true
|
||||
focus: true
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "number"
|
||||
when: columnType === CollectionDetails.DataType.Number
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
__modifier: numberEditor
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: numberEditor
|
||||
visible: true
|
||||
focus: true
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "bool"
|
||||
when: columnType === CollectionDetails.DataType.Boolean
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
__modifier: boolEditor
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: boolEditor
|
||||
visible: true
|
||||
focus: true
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "color"
|
||||
when: columnType === CollectionDetails.DataType.Color
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
__modifier: colorEditor
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: colorEditor
|
||||
visible: true
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.platform as PlatformWidgets
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import CollectionEditorBackend
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real iconHeight: 2 * StudioTheme.Values.bigFont
|
||||
required property var model
|
||||
required property var backend
|
||||
property int selectedRow: -1
|
||||
|
||||
implicitHeight: container.height
|
||||
|
||||
function addNewColumn() {
|
||||
addColumnDialog.popUp(root.model.columnCount())
|
||||
}
|
||||
|
||||
function addNewRow() {
|
||||
root.model.insertRow(root.model.rowCount())
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: container
|
||||
width: parent.width
|
||||
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
RowLayout {
|
||||
id: leftSideToolbar
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.addcolumnleft_medium
|
||||
tooltip: qsTr("Add property left %1").arg(leftSideToolbar.topPadding)
|
||||
enabled: root.model.selectedColumn > -1
|
||||
onClicked: addColumnDialog.popUp(root.model.selectedColumn - 1)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.addcolumnright_medium
|
||||
tooltip: qsTr("Add property right")
|
||||
enabled: root.model.selectedColumn > -1
|
||||
onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.deletecolumn_medium
|
||||
tooltip: qsTr("Delete selected property")
|
||||
enabled: root.model.selectedColumn > -1
|
||||
onClicked: root.model.removeColumn(root.model.selectedColumn)
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
implicitWidth: StudioTheme.Values.toolbarSpacing
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.addrowbelow_medium
|
||||
tooltip: qsTr("Insert row below")
|
||||
enabled: root.model.selectedRow > -1
|
||||
onClicked: root.model.insertRow(root.model.selectedRow + 1)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.addrowabove_medium
|
||||
tooltip: qsTr("Insert row above")
|
||||
enabled: root.model.selectedRow > -1
|
||||
onClicked: root.model.insertRow(root.model.selectedRow)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.deleterow_medium
|
||||
tooltip: qsTr("Delete selected row")
|
||||
enabled: root.model.selectedRow > -1
|
||||
onClicked: root.model.removeRow(root.model.selectedRow)
|
||||
}
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
Layout.minimumHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rightSideToolbar
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.updateContent_medium
|
||||
tooltip: qsTr("Update existing file with changes")
|
||||
enabled: root.model.collectionName !== ""
|
||||
onClicked:
|
||||
{
|
||||
if (root.backend.selectedSourceAddress().indexOf("json") !== -1)
|
||||
root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "JSON")
|
||||
else
|
||||
root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "CSV")
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.export_medium
|
||||
tooltip: qsTr("Export the model to a new file")
|
||||
enabled: root.model.collectionName !== ""
|
||||
onClicked: exportMenu.popup()
|
||||
}
|
||||
|
||||
StudioControls.Menu {
|
||||
id: exportMenu
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Export as JSON")
|
||||
onTriggered:
|
||||
{
|
||||
fileDialog.defaultSuffix = "json"
|
||||
fileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Export as CSV")
|
||||
onTriggered:
|
||||
{
|
||||
fileDialog.defaultSuffix = "csv"
|
||||
fileDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlatformWidgets.FileDialog {
|
||||
id: fileDialog
|
||||
fileMode: PlatformWidgets.FileDialog.SaveFile
|
||||
onAccepted:
|
||||
{
|
||||
var fileAddress = file.toString()
|
||||
|
||||
if (fileAddress.indexOf("json") !== -1)
|
||||
root.model.exportCollection(fileAddress, root.model.collectionName, "JSON")
|
||||
else if (fileAddress.indexOf("csv") !== -1)
|
||||
root.model.exportCollection(fileAddress, root.model.collectionName, "CSV")
|
||||
|
||||
fileDialog.reject()
|
||||
}
|
||||
}
|
||||
|
||||
component IconButton: HelperWidgets.IconButton {
|
||||
Layout.preferredHeight: root.iconHeight
|
||||
Layout.preferredWidth: root.iconHeight
|
||||
radius: StudioTheme.Values.smallRadius
|
||||
iconSize: StudioTheme.Values.bigFont
|
||||
}
|
||||
|
||||
component Spacer: Item {
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.columnGap
|
||||
}
|
||||
|
||||
RegularExpressionValidator {
|
||||
id: nameValidator
|
||||
regularExpression: /^\w+$/
|
||||
}
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: addColumnDialog
|
||||
|
||||
property int clickedIndex: -1
|
||||
property bool nameIsValid
|
||||
|
||||
title: qsTr("Add Column")
|
||||
|
||||
function popUp(index)
|
||||
{
|
||||
addColumnDialog.clickedIndex = index
|
||||
columnName.text = ""
|
||||
addedPropertyType.currentIndex = addedPropertyType.find("String")
|
||||
|
||||
addColumnDialog.open()
|
||||
}
|
||||
|
||||
function addColumnName() {
|
||||
if (addColumnDialog.nameIsValid) {
|
||||
root.model.addColumn(addColumnDialog.clickedIndex, columnName.text, addedPropertyType.currentText)
|
||||
addColumnDialog.accept()
|
||||
} else {
|
||||
addColumnDialog.reject()
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: qsTr("Column name:")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: columnName
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: nameValidator
|
||||
|
||||
Keys.onEnterPressed: addColumnDialog.addColumnName()
|
||||
Keys.onReturnPressed: addColumnDialog.addColumnName()
|
||||
Keys.onEscapePressed: addColumnDialog.reject()
|
||||
|
||||
onTextChanged: {
|
||||
addColumnDialog.nameIsValid = (columnName.text !== ""
|
||||
&& !root.model.isPropertyAvailable(columnName.text))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer { implicitHeight: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("The model already contains \"%1\"!").arg(columnName.text)
|
||||
visible: columnName.text !== "" && !addColumnDialog.nameIsValid
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Label.WordWrap
|
||||
padding: 5
|
||||
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
border.width: StudioTheme.Values.border
|
||||
border.color: StudioTheme.Values.themeWarning
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
|
||||
Text {
|
||||
text: qsTr("Type:")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.ComboBox {
|
||||
id: addedPropertyType
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
model: root.model.typesList()
|
||||
actionIndicatorVisible: false
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
HelperWidgets.Button {
|
||||
enabled: addColumnDialog.nameIsValid
|
||||
text: qsTr("Add")
|
||||
onClicked: addColumnDialog.addColumnName()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: addColumnDialog.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import StudioControls 1.0 as StudioControls
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
required property var model
|
||||
required property var backend
|
||||
required property var sortedModel
|
||||
|
||||
implicitWidth: 300
|
||||
implicitHeight: 400
|
||||
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
|
||||
ColumnLayout {
|
||||
id: topRow
|
||||
|
||||
visible: collectionNameText.text !== ""
|
||||
|
||||
spacing: 0
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 10
|
||||
leftMargin: 15
|
||||
rightMargin: 15
|
||||
bottomMargin: 10
|
||||
}
|
||||
|
||||
Text {
|
||||
id: collectionNameText
|
||||
|
||||
leftPadding: 8
|
||||
rightPadding: 8
|
||||
topPadding: 3
|
||||
bottomPadding: 3
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
text: root.model.collectionName
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
implicitWidth: 1
|
||||
implicitHeight: 10
|
||||
}
|
||||
|
||||
CollectionDetailsToolbar {
|
||||
id: toolbar
|
||||
model: root.model
|
||||
backend: root.backend
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: implicitWidth
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
implicitWidth: 1
|
||||
implicitHeight: 5
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: 3
|
||||
rowSpacing: 1
|
||||
columnSpacing: 1
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.maximumWidth: parent.width
|
||||
|
||||
Rectangle {
|
||||
clip: true
|
||||
visible: !tableView.model.isEmpty
|
||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
border.color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
border.width: 2
|
||||
|
||||
Layout.preferredWidth: rowIdView.width
|
||||
Layout.preferredHeight: headerView.height
|
||||
Layout.minimumWidth: rowIdView.width
|
||||
Layout.minimumHeight: headerView.height
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
font: headerTextMetrics.font
|
||||
text: "#"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalHeaderView {
|
||||
id: headerView
|
||||
|
||||
property real topPadding: 5
|
||||
property real bottomPadding: 5
|
||||
|
||||
Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding
|
||||
Layout.columnSpan: 2
|
||||
syncView: tableView
|
||||
clip: true
|
||||
|
||||
delegate: HeaderDelegate {
|
||||
id: horizontalHeaderItem
|
||||
|
||||
selectedItem: tableView.model.selectedColumn
|
||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
|
||||
function getGlobalBottomLeft() {
|
||||
return mapToGlobal(0, horizontalHeaderItem.height)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: (mouse) => {
|
||||
tableView.model.selectColumn(index)
|
||||
|
||||
if (mouse.button === Qt.RightButton)
|
||||
headerMenu.popIndex(index, horizontalHeaderItem.getGlobalBottomLeft())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.Menu {
|
||||
id: headerMenu
|
||||
|
||||
property int clickedHeader: -1
|
||||
property point initialPosition
|
||||
|
||||
function popIndex(clickedIndex, clickedRect)
|
||||
{
|
||||
headerMenu.clickedHeader = clickedIndex
|
||||
headerMenu.initialPosition = clickedRect
|
||||
headerMenu.popup()
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
headerMenu.clickedHeader = -1
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Edit")
|
||||
onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader, headerMenu.initialPosition)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Delete")
|
||||
onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Sort Ascending")
|
||||
onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.AscendingOrder)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Sort Descending")
|
||||
onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.DescendingOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalHeaderView {
|
||||
id: rowIdView
|
||||
|
||||
syncView: tableView
|
||||
clip: true
|
||||
|
||||
Layout.preferredHeight: tableView.height
|
||||
Layout.rowSpan: 2
|
||||
Layout.alignment: Qt.AlignTop + Qt.AlignLeft
|
||||
|
||||
delegate: HeaderDelegate {
|
||||
selectedItem: tableView.model.selectedRow
|
||||
color: StudioTheme.Values.themeControlBackgroundHover
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: tableView.model.selectRow(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
|
||||
model: root.sortedModel
|
||||
clip: true
|
||||
|
||||
Layout.preferredWidth: tableView.contentWidth
|
||||
Layout.preferredHeight: tableView.contentHeight
|
||||
Layout.minimumWidth: 100
|
||||
Layout.minimumHeight: 20
|
||||
Layout.maximumWidth: root.width
|
||||
|
||||
delegate: Rectangle {
|
||||
id: itemCell
|
||||
implicitWidth: 100
|
||||
implicitHeight: itemText.height
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: itemText
|
||||
|
||||
text: display ? display : ""
|
||||
|
||||
width: parent.width
|
||||
leftPadding: 5
|
||||
topPadding: 3
|
||||
bottomPadding: 3
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
TableView.editDelegate: CollectionDetailsEditDelegate {
|
||||
anchors {
|
||||
top: itemText.top
|
||||
left: itemText.left
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
when: !itemSelected
|
||||
|
||||
PropertyChanges {
|
||||
target: itemCell
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: itemText
|
||||
color: StudioTheme.Values.themePlaceholderTextColorInteraction
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "selected"
|
||||
when: itemSelected
|
||||
|
||||
PropertyChanges {
|
||||
target: itemCell
|
||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
border.color: StudioTheme.Values.themeControlBackground
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: itemText
|
||||
color: StudioTheme.Values.themeInteraction
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.IconButton {
|
||||
id: addColumnContainer
|
||||
|
||||
iconSize:16
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: tableView.height
|
||||
Layout.minimumHeight: 24
|
||||
Layout.alignment: Qt.AlignLeft + Qt.AlignVCenter
|
||||
|
||||
icon: StudioTheme.Constants.create_medium
|
||||
tooltip: "Add Column"
|
||||
|
||||
onClicked: toolbar.addNewColumn()
|
||||
}
|
||||
|
||||
HelperWidgets.IconButton {
|
||||
id: addRowContainer
|
||||
|
||||
iconSize:16
|
||||
Layout.preferredWidth: tableView.width
|
||||
Layout.preferredHeight: 24
|
||||
Layout.minimumWidth: 24
|
||||
Layout.alignment: Qt.AlignTop + Qt.AlignHCenter
|
||||
|
||||
icon: StudioTheme.Constants.create_medium
|
||||
tooltip: "Add Row"
|
||||
|
||||
onClicked: toolbar.addNewRow()
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
text: qsTr("Select a model to continue")
|
||||
visible: !topRow.visible
|
||||
textFormat: Text.RichText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: headerTextMetrics
|
||||
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
text: "Xq"
|
||||
}
|
||||
|
||||
component HeaderDelegate: Rectangle {
|
||||
id: headerItem
|
||||
|
||||
required property int selectedItem
|
||||
property alias horizontalAlignment: headerText.horizontalAlignment
|
||||
property alias verticalAlignment: headerText.verticalAlignment
|
||||
|
||||
implicitWidth: headerText.implicitWidth
|
||||
implicitHeight: headerText.implicitHeight
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
id: headerText
|
||||
|
||||
topPadding: headerView.topPadding
|
||||
bottomPadding: headerView.bottomPadding
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
text: display
|
||||
font: headerTextMetrics.font
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.fill: parent
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
when: index !== selectedItem
|
||||
PropertyChanges {
|
||||
target: headerItem
|
||||
border.color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: headerText
|
||||
font.bold: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "selected"
|
||||
when: index === selectedItem
|
||||
|
||||
PropertyChanges {
|
||||
target: headerItem
|
||||
border.color: StudioTheme.Values.themeControlBackground
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: headerText
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
EditPropertyDialog {
|
||||
id: editProperyDialog
|
||||
model: root.model
|
||||
}
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: deleteColumnDialog
|
||||
|
||||
property int clickedIndex: -1
|
||||
|
||||
title: qsTr("Delete Column")
|
||||
width: 400
|
||||
|
||||
onAccepted: {
|
||||
root.model.removeColumn(clickedIndex)
|
||||
}
|
||||
|
||||
function popUp(index)
|
||||
{
|
||||
deleteColumnDialog.clickedIndex = index
|
||||
deleteColumnDialog.open()
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: StudioTheme.Values.sectionColumnSpacing
|
||||
|
||||
Text {
|
||||
text: qsTr("Are you sure that you want to delete column \"%1\"?").arg(
|
||||
root.model.headerData(
|
||||
deleteColumnDialog.clickedIndex, Qt.Horizontal))
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Delete")
|
||||
onClicked: deleteColumnDialog.accept()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: deleteColumnDialog.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform as PlatformWidgets
|
||||
import QtQuick.Layouts
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
@@ -12,9 +12,10 @@ Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: 300
|
||||
implicitHeight: innerRect.height + 6
|
||||
implicitHeight: innerRect.height + 3
|
||||
|
||||
property color textColor
|
||||
property string sourceType
|
||||
|
||||
signal selectItem(int itemIndex)
|
||||
signal deleteItem()
|
||||
@@ -23,7 +24,7 @@ Item {
|
||||
id: boundingRect
|
||||
|
||||
anchors.centerIn: root
|
||||
width: root.width - 24
|
||||
width: parent.width
|
||||
height: nameHolder.height
|
||||
clip: true
|
||||
|
||||
@@ -47,21 +48,23 @@ Item {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width - threeDots.width
|
||||
leftPadding: 20
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
id: moveTool
|
||||
|
||||
property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
|
||||
|
||||
width: moveTool.style.squareControlSize.width
|
||||
height: nameHolder.height
|
||||
Layout.preferredWidth: moveTool.style.squareControlSize.width
|
||||
Layout.preferredHeight: nameHolder.height
|
||||
Layout.leftMargin: 12
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
text: StudioTheme.Constants.dragmarks
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
font.pixelSize: moveTool.style.baseIconFontSize
|
||||
color: root.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
@@ -69,9 +72,12 @@ Item {
|
||||
Text {
|
||||
id: nameHolder
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
text: collectionName
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
color: textColor
|
||||
color: root.textColor
|
||||
leftPadding: 5
|
||||
topPadding: 8
|
||||
rightPadding: 8
|
||||
@@ -79,82 +85,92 @@ Item {
|
||||
elide: Text.ElideMiddle
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: threeDots
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: StudioTheme.Constants.more_medium
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||
color: textColor
|
||||
anchors.right: boundingRect.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.textColor
|
||||
rightPadding: 12
|
||||
topPadding: nameHolder.topPadding
|
||||
bottomPadding: nameHolder.bottomPadding
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton + Qt.LeftButton
|
||||
onClicked: (event) => {
|
||||
collectionMenu.open()
|
||||
event.accepted = true
|
||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
||||
onClicked: collectionMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlatformWidgets.Menu {
|
||||
StudioControls.Menu {
|
||||
id: collectionMenu
|
||||
|
||||
PlatformWidgets.MenuItem {
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Delete")
|
||||
shortcut: StandardKey.Delete
|
||||
onTriggered: deleteDialog.open()
|
||||
}
|
||||
|
||||
PlatformWidgets.MenuItem {
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Rename")
|
||||
shortcut: StandardKey.Replace
|
||||
onTriggered: renameDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
component Spacer: Item {
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.columnGap
|
||||
}
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: deleteDialog
|
||||
|
||||
title: qsTr("Deleting whole collection")
|
||||
title: qsTr("Deleting the model")
|
||||
clip: true
|
||||
|
||||
contentItem: Column {
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: qsTr("Are you sure that you want to delete collection \"" + collectionName + "\"?")
|
||||
Layout.fillWidth: true
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
text: {
|
||||
if (root.sourceType === "json") {
|
||||
qsTr("Are you sure that you want to delete model \"%1\"?"
|
||||
+ "\nThe model will be deleted permanently.").arg(collectionName)
|
||||
} else if (root.sourceType === "csv") {
|
||||
qsTr("Are you sure that you want to delete model \"%1\"?"
|
||||
+ "\nThe model will be removed from the project "
|
||||
+ "but the file will not be deleted.").arg(collectionName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
}
|
||||
Spacer {}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnDelete
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("Delete")
|
||||
onClicked: root.deleteItem(index)
|
||||
onClicked: root.deleteItem()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: deleteDialog.reject()
|
||||
}
|
||||
}
|
||||
@@ -164,7 +180,7 @@ Item {
|
||||
StudioControls.Dialog {
|
||||
id: renameDialog
|
||||
|
||||
title: qsTr("Rename collection")
|
||||
title: qsTr("Rename model")
|
||||
|
||||
onAccepted: {
|
||||
if (newNameField.text !== "")
|
||||
@@ -175,7 +191,7 @@ Item {
|
||||
newNameField.text = collectionName
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
@@ -183,9 +199,10 @@ Item {
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
Spacer {}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
text: qsTr("New name:")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
@@ -193,7 +210,9 @@ Item {
|
||||
StudioControls.TextField {
|
||||
id: newNameField
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: newNameValidator
|
||||
@@ -206,37 +225,31 @@ Item {
|
||||
btnRename.enabled = newNameField.text !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
}
|
||||
Spacer {}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnRename
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Rename")
|
||||
onClicked: renameDialog.accept()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: renameDialog.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
RegularExpressionValidator {
|
||||
id: newNameValidator
|
||||
regExp: /^\w+$/
|
||||
regularExpression: /^\w+$/
|
||||
}
|
||||
|
||||
states: [
|
||||
@@ -277,12 +290,12 @@ Item {
|
||||
PropertyChanges {
|
||||
target: innerRect
|
||||
opacity: 1
|
||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
color: StudioTheme.Values.themeIconColorSelected
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
textColor: StudioTheme.Values.themeIconColorSelected
|
||||
textColor: StudioTheme.Values.themeTextSelectedTextColor
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
@@ -14,7 +15,8 @@ Item {
|
||||
|
||||
property var rootView: CollectionEditorBackend.rootView
|
||||
property var model: CollectionEditorBackend.model
|
||||
property var singleCollectionModel: CollectionEditorBackend.singleCollectionModel
|
||||
property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel
|
||||
property var collectionDetailsSortFilterModel: CollectionEditorBackend.collectionDetailsSortFilterModel
|
||||
|
||||
function showWarning(title, message) {
|
||||
warningDialog.title = title
|
||||
@@ -40,6 +42,7 @@ Item {
|
||||
id: newCollection
|
||||
|
||||
backendValue: root.rootView
|
||||
sourceModel: root.model
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
@@ -50,57 +53,63 @@ Item {
|
||||
message: ""
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: collectionsRect
|
||||
GridLayout {
|
||||
id: grid
|
||||
readonly property bool isHorizontal: width >= 500
|
||||
|
||||
color: StudioTheme.Values.themeToolbarBackground
|
||||
width: 300
|
||||
height: root.height
|
||||
anchors.fill: parent
|
||||
columns: isHorizontal ? 3 : 1
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
ColumnLayout {
|
||||
id: collectionsSideBar
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: StudioTheme.Values.height + 5
|
||||
color: StudioTheme.Values.themeToolbarBackground
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Layout.minimumWidth: 300
|
||||
Layout.fillWidth: !grid.isHorizontal
|
||||
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 50
|
||||
|
||||
Text {
|
||||
id: collectionText
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Collections")
|
||||
font.pixelSize: StudioTheme.Values.mediumIconFont
|
||||
text: qsTr("Data Models")
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
leftPadding: 15
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rightPadding: 12
|
||||
spacing: 2
|
||||
IconTextButton {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
HelperWidgets.IconButton {
|
||||
icon: StudioTheme.Constants.downloadjson_large
|
||||
tooltip: qsTr("Import Json")
|
||||
icon: StudioTheme.Constants.import_medium
|
||||
text: qsTr("JSON")
|
||||
tooltip: qsTr("Import JSON")
|
||||
radius: StudioTheme.Values.smallRadius
|
||||
|
||||
onClicked: jsonImporter.open()
|
||||
}
|
||||
|
||||
HelperWidgets.IconButton {
|
||||
icon: StudioTheme.Constants.downloadcsv_large
|
||||
IconTextButton {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
icon: StudioTheme.Constants.import_medium
|
||||
text: qsTr("CSV")
|
||||
tooltip: qsTr("Import CSV")
|
||||
radius: StudioTheme.Values.smallRadius
|
||||
|
||||
onClicked: csvImporter.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Collections
|
||||
width: parent.width
|
||||
Rectangle { // Model Groups
|
||||
Layout.fillWidth: true
|
||||
color: StudioTheme.Values.themeBackgroundColorNormal
|
||||
height: 330
|
||||
Layout.minimumHeight: 150
|
||||
Layout.preferredHeight: sourceListView.contentHeight
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -114,41 +123,46 @@ Item {
|
||||
ListView {
|
||||
id: sourceListView
|
||||
|
||||
width: parent.width
|
||||
height: contentHeight
|
||||
anchors.fill: parent
|
||||
model: root.model
|
||||
|
||||
delegate: ModelSourceItem {
|
||||
implicitWidth: sourceListView.width
|
||||
onDeleteItem: root.model.removeRow(index)
|
||||
hasSelectedTarget: root.rootView.targetNodeSelected
|
||||
onAssignToSelected: root.rootView.assignSourceNodeToSelectedItem(sourceNode)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: addCollectionButton.height
|
||||
color: StudioTheme.Values.themeBackgroundColorNormal
|
||||
|
||||
IconTextButton {
|
||||
HelperWidgets.IconButton {
|
||||
id: addCollectionButton
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Add new collection")
|
||||
iconSize:16
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 24
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
|
||||
tooltip: qsTr("Add a new model")
|
||||
icon: StudioTheme.Constants.create_medium
|
||||
onClicked: newCollection.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Splitter
|
||||
Layout.fillWidth: !grid.isHorizontal
|
||||
Layout.fillHeight: grid.isHorizontal
|
||||
Layout.minimumWidth: 2
|
||||
Layout.minimumHeight: 2
|
||||
color: "black"
|
||||
}
|
||||
|
||||
SingleCollectionView {
|
||||
model: root.singleCollectionModel
|
||||
anchors {
|
||||
left: collectionsRect.right
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
CollectionDetailsView {
|
||||
model: root.collectionDetailsModel
|
||||
backend: root.model
|
||||
sortedModel: root.collectionDetailsSortFilterModel
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import Qt.labs.platform as PlatformWidgets
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
@@ -32,9 +33,9 @@ StudioControls.Dialog {
|
||||
fileName.text = ""
|
||||
}
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
RegularExpressionValidator {
|
||||
id: fileNameValidator
|
||||
regExp: /^(\w[^*><?|]*)[^/\\:*><?|]$/
|
||||
regularExpression: /^(\w[^*><?|]*)[^/\\:*><?|]$/
|
||||
}
|
||||
|
||||
PlatformWidgets.FileDialog {
|
||||
@@ -51,21 +52,30 @@ StudioControls.Dialog {
|
||||
onClosed: root.reject()
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 10
|
||||
component Spacer: Item {
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.columnGap
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
Text {
|
||||
text: qsTr("File name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
StudioControls.TextField {
|
||||
id: fileName
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: fileNameValidator
|
||||
@@ -74,49 +84,58 @@ StudioControls.Dialog {
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
|
||||
onTextChanged: {
|
||||
root.fileExists = root.backendValue.isCsvFile(fileName.text)
|
||||
}
|
||||
onTextChanged: root.fileExists = root.backendValue.isCsvFile(fileName.text)
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: fileDialogButton
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
text: qsTr("Open")
|
||||
onClicked: fileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
Spacer {}
|
||||
|
||||
Text {
|
||||
text: qsTr("Collection name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("The model name: ")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: collectionName
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: HelperWidgets.RegExpValidator {
|
||||
regExp: /^\w+$/
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /^\w+$/
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: btnCreate.onClicked()
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Spacer { implicitHeight: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
Label {
|
||||
id: fieldErrorText
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
anchors.right: parent.right
|
||||
wrapMode: Label.WordWrap
|
||||
padding: 5
|
||||
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
border.width: StudioTheme.Values.border
|
||||
border.color: StudioTheme.Values.themeWarning
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
@@ -145,31 +164,28 @@ StudioControls.Dialog {
|
||||
|
||||
PropertyChanges {
|
||||
target: fieldErrorText
|
||||
text: qsTr("Collection name can not be empty")
|
||||
text: qsTr("The model name can not be empty")
|
||||
visible: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
}
|
||||
Spacer {}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnCreate
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Import")
|
||||
enabled: root.fileExists && collectionName.text !== ""
|
||||
|
||||
onClicked: {
|
||||
let csvLoaded = root.backendValue.loadCsvFile(collectionName.text, fileName.text)
|
||||
let csvLoaded = root.backendValue.loadCsvFile(fileName.text, collectionName.text)
|
||||
|
||||
if (csvLoaded)
|
||||
root.accept()
|
||||
@@ -180,7 +196,6 @@ StudioControls.Dialog {
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: root
|
||||
|
||||
required property var model
|
||||
property int __propertyIndex: -1
|
||||
property string __oldName
|
||||
|
||||
title: qsTr("Edit Property")
|
||||
clip: true
|
||||
|
||||
function editProperty(index, initialPosition) {
|
||||
root.__propertyIndex = index
|
||||
|
||||
if (root.__propertyIndex < 0)
|
||||
return
|
||||
|
||||
let previousName = root.model.propertyName(root.__propertyIndex)
|
||||
let previousType = root.model.propertyType(root.__propertyIndex)
|
||||
|
||||
root.__oldName = previousName
|
||||
newNameField.text = previousName
|
||||
|
||||
propertyType.initialType = previousType
|
||||
|
||||
forceChangeType.checked = false
|
||||
|
||||
let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y)
|
||||
x = newPoint.x
|
||||
y = newPoint.y
|
||||
|
||||
root.open()
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (newNameField.text !== "" && newNameField.text !== root.__oldName)
|
||||
root.model.renameColumn(root.__propertyIndex, newNameField.text)
|
||||
|
||||
if (propertyType.changed || forceChangeType.checked)
|
||||
root.model.setPropertyType(root.__propertyIndex, propertyType.currentText, forceChangeType.checked)
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
let currentDatatype = propertyType.initialType
|
||||
propertyType.currentIndex = propertyType.find(currentDatatype)
|
||||
}
|
||||
|
||||
component Spacer: Item {
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.columnGap
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: qsTr("Name")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: newNameField
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
|
||||
Keys.onEnterPressed: root.accept()
|
||||
Keys.onReturnPressed: root.accept()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /^\w+$/
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
editButton.enabled = newNameField.text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
|
||||
Text {
|
||||
text: qsTr("Type")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.ComboBox {
|
||||
id: propertyType
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
property string initialType
|
||||
readonly property bool changed: propertyType.initialType !== propertyType.currentText
|
||||
|
||||
model: root.model.typesList()
|
||||
actionIndicatorVisible: false
|
||||
|
||||
onInitialTypeChanged: propertyType.currentIndex = propertyType.find(initialType)
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
StudioControls.CheckBox {
|
||||
id: forceChangeType
|
||||
actionIndicatorVisible: false
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Force update values")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {
|
||||
visible: warningBox.visible
|
||||
implicitHeight: StudioTheme.Values.controlLabelGap
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: warningBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: warning.height
|
||||
|
||||
visible: propertyType.initialType !== propertyType.currentText
|
||||
color: "transparent"
|
||||
clip: true
|
||||
border.color: StudioTheme.Values.themeWarning
|
||||
|
||||
RowLayout {
|
||||
id: warning
|
||||
|
||||
width: parent.width
|
||||
|
||||
HelperWidgets.IconLabel {
|
||||
icon: StudioTheme.Constants.warning_medium
|
||||
Layout.leftMargin: 10
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Conversion from %1 to %2 may lead to irreversible data loss")
|
||||
.arg(propertyType.initialType)
|
||||
.arg(propertyType.currentText)
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: editButton
|
||||
|
||||
text: qsTr("Edit")
|
||||
onClicked: root.accept()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Rectangle {
|
||||
@@ -10,42 +12,45 @@ Rectangle {
|
||||
required property string text
|
||||
required property string icon
|
||||
|
||||
property alias tooltip: toolTip.text
|
||||
property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
|
||||
property int fontSize: StudioTheme.Values.baseFontSize
|
||||
|
||||
implicitHeight: style.squareControlSize.height
|
||||
implicitWidth: rowAlign.width
|
||||
|
||||
signal clicked()
|
||||
|
||||
Row {
|
||||
RowLayout {
|
||||
id: rowAlign
|
||||
spacing: 0
|
||||
leftPadding: StudioTheme.Values.inputHorizontalPadding
|
||||
rightPadding: StudioTheme.Values.inputHorizontalPadding
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: StudioTheme.Values.inputHorizontalPadding
|
||||
|
||||
Text {
|
||||
id: iconItem
|
||||
width: root.style.squareControlSize.width
|
||||
height: root.height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
text: root.icon
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
font.pixelSize: root.style.baseIconFontSize
|
||||
font.pixelSize: StudioTheme.Values.bigFont
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: StudioTheme.Values.inputHorizontalPadding
|
||||
}
|
||||
|
||||
Text {
|
||||
id: textItem
|
||||
height: root.height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
text: root.text
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.family: StudioTheme.Constants.font.family
|
||||
font.pixelSize: root.style.baseIconFontSize
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
rightPadding: StudioTheme.Values.inputHorizontalPadding
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +61,20 @@ Rectangle {
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
id: toolTip
|
||||
|
||||
visible: mouseArea.containsMouse && text !== ""
|
||||
delay: 1000
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
when: !mouseArea.pressed && !mouseArea.containsMouse
|
||||
PropertyChanges {
|
||||
target: root
|
||||
color: StudioTheme.Values.themeBackgroundColorNormal
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import Qt.labs.platform as PlatformWidgets
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
@@ -12,7 +13,7 @@ import StudioTheme as StudioTheme
|
||||
StudioControls.Dialog {
|
||||
id: root
|
||||
|
||||
title: qsTr("Import Collections")
|
||||
title: qsTr("Import Models")
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
modal: true
|
||||
@@ -21,7 +22,7 @@ StudioControls.Dialog {
|
||||
property bool fileExists: false
|
||||
|
||||
onOpened: {
|
||||
fileName.text = qsTr("New Json File")
|
||||
fileName.text = qsTr("New JSON File")
|
||||
fileName.selectAll()
|
||||
fileName.forceActiveFocus()
|
||||
}
|
||||
@@ -30,9 +31,9 @@ StudioControls.Dialog {
|
||||
fileName.text = ""
|
||||
}
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
RegularExpressionValidator {
|
||||
id: fileNameValidator
|
||||
regExp: /^(\w[^*><?|]*)[^/\\:*><?|]$/
|
||||
regularExpression: /^(\w[^*><?|]*)[^/\\:*><?|]$/
|
||||
}
|
||||
|
||||
PlatformWidgets.FileDialog {
|
||||
@@ -49,21 +50,25 @@ StudioControls.Dialog {
|
||||
onClosed: root.reject()
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
Text {
|
||||
text: qsTr("File name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
StudioControls.TextField {
|
||||
id: fileName
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: fileNameValidator
|
||||
@@ -72,38 +77,52 @@ StudioControls.Dialog {
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
|
||||
onTextChanged: {
|
||||
root.fileExists = root.backendValue.isJsonFile(fileName.text)
|
||||
}
|
||||
onTextChanged: root.fileExists = root.backendValue.isJsonFile(fileName.text)
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: fileDialogButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
text: qsTr("Open")
|
||||
onClicked: fileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Item { // spacer
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.controlLabelGap
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
padding: 5
|
||||
text: qsTr("File name cannot be empty.")
|
||||
wrapMode: Label.WordWrap
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
anchors.right: parent.right
|
||||
visible: fileName.text === ""
|
||||
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
border.width: StudioTheme.Values.border
|
||||
border.color: StudioTheme.Values.themeWarning
|
||||
}
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.sectionColumnSpacing
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnCreate
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("Import")
|
||||
enabled: root.fileExists
|
||||
@@ -119,7 +138,6 @@ StudioControls.Dialog {
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
@@ -17,22 +18,22 @@ StudioControls.Dialog {
|
||||
implicitWidth: 300
|
||||
modal: true
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 20
|
||||
width: parent.width
|
||||
contentItem: ColumnLayout {
|
||||
spacing: StudioTheme.Values.sectionColumnSpacing
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.message
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
width: root.width
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
text: qsTr("Close")
|
||||
anchors.right: parent.right
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
@@ -11,7 +12,9 @@ Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: 300
|
||||
implicitHeight: wholeColumn.height + 6
|
||||
implicitHeight: wholeColumn.height
|
||||
|
||||
property bool hasSelectedTarget
|
||||
|
||||
property color textColor
|
||||
property var collectionModel
|
||||
@@ -20,16 +23,24 @@ Item {
|
||||
|
||||
signal selectItem(int itemIndex)
|
||||
signal deleteItem()
|
||||
signal assignToSelected()
|
||||
|
||||
Column {
|
||||
function toggleExpanded() {
|
||||
if (collectionListView.count > 0)
|
||||
root.expanded = !root.expanded || sourceIsSelected;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: wholeColumn
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: boundingRect
|
||||
|
||||
anchors.centerIn: root
|
||||
width: root.width - 24
|
||||
height: nameHolder.height
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: nameHolder.height
|
||||
Layout.leftMargin: 6
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
@@ -48,8 +59,7 @@ Item {
|
||||
}
|
||||
|
||||
onDoubleClicked: (event) => {
|
||||
if (collectionListView.count > 0)
|
||||
root.expanded = !root.expanded;
|
||||
root.toggleExpanded()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,26 +68,27 @@ Item {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width - threeDots.width
|
||||
leftPadding: 20
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
id: expandButton
|
||||
|
||||
property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
|
||||
|
||||
width: expandButton.style.squareControlSize.width
|
||||
height: nameHolder.height
|
||||
Layout.preferredWidth: expandButton.style.squareControlSize.width
|
||||
Layout.preferredHeight: nameHolder.height
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
text: StudioTheme.Constants.startNode
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
font.pixelSize: expandButton.style.baseIconFontSize
|
||||
font.pixelSize: 6
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: textColor
|
||||
|
||||
rotation: root.expanded ? 90 : 0
|
||||
visible: collectionListView.count > 0
|
||||
|
||||
Behavior on rotation {
|
||||
SpringAnimation { spring: 2; damping: 0.2 }
|
||||
@@ -85,18 +96,17 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton + Qt.LeftButton
|
||||
onClicked: (event) => {
|
||||
root.expanded = !root.expanded
|
||||
event.accepted = true
|
||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
||||
onClicked: root.toggleExpanded()
|
||||
}
|
||||
}
|
||||
visible: collectionListView.count > 0
|
||||
}
|
||||
|
||||
Text {
|
||||
id: nameHolder
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
text: sourceName
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
color: textColor
|
||||
@@ -105,30 +115,29 @@ Item {
|
||||
rightPadding: 8
|
||||
bottomPadding: 8
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: threeDots
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
|
||||
text: StudioTheme.Constants.more_medium
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||
color: textColor
|
||||
anchors.right: boundingRect.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rightPadding: 12
|
||||
topPadding: nameHolder.topPadding
|
||||
bottomPadding: nameHolder.bottomPadding
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton + Qt.LeftButton
|
||||
onClicked: (event) => {
|
||||
collectionMenu.popup()
|
||||
event.accepted = true
|
||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
||||
onClicked: collectionMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,18 +146,20 @@ Item {
|
||||
ListView {
|
||||
id: collectionListView
|
||||
|
||||
width: parent.width
|
||||
height: root.expanded ? contentHeight : 0
|
||||
model: collections
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.expanded ? contentHeight : 0
|
||||
Layout.leftMargin: 6
|
||||
model: internalModels
|
||||
clip: true
|
||||
|
||||
Behavior on height {
|
||||
Behavior on Layout.preferredHeight {
|
||||
NumberAnimation {duration: 500}
|
||||
}
|
||||
|
||||
delegate: CollectionItem {
|
||||
width: parent.width
|
||||
onDeleteItem: root.model.removeRow(index)
|
||||
width: collectionListView.width
|
||||
sourceType: collectionListView.model.sourceType
|
||||
onDeleteItem: collectionListView.model.removeRow(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,6 +167,8 @@ Item {
|
||||
StudioControls.Menu {
|
||||
id: collectionMenu
|
||||
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Delete")
|
||||
shortcut: StandardKey.Delete
|
||||
@@ -167,6 +180,17 @@ Item {
|
||||
shortcut: StandardKey.Replace
|
||||
onTriggered: renameDialog.open()
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Assign to the selected node")
|
||||
enabled: root.hasSelectedTarget
|
||||
onTriggered: root.assignToSelected()
|
||||
}
|
||||
}
|
||||
|
||||
component Spacer: Item {
|
||||
implicitWidth: 1
|
||||
implicitHeight: StudioTheme.Values.sectionColumnSpacing
|
||||
}
|
||||
|
||||
StudioControls.Dialog {
|
||||
@@ -174,22 +198,17 @@ Item {
|
||||
|
||||
title: qsTr("Deleting source")
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 2
|
||||
contentItem: ColumnLayout {
|
||||
spacing: StudioTheme.Values.sectionColumnSpacing
|
||||
|
||||
Text {
|
||||
text: qsTr("Are you sure that you want to delete source \"" + sourceName + "\"?")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnDelete
|
||||
@@ -220,7 +239,7 @@ Item {
|
||||
newNameField.text = sourceName
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
@@ -228,8 +247,8 @@ Item {
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
Spacer {}
|
||||
|
||||
Text {
|
||||
text: qsTr("New name:")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
@@ -238,6 +257,7 @@ Item {
|
||||
StudioControls.TextField {
|
||||
id: newNameField
|
||||
|
||||
Layout.fillWidth: true
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: newNameValidator
|
||||
@@ -250,16 +270,12 @@ Item {
|
||||
btnRename.enabled = newNameField.text !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
}
|
||||
Spacer {}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnRename
|
||||
@@ -276,9 +292,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
RegularExpressionValidator {
|
||||
id: newNameValidator
|
||||
regExp: /^\w+$/
|
||||
regularExpression: /^\w+$/
|
||||
}
|
||||
|
||||
states: [
|
||||
@@ -325,6 +341,12 @@ Item {
|
||||
PropertyChanges {
|
||||
target: root
|
||||
textColor: StudioTheme.Values.themeIconColorSelected
|
||||
expanded: true
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: expandButton
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,23 +3,37 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import Qt.labs.platform as PlatformWidgets
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import CollectionEditor 1.0
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: root
|
||||
|
||||
title: qsTr("Add a new Collection")
|
||||
enum SourceType { NewJson, NewCsv, ExistingCollection, NewCollectionToJson }
|
||||
|
||||
required property var backendValue
|
||||
required property var sourceModel
|
||||
|
||||
readonly property alias collectionType: typeMode.collectionType
|
||||
readonly property bool isValid: collectionName.isValid
|
||||
&& jsonCollections.isValid
|
||||
&& newCollectionPath.isValid
|
||||
|
||||
title: qsTr("Add a new Model")
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
modal: true
|
||||
|
||||
required property var backendValue
|
||||
|
||||
onOpened: {
|
||||
collectionName.text = "Collection"
|
||||
collectionName.text = qsTr("Model")
|
||||
updateType()
|
||||
updateJsonSourceIndex()
|
||||
updateCollectionExists()
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
@@ -27,65 +41,252 @@ StudioControls.Dialog {
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (collectionName.text !== "")
|
||||
root.backendValue.addCollection(collectionName.text)
|
||||
if (root.isValid) {
|
||||
root.backendValue.addCollection(collectionName.text,
|
||||
root.collectionType,
|
||||
newCollectionPath.text,
|
||||
jsonCollections.currentValue)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 10
|
||||
Row {
|
||||
spacing: 10
|
||||
Text {
|
||||
text: qsTr("Collection name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
function updateType() {
|
||||
newCollectionPath.text = ""
|
||||
if (typeMode.currentValue === NewCollectionDialog.SourceType.NewJson) {
|
||||
newCollectionFileDialog.nameFilters = ["JSON Files (*.json)"]
|
||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
|
||||
newCollectionPath.enabled = true
|
||||
jsonCollections.enabled = false
|
||||
typeMode.collectionType = "json"
|
||||
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCsv) {
|
||||
newCollectionFileDialog.nameFilters = ["Comma-Separated Values (*.csv)"]
|
||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
|
||||
newCollectionPath.enabled = true
|
||||
jsonCollections.enabled = false
|
||||
typeMode.collectionType = "csv"
|
||||
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.ExistingCollection) {
|
||||
newCollectionFileDialog.nameFilters = ["All Model Group Files (*.json *.csv)",
|
||||
"JSON Files (*.json)",
|
||||
"Comma-Separated Values (*.csv)"]
|
||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.OpenFile
|
||||
newCollectionPath.enabled = true
|
||||
jsonCollections.enabled = false
|
||||
typeMode.collectionType = "existing"
|
||||
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCollectionToJson) {
|
||||
newCollectionFileDialog.nameFilters = [""]
|
||||
newCollectionPath.enabled = false
|
||||
jsonCollections.enabled = true
|
||||
typeMode.collectionType = "json"
|
||||
}
|
||||
}
|
||||
|
||||
function updateJsonSourceIndex() {
|
||||
if (!jsonCollections.enabled) {
|
||||
jsonCollections.currentIndex = -1
|
||||
return
|
||||
}
|
||||
|
||||
if (jsonCollections.currentIndex === -1 && jsonCollections.model.rowCount())
|
||||
jsonCollections.currentIndex = 0
|
||||
}
|
||||
|
||||
function updateCollectionExists() {
|
||||
collectionName.alreadyExists = sourceModel.collectionExists(jsonCollections.currentValue,
|
||||
collectionName.text)
|
||||
}
|
||||
|
||||
component NameField: Text {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
horizontalAlignment: Qt.AlignRight
|
||||
verticalAlignment: Qt.AlignCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.family: StudioTheme.Constants.font.family
|
||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||
}
|
||||
|
||||
component ErrorField: Text {
|
||||
Layout.columnSpan: 2
|
||||
color: StudioTheme.Values.themeError
|
||||
font.family: StudioTheme.Constants.font.family
|
||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||
}
|
||||
|
||||
component Spacer: Item {
|
||||
Layout.minimumWidth: 1
|
||||
Layout.preferredHeight: StudioTheme.Values.columnGap
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 5
|
||||
|
||||
NameField {
|
||||
text: qsTr("Type")
|
||||
}
|
||||
|
||||
StudioControls.ComboBox {
|
||||
id: typeMode
|
||||
|
||||
property string collectionType
|
||||
|
||||
Layout.minimumWidth: 300
|
||||
Layout.fillWidth: true
|
||||
|
||||
model: ListModel {
|
||||
ListElement { text: qsTr("New JSON model group"); value: NewCollectionDialog.SourceType.NewJson}
|
||||
ListElement { text: qsTr("New CSV model"); value: NewCollectionDialog.SourceType.NewCsv}
|
||||
ListElement { text: qsTr("Import an existing model group"); value: NewCollectionDialog.SourceType.ExistingCollection}
|
||||
ListElement { text: qsTr("Add a model to an available JSON model group"); value: NewCollectionDialog.SourceType.NewCollectionToJson}
|
||||
}
|
||||
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
actionIndicatorVisible: false
|
||||
|
||||
onCurrentValueChanged: root.updateType()
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
|
||||
RowLayout {
|
||||
visible: newCollectionPath.enabled
|
||||
|
||||
NameField {
|
||||
text: qsTr("File location")
|
||||
visible: newCollectionPath.enabled
|
||||
}
|
||||
|
||||
Text {
|
||||
id: newCollectionPath
|
||||
|
||||
readonly property bool isValid: !newCollectionPath.enabled || newCollectionPath.text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
font.family: StudioTheme.Constants.font.family
|
||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||
color: StudioTheme.Values.themePlaceholderTextColor
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
text: qsTr("Select")
|
||||
|
||||
onClicked: newCollectionFileDialog.open()
|
||||
|
||||
PlatformWidgets.FileDialog {
|
||||
id: newCollectionFileDialog
|
||||
|
||||
title: qsTr("Select source file")
|
||||
fileMode: PlatformWidgets.FileDialog.OpenFile
|
||||
acceptLabel: newCollectionFileDialog.fileMode === PlatformWidgets.FileDialog.OpenFile
|
||||
? qsTr("Open")
|
||||
: qsTr("Add")
|
||||
|
||||
onAccepted: newCollectionPath.text = newCollectionFileDialog.currentFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorField {
|
||||
visible: !newCollectionPath.isValid
|
||||
text: qsTr("Select a file to continue")
|
||||
}
|
||||
|
||||
Spacer { visible: newCollectionPath.enabled }
|
||||
|
||||
NameField {
|
||||
text: qsTr("JSON model group")
|
||||
visible: jsonCollections.enabled
|
||||
}
|
||||
|
||||
StudioControls.ComboBox {
|
||||
id: jsonCollections
|
||||
|
||||
readonly property bool isValid: !jsonCollections.enabled || jsonCollections.currentIndex !== -1
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitWidth: 300
|
||||
textRole: "sourceName"
|
||||
valueRole: "sourceNode"
|
||||
visible: jsonCollections.enabled
|
||||
actionIndicatorVisible: false
|
||||
|
||||
model: CollectionJsonSourceFilterModel {
|
||||
sourceModel: root.sourceModel
|
||||
onRowsInserted: root.updateJsonSourceIndex()
|
||||
onModelReset: root.updateJsonSourceIndex()
|
||||
onRowsRemoved: root.updateJsonSourceIndex()
|
||||
}
|
||||
|
||||
onEnabledChanged: root.updateJsonSourceIndex()
|
||||
onCurrentValueChanged: root.updateCollectionExists()
|
||||
}
|
||||
|
||||
ErrorField {
|
||||
visible: !jsonCollections.isValid
|
||||
text: qsTr("Add a JSON resource to continue")
|
||||
}
|
||||
|
||||
Spacer {visible: jsonCollections.visible }
|
||||
|
||||
NameField {
|
||||
text: qsTr("The model name")
|
||||
visible: collectionName.enabled
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: collectionName
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
readonly property bool isValid: !collectionName.enabled
|
||||
|| (collectionName.text !== "" && !collectionName.alreadyExists)
|
||||
property bool alreadyExists
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: collectionName.enabled
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: HelperWidgets.RegExpValidator {
|
||||
regExp: /^\w+$/
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /^\w+$/
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: btnCreate.onClicked()
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
}
|
||||
|
||||
onTextChanged: root.updateCollectionExists()
|
||||
}
|
||||
|
||||
Text {
|
||||
id: fieldErrorText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Collection name can not be empty")
|
||||
visible: collectionName.text === ""
|
||||
ErrorField {
|
||||
text: qsTr("The model name can not be empty")
|
||||
visible: collectionName.enabled && collectionName.text === ""
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
ErrorField {
|
||||
text: qsTr("The model name already exists %1").arg(collectionName.text)
|
||||
visible: collectionName.enabled && collectionName.alreadyExists
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
Spacer { visible: collectionName.visible }
|
||||
|
||||
RowLayout {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnCreate
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: qsTr("Create")
|
||||
enabled: collectionName.text !== ""
|
||||
enabled: root.isValid
|
||||
onClicked: root.accept()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: qsTr("Cancel")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
required property var model
|
||||
|
||||
property alias leftPadding: topRow.leftPadding
|
||||
property real rightPadding: topRow.rightPadding
|
||||
|
||||
color: StudioTheme.Values.themeBackgroundColorAlternate
|
||||
|
||||
Column {
|
||||
id: topRow
|
||||
|
||||
spacing: 0
|
||||
width: parent.width
|
||||
leftPadding: 20
|
||||
rightPadding: 0
|
||||
topPadding: 5
|
||||
|
||||
Text {
|
||||
id: collectionNameText
|
||||
|
||||
leftPadding: 8
|
||||
rightPadding: 8
|
||||
topPadding: 3
|
||||
bottomPadding: 3
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
text: root.model.collectionName
|
||||
font.pixelSize: StudioTheme.Values.mediumIconFont
|
||||
elide: Text.ElideRight
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
z: parent.z - 1
|
||||
color: StudioTheme.Values.themeBackgroundColorNormal
|
||||
}
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 10
|
||||
}
|
||||
|
||||
HorizontalHeaderView {
|
||||
id: headerView
|
||||
|
||||
property real topPadding: 5
|
||||
property real bottomPadding: 5
|
||||
|
||||
height: headerMetrics.height + topPadding + bottomPadding
|
||||
|
||||
syncView: tableView
|
||||
clip: true
|
||||
|
||||
delegate: Rectangle {
|
||||
implicitWidth: 100
|
||||
implicitHeight: headerText.height
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.width: 2
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
id: headerText
|
||||
|
||||
topPadding: headerView.topPadding
|
||||
bottomPadding: headerView.bottomPadding
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
text: display
|
||||
font.pixelSize: headerMetrics.font
|
||||
color: StudioTheme.Values.themeIdleGreen
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: headerMetrics
|
||||
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
text: "Xq"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
|
||||
anchors {
|
||||
top: topRow.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
leftMargin: root.leftPadding
|
||||
rightMargin: root.rightPadding
|
||||
}
|
||||
|
||||
model: root.model
|
||||
|
||||
delegate: Rectangle {
|
||||
implicitWidth: 100
|
||||
implicitHeight: itemText.height
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.width: 1
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
|
||||
Text {
|
||||
id: itemText
|
||||
|
||||
text: display
|
||||
width: parent.width
|
||||
leftPadding: 5
|
||||
topPadding: 3
|
||||
bottomPadding: 3
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import StudioTheme as StudioTheme
|
||||
import StudioControls as StudioControls
|
||||
import HelperWidgets as HelperWidgets
|
||||
|
||||
PopupDialog {
|
||||
StudioControls.PopupDialog {
|
||||
property alias backend: form.backend
|
||||
|
||||
titleBar: Row {
|
||||
spacing: 30 // TODO
|
||||
anchors.fill: parent
|
||||
@@ -16,6 +18,7 @@ PopupDialog {
|
||||
text: qsTr("Owner")
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
HelperWidgets.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
tooltip: qsTr("The owner of the property")
|
||||
@@ -27,14 +30,10 @@ PopupDialog {
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: form.backend.targetNode
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BindingsDialogForm {
|
||||
id: form
|
||||
y: 32
|
||||
height: 160
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioControls as StudioControls
|
||||
@@ -10,12 +10,11 @@ Column {
|
||||
id: root
|
||||
|
||||
readonly property real horizontalSpacing: 10
|
||||
readonly property real verticalSpacing: 16
|
||||
readonly property real verticalSpacing: 12
|
||||
readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2
|
||||
|
||||
property var backend
|
||||
|
||||
y: StudioTheme.Values.popupMargin
|
||||
width: parent.width
|
||||
spacing: root.verticalSpacing
|
||||
|
||||
@@ -53,8 +52,8 @@ Column {
|
||||
PopupLabel {
|
||||
width: root.columnWidth
|
||||
text: backend.targetNode
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
|
||||
@@ -15,12 +15,6 @@ ListView {
|
||||
|
||||
property bool adsFocus: false
|
||||
|
||||
// Temporarily remove due to dockwidget focus issue
|
||||
//onAdsFocusChanged: {
|
||||
// if (!root.adsFocus)
|
||||
// dialog.close()
|
||||
//}
|
||||
|
||||
clip: true
|
||||
interactive: true
|
||||
highlightMoveDuration: 0
|
||||
@@ -43,9 +37,7 @@ ListView {
|
||||
&& verticalScrollBar.isNeeded
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
onVisibleChanged: dialog.close()
|
||||
|
||||
property int modelCurrentIndex: root.model.currentIndex ?? 0
|
||||
|
||||
@@ -72,7 +64,7 @@ ListView {
|
||||
function addBinding() {
|
||||
ConnectionsEditorEditorBackend.bindingModel.add()
|
||||
if (root.currentItem)
|
||||
dialog.popup(root.currentItem.delegateMouseArea)
|
||||
dialog.show(root.currentItem.delegateMouseArea)
|
||||
}
|
||||
|
||||
function resetIndex() {
|
||||
@@ -104,9 +96,11 @@ ListView {
|
||||
|
||||
property alias delegateMouseArea: mouseArea
|
||||
|
||||
property bool hovered: mouseArea.containsMouse || toolTipArea.containsMouse
|
||||
|
||||
width: ListView.view.width
|
||||
height: root.style.squareControlSize.height
|
||||
color: mouseArea.containsMouse ?
|
||||
color: itemDelegate.hovered ?
|
||||
itemDelegate.ListView.isCurrentItem ? root.style.interactionHover
|
||||
: root.style.background.hover
|
||||
: "transparent"
|
||||
@@ -120,7 +114,7 @@ ListView {
|
||||
onClicked: {
|
||||
root.model.currentIndex = itemDelegate.index
|
||||
root.currentIndex = itemDelegate.index
|
||||
dialog.popup(mouseArea)
|
||||
dialog.show(mouseArea)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import HelperWidgets as HelperWidgets
|
||||
|
||||
PopupDialog {
|
||||
StudioControls.PopupDialog {
|
||||
id: root
|
||||
|
||||
property alias backend: form.backend
|
||||
|
||||
titleBar: Row {
|
||||
@@ -19,6 +20,7 @@ PopupDialog {
|
||||
text: qsTr("Target")
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
HelperWidgets.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
tooltip: qsTr("Sets the Component that is connected to a <b>Signal</b>.")
|
||||
@@ -46,6 +48,9 @@ PopupDialog {
|
||||
function onPopupShouldClose() {
|
||||
root.close()
|
||||
}
|
||||
function onPopupShouldOpen() {
|
||||
root.showGlobal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ Column {
|
||||
|
||||
property var backend
|
||||
|
||||
y: StudioTheme.Values.popupMargin
|
||||
width: parent.width
|
||||
spacing: root.verticalSpacing
|
||||
|
||||
@@ -45,6 +44,7 @@ Column {
|
||||
|
||||
StudioControls.TopLevelComboBox {
|
||||
id: signal
|
||||
|
||||
style: StudioTheme.Values.connectionPopupControlStyle
|
||||
width: root.columnWidth
|
||||
|
||||
@@ -216,12 +216,38 @@ Column {
|
||||
&& backend.hasCondition && backend.hasElse
|
||||
}
|
||||
|
||||
// code preview toolbar
|
||||
Column {
|
||||
id: miniToolbarEditor
|
||||
width: parent.width
|
||||
spacing: -2
|
||||
|
||||
Rectangle {
|
||||
id: miniToolbar
|
||||
width: parent.width
|
||||
height: editorButton.height + 2
|
||||
radius: 4
|
||||
z: -1
|
||||
color: StudioTheme.Values.themeConnectionEditorMicroToolbar
|
||||
|
||||
Row {
|
||||
spacing: 2
|
||||
HelperWidgets.AbstractButton {
|
||||
id: editorButton
|
||||
style: StudioTheme.Values.microToolbarButtonStyle
|
||||
buttonIcon: StudioTheme.Constants.codeEditor_medium
|
||||
tooltip: qsTr("Write the conditions for the components and the signals manually.")
|
||||
onClicked: expressionDialogLoader.show()
|
||||
}
|
||||
HelperWidgets.AbstractButton {
|
||||
id: jumpToCodeButton
|
||||
style: StudioTheme.Values.microToolbarButtonStyle
|
||||
buttonIcon: StudioTheme.Constants.jumpToCode_medium
|
||||
tooltip: qsTr("Jump to the code.")
|
||||
onClicked: backend.jumpToCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Editor
|
||||
Rectangle {
|
||||
@@ -244,6 +270,7 @@ Column {
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loader {
|
||||
id: expressionDialogLoader
|
||||
parent: editor
|
||||
@@ -281,6 +308,8 @@ Column {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // loader
|
||||
} // rect
|
||||
} //col 2
|
||||
}//col1
|
||||
|
||||
|
||||
@@ -15,12 +15,6 @@ ListView {
|
||||
|
||||
property bool adsFocus: false
|
||||
|
||||
// Temporarily remove due to dockwidget focus issue
|
||||
//onAdsFocusChanged: {
|
||||
// if (!root.adsFocus)
|
||||
// dialog.close()
|
||||
//}
|
||||
|
||||
clip: true
|
||||
interactive: true
|
||||
highlightMoveDuration: 0
|
||||
@@ -43,9 +37,7 @@ ListView {
|
||||
&& verticalScrollBar.isNeeded
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
onVisibleChanged: dialog.close()
|
||||
|
||||
property int modelCurrentIndex: root.model.currentIndex ?? 0
|
||||
|
||||
@@ -73,7 +65,7 @@ ListView {
|
||||
function addConnection() {
|
||||
ConnectionsEditorEditorBackend.connectionModel.add()
|
||||
if (root.currentItem)
|
||||
dialog.popup(root.currentItem.delegateMouseArea)
|
||||
dialog.show(root.currentItem.delegateMouseArea)
|
||||
}
|
||||
|
||||
function resetIndex() {
|
||||
@@ -124,7 +116,7 @@ ListView {
|
||||
root.model.currentIndex = itemDelegate.index
|
||||
root.currentIndex = itemDelegate.index
|
||||
dialog.backend.currentRow = itemDelegate.index
|
||||
dialog.popup(mouseArea)
|
||||
dialog.show(mouseArea)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Rectangle {
|
||||
Column {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
spacing: 8 // TODO
|
||||
spacing: 5 // TODO
|
||||
|
||||
Rectangle {
|
||||
id: toolbar
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
Window {
|
||||
id: window
|
||||
|
||||
property alias titleBar: titleBarContent.children
|
||||
default property alias content: mainContent.children
|
||||
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||
|
||||
width: 300
|
||||
height: column.implicitHeight
|
||||
visible: true
|
||||
flags: Qt.FramelessWindowHint | Qt.Dialog
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: StudioTheme.Values.themePopoutBackground
|
||||
border.color: "#636363"//StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
function ensureVerticalPosition() {
|
||||
if ((window.y + window.height) > (Screen.height - window.style.dialogScreenMargin)) {
|
||||
window.y = (Screen.height - window.height - window.style.dialogScreenMargin)
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: window.ensureVerticalPosition()
|
||||
|
||||
function popup(item) {
|
||||
var padding = 12
|
||||
var p = item.mapToGlobal(0, 0)
|
||||
window.x = p.x - window.width - padding
|
||||
if (window.x < 0)
|
||||
window.x = p.x + item.width + padding
|
||||
window.y = p.y
|
||||
|
||||
window.ensureVerticalPosition()
|
||||
|
||||
window.show()
|
||||
window.raise()
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
|
||||
Item {
|
||||
id: titleBarItem
|
||||
width: parent.width
|
||||
height: StudioTheme.Values.titleBarHeight
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
|
||||
target: null
|
||||
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||
onActiveChanged: {
|
||||
if (dragHandler.active)
|
||||
window.startSystemMove() // QTBUG-102488
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: StudioTheme.Values.popupMargin
|
||||
anchors.rightMargin: StudioTheme.Values.popupMargin
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: titleBarContent
|
||||
width: row.width - closeIndicator.width //- row.anchors.leftMargin
|
||||
height: row.height
|
||||
}
|
||||
|
||||
HelperWidgets.IconIndicator {
|
||||
id: closeIndicator
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: StudioTheme.Constants.colorPopupClose
|
||||
pixelSize: StudioTheme.Values.myIconFontSize// * 1.4
|
||||
onClicked: window.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width - 8
|
||||
height: 1
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "#636363"
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mainContent
|
||||
width: parent.width - 2 * StudioTheme.Values.popupMargin
|
||||
height: mainContent.childrenRect.height + 2 * StudioTheme.Values.popupMargin
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import StudioTheme as StudioTheme
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
|
||||
PopupDialog {
|
||||
StudioControls.PopupDialog {
|
||||
property alias backend: form.backend
|
||||
|
||||
titleBar: Row {
|
||||
@@ -17,6 +18,7 @@ PopupDialog {
|
||||
text: qsTr("Owner")
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
HelperWidgets.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
tooltip: qsTr("The owner of the property")
|
||||
@@ -29,12 +31,9 @@ PopupDialog {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: form.backend.targetNode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PropertiesDialogForm {
|
||||
id: form
|
||||
y: 32
|
||||
height: 180
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,11 @@ Column {
|
||||
id: root
|
||||
|
||||
readonly property real horizontalSpacing: 10
|
||||
readonly property real verticalSpacing: 16
|
||||
readonly property real verticalSpacing: 12
|
||||
readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2
|
||||
|
||||
property var backend
|
||||
|
||||
y: StudioTheme.Values.popupMargin
|
||||
width: parent.width
|
||||
spacing: root.verticalSpacing
|
||||
|
||||
@@ -23,11 +22,11 @@ Column {
|
||||
text: qsTr("Type")
|
||||
tooltip: qsTr("Sets the category of the <b>Local Custom Property</b>.")
|
||||
}
|
||||
|
||||
StudioControls.TopLevelComboBox {
|
||||
id: type
|
||||
style: StudioTheme.Values.connectionPopupControlStyle
|
||||
width: root.columnWidth
|
||||
//width: root.width
|
||||
|
||||
model: backend.type.model ?? []
|
||||
onActivated: backend.type.activateIndex(type.currentIndex)
|
||||
@@ -53,6 +52,7 @@ Column {
|
||||
|
||||
Row {
|
||||
spacing: root.horizontalSpacing
|
||||
|
||||
StudioControls.TextField {
|
||||
id: name
|
||||
|
||||
@@ -61,10 +61,9 @@ Column {
|
||||
translationIndicatorVisible: false
|
||||
|
||||
text: backend.name.text ?? ""
|
||||
onEditingFinished: {
|
||||
backend.name.activateText(name.text)
|
||||
}
|
||||
onEditingFinished: backend.name.activateText(name.text)
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: value
|
||||
|
||||
@@ -74,9 +73,7 @@ Column {
|
||||
|
||||
|
||||
text: backend.value.text ?? ""
|
||||
onEditingFinished: {
|
||||
backend.value.activateText(value.text)
|
||||
}
|
||||
onEditingFinished: backend.value.activateText(value.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,6 @@ ListView {
|
||||
|
||||
property bool adsFocus: false
|
||||
|
||||
// Temporarily remove due to dockwidget focus issue
|
||||
//onAdsFocusChanged: {
|
||||
// if (!root.adsFocus)
|
||||
// dialog.close()
|
||||
//}
|
||||
|
||||
clip: true
|
||||
interactive: true
|
||||
highlightMoveDuration: 0
|
||||
@@ -43,9 +37,7 @@ ListView {
|
||||
&& verticalScrollBar.isNeeded
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
onVisibleChanged: dialog.close()
|
||||
|
||||
property int modelCurrentIndex: root.model.currentIndex ?? 0
|
||||
|
||||
@@ -72,7 +64,7 @@ ListView {
|
||||
function addProperty() {
|
||||
ConnectionsEditorEditorBackend.dynamicPropertiesModel.add()
|
||||
if (root.currentItem)
|
||||
dialog.popup(root.currentItem.delegateMouseArea)
|
||||
dialog.show(root.currentItem.delegateMouseArea)
|
||||
}
|
||||
|
||||
function resetIndex() {
|
||||
@@ -104,9 +96,11 @@ ListView {
|
||||
|
||||
property alias delegateMouseArea: mouseArea
|
||||
|
||||
property bool hovered: mouseArea.containsMouse || toolTipArea.containsMouse
|
||||
|
||||
width: ListView.view.width
|
||||
height: root.style.squareControlSize.height
|
||||
color: mouseArea.containsMouse ?
|
||||
color: itemDelegate.hovered ?
|
||||
itemDelegate.ListView.isCurrentItem ? root.style.interactionHover
|
||||
: root.style.background.hover
|
||||
: "transparent"
|
||||
@@ -124,7 +118,7 @@ ListView {
|
||||
function onClicked() {
|
||||
root.model.currentIndex = itemDelegate.index
|
||||
root.currentIndex = itemDelegate.index
|
||||
dialog.popup(mouseArea)
|
||||
dialog.show(mouseArea)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ Item {
|
||||
objectName: "__mainSrollView"
|
||||
|
||||
// Called also from C++ to close context menu on focus out
|
||||
function closeContextMenu()
|
||||
{
|
||||
function closeContextMenu() {
|
||||
materialsView.closeContextMenu()
|
||||
texturesView.closeContextMenu()
|
||||
environmentsView.closeContextMenu()
|
||||
@@ -29,9 +28,43 @@ Item {
|
||||
}
|
||||
|
||||
// Called from C++
|
||||
function clearSearchFilter()
|
||||
{
|
||||
searchBox.clear();
|
||||
function clearSearchFilter() {
|
||||
searchBox.clear()
|
||||
}
|
||||
|
||||
property int numColumns: 4
|
||||
property real thumbnailSize: 100
|
||||
|
||||
readonly property int minThumbSize: 100
|
||||
readonly property int maxThumbSize: 150
|
||||
|
||||
function responsiveResize(width: int, height: int) {
|
||||
width -= 2 * StudioTheme.Values.sectionPadding
|
||||
|
||||
let numColumns = Math.floor(width / root.minThumbSize)
|
||||
let remainder = width % root.minThumbSize
|
||||
let space = (numColumns - 1) * StudioTheme.Values.sectionGridSpacing
|
||||
|
||||
if (remainder < space)
|
||||
numColumns -= 1
|
||||
|
||||
if (numColumns < 1)
|
||||
return
|
||||
|
||||
let maxItems = Math.max(materialsView.count,
|
||||
texturesView.count,
|
||||
environmentsView.count,
|
||||
effectsView.count)
|
||||
|
||||
if (numColumns > maxItems)
|
||||
numColumns = maxItems
|
||||
|
||||
let rest = width - (numColumns * root.minThumbSize)
|
||||
- ((numColumns - 1) * StudioTheme.Values.sectionGridSpacing)
|
||||
|
||||
root.thumbnailSize = Math.min(root.minThumbSize + (rest / numColumns),
|
||||
root.maxThumbSize)
|
||||
root.numColumns = numColumns
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -46,11 +79,11 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 6
|
||||
anchors.bottomMargin: 6
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 12
|
||||
anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin
|
||||
anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin
|
||||
anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin
|
||||
anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin
|
||||
spacing: StudioTheme.Values.toolbarColumnSpacing
|
||||
|
||||
StudioControls.SearchBox {
|
||||
id: searchBox
|
||||
@@ -94,16 +127,24 @@ Item {
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
width: root.width
|
||||
height: root.height - y
|
||||
currentIndex: tabBar.currIndex
|
||||
|
||||
onWidthChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
|
||||
|
||||
ContentLibraryMaterialsView {
|
||||
id: materialsView
|
||||
|
||||
adsFocus: root.adsFocus
|
||||
width: root.width
|
||||
|
||||
cellWidth: root.thumbnailSize
|
||||
cellHeight: root.thumbnailSize + 20
|
||||
numColumns: root.numColumns
|
||||
hideHorizontalScrollBar: true
|
||||
|
||||
searchBox: searchBox
|
||||
|
||||
onUnimport: (bundleMat) => {
|
||||
@@ -111,6 +152,8 @@ Item {
|
||||
confirmUnimportDialog.targetBundleType = "material"
|
||||
confirmUnimportDialog.open()
|
||||
}
|
||||
|
||||
onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
|
||||
}
|
||||
|
||||
ContentLibraryTexturesView {
|
||||
@@ -118,10 +161,18 @@ Item {
|
||||
|
||||
adsFocus: root.adsFocus
|
||||
width: root.width
|
||||
|
||||
cellWidth: root.thumbnailSize
|
||||
cellHeight: root.thumbnailSize
|
||||
numColumns: root.numColumns
|
||||
hideHorizontalScrollBar: true
|
||||
|
||||
model: ContentLibraryBackend.texturesModel
|
||||
sectionCategory: "ContentLib_Tex"
|
||||
|
||||
searchBox: searchBox
|
||||
|
||||
onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
|
||||
}
|
||||
|
||||
ContentLibraryTexturesView {
|
||||
@@ -129,10 +180,18 @@ Item {
|
||||
|
||||
adsFocus: root.adsFocus
|
||||
width: root.width
|
||||
|
||||
cellWidth: root.thumbnailSize
|
||||
cellHeight: root.thumbnailSize
|
||||
numColumns: root.numColumns
|
||||
hideHorizontalScrollBar: true
|
||||
|
||||
model: ContentLibraryBackend.environmentsModel
|
||||
sectionCategory: "ContentLib_Env"
|
||||
|
||||
searchBox: searchBox
|
||||
|
||||
onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
|
||||
}
|
||||
|
||||
ContentLibraryEffectsView {
|
||||
@@ -141,6 +200,11 @@ Item {
|
||||
adsFocus: root.adsFocus
|
||||
width: root.width
|
||||
|
||||
cellWidth: root.thumbnailSize
|
||||
cellHeight: root.thumbnailSize + 20
|
||||
numColumns: root.numColumns
|
||||
hideHorizontalScrollBar: true
|
||||
|
||||
searchBox: searchBox
|
||||
|
||||
onUnimport: (bundleItem) => {
|
||||
@@ -148,6 +212,8 @@ Item {
|
||||
confirmUnimportDialog.targetBundleType = "effect"
|
||||
confirmUnimportDialog.open()
|
||||
}
|
||||
|
||||
onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,10 @@ Item {
|
||||
anchors.fill: parent
|
||||
spacing: 1
|
||||
|
||||
Item { width: 1; height: 5 } // spacer
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
width: root.width - 10
|
||||
width: root.width
|
||||
height: img.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: modelData.bundleItemIcon
|
||||
@@ -74,16 +72,13 @@ Item {
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData.bundleItemName
|
||||
|
||||
width: img.width
|
||||
clip: true
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
|
||||
text: modelData.bundleItemName
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
} // Column
|
||||
|
||||
@@ -14,20 +14,28 @@ HelperWidgets.ScrollView {
|
||||
interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging
|
||||
&& !HelperWidgets.Controller.contextMenuOpened
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 120
|
||||
property real cellWidth: 100
|
||||
property real cellHeight: 120
|
||||
property int numColumns: 4
|
||||
|
||||
property int count: 0
|
||||
function assignMaxCount() {
|
||||
let c = 0
|
||||
for (let i = 0; i < categoryRepeater.count; ++i)
|
||||
c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0)
|
||||
|
||||
root.count = c
|
||||
}
|
||||
|
||||
required property var searchBox
|
||||
|
||||
signal unimport(var bundleItem);
|
||||
|
||||
function closeContextMenu()
|
||||
{
|
||||
function closeContextMenu() {
|
||||
ctxMenu.close()
|
||||
}
|
||||
|
||||
function expandVisibleSections()
|
||||
{
|
||||
function expandVisibleSections() {
|
||||
for (let i = 0; i < categoryRepeater.count; ++i) {
|
||||
let cat = categoryRepeater.itemAt(i)
|
||||
if (cat.visible && !cat.expanded)
|
||||
@@ -48,10 +56,15 @@ HelperWidgets.ScrollView {
|
||||
model: ContentLibraryBackend.effectsModel
|
||||
|
||||
delegate: HelperWidgets.Section {
|
||||
id: section
|
||||
|
||||
width: root.width
|
||||
leftPadding: StudioTheme.Values.sectionPadding
|
||||
rightPadding: StudioTheme.Values.sectionPadding
|
||||
topPadding: StudioTheme.Values.sectionPadding
|
||||
bottomPadding: StudioTheme.Values.sectionPadding
|
||||
|
||||
caption: bundleCategoryName
|
||||
addTopPadding: false
|
||||
sectionBackgroundColor: "transparent"
|
||||
visible: bundleCategoryVisible && !ContentLibraryBackend.effectsModel.isEmpty
|
||||
expanded: bundleCategoryExpanded
|
||||
expandOnClick: false
|
||||
@@ -65,14 +78,17 @@ HelperWidgets.ScrollView {
|
||||
bundleCategoryExpanded = true
|
||||
}
|
||||
|
||||
property alias count: repeater.count
|
||||
|
||||
onCountChanged: root.assignMaxCount()
|
||||
|
||||
Grid {
|
||||
width: root.width
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
bottomPadding: 5
|
||||
columns: root.width / root.cellWidth
|
||||
width: section.width - section.leftPadding - section.rightPadding
|
||||
spacing: StudioTheme.Values.sectionGridSpacing
|
||||
columns: root.numColumns
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: bundleCategoryItems
|
||||
|
||||
delegate: ContentLibraryEffect {
|
||||
@@ -81,6 +97,8 @@ HelperWidgets.ScrollView {
|
||||
|
||||
onShowContextMenu: ctxMenu.popupMenu(modelData)
|
||||
}
|
||||
|
||||
onCountChanged: root.assignMaxCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import ContentLibraryBackend
|
||||
|
||||
import WebFetcher 1.0
|
||||
import WebFetcher
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -45,8 +44,6 @@ Item {
|
||||
anchors.fill: parent
|
||||
spacing: 1
|
||||
|
||||
Item { width: 1; height: 5 } // spacer
|
||||
|
||||
DownloadPane {
|
||||
id: downloadPane
|
||||
width: root.width - 10
|
||||
@@ -59,7 +56,7 @@ Item {
|
||||
Image {
|
||||
id: img
|
||||
|
||||
width: root.width - 10
|
||||
width: root.width
|
||||
height: img.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: modelData.bundleMaterialIcon
|
||||
@@ -108,7 +105,7 @@ Item {
|
||||
onClicked: {
|
||||
ContentLibraryBackend.materialsModel.addToProject(modelData)
|
||||
}
|
||||
} // IconButton
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: downloadIcon
|
||||
@@ -154,29 +151,22 @@ Item {
|
||||
root.downloadState = ""
|
||||
downloader.start()
|
||||
}
|
||||
} // IconButton
|
||||
} // Image
|
||||
}
|
||||
}
|
||||
|
||||
TextInput {
|
||||
Text {
|
||||
id: matName
|
||||
|
||||
text: modelData.bundleMaterialName
|
||||
|
||||
width: img.width
|
||||
clip: true
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
|
||||
text: modelData.bundleMaterialName
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
|
||||
readOnly: true
|
||||
selectByMouse: !matName.readOnly
|
||||
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
selectionColor: StudioTheme.Values.themeTextSelectionColor
|
||||
selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
|
||||
}
|
||||
} // Column
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedFinish
|
||||
@@ -223,6 +213,6 @@ Item {
|
||||
probeUrl: false
|
||||
downloadEnabled: true
|
||||
targetFilePath: downloader.nextTargetPath
|
||||
} // FileDownloader
|
||||
} // MultiFileDownloader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,18 @@ HelperWidgets.ScrollView {
|
||||
interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging
|
||||
&& !HelperWidgets.Controller.contextMenuOpened
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 120
|
||||
property real cellWidth: 100
|
||||
property real cellHeight: 120
|
||||
property int numColumns: 4
|
||||
|
||||
property int count: 0
|
||||
function assignMaxCount() {
|
||||
let c = 0
|
||||
for (let i = 0; i < categoryRepeater.count; ++i)
|
||||
c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0)
|
||||
|
||||
root.count = c
|
||||
}
|
||||
|
||||
property var currMaterialItem: null
|
||||
property var rootItem: null
|
||||
@@ -23,15 +33,13 @@ HelperWidgets.ScrollView {
|
||||
|
||||
required property var searchBox
|
||||
|
||||
signal unimport(var bundleMat);
|
||||
signal unimport(var bundleMat)
|
||||
|
||||
function closeContextMenu()
|
||||
{
|
||||
function closeContextMenu() {
|
||||
ctxMenu.close()
|
||||
}
|
||||
|
||||
function expandVisibleSections()
|
||||
{
|
||||
function expandVisibleSections() {
|
||||
for (let i = 0; i < categoryRepeater.count; ++i) {
|
||||
let cat = categoryRepeater.itemAt(i)
|
||||
if (cat.visible && !cat.expanded)
|
||||
@@ -56,10 +64,15 @@ HelperWidgets.ScrollView {
|
||||
model: materialsModel
|
||||
|
||||
delegate: HelperWidgets.Section {
|
||||
id: section
|
||||
|
||||
width: root.width
|
||||
leftPadding: StudioTheme.Values.sectionPadding
|
||||
rightPadding: StudioTheme.Values.sectionPadding
|
||||
topPadding: StudioTheme.Values.sectionPadding
|
||||
bottomPadding: StudioTheme.Values.sectionPadding
|
||||
|
||||
caption: bundleCategoryName
|
||||
addTopPadding: false
|
||||
sectionBackgroundColor: "transparent"
|
||||
visible: bundleCategoryVisible && !materialsModel.isEmpty
|
||||
expanded: bundleCategoryExpanded
|
||||
expandOnClick: false
|
||||
@@ -73,14 +86,17 @@ HelperWidgets.ScrollView {
|
||||
bundleCategoryExpanded = true
|
||||
}
|
||||
|
||||
property alias count: repeater.count
|
||||
|
||||
onCountChanged: root.assignMaxCount()
|
||||
|
||||
Grid {
|
||||
width: root.width
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
bottomPadding: 5
|
||||
columns: root.width / root.cellWidth
|
||||
width: section.width - section.leftPadding - section.rightPadding
|
||||
spacing: StudioTheme.Values.sectionGridSpacing
|
||||
columns: root.numColumns
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: bundleCategoryMaterials
|
||||
|
||||
delegate: ContentLibraryMaterial {
|
||||
@@ -89,6 +105,8 @@ HelperWidgets.ScrollView {
|
||||
|
||||
onShowContextMenu: ctxMenu.popupMenu(modelData)
|
||||
}
|
||||
|
||||
onCountChanged: root.assignMaxCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
import WebFetcher 1.0
|
||||
import ContentLibraryBackend
|
||||
import WebFetcher
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -30,8 +29,7 @@ Item {
|
||||
|
||||
signal showContextMenu()
|
||||
|
||||
function statusText()
|
||||
{
|
||||
function statusText() {
|
||||
if (root.downloadState === "downloaded")
|
||||
return qsTr("Texture was already downloaded.")
|
||||
if (root.downloadState === "unavailable")
|
||||
@@ -42,16 +40,14 @@ Item {
|
||||
return qsTr("Click to download the texture.")
|
||||
}
|
||||
|
||||
function startDownload(message)
|
||||
{
|
||||
function startDownload(message) {
|
||||
if (root.downloadState !== "" && root.downloadState !== "failed")
|
||||
return
|
||||
|
||||
root._startDownload(textureDownloader, message)
|
||||
}
|
||||
|
||||
function updateTexture()
|
||||
{
|
||||
function updateTexture() {
|
||||
if (root.downloadState !== "downloaded")
|
||||
return
|
||||
|
||||
@@ -59,8 +55,7 @@ Item {
|
||||
root._startDownload(textureDownloader, qsTr("Updating..."))
|
||||
}
|
||||
|
||||
function _startDownload(downloader, message)
|
||||
{
|
||||
function _startDownload(downloader, message) {
|
||||
progressBar.visible = true
|
||||
tooltip.visible = false
|
||||
root.progressText = message
|
||||
@@ -144,8 +139,8 @@ Item {
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
} // TextureProgressBar
|
||||
} // Rectangle
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
@@ -197,7 +192,7 @@ Item {
|
||||
onClicked: {
|
||||
root.startDownload(qsTr("Downloading..."))
|
||||
}
|
||||
} // IconButton
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: updateButton
|
||||
@@ -233,7 +228,7 @@ Item {
|
||||
|
||||
scale: updateButton.containsMouse ? 1.2 : 1
|
||||
}
|
||||
} // Update IconButton
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: isNewFlag
|
||||
@@ -282,7 +277,7 @@ Item {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
} // Image
|
||||
}
|
||||
|
||||
FileDownloader {
|
||||
id: textureDownloader
|
||||
|
||||
@@ -14,8 +14,18 @@ HelperWidgets.ScrollView {
|
||||
interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging
|
||||
&& !HelperWidgets.Controller.contextMenuOpened
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 100
|
||||
property int cellWidth: 100
|
||||
property int cellHeight: 100
|
||||
property int numColumns: 4
|
||||
|
||||
property int count: 0
|
||||
function assignMaxCount() {
|
||||
let c = 0
|
||||
for (let i = 0; i < categoryRepeater.count; ++i)
|
||||
c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0)
|
||||
|
||||
root.count = c
|
||||
}
|
||||
|
||||
property var currMaterialItem: null
|
||||
property var rootItem: null
|
||||
@@ -24,21 +34,20 @@ HelperWidgets.ScrollView {
|
||||
required property var model
|
||||
required property string sectionCategory
|
||||
|
||||
signal unimport(var bundleMat);
|
||||
signal unimport(var bundleMat)
|
||||
|
||||
function closeContextMenu()
|
||||
{
|
||||
function closeContextMenu() {
|
||||
ctxMenu.close()
|
||||
}
|
||||
|
||||
function expandVisibleSections()
|
||||
{
|
||||
function expandVisibleSections() {
|
||||
for (let i = 0; i < categoryRepeater.count; ++i) {
|
||||
let cat = categoryRepeater.itemAt(i)
|
||||
if (cat.visible && !cat.expanded)
|
||||
cat.expandSection()
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
ContentLibraryTextureContextMenu {
|
||||
id: ctxMenu
|
||||
@@ -52,10 +61,15 @@ HelperWidgets.ScrollView {
|
||||
model: root.model
|
||||
|
||||
delegate: HelperWidgets.Section {
|
||||
id: section
|
||||
|
||||
width: root.width
|
||||
leftPadding: StudioTheme.Values.sectionPadding
|
||||
rightPadding: StudioTheme.Values.sectionPadding
|
||||
topPadding: StudioTheme.Values.sectionPadding
|
||||
bottomPadding: StudioTheme.Values.sectionPadding
|
||||
|
||||
caption: bundleCategoryName
|
||||
addTopPadding: false
|
||||
sectionBackgroundColor: "transparent"
|
||||
visible: bundleCategoryVisible && !root.model.isEmpty
|
||||
expanded: bundleCategoryExpanded
|
||||
expandOnClick: false
|
||||
@@ -69,15 +83,17 @@ HelperWidgets.ScrollView {
|
||||
bundleCategoryExpanded = true
|
||||
}
|
||||
|
||||
property alias count: repeater.count
|
||||
|
||||
onCountChanged: root.assignMaxCount()
|
||||
|
||||
Grid {
|
||||
width: root.width
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
bottomPadding: 5
|
||||
spacing: 5
|
||||
columns: root.width / root.cellWidth
|
||||
width: section.width - section.leftPadding - section.rightPadding
|
||||
spacing: StudioTheme.Values.sectionGridSpacing
|
||||
columns: root.numColumns
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: bundleCategoryTextures
|
||||
|
||||
delegate: ContentLibraryTexture {
|
||||
@@ -86,6 +102,8 @@ HelperWidgets.ScrollView {
|
||||
|
||||
onShowContextMenu: ctxMenu.popupMenu(modelData)
|
||||
}
|
||||
|
||||
onCountChanged: root.assignMaxCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@
|
||||
"EnterComponentIcon": {
|
||||
"iconName": "editComponent_small"
|
||||
},
|
||||
"JumpToCodeIcon": {
|
||||
"iconName": "jumpToCode_small"
|
||||
},
|
||||
"EventListIcon": {
|
||||
"iconName": "events_small"
|
||||
},
|
||||
@@ -218,6 +221,9 @@
|
||||
"EditColorIcon": {
|
||||
"iconName": "colorSelection_medium"
|
||||
},
|
||||
"BakeLightIcon": {
|
||||
"iconName": "bakeLights_medium"
|
||||
},
|
||||
"EditLightIcon": {
|
||||
"Off": {
|
||||
"iconName": "editLightOff_medium"
|
||||
@@ -264,6 +270,9 @@
|
||||
"SnappingConfIcon": {
|
||||
"iconName": "snapping_conf_medium"
|
||||
},
|
||||
"SplitViewIcon": {
|
||||
"iconName": "splitScreen_medium"
|
||||
},
|
||||
"ToggleGroupIcon": {
|
||||
"Off": {
|
||||
"iconName": "selectOutline_medium"
|
||||
|
||||
@@ -12,14 +12,7 @@ import EffectMakerBackend
|
||||
HelperWidgets.Section {
|
||||
id: root
|
||||
|
||||
// model properties
|
||||
required property string nodeName
|
||||
required property bool nodeEnabled
|
||||
required property var nodeUniformsModel
|
||||
|
||||
required property int index
|
||||
|
||||
caption: root.nodeName
|
||||
caption: nodeName
|
||||
category: "EffectMaker"
|
||||
|
||||
draggable: true
|
||||
@@ -32,18 +25,18 @@ HelperWidgets.Section {
|
||||
}
|
||||
|
||||
showEyeButton: true
|
||||
eyeEnabled: root.nodeEnabled
|
||||
eyeEnabled: nodeEnabled
|
||||
eyeButtonToolTip: qsTr("Enable/Disable Node")
|
||||
|
||||
onEyeButtonClicked: {
|
||||
root.nodeEnabled = root.eyeEnabled
|
||||
nodeEnabled = root.eyeEnabled
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
|
||||
Repeater {
|
||||
model: root.nodeUniformsModel
|
||||
model: nodeUniformsModel
|
||||
|
||||
EffectCompositionNodeUniform {
|
||||
width: root.width
|
||||
|
||||
@@ -28,7 +28,7 @@ Item {
|
||||
valueLoader.source = "ValueBool.qml"
|
||||
else if (uniformType === "color")
|
||||
valueLoader.source = "ValueColor.qml"
|
||||
else if (uniformType === "image")
|
||||
else if (uniformType === "sampler2D")
|
||||
valueLoader.source = "ValueImage.qml"
|
||||
else if (uniformType === "define")
|
||||
valueLoader.source = "ValueDefine.qml"
|
||||
@@ -50,6 +50,7 @@ Item {
|
||||
Layout.maximumWidth: 140
|
||||
Layout.minimumWidth: 140
|
||||
Layout.preferredWidth: 140
|
||||
elide: Text.ElideRight
|
||||
|
||||
HelperWidgets.ToolTipArea {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -14,6 +14,18 @@ Item {
|
||||
property var secsY: []
|
||||
property int moveFromIdx: 0
|
||||
property int moveToIdx: 0
|
||||
property bool previewAnimationRunning: false
|
||||
|
||||
SaveDialog {
|
||||
id: saveDialog
|
||||
compositionName: EffectMakerBackend.effectMakerModel.currentComposition
|
||||
anchors.centerIn: parent
|
||||
onAccepted: {
|
||||
let name = saveDialog.compositionName
|
||||
EffectMakerBackend.effectMakerModel.exportComposition(name)
|
||||
EffectMakerBackend.effectMakerModel.exportResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
@@ -21,11 +33,17 @@ Item {
|
||||
spacing: 1
|
||||
|
||||
EffectMakerTopBar {
|
||||
|
||||
onSaveClicked: saveDialog.open()
|
||||
}
|
||||
|
||||
EffectMakerPreview {
|
||||
mainRoot: root
|
||||
|
||||
FrameAnimation {
|
||||
id: previewFrameTimer
|
||||
running: true
|
||||
paused: !previewAnimationRunning
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -47,11 +65,14 @@ Item {
|
||||
style: StudioTheme.Values.viewBarButtonStyle
|
||||
buttonIcon: StudioTheme.Constants.code
|
||||
tooltip: qsTr("Open Shader in Code Editor")
|
||||
visible: false // TODO: to be implemented
|
||||
|
||||
onClicked: {} // TODO
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: HelperWidgets.Controller.mainScrollView = scrollView
|
||||
|
||||
HelperWidgets.ScrollView {
|
||||
id: scrollView
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
@@ -12,24 +11,58 @@ import EffectMakerBackend
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property real animatedTime: previewFrameTimer.elapsedTime
|
||||
property int animatedFrame: previewFrameTimer.currentFrame
|
||||
property bool timeRunning: previewAnimationRunning
|
||||
|
||||
width: parent.width
|
||||
|
||||
required property Item mainRoot
|
||||
property var effectMakerModel: EffectMakerBackend.effectMakerModel
|
||||
property alias source: source
|
||||
// The delay in ms to wait until updating the effect
|
||||
readonly property int updateDelay: 200
|
||||
readonly property int updateDelay: 100
|
||||
|
||||
// Create a dummy parent to host the effect qml object
|
||||
function createNewComponent() {
|
||||
// If we have a working effect, do not show preview image as it shows through
|
||||
// transparent parts of the final image
|
||||
source.visible = false;
|
||||
|
||||
var oldComponent = componentParent.children[0];
|
||||
if (oldComponent)
|
||||
oldComponent.destroy();
|
||||
try {
|
||||
const newObject = Qt.createQmlObject(
|
||||
effectMakerModel.qmlComponentString,
|
||||
componentParent,
|
||||
""
|
||||
);
|
||||
effectMakerModel.resetEffectError(0);
|
||||
} catch (error) {
|
||||
let errorString = "QML: ERROR: ";
|
||||
let errorLine = -1;
|
||||
if (error.qmlErrors.length > 0) {
|
||||
// Show the first QML error
|
||||
let e = error.qmlErrors[0];
|
||||
errorString += e.lineNumber + ": " + e.message;
|
||||
errorLine = e.lineNumber;
|
||||
}
|
||||
effectMakerModel.setEffectError(errorString, 0, errorLine);
|
||||
source.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // toolbar
|
||||
width: parent.width
|
||||
height: StudioTheme.Values.toolbarHeight
|
||||
color: StudioTheme.Values.themeToolbarBackground
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
Row {
|
||||
spacing: 5
|
||||
anchors.rightMargin: 5
|
||||
anchors.leftMargin: 5
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
PreviewImagesComboBox {
|
||||
id: imagesComboBox
|
||||
@@ -37,9 +70,19 @@ Column {
|
||||
mainRoot: root.mainRoot
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
StudioControls.ColorEditor {
|
||||
id: colorEditor
|
||||
|
||||
actionIndicatorVisible: false
|
||||
showHexTextField: false
|
||||
color: "#dddddd"
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
HelperWidgets.AbstractButton {
|
||||
enabled: sourceImage.scale > .4
|
||||
@@ -73,20 +116,24 @@ Column {
|
||||
sourceImage.scale = 1
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 5
|
||||
anchors.rightMargin: 5
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Column {
|
||||
Text {
|
||||
text: "0.000s"
|
||||
text: animatedTime >= 100
|
||||
? animatedTime.toFixed(1) + " s" : animatedTime.toFixed(3) + " s"
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: 10
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "0000000"
|
||||
text: (animatedFrame).toString().padStart(6, '0')
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: 10
|
||||
}
|
||||
@@ -97,15 +144,20 @@ Column {
|
||||
buttonIcon: StudioTheme.Constants.toStartFrame_medium
|
||||
tooltip: qsTr("Restart Animation")
|
||||
|
||||
onClicked: {} // TODO
|
||||
onClicked: {
|
||||
previewFrameTimer.reset()
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.AbstractButton {
|
||||
style: StudioTheme.Values.viewBarButtonStyle
|
||||
buttonIcon: StudioTheme.Constants.topToolbar_runProject
|
||||
buttonIcon: previewAnimationRunning ? StudioTheme.Constants.pause_medium
|
||||
: StudioTheme.Constants.playOutline_medium
|
||||
tooltip: qsTr("Play Animation")
|
||||
|
||||
onClicked: {} // TODO
|
||||
onClicked: {
|
||||
previewAnimationRunning = !previewAnimationRunning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +165,7 @@ Column {
|
||||
Rectangle { // preview image
|
||||
id: preview
|
||||
|
||||
color: "#dddddd"
|
||||
color: colorEditor.color
|
||||
width: parent.width
|
||||
height: 200
|
||||
clip: true
|
||||
@@ -147,53 +199,26 @@ Column {
|
||||
width: source.width
|
||||
height: source.height
|
||||
anchors.centerIn: parent
|
||||
scale: 1 //TODO should come from toolbar
|
||||
// Cache the layer. This way heavy shaders rendering doesn't
|
||||
// slow down code editing & rest of the UI.
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
}
|
||||
|
||||
// Create a dummy parent to host the effect qml object
|
||||
function createNewComponent() {
|
||||
var oldComponent = componentParent.children[0];
|
||||
if (oldComponent)
|
||||
oldComponent.destroy();
|
||||
|
||||
try {
|
||||
const newObject = Qt.createQmlObject(
|
||||
effectMakerModel.qmlComponentString,
|
||||
componentParent,
|
||||
""
|
||||
);
|
||||
effectMakerModel.resetEffectError(0);
|
||||
} catch (error) {
|
||||
let errorString = "QML: ERROR: ";
|
||||
let errorLine = -1;
|
||||
if (error.qmlErrors.length > 0) {
|
||||
// Show the first QML error
|
||||
let e = error.qmlErrors[0];
|
||||
errorString += e.lineNumber + ": " + e.message;
|
||||
errorLine = e.lineNumber;
|
||||
}
|
||||
effectMakerModel.setEffectError(errorString, 0, errorLine);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: effectMakerModel
|
||||
function onShadersBaked() {
|
||||
console.log("Shaders Baked!")
|
||||
//updateTimer.restart(); // Disable for now
|
||||
updateTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: updateTimer
|
||||
interval: updateDelay;
|
||||
interval: updateDelay
|
||||
onTriggered: {
|
||||
effectMakerModel.updateQmlComponent();
|
||||
createNewComponent();
|
||||
effectMakerModel.updateQmlComponent()
|
||||
createNewComponent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@ Rectangle {
|
||||
height: StudioTheme.Values.toolbarHeight
|
||||
color: StudioTheme.Values.themeToolbarBackground
|
||||
|
||||
signal saveClicked
|
||||
|
||||
HelperWidgets.Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: 5
|
||||
|
||||
text: qsTr("Save in Library")
|
||||
|
||||
onClicked: {
|
||||
// TODO
|
||||
}
|
||||
onClicked: root.saveClicked()
|
||||
}
|
||||
|
||||
HelperWidgets.AbstractButton {
|
||||
|
||||
@@ -47,7 +47,7 @@ StudioControls.ComboBox {
|
||||
|
||||
width: row.width + 2 // 2: scrollView left and right 1px margins
|
||||
height: Math.min(800, Math.min(row.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar
|
||||
flags: Qt.Dialog | Qt.FramelessWindowHint
|
||||
flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
|
||||
|
||||
onActiveFocusItemChanged: {
|
||||
if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened)
|
||||
|
||||
@@ -67,7 +67,7 @@ StudioControls.ComboBox {
|
||||
|
||||
width: col.width + 2 // 2: scrollView left and right 1px margins
|
||||
height: Math.min(800, Math.min(col.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar
|
||||
flags: Qt.Dialog | Qt.FramelessWindowHint
|
||||
flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
|
||||
|
||||
onActiveFocusItemChanged: {
|
||||
if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import AssetsLibraryBackend
|
||||
|
||||
StudioControls.Dialog {
|
||||
id: root
|
||||
|
||||
title: qsTr("Save Effect")
|
||||
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
modal: true
|
||||
implicitWidth: 250
|
||||
implicitHeight: 160
|
||||
|
||||
property string compositionName: ""
|
||||
|
||||
onOpened: {
|
||||
nameText.text = compositionName //TODO: Generate unique name
|
||||
emptyText.text = ""
|
||||
nameText.forceActiveFocus()
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Column {
|
||||
spacing: 2
|
||||
|
||||
Row {
|
||||
id: row
|
||||
Text {
|
||||
text: qsTr("Effect name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: nameText
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
|
||||
onTextChanged: {
|
||||
let errMsg = ""
|
||||
if (/[^A-Za-z0-9_]+/.test(text))
|
||||
errMsg = qsTr("Name contains invalid characters.")
|
||||
else if (!/^[A-Z]/.test(text))
|
||||
errMsg = qsTr("Name must start with a capital letter")
|
||||
else if (text.length < 3)
|
||||
errMsg = qsTr("Name must have at least 3 characters")
|
||||
else if (/\s/.test(text))
|
||||
errMsg = qsTr("Name cannot contain white space")
|
||||
|
||||
emptyText.text = errMsg
|
||||
btnSave.enabled = errMsg.length === 0
|
||||
}
|
||||
Keys.onEnterPressed: btnSave.onClicked()
|
||||
Keys.onReturnPressed: btnSave.onClicked()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: emptyText
|
||||
|
||||
color: StudioTheme.Values.themeError
|
||||
anchors.right: row.right
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
spacing: 2
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnSave
|
||||
|
||||
text: qsTr("Save")
|
||||
enabled: nameText.text !== ""
|
||||
onClicked: {
|
||||
root.compositionName = nameText.text
|
||||
root.accept() //TODO: Check if name is unique
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import EffectMakerBackend
|
||||
|
||||
@@ -13,11 +13,11 @@ Row {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
HelperWidgets.ColorEditor {
|
||||
backendValue: uniformBackendValue
|
||||
StudioControls.ColorEditor {
|
||||
actionIndicatorVisible: false
|
||||
|
||||
showExtendedFunctionButton: false
|
||||
Component.onCompleted: color = uniformValue
|
||||
|
||||
onValueChanged: uniformValue = convertColorToString(color)
|
||||
onColorChanged: uniformValue = color
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ Rectangle {
|
||||
id: column
|
||||
width: root.width
|
||||
|
||||
Section {
|
||||
HelperWidgets.Section {
|
||||
id: trackingSection
|
||||
caption: qsTr("Tracking")
|
||||
anchors.left: parent.left
|
||||
@@ -167,7 +167,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
HelperWidgets.Section {
|
||||
id: predefinedSection
|
||||
caption: qsTr("Predefined Categories")
|
||||
anchors.left: parent.left
|
||||
@@ -268,7 +268,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
HelperWidgets.Section {
|
||||
id: customSection
|
||||
caption: qsTr("Custom Categories")
|
||||
anchors.left: parent.left
|
||||
|
||||
@@ -237,9 +237,7 @@ Item {
|
||||
Repeater {
|
||||
model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context
|
||||
delegate: HelperWidgets.Section {
|
||||
width: itemsView.width -
|
||||
(verticalScrollView.verticalScrollBarVisible
|
||||
? verticalScrollView.verticalThickness : 0)
|
||||
width: itemsView.width
|
||||
caption: importName
|
||||
visible: importVisible
|
||||
sectionHeight: 30
|
||||
@@ -270,9 +268,7 @@ Item {
|
||||
Repeater {
|
||||
model: categoryModel
|
||||
delegate: HelperWidgets.Section {
|
||||
width: itemsView.width -
|
||||
(verticalScrollView.verticalScrollBarVisible
|
||||
? verticalScrollView.verticalThickness : 0)
|
||||
width: itemsView.width
|
||||
sectionBackgroundColor: "transparent"
|
||||
showTopSeparator: index > 0
|
||||
hideHeader: categoryModel.rowCount() <= 1
|
||||
@@ -351,9 +347,7 @@ Item {
|
||||
Repeater {
|
||||
model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context
|
||||
delegate: HelperWidgets.Section {
|
||||
width: 265 -
|
||||
(horizontalScrollView.verticalScrollBarVisible
|
||||
? horizontalScrollView.verticalThickness : 0)
|
||||
width: 265
|
||||
caption: importName
|
||||
visible: importVisible
|
||||
sectionHeight: 30
|
||||
@@ -384,9 +378,7 @@ Item {
|
||||
Repeater {
|
||||
model: categoryModel
|
||||
delegate: Rectangle {
|
||||
width: 265 -
|
||||
(horizontalScrollView.verticalScrollBarVisible
|
||||
? horizontalScrollView.verticalThickness : 0)
|
||||
width: 265
|
||||
height: 25
|
||||
visible: categoryVisible
|
||||
border.width: StudioTheme.Values.border
|
||||
|
||||
@@ -12,8 +12,8 @@ Item {
|
||||
id: root
|
||||
focus: true
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 120
|
||||
readonly property real cellWidth: root.thumbnailSize
|
||||
readonly property real cellHeight: root.thumbnailSize + 20
|
||||
readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary
|
||||
&& materialBrowserModel.hasQuick3DImport
|
||||
|
||||
@@ -22,30 +22,60 @@ Item {
|
||||
property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel
|
||||
property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
|
||||
|
||||
property int numColumns: 0
|
||||
property real thumbnailSize: 100
|
||||
|
||||
readonly property int minThumbSize: 100
|
||||
readonly property int maxThumbSize: 150
|
||||
|
||||
function responsiveResize(width: int, height: int) {
|
||||
width -= 2 * StudioTheme.Values.sectionPadding
|
||||
|
||||
let numColumns = Math.floor(width / root.minThumbSize)
|
||||
let remainder = width % root.minThumbSize
|
||||
let space = (numColumns - 1) * StudioTheme.Values.sectionGridSpacing
|
||||
|
||||
if (remainder < space)
|
||||
numColumns -= 1
|
||||
|
||||
if (numColumns < 1)
|
||||
return
|
||||
|
||||
let maxItems = Math.max(texturesRepeater.count, materialRepeater.count)
|
||||
|
||||
if (numColumns > maxItems)
|
||||
numColumns = maxItems
|
||||
|
||||
let rest = width - (numColumns * root.minThumbSize)
|
||||
- ((numColumns - 1) * StudioTheme.Values.sectionGridSpacing)
|
||||
|
||||
root.thumbnailSize = Math.min(root.minThumbSize + (rest / numColumns),
|
||||
root.maxThumbSize)
|
||||
root.numColumns = numColumns
|
||||
}
|
||||
|
||||
onWidthChanged: root.responsiveResize(root.width, root.height)
|
||||
|
||||
// Called also from C++ to close context menu on focus out
|
||||
function closeContextMenu()
|
||||
{
|
||||
function closeContextMenu() {
|
||||
ctxMenu.close()
|
||||
ctxMenuTextures.close()
|
||||
HelperWidgets.Controller.closeContextMenu()
|
||||
}
|
||||
|
||||
// Called from C++ to refresh a preview material after it changes
|
||||
function refreshPreview(idx)
|
||||
{
|
||||
function refreshPreview(idx) {
|
||||
var item = materialRepeater.itemAt(idx);
|
||||
if (item)
|
||||
item.refreshPreview();
|
||||
item.refreshPreview()
|
||||
}
|
||||
|
||||
// Called from C++
|
||||
function clearSearchFilter()
|
||||
{
|
||||
searchBox.clear();
|
||||
function clearSearchFilter() {
|
||||
searchBox.clear()
|
||||
}
|
||||
|
||||
function nextVisibleItem(idx, count, itemModel)
|
||||
{
|
||||
function nextVisibleItem(idx, count, itemModel) {
|
||||
if (count === 0)
|
||||
return idx
|
||||
|
||||
@@ -66,8 +96,7 @@ Item {
|
||||
return newIdx
|
||||
}
|
||||
|
||||
function visibleItemCount(itemModel)
|
||||
{
|
||||
function visibleItemCount(itemModel) {
|
||||
let curIdx = 0
|
||||
let count = 0
|
||||
|
||||
@@ -79,8 +108,7 @@ Item {
|
||||
return count
|
||||
}
|
||||
|
||||
function rowIndexOfItem(idx, rowSize, itemModel)
|
||||
{
|
||||
function rowIndexOfItem(idx, rowSize, itemModel) {
|
||||
if (rowSize === 1)
|
||||
return 1
|
||||
|
||||
@@ -98,8 +126,7 @@ Item {
|
||||
return count % rowSize
|
||||
}
|
||||
|
||||
function selectNextVisibleItem(delta)
|
||||
{
|
||||
function selectNextVisibleItem(delta) {
|
||||
if (searchBox.activeFocus)
|
||||
return
|
||||
|
||||
@@ -112,35 +139,35 @@ Item {
|
||||
|
||||
if (delta < 0) {
|
||||
if (matSecFocused) {
|
||||
targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex,
|
||||
targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex,
|
||||
delta, materialBrowserModel)
|
||||
if (targetIdx >= 0)
|
||||
materialBrowserModel.selectMaterial(targetIdx)
|
||||
} else if (texSecFocused) {
|
||||
targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
|
||||
targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
|
||||
delta, materialBrowserTexturesModel)
|
||||
if (targetIdx >= 0) {
|
||||
materialBrowserTexturesModel.selectTexture(targetIdx)
|
||||
} else if (!materialBrowserModel.isEmpty && materialsSection.expanded) {
|
||||
targetIdx = nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel)
|
||||
targetIdx = root.nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel)
|
||||
if (targetIdx >= 0) {
|
||||
if (delta !== -1) {
|
||||
// Try to match column when switching between materials/textures
|
||||
origRowIdx = rowIndexOfItem(materialBrowserTexturesModel.selectedIndex,
|
||||
origRowIdx = root.rowIndexOfItem(materialBrowserTexturesModel.selectedIndex,
|
||||
-delta, materialBrowserTexturesModel)
|
||||
if (visibleItemCount(materialBrowserModel) > origRowIdx) {
|
||||
rowIdx = rowIndexOfItem(targetIdx, -delta, materialBrowserModel)
|
||||
if (root.visibleItemCount(materialBrowserModel) > origRowIdx) {
|
||||
rowIdx = root.rowIndexOfItem(targetIdx, -delta, materialBrowserModel)
|
||||
if (rowIdx >= origRowIdx) {
|
||||
newTargetIdx = nextVisibleItem(targetIdx,
|
||||
newTargetIdx = root.nextVisibleItem(targetIdx,
|
||||
-(rowIdx - origRowIdx),
|
||||
materialBrowserModel)
|
||||
} else {
|
||||
newTargetIdx = nextVisibleItem(targetIdx,
|
||||
newTargetIdx = root.nextVisibleItem(targetIdx,
|
||||
-(-delta - origRowIdx + rowIdx),
|
||||
materialBrowserModel)
|
||||
}
|
||||
} else {
|
||||
newTargetIdx = nextVisibleItem(materialBrowserModel.rowCount(),
|
||||
newTargetIdx = root.nextVisibleItem(materialBrowserModel.rowCount(),
|
||||
-1, materialBrowserModel)
|
||||
}
|
||||
if (newTargetIdx >= 0)
|
||||
@@ -153,24 +180,24 @@ Item {
|
||||
}
|
||||
} else if (delta > 0) {
|
||||
if (matSecFocused) {
|
||||
targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex,
|
||||
targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex,
|
||||
delta, materialBrowserModel)
|
||||
if (targetIdx >= 0) {
|
||||
materialBrowserModel.selectMaterial(targetIdx)
|
||||
} else if (!materialBrowserTexturesModel.isEmpty && texturesSection.expanded) {
|
||||
targetIdx = nextVisibleItem(-1, 1, materialBrowserTexturesModel)
|
||||
targetIdx = root.nextVisibleItem(-1, 1, materialBrowserTexturesModel)
|
||||
if (targetIdx >= 0) {
|
||||
if (delta !== 1) {
|
||||
// Try to match column when switching between materials/textures
|
||||
origRowIdx = rowIndexOfItem(materialBrowserModel.selectedIndex,
|
||||
origRowIdx = root.rowIndexOfItem(materialBrowserModel.selectedIndex,
|
||||
delta, materialBrowserModel)
|
||||
if (visibleItemCount(materialBrowserTexturesModel) > origRowIdx) {
|
||||
if (root.visibleItemCount(materialBrowserTexturesModel) > origRowIdx) {
|
||||
if (origRowIdx > 0) {
|
||||
newTargetIdx = nextVisibleItem(targetIdx, origRowIdx,
|
||||
newTargetIdx = root.nextVisibleItem(targetIdx, origRowIdx,
|
||||
materialBrowserTexturesModel)
|
||||
}
|
||||
} else {
|
||||
newTargetIdx = nextVisibleItem(materialBrowserTexturesModel.rowCount(),
|
||||
newTargetIdx = root.nextVisibleItem(materialBrowserTexturesModel.rowCount(),
|
||||
-1, materialBrowserTexturesModel)
|
||||
}
|
||||
if (newTargetIdx >= 0)
|
||||
@@ -181,7 +208,7 @@ Item {
|
||||
}
|
||||
}
|
||||
} else if (texSecFocused) {
|
||||
targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
|
||||
targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
|
||||
delta, materialBrowserTexturesModel)
|
||||
if (targetIdx >= 0)
|
||||
materialBrowserTexturesModel.selectTexture(targetIdx)
|
||||
@@ -190,24 +217,12 @@ Item {
|
||||
}
|
||||
|
||||
Keys.enabled: true
|
||||
Keys.onDownPressed: {
|
||||
selectNextVisibleItem(gridMaterials.columns)
|
||||
}
|
||||
Keys.onDownPressed: root.selectNextVisibleItem(gridMaterials.columns)
|
||||
Keys.onUpPressed: root.selectNextVisibleItem(-gridMaterials.columns)
|
||||
Keys.onLeftPressed: root.selectNextVisibleItem(-1)
|
||||
Keys.onRightPressed: root.selectNextVisibleItem(1)
|
||||
|
||||
Keys.onUpPressed: {
|
||||
selectNextVisibleItem(-gridMaterials.columns)
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
selectNextVisibleItem(-1)
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
selectNextVisibleItem(1)
|
||||
}
|
||||
|
||||
function handleEnterPress()
|
||||
{
|
||||
function handleEnterPress() {
|
||||
if (searchBox.activeFocus)
|
||||
return
|
||||
|
||||
@@ -217,13 +232,8 @@ Item {
|
||||
materialBrowserTexturesModel.openTextureEditor()
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
handleEnterPress()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
handleEnterPress()
|
||||
}
|
||||
Keys.onEnterPressed: root.handleEnterPress()
|
||||
Keys.onReturnPressed: root.handleEnterPress()
|
||||
|
||||
MouseArea {
|
||||
id: focusGrabber
|
||||
@@ -249,10 +259,10 @@ Item {
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (!root.enableUiElements)
|
||||
return;
|
||||
return
|
||||
|
||||
var matsSecBottom = mapFromItem(materialsSection, 0, materialsSection.y).y
|
||||
+ materialsSection.height;
|
||||
+ materialsSection.height
|
||||
|
||||
if (mouse.y < matsSecBottom)
|
||||
ctxMenu.popupMenu()
|
||||
@@ -261,8 +271,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureVisible(yPos, itemHeight)
|
||||
{
|
||||
function ensureVisible(yPos, itemHeight) {
|
||||
let currentY = contentYBehavior.targetValue && scrollViewAnim.running
|
||||
? contentYBehavior.targetValue : scrollView.contentY
|
||||
|
||||
@@ -286,18 +295,18 @@ Item {
|
||||
return false
|
||||
}
|
||||
|
||||
function ensureSelectedVisible()
|
||||
{
|
||||
function ensureSelectedVisible() {
|
||||
if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem
|
||||
&& materialBrowserModel.isVisible(materialBrowserModel.selectedIndex)) {
|
||||
return ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
||||
return root.ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
||||
root.currMaterialItem.height)
|
||||
} else if (!rootView.materialSectionFocused && texturesSection.expanded) {
|
||||
let currItem = texturesRepeater.itemAt(materialBrowserTexturesModel.selectedIndex)
|
||||
if (currItem && materialBrowserTexturesModel.isVisible(materialBrowserTexturesModel.selectedIndex))
|
||||
return ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y, currItem.height)
|
||||
return root.ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
||||
currItem.height)
|
||||
} else {
|
||||
return ensureVisible(0, 90)
|
||||
return root.ensureVisible(0, 90)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,15 +319,14 @@ Item {
|
||||
onTriggered: {
|
||||
// Redo until ensuring didn't change things
|
||||
if (!root.ensureSelectedVisible()) {
|
||||
stop()
|
||||
interval = 20
|
||||
triggeredOnStart = true
|
||||
ensureTimer.stop()
|
||||
ensureTimer.interval = 20
|
||||
ensureTimer.triggeredOnStart = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startDelayedEnsureTimer(delay)
|
||||
{
|
||||
function startDelayedEnsureTimer(delay) {
|
||||
// Ensuring visibility immediately in some cases like before new search results are rendered
|
||||
// causes mapToItem return incorrect values, leading to undesirable flicker,
|
||||
// so delay ensuring visibility a bit.
|
||||
@@ -330,8 +338,7 @@ Item {
|
||||
Connections {
|
||||
target: materialBrowserModel
|
||||
|
||||
function onSelectedIndexChanged()
|
||||
{
|
||||
function onSelectedIndexChanged() {
|
||||
// commit rename upon changing selection
|
||||
if (root.currMaterialItem)
|
||||
root.currMaterialItem.forceFinishEditing();
|
||||
@@ -341,8 +348,7 @@ Item {
|
||||
ensureTimer.start()
|
||||
}
|
||||
|
||||
function onIsEmptyChanged()
|
||||
{
|
||||
function onIsEmptyChanged() {
|
||||
ensureTimer.start()
|
||||
}
|
||||
}
|
||||
@@ -350,13 +356,11 @@ Item {
|
||||
Connections {
|
||||
target: materialBrowserTexturesModel
|
||||
|
||||
function onSelectedIndexChanged()
|
||||
{
|
||||
function onSelectedIndexChanged() {
|
||||
ensureTimer.start()
|
||||
}
|
||||
|
||||
function onIsEmptyChanged()
|
||||
{
|
||||
function onIsEmptyChanged() {
|
||||
ensureTimer.start()
|
||||
}
|
||||
}
|
||||
@@ -364,8 +368,7 @@ Item {
|
||||
Connections {
|
||||
target: rootView
|
||||
|
||||
function onMaterialSectionFocusedChanged()
|
||||
{
|
||||
function onMaterialSectionFocusedChanged() {
|
||||
ensureTimer.start()
|
||||
}
|
||||
}
|
||||
@@ -614,9 +617,10 @@ Item {
|
||||
id: scrollView
|
||||
|
||||
width: root.width
|
||||
height: root.height - toolbar.height
|
||||
height: root.height - toolbar.height - col.spacing
|
||||
clip: true
|
||||
visible: root.enableUiElements
|
||||
hideHorizontalScrollBar: true
|
||||
interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging
|
||||
&& !HelperWidgets.Controller.contextMenuOpened
|
||||
|
||||
@@ -637,6 +641,12 @@ Item {
|
||||
id: materialsSection
|
||||
|
||||
width: root.width
|
||||
|
||||
leftPadding: StudioTheme.Values.sectionPadding
|
||||
rightPadding: StudioTheme.Values.sectionPadding
|
||||
topPadding: StudioTheme.Values.sectionPadding
|
||||
bottomPadding: StudioTheme.Values.sectionPadding
|
||||
|
||||
caption: qsTr("Materials")
|
||||
dropEnabled: true
|
||||
category: "MaterialBrowser"
|
||||
@@ -671,11 +681,10 @@ Item {
|
||||
Grid {
|
||||
id: gridMaterials
|
||||
|
||||
width: scrollView.width
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
bottomPadding: 5
|
||||
columns: root.width / root.cellWidth
|
||||
width: scrollView.width - materialsSection.leftPadding
|
||||
- materialsSection.rightPadding
|
||||
spacing: StudioTheme.Values.sectionGridSpacing
|
||||
columns: root.numColumns
|
||||
|
||||
Repeater {
|
||||
id: materialRepeater
|
||||
@@ -691,10 +700,10 @@ Item {
|
||||
width: root.cellWidth
|
||||
height: root.cellHeight
|
||||
|
||||
onShowContextMenu: {
|
||||
ctxMenu.popupMenu(this, model)
|
||||
}
|
||||
onShowContextMenu: ctxMenu.popupMenu(this, model)
|
||||
}
|
||||
|
||||
onCountChanged: root.responsiveResize(root.width, root.height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,6 +736,11 @@ Item {
|
||||
id: texturesSection
|
||||
|
||||
width: root.width
|
||||
leftPadding: StudioTheme.Values.sectionPadding
|
||||
rightPadding: StudioTheme.Values.sectionPadding
|
||||
topPadding: StudioTheme.Values.sectionPadding
|
||||
bottomPadding: StudioTheme.Values.sectionPadding
|
||||
|
||||
caption: qsTr("Textures")
|
||||
category: "MaterialBrowser"
|
||||
|
||||
@@ -768,11 +782,10 @@ Item {
|
||||
Grid {
|
||||
id: gridTextures
|
||||
|
||||
width: scrollView.width
|
||||
leftPadding: 5
|
||||
rightPadding: 5
|
||||
bottomPadding: 5
|
||||
columns: root.width / root.cellWidth
|
||||
width: scrollView.width - texturesSection.leftPadding
|
||||
- texturesSection.rightPadding
|
||||
spacing: StudioTheme.Values.sectionGridSpacing
|
||||
columns: root.numColumns
|
||||
|
||||
Repeater {
|
||||
id: texturesRepeater
|
||||
@@ -782,10 +795,10 @@ Item {
|
||||
width: root.cellWidth
|
||||
height: root.cellHeight
|
||||
|
||||
onShowContextMenu: {
|
||||
ctxMenuTextures.popupMenu(model)
|
||||
}
|
||||
onShowContextMenu: ctxMenuTextures.popupMenu(model)
|
||||
}
|
||||
|
||||
onCountChanged: root.responsiveResize(root.width, root.height)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,32 +8,24 @@ import HelperWidgets 2.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import MaterialBrowserBackend
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal showContextMenu()
|
||||
|
||||
function refreshPreview()
|
||||
{
|
||||
function refreshPreview() {
|
||||
img.source = ""
|
||||
img.source = "image://materialBrowser/" + materialInternalId
|
||||
}
|
||||
|
||||
function forceFinishEditing()
|
||||
{
|
||||
function forceFinishEditing() {
|
||||
matName.commitRename()
|
||||
}
|
||||
|
||||
function startRename()
|
||||
{
|
||||
function startRename() {
|
||||
matName.startRename()
|
||||
}
|
||||
|
||||
border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
|
||||
border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index
|
||||
? StudioTheme.Values.themeControlOutlineInteraction
|
||||
: "transparent"
|
||||
color: "transparent"
|
||||
visible: materialVisible
|
||||
|
||||
DropArea {
|
||||
@@ -81,12 +73,10 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
spacing: 1
|
||||
|
||||
Item { width: 1; height: 5 } // spacer
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
width: root.width - 10
|
||||
width: root.width
|
||||
height: img.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: "image://materialBrowser/" + materialInternalId
|
||||
@@ -94,8 +84,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
// Eat keys so they are not passed to parent while editing name
|
||||
Keys.onPressed: (e) => {
|
||||
e.accepted = true;
|
||||
Keys.onPressed: (event) => {
|
||||
event.accepted = true
|
||||
}
|
||||
|
||||
MaterialBrowserItemName {
|
||||
@@ -116,4 +106,14 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: marker
|
||||
anchors.fill: parent
|
||||
border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
|
||||
border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index
|
||||
? StudioTheme.Values.themeControlOutlineInteraction
|
||||
: "transparent"
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,22 +9,14 @@ import HelperWidgets
|
||||
import StudioTheme as StudioTheme
|
||||
import MaterialBrowserBackend
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
visible: textureVisible
|
||||
|
||||
color: "transparent"
|
||||
border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
|
||||
? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
|
||||
border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
|
||||
? StudioTheme.Values.themeControlOutlineInteraction
|
||||
: "transparent"
|
||||
|
||||
signal showContextMenu()
|
||||
|
||||
function forceFinishEditing()
|
||||
{
|
||||
function forceFinishEditing() {
|
||||
txtId.commitRename()
|
||||
}
|
||||
|
||||
@@ -68,12 +60,11 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
spacing: 1
|
||||
|
||||
Item { width: 1; height: 5 } // spacer
|
||||
Image {
|
||||
id: img
|
||||
source: "image://materialBrowserTex/" + textureSource
|
||||
asynchronous: true
|
||||
width: root.width - 10
|
||||
width: root.width
|
||||
height: img.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
smooth: true
|
||||
@@ -81,8 +72,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
// Eat keys so they are not passed to parent while editing name
|
||||
Keys.onPressed: (e) => {
|
||||
e.accepted = true;
|
||||
Keys.onPressed: (event) => {
|
||||
event.accepted = true
|
||||
}
|
||||
|
||||
MaterialBrowserItemName {
|
||||
@@ -103,4 +94,16 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: marker
|
||||
anchors.fill: parent
|
||||
|
||||
color: "transparent"
|
||||
border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
|
||||
? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
|
||||
border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
|
||||
? StudioTheme.Values.themeControlOutlineInteraction
|
||||
: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import QtQuick.Window 2.15
|
||||
|
||||
Window {
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
Window {
|
||||
}
|
||||
@@ -20,6 +20,21 @@ Section {
|
||||
// TODO position property, what should be the range?!
|
||||
|
||||
SectionLayout {
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Source")
|
||||
tooltip: qsTr("Adds an image from the local file system.")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
UrlChooser {
|
||||
backendValue: backendValues.source
|
||||
filter: "*.avi *.mp4 *.mpeg *.wav"
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel { text: qsTr("Playback rate") }
|
||||
|
||||
SecondColumnLayout {
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Section {
|
||||
caption: qsTr("Animated Sprite")
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
SectionLayout {
|
||||
PropertyLabel {
|
||||
text: qsTr("Source")
|
||||
tooltip: qsTr("Adds an image from the local file system.")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
UrlChooser {
|
||||
backendValue: backendValues.source
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Frame size")
|
||||
tooltip: qsTr("Sets the width and height of the frame.")
|
||||
blockedByTemplate: !(backendValues.frameWidth.isAvailable || backendValues.frameHeight.isAvailable)
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameWidth
|
||||
minimumValue: 0
|
||||
maximumValue: 8192
|
||||
decimals: 0
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
//: The width of the animated sprite frame
|
||||
text: qsTr("W", "width")
|
||||
tooltip: qsTr("Width.")
|
||||
enabled: backendValues.frameWidth.isAvailable
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameHeight
|
||||
minimumValue: 0
|
||||
maximumValue: 8192
|
||||
decimals: 0
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
//: The height of the animated sprite frame
|
||||
text: qsTr("H", "height")
|
||||
tooltip: qsTr("Height.")
|
||||
enabled: backendValues.frameHeight.isAvailable
|
||||
}
|
||||
/*
|
||||
TODO QDS-4836
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
LinkIndicator2D {}
|
||||
*/
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Frame coordinates")
|
||||
tooltip: qsTr("Sets the coordinates of the first frame of the animated sprite.")
|
||||
blockedByTemplate: !(backendValues.frameX.isAvailable || backendValues.frameY.isAvailable)
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameX
|
||||
minimumValue: 0
|
||||
maximumValue: 8192
|
||||
decimals: 0
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
//: The width of the animated sprite frame
|
||||
text: qsTr("X", "Frame X")
|
||||
tooltip: qsTr("Frame X coordinate.")
|
||||
enabled: backendValues.frameX.isAvailable
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameY
|
||||
minimumValue: 0
|
||||
maximumValue: 8192
|
||||
decimals: 0
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
//: The height of the animated sprite frame
|
||||
text: qsTr("Y", "Frame Y")
|
||||
tooltip: qsTr("Frame Y coordinate.")
|
||||
enabled: backendValues.frameY.isAvailable
|
||||
}
|
||||
/*
|
||||
TODO QDS-4836
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
LinkIndicator2D {}
|
||||
*/
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Frame count")
|
||||
tooltip: qsTr("Sets the number of frames in this animated sprite.")
|
||||
blockedByTemplate: !backendValues.frameCount.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameCount
|
||||
decimals: 0
|
||||
minimumValue: 0
|
||||
maximumValue: 10000
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
//frame rate OR frame duration OR frame sync should be used
|
||||
//frame rate has priority over frame duration
|
||||
//frame sync has priority over rate and duration
|
||||
PropertyLabel {
|
||||
text: qsTr("Frame rate")
|
||||
tooltip: qsTr("Sets the number of frames per second to show in the animation.")
|
||||
blockedByTemplate: !backendValues.frameRate.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameRate
|
||||
decimals: 2
|
||||
minimumValue: 0
|
||||
maximumValue: 1000
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
//frame duration OR frame rate OR frame sync should be used
|
||||
//frame rate has priority over frame duration
|
||||
//frame sync has priority over rate and duration
|
||||
PropertyLabel {
|
||||
text: qsTr("Frame duration")
|
||||
tooltip: qsTr("Sets the duration of each frame of the animation in milliseconds.")
|
||||
blockedByTemplate: !backendValues.frameDuration.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameDuration
|
||||
decimals: 0
|
||||
minimumValue: 0
|
||||
maximumValue: 100000
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
//frame sync OR frame rate OR frame duration should be used
|
||||
//frame rate has priority over frame duration
|
||||
//frame sync has priority over rate and duration
|
||||
PropertyLabel {
|
||||
text: qsTr("Frame sync")
|
||||
tooltip: qsTr("Sets frame advancements one frame each time a frame is rendered to the screen.")
|
||||
blockedByTemplate: !backendValues.frameSync.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.frameSync.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.frameSync
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Loops")
|
||||
tooltip: qsTr("After playing the animation this many times, the animation will automatically stop.")
|
||||
blockedByTemplate: !backendValues.loops.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.loops
|
||||
decimals: 0
|
||||
minimumValue: -1 //AnimatedSprite.Infinite = -1
|
||||
maximumValue: 100000
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Interpolate")
|
||||
tooltip: qsTr("If true, interpolation will occur between sprite frames to make the animation appear smoother.")
|
||||
blockedByTemplate: !backendValues.interpolate.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.interpolate.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.interpolate
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Finish behavior")
|
||||
tooltip: qsTr("Sets the behavior when the animation finishes on its own.")
|
||||
blockedByTemplate: !backendValues.finishBehavior.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
ComboBox {
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
width: implicitWidth
|
||||
scope: "AnimatedSprite"
|
||||
model: ["FinishAtInitialFrame", "FinishAtFinalFrame"]
|
||||
backendValue: backendValues.finishBehavior
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Reverse")
|
||||
tooltip: qsTr("If true, the animation will be played in reverse.")
|
||||
blockedByTemplate: !backendValues.reverse.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.reverse.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.reverse
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Running")
|
||||
tooltip: qsTr("Whether the sprite is animating or not.")
|
||||
blockedByTemplate: !backendValues.running.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.running.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.running
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Paused")
|
||||
tooltip: qsTr("When paused, the current frame can be advanced manually.")
|
||||
blockedByTemplate: !backendValues.paused.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.paused.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.paused
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Current frame")
|
||||
tooltip: qsTr("When paused, the current frame can be advanced manually by setting this property.")
|
||||
blockedByTemplate: !backendValues.currentFrame.isAvailable
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.currentFrame
|
||||
decimals: 0
|
||||
minimumValue: 0
|
||||
maximumValue: 100000
|
||||
enabled: backendValue.isAvailable
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,11 @@ PropertyEditorPane {
|
||||
|
||||
ComponentSection {}
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: SelectionDynamicPropertiesModel {}
|
||||
visible: !hasMultiSelection
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Shapes 1.15
|
||||
import QtQuick.Templates 2.15 as T
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import QtQuickDesignerColorPalette 1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Shapes
|
||||
import QtQuick.Templates as T
|
||||
import StudioTheme as StudioTheme
|
||||
import StudioControls as StudioControls
|
||||
import QtQuickDesignerTheme
|
||||
import QtQuickDesignerColorPalette
|
||||
|
||||
SecondColumnLayout {
|
||||
id: colorEditor
|
||||
@@ -29,7 +30,7 @@ SecondColumnLayout {
|
||||
return colorEditor.backendValue.value
|
||||
}
|
||||
|
||||
property alias gradientPropertyName: popupLoader.gradientPropertyName
|
||||
property alias gradientPropertyName: popupDialog.gradientPropertyName
|
||||
|
||||
property alias gradientThumbnail: gradientThumbnail
|
||||
property alias shapeGradientThumbnail: shapeGradientThumbnail
|
||||
@@ -66,12 +67,12 @@ SecondColumnLayout {
|
||||
target: colorEditor
|
||||
|
||||
function onValueChanged() {
|
||||
if (popupLoader.isNotInGradientMode())
|
||||
if (popupDialog.isSolid())
|
||||
colorEditor.syncColor()
|
||||
}
|
||||
|
||||
function onBackendValueChanged() {
|
||||
if (popupLoader.isNotInGradientMode())
|
||||
if (popupDialog.isSolid())
|
||||
colorEditor.syncColor()
|
||||
}
|
||||
}
|
||||
@@ -101,13 +102,13 @@ SecondColumnLayout {
|
||||
if (colorEditor.__block)
|
||||
return
|
||||
|
||||
if (!popupLoader.isInValidState)
|
||||
if (!popupDialog.isInValidState)
|
||||
return
|
||||
|
||||
popupLoader.commitToGradient()
|
||||
popupDialog.commitToGradient()
|
||||
|
||||
// Delay setting the color to keep ui responsive
|
||||
if (popupLoader.isNotInGradientMode())
|
||||
if (popupDialog.isSolid())
|
||||
colorEditorTimer.restart()
|
||||
}
|
||||
|
||||
@@ -127,17 +128,16 @@ SecondColumnLayout {
|
||||
id: gradientThumbnail
|
||||
anchors.fill: parent
|
||||
anchors.margins: StudioTheme.Values.border
|
||||
visible: !popupLoader.isNotInGradientMode()
|
||||
visible: !popupDialog.isSolid()
|
||||
&& !colorEditor.shapeGradients
|
||||
&& popupLoader.hasLinearGradient()
|
||||
&& popupDialog.isLinearGradient()
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: shape
|
||||
anchors.fill: parent
|
||||
anchors.margins: StudioTheme.Values.border
|
||||
visible: !popupLoader.isNotInGradientMode()
|
||||
&& colorEditor.shapeGradients
|
||||
visible: !popupDialog.isSolid() && colorEditor.shapeGradients
|
||||
|
||||
ShapePath {
|
||||
id: shapeGradientThumbnail
|
||||
@@ -171,78 +171,77 @@ SecondColumnLayout {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popupLoader.opened ? popupLoader.close() : popupLoader.open()
|
||||
popupDialog.visibility ? popupDialog.close() : popupDialog.open()
|
||||
forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: popupLoader
|
||||
|
||||
property bool isInValidState: popupLoader.active ? popupLoader.dialog.isInValidState : true
|
||||
|
||||
property QtObject dialog: popupLoader.loader.item
|
||||
|
||||
property bool opened: popupLoader.active ? popupLoader.dialog.opened : false
|
||||
StudioControls.PopupDialog {
|
||||
id: popupDialog
|
||||
|
||||
property bool isInValidState: loader.active ? popupDialog.loaderItem.isInValidState : true
|
||||
property QtObject loaderItem: loader.item
|
||||
property string gradientPropertyName
|
||||
|
||||
width: 260
|
||||
maximumHeight: Screen.desktopAvailableHeight * 0.7
|
||||
|
||||
function commitToGradient() {
|
||||
if (!popupLoader.active)
|
||||
if (!loader.active)
|
||||
return
|
||||
|
||||
if (colorEditor.supportGradient && popupLoader.dialog.gradientModel.hasGradient) {
|
||||
if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) {
|
||||
var hexColor = convertColorToString(colorEditor.color)
|
||||
hexTextField.text = hexColor
|
||||
popupLoader.dialog.commitGradientColor()
|
||||
popupDialog.loaderItem.commitGradientColor()
|
||||
}
|
||||
}
|
||||
|
||||
function isNotInGradientMode() {
|
||||
if (!popupLoader.active)
|
||||
function isSolid() {
|
||||
if (!loader.active)
|
||||
return true
|
||||
return popupLoader.dialog.isNotInGradientMode()
|
||||
|
||||
return popupDialog.loaderItem.isSolid()
|
||||
}
|
||||
|
||||
function hasLinearGradient(){
|
||||
if (!popupLoader.active)
|
||||
function isLinearGradient(){
|
||||
if (!loader.active)
|
||||
return false
|
||||
return popupLoader.dialog.hasLinearGradient()
|
||||
|
||||
return popupDialog.loaderItem.isLinearGradient()
|
||||
}
|
||||
|
||||
function ensureLoader() {
|
||||
if (!popupLoader.active)
|
||||
popupLoader.active = true
|
||||
if (!loader.active)
|
||||
loader.active = true
|
||||
}
|
||||
|
||||
function open() {
|
||||
popupLoader.ensureLoader()
|
||||
popupLoader.dialog.open()
|
||||
}
|
||||
|
||||
function close() {
|
||||
popupLoader.ensureLoader()
|
||||
popupLoader.dialog.close()
|
||||
popupDialog.ensureLoader()
|
||||
popupDialog.show(preview)
|
||||
}
|
||||
|
||||
function determineActiveColorMode() {
|
||||
if (popupLoader.active && popupLoader.dialog)
|
||||
popupLoader.dialog.determineActiveColorMode()
|
||||
if (loader.active && popupDialog.loaderItem)
|
||||
popupDialog.loaderItem.determineActiveColorMode()
|
||||
else
|
||||
colorEditor.syncColor()
|
||||
}
|
||||
|
||||
property alias active: popupLoader.loader.active
|
||||
property Loader loader: Loader {
|
||||
parent: preview
|
||||
Loader {
|
||||
id: loader
|
||||
active: colorEditor.supportGradient
|
||||
|
||||
sourceComponent: ColorEditorPopup {
|
||||
id: cePopup
|
||||
x: cePopup.__defaultX
|
||||
y: cePopup.__defaultY
|
||||
shapeGradients: colorEditor.shapeGradients
|
||||
supportGradient: colorEditor.supportGradient
|
||||
width: popupDialog.contentWidth
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
popupDialog.loaderItem.initEditor()
|
||||
popupDialog.titleBar = loader.item.titleBarContent
|
||||
}
|
||||
onLoaded: popupLoader.dialog.initEditor()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,19 +254,26 @@ SecondColumnLayout {
|
||||
id: hexTextField
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
width: implicitWidth
|
||||
enabled: popupLoader.isNotInGradientMode()
|
||||
width: hexTextField.implicitWidth
|
||||
enabled: popupDialog.isSolid()
|
||||
writeValueManually: true
|
||||
validator: RegExpValidator {
|
||||
regExp: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g
|
||||
}
|
||||
showTranslateCheckBox: false
|
||||
indicatorVisible: true
|
||||
indicator.icon.text: StudioTheme.Constants.copy_small
|
||||
indicator.onClicked: {
|
||||
hexTextField.selectAll()
|
||||
hexTextField.copy()
|
||||
hexTextField.deselect()
|
||||
}
|
||||
backendValue: colorEditor.backendValue
|
||||
|
||||
onAccepted: colorEditor.color = colorFromString(hexTextField.text)
|
||||
onCommitData: {
|
||||
colorEditor.color = colorFromString(hexTextField.text)
|
||||
if (popupLoader.isNotInGradientMode()) {
|
||||
if (popupDialog.isSolid()) {
|
||||
if (colorEditor.isVector3D) {
|
||||
backendValue.value = Qt.vector3d(colorEditor.color.r,
|
||||
colorEditor.color.g,
|
||||
@@ -283,9 +289,7 @@ SecondColumnLayout {
|
||||
id: spacer
|
||||
}
|
||||
|
||||
Component.onCompleted: popupLoader.determineActiveColorMode()
|
||||
Component.onCompleted: popupDialog.determineActiveColorMode()
|
||||
|
||||
onBackendValueChanged: {
|
||||
popupLoader.determineActiveColorMode()
|
||||
}
|
||||
onBackendValueChanged: popupDialog.determineActiveColorMode()
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import HelperWidgets 2.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Item {
|
||||
property alias currentColor: colorLine.color
|
||||
|
||||
width: 300
|
||||
height: StudioTheme.Values.colorEditorPopupLineHeight
|
||||
|
||||
Image {
|
||||
id: checkerboard
|
||||
anchors.fill: colorLine
|
||||
source: "images/checkers.png"
|
||||
fillMode: Image.Tile
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: colorLine
|
||||
height: StudioTheme.Values.hueSliderHeight
|
||||
width: parent.width
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
border.width: StudioTheme.Values.border
|
||||
color: "white"
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||