Merge remote-tracking branch 'origin/qds/dev'

Conflicts:
  src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
  src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp

Change-Id: Ieda6242b845387100022b08251283891fb0fbda7
This commit is contained in:
Tim Jenssen
2022-12-14 15:29:20 +01:00
255 changed files with 7612 additions and 2521 deletions

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -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

View File

@@ -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.

View File

@@ -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}

View File

@@ -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}

View File

@@ -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.

View File

@@ -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"

View File

@@ -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

View 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
*/

View File

@@ -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
*/

View 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}.
*/

View File

@@ -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

View File

@@ -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

View File

@@ -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" \

View File

@@ -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()
}
}

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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
}
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()
}
}
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()
}
}
}

View File

@@ -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()
}

View File

@@ -68,6 +68,8 @@ Item {
id: mouseRegion
anchors.fill: parent
tooltip: toolTip
onShowContextMenu: delegateRoot.showContextMenu()
onPressed: (mouse)=> {
allowTooltip = false

View File

@@ -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 = ""
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}
}
}
}

View File

@@ -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()
}
}
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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.")
}

View File

@@ -140,6 +140,7 @@ Column {
anchors.centerIn: parent
source: "image://materialEditor/preview"
cache: false
smooth: true
}
}

View File

@@ -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()

View File

@@ -15,6 +15,10 @@ PropertyEditorPane {
showState: true
}
InsightSection {
visible: insightEnabled
}
DynamicPropertiesSection {
propertiesModel: SelectionDynamicPropertiesModel {}
visible: !hasMultiSelection

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 {}
}
}
}

View File

@@ -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

View File

@@ -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 {}

View File

@@ -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))

View File

@@ -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

View File

@@ -57,7 +57,7 @@ TextInput {
myControl.focus = false
} else {
myControl.popup.open()
myControl.forceActiveFocus()
//myControl.forceActiveFocus()
}
} else {
textInput.forceActiveFocus()

View File

@@ -1,4 +1,7 @@
import QtQuick %{QtQuickVersion}
@if !%{IsQt6Project}
import QtQuick.Window %{QtQuickVersion}
@endif
import %{ApplicationImport}
@if %{UseVirtualKeyboard}
import QtQuick.VirtualKeyboard %{QtQuickVersion}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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}

View File

@@ -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)

View File

@@ -100,7 +100,7 @@ Project {
/* Required for deployment */
targetDirectory: "/opt/%{ProjectName}"
qdsVersion: "3.8"
qdsVersion: "3.9"
quickVersion: "%{QtQuickVersion}"

View File

@@ -1,4 +1,7 @@
import QtQuick %{QtQuickVersion}
@if !%{IsQt6Project}
import QtQuick.Window %{QtQuickVersion}
@endif
import %{ApplicationImport}
Window {

View File

@@ -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

View File

@@ -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[])

View File

@@ -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
)

View File

@@ -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}
)

View File

@@ -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"

View File

@@ -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"

View File

@@ -1,5 +1,8 @@
import QtQuick %{QtQuickVersion}
import QtQuick.Controls %{QtQuickVersion}
@if !%{IsQt6Project}
import QtQuick.Window %{QtQuickVersion}
@endif
import %{ImportModuleName} %{ImportModuleVersion}
Window {

View File

@@ -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"

View File

@@ -1,5 +1,8 @@
import QtQuick %{QtQuickVersion}
import QtQuick.Controls %{QtQuickVersion}
@if !%{IsQt6Project}
import QtQuick.Window %{QtQuickVersion}
@endif
import %{ImportModuleName} %{ImportModuleVersion}
Window {

View File

@@ -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"

View File

@@ -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
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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.")
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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) { }

View File

@@ -123,7 +123,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;

View File

@@ -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()

View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More