Merge "Merge remote-tracking branch 'origin/qds/dev'"
@@ -155,27 +155,14 @@
|
||||
{Qt Code Review}.
|
||||
|
||||
For example:
|
||||
\list 1
|
||||
\li Clone the module repository.
|
||||
\badcode
|
||||
git clone https://code.qt.io/qt-labs/qtquickdesigner-components.git
|
||||
\endcode
|
||||
|
||||
Then use qmake from your Qt installation to build the module and to add it
|
||||
to your Qt. Switch to the directory that contains the sources (usually,
|
||||
qtquickdesigner-components), make sure you checkout the qmake branch, and enter
|
||||
the following commands:
|
||||
|
||||
\badcode
|
||||
<path_to_qmake>\qmake -r
|
||||
make
|
||||
make install
|
||||
\endcode
|
||||
|
||||
On Windows, use the \c nmake and \c {nmake install} commands instead.
|
||||
|
||||
If you prefer CMake instead and you want to benefit from the QML compilation,
|
||||
then you can checkout the dev branch instead. CMake is only supported since Qt 6.2.
|
||||
\li Install the Qt Quick Designer Components module.
|
||||
Enter the following commands:
|
||||
|
||||
\badcode
|
||||
mkdir build
|
||||
cd build
|
||||
@@ -183,28 +170,9 @@
|
||||
cmake --build .
|
||||
cmake --install .
|
||||
\endcode
|
||||
|
||||
\section1 Adding Qt Quick Timeline Module to Qt Installations
|
||||
|
||||
\note You only need to do this if your Qt version is older than 5.14.
|
||||
|
||||
Check out the \l{Qt Quick Timeline} module from
|
||||
\l{https://codereview.qt-project.org/#/admin/projects/qt/qtquicktimeline}
|
||||
{Qt Code Review}.
|
||||
|
||||
For example:
|
||||
\badcode
|
||||
git clone "https://codereview.qt-project.org/qt/qtquicktimeline"
|
||||
\endcode
|
||||
|
||||
To use qmake, you need to check out a branch or tag that contains the
|
||||
qmake configuration files.
|
||||
|
||||
For example:
|
||||
\badcode
|
||||
git checkout v5.15.2
|
||||
\endcode
|
||||
|
||||
Then build the module and add it to your Qt as described in the previous
|
||||
section.
|
||||
\note Here, \e <path_to_qt_install_directory> and \e <path_to_qtquickdesigner-components>
|
||||
needs to be replaced with the real location on your local drive. For example,
|
||||
\e <path_to_qt_install_directory> can be something like \e /Qt/6.3.0/msvc2019_64
|
||||
and \e <path_to_qtquickdesigner-components> like this \e ../qtquickdesigner-components/
|
||||
\endlist
|
||||
*/
|
||||
|
@@ -4,7 +4,11 @@
|
||||
/*!
|
||||
\page creator-open-documents-view.html
|
||||
\previouspage creator-file-system-view.html
|
||||
\if defined(qtdesignstudio)
|
||||
\nextpage studio-content-library.html
|
||||
\else
|
||||
\nextpage creator-output-panes.html
|
||||
\endif
|
||||
|
||||
\title Open Documents
|
||||
|
||||
|
@@ -108,7 +108,7 @@
|
||||
|
||||
We will now copy the color animation from the text label to the indicator.
|
||||
First, we right-click the text component in the \uicontrol Timeline view to
|
||||
open a context menu and select \uicontrol {Copy All Keyframes from Item} to
|
||||
open a context menu and select \uicontrol {Copy All Keyframes} to
|
||||
copy the keyframe values we specified for the text label.
|
||||
|
||||
Next, we select the indicator in the \uicontrol Navigator, and then select
|
||||
|
BIN
doc/qtdesignstudio/images/content-library-add-texture.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
doc/qtdesignstudio/images/content-library.webp
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
doc/qtdesignstudio/images/icons/area.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
doc/qtdesignstudio/images/icons/directional.png
Normal file
After Width: | Height: | Size: 265 B |
BIN
doc/qtdesignstudio/images/icons/line-particles-16px.png
Normal file
After Width: | Height: | Size: 550 B |
BIN
doc/qtdesignstudio/images/icons/point.png
Normal file
After Width: | Height: | Size: 458 B |
BIN
doc/qtdesignstudio/images/icons/repeller-16px.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
doc/qtdesignstudio/images/icons/scale-affector-16px.png
Normal file
After Width: | Height: | Size: 518 B |
BIN
doc/qtdesignstudio/images/icons/spot.png
Normal file
After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 41 KiB |
BIN
doc/qtdesignstudio/images/select-material-property.png
Normal file
After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 36 KiB |
BIN
doc/qtdesignstudio/images/studio-3d-properties-line-particle.png
Normal file
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/qtdesignstudio/images/texture-editor.png
Normal file
After Width: | Height: | Size: 55 KiB |
@@ -44,23 +44,19 @@
|
||||
|
||||
Property bindings are created implicitly whenever a property is assigned a
|
||||
JavaScript expression. To set JavaScript expressions as values of properties
|
||||
in the \l Properties view, select the \inlineimage icons/action-icon.png
|
||||
in the \l Properties view:
|
||||
\list 1
|
||||
\li Select the \inlineimage icons/action-icon.png
|
||||
(\uicontrol Actions) menu next to a property, and then select
|
||||
\uicontrol {Set Binding}.
|
||||
|
||||
\image qmldesigner-set-expression.png "Actions menu"
|
||||
|
||||
In \uicontrol {Binding Editor}, select a component and a property from
|
||||
\li In \uicontrol {Binding Editor}, select a component and a property from
|
||||
lists of available components and their properties.
|
||||
|
||||
\image qmldesigner-binding-editor.png "Binding Editor"
|
||||
|
||||
Alternatively, start typing a
|
||||
string and press \key Ctrl+Space to display a list of properties, IDs, and
|
||||
code snippets. When you enter a period (.) after a property name, a list of
|
||||
available values is displayed. Press \key Enter to accept the first
|
||||
suggestion in the list and to complete the code. For more information, see
|
||||
\l{Completing Code}.
|
||||
\endlist
|
||||
|
||||
When a binding is set, the \uicontrol Actions menu icon changes to
|
||||
\inlineimage icons/action-icon-binding.png
|
||||
|
@@ -31,11 +31,15 @@
|
||||
\list 1
|
||||
\li Open the application in \QDS.
|
||||
\li Select \uicontrol File > \uicontrol {Share Application Online}.
|
||||
\li In the dialog, select \uicontrol Share.
|
||||
\li Optionally, select \uicontrol {Protect with password}, and enter a password to prevent
|
||||
unauthorized viewing of the application.
|
||||
\note If you share the same application again, you must set the password
|
||||
again. Otherwise, the application is not password protected any longer.
|
||||
\li Select \uicontrol Share.
|
||||
\image share-online.webp
|
||||
\endlist
|
||||
|
||||
In the dialog, you can now open the application in a web
|
||||
You can now open the application in a web
|
||||
browser, copy the link to share with others, or manage your shared
|
||||
applications.
|
||||
|
||||
|
@@ -32,6 +32,8 @@
|
||||
\li \l{Projects}
|
||||
\li \l{File System}
|
||||
\li \l{Open Documents}
|
||||
\li \l{Content Library}
|
||||
\li \l{Texture Editor}
|
||||
\endlist
|
||||
\li \l{Managing Workspaces}
|
||||
\li \l{Managing Sessions}
|
||||
|
@@ -41,25 +41,25 @@
|
||||
\li More Information
|
||||
|
||||
\row
|
||||
\li \inlineimage directional.png
|
||||
\li \inlineimage icons/directional.png
|
||||
\li Directional Light
|
||||
\li
|
||||
\li \l{DirectionalLight}{Light Directional}
|
||||
|
||||
\row
|
||||
\li \inlineimage point.png
|
||||
\li \inlineimage icons/point.png
|
||||
\li Point Light
|
||||
\li
|
||||
\li \l{PointLight}{Light Point}
|
||||
|
||||
\row
|
||||
\li \inlineimage spot.png
|
||||
\li \inlineimage icons/spot.png
|
||||
\li Spot Light
|
||||
\li
|
||||
\li \l{SpotLight}{Light Spot}
|
||||
|
||||
\row
|
||||
\li \inlineimage area.png
|
||||
\li \inlineimage icons/area.png
|
||||
\li Area Light
|
||||
\li \inlineimage ok.png
|
||||
\li \l{AreaLight}{Light Area}
|
||||
|
@@ -70,10 +70,15 @@
|
||||
\li \inlineimage icons/attractor-16px.png
|
||||
\li Attractor
|
||||
\li Attracts particles towards a specific point.
|
||||
\row
|
||||
\li \inlineimage icons/emit-burst-16px.png
|
||||
\li Dynamic Burst
|
||||
\li Emits particles in dynamic bursts. Use dynamic burst for
|
||||
emitters that are moving.
|
||||
\row
|
||||
\li \inlineimage icons/emit-burst-16px.png
|
||||
\li Emit Burst
|
||||
\li Generates declarative emitter bursts.
|
||||
\li Emits particles in bursts.
|
||||
\row
|
||||
\li \inlineimage icons/emitter-16px.png
|
||||
\li Emitter
|
||||
@@ -83,6 +88,10 @@
|
||||
\li Gravity
|
||||
\li Accelerates particles to a vector of the specified magnitude in the
|
||||
specified direction.
|
||||
\row
|
||||
\li \inlineimage icons/line-particles-16px.png
|
||||
\li Line Particle
|
||||
\li Creates line-shaped sprite particles.
|
||||
\row
|
||||
\li \inlineimage icons/model-blend-particle-16px.png
|
||||
\li Model Blend Particle
|
||||
@@ -110,6 +119,15 @@
|
||||
\li \inlineimage icons/point-rotator-16px.png
|
||||
\li Point Rotator
|
||||
\li Rotates particles around a pivot point.
|
||||
\row
|
||||
\li \inlineimage icons/repeller-16px.png
|
||||
\li Repeller
|
||||
\li Repels particles from its location.
|
||||
\row
|
||||
\li \inlineimage icons/scale-affector-16px.png
|
||||
\li Scale Affector
|
||||
\li Scales particles based on the particles' lifetime and other
|
||||
parameters.
|
||||
\row
|
||||
\li \inlineimage icons/sprite-particle-16px.png
|
||||
\li Sprite Particle
|
||||
@@ -399,19 +417,23 @@
|
||||
visualized, and some logical particles could lead to multiple visual
|
||||
particles being drawn on screen.
|
||||
|
||||
Two different logical particle components are supported:
|
||||
\uicontrol {Sprite Particle} for \l{Textures}{2D texture} particles and
|
||||
\uicontrol {Model Particle} for \l{3D Models}{3D model} particles. Model
|
||||
particles use \l{Instanced Rendering}{instanced rendering} to enabled the
|
||||
rendering of thousands of particles, with full \l{3D Materials}{materials}
|
||||
and \l{Lights}{lights} support.
|
||||
\QDS supports the following logical particle components:
|
||||
|
||||
The following components are available for adding logical particles and
|
||||
for modifying their actions and appearance:
|
||||
\list
|
||||
\li \uicontrol {Sprite Particle} and \uicontrol{Line Particle} for
|
||||
\l{Textures}{2D texture} particles.
|
||||
\li \uicontrol {Model Particle} for \l{3D Models}{3D model} particles.
|
||||
Model particles use \l{Instanced Rendering}{instanced rendering} to render
|
||||
thousands of particles, with full \l{3D Materials}{materials} and
|
||||
\l{Lights}{lights} support.
|
||||
\endlist
|
||||
|
||||
You can use the following components to add and modify logical particles:
|
||||
|
||||
\list
|
||||
\li \l{Sprite Particle}
|
||||
\li \l{Sprite Sequence}
|
||||
\li \l{Line Particle}
|
||||
\li \l{Model Particle}
|
||||
\li \l{Model Blend Particle}
|
||||
\endlist
|
||||
@@ -509,6 +531,48 @@
|
||||
\uicontrol {Frame index} is rendered. When it is enabled, each particle
|
||||
renders a random frame.
|
||||
|
||||
\section1 Line Particle
|
||||
|
||||
Specify properties for line particles in \uicontrol Properties >
|
||||
\uicontrol {Line Particle}.
|
||||
|
||||
\image studio-3d-properties-line-particle.png
|
||||
|
||||
\uicontrol {Segments} defines the number of segments in each line.
|
||||
|
||||
\uicontrol {Alpha Fade} defines the alpha fade factor of the lines. The
|
||||
value range is [0, 1]. When the value is greater than 0.0, the line fades
|
||||
more the further the segment is from the first particle segment.
|
||||
|
||||
\uicontrol {Scale Multiplier} modifies the line size for the line segments.
|
||||
The value range is [0, 2]. If the value is less than 1.0,
|
||||
the line gets smaller the further a segment is from the first segment and
|
||||
if the value is greater than 1.0 the line gets bigger.
|
||||
|
||||
\uicontrol {Texcoord Multipier} defines the texture coordinate multiplier of
|
||||
the line. This value is factored to the texture coordinate values of the
|
||||
line.
|
||||
|
||||
\uicontrol {Texcoord Mode} defines the texture coordinate mode of the line.
|
||||
|
||||
\uicontrol {Line Length} defines the length of the line. If the value is
|
||||
set, the line length is limited to the value. In this case the minimum
|
||||
delta of the line is the length divided by the segment count. If the value
|
||||
is not set, the line length varies based on the particle speed, segment
|
||||
count, and minimum delta.
|
||||
|
||||
\uicontrol {Line Length Variation} defines the length variation of the line.
|
||||
This parameter is not used if \uicontrol {Line Length} has not been set.
|
||||
When the length is set, this parameter can be used to vary the length of
|
||||
each line.
|
||||
|
||||
\uicontrol {Minimum Segment Length} defines he minimum length between
|
||||
segment points. This parameter is ignored if \uicontrol {Line Length} is set.
|
||||
|
||||
\uicontrol {Eol Fade Out Duration} defines the end-of-life fade-out duration
|
||||
of the line. If set, each line remains in the place it was when the particle
|
||||
reached the end of its lifetime, then fades out during this time period.
|
||||
|
||||
\section1 Model Particle
|
||||
|
||||
Specify properties for model particles in \uicontrol Properties >
|
||||
@@ -678,6 +742,7 @@
|
||||
\li \l Emitter
|
||||
\li \l {Trail Emitter}
|
||||
\li \l {Emit Burst}
|
||||
\li \l {Dynamic Burst}
|
||||
\li \l {Model Shape}
|
||||
\li \l {Particle Shape}
|
||||
\endlist
|
||||
@@ -775,6 +840,30 @@
|
||||
instance, set \uicontrol Time to 2000, \uicontrol Amount to 50, and
|
||||
\uicontrol Duration to 200.
|
||||
|
||||
\section1 Dynamic Burst
|
||||
|
||||
Specify properties for emit bursts in \uicontrol Properties >
|
||||
\uicontrol {Dynamic Burst}.
|
||||
|
||||
\image studio-3d-properties-particle-dynamic-burst.png
|
||||
|
||||
\uicontrol {Trigger Mode} defines when the burst is triggered:
|
||||
|
||||
\list
|
||||
\li Select \uicontrol TriggerTime to emit the burst when the burst time
|
||||
is due.
|
||||
\li Select \uicontrol TriggerStart to emit the burst when the followed
|
||||
particle is emitted.
|
||||
\note This mode only works with trail emitters.
|
||||
\note In this mode, \uicontrol Time and \uicontrol Duration don't have an
|
||||
effect.
|
||||
\li Select \uicontrol TriggerEnd to emit the burst when the followed
|
||||
particle life span ends.
|
||||
\endlist
|
||||
|
||||
\uicontrol {Amount Variation} defines the random variation in the number of
|
||||
emitted particles.
|
||||
|
||||
\section1 Particle Shape
|
||||
|
||||
The \uicontrol {Particle Shape} component supports shapes, such as cube,
|
||||
@@ -830,6 +919,9 @@
|
||||
\li \l Gravity accelerates particles to a vector of the specified
|
||||
magnitude in the specified direction.
|
||||
\li \l {Point Rotator} rotates particles around a pivot point.
|
||||
\li \l Repeller repels particles from its location.
|
||||
\li \l {Scale Affector} scales particles based on its lifetime and other
|
||||
parameters.
|
||||
\li \l Wander applies random wave curves to particles.
|
||||
\endlist
|
||||
|
||||
@@ -909,20 +1001,57 @@
|
||||
|
||||
\section1 Point Rotator
|
||||
|
||||
Specify settings for \uicontrol {Point Rotator} component instances in
|
||||
\uicontrol Properties > \uicontrol {Point Rotator}.
|
||||
|
||||
\image studio-3d-properties-particle-point-rotator.png "Particle Point Rotator properties"
|
||||
|
||||
The \uicontrol {Point Rotator} component rotates particles around the
|
||||
pivot point specified in \uicontrol {Pivot point} towards the direction
|
||||
specified in \uicontrol Direction. Direction \uicontrol X, \uicontrol Y, and
|
||||
\uicontrol Z values are automatically normalized to a unit vector.
|
||||
|
||||
Specify settings for \uicontrol {Point Rotator} component instances in
|
||||
\uicontrol Properties > \uicontrol {Point Rotator}.
|
||||
|
||||
\image studio-3d-properties-particle-point-rotator.png "Particle Point Rotator properties"
|
||||
|
||||
\uicontrol Magnitude defines the magnitude in particle position change in
|
||||
degrees per second. A negative value accelerates in the opposite way from
|
||||
the direction specified in \uicontrol Direction.
|
||||
|
||||
\section1 Repeller
|
||||
|
||||
The \uicontrol Repeller component repels particles from its location.
|
||||
|
||||
Specify settings for \uicontrol Repeller component instances in
|
||||
\uicontrol Properties > \uicontrol {Particle Repeller}.
|
||||
|
||||
\image studio-3d-properties-particle-repeller.png
|
||||
|
||||
\uicontrol {Outer Radius} defines the outer radius of the repeller. The
|
||||
particle is not affected until it enters this radius and the repel
|
||||
strength grows smoothly until the particle reaches \uicontrol Radius.
|
||||
|
||||
\uicontrol Radius defines the inner radius of the repeller. Particles
|
||||
located inside \uicontrol Radius are repelled at full strength.
|
||||
|
||||
\uicontrol Strength defines the strength of the repeller.
|
||||
|
||||
\section1 Scale Affector
|
||||
|
||||
\uicontrol {Scale Affector} scales particles based on their lifetime and
|
||||
other parameters.
|
||||
|
||||
\image studio-3d-properties-particle-scale-affector.png
|
||||
|
||||
\uicontrol {Minimum Size} defines the minimum size that the affector can
|
||||
scale particles to.
|
||||
|
||||
\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
|
||||
milliseconds.
|
||||
|
||||
\uicontrol {Easing Curve} defines the
|
||||
\l{Editing Easing Curves}{easing curve} for the scaling animation.
|
||||
|
||||
\section1 Wander
|
||||
|
||||
The \uicontrol Wander component applies random wave curves to particles.
|
||||
|
@@ -92,7 +92,7 @@
|
||||
\image studio-logic-helper-not.png "NOT operator properties"
|
||||
|
||||
We then select the other check box instance and bind the value of its
|
||||
\uicontrol Checked field to the value of of \uicontrol Output
|
||||
\uicontrol Checked field to the value of \uicontrol Output
|
||||
field of the \uicontrol {Not Operator} component.
|
||||
|
||||
\image studio-logic-helper-not-check-box.png "Check box checked property bound to NOT operator output"
|
||||
|
@@ -51,6 +51,12 @@
|
||||
\li Provides an editor for files you created using 3D graphics
|
||||
applications and stored in one of the supported formats.
|
||||
\li \l {3D}
|
||||
\row
|
||||
\li \l {Material Editor and Browser}
|
||||
\li In the \uicontrol {Material Editor} and
|
||||
\uicontrol {Material Browser} views, you create and manage materials and
|
||||
textures.
|
||||
\li \l {Material Editor and Browser}
|
||||
\row
|
||||
\li \l Components
|
||||
\li Contains preset components and your own components, that you can use
|
||||
@@ -121,6 +127,16 @@
|
||||
\li \l{Open Documents}
|
||||
\li Shows currently open files.
|
||||
\li \l{Open Documents}
|
||||
\row
|
||||
\li \l{Content Library}
|
||||
\li The \uicontrol {Content Library} view contains material, texture,
|
||||
and environment bundles with assets that you can use in your project.
|
||||
\li \l{Content Library}
|
||||
\row
|
||||
\li \l{Texture Editor}
|
||||
\li In the \uicontrol {Texture Editor} view, you create and manage
|
||||
textures.
|
||||
\li \l{Texture Editor}
|
||||
\endtable
|
||||
|
||||
\section1 Summary of Main Toolbar Actions
|
||||
|
66
doc/qtdesignstudio/src/views/studio-content-library.qdoc
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\page studio-content-library.html
|
||||
\previouspage creator-open-documents-view.html
|
||||
\nextpage studio-texture-editor.html
|
||||
|
||||
\title Content Library
|
||||
|
||||
\note The \uicontrol {Content Library} view is included in the
|
||||
\l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}.
|
||||
|
||||
The \uicontrol{Content Library} view contains material, texture, and
|
||||
environment bundles with assets that you can use in your project. When you
|
||||
have added an asset from \uicontrol {Content Library}, you can use it in
|
||||
your project.
|
||||
|
||||
\image content-library.webp
|
||||
|
||||
\section1 Adding a Material to Your Project
|
||||
|
||||
You can use materials on 3D models.
|
||||
|
||||
To add a material from the \uicontrol {Content Library} view to your
|
||||
project, do one of the following:
|
||||
|
||||
\list
|
||||
\li Select the \inlineimage icons/plus.png
|
||||
next to the material in the \uicontrol {Content Library} view.
|
||||
\li Right-click the material in the \uicontrol {Content Library} view and
|
||||
select \uicontrol{Add an instance to the project}.
|
||||
\endlist
|
||||
|
||||
You can also add a material to a 3D model straight from the \uicontrol {Content Library} view.
|
||||
This also adds the material to the project.
|
||||
|
||||
To add a material to a 3D model, do one of the following:
|
||||
|
||||
\list
|
||||
\li Drag the material from the \uicontrol {Content Library} view to a 3D
|
||||
model in the \uicontrol 3D or \uicontrol Navigator view.
|
||||
\li With a 3D model selected, right-click the material in the
|
||||
\uicontrol {Content Library} view and select \uicontrol{Apply to selected}.
|
||||
\endlist
|
||||
|
||||
\section1 Adding a Texture or Environment to Your Project
|
||||
|
||||
To add a texture or environment to your project, right-click the image
|
||||
in the \uicontrol {Content Library} view and select one of the options:
|
||||
\list
|
||||
\li \uicontrol {Add image}. This adds the image as an asset to your project. You can
|
||||
access the image from the \uicontrol Assets view.
|
||||
\li \uicontrol {Add texture}. This adds the image as a texture to your project. You can
|
||||
access the texture from the \uicontrol Textures section in the \uicontrol{Material Browser}
|
||||
view.
|
||||
\li \uicontrol {Add light probe}. This adds the image as a light probe for your scene, using
|
||||
the image to illuminate the scene and as a skybox. When you set an image as light probe, the
|
||||
following properties are set for \uicontrol {Scene Environment}:
|
||||
\list
|
||||
\li \uicontrol Background Mode is set to skybox.
|
||||
\li \uicontrol {Light Probe} > \uicontrol Image is set to the selected texture.
|
||||
\endlist
|
||||
\endlist
|
||||
|
||||
*/
|
@@ -10,11 +10,13 @@
|
||||
\title Material Editor and Browser
|
||||
|
||||
In the \uicontrol {Material Editor} and \uicontrol {Material Browser} views,
|
||||
you create and manage materials.
|
||||
you create and manage materials and textures.
|
||||
|
||||
\image material-editor-browser.webp "Material Editor and Browser"
|
||||
|
||||
\section1 Creating a Material
|
||||
\section1 Working with Materials
|
||||
|
||||
\section2 Creating a Material
|
||||
|
||||
To create a new material, do one of the following:
|
||||
|
||||
@@ -25,7 +27,7 @@
|
||||
.
|
||||
\endlist
|
||||
|
||||
\section1 Editing a Material
|
||||
\section2 Editing a Material
|
||||
|
||||
To edit a material, select it in \uicontrol{Material Browser} and edit its
|
||||
properties in \uicontrol{Material Editor}. If \uicontrol {Material Editor}
|
||||
@@ -37,7 +39,7 @@
|
||||
\li In \uicontrol{Material Browser}, double-click a material.
|
||||
\endlist
|
||||
|
||||
\section1 Assigning a Material to an Object
|
||||
\section2 Assigning a Material to an Object
|
||||
|
||||
To assign a material to a 3D object in your project, drag the material from
|
||||
\uicontrol {Material Browser} to the object in the \uicontrol Navigator or
|
||||
@@ -57,7 +59,7 @@
|
||||
. This replaces any material already assigned to the object.
|
||||
\endlist
|
||||
|
||||
\section1 Removing a Material from an Object
|
||||
\section2 Removing a Material from an Object
|
||||
|
||||
To remove an assigned material from an object:
|
||||
\list 1
|
||||
@@ -68,7 +70,7 @@
|
||||
\image materials-remove-material.png
|
||||
\endlist
|
||||
|
||||
\section1 Copying and Pasting Material Properties
|
||||
\section2 Copying and Pasting Material Properties
|
||||
|
||||
You can copy properties from one material to another. You can choose if you
|
||||
want to copy all properties or certain property groups.
|
||||
@@ -88,7 +90,7 @@
|
||||
\note You can't copy material properties between materials of different
|
||||
material types.
|
||||
|
||||
\section1 Using Texture Maps
|
||||
\section2 Using Texture Maps
|
||||
|
||||
In \QDS you can add many different texture maps to your material.
|
||||
|
||||
@@ -100,7 +102,7 @@
|
||||
the image to \uicontrol{Diffuse Map} in \uicontrol{Material Editor}.
|
||||
\endlist
|
||||
|
||||
\section2 Using a Reflection Map for Environmental Mapping
|
||||
\section3 Using a Reflection Map for Environmental Mapping
|
||||
|
||||
To use a texture for environmental mapping, you need to set the mapping
|
||||
mode to \e {environment}.
|
||||
@@ -122,7 +124,7 @@
|
||||
\uicontrol {Environment}.
|
||||
\endlist
|
||||
|
||||
\section1 Blending Colors
|
||||
\section2 Blending Colors
|
||||
|
||||
To determine how the colors of a model blend with the colors of the models
|
||||
behind it, set the \uicontrol {Blend mode} property. To make opaque objects
|
||||
@@ -144,7 +146,7 @@
|
||||
For a result with higher contrast, select \uicontrol Overlay, which is a mix
|
||||
of the multiply and screen modes.
|
||||
|
||||
\section1 Lighting Materials
|
||||
\section2 Lighting Materials
|
||||
|
||||
To set the lighting method for generating a material, use the
|
||||
\uicontrol Lighting property. Select \uicontrol {Fragment lighting} to
|
||||
@@ -164,7 +166,7 @@
|
||||
the opacity of the material independently of the model as the value of the
|
||||
\uicontrol Opacity property.
|
||||
|
||||
\section1 Self-Illuminating Materials
|
||||
\section2 Self-Illuminating Materials
|
||||
|
||||
To set the color and amount of self-illumination for a material, use the
|
||||
\uicontrol {Emissive color} and \uicontrol {Emissive factor} properties. In
|
||||
@@ -177,7 +179,7 @@
|
||||
image does not affect the color of the result, while using a color image
|
||||
produces glowing regions with the color affected by the emissive map.
|
||||
|
||||
\section1 Using Highlights and Reflections
|
||||
\section2 Using Highlights and Reflections
|
||||
|
||||
You can control the highlights and reflections on a material by setting the
|
||||
properties in the \uicontrol Specular group. You can use the color picker
|
||||
@@ -221,7 +223,7 @@
|
||||
highlights and blurring reflections. To control the specular roughness of
|
||||
the material using a Texture, set the \uicontrol {Roughness map property}.
|
||||
|
||||
\section1 Simulating Geometry Displacement
|
||||
\section2 Simulating Geometry Displacement
|
||||
|
||||
Specify the properties in the \uicontrol {Bump/Normal} group to simulate
|
||||
fine geometry displacement across the surface of the material. Set the
|
||||
@@ -240,7 +242,7 @@
|
||||
of the material. The \uicontrol {Displacement amount} property specifies the
|
||||
offset amount.
|
||||
|
||||
\section1 Specifying Material Translucency
|
||||
\section2 Specifying Material Translucency
|
||||
|
||||
Set the properties in the \uicontrol Translucency group to control how much
|
||||
light can pass through the material from behind. To use a grayscale texture,
|
||||
@@ -254,7 +256,7 @@
|
||||
the angle of the normals of the object to the light source, set
|
||||
the \uicontrol {Translucency falloff} property.
|
||||
|
||||
\section1 Culling Faces
|
||||
\section2 Culling Faces
|
||||
|
||||
Set the \uicontrol {Culling mode} property to determine whether the front
|
||||
and back faces of a model are rendered. Culling modes check whether the
|
||||
@@ -265,4 +267,32 @@
|
||||
is not rendered. Culling makes rendering objects quicker and more efficient
|
||||
by reducing the number of polygons to draw.
|
||||
|
||||
\section1 Working with Textures
|
||||
|
||||
\target creating texture material browser
|
||||
|
||||
\section2 Creating a Texture
|
||||
|
||||
To create a new texture, do one of the following in
|
||||
\uicontrol {Material Browser}:
|
||||
\list
|
||||
\li Select \inlineimage icons/plus.png
|
||||
in the \uicontrol Textures section.
|
||||
\li Right-click anywhere in the \uicontrol Textures section and select
|
||||
\uicontrol {Create new texture}.
|
||||
\endlist
|
||||
|
||||
\note You can also create textures from the \l {Assets} or
|
||||
\l {Texture Editor} views.
|
||||
|
||||
\section2 Applying a Texture to a Material
|
||||
|
||||
To apply a texture to a material, select the material in
|
||||
\uicontrol {Material Browser} and do one of the following:
|
||||
\list
|
||||
\li Right-click the texture and select
|
||||
\uicontrol {Apply to selected material}.
|
||||
\li Drag the texture to a supported property in the
|
||||
\uicontrol {Material Editor}
|
||||
\endlist
|
||||
*/
|
||||
|
47
doc/qtdesignstudio/src/views/studio-texture-editor.qdoc
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\page studio-texture-editor.html
|
||||
\previouspage studio-content-library.html
|
||||
\nextpage creator-project-managing-workspaces.html
|
||||
|
||||
\title Texture Editor
|
||||
|
||||
In the \uicontrol {Texture Editor} view, you create and manage textures.
|
||||
|
||||
\image texture-editor.png
|
||||
|
||||
\section1 Creating a Texture
|
||||
|
||||
To create a texture, select \inlineimage icons/plus.png
|
||||
in the \uicontrol {Texture Editor} view.
|
||||
|
||||
\note You can also create textures from the
|
||||
\l{creating texture material browser}{Material Browser view}.
|
||||
|
||||
When you create a texture, it is empty. To add an image to the texture,
|
||||
do one of the following:
|
||||
|
||||
\list
|
||||
\li In the \uicontrol{Texture Editor} view, set the image in the
|
||||
\uicontrol Source property.
|
||||
\li From the \uicontrol Assets view, drag an image to the
|
||||
\uicontrol Source property in the \uicontrol {Texture Editor} view.
|
||||
\endlist
|
||||
|
||||
\section1 Applying a Texture to a Material
|
||||
|
||||
To apply a texture to a material, first select the material in the
|
||||
\uicontrol {Material Browser} view and then:
|
||||
\list 1
|
||||
\li Select \inlineimage icons/apply-material.png
|
||||
.
|
||||
\li Select the material and property that you want to add the texture to.
|
||||
\image select-material-property.png
|
||||
\endlist
|
||||
|
||||
\note You can also apply textures to materials in the
|
||||
\l {Material Editor and Browser}{Material Browser view}.
|
||||
|
||||
*/
|
@@ -3,7 +3,11 @@
|
||||
|
||||
/*!
|
||||
\page creator-project-managing-workspaces.html
|
||||
\if defined(qtdesignstudio)
|
||||
\previouspage studio-texture-editor.html
|
||||
\else
|
||||
\previouspage creator-open-documents-view.html
|
||||
\endif
|
||||
\nextpage creator-project-managing-sessions.html
|
||||
|
||||
\title Managing Workspaces
|
||||
|
@@ -14,3 +14,4 @@ IDE_DOC_FILES_ONLINE = $$PWD/doc/qtcreator/qtcreator-online.qdocconf \
|
||||
$$PWD/doc/qtcreatordev/qtcreator-dev-online.qdocconf
|
||||
IDE_DOC_FILES = $$PWD/doc/qtcreator/qtcreator.qdocconf \
|
||||
$$PWD/doc/qtcreatordev/qtcreator-dev.qdocconf
|
||||
|
||||
|
@@ -126,11 +126,15 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then
|
||||
if [ -f "$qml2puppetapp" ]; then
|
||||
qml2puppetArgument="-executable=$qml2puppetapp"
|
||||
fi
|
||||
sdktoolapp="$libexec_path/sdktool"
|
||||
if [ -f "$sdktoolapp" ]; then
|
||||
sdktoolArgument="-executable=$sdktoolapp"
|
||||
fi
|
||||
|
||||
"$bin_src/macdeployqt" "$app_path" \
|
||||
"-executable=$app_path/Contents/MacOS/qtdiag" \
|
||||
"-executable=$libexec_path/qtpromaker" \
|
||||
"-executable=$libexec_path/sdktool" \
|
||||
"$sdktoolArgument" \
|
||||
"-executable=$libexec_path/ios/iostool" \
|
||||
"-executable=$libexec_path/buildoutputparser" \
|
||||
"-executable=$libexec_path/cpaster" \
|
||||
|
@@ -35,45 +35,32 @@ Item {
|
||||
id: searchBox
|
||||
|
||||
width: root.width
|
||||
enabled: !materialsModel.hasMaterialRoot && materialsModel.hasQuick3DImport
|
||||
enabled: {
|
||||
if (tabBar.currIndex == 0) { // Materials tab
|
||||
materialsModel.matBundleExists
|
||||
&& rootView.hasMaterialLibrary
|
||||
&& materialsModel.hasRequiredQuick3DImport
|
||||
} else { // Textures / Environments tabs
|
||||
texturesModel.texBundleExists
|
||||
}
|
||||
}
|
||||
|
||||
onSearchChanged: (searchText) => {
|
||||
rootView.handleSearchFilterChanged(searchText)
|
||||
|
||||
// make sure categories with matches are expanded
|
||||
materialsView.expandVisibleSections()
|
||||
texturesView.expandVisibleSections()
|
||||
environmentsView.expandVisibleSections()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
// TODO: only disable the materials section, textures should be available
|
||||
text: {
|
||||
if (materialsModel.hasMaterialRoot)
|
||||
qsTr("<b>Content Library</b> is disabled inside a material component.")
|
||||
else if (!materialsModel.hasQuick3DImport)
|
||||
qsTr("To use <b>Content Library</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||
else
|
||||
""
|
||||
}
|
||||
|
||||
textFormat: Text.RichText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||
topPadding: 30
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
width: root.width
|
||||
visible: text !== ""
|
||||
}
|
||||
|
||||
UnimportBundleMaterialDialog {
|
||||
id: confirmUnimportDialog
|
||||
}
|
||||
|
||||
ContentLibraryTabBar {
|
||||
id: tabBar
|
||||
|
||||
visible: materialsModel.hasQuick3DImport
|
||||
// TODO: update icons
|
||||
tabsModel: [{name: qsTr("Materials"), icon: StudioTheme.Constants.gradient},
|
||||
{name: qsTr("Textures"), icon: StudioTheme.Constants.materialPreviewEnvironment},
|
||||
@@ -84,7 +71,6 @@ Item {
|
||||
width: root.width
|
||||
height: root.height - y
|
||||
currentIndex: tabBar.currIndex
|
||||
visible: materialsModel.hasQuick3DImport
|
||||
|
||||
ContentLibraryMaterialsView {
|
||||
id: materialsView
|
||||
@@ -94,8 +80,8 @@ Item {
|
||||
searchBox: searchBox
|
||||
|
||||
onUnimport: (bundleMat) => {
|
||||
unimportBundleMaterialDialog.targetBundleMaterial = bundleMat
|
||||
unimportBundleMaterialDialog.open()
|
||||
confirmUnimportDialog.targetBundleMaterial = bundleMat
|
||||
confirmUnimportDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ Item {
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
@@ -81,6 +82,7 @@ Item {
|
||||
anchors.right: img.right
|
||||
anchors.bottom: img.bottom
|
||||
enabled: !materialsModel.importerRunning
|
||||
visible: containsMouse || mouseArea.containsMouse
|
||||
|
||||
onClicked: {
|
||||
materialsModel.addToProject(modelData)
|
||||
|
@@ -10,7 +10,13 @@ StudioControls.Menu {
|
||||
id: root
|
||||
|
||||
property var targetMaterial: null
|
||||
property bool hasModelSelection: false
|
||||
property bool importerRunning: false
|
||||
|
||||
readonly property bool targetAvailable: targetMaterial && !importerRunning
|
||||
|
||||
signal unimport(var bundleMat);
|
||||
signal addToProject(var bundleMat)
|
||||
|
||||
function popupMenu(targetMaterial = null)
|
||||
{
|
||||
@@ -22,31 +28,31 @@ StudioControls.Menu {
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Apply to selected (replace)")
|
||||
enabled: root.targetMaterial && materialsModel.hasModelSelection
|
||||
onTriggered: materialsModel.applyToSelected(root.targetMaterial, false)
|
||||
enabled: root.targetAvailable && root.hasModelSelection
|
||||
onTriggered: root.applyToSelected(root.targetMaterial, false)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Apply to selected (add)")
|
||||
enabled: root.targetMaterial && materialsModel.hasModelSelection
|
||||
onTriggered: materialsModel.applyToSelected(root.targetMaterial, true)
|
||||
enabled: root.targetAvailable && root.hasModelSelection
|
||||
onTriggered: root.applyToSelected(root.targetMaterial, true)
|
||||
}
|
||||
|
||||
StudioControls.MenuSeparator {}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
enabled: !materialsModel.importerRunning
|
||||
enabled: root.targetAvailable
|
||||
text: qsTr("Add an instance to project")
|
||||
|
||||
onTriggered: {
|
||||
materialsModel.addToProject(root.targetMaterial)
|
||||
root.addToProject(root.targetMaterial)
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
enabled: !materialsModel.importerRunning && root.targetMaterial && root.targetMaterial.bundleMaterialImported
|
||||
enabled: root.targetAvailable && root.targetMaterial.bundleMaterialImported
|
||||
text: qsTr("Remove from project")
|
||||
|
||||
onTriggered: root.unimport(root.targetMaterial);
|
||||
onTriggered: root.unimport(root.targetMaterial)
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ HelperWidgets.ScrollView {
|
||||
id: root
|
||||
|
||||
clip: true
|
||||
interactive: !ctxMenu.opened
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 120
|
||||
@@ -31,7 +32,7 @@ HelperWidgets.ScrollView {
|
||||
for (let i = 0; i < categoryRepeater.count; ++i) {
|
||||
let cat = categoryRepeater.itemAt(i)
|
||||
if (cat.visible && !cat.expanded)
|
||||
cat.expanded = true
|
||||
cat.expandSection()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +40,11 @@ HelperWidgets.ScrollView {
|
||||
ContentLibraryMaterialContextMenu {
|
||||
id: ctxMenu
|
||||
|
||||
hasModelSelection: materialsModel.hasModelSelection
|
||||
importerRunning: materialsModel.importerRunning
|
||||
|
||||
onUnimport: (bundleMat) => root.unimport(bundleMat)
|
||||
onAddToProject: (bundleMat) => materialsModel.addToProject(bundleMat)
|
||||
}
|
||||
|
||||
Repeater {
|
||||
@@ -52,10 +57,16 @@ HelperWidgets.ScrollView {
|
||||
caption: bundleCategoryName
|
||||
addTopPadding: false
|
||||
sectionBackgroundColor: "transparent"
|
||||
visible: bundleCategoryVisible
|
||||
visible: bundleCategoryVisible && !materialsModel.isEmpty
|
||||
expanded: bundleCategoryExpanded
|
||||
expandOnClick: false
|
||||
onToggleExpand: bundleCategoryExpanded = !bundleCategoryExpanded
|
||||
onExpand: bundleCategoryExpanded = true
|
||||
onCollapse: bundleCategoryExpanded = false
|
||||
|
||||
function expandSection() {
|
||||
bundleCategoryExpanded = true
|
||||
}
|
||||
|
||||
Grid {
|
||||
width: root.width
|
||||
@@ -79,13 +90,26 @@ HelperWidgets.ScrollView {
|
||||
}
|
||||
|
||||
Text {
|
||||
id: noMatchText
|
||||
text: qsTr("No match found.");
|
||||
id: infoText
|
||||
text: {
|
||||
if (!materialsModel.matBundleExists)
|
||||
qsTr("<b>Content Library</b> materials are not installed.")
|
||||
else if (!rootView.hasQuick3DImport)
|
||||
qsTr("To use <b>Content Library</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||
else if (!materialsModel.hasRequiredQuick3DImport)
|
||||
qsTr("To use <b>Content Library</b>, version 6.3 or later of the QtQuick3D module is required.")
|
||||
else if (!rootView.hasMaterialLibrary)
|
||||
qsTr("<b>Content Library</b> is disabled inside a non-visual component.")
|
||||
else if (!searchBox.isEmpty())
|
||||
qsTr("No match found.")
|
||||
else
|
||||
""
|
||||
}
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
topPadding: 10
|
||||
leftPadding: 10
|
||||
visible: materialsModel.isEmpty && !searchBox.isEmpty() && !materialsModel.hasMaterialRoot
|
||||
visible: materialsModel.isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,9 +12,12 @@ StudioControls.Menu {
|
||||
property var targetTexture: null
|
||||
property bool hasSceneEnv: false
|
||||
|
||||
property bool canUse3D: targetTexture && rootView.hasQuick3DImport && rootView.hasMaterialLibrary
|
||||
|
||||
function popupMenu(targetTexture = null)
|
||||
{
|
||||
this.targetTexture = targetTexture
|
||||
rootView.updateSceneEnvState();
|
||||
popup()
|
||||
}
|
||||
|
||||
@@ -28,13 +31,13 @@ StudioControls.Menu {
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Add texture")
|
||||
enabled: root.targetTexture
|
||||
enabled: canUse3D
|
||||
onTriggered: rootView.addTexture(root.targetTexture)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Add light probe")
|
||||
enabled: root.hasSceneEnv && root.targetTexture
|
||||
enabled: root.hasSceneEnv && canUse3D
|
||||
onTriggered: rootView.addLightProbe(root.targetTexture)
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ HelperWidgets.ScrollView {
|
||||
id: root
|
||||
|
||||
clip: true
|
||||
interactive: !ctxMenu.opened
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 100
|
||||
@@ -32,7 +33,7 @@ HelperWidgets.ScrollView {
|
||||
for (let i = 0; i < categoryRepeater.count; ++i) {
|
||||
let cat = categoryRepeater.itemAt(i)
|
||||
if (cat.visible && !cat.expanded)
|
||||
cat.expanded = true
|
||||
cat.expandSection()
|
||||
}
|
||||
}
|
||||
Column {
|
||||
@@ -52,10 +53,16 @@ HelperWidgets.ScrollView {
|
||||
caption: bundleCategoryName
|
||||
addTopPadding: false
|
||||
sectionBackgroundColor: "transparent"
|
||||
visible: bundleCategoryVisible
|
||||
visible: bundleCategoryVisible && !root.model.isEmpty
|
||||
expanded: bundleCategoryExpanded
|
||||
expandOnClick: false
|
||||
onToggleExpand: bundleCategoryExpanded = !bundleCategoryExpanded
|
||||
onExpand: bundleCategoryExpanded = true
|
||||
onCollapse: bundleCategoryExpanded = false
|
||||
|
||||
function expandSection() {
|
||||
bundleCategoryExpanded = true
|
||||
}
|
||||
|
||||
Grid {
|
||||
width: root.width
|
||||
@@ -80,13 +87,20 @@ HelperWidgets.ScrollView {
|
||||
}
|
||||
|
||||
Text {
|
||||
id: noMatchText
|
||||
text: qsTr("No match found.");
|
||||
id: infoText
|
||||
text: {
|
||||
if (!root.model.texBundleExists)
|
||||
qsTr("<b>Content Library</b> textures are not installed.")
|
||||
else if (!searchBox.isEmpty())
|
||||
qsTr("No match found.")
|
||||
else
|
||||
""
|
||||
}
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
topPadding: 10
|
||||
leftPadding: 10
|
||||
visible: root.model.isEmpty && !searchBox.isEmpty() && !root.model.hasMaterialRoot
|
||||
visible: root.model.isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,327 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
TreeViewDelegate {
|
||||
id: root
|
||||
|
||||
required property Item assetsView
|
||||
required property Item assetsRoot
|
||||
|
||||
property bool hasChildWithDropHover: false
|
||||
property bool isHoveringDrop: false
|
||||
readonly property string suffix: model.fileName.substr(-4)
|
||||
readonly property bool isFont: root.suffix === ".ttf" || root.suffix === ".otf"
|
||||
readonly property bool isEffect: root.suffix === ".qep"
|
||||
property bool currFileSelected: false
|
||||
property int initialDepth: -1
|
||||
property bool __isDirectory: assetsModel.isDirectory(model.filePath)
|
||||
property int __currentRow: model.index
|
||||
property string __itemPath: model.filePath
|
||||
|
||||
readonly property int __fileItemHeight: thumbnailImage.height
|
||||
readonly property int __dirItemHeight: 21
|
||||
|
||||
implicitHeight: root.__isDirectory ? root.__dirItemHeight : root.__fileItemHeight
|
||||
implicitWidth: root.assetsView.width > 0 ? root.assetsView.width : 10
|
||||
|
||||
leftMargin: root.__isDirectory ? 0 : thumbnailImage.width
|
||||
|
||||
Component.onCompleted: {
|
||||
// the depth of the root path will become available before we get to the actual
|
||||
// items we display, so it's safe to set assetsView.rootPathDepth here. All other
|
||||
// tree items (below the root) will have the indentation (basically, depth) adjusted.
|
||||
if (model.filePath === assetsModel.rootPath()) {
|
||||
root.assetsView.rootPathDepth = root.depth
|
||||
root.assetsView.rootPathRow = root.__currentRow
|
||||
} else if (model.filePath.includes(assetsModel.rootPath())) {
|
||||
root.depth -= root.assetsView.rootPathDepth
|
||||
root.initialDepth = root.depth
|
||||
}
|
||||
}
|
||||
|
||||
// workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721
|
||||
onYChanged: {
|
||||
if (root.__currentRow === root.assetsView.firstRow) {
|
||||
if (root.y > root.assetsView.contentY) {
|
||||
let item = root.assetsView.itemAtCell(0, root.assetsView.rootPathRow)
|
||||
if (!item)
|
||||
root.assetsView.contentY = root.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onImplicitWidthChanged: {
|
||||
// a small hack, to fix a glitch: when resizing the width of the tree view,
|
||||
// the widths of the delegate items remain the same as before, unless we re-set
|
||||
// that width explicitly.
|
||||
var newWidth = root.implicitWidth - (root.assetsView.verticalScrollBar.scrollBarVisible
|
||||
? root.assetsView.verticalScrollBar.width
|
||||
: 0)
|
||||
bg.width = newWidth
|
||||
bg.implicitWidth = newWidth
|
||||
}
|
||||
|
||||
onDepthChanged: {
|
||||
if (root.depth > root.initialDepth && root.initialDepth >= 0)
|
||||
root.depth = root.initialDepth
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: bg
|
||||
|
||||
color: {
|
||||
if (root.__isDirectory && (root.isHoveringDrop || root.hasChildWithDropHover))
|
||||
return StudioTheme.Values.themeInteraction
|
||||
|
||||
if (!root.__isDirectory && root.assetsView.selectedAssets[root.__itemPath])
|
||||
return StudioTheme.Values.themeInteraction
|
||||
|
||||
if (mouseArea.containsMouse)
|
||||
return StudioTheme.Values.themeSectionHeadBackground
|
||||
|
||||
return root.__isDirectory
|
||||
? StudioTheme.Values.themeSectionHeadBackground
|
||||
: "transparent"
|
||||
}
|
||||
|
||||
// this rectangle exists so as to have some visual indentation for the directories
|
||||
// We prepend a default pane-colored rectangle so that the nested directory will
|
||||
// look moved a bit to the right
|
||||
Rectangle {
|
||||
anchors.top: bg.top
|
||||
anchors.bottom: bg.bottom
|
||||
anchors.left: bg.left
|
||||
|
||||
width: root.indentation * root.depth
|
||||
implicitWidth: root.indentation * root.depth
|
||||
color: StudioTheme.Values.themePanelBackground
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
id: assetLabel
|
||||
text: assetLabel.__computeText()
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: 14
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
function __computeText()
|
||||
{
|
||||
return root.__isDirectory
|
||||
? (root.hasChildren
|
||||
? model.display.toUpperCase()
|
||||
: model.display.toUpperCase() + qsTr(" (empty)"))
|
||||
: model.display
|
||||
}
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: treeDropArea
|
||||
|
||||
enabled: true
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: (drag) => {
|
||||
root.assetsRoot.updateDropExtFiles(drag)
|
||||
root.isHoveringDrop = drag.accepted && root.assetsRoot.dropSimpleExtFiles.length > 0
|
||||
if (root.isHoveringDrop)
|
||||
root.assetsView.startDropHoverOver(root.__currentRow)
|
||||
}
|
||||
|
||||
onDropped: (drag) => {
|
||||
root.isHoveringDrop = false
|
||||
root.assetsView.endDropHover(root.__currentRow)
|
||||
|
||||
let dirPath = root.__isDirectory
|
||||
? model.filePath
|
||||
: assetsModel.parentDirPath(model.filePath);
|
||||
|
||||
rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles,
|
||||
root.assetsRoot.dropComplexExtFiles,
|
||||
dirPath)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (root.isHoveringDrop) {
|
||||
root.isHoveringDrop = false
|
||||
root.assetsView.endDropHover(root.__currentRow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
property bool allowTooltip: true
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onExited: tooltipBackend.hideTooltip()
|
||||
onEntered: mouseArea.allowTooltip = true
|
||||
|
||||
onCanceled: {
|
||||
tooltipBackend.hideTooltip()
|
||||
mouseArea.allowTooltip = true
|
||||
}
|
||||
|
||||
onPositionChanged: tooltipBackend.reposition()
|
||||
|
||||
onPressed: (mouse) => {
|
||||
forceActiveFocus()
|
||||
mouseArea.allowTooltip = false
|
||||
tooltipBackend.hideTooltip()
|
||||
|
||||
if (root.__isDirectory)
|
||||
return
|
||||
|
||||
var ctrlDown = mouse.modifiers & Qt.ControlModifier
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (!root.assetsView.isAssetSelected(root.__itemPath) && !ctrlDown)
|
||||
root.assetsView.clearSelectedAssets()
|
||||
root.currFileSelected = ctrlDown ? !root.assetsView.isAssetSelected(root.__itemPath) : true
|
||||
root.assetsView.setAssetSelected(root.__itemPath, root.currFileSelected)
|
||||
|
||||
if (root.currFileSelected) {
|
||||
let selectedPaths = root.assetsView.selectedPathsAsList()
|
||||
rootView.startDragAsset(selectedPaths, mapToGlobal(mouse.x, mouse.y))
|
||||
}
|
||||
} else {
|
||||
if (!root.assetsView.isAssetSelected(root.__itemPath) && !ctrlDown)
|
||||
root.assetsView.clearSelectedAssets()
|
||||
root.currFileSelected = root.assetsView.isAssetSelected(root.__itemPath) || !ctrlDown
|
||||
root.assetsView.setAssetSelected(root.__itemPath, root.currFileSelected)
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: (mouse) => {
|
||||
mouseArea.allowTooltip = true
|
||||
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (!(mouse.modifiers & Qt.ControlModifier))
|
||||
root.assetsView.selectedAssets = {}
|
||||
root.assetsView.selectedAssets[root.__itemPath] = root.currFileSelected
|
||||
root.assetsView.selectedAssetsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: (mouse) => {
|
||||
forceActiveFocus()
|
||||
allowTooltip = false
|
||||
tooltipBackend.hideTooltip()
|
||||
if (mouse.button === Qt.LeftButton && isEffect)
|
||||
rootView.openEffectMaker(filePath)
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
visible: !root.isFont && mouseArea.containsMouse && !root.assetsView.contextMenu.visible
|
||||
text: model.filePath
|
||||
delay: 1000
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: mouseArea.containsMouse && mouseArea.allowTooltip
|
||||
onTriggered: {
|
||||
if (suffix === ".ttf" || suffix === ".otf") {
|
||||
tooltipBackend.name = model.fileName
|
||||
tooltipBackend.path = model.filePath
|
||||
tooltipBackend.showTooltip()
|
||||
}
|
||||
}
|
||||
} // Timer
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (mouse.button === Qt.LeftButton)
|
||||
root.__toggleExpandCurrentRow()
|
||||
else
|
||||
root.__openContextMenuForCurrentRow()
|
||||
|
||||
|
||||
}
|
||||
} // MouseArea
|
||||
|
||||
function __openContextMenuForCurrentRow()
|
||||
{
|
||||
let modelIndex = assetsModel.indexForPath(model.filePath)
|
||||
|
||||
function onFolderCreated(path) {
|
||||
root.assetsView.addCreatedFolder(path)
|
||||
}
|
||||
|
||||
if (root.__isDirectory) {
|
||||
var row = root.assetsView.rowAtIndex(modelIndex)
|
||||
var expanded = root.assetsView.isExpanded(row)
|
||||
|
||||
var allExpandedState = root.assetsView.computeAllExpandedState()
|
||||
|
||||
function onFolderRenamed() {
|
||||
if (expanded)
|
||||
root.assetsView.rowToExpand = row
|
||||
}
|
||||
|
||||
root.assetsView.contextMenu.openContextMenuForDir(modelIndex, model.filePath,
|
||||
model.fileName, allExpandedState, onFolderCreated, onFolderRenamed)
|
||||
} else {
|
||||
let parentDirIndex = assetsModel.parentDirIndex(model.filePath)
|
||||
let selectedPaths = root.assetsView.selectedPathsAsList()
|
||||
root.assetsView.contextMenu.openContextMenuForFile(modelIndex, parentDirIndex,
|
||||
selectedPaths, onFolderCreated)
|
||||
}
|
||||
}
|
||||
|
||||
function __toggleExpandCurrentRow()
|
||||
{
|
||||
if (!root.__isDirectory)
|
||||
return
|
||||
|
||||
let index = root.assetsView.__modelIndex(root.__currentRow)
|
||||
// if the user manually clicked on a directory, then this is definitely not a
|
||||
// an automatic request to expand all.
|
||||
root.assetsView.requestedExpandAll = false
|
||||
|
||||
if (root.assetsView.isExpanded(root.__currentRow)) {
|
||||
root.assetsView.requestedExpandAll = false
|
||||
root.assetsView.collapse(root.__currentRow)
|
||||
} else {
|
||||
root.assetsView.expand(root.__currentRow)
|
||||
}
|
||||
}
|
||||
|
||||
function reloadImage()
|
||||
{
|
||||
if (root.__isDirectory)
|
||||
return
|
||||
|
||||
thumbnailImage.source = ""
|
||||
thumbnailImage.source = thumbnailImage.__computeSource()
|
||||
}
|
||||
|
||||
Image {
|
||||
id: thumbnailImage
|
||||
visible: !root.__isDirectory
|
||||
x: root.depth * root.indentation
|
||||
width: 48
|
||||
height: 48
|
||||
cache: false
|
||||
sourceSize.width: 48
|
||||
sourceSize.height: 48
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: thumbnailImage.__computeSource()
|
||||
|
||||
function __computeSource()
|
||||
{
|
||||
return root.__isDirectory
|
||||
? ""
|
||||
: "image://qmldesigner_assets/" + model.filePath
|
||||
}
|
||||
|
||||
} // Image
|
||||
} // TreeViewDelegate
|
@@ -1,31 +1,26 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import QtQuick
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var selectedAssets: ({})
|
||||
property int allExpandedState: 0
|
||||
property string contextFilePath: ""
|
||||
property var contextDir: undefined
|
||||
property bool isDirContextMenu: false
|
||||
|
||||
// Array of supported externally dropped files that are imported as-is
|
||||
property var dropSimpleExtFiles: []
|
||||
|
||||
// Array of supported externally dropped files that trigger custom import process
|
||||
property var dropComplexExtFiles: []
|
||||
|
||||
readonly property int qtVersionAtLeast6_4: rootView.qtVersionIsAtLeast6_4()
|
||||
property bool __searchBoxEmpty: true
|
||||
|
||||
AssetsContextMenu {
|
||||
id: contextMenu
|
||||
assetsView: assetsView
|
||||
}
|
||||
|
||||
function clearSearchFilter()
|
||||
@@ -63,7 +58,7 @@ Item {
|
||||
|
||||
onDropped: {
|
||||
rootView.handleExtFilesDrop(root.dropSimpleExtFiles, root.dropComplexExtFiles,
|
||||
assetsModel.rootDir().dirPath)
|
||||
assetsModel.rootPath())
|
||||
}
|
||||
|
||||
Canvas { // marker for the drop area
|
||||
@@ -90,11 +85,15 @@ Item {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: {
|
||||
if (!assetsModel.isEmpty) {
|
||||
root.contextFilePath = ""
|
||||
root.contextDir = assetsModel.rootDir()
|
||||
root.isDirContextMenu = false
|
||||
contextMenu.popup()
|
||||
if (assetsModel.haveFiles) {
|
||||
function onFolderCreated(path) {
|
||||
assetsView.addCreatedFolder(path)
|
||||
}
|
||||
|
||||
var rootIndex = assetsModel.rootIndex()
|
||||
var dirPath = assetsModel.filePath(rootIndex)
|
||||
var dirName = assetsModel.fileName(rootIndex)
|
||||
contextMenu.openContextMenuForRoot(rootIndex, dirPath, dirName, onFolderCreated)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,13 +102,8 @@ Item {
|
||||
function handleViewFocusOut()
|
||||
{
|
||||
contextMenu.close()
|
||||
root.selectedAssets = {}
|
||||
root.selectedAssetsChanged()
|
||||
}
|
||||
|
||||
RegExpValidator {
|
||||
id: folderNameValidator
|
||||
regExp: /^(\w[^*/><?\\|:]*)$/
|
||||
assetsView.selectedAssets = {}
|
||||
assetsView.selectedAssetsChanged()
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -127,10 +121,29 @@ Item {
|
||||
|
||||
width: parent.width - addAssetButton.width - 5
|
||||
|
||||
onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText)
|
||||
onSearchChanged: (searchText) => {
|
||||
updateSearchFilterTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
Timer {
|
||||
id: updateSearchFilterTimer
|
||||
interval: 200
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
assetsView.resetVerticalScrollPosition()
|
||||
rootView.handleSearchFilterChanged(searchBox.text)
|
||||
assetsView.expandAll()
|
||||
|
||||
if (root.__searchBoxEmpty && searchBox.text)
|
||||
root.__searchBoxEmpty = false
|
||||
else if (!root.__searchBoxEmpty && !searchBox.text)
|
||||
root.__searchBoxEmpty = true
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.IconButton {
|
||||
id: addAssetButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
tooltip: qsTr("Add a new asset to the project.")
|
||||
@@ -146,14 +159,13 @@ Item {
|
||||
leftPadding: 10
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: 12
|
||||
visible: assetsModel.isEmpty && !searchBox.isEmpty()
|
||||
visible: !assetsModel.haveFiles && !root.__searchBoxEmpty
|
||||
}
|
||||
|
||||
|
||||
Item { // placeholder when the assets library is empty
|
||||
width: parent.width
|
||||
height: parent.height - searchRow.height
|
||||
visible: assetsModel.isEmpty && searchBox.isEmpty()
|
||||
visible: !assetsModel.haveFiles && root.__searchBoxEmpty
|
||||
clip: true
|
||||
|
||||
DropArea { // handles external drop (goes into default folder based on suffix)
|
||||
@@ -164,7 +176,7 @@ Item {
|
||||
}
|
||||
|
||||
onDropped: {
|
||||
rootView.handleExtFilesDrop(root.dropSimpleExtFiles, root.dropComplexExtFiles)
|
||||
rootView.emitExtFilesDrop(root.dropSimpleExtFiles, root.dropComplexExtFiles)
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -217,8 +229,11 @@ Item {
|
||||
|
||||
AssetsView {
|
||||
id: assetsView
|
||||
assetsRoot: root
|
||||
contextMenu: contextMenu
|
||||
|
||||
width: parent.width
|
||||
height: parent.height - y
|
||||
}
|
||||
}
|
||||
} // Column
|
||||
}
|
||||
|
@@ -1,90 +1,148 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
StudioControls.Menu {
|
||||
id: contextMenu
|
||||
id: root
|
||||
|
||||
required property Item assetsView
|
||||
|
||||
property bool __isDirectory: false
|
||||
property var __fileIndex: null
|
||||
property string __dirPath: ""
|
||||
property string __dirName: ""
|
||||
property var __onFolderCreated: null
|
||||
property var __onFolderRenamed: null
|
||||
property var __dirIndex: null
|
||||
property string __allExpandedState: ""
|
||||
property var __selectedAssetPathsList: null
|
||||
|
||||
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
|
||||
|
||||
onOpened: {
|
||||
var numSelected = Object.values(root.selectedAssets).filter(p => p).length
|
||||
deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File")
|
||||
function openContextMenuForRoot(rootModelIndex, dirPath, dirName, onFolderCreated)
|
||||
{
|
||||
root.__onFolderCreated = onFolderCreated
|
||||
root.__fileIndex = ""
|
||||
root.__dirPath = dirPath
|
||||
root.__dirName = dirName
|
||||
root.__dirIndex = rootModelIndex
|
||||
root.__isDirectory = false
|
||||
root.popup()
|
||||
}
|
||||
|
||||
function openContextMenuForDir(dirModelIndex, dirPath, dirName, allExpandedState,
|
||||
onFolderCreated, onFolderRenamed)
|
||||
{
|
||||
root.__onFolderCreated = onFolderCreated
|
||||
root.__onFolderRenamed = onFolderRenamed
|
||||
root.__dirPath = dirPath
|
||||
root.__dirName = dirName
|
||||
root.__fileIndex = ""
|
||||
root.__dirIndex = dirModelIndex
|
||||
root.__isDirectory = true
|
||||
root.__allExpandedState = allExpandedState
|
||||
root.popup()
|
||||
}
|
||||
|
||||
function openContextMenuForFile(fileIndex, dirModelIndex, selectedAssetPathsList, onFolderCreated)
|
||||
{
|
||||
if (selectedAssetPathsList.length > 1) {
|
||||
deleteFileItem.text = qsTr("Delete Files")
|
||||
addTexturesItem.text = qsTr("Add Textures")
|
||||
} else {
|
||||
deleteFileItem.text = qsTr("Delete File")
|
||||
addTexturesItem.text = qsTr("Add Texture")
|
||||
}
|
||||
|
||||
root.__onFolderCreated = onFolderCreated
|
||||
root.__selectedAssetPathsList = selectedAssetPathsList
|
||||
root.__fileIndex = fileIndex
|
||||
root.__dirIndex = dirModelIndex
|
||||
root.__dirPath = assetsModel.filePath(dirModelIndex)
|
||||
root.__isDirectory = false
|
||||
root.popup()
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Expand All")
|
||||
enabled: root.allExpandedState !== 1
|
||||
visible: root.isDirContextMenu
|
||||
enabled: root.__allExpandedState !== "all_expanded"
|
||||
visible: root.__isDirectory
|
||||
height: visible ? implicitHeight : 0
|
||||
onTriggered: assetsModel.toggleExpandAll(true)
|
||||
onTriggered: root.assetsView.expandAll()
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Collapse All")
|
||||
enabled: root.allExpandedState !== 2
|
||||
visible: root.isDirContextMenu
|
||||
enabled: root.__allExpandedState !== "all_collapsed"
|
||||
visible: root.__isDirectory
|
||||
height: visible ? implicitHeight : 0
|
||||
onTriggered: assetsModel.toggleExpandAll(false)
|
||||
onTriggered: root.assetsView.collapseAll()
|
||||
}
|
||||
|
||||
StudioControls.MenuSeparator {
|
||||
visible: root.isDirContextMenu
|
||||
visible: root.__isDirectory
|
||||
height: visible ? StudioTheme.Values.border : 0
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
id: addTexturesItem
|
||||
text: qsTr("Add Texture")
|
||||
visible: root.__fileIndex && assetsModel.allFilePathsAreImages(root.__selectedAssetPathsList)
|
||||
height: addTexturesItem.visible ? addTexturesItem.implicitHeight : 0
|
||||
onTriggered: rootView.addTextures(root.__selectedAssetPathsList)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
id: addLightProbes
|
||||
text: qsTr("Add Light Probe")
|
||||
visible: root.__fileIndex && root.__selectedAssetPathsList.length === 1
|
||||
&& assetsModel.allFilePathsAreImages(root.__selectedAssetPathsList)
|
||||
height: addLightProbes.visible ? addLightProbes.implicitHeight : 0
|
||||
onTriggered: rootView.addLightProbe(root.__selectedAssetPathsList[0])
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
id: deleteFileItem
|
||||
text: qsTr("Delete File")
|
||||
visible: root.contextFilePath
|
||||
visible: root.__fileIndex
|
||||
height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
|
||||
onTriggered: {
|
||||
assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
|
||||
let deleted = assetsModel.requestDeleteFiles(root.__selectedAssetPathsList)
|
||||
if (!deleted)
|
||||
confirmDeleteFiles.open()
|
||||
}
|
||||
|
||||
ConfirmDeleteFilesDialog {
|
||||
id: confirmDeleteFiles
|
||||
parent: root.assetsView
|
||||
files: root.__selectedAssetPathsList
|
||||
|
||||
onAccepted: root.assetsView.selectedAssets = {}
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.MenuSeparator {
|
||||
visible: root.contextFilePath
|
||||
visible: root.__fileIndex
|
||||
height: visible ? StudioTheme.Values.border : 0
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Rename Folder")
|
||||
visible: root.isDirContextMenu
|
||||
visible: root.__isDirectory
|
||||
height: visible ? implicitHeight : 0
|
||||
onTriggered: renameFolderDialog.open()
|
||||
|
||||
RenameFolderDialog {
|
||||
id: renameFolderDialog
|
||||
parent: root.assetsView
|
||||
dirPath: root.__dirPath
|
||||
dirName: root.__dirName
|
||||
|
||||
onAccepted: root.__onFolderRenamed()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +151,10 @@ StudioControls.Menu {
|
||||
|
||||
NewFolderDialog {
|
||||
id: newFolderDialog
|
||||
parent: root.assetsView
|
||||
dirPath: root.__dirPath
|
||||
|
||||
onAccepted: root.__onFolderCreated(newFolderDialog.createdDirPath)
|
||||
}
|
||||
|
||||
onTriggered: newFolderDialog.open()
|
||||
@@ -100,21 +162,25 @@ StudioControls.Menu {
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Delete Folder")
|
||||
visible: root.isDirContextMenu
|
||||
visible: root.__isDirectory
|
||||
height: visible ? implicitHeight : 0
|
||||
|
||||
ConfirmDeleteFolderDialog {
|
||||
id: confirmDeleteFolderDialog
|
||||
parent: root.assetsView
|
||||
dirName: root.__dirName
|
||||
dirIndex: root.__dirIndex
|
||||
}
|
||||
|
||||
onTriggered: {
|
||||
var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
|
||||
&& !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
|
||||
|
||||
if (dirEmpty)
|
||||
assetsModel.deleteFolder(root.contextDir.dirPath)
|
||||
else
|
||||
if (!assetsModel.hasChildren(root.__dirIndex)) {
|
||||
// NOTE: the folder may still not be empty -- it doesn't have files visible to the
|
||||
// user, but that doesn't mean that there are no other files (e.g. files of unknown
|
||||
// types) on disk in this directory.
|
||||
assetsModel.deleteFolderRecursively(root.__dirIndex)
|
||||
} else {
|
||||
confirmDeleteFolderDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,255 +1,311 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
|
||||
id: assetsView
|
||||
TreeView {
|
||||
id: root
|
||||
clip: true
|
||||
interactive: assetsView.verticalScrollBarVisible && !contextMenu.opened
|
||||
interactive: verticalScrollBar.visible && !root.contextMenu.opened
|
||||
reuseItems: false
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
rowSpacing: 5
|
||||
|
||||
Column {
|
||||
Repeater {
|
||||
model: assetsModel // context property
|
||||
delegate: dirSection
|
||||
required property Item assetsRoot
|
||||
required property StudioControls.Menu contextMenu
|
||||
property alias verticalScrollBar: verticalScrollBar
|
||||
|
||||
property var selectedAssets: ({})
|
||||
|
||||
// used to see if the op requested is to expand or to collapse.
|
||||
property int lastRowCount: -1
|
||||
// we need this to know if we need to expand further, while we're in onRowsChanged()
|
||||
property bool requestedExpandAll: true
|
||||
// used to compute the visual depth of the items we show to the user.
|
||||
property int rootPathDepth: 0
|
||||
property int rootPathRow: 0
|
||||
// i.e. first child of the root path
|
||||
readonly property int firstRow: root.rootPathRow + 1
|
||||
property int rowToExpand: -1
|
||||
property var __createdDirectories: []
|
||||
|
||||
rowHeightProvider: (row) => {
|
||||
if (row <= root.rootPathRow)
|
||||
return 0
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
Component {
|
||||
id: dirSection
|
||||
|
||||
Section {
|
||||
id: section
|
||||
|
||||
width: assetsView.width -
|
||||
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
|
||||
caption: dirName
|
||||
sectionHeight: 30
|
||||
sectionFontSize: 15
|
||||
leftPadding: 0
|
||||
topPadding: dirDepth > 0 ? 5 : 0
|
||||
bottomPadding: 0
|
||||
hideHeader: dirDepth === 0
|
||||
showLeftBorder: dirDepth > 0
|
||||
expanded: dirExpanded
|
||||
visible: dirVisible
|
||||
expandOnClick: false
|
||||
useDefaulContextMenu: false
|
||||
dropEnabled: true
|
||||
|
||||
onToggleExpand: {
|
||||
dirExpanded = !dirExpanded
|
||||
ScrollBar.vertical: HelperWidgets.VerticalScrollBar {
|
||||
id: verticalScrollBar
|
||||
scrollBarVisible: root.contentHeight > root.height
|
||||
}
|
||||
|
||||
onDropEnter: (drag)=> {
|
||||
root.updateDropExtFiles(drag)
|
||||
section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0
|
||||
model: assetsModel
|
||||
|
||||
onRowsChanged: {
|
||||
if (root.rows > root.rootPathRow + 1 && !assetsModel.haveFiles ||
|
||||
root.rows <= root.rootPathRow + 1 && assetsModel.haveFiles) {
|
||||
assetsModel.syncHaveFiles()
|
||||
}
|
||||
|
||||
onDropExit: {
|
||||
section.highlight = false
|
||||
}
|
||||
|
||||
onDrop: {
|
||||
section.highlight = false
|
||||
rootView.handleExtFilesDrop(root.dropSimpleExtFiles,
|
||||
root.dropComplexExtFiles,
|
||||
dirPath)
|
||||
}
|
||||
|
||||
onShowContextMenu: {
|
||||
root.contextFilePath = ""
|
||||
root.contextDir = model
|
||||
root.isDirContextMenu = true
|
||||
root.allExpandedState = assetsModel.getAllExpandedState()
|
||||
contextMenu.popup()
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
leftPadding: 5
|
||||
|
||||
Repeater {
|
||||
model: dirsModel
|
||||
delegate: dirSection
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: filesModel
|
||||
delegate: fileSection
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Empty folder")
|
||||
color: StudioTheme.Values.themeTextColorDisabled
|
||||
font.pixelSize: 12
|
||||
visible: !(dirsModel && dirsModel.rowCount() > 0)
|
||||
&& !(filesModel && filesModel.rowCount() > 0)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: {
|
||||
root.contextFilePath = ""
|
||||
root.contextDir = model
|
||||
root.isDirContextMenu = true
|
||||
contextMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileSection
|
||||
|
||||
Rectangle {
|
||||
width: assetsView.width -
|
||||
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
|
||||
height: img.height
|
||||
color: root.selectedAssets[filePath]
|
||||
? StudioTheme.Values.themeInteraction
|
||||
: (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
|
||||
: "transparent")
|
||||
|
||||
Row {
|
||||
spacing: 5
|
||||
|
||||
Image {
|
||||
id: img
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
width: 48
|
||||
height: 48
|
||||
source: "image://qmldesigner_assets/" + filePath
|
||||
}
|
||||
|
||||
Text {
|
||||
text: fileName
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: 14
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
readonly property string suffix: fileName.substr(-4)
|
||||
readonly property bool isFont: suffix === ".ttf" || suffix === ".otf"
|
||||
readonly property bool isEffect: suffix === ".qep"
|
||||
property bool currFileSelected: false
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
property bool allowTooltip: true
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onExited: tooltipBackend.hideTooltip()
|
||||
onEntered: allowTooltip = true
|
||||
onCanceled: {
|
||||
tooltipBackend.hideTooltip()
|
||||
allowTooltip = true
|
||||
}
|
||||
onPositionChanged: tooltipBackend.reposition()
|
||||
onPressed: (mouse) => {
|
||||
forceActiveFocus()
|
||||
allowTooltip = false
|
||||
tooltipBackend.hideTooltip()
|
||||
var ctrlDown = mouse.modifiers & Qt.ControlModifier
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (!root.selectedAssets[filePath] && !ctrlDown)
|
||||
root.selectedAssets = {}
|
||||
currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
|
||||
root.selectedAssets[filePath] = currFileSelected
|
||||
root.selectedAssetsChanged()
|
||||
|
||||
if (currFileSelected) {
|
||||
rootView.startDragAsset(
|
||||
Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
|
||||
mapToGlobal(mouse.x, mouse.y))
|
||||
}
|
||||
} else {
|
||||
if (!root.selectedAssets[filePath] && !ctrlDown)
|
||||
root.selectedAssets = {}
|
||||
currFileSelected = root.selectedAssets[filePath] || !ctrlDown
|
||||
root.selectedAssets[filePath] = currFileSelected
|
||||
root.selectedAssetsChanged()
|
||||
|
||||
root.contextFilePath = filePath
|
||||
root.contextDir = model.fileDir
|
||||
root.isDirContextMenu = false
|
||||
|
||||
contextMenu.popup()
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: (mouse) => {
|
||||
allowTooltip = true
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (!(mouse.modifiers & Qt.ControlModifier))
|
||||
root.selectedAssets = {}
|
||||
root.selectedAssets[filePath] = currFileSelected
|
||||
root.selectedAssetsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: (mouse) => {
|
||||
forceActiveFocus()
|
||||
allowTooltip = false
|
||||
tooltipBackend.hideTooltip()
|
||||
if (mouse.button === Qt.LeftButton && isEffect)
|
||||
rootView.openEffectMaker(filePath)
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
visible: !isFont && mouseArea.containsMouse && !contextMenu.visible
|
||||
text: filePath
|
||||
delay: 1000
|
||||
updateRows()
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: mouseArea.containsMouse && mouseArea.allowTooltip
|
||||
id: updateRowsTimer
|
||||
interval: 200
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
if (suffix === ".ttf" || suffix === ".otf") {
|
||||
tooltipBackend.name = fileName
|
||||
tooltipBackend.path = filePath
|
||||
tooltipBackend.showTooltip()
|
||||
root.updateRows()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: rootView
|
||||
|
||||
function onDirectoryCreated(path)
|
||||
{
|
||||
root.__createdDirectories.push(path)
|
||||
|
||||
updateRowsTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: assetsModel
|
||||
function onDirectoryLoaded(path)
|
||||
{
|
||||
// updating rows for safety: the rows might have been created before the
|
||||
// directory (esp. the root path) has been loaded, so we must make sure all rows are
|
||||
// expanded -- otherwise, the tree may not become visible.
|
||||
|
||||
updateRowsTimer.restart()
|
||||
|
||||
let idx = assetsModel.indexForPath(path)
|
||||
let row = root.rowAtIndex(idx)
|
||||
let column = root.columnAtIndex(idx)
|
||||
|
||||
if (row >= root.rootPathRow && !root.isExpanded(row))
|
||||
root.expand(row)
|
||||
}
|
||||
|
||||
function onRootPathChanged()
|
||||
{
|
||||
// when we switch from one project to another, we need to reset the state of the
|
||||
// view: make sure we will do an "expand all" (otherwise, the whole tree might
|
||||
// be collapsed, and with our visible root not being the actual root of the tree,
|
||||
// the entire tree would be invisible)
|
||||
root.lastRowCount = -1
|
||||
root.requestedExpandAll = true
|
||||
}
|
||||
|
||||
function onFileChanged(filePath)
|
||||
{
|
||||
rootView.invalidateThumbnail(filePath)
|
||||
|
||||
let index = assetsModel.indexForPath(filePath)
|
||||
let cell = root.cellAtIndex(index)
|
||||
let fileItem = root.itemAtCell(cell)
|
||||
|
||||
if (fileItem)
|
||||
fileItem.reloadImage()
|
||||
}
|
||||
|
||||
} // Connections
|
||||
|
||||
function addCreatedFolder(path)
|
||||
{
|
||||
root.__createdDirectories.push(path)
|
||||
}
|
||||
|
||||
function selectedPathsAsList()
|
||||
{
|
||||
return Object.keys(root.selectedAssets)
|
||||
.filter(itemPath => root.selectedAssets[itemPath])
|
||||
}
|
||||
|
||||
// workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721
|
||||
function resetVerticalScrollPosition()
|
||||
{
|
||||
root.contentY = 0
|
||||
}
|
||||
|
||||
function updateRows()
|
||||
{
|
||||
if (root.rows <= 0)
|
||||
return
|
||||
|
||||
while (root.__createdDirectories.length > 0) {
|
||||
let dirPath = root.__createdDirectories.pop()
|
||||
let index = assetsModel.indexForPath(dirPath)
|
||||
let row = root.rowAtIndex(index)
|
||||
|
||||
if (row > 0)
|
||||
root.expand(row)
|
||||
else if (row === -1 && assetsModel.indexIsValid(index)) {
|
||||
// It is possible that this directory, dirPath, was created inside of a parent
|
||||
// directory that was not yet expanded in the TreeView. This can happen with the
|
||||
// bridge plugin. In such a situation, we don't have a "row" for it yet, so we have
|
||||
// to expand its parents, from root to our `index`
|
||||
let parents = assetsModel.parentIndices(index);
|
||||
parents.reverse().forEach(idx => {
|
||||
let row = root.rowAtIndex(idx)
|
||||
if (row > 0)
|
||||
root.expand(row)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// we have no way to know beyond doubt here if updateRows() was called due
|
||||
// to a request to expand or to collapse rows - but it should be safe to
|
||||
// assume that, if we have more rows now than the last time, then it's an expand
|
||||
var expanding = (root.rows >= root.lastRowCount)
|
||||
|
||||
if (expanding) {
|
||||
if (root.requestedExpandAll)
|
||||
root.__doExpandAll()
|
||||
} else {
|
||||
if (root.rowToExpand > 0) {
|
||||
root.expand(root.rowToExpand)
|
||||
root.rowToExpand = -1
|
||||
}
|
||||
|
||||
// on collapsing, set expandAll flag to false.
|
||||
root.requestedExpandAll = false;
|
||||
}
|
||||
|
||||
root.lastRowCount = root.rows
|
||||
}
|
||||
|
||||
function __doExpandAll()
|
||||
{
|
||||
let expandedAny = false
|
||||
for (let nRow = 0; nRow < root.rows; ++nRow) {
|
||||
let index = root.__modelIndex(nRow)
|
||||
if (assetsModel.isDirectory(index) && !root.isExpanded(nRow)) {
|
||||
root.expand(nRow);
|
||||
expandedAny = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!expandedAny)
|
||||
Qt.callLater(root.forceLayout)
|
||||
}
|
||||
|
||||
function expandAll()
|
||||
{
|
||||
// In order for __doExpandAll() to be called repeatedly (every time a new node is
|
||||
// loaded, and then, expanded), we need to set requestedExpandAll to true.
|
||||
root.requestedExpandAll = true
|
||||
root.__doExpandAll()
|
||||
}
|
||||
|
||||
function collapseAll()
|
||||
{
|
||||
root.resetVerticalScrollPosition()
|
||||
|
||||
// collapse all, except for the root path - from the last item (leaves) up to the root
|
||||
for (let nRow = root.rows - 1; nRow >= 0; --nRow) {
|
||||
let index = root.__modelIndex(nRow)
|
||||
// we don't want to collapse the root path, because doing so will hide the contents
|
||||
// of the tree.
|
||||
if (assetsModel.filePath(index) === assetsModel.rootPath())
|
||||
break
|
||||
|
||||
root.collapse(nRow)
|
||||
}
|
||||
}
|
||||
|
||||
// workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721
|
||||
onContentHeightChanged: {
|
||||
if (root.contentHeight <= root.height) {
|
||||
let first = root.itemAtCell(0, root.firstRow)
|
||||
if (!first)
|
||||
root.contentY = 0
|
||||
}
|
||||
}
|
||||
|
||||
function computeAllExpandedState()
|
||||
{
|
||||
var dirsWithChildren = [...Array(root.rows).keys()].filter(row => {
|
||||
let index = root.__modelIndex(row)
|
||||
return assetsModel.isDirectory(index) && assetsModel.hasChildren(index)
|
||||
})
|
||||
|
||||
var countExpanded = dirsWithChildren.filter(row => root.isExpanded(row)).length
|
||||
|
||||
if (countExpanded === dirsWithChildren.length)
|
||||
return "all_expanded"
|
||||
|
||||
if (countExpanded === 0)
|
||||
return "all_collapsed"
|
||||
return ""
|
||||
}
|
||||
|
||||
function startDropHoverOver(row)
|
||||
{
|
||||
let index = root.__modelIndex(row)
|
||||
if (assetsModel.isDirectory(index))
|
||||
return
|
||||
|
||||
let parentItem = root.__getDelegateParentForIndex(index)
|
||||
if (parentItem)
|
||||
parentItem.hasChildWithDropHover = true
|
||||
}
|
||||
|
||||
function endDropHover(row)
|
||||
{
|
||||
let index = root.__modelIndex(row)
|
||||
if (assetsModel.isDirectory(index))
|
||||
return
|
||||
|
||||
let parentItem = root.__getDelegateParentForIndex(index)
|
||||
if (parentItem)
|
||||
parentItem.hasChildWithDropHover = false
|
||||
}
|
||||
|
||||
function isAssetSelected(itemPath)
|
||||
{
|
||||
return root.selectedAssets[itemPath] ? true : false
|
||||
}
|
||||
|
||||
function clearSelectedAssets()
|
||||
{
|
||||
root.selectedAssets = {}
|
||||
}
|
||||
|
||||
function setAssetSelected(itemPath, selected)
|
||||
{
|
||||
root.selectedAssets[itemPath] = selected
|
||||
root.selectedAssetsChanged()
|
||||
}
|
||||
|
||||
function __getDelegateParentForIndex(index)
|
||||
{
|
||||
let parentIndex = assetsModel.parentDirIndex(index)
|
||||
let parentCell = root.cellAtIndex(parentIndex)
|
||||
return root.itemAtCell(parentCell)
|
||||
}
|
||||
|
||||
function __modelIndex(row)
|
||||
{
|
||||
// The modelIndex() function exists since 6.3. In Qt 6.3, this modelIndex() function was a
|
||||
// member of the TreeView, while in Qt6.4 it was moved to TableView. In Qt6.4, the order of
|
||||
// the arguments was changed.
|
||||
if (assetsRoot.qtVersionAtLeast6_4)
|
||||
return root.modelIndex(0, row)
|
||||
else
|
||||
return root.modelIndex(row, 0)
|
||||
}
|
||||
|
||||
delegate: AssetDelegate {
|
||||
assetsView: root
|
||||
assetsRoot: root.assetsRoot
|
||||
indentation: 5
|
||||
}
|
||||
} // TreeView
|
||||
|
@@ -0,0 +1,107 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioTheme as StudioTheme
|
||||
import StudioControls as StudioControls
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
title: qsTr("Confirm Delete Files")
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
implicitWidth: 350
|
||||
modal: true
|
||||
|
||||
required property var files
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 20
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
id: confirmDeleteText
|
||||
|
||||
text: root.files && root.files.length
|
||||
? qsTr("Some files might be in use. Delete anyway?")
|
||||
: ""
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
width: root.width
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
|
||||
Keys.onEnterPressed: btnDelete.onClicked()
|
||||
Keys.onReturnPressed: btnDelete.onClicked()
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: fileListLoader
|
||||
sourceComponent: null
|
||||
width: parent.width - 20
|
||||
x: 10
|
||||
height: 50
|
||||
}
|
||||
|
||||
StudioControls.CheckBox {
|
||||
id: dontAskAgain
|
||||
text: qsTr("Do not ask this again")
|
||||
actionIndicatorVisible: false
|
||||
width: parent.width
|
||||
x: 10
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnDelete
|
||||
|
||||
text: qsTr("Delete")
|
||||
onClicked: {
|
||||
assetsModel.deleteFiles(root.files, dontAskAgain.checked)
|
||||
root.accept()
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
fileListLoader.sourceComponent = null
|
||||
fileListLoader.sourceComponent = fileListComponent
|
||||
|
||||
confirmDeleteText.forceActiveFocus()
|
||||
}
|
||||
|
||||
onClosed: fileListLoader.sourceComponent = null
|
||||
|
||||
Component {
|
||||
id: fileListComponent
|
||||
|
||||
ListView {
|
||||
id: filesListView
|
||||
model: root.files
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
||||
ScrollBar.vertical: HelperWidgets.VerticalScrollBar {
|
||||
id: verticalScrollBar
|
||||
scrollBarVisible: filesListView.contentHeight > filesListView.height
|
||||
}
|
||||
|
||||
delegate: Text {
|
||||
elide: Text.ElideLeft
|
||||
text: model.modelData.replace(assetsModel.currentProjectDirPath(), "")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
width: parent.width - (verticalScrollBar.scrollBarVisible ? verticalScrollBar.width : 0)
|
||||
}
|
||||
} // ListView
|
||||
} // Component
|
||||
}
|
@@ -1,38 +1,13 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Dialog {
|
||||
id: confirmDeleteFolderDialog
|
||||
id: root
|
||||
|
||||
title: qsTr("Folder Not Empty")
|
||||
anchors.centerIn: parent
|
||||
@@ -40,6 +15,9 @@ Dialog {
|
||||
implicitWidth: 300
|
||||
modal: true
|
||||
|
||||
required property string dirName
|
||||
required property var dirIndex
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 20
|
||||
width: parent.width
|
||||
@@ -47,11 +25,10 @@ Dialog {
|
||||
Text {
|
||||
id: folderNotEmpty
|
||||
|
||||
text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
|
||||
.arg(root.contextDir ? root.contextDir.dirName : "")
|
||||
text: qsTr("Folder \"%1\" is not empty. Delete it anyway?").arg(root.dirName)
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
width: confirmDeleteFolderDialog.width
|
||||
width: root.width
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
|
||||
@@ -63,27 +40,27 @@ Dialog {
|
||||
text: qsTr("If the folder has assets in use, deleting it might cause the project to not work correctly.")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
width: confirmDeleteFolderDialog.width
|
||||
width: root.width
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
Button {
|
||||
HelperWidgets.Button {
|
||||
id: btnDelete
|
||||
|
||||
text: qsTr("Delete")
|
||||
|
||||
onClicked: {
|
||||
assetsModel.deleteFolder(root.contextDir.dirPath)
|
||||
confirmDeleteFolderDialog.accept()
|
||||
assetsModel.deleteFolderRecursively(root.dirIndex)
|
||||
root.accept()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: confirmDeleteFolderDialog.reject()
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
required property string message
|
||||
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
implicitWidth: 300
|
||||
modal: true
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 20
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
text: root.message
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
width: root.width
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Close")
|
||||
anchors.right: parent.right
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: root.forceActiveFocus()
|
||||
}
|
@@ -68,6 +68,8 @@ Item {
|
||||
id: mouseRegion
|
||||
anchors.fill: parent
|
||||
|
||||
tooltip: toolTip
|
||||
|
||||
onShowContextMenu: delegateRoot.showContextMenu()
|
||||
onPressed: (mouse)=> {
|
||||
allowTooltip = false
|
||||
|
@@ -1,44 +1,35 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Dialog {
|
||||
id: newFolderDialog
|
||||
id: root
|
||||
|
||||
title: qsTr("Create New Folder")
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
modal: true
|
||||
|
||||
required property string dirPath
|
||||
property string createdDirPath: ""
|
||||
readonly property int __maxPath: 260
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
id: folderNameValidator
|
||||
regExp: /^(\w[^*/><?\\|:]*)$/
|
||||
}
|
||||
|
||||
ErrorDialog {
|
||||
id: creationFailedDialog
|
||||
title: qsTr("Could not create folder")
|
||||
message: qsTr("An error occurred while trying to create the folder.")
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 2
|
||||
|
||||
@@ -58,6 +49,10 @@ Dialog {
|
||||
|
||||
Keys.onEnterPressed: btnCreate.onClicked()
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
|
||||
onTextChanged: {
|
||||
root.createdDirPath = root.dirPath + '/' + folderName.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +63,13 @@ Dialog {
|
||||
visible: folderName.text === ""
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Folder path is too long.")
|
||||
color: "#ff0000"
|
||||
anchors.right: parent.right
|
||||
visible: root.createdDirPath.length > root.__maxPath
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
@@ -76,20 +78,23 @@ Dialog {
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
|
||||
Button {
|
||||
HelperWidgets.Button {
|
||||
id: btnCreate
|
||||
|
||||
text: qsTr("Create")
|
||||
enabled: folderName.text !== ""
|
||||
enabled: folderName.text !== "" && root.createdDirPath.length <= root.__maxPath
|
||||
onClicked: {
|
||||
assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
|
||||
newFolderDialog.accept()
|
||||
root.createdDirPath = root.dirPath + '/' + folderName.text
|
||||
if (assetsModel.addNewFolder(root.createdDirPath))
|
||||
root.accept()
|
||||
else
|
||||
creationFailedDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: newFolderDialog.reject()
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,4 +104,8 @@ Dialog {
|
||||
folderName.selectAll()
|
||||
folderName.forceActiveFocus()
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
root.createdDirPath = ""
|
||||
}
|
||||
}
|
||||
|
@@ -1,38 +1,14 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Dialog {
|
||||
id: renameFolderDialog
|
||||
id: root
|
||||
|
||||
title: qsTr("Rename Folder")
|
||||
anchors.centerIn: parent
|
||||
@@ -41,6 +17,13 @@ Dialog {
|
||||
modal: true
|
||||
|
||||
property bool renameError: false
|
||||
required property string dirPath
|
||||
required property string dirName
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
id: folderNameValidator
|
||||
regExp: /^(\w[^*/><?\\|:]*)$/
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 2
|
||||
@@ -50,10 +33,10 @@ Dialog {
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
width: renameFolderDialog.width - 12
|
||||
width: root.width - 12
|
||||
validator: folderNameValidator
|
||||
|
||||
onEditChanged: renameFolderDialog.renameError = false
|
||||
onEditChanged: root.renameError = false
|
||||
Keys.onEnterPressed: btnRename.onClicked()
|
||||
Keys.onReturnPressed: btnRename.onClicked()
|
||||
}
|
||||
@@ -61,15 +44,15 @@ Dialog {
|
||||
Text {
|
||||
text: qsTr("Folder name cannot be empty.")
|
||||
color: "#ff0000"
|
||||
visible: folderRename.text === "" && !renameFolderDialog.renameError
|
||||
visible: folderRename.text === "" && !root.renameError
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Could not rename folder. Make sure no folder with the same name exists.")
|
||||
wrapMode: Text.WordWrap
|
||||
width: renameFolderDialog.width - 12
|
||||
width: root.width - 12
|
||||
color: "#ff0000"
|
||||
visible: renameFolderDialog.renameError
|
||||
visible: root.renameError
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
@@ -81,7 +64,7 @@ Dialog {
|
||||
text: qsTr("If the folder has assets in use, renaming it might cause the project to not work correctly.")
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
wrapMode: Text.WordWrap
|
||||
width: renameFolderDialog.width
|
||||
width: root.width
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
}
|
||||
@@ -94,31 +77,31 @@ Dialog {
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
|
||||
Button {
|
||||
HelperWidgets.Button {
|
||||
id: btnRename
|
||||
|
||||
text: qsTr("Rename")
|
||||
enabled: folderRename.text !== ""
|
||||
onClicked: {
|
||||
var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
|
||||
var success = assetsModel.renameFolder(root.dirPath, folderRename.text)
|
||||
if (success)
|
||||
renameFolderDialog.accept()
|
||||
root.accept()
|
||||
|
||||
renameFolderDialog.renameError = !success
|
||||
root.renameError = !success
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: renameFolderDialog.reject()
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
folderRename.text = root.contextDir.dirName
|
||||
folderRename.text = root.dirName
|
||||
folderRename.selectAll()
|
||||
folderRename.forceActiveFocus()
|
||||
renameFolderDialog.renameError = false
|
||||
root.renameError = false
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,153 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: StudioTheme.Values.themePanelBackground
|
||||
|
||||
Column {
|
||||
id: col
|
||||
padding: 5
|
||||
spacing: 5
|
||||
|
||||
Row {
|
||||
spacing: 5
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
text: qsTr("Select material:")
|
||||
font.bold: true
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: materialsListView
|
||||
|
||||
width: root.width * .5 - 5
|
||||
height: root.height - 60
|
||||
focus: true
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
ScrollBar.vertical: StudioControls.ScrollBar {
|
||||
visible: materialsListView.height < materialsListView.contentHeight
|
||||
}
|
||||
model: materialsModel
|
||||
delegate: Rectangle {
|
||||
width: materialsListView.width
|
||||
height: 20
|
||||
color: ListView.isCurrentItem ? StudioTheme.Values.themeTextSelectionColor
|
||||
: "transparent"
|
||||
|
||||
function id() {
|
||||
return modelData.match(/\((.*)\)/).pop()
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
leftPadding: 5
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
materialsListView.currentIndex = index
|
||||
rootView.updatePropsModel(id())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
Text {
|
||||
text: qsTr("Select property:")
|
||||
font.bold: true
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: propertiesListView
|
||||
|
||||
width: root.width * .5 - 5
|
||||
height: root.height - 60
|
||||
focus: true
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
ScrollBar.vertical: StudioControls.ScrollBar {
|
||||
visible: propertiesListView.height < propertiesListView.contentHeight
|
||||
}
|
||||
model: propertiesModel
|
||||
delegate: Rectangle {
|
||||
width: propertiesListView.width
|
||||
height: 20
|
||||
color: ListView.isCurrentItem ? StudioTheme.Values.themeTextSelectionColor
|
||||
: "transparent"
|
||||
|
||||
function propName() {
|
||||
return modelData
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: StudioTheme.Values.myFontSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
leftPadding: 5
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
propertiesListView.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 5
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
|
||||
Button {
|
||||
text: qsTr("Cancel")
|
||||
|
||||
onClicked: {
|
||||
rootView.closeChooseMatPropsView()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Apply")
|
||||
|
||||
onClicked: {
|
||||
let matId = materialsListView.currentItem.id()
|
||||
let prop = propertiesListView.currentItem.propName()
|
||||
|
||||
rootView.applyTextureToProperty(matId, prop)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,6 +12,8 @@ Item {
|
||||
|
||||
readonly property int cellWidth: 100
|
||||
readonly property int cellHeight: 120
|
||||
readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary
|
||||
&& materialBrowserModel.hasQuick3DImport
|
||||
|
||||
property var currMaterialItem: null
|
||||
|
||||
@@ -19,6 +21,7 @@ Item {
|
||||
function closeContextMenu()
|
||||
{
|
||||
ctxMenu.close()
|
||||
ctxMenuTextures.close()
|
||||
}
|
||||
|
||||
// Called from C++ to refresh a preview material after it changes
|
||||
@@ -54,16 +57,16 @@ Item {
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (materialBrowserModel.hasMaterialRoot || !materialBrowserModel.hasQuick3DImport)
|
||||
if (!root.enableUiElements)
|
||||
return;
|
||||
|
||||
var matsSecBottom = mapFromItem(materialsSection, 0, materialsSection.y).y
|
||||
+ materialsSection.height;
|
||||
|
||||
if (!materialBrowserModel.hasMaterialRoot && materialBrowserModel.hasQuick3DImport
|
||||
&& mouse.y < matsSecBottom) {
|
||||
if (mouse.y < matsSecBottom)
|
||||
ctxMenu.popupMenu()
|
||||
}
|
||||
else
|
||||
ctxMenuTextures.popupMenu()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +86,10 @@ Item {
|
||||
id: ctxMenu
|
||||
}
|
||||
|
||||
TextureBrowserContextMenu {
|
||||
id: ctxMenuTextures
|
||||
}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
y: 5
|
||||
@@ -90,37 +97,25 @@ Item {
|
||||
|
||||
Row {
|
||||
width: root.width
|
||||
enabled: !materialBrowserModel.hasMaterialRoot && materialBrowserModel.hasQuick3DImport
|
||||
enabled: root.enableUiElements
|
||||
|
||||
StudioControls.SearchBox {
|
||||
id: searchBox
|
||||
|
||||
width: root.width - addMaterialButton.width
|
||||
width: root.width
|
||||
|
||||
onSearchChanged: (searchText) => {
|
||||
rootView.handleSearchFilterChanged(searchText)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: addMaterialButton
|
||||
|
||||
tooltip: qsTr("Add a material.")
|
||||
|
||||
icon: StudioTheme.Constants.plus
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
buttonSize: searchBox.height
|
||||
onClicked: materialBrowserModel.addNewMaterial()
|
||||
enabled: materialBrowserModel.hasQuick3DImport
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: {
|
||||
if (materialBrowserModel.hasMaterialRoot)
|
||||
qsTr("<b>Material Browser</b> is disabled inside a material component.")
|
||||
else if (!materialBrowserModel.hasQuick3DImport)
|
||||
if (!materialBrowserModel.hasQuick3DImport)
|
||||
qsTr("To use <b>Material Browser</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||
else if (!materialBrowserModel.hasMaterialLibrary)
|
||||
qsTr("<b>Material Browser</b> is disabled inside a non-visual component.")
|
||||
else
|
||||
""
|
||||
}
|
||||
@@ -141,10 +136,14 @@ Item {
|
||||
width: root.width
|
||||
height: root.height - searchBox.height
|
||||
clip: true
|
||||
visible: materialBrowserModel.hasQuick3DImport && !materialBrowserModel.hasMaterialRoot
|
||||
interactive: !ctxMenu.opened
|
||||
visible: root.enableUiElements
|
||||
interactive: !ctxMenu.opened && !ctxMenuTextures.opened
|
||||
|
||||
Column {
|
||||
Item {
|
||||
width: root.width
|
||||
height: materialsSection.height
|
||||
|
||||
Section {
|
||||
id: materialsSection
|
||||
|
||||
@@ -195,7 +194,7 @@ Item {
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
leftPadding: 10
|
||||
visible: materialBrowserModel.isEmpty && !searchBox.isEmpty() && !materialBrowserModel.hasMaterialRoot
|
||||
visible: materialBrowserModel.isEmpty && !searchBox.isEmpty()
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -210,12 +209,47 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: addMaterialButton
|
||||
|
||||
tooltip: qsTr("Add a material.")
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: scrollView.verticalScrollBarVisible ? 10 : 0
|
||||
icon: StudioTheme.Constants.plus
|
||||
normalColor: "transparent"
|
||||
buttonSize: StudioTheme.Values.sectionHeadHeight
|
||||
onClicked: materialBrowserModel.addNewMaterial()
|
||||
enabled: root.enableUiElements
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: root.width
|
||||
height: texturesSection.height
|
||||
|
||||
Section {
|
||||
id: texturesSection
|
||||
|
||||
width: root.width
|
||||
caption: qsTr("Textures")
|
||||
|
||||
dropEnabled: true
|
||||
|
||||
onDropEnter: (drag) => {
|
||||
drag.accepted = drag.formats[0] === "application/vnd.qtdesignstudio.bundletexture"
|
||||
highlight = drag.accepted
|
||||
}
|
||||
|
||||
onDropExit: {
|
||||
highlight = false
|
||||
}
|
||||
|
||||
onDrop: {
|
||||
highlight = false
|
||||
rootView.acceptBundleTextureDrop()
|
||||
}
|
||||
|
||||
Grid {
|
||||
width: scrollView.width
|
||||
leftPadding: 5
|
||||
@@ -232,7 +266,7 @@ Item {
|
||||
height: root.cellWidth
|
||||
|
||||
onShowContextMenu: {
|
||||
// ctxMenuTexture.popupMenu(this, model) // TODO: implement textures context menu
|
||||
ctxMenuTextures.popupMenu(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,11 +277,11 @@ Item {
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||
leftPadding: 10
|
||||
visible: materialBrowserModel.isEmpty && !searchBox.isEmpty() && !materialBrowserModel.hasMaterialRoot
|
||||
visible: materialBrowserTexturesModel.isEmpty && !searchBox.isEmpty()
|
||||
}
|
||||
|
||||
Text {
|
||||
text:qsTr("There are no texture in this project.")
|
||||
text:qsTr("There are no textures in this project.")
|
||||
visible: materialBrowserTexturesModel.isEmpty && searchBox.isEmpty()
|
||||
textFormat: Text.RichText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
@@ -257,6 +291,36 @@ Item {
|
||||
width: root.width
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: addTextureButton
|
||||
|
||||
tooltip: qsTr("Add a texture.")
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: scrollView.verticalScrollBarVisible ? 10 : 0
|
||||
icon: StudioTheme.Constants.plus
|
||||
normalColor: "transparent"
|
||||
buttonSize: StudioTheme.Values.sectionHeadHeight
|
||||
onClicked: materialBrowserTexturesModel.addNewTexture()
|
||||
enabled: root.enableUiElements
|
||||
}
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: masterDropArea
|
||||
|
||||
property int emptyHeight: scrollView.height - materialsSection.height - texturesSection.height
|
||||
|
||||
width: root.width
|
||||
height: emptyHeight > 0 ? emptyHeight : 0
|
||||
|
||||
enabled: true
|
||||
|
||||
onEntered: (drag) => texturesSection.dropEnter(drag)
|
||||
onDropped: (drag) => texturesSection.drop(drag)
|
||||
onExited: texturesSection.dropExit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,13 +36,25 @@ Rectangle {
|
||||
mouseArea.forceActiveFocus()
|
||||
}
|
||||
|
||||
border.width: materialBrowserModel.selectedIndex === index ? 1 : 0
|
||||
border.width: materialBrowserModel.selectedIndex === index ? rootView.materialSectionFocused ? 3 : 1 : 0
|
||||
border.color: materialBrowserModel.selectedIndex === index
|
||||
? StudioTheme.Values.themeControlOutlineInteraction
|
||||
: "transparent"
|
||||
color: "transparent"
|
||||
visible: materialVisible
|
||||
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: (drag) => {
|
||||
drag.accepted = drag.formats[0] === "application/vnd.qtdesignstudio.texture"
|
||||
}
|
||||
|
||||
onDropped: (drag) => {
|
||||
rootView.acceptTextureDropOnMaterial(index, drag.getDataAsString(drag.keys[0]))
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
@@ -50,6 +62,7 @@ Rectangle {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onPressed: (mouse) => {
|
||||
rootView.focusMaterialSection(true)
|
||||
materialBrowserModel.selectMaterial(index)
|
||||
|
||||
if (mouse.button === Qt.LeftButton)
|
||||
@@ -116,7 +129,10 @@ Rectangle {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: materialBrowserModel.selectMaterial(index)
|
||||
onClicked: {
|
||||
rootView.focusMaterialSection(true)
|
||||
materialBrowserModel.selectMaterial(index)
|
||||
}
|
||||
onDoubleClicked: root.startRename()
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
StudioControls.Menu {
|
||||
id: root
|
||||
|
||||
property var targetTexture: null
|
||||
property int copiedTextureInternalId: -1
|
||||
|
||||
function popupMenu(targetTexture = null)
|
||||
{
|
||||
this.targetTexture = targetTexture
|
||||
materialBrowserTexturesModel.updateSceneEnvState()
|
||||
materialBrowserTexturesModel.updateModelSelectionState()
|
||||
popup()
|
||||
}
|
||||
|
||||
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Apply to selected model")
|
||||
enabled: root.targetTexture && materialBrowserTexturesModel.hasSingleModelSelection
|
||||
onTriggered: materialBrowserTexturesModel.applyToSelectedModel(root.targetTexture.textureInternalId)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Apply to selected material")
|
||||
enabled: root.targetTexture && materialBrowserModel.selectedIndex >= 0
|
||||
onTriggered: materialBrowserTexturesModel.applyToSelectedMaterial(root.targetTexture.textureInternalId)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Apply as light probe")
|
||||
enabled: root.targetTexture && materialBrowserTexturesModel.hasSceneEnv
|
||||
onTriggered: materialBrowserTexturesModel.applyAsLightProbe(root.targetTexture.textureInternalId)
|
||||
}
|
||||
|
||||
StudioControls.MenuSeparator {}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Duplicate")
|
||||
enabled: root.targetTexture
|
||||
onTriggered: materialBrowserTexturesModel.duplicateTexture(materialBrowserTexturesModel.selectedIndex)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Delete")
|
||||
enabled: root.targetTexture
|
||||
onTriggered: materialBrowserTexturesModel.deleteTexture(materialBrowserTexturesModel.selectedIndex)
|
||||
}
|
||||
|
||||
StudioControls.MenuSeparator {}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Create New Texture")
|
||||
onTriggered: materialBrowserTexturesModel.addNewTexture()
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme
|
||||
import HelperWidgets
|
||||
@@ -13,7 +14,8 @@ Rectangle {
|
||||
visible: textureVisible
|
||||
|
||||
color: "transparent"
|
||||
border.width: materialBrowserTexturesModel.selectedIndex === index ? 1 : 0
|
||||
border.width: materialBrowserTexturesModel.selectedIndex === index
|
||||
? !rootView.materialSectionFocused ? 3 : 1 : 0
|
||||
border.color: materialBrowserTexturesModel.selectedIndex === index
|
||||
? StudioTheme.Values.themeControlOutlineInteraction
|
||||
: "transparent"
|
||||
@@ -25,8 +27,10 @@ Rectangle {
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: (mouse) => {
|
||||
rootView.focusMaterialSection(false)
|
||||
materialBrowserTexturesModel.selectTexture(index)
|
||||
|
||||
if (mouse.button === Qt.LeftButton)
|
||||
@@ -34,13 +38,32 @@ Rectangle {
|
||||
else if (mouse.button === Qt.RightButton)
|
||||
root.showContextMenu()
|
||||
}
|
||||
|
||||
onDoubleClicked: materialBrowserTexturesModel.openTextureEditor();
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
visible: mouseArea.containsMouse
|
||||
// contentWidth is not calculated correctly by the toolTip (resulting in a wider tooltip than
|
||||
// needed). Using a helper Text to calculate the correct width
|
||||
contentWidth: helperText.width
|
||||
bottomInset: -2
|
||||
text: textureToolTip
|
||||
delay: 1000
|
||||
|
||||
Text {
|
||||
id: helperText
|
||||
text: textureToolTip
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
source: textureSource
|
||||
source: "image://materialBrowserTex/" + textureSource
|
||||
asynchronous: true
|
||||
sourceSize.width: root.width - 10
|
||||
sourceSize.height: root.height - 10
|
||||
anchors.centerIn: parent
|
||||
cache: false
|
||||
smooth: true
|
||||
}
|
||||
}
|
||||
|
@@ -32,8 +32,14 @@ PropertyEditorPane {
|
||||
height: 150
|
||||
|
||||
Text {
|
||||
text: hasQuick3DImport ? qsTr("There are no materials in this project.<br>Select '<b>+</b>' to create one.")
|
||||
: qsTr("To use <b>Material Editor</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||
text: {
|
||||
if (!hasQuick3DImport)
|
||||
qsTr("To use <b>Material Editor</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||
else if (!hasMaterialLibrary)
|
||||
qsTr("<b>Material Editor</b> is disabled inside a non-visual component.")
|
||||
else
|
||||
qsTr("There are no materials in this project.<br>Select '<b>+</b>' to create one.")
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||
|
@@ -5,7 +5,7 @@ import QtQuick 2.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import ToolBarAction 1.0
|
||||
import MaterialToolBarAction 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -28,7 +28,7 @@ Rectangle {
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasMaterial && hasModelSelection && hasQuick3DImport && !hasMaterialRoot
|
||||
enabled: hasMaterial && hasModelSelection && hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
|
||||
tooltip: qsTr("Apply material to selected model.")
|
||||
}
|
||||
@@ -39,7 +39,7 @@ Rectangle {
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasQuick3DImport && !hasMaterialRoot
|
||||
enabled: hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.AddNewMaterial)
|
||||
tooltip: qsTr("Create new material.")
|
||||
}
|
||||
@@ -50,7 +50,7 @@ Rectangle {
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasMaterial && hasQuick3DImport && !hasMaterialRoot
|
||||
enabled: hasMaterial && hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentMaterial)
|
||||
tooltip: qsTr("Delete current material.")
|
||||
}
|
||||
@@ -61,7 +61,7 @@ Rectangle {
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasMaterial && hasQuick3DImport && !hasMaterialRoot
|
||||
enabled: hasMaterial && hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
|
||||
tooltip: qsTr("Open material browser.")
|
||||
}
|
||||
|
@@ -140,6 +140,7 @@ Column {
|
||||
anchors.centerIn: parent
|
||||
source: "image://materialEditor/preview"
|
||||
cache: false
|
||||
smooth: true
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -45,6 +45,10 @@ Rectangle {
|
||||
onWidthChanged: root.responsiveResize(root.width, root.height)
|
||||
onHeightChanged: root.responsiveResize(root.width, root.height)
|
||||
|
||||
function showEvent() {
|
||||
addCanvas.requestPaint()
|
||||
}
|
||||
|
||||
Component.onCompleted: root.responsiveResize(root.width, root.height)
|
||||
|
||||
function numFit(overall, size, space) {
|
||||
@@ -258,6 +262,14 @@ Rectangle {
|
||||
// the close of the old popup. Using an int keeps track of number of opened popups.
|
||||
property int menuOpen: 0
|
||||
|
||||
Connections {
|
||||
target: statesEditorModel
|
||||
function onModelReset() {
|
||||
root.menuOpen = 0
|
||||
editDialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
// This timer is used to delay the current state animation as it didn't work due to the
|
||||
// repeaters item not being positioned in time resulting in 0 x and y position if the grids
|
||||
// row and column were not changed during the layout algorithm .
|
||||
@@ -303,15 +315,8 @@ Rectangle {
|
||||
standardButtons: Dialog.Apply | Dialog.Cancel
|
||||
x: editButton.x - Math.max(0, editButton.x + editDialog.width - root.width)
|
||||
y: toolBar.height
|
||||
closePolicy: Popup.NoAutoClose
|
||||
|
||||
width: Math.min(300, root.width)
|
||||
|
||||
function apply() {
|
||||
let renamed = statesEditorModel.renameActiveStateGroup(editTextField.text)
|
||||
if (renamed)
|
||||
editDialog.close()
|
||||
}
|
||||
closePolicy: Popup.NoAutoClose
|
||||
|
||||
onApplied: editDialog.accept()
|
||||
|
||||
@@ -541,7 +546,7 @@ Rectangle {
|
||||
baseState: true
|
||||
defaultChecked: !statesEditorModel.baseState.modelHasDefaultState // TODO Make this one a model property
|
||||
isChecked: root.currentStateInternalId === 0
|
||||
thumbnailImageSource: statesEditorModel.baseState.stateImageSource // TODO Get rid of the QVariantMap
|
||||
thumbnailImageSource: statesEditorModel.baseState.stateImageSource ?? "" // TODO Get rid of the QVariantMap
|
||||
isTiny: root.tinyMode
|
||||
|
||||
onFocusSignal: root.currentStateInternalId = 0
|
||||
@@ -854,28 +859,29 @@ Rectangle {
|
||||
|
||||
Item {
|
||||
id: addWrapper
|
||||
visible: canAddNewStates
|
||||
|
||||
Canvas {
|
||||
id: addCanvas
|
||||
width: root.thumbWidth
|
||||
height: root.thumbHeight
|
||||
|
||||
property int plusExtend: 20
|
||||
property int halfWidth: addCanvas.width / 2
|
||||
property int halfHeight: addCanvas.height / 2
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
|
||||
ctx.strokeStyle = StudioTheme.Values.themeStateHighlight
|
||||
ctx.lineWidth = 6
|
||||
|
||||
var plusExtend = 20
|
||||
var halfWidth = addCanvas.width / 2
|
||||
var halfHeight = addCanvas.height / 2
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(halfWidth, halfHeight - plusExtend)
|
||||
ctx.lineTo(halfWidth, halfHeight + plusExtend)
|
||||
ctx.moveTo(addCanvas.halfWidth, addCanvas.halfHeight - addCanvas.plusExtend)
|
||||
ctx.lineTo(addCanvas.halfWidth, addCanvas.halfHeight + addCanvas.plusExtend)
|
||||
|
||||
ctx.moveTo(halfWidth - plusExtend, halfHeight)
|
||||
ctx.lineTo(halfWidth + plusExtend, halfHeight)
|
||||
ctx.moveTo(addCanvas.halfWidth - addCanvas.plusExtend, addCanvas.halfHeight)
|
||||
ctx.lineTo(addCanvas.halfWidth + addCanvas.plusExtend, addCanvas.halfHeight)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.save()
|
||||
|
@@ -15,6 +15,10 @@ PropertyEditorPane {
|
||||
showState: true
|
||||
}
|
||||
|
||||
InsightSection {
|
||||
visible: insightEnabled
|
||||
}
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: SelectionDynamicPropertiesModel {}
|
||||
visible: !hasMultiSelection
|
||||
|
@@ -59,10 +59,10 @@ StudioControls.ComboBox {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property string assetPath: ""
|
||||
property string dropData: ""
|
||||
|
||||
onEntered: (drag) => {
|
||||
dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0]
|
||||
dropArea.dropData = drag.getDataAsString(drag.keys[0]).split(",")[0]
|
||||
drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag
|
||||
comboBox.hasActiveHoverDrag = drag.accepted
|
||||
}
|
||||
@@ -70,7 +70,7 @@ StudioControls.ComboBox {
|
||||
onExited: comboBox.hasActiveHoverDrag = false
|
||||
|
||||
onDropped: {
|
||||
comboBox.backendValue.commitDrop(dropArea.assetPath)
|
||||
comboBox.backendValue.commitDrop(dropArea.dropData)
|
||||
comboBox.hasActiveHoverDrag = false
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ Rectangle {
|
||||
property alias icon: icon.text
|
||||
property alias tooltip: toolTip.text
|
||||
property alias iconSize: icon.font.pixelSize
|
||||
property alias containsMouse: mouseArea.containsMouse
|
||||
|
||||
property bool enabled: true
|
||||
property int buttonSize: StudioTheme.Values.height
|
||||
|
@@ -5,7 +5,7 @@ import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import HelperWidgets 2.0
|
||||
|
||||
MouseArea {
|
||||
ToolTipArea {
|
||||
id: mouseArea
|
||||
|
||||
property bool allowTooltip: true
|
||||
|
@@ -0,0 +1,88 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import HelperWidgets 2.0
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Section {
|
||||
id: root
|
||||
caption: qsTr("Analytics")
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
property string defaultItem: qsTr("[None]")
|
||||
|
||||
function addDefaultItem(arr)
|
||||
{
|
||||
var copy = arr.slice()
|
||||
copy.unshift(root.defaultItem)
|
||||
return copy
|
||||
}
|
||||
|
||||
SectionLayout {
|
||||
PropertyLabel { text: qsTr("Category") }
|
||||
|
||||
SecondColumnLayout {
|
||||
Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
|
||||
|
||||
StudioControls.ComboBox {
|
||||
id: comboBox
|
||||
property var backendValue: backendValues.InsightCategory_category
|
||||
property var valueFromBackend: comboBox.backendValue === undefined ? 0 : comboBox.backendValue.value
|
||||
|
||||
onValueFromBackendChanged: comboBox.invalidate()
|
||||
onModelChanged: comboBox.invalidate()
|
||||
|
||||
actionIndicatorVisible: false
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
width: implicitWidth
|
||||
model: root.addDefaultItem(insightCategories)
|
||||
editable: false
|
||||
|
||||
onCompressedActivated: function(index, reason) {
|
||||
if (comboBox.backendValue === undefined)
|
||||
return
|
||||
|
||||
verifyInsightImport()
|
||||
|
||||
if (index === 0)
|
||||
comboBox.backendValue.resetValue()
|
||||
else
|
||||
comboBox.backendValue.value = comboBox.currentText
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelNodeBackend
|
||||
function onSelectionToBeChanged() {
|
||||
comboBox.popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
function invalidate() {
|
||||
var index = comboBox.find(comboBox.valueFromBackend)
|
||||
if (index < 0) {
|
||||
if (comboBox.valueFromBackend === "") {
|
||||
comboBox.currentIndex = 0
|
||||
comboBox.labelColor = StudioTheme.Values.themeTextColor
|
||||
} else {
|
||||
comboBox.currentIndex = index
|
||||
comboBox.editText = comboBox.valueFromBackend
|
||||
comboBox.labelColor = StudioTheme.Values.themeError
|
||||
}
|
||||
} else {
|
||||
if (index !== comboBox.currentIndex)
|
||||
comboBox.currentIndex = index
|
||||
|
||||
comboBox.labelColor = StudioTheme.Values.themeTextColor
|
||||
}
|
||||
}
|
||||
Component.onCompleted: comboBox.invalidate()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ Item {
|
||||
|
||||
property alias minimumValue: spinBox.realFrom
|
||||
property alias maximumValue: spinBox.realTo
|
||||
property alias value: spinBox.realValue
|
||||
property alias stepSize: spinBox.realStepSize
|
||||
|
||||
property alias backendValue: spinBox.backendValue
|
||||
|
@@ -119,9 +119,9 @@ Section {
|
||||
text: qsTr("Render type quality")
|
||||
tooltip: qsTr("Overrides the default rendering type quality for this component.")
|
||||
blockedByTemplate: !root.isBackendValueAvailable("renderTypeQuality")
|
||||
enabled: backendValues.renderType !== undefined
|
||||
? backendValues.renderType.enumeration === "QtRendering"
|
||||
: false
|
||||
enabled: root.isBackendValueAvailable("renderTypeQuality")
|
||||
&& (backendValues.renderType.value === "QtRendering"
|
||||
|| backendValues.renderType.enumeration === "QtRendering")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
@@ -134,7 +134,8 @@ Section {
|
||||
"HighRenderTypeQuality", "VeryHighRenderTypeQuality"]
|
||||
backendValue: backendValues.renderTypeQuality
|
||||
enabled: root.isBackendValueAvailable("renderTypeQuality")
|
||||
&& backendValues.renderType.enumeration === "QtRendering"
|
||||
&& (backendValues.renderType.value === "QtRendering"
|
||||
|| backendValues.renderType.enumeration === "QtRendering")
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
|
@@ -102,8 +102,8 @@ Row {
|
||||
|
||||
Item {
|
||||
visible: thumbnail.status === Image.Ready
|
||||
Layout.preferredWidth: 100
|
||||
Layout.preferredHeight: 100
|
||||
Layout.preferredWidth: 96
|
||||
Layout.preferredHeight: 96
|
||||
|
||||
Image {
|
||||
id: checker
|
||||
@@ -116,7 +116,10 @@ Row {
|
||||
Image {
|
||||
id: thumbnail
|
||||
asynchronous: true
|
||||
anchors.fill: parent
|
||||
sourceSize.height: 96
|
||||
sourceSize.width: 96
|
||||
height: 96
|
||||
width: 96
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: {
|
||||
if (root.isBuiltInPrimitive(root.absoluteFilePath))
|
||||
@@ -231,8 +234,8 @@ Row {
|
||||
|
||||
Item {
|
||||
visible: delegateThumbnail.status === Image.Ready
|
||||
Layout.preferredWidth: 100
|
||||
Layout.preferredHeight: 100
|
||||
Layout.preferredWidth: 96
|
||||
Layout.preferredHeight: 96
|
||||
|
||||
Image {
|
||||
id: delegateChecker
|
||||
@@ -245,7 +248,10 @@ Row {
|
||||
Image {
|
||||
id: delegateThumbnail
|
||||
asynchronous: true
|
||||
anchors.fill: parent
|
||||
sourceSize.height: 96
|
||||
sourceSize.width: 96
|
||||
height: 96
|
||||
width: 96
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: {
|
||||
if (root.isBuiltInPrimitive(delegateRoot.name))
|
||||
|
@@ -42,6 +42,7 @@ IconButton 2.0 IconButton.qml
|
||||
IconLabel 2.0 IconLabel.qml
|
||||
ImagePreviewTooltipArea 2.0 ImagePreviewTooltipArea.qml
|
||||
ImageSection 2.0 ImageSection.qml
|
||||
InsightSection 2.0 InsightSection.qml
|
||||
ItemFilterComboBox 2.0 ItemFilterComboBox.qml
|
||||
Label 2.0 Label.qml
|
||||
LineEdit 2.0 LineEdit.qml
|
||||
|
@@ -57,7 +57,7 @@ TextInput {
|
||||
myControl.focus = false
|
||||
} else {
|
||||
myControl.popup.open()
|
||||
myControl.forceActiveFocus()
|
||||
//myControl.forceActiveFocus()
|
||||
}
|
||||
} else {
|
||||
textInput.forceActiveFocus()
|
||||
|
@@ -1,4 +1,7 @@
|
||||
import QtQuick %{QtQuickVersion}
|
||||
@if !%{IsQt6Project}
|
||||
import QtQuick.Window %{QtQuickVersion}
|
||||
@endif
|
||||
import %{ApplicationImport}
|
||||
@if %{UseVirtualKeyboard}
|
||||
import QtQuick.VirtualKeyboard %{QtQuickVersion}
|
||||
|
@@ -299,6 +299,10 @@
|
||||
"source": "../common/qmlmodules.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlmodules"
|
||||
},
|
||||
{
|
||||
"source": "../common/qmlcomponents.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlcomponents"
|
||||
},
|
||||
{
|
||||
"source": "../common/main.qml",
|
||||
"target": "%{ProjectDirectory}/main.qml"
|
||||
@@ -319,6 +323,11 @@
|
||||
"source": "../common/import_qml_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/import_qml_components_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_components_plugins.h"
|
||||
},
|
||||
|
||||
{
|
||||
"source": "../common/CMakeLists.content.txt.tpl",
|
||||
"target": "%{ProjectDirectory}/content/CMakeLists.txt"
|
||||
|
@@ -295,6 +295,10 @@
|
||||
"source": "../common/qmlmodules.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlmodules"
|
||||
},
|
||||
{
|
||||
"source": "../common/qmlcomponents.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlcomponents"
|
||||
},
|
||||
{
|
||||
"source": "../common/main.qml",
|
||||
"target": "%{ProjectDirectory}/main.qml"
|
||||
@@ -315,6 +319,10 @@
|
||||
"source": "../common/import_qml_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/import_qml_components_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_components_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/CMakeLists.content.txt.tpl",
|
||||
"target": "%{ProjectDirectory}/content/CMakeLists.txt"
|
||||
|
@@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0
|
||||
|
||||
import QtQuick %{QtQuickVersion}
|
||||
@if !%{IsQt6Project}
|
||||
import QtQuick.Window %{QtQuickVersion}
|
||||
@endif
|
||||
import %{ImportModuleName} %{ImportModuleVersion}
|
||||
@if %{UseVirtualKeyboard}
|
||||
import QtQuick.VirtualKeyboard %{QtQuickVersion}
|
||||
|
@@ -1,24 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
cmake_minimum_required(VERSION 3.21.1)
|
||||
|
||||
set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components")
|
||||
|
||||
project(%{ProjectName}App LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(Qt6 COMPONENTS Gui Qml Quick)
|
||||
qt_add_executable(%{ProjectExecutableName} src/main.cpp)
|
||||
find_package(QT NAMES Qt6 COMPONENTS Gui Qml Quick)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick)
|
||||
|
||||
qt_add_resources(%{ProjectExecutableName} "configuration"
|
||||
qt_add_executable(${CMAKE_PROJECT_NAME} src/main.cpp)
|
||||
|
||||
# qt_standard_project_setup() requires Qt 6.3 or higher. See https://doc.qt.io/qt-6/qt-standard-project-setup.html for details.
|
||||
if (${QT_VERSION_MINOR} GREATER_EQUAL 3)
|
||||
qt6_standard_project_setup()
|
||||
endif()
|
||||
|
||||
qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
qtquickcontrols2.conf
|
||||
)
|
||||
|
||||
target_link_libraries(%{ProjectExecutableName} PRIVATE
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Quick
|
||||
Qt${QT_VERSION_MAJOR}::Qml
|
||||
)
|
||||
|
||||
if (${BUILD_QDS_COMPONENTS})
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents)
|
||||
endif ()
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
|
||||
|
@@ -100,7 +100,7 @@ Project {
|
||||
/* Required for deployment */
|
||||
targetDirectory: "/opt/%{ProjectName}"
|
||||
|
||||
qdsVersion: "3.8"
|
||||
qdsVersion: "3.9"
|
||||
|
||||
quickVersion: "%{QtQuickVersion}"
|
||||
|
||||
|
@@ -1,4 +1,7 @@
|
||||
import QtQuick %{QtQuickVersion}
|
||||
@if !%{IsQt6Project}
|
||||
import QtQuick.Window %{QtQuickVersion}
|
||||
@endif
|
||||
import %{ApplicationImport}
|
||||
|
||||
Window {
|
||||
|
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* This file is automatically generated by Qt Design Studio.
|
||||
* Do not change.
|
||||
*/
|
||||
|
||||
#include "qqmlextensionplugin.h"
|
||||
|
||||
#ifdef BULD_QDS_COMPONENTS
|
||||
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ApplicationPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(FlowViewPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_LogicHelperPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_MultiTextPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin)
|
||||
|
||||
#endif
|
@@ -5,6 +5,7 @@
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#include "app_environment.h"
|
||||
#include "import_qml_components_plugins.h"
|
||||
#include "import_qml_plugins.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@@ -0,0 +1,33 @@
|
||||
### This file is automatically generated by Qt Design Studio.
|
||||
### Do not change
|
||||
|
||||
message("Building designer components.")
|
||||
|
||||
set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
ds
|
||||
GIT_TAG qds-3.9
|
||||
GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(ds)
|
||||
FetchContent_Populate(ds)
|
||||
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
QuickStudioComponentsplugin
|
||||
QuickStudioEffectsplugin
|
||||
QuickStudioApplicationplugin
|
||||
FlowViewplugin
|
||||
QuickStudioLogicHelperplugin
|
||||
QuickStudioMultiTextplugin
|
||||
QuickStudioEventSimulatorplugin
|
||||
QuickStudioEventSystemplugin
|
||||
)
|
||||
|
||||
add_subdirectory(${ds_SOURCE_DIR} ${ds_BINARY_DIR})
|
||||
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
BULD_QDS_COMPONENTS=true
|
||||
)
|
@@ -1,7 +1,7 @@
|
||||
### This file is automatically generated by Qt Design Studio.
|
||||
### Do not change
|
||||
|
||||
qt6_add_qml_module(%{ProjectExecutableName}
|
||||
qt6_add_qml_module(${CMAKE_PROJECT_NAME}
|
||||
URI "Main"
|
||||
VERSION 1.0
|
||||
NO_PLUGIN
|
||||
@@ -11,7 +11,7 @@ qt6_add_qml_module(%{ProjectExecutableName}
|
||||
add_subdirectory(content)
|
||||
add_subdirectory(imports)
|
||||
|
||||
target_link_libraries(%{ProjectExecutableName} PRIVATE
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
contentplugin
|
||||
%{ProjectPluginName}
|
||||
)
|
||||
|
@@ -293,6 +293,10 @@
|
||||
"source": "../common/qmlmodules.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlmodules"
|
||||
},
|
||||
{
|
||||
"source": "../common/qmlcomponents.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlcomponents"
|
||||
},
|
||||
{
|
||||
"source": "../common/main.qml",
|
||||
"target": "%{ProjectDirectory}/main.qml"
|
||||
@@ -313,6 +317,10 @@
|
||||
"source": "../common/import_qml_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/import_qml_components_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_components_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/CMakeLists.content.txt.tpl",
|
||||
"target": "%{ProjectDirectory}/content/CMakeLists.txt"
|
||||
|
@@ -252,6 +252,10 @@
|
||||
"source": "../common/qmlmodules.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlmodules"
|
||||
},
|
||||
{
|
||||
"source": "../common/qmlcomponents.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlcomponents"
|
||||
},
|
||||
{
|
||||
"source": "../common/main.qml",
|
||||
"target": "%{ProjectDirectory}/main.qml"
|
||||
@@ -272,6 +276,10 @@
|
||||
"source": "../common/import_qml_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/import_qml_components_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_components_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/CMakeLists.content.txt.tpl",
|
||||
"target": "%{ProjectDirectory}/content/CMakeLists.txt"
|
||||
|
@@ -1,5 +1,8 @@
|
||||
import QtQuick %{QtQuickVersion}
|
||||
import QtQuick.Controls %{QtQuickVersion}
|
||||
@if !%{IsQt6Project}
|
||||
import QtQuick.Window %{QtQuickVersion}
|
||||
@endif
|
||||
import %{ImportModuleName} %{ImportModuleVersion}
|
||||
|
||||
Window {
|
||||
|
@@ -249,6 +249,10 @@
|
||||
"source": "../common/qmlmodules.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlmodules"
|
||||
},
|
||||
{
|
||||
"source": "../common/qmlcomponents.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlcomponents"
|
||||
},
|
||||
{
|
||||
"source": "../common/main.qml",
|
||||
"target": "%{ProjectDirectory}/main.qml"
|
||||
@@ -269,6 +273,10 @@
|
||||
"source": "../common/import_qml_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/import_qml_components_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_components_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "CMakeLists.content.txt.tpl",
|
||||
"target": "%{ProjectDirectory}/content/CMakeLists.txt"
|
||||
|
@@ -1,5 +1,8 @@
|
||||
import QtQuick %{QtQuickVersion}
|
||||
import QtQuick.Controls %{QtQuickVersion}
|
||||
@if !%{IsQt6Project}
|
||||
import QtQuick.Window %{QtQuickVersion}
|
||||
@endif
|
||||
import %{ImportModuleName} %{ImportModuleVersion}
|
||||
|
||||
Window {
|
||||
|
@@ -249,6 +249,10 @@
|
||||
"source": "../common/qmlmodules.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlmodules"
|
||||
},
|
||||
{
|
||||
"source": "../common/qmlcomponents.tpl",
|
||||
"target": "%{ProjectDirectory}/qmlcomponents"
|
||||
},
|
||||
{
|
||||
"source": "../common/main.qml",
|
||||
"target": "%{ProjectDirectory}/main.qml"
|
||||
@@ -269,6 +273,10 @@
|
||||
"source": "../common/import_qml_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "../common/import_qml_components_plugins.h.tpl",
|
||||
"target": "%{ProjectDirectory}/src/import_qml_components_plugins.h"
|
||||
},
|
||||
{
|
||||
"source": "CMakeLists.content.txt.tpl",
|
||||
"target": "%{ProjectDirectory}/content/CMakeLists.txt"
|
||||
|
@@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
PropertyEditorPane {
|
||||
id: root
|
||||
|
||||
signal toolBarAction(int action)
|
||||
|
||||
// Called from C++, dummy method to avoid warnings
|
||||
function closeContextMenu() {}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
|
||||
TextureEditorToolBar {
|
||||
width: root.width
|
||||
|
||||
onToolBarAction: (action) => root.toolBarAction(action)
|
||||
}
|
||||
|
||||
Item {
|
||||
width: root.width - 2 * col.padding
|
||||
height: 150
|
||||
|
||||
Text {
|
||||
text: {
|
||||
if (!hasQuick3DImport)
|
||||
qsTr("To use <b>Texture Editor</b>, first add the QtQuick3D module in the <b>Components</b> view.")
|
||||
else if (!hasMaterialLibrary)
|
||||
qsTr("<b>Texture Editor</b> is disabled inside a non-visual component.")
|
||||
else
|
||||
qsTr("There are no textures in this project.<br>Select '<b>+</b>' to create one.")
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
width: root.width
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
|
||||
PropertyEditorPane {
|
||||
id: itemPane
|
||||
|
||||
signal toolBarAction(int action)
|
||||
|
||||
// invoked from C++ to refresh material preview image
|
||||
function refreshPreview()
|
||||
{
|
||||
topSection.refreshPreview()
|
||||
}
|
||||
|
||||
// Called also from C++ to close context menu on focus out
|
||||
function closeContextMenu()
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
TextureEditorTopSection {
|
||||
id: topSection
|
||||
|
||||
onToolBarAction: (action) => itemPane.toolBarAction(action)
|
||||
}
|
||||
|
||||
Item { width: 1; height: 10 }
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: TextureEditorDynamicPropertiesModel {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: specificsTwo
|
||||
|
||||
property string theSource: specificQmlData
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
visible: theSource !== ""
|
||||
sourceComponent: specificQmlComponent
|
||||
|
||||
onTheSourceChanged: {
|
||||
active = false
|
||||
active = true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 1
|
||||
height: 10
|
||||
visible: specificsTwo.visible
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: specificsOne
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
source: specificsUrl
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import HelperWidgets 2.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import TextureToolBarAction 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: StudioTheme.Values.themeSectionHeadBackground
|
||||
width: row.width
|
||||
height: 40
|
||||
|
||||
signal toolBarAction(int action)
|
||||
|
||||
Row {
|
||||
id: row
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
leftPadding: 6
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.applyMaterialToSelected
|
||||
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasTexture && hasSingleModelSelection && hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
|
||||
tooltip: qsTr("Apply texture to selected model's material.")
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.newMaterial
|
||||
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.AddNewTexture)
|
||||
tooltip: qsTr("Create new texture.")
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.deleteMaterial
|
||||
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasTexture && hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentTexture)
|
||||
tooltip: qsTr("Delete current texture.")
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: StudioTheme.Constants.openMaterialBrowser
|
||||
|
||||
normalColor: StudioTheme.Values.themeSectionHeadBackground
|
||||
iconSize: StudioTheme.Values.bigIconFontSize
|
||||
buttonSize: root.height
|
||||
enabled: hasQuick3DImport && hasMaterialLibrary
|
||||
onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
|
||||
tooltip: qsTr("Open material browser.")
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
signal toolBarAction(int action)
|
||||
|
||||
function refreshPreview()
|
||||
{
|
||||
texturePreview.source = ""
|
||||
texturePreview.source = "image://qmldesigner_thumbnails/" + resolveResourcePath(backendValues.source.valueToString)
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
TextureEditorToolBar {
|
||||
width: root.width
|
||||
|
||||
onToolBarAction: (action) => root.toolBarAction(action)
|
||||
}
|
||||
|
||||
Item { width: 1; height: 10 } // spacer
|
||||
|
||||
Rectangle {
|
||||
id: previewRect
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 152
|
||||
height: 152
|
||||
color: "#000000"
|
||||
|
||||
Image {
|
||||
id: texturePreview
|
||||
asynchronous: true
|
||||
sourceSize.width: 150
|
||||
sourceSize.height: 150
|
||||
anchors.centerIn: parent
|
||||
source: "image://qmldesigner_thumbnails/" + resolveResourcePath(backendValues.source.valueToString)
|
||||
}
|
||||
}
|
||||
}
|
@@ -525,7 +525,7 @@ Export::Export(ImportKey exportName,
|
||||
|
||||
bool Export::visibleInVContext(const ViewerContext &vContext) const
|
||||
{
|
||||
return pathRequired.isEmpty() || vContext.paths.contains(pathRequired);
|
||||
return pathRequired.isEmpty() || vContext.paths.count(pathRequired);
|
||||
}
|
||||
|
||||
CoreImport::CoreImport() : language(Dialect::Qml) { }
|
||||
|
@@ -126,7 +126,8 @@ Link::Link(const Snapshot &snapshot, const ViewerContext &vContext, const Librar
|
||||
{
|
||||
d->m_valueOwner = new ValueOwner;
|
||||
d->m_snapshot = snapshot;
|
||||
d->m_importPaths = vContext.paths;
|
||||
const QList<Utils::FilePath> list(vContext.paths.begin(), vContext.paths.end());
|
||||
d->m_importPaths = list;
|
||||
d->m_applicationDirectories = vContext.applicationDirectories;
|
||||
d->m_builtins = builtins;
|
||||
d->m_vContext = vContext;
|
||||
|
@@ -63,8 +63,8 @@ static const char *qtQuickUISuffix = "ui.qml";
|
||||
|
||||
static void maybeAddPath(ViewerContext &context, const Utils::FilePath &path)
|
||||
{
|
||||
if (!path.isEmpty() && !context.paths.contains(path))
|
||||
context.paths.append(path);
|
||||
if (!path.isEmpty() && !(context.paths.count(path) > 0))
|
||||
context.paths.insert(path);
|
||||
}
|
||||
|
||||
static QList<Utils::FilePath> environmentImportPaths()
|
||||
|
@@ -188,6 +188,9 @@ public:
|
||||
void removeProjectInfo(ProjectExplorer::Project *project);
|
||||
void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
|
||||
|
||||
QFuture<void> refreshSourceFiles(const QList<Utils::FilePath> &sourceFiles,
|
||||
bool emitDocumentOnDiskChanged);
|
||||
|
||||
signals:
|
||||
void documentUpdated(QmlJS::Document::Ptr doc);
|
||||
void documentChangedOnDisk(QmlJS::Document::Ptr doc);
|
||||
@@ -207,9 +210,6 @@ protected:
|
||||
virtual void addTaskInternal(const QFuture<void> &result, const QString &msg,
|
||||
const char *taskId) const;
|
||||
|
||||
QFuture<void> refreshSourceFiles(const QList<Utils::FilePath> &sourceFiles,
|
||||
bool emitDocumentOnDiskChanged);
|
||||
|
||||
static void parseLoop(QSet<Utils::FilePath> &scannedPaths,
|
||||
QSet<Utils::FilePath> &newLibraries,
|
||||
const WorkingCopy &workingCopyInternal,
|
||||
|
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
struct QMLJS_EXPORT ViewerContext
|
||||
@@ -21,7 +23,7 @@ struct QMLJS_EXPORT ViewerContext
|
||||
};
|
||||
|
||||
QStringList selectors;
|
||||
QList<Utils::FilePath> paths;
|
||||
std::set<Utils::FilePath> paths;
|
||||
QList<Utils::FilePath> applicationDirectories;
|
||||
Dialect language = Dialect::Qml;
|
||||
Flags flags = AddAllPaths;
|
||||
|
@@ -421,6 +421,7 @@ extend_qtc_plugin(QmlDesigner
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/itemlibrary
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/materialbrowser
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/materialeditor
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/textureeditor
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/navigator
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/propertyeditor
|
||||
${CMAKE_CURRENT_LIST_DIR}/components/stateseditor
|
||||
@@ -594,6 +595,7 @@ extend_qtc_plugin(QmlDesigner
|
||||
PUBLIC_INCLUDES components
|
||||
DEFINES QMLDESIGNERCOMPONENTS_LIBRARY
|
||||
SOURCES
|
||||
createtexture.cpp createtexture.h
|
||||
qmldesignercomponents_global.h
|
||||
)
|
||||
|
||||
@@ -623,6 +625,7 @@ extend_qtc_plugin(QmlDesigner
|
||||
selectioncontext.cpp selectioncontext.h
|
||||
theme.cpp theme.h
|
||||
zoomaction.cpp zoomaction.h
|
||||
anchoraction.cpp anchoraction.h
|
||||
svgpasteaction.cpp svgpasteaction.h
|
||||
viewmanager.cpp viewmanager.h
|
||||
)
|
||||
@@ -746,9 +749,6 @@ extend_qtc_plugin(QmlDesigner
|
||||
assetslibrarywidget.cpp assetslibrarywidget.h
|
||||
assetslibrarymodel.cpp assetslibrarymodel.h
|
||||
assetslibraryiconprovider.cpp assetslibraryiconprovider.h
|
||||
assetslibrarydir.cpp assetslibrarydir.h
|
||||
assetslibrarydirsmodel.cpp assetslibrarydirsmodel.h
|
||||
assetslibraryfilesmodel.cpp assetslibraryfilesmodel.h
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
@@ -821,6 +821,17 @@ extend_qtc_plugin(QmlDesigner
|
||||
materialeditor.qrc
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
SOURCES_PREFIX components/textureeditor
|
||||
SOURCES
|
||||
textureeditorcontextobject.cpp textureeditorcontextobject.h
|
||||
textureeditordynamicpropertiesproxymodel.cpp textureeditordynamicpropertiesproxymodel.h
|
||||
textureeditorqmlbackend.cpp textureeditorqmlbackend.h
|
||||
textureeditortransaction.cpp textureeditortransaction.h
|
||||
textureeditorview.cpp textureeditorview.h
|
||||
textureeditor.qrc
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
SOURCES_PREFIX components/materialbrowser
|
||||
SOURCES
|
||||
|
@@ -1,7 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/AssetsLibrary">
|
||||
<file>images/asset_default.png</file>
|
||||
<file>images/asset_default@2x.png</file>
|
||||
<file>images/asset_shader.png</file>
|
||||
<file>images/asset_shader@2x.png</file>
|
||||
<file>images/asset_shader_128.png</file>
|
||||
@@ -10,7 +8,16 @@
|
||||
<file>images/asset_sound_128.png</file>
|
||||
<file>images/asset_video.png</file>
|
||||
<file>images/asset_video@2x.png</file>
|
||||
<file>images/asset_effectClass.png</file>
|
||||
<file>images/asset_effectClass@2x.png</file>
|
||||
<file>images/asset_effectExported.png</file>
|
||||
<file>images/asset_effectExported@2x.png</file>
|
||||
<file>images/browse.png</file>
|
||||
<file>images/browse@2x.png</file>
|
||||
<file>images/assets_default.png</file>
|
||||
<file>images/assets_default@2x.png</file>
|
||||
<file>images/assets_default_128.png</file>
|
||||
<file>images/asset_effectClass_128.png</file>
|
||||
<file>images/asset_effectExported_128.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@@ -1,75 +0,0 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "assetslibrarydir.h"
|
||||
#include "assetslibrarydirsmodel.h"
|
||||
#include "assetslibraryfilesmodel.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
AssetsLibraryDir::AssetsLibraryDir(const QString &path, int depth, bool expanded, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_dirPath(path)
|
||||
, m_dirDepth(depth)
|
||||
, m_dirExpanded(expanded)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString AssetsLibraryDir::dirName() const { return m_dirPath.split('/').last(); }
|
||||
QString AssetsLibraryDir::dirPath() const { return m_dirPath; }
|
||||
int AssetsLibraryDir::dirDepth() const { return m_dirDepth; }
|
||||
bool AssetsLibraryDir::dirExpanded() const { return m_dirExpanded; }
|
||||
bool AssetsLibraryDir::dirVisible() const { return m_dirVisible; }
|
||||
|
||||
void AssetsLibraryDir::setDirExpanded(bool expand)
|
||||
{
|
||||
if (m_dirExpanded != expand) {
|
||||
m_dirExpanded = expand;
|
||||
emit dirExpandedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetsLibraryDir::setDirVisible(bool visible)
|
||||
{
|
||||
if (m_dirVisible != visible) {
|
||||
m_dirVisible = visible;
|
||||
emit dirVisibleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QObject *AssetsLibraryDir::filesModel() const
|
||||
{
|
||||
return m_filesModel;
|
||||
}
|
||||
|
||||
QObject *AssetsLibraryDir::dirsModel() const
|
||||
{
|
||||
return m_dirsModel;
|
||||
}
|
||||
|
||||
QList<AssetsLibraryDir *> AssetsLibraryDir::childAssetsDirs() const
|
||||
{
|
||||
if (m_dirsModel)
|
||||
return m_dirsModel->assetsDirs();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void AssetsLibraryDir::addDir(AssetsLibraryDir *assetsDir)
|
||||
{
|
||||
if (!m_dirsModel)
|
||||
m_dirsModel = new AssetsLibraryDirsModel(this);
|
||||
|
||||
m_dirsModel->addDir(assetsDir);
|
||||
}
|
||||
|
||||
void AssetsLibraryDir::addFile(const QString &filePath)
|
||||
{
|
||||
if (!m_filesModel)
|
||||
m_filesModel = new AssetsLibraryFilesModel(this);
|
||||
|
||||
m_filesModel->addFile(filePath);
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -1,63 +0,0 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class AssetsLibraryDirsModel;
|
||||
class AssetsLibraryFilesModel;
|
||||
|
||||
class AssetsLibraryDir : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString dirName READ dirName NOTIFY dirNameChanged)
|
||||
Q_PROPERTY(QString dirPath READ dirPath NOTIFY dirPathChanged)
|
||||
Q_PROPERTY(bool dirExpanded READ dirExpanded WRITE setDirExpanded NOTIFY dirExpandedChanged)
|
||||
Q_PROPERTY(bool dirVisible READ dirVisible WRITE setDirVisible NOTIFY dirVisibleChanged)
|
||||
Q_PROPERTY(int dirDepth READ dirDepth NOTIFY dirDepthChanged)
|
||||
Q_PROPERTY(QObject *filesModel READ filesModel NOTIFY filesModelChanged)
|
||||
Q_PROPERTY(QObject *dirsModel READ dirsModel NOTIFY dirsModelChanged)
|
||||
|
||||
public:
|
||||
AssetsLibraryDir(const QString &path, int depth, bool expanded = true, QObject *parent = nullptr);
|
||||
|
||||
QString dirName() const;
|
||||
QString dirPath() const;
|
||||
int dirDepth() const;
|
||||
|
||||
bool dirExpanded() const;
|
||||
bool dirVisible() const;
|
||||
void setDirExpanded(bool expand);
|
||||
void setDirVisible(bool visible);
|
||||
|
||||
QObject *filesModel() const;
|
||||
QObject *dirsModel() const;
|
||||
|
||||
QList<AssetsLibraryDir *> childAssetsDirs() const;
|
||||
|
||||
void addDir(AssetsLibraryDir *assetsDir);
|
||||
void addFile(const QString &filePath);
|
||||
|
||||
signals:
|
||||
void dirNameChanged();
|
||||
void dirPathChanged();
|
||||
void dirDepthChanged();
|
||||
void dirExpandedChanged();
|
||||
void dirVisibleChanged();
|
||||
void filesModelChanged();
|
||||
void dirsModelChanged();
|
||||
|
||||
private:
|
||||
QString m_dirPath;
|
||||
int m_dirDepth = 0;
|
||||
bool m_dirExpanded = true;
|
||||
bool m_dirVisible = true;
|
||||
AssetsLibraryDirsModel *m_dirsModel = nullptr;
|
||||
AssetsLibraryFilesModel *m_filesModel = nullptr;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -1,71 +0,0 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "assetslibrarydirsmodel.h"
|
||||
#include "assetslibrarymodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaProperty>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
AssetsLibraryDirsModel::AssetsLibraryDirsModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
// add roles
|
||||
const QMetaObject meta = AssetsLibraryDir::staticMetaObject;
|
||||
for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
|
||||
m_roleNames.insert(i, meta.property(i).name());
|
||||
}
|
||||
|
||||
QVariant AssetsLibraryDirsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_roleNames.contains(role))
|
||||
return m_dirs[index.row()]->property(m_roleNames[role]);
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool AssetsLibraryDirsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
// currently only dirExpanded property is updatable
|
||||
if (index.isValid() && m_roleNames.contains(role)) {
|
||||
QVariant currValue = m_dirs.at(index.row())->property(m_roleNames.value(role));
|
||||
if (currValue != value) {
|
||||
m_dirs.at(index.row())->setProperty(m_roleNames.value(role), value);
|
||||
if (m_roleNames.value(role) == "dirExpanded")
|
||||
AssetsLibraryModel::saveExpandedState(value.toBool(), m_dirs.at(index.row())->dirPath());
|
||||
emit dataChanged(index, index, {role});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int AssetsLibraryDirsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
|
||||
{
|
||||
return m_dirs.size();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AssetsLibraryDirsModel::roleNames() const
|
||||
{
|
||||
return m_roleNames;
|
||||
}
|
||||
|
||||
void AssetsLibraryDirsModel::addDir(AssetsLibraryDir *assetsDir)
|
||||
{
|
||||
m_dirs.append(assetsDir);
|
||||
}
|
||||
|
||||
const QList<AssetsLibraryDir *> AssetsLibraryDirsModel::assetsDirs() const
|
||||
{
|
||||
return m_dirs;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|