Merge "Merge remote-tracking branch 'origin/7.0' into 8.0" into 8.0

This commit is contained in:
The Qt Project
2022-07-01 13:27:55 +00:00
151 changed files with 2805 additions and 986 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -60,7 +60,7 @@
\li \l{Lists and Other Data Models}
\row
\li Timeline
\li \l{Creating Timelines}
\li \l{Creating a Timeline}
\if defined(qtdesignstudio)
\row
\li Event List

View File

@@ -39,6 +39,7 @@
\list
\li \l{Form Editor}
\li \l{3D Editor}
\li \l{Material Editor and Browser}
\li \l{Components}
\li \l{Assets}
\li \l{Navigator}

View File

@@ -26,7 +26,7 @@
/*!
\previouspage qtquick-form-editor.html
\page studio-3d-editor.html
\nextpage quick-components-view.html
\nextpage studio-material-editor.html
\title 3D Editor

View File

@@ -32,7 +32,7 @@
\title Materials and Shaders
\image studio-qtquick-3d-material.png "Material attached to model in Design mode"
\image studio-qtquick-3d-material.webp "Material attached to model in Design mode"
Materials and shaders define how object surfaces are rendered in \QDS and
live preview. As you change the properties of materials, new shaders are
@@ -40,6 +40,10 @@
a shader depends on a combination of the properties that are set on it, and
the context of the scene itself.
It is recommended that you use the \l {Material Editor and Browser} when
working with materials, but you can also add materials using the components
library.
The materials that you used in your imported scenes are imported to \QDS
as \l{Qt Quick 3D} components. When you add a View3D component, it contains
a DefaultMaterial component. You can use the following predefined Qt Quick
@@ -62,171 +66,12 @@
defines an image and how the image is mapped to meshes in a 3D scene. For
more information, see \l {Textures}.
You can modify material properties in the \uicontrol Properties view, as
instructed in the following sections. The availability of the properties
depends on the material type.
You can create and modify materials in
\uicontrol {Material Editor} and \uicontrol {Material Browser}. The availability
of the properties depends on the material type.
\image studio-qtquick-3d-default-material.png "DefaultMaterial properties"
To enable the material to use vertex colors from the mesh, select the
\uicontrol {Enable vertex colors} check box. These are multiplied
by any other colors specified for the material.
\image studio-qtquick-3d-default-material.webp "DefaultMaterial properties"
You can animate material properties in the \uicontrol Timeline view, as
instructed in \l {Creating Timeline Animations}.
\section1 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
occlude the objects behind them, select \uicontrol {SourceOver}.
For a lighter result, select \uicontrol Screen to blend colors using an
inverted multiply or \uicontrol ColorDodge to blend them by inverted
division. Color dodge procudes an even lighter result than screen.
For a darker result, select \uicontrol Multiply to blend colors using a
multiply or \uicontrol ColorBurn to blend them by inverted division, where
the result also is inverted. Color burn produces an even darker result than
multiply.
The screen and multiply modes are order-independent, so select them to
avoid \e popping, which can happen when using semi-opaque objects and
sorting the back and front faces or models.
For a result with higher contrast, select \uicontrol Overlay, which is a mix
of the multiply and screen modes.
\section1 Lighting Materials
To set the lighting method for generating a material, use the
\uicontrol Lighting property. Select \uicontrol {Fragment lighting} to
calculate diffuse and specular lighting for each rendered pixel. Some
effects, such as Fresnel or a bump map, require fragment lighting.
To skip lighting calculation, select \uicontrol {No lighting}. This is very
fast and quite effective when using image maps that do not need to be shaded
by lighting.
To set the base color for the material, use the \uicontrol {Diffuse Color}
property. You can either use the color picker or specify a RBG value. Set
the diffuse color to black to create purely-specular materials, such as
metals or mirrors. To apply a texture to a material, set it as the value of
the \uicontrol {Diffuse map} property. Using a texture with transparency
also applies the alpha channel as an \uicontrol {Opacity map}. You can set
the opacity of the material independently of the model as the value of the
\uicontrol Opacity property.
\section1 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
a scene with black ambient lighting, a material with an emissive factor of 0
is black where the light does not shine on it. Setting the emissive factor
to 1 shows the material in its diffuse color instead.
To use a Texture for specifying the emissive factor for different parts of
the material, set the \uicontrol {Emissive map} property. Using a grayscale
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
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
or set a RGB value to specify the color used to adjust specular reflections.
Use white for no effect
To use a color texture to modulate the amount and the color of specularity
across the surface of a material, set the \uicontrol {Specular map}
property. Set the \uicontrol {Specular amount} property to specify the
strength of specularity. This property does not affect the specular
reflection map, but it does affect the amount of reflections from a scene's
light probe.
\note Unless your mesh is high-resolution, you may need to use fragment
lighting to get good specular highlights from scene lights.
To determine how to calculate specular highlights for lights in the scene,
set the \uicontrol {Specular model}. In addition to the default mode, you
can use the GGX or Ward lighting model.
To use a Texture for specular highlighting on a material, set the
\uicontrol {Reflection map} property. When the texture is applied using
environmental mapping (not UV mapping), the map appears to be reflecting
from the environment as you rotate the model. Specular reflection maps are
an easy way to add a high-quality look at a relatively low cost.
To specify an image to use as the specular reflection map, set the
\uicontrol {Light probe} property.
Crisp images cause your material to look very glossy. The more you
blur your image, the softer your material appears.
To decrease head-on reflections (looking directly at the surface)
while maintaining reflections seen at grazing angles, set the
\uicontrol {Fresnel power} property. To select the angles to control,
set the \uicontrol {Index of refraction} property.
To control the size of the specular highlights generated from lights and the
clarity of reflections in general, set the \uicontrol {Specular roughness}
property. Larger values increase the roughness, while softening specular
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
Specify the properties in the \uicontrol {Bump/Normal} group to simulate
fine geometry displacement across the surface of the material. Set the
\uicontrol {Bump map} property to use a grayscale texture for the
simulation. Brighter pixels indicate raised regions.
To use a RGB image for simulation, set the \uicontrol {Normal map} property.
The RGB channels indicate XYZ normal deviations.
The amount of displacement is controlled by the \uicontrol {Bump amount}
property.
Bump and normal maps do not affect the silhouette of a model. To affect the
silhouette, set the \uicontrol {Displacement map} property. It specifies a
grayscale image used to offset the vertices of geometry across the surface
of the material. The \uicontrol {Displacement amount} property specifies the
offset amount.
\section1 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,
specify it as the value of the \uicontrol {Translucency map} property.
To specify the amount of light wrap for the translucency map, set the
\uicontrol {Diffuse light wrap} property. A value of 0 does not wrap the
light at all, while a value of 1 wraps the light all around the object.
To specify the amount of falloff for the translucency based on
the angle of the normals of the object to the light source, set
the \uicontrol {Translucency falloff} property.
\section1 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
points in the polygon appear in clockwise or counter-clockwise order when
projected onto the screen. If front-facing polygons have a clockwise
winding, but the polygon projected on the screen has a counter-clockwise
winding, the projected polygon is rotated to face away from the camera and
is not rendered. Culling makes rendering objects quicker and more efficient
by reducing the number of polygons to draw.
\section1 Applying Materials to Models
To apply materials to models, you should first delete the default material,
and then drag-and-drop a new material from \l Assets to a model component
in \l Navigator.
You can apply the same material to another component as well. Again,
delete the default material first. You should then select the component and
go to the \uicontrol Properties view. Find the \uicontrol Materials property,
select the \inlineimage icons/plus.png
icon, and choose the new material in the dropdown menu.
*/

View File

@@ -25,7 +25,7 @@
/*!
\page quick-components-view.html
\previouspage studio-3d-editor.html
\previouspage studio-material-editor.html
\nextpage quick-assets.html
\title Components

View File

@@ -38,9 +38,9 @@
\image studio-timeline-empty.png "Empty Timeline view"
Select the \inlineimage icons/plus.png
(\uicontrol {Add Timeline}) button to \l{Creating Timelines}
{create a timeline} and specify settings for it in the
\uicontrol {Timeline Settings} dialog.
(\uicontrol {Add Timeline}) button to
\l{Creating a Timeline}{create a timeline} and specify settings for it in
the \uicontrol {Timeline Settings} dialog.
\image studio-timeline-settings.png "Timeline Settings dialog"
@@ -124,11 +124,11 @@
\li \inlineimage icons/animation.png
\li Opens the \uicontrol {Timeline Settings} dialog for editing
timeline settings.
\li \l{Creating Timelines}
\li \l{Creating a Timeline}
\row
\li Timeline ID
\li Displays the ID of the current timeline.
\li \l{Creating Timelines}
\li \l{Creating a Timeline}
\row
\li \inlineimage icons/to_first_frame.png
\li \uicontrol {To Start (Home)} moves to the first frame on the
@@ -170,7 +170,7 @@
\li Specifies the first frame of the timeline. Negative values are
allowed. The difference between the start frame and the end frame
determines the duration of the animation.
\li \l{Creating Timelines}
\li \l{Creating a Timeline}
\row
\li \inlineimage icons/zoom_small.png
\li \uicontrol {Zoom Out} (\key Ctrl+-) zooms out of the view.
@@ -189,7 +189,7 @@
the start frame and the end frame determines the duration of the
animation, so if the start frame is 0, the end frame equals the
duration.
\li \l{Creating Timelines}
\li \l{Creating a Timeline}
\row
\li State Name
\li Displays the name of the current state.

View File

@@ -30,18 +30,19 @@
\title Creating Timeline Animations
You can create timeline and keyframe based animations for linear
You can create timelines and keyframe-based animations for linear
interpolation through intermediate values at specified keyframes
instead of immediately changing to the target value.
\section1 Creating Timelines
You can also bind the timeline to a property value of a component such as
a slider and control the animation this way.
You specify settings for the timeline and for running the animation in the
\uicontrol {Timeline Settings} dialog. The \uicontrol Animation radio button
is selected for a timeline animation and the \uicontrol {Expression binding}
radio button for a \l{Setting Bindings}{property animation}.
\section1 Creating an Animation
\image studio-timeline-settings.png "Timeline Settings dialog"
To create an animation, whether it's a keyframe animation or an animation
bound to a property value, you first need to create a timeline.
\section2 Creating a Timeline
To create a timeline to animate a UI component:
@@ -50,95 +51,97 @@
(\uicontrol {Add Timeline}) button to specify settings
for the timeline and running the animation
in the \uicontrol {Timeline Settings} dialog.
\li In the \uicontrol {Timeline ID} field, enter an ID that describes
the animated component.
\li In the \uicontrol {Start frame} field, set the first frame of the
timeline. Negative values are allowed.
\li In the \uicontrol {End frame} field, set the last frame of the
timeline.
\li In the \uicontrol {Animation ID} field, enter an ID for the
animation.
\li Select the \uicontrol {Running in Base State} check box to run the
animation when the base state is applied. Deselect the check box
if you want to run the animation when some other state is applied.
For more information, see \l{Binding Animations to States}.
\li In the \uicontrol {Start frame} field, set the first frame of the
animation.
\li In the \uicontrol {End frame} field, set the last frame of the
animation.
\li In the \uicontrol {Duration} field, set the length of the
animation from the start frame to the end frame. If you set a
shorter duration than the number of frames, frames are left out
from the end of the animation when viewing it.
\li Select the \uicontrol Continuous check box to loop the animation
indefinitely.
\li In the \uicontrol Loops field, select the number of times to run
the animation as a loop. The default number of loops is one, which
means that you must restart the animation to see it again
\li Select the \uicontrol {Ping pong} check box to play the animation
backwards back to the beginning when it reaches the end.
\li In the \uicontrol Finished field, select the state
to apply when the animation finishes.
\li On the \uicontrol {Timeline Settings} tab:
\list
\li In the \uicontrol {Timeline ID} field, enter an id that
describes the timeline.
\li In the \uicontrol {Start frame} field, set the first frame
of the timeline. Negative values are allowed.
\li In the \uicontrol {End frame} field, set the last frame
of the timeline.
\image timeline-settings-dialog.png
\endlist
\li On the \uicontrol {Animation Settings} tab:
\list
\li In the \uicontrol {Animation ID} field, enter an ID for the
animation.
\li Optional. Select the \uicontrol {Running in Base State}
check box to run the animation when the base state is applied.
Clear the check box to run the animation when some other state
is applied. For more information, see
\l{Binding Animations to States}.
\li In the \uicontrol {Start frame} field, set the first frame
of the animation.
\li In the \uicontrol {End frame} field, set the last frame of
the animation.
\li In the \uicontrol {Duration} field, set the length of the
animation in milliseconds.
\li Optional. Select the \uicontrol Continuous check box to
loop the animation indefinitely.
\li Optional. In the \uicontrol Loops field, set the number of
times to run the animation. The default number of
loops is one, which means that you must restart the animation
to see it again.
\li Optional. Select the \uicontrol {Ping pong} check box to
play the animation backwards back to the beginning when it
reaches the end.
\li Optional. In the \uicontrol Finished field, select the state
to transition to when the animation finishes.
\endlist
\li Select \uicontrol Close to close the dialog and save the settings.
\endlist
To create additional timelines, select the \inlineimage icons/plus.png
(\uicontrol {Add Timeline}) button next to the
\uicontrol {Timeline Settings} tab.
Now, with the settings set for the timeline and the animation, you
set the keyframes for the properties to animate.
To specify settings for running timeline animations, select the
\inlineimage icons/plus.png
(\uicontrol {Add Animation}) button next to the
\uicontrol {Animation Settings} tab. For example, you could create
settings for running a part of the timeline animation between specified
frames or for running the animation backwards from the last frame to the
first.
\section3 Creating Additional Timelines
To modify the settings, select the \inlineimage icons/animation.png
(\uicontrol {Timeline Settings (S)}) button on the \l{Timeline Toolbar}
{toolbar} (or press \key S) in the \l Timeline view.
You can create more than one timeline. The purpose of several timelines is
to use different timelines in different states.
\section2 Binding Animations to States
To create a timeline for a second state:
The table at the bottom of the \uicontrol {Timeline Settings} dialog lists
the available states. Double-click the values in the \uicontrol Timeline
and \uicontrol Animation column to bind the states to animations. In the
\uicontrol {Fixed Frame} column, you can bind the states that don't have
animations to fixed frames.
\list 1
\li In \uicontrol {Timeline}, open the \uicontrol {Timeline Settings}
dialog.
\li Next to the \uicontrol {Timeline Settings} tab, select
\inlineimage icons/plus.png
. This creates another timeline.
\li In the table below the \uicontrol {Animation Settings} tab, set the
Timeline for the state where you want to use it.
\image timeline-settings-dialog-second.png
\endlist
To set the keyframe values for the timeline you created, first select the
state in \uicontrol {States} and the timeline is available in
\uicontrol{Timelines}.
\section1 Managing Keyframes
To animate components in the \l Timeline view, move to a frame
on the timeline and specify changes in the values of a property. \QC
automatically adds keyframes between two keyframes and sets their values
evenly to create an appearance of movement or transformation.
\image studio-timeline-with-tracks.png "Timeline view"
\image timeline-states.png
\section2 Setting Keyframe Values
You can insert keyframes for all the properties of all the components that
you want to animate first, and then record the changes in their values by
selecting the \inlineimage icons/local_record_keyframes.png
(\uicontrol {Per Property Recording}) button for one property at a time.
For example, you can hide and show components by turning their visibility
off and on or by setting their opacity to 0 or 1.
When you create a timeline, \QDS creates one animation for the timeline.
You can create as many animations for a timeline as you want. For example,
you can create animations to run just a small section of the timeline or to
run the timeline backwards.
You can also select the \uicontrol {Auto Key (K)} button (or press \key K)
to record changes in property values, but you need to be more careful about
which property values you are changing to avoid surprises.
To animate components in the \l Timeline view, you set keyframe values for
the property to animate. \QDS automatically adds keyframes between two
keyframes and sets their values evenly to create, for example, movement or
transformation.
To record the changes of property values:
To set keyframe values for a component property:
\list 1
\li In the \l Navigator view, select the component to animate.
\li In the \l Properties view, select \inlineimage icons/action-icon.png
(\uicontrol Actions) > \uicontrol {Insert Keyframe} for the property
that you want to animate.
\image timeline-insert-keyframe.png
\li In the \l Timeline view, select the
\uicontrol {Per Property Recording} button
to start recording property changes.
\li Check that the playhead is in frame 0 and enter the value of the
\image timeline-per-property-recording.png
\li Ensure that the playhead is in frame 0 and enter the value of the
property in the field next to the property name on the timeline.
Press \key Enter to save the value.
\li Move the playhead to another frame on the timeline and specify
@@ -148,11 +151,67 @@
\uicontrol {Per Property Recording} again to stop recording.
\endlist
\section2 Binding a Timeline to a Property
When you bind a timeline to a component property, the animation's
current frame is controlled by the value of the property.
In this example, you bind the timeline to a slider component.
With a timeline created and keyframe values set:
\list 1
\li From \uicontrol {Components}, drag a slider to
\uicontrol {Form Editor} or \uicontrol {Navigator}.
\li In \uicontrol {Navigator}, select \e slider and in
\uicontrol {Properties}, set:
\list
\li \uicontrol To to 1000.
\note The \uicontrol From and \uicontrol To values of the slider
should match the \uicontrol {Start Frame} and
\uicontrol {End Frame} values of the timeline if you want to
control the complete animation with the slider.
\endlist
\li In the \uicontrol {Timeline Settings} dialog, select
\inlineimage icons/minus.png
next to the \uicontrol {Animation Settings} tab to delete the
animation. If you have several animations, delete all.
\li In \uicontrol {Expression binding}, enter \c {slider.value}.
\image timeline-settings-property-binding.png
\endlist
\section2 Binding Animations to States
You can bind animations to states, this means that the animation will run
when you enter the state.
To bind an animation to a state:
\list 1
\li In the table at the bottom of the \uicontrol {Timeline Settings}
dialog lists:
\list
\li Double-click the value in the \uicontrol Timeline field and select
the timeline with the animation you want to bind to the state.
\li Double-click the value in the \uicontrol Animation field and
select the animation you want to bind to the state.
\image timeline-bind-animation-state.png
\endlist
\endlist
To bind a state to a certain keyframe in an animation without running the
animation, set the keyframe in the \uicontrol{Fixed Frame} field.
\section1 Managing Keyframes
\image studio-timeline-with-tracks.png "Timeline view"
\section2 Editing Keyframes
To remove all the changes you recorded for a property, right-click the
property name on the timeline and select \uicontrol {Remove Property}.
To add keyframes to the keyframe track of a component at the current
position of the playhead, select \uicontrol {Add Keyframes at Current Frame}.
position of the playhead, right-click the component name on the timeline and
select \uicontrol {Add Keyframes at Current Frame}.
Keyframes are marked on the timeline by using \l{keyframe_marker}{markers}
of different colors and shapes, depending on whether they are active or
@@ -162,7 +221,7 @@
\section2 Editing Keyframe Values
To fine-tune the value of a keyframe, double-click a keyframe marker or
select \uicontrol {Edit Keyframe} in the context menu.
right-click it and select \uicontrol {Edit Keyframe} in the context menu.
The \uicontrol {Edit Keyframe} dialog displays the name of the property
you are animating and its current value at the frame specified in the
@@ -173,27 +232,36 @@
\section2 Copying Keyframes
You can copy the keyframes from the keyframe track for a component and
paste them to the keyframe track of another component. To copy all
keyframes from one track to another one, first right-click the component ID
and select \uicontrol {Copy All Keyframes} in the context menu.
Then right-click the other component ID, and select
\uicontrol {Paste Keyframes} in the context menu.
paste them to the keyframe track of another component.
To copy all keyframes from one track to another one:
\list 1
\li Right-click the component ID and select
\uicontrol {Copy All Keyframes} in the context menu.
\li Right-click the other component ID, and select
\uicontrol {Paste Keyframes} in the context menu.
\endlist
\section2 Deleting Keyframes
To delete the selected keyframe, select \uicontrol {Delete Keyframe} in the
context menu.
To delete a keyframe, right-click it and select \uicontrol {Delete Keyframe}
in the context menu.
To delete all keyframes from the selected component, select
To delete all keyframes from the selected component, right-click the
component name in \uicontrol {Timeline} and select
\uicontrol {Delete All Keyframes} in the context menu.
\section1 Viewing the Animation
You can view the animation on the canvas by moving the playhead along the
timeline.
To preview your animation, do one of the following in the
\uicontrol{Timeline} view:
\list
\li Drag the playhead along the timeline.
\li Select \inlineimage icons/start_playback.png
button or press \key Space.
\endlist
To preview the animation, select the \uicontrol {Play (Space)}
button or press \key Space. To preview the whole UI, select the
To preview the whole UI, select the
\inlineimage icons/live_preview.png
(\uicontrol {Show Live Preview}) button on the canvas toolbar
or press \key {Alt+P}.

View File

@@ -0,0 +1,266 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
**
** 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 Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
**
****************************************************************************/
/*!
\page studio-material-editor.html
\previouspage studio-3d-editor.html
\nextpage quick-components-view.html
\title Material Editor and Browser
In the \uicontrol {Material Editor} and \uicontrol {Material Browser} views,
you create and manage materials.
\image material-editor-browser.webp "Material Editor and Browser"
\section1 Creating a Material
To create a new material, do one of the following:
\list
\li In \uicontrol {Material Browser}, select \inlineimage icons/plus.png
.
\li In \uicontrol {Material Editor}, select \inlineimage icons/plus.png
.
\endlist
\section1 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}
is closed, open it in one of the following ways:
\list
\li In \uicontrol{Navigator}, right-click an object that has the material
assigned to it and select \uicontrol {Edit Material}.
\li In \uicontrol{Material Browser}, double-click a material.
\endlist
\section1 Assigning a Material to an Object
To assign a material to a 3D object in your project, first select the object
in \uicontrol Navigator or \uicontrol {3D Editor}. Then, do one of the
following:
\list
\li In \uicontrol {Material Browser}, right-click the material and select
\uicontrol {Apply to Selected}. If there already is any material assigned
to the object, you can select whether to replace the material or to add
another material to the object.
\li In \uicontrol {Material Editor}, select
\inlineimage icons/apply-material.png
. This replaces any material already assigned to the object.
\endlist
\section1 Removing a Material from an Object
To remove an assigned material from an object:
\list 1
\li In \uicontrol{Navigator}, select the object.
\li In \uicontrol{Properties}, select
\inlineimage icons/close.png
next to the material.
\image materials-remove-material.png
\endlist
\section1 Using Texture Maps
In \QDS you can add many different texture maps to your material.
To add a texture map to a material:
\list 1
\li Select the material in \uicontrol{Material Browser}.
\li From \uicontrol {Assets}, drag an image to the correct map field
in \uicontrol {Material Editor}. For example, to add a diffuse map, drag
the image to \uicontrol{Diffuse Map} in \uicontrol{Material Editor}.
\endlist
\section2 Using a Reflection Map for Environmental Mapping
To use a texture for environmental mapping, you need to set the mapping
mode to \e {environment}.
To add a reflection map for environmental mapping to a material:
\list 1
\li Select the material in \uicontrol {Material Browser}.
\li From \uicontrol{Assets}, drag an image to
\uicontrol{Reflection Map}.
\li In \uicontrol {Navigator}, select
\inlineimage icons/filtericon.png
and then clear \uicontrol {Show Only Visible Components}. Now the
texture you just added to the material is visible in
\uicontrol {Navigator}.
\image navigator-material-texture.png
\li In \uicontrol {Navigator}, select the texture.
\li In \uicontrol {Properties}, set \uicontrol {Texture Mapping} to
\uicontrol {Environment}.
\endlist
\section1 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
occlude the objects behind them, select \uicontrol {SourceOver}.
For a lighter result, select \uicontrol Screen to blend colors using an
inverted multiply or \uicontrol ColorDodge to blend them by inverted
division. Color dodge produces an even lighter result than screen.
For a darker result, select \uicontrol Multiply to blend colors using a
multiply or \uicontrol ColorBurn to blend them by inverted division, where
the result also is inverted. Color burn produces an even darker result than
multiply.
The screen and multiply modes are order-independent, so select them to
avoid \e popping, which can happen when using semi-opaque objects and
sorting the back and front faces or models.
For a result with higher contrast, select \uicontrol Overlay, which is a mix
of the multiply and screen modes.
\section1 Lighting Materials
To set the lighting method for generating a material, use the
\uicontrol Lighting property. Select \uicontrol {Fragment lighting} to
calculate diffuse and specular lighting for each rendered pixel. Some
effects, such as Fresnel or a bump map, require fragment lighting.
To skip lighting calculation, select \uicontrol {No lighting}. This is very
fast and quite effective when using image maps that do not need to be shaded
by lighting.
To set the base color for the material, use the \uicontrol {Diffuse Color}
property. You can either use the color picker or specify a RBG value. Set
the diffuse color to black to create purely-specular materials, such as
metals or mirrors. To apply a texture to a material, set it as the value of
the \uicontrol {Diffuse map} property. Using a texture with transparency
also applies the alpha channel as an \uicontrol {Opacity map}. You can set
the opacity of the material independently of the model as the value of the
\uicontrol Opacity property.
\section1 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
a scene with black ambient lighting, a material with an emissive factor of 0
is black where the light does not shine on it. Setting the emissive factor
to 1 shows the material in its diffuse color instead.
To use a Texture for specifying the emissive factor for different parts of
the material, set the \uicontrol {Emissive map} property. Using a grayscale
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
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
or set a RGB value to specify the color used to adjust specular reflections.
Use white for no effect.
To use a color texture to modulate the amount and the color of specularity
across the surface of a material, set the \uicontrol {Specular map}
property. Set the \uicontrol {Specular amount} property to specify the
strength of specularity. This property does not affect the specular
reflection map, but it does affect the amount of reflections from a scene's
light probe.
\note Unless your mesh is high-resolution, you may need to use fragment
lighting to get good specular highlights from scene lights.
To determine how to calculate specular highlights for lights in the scene,
set the \uicontrol {Specular model}. In addition to the default mode, you
can use the GGX or Ward lighting model.
To use a Texture for specular highlighting on a material, set the
\uicontrol {Reflection map} property. When the texture is applied using
environmental mapping (not UV mapping), the map appears to be reflecting
from the environment as you rotate the model. Specular reflection maps are
an easy way to add a high-quality look at a relatively low cost.
To specify an image to use as the specular reflection map, set the
\uicontrol {Light probe} property.
Crisp images cause your material to look very glossy. The more you
blur your image, the softer your material appears.
To decrease head-on reflections (looking directly at the surface)
while maintaining reflections seen at grazing angles, set the
\uicontrol {Fresnel power} property. To select the angles to control,
set the \uicontrol {Index of refraction} property.
To control the size of the specular highlights generated from lights and the
clarity of reflections in general, set the \uicontrol {Specular roughness}
property. Larger values increase the roughness, while softening specular
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
Specify the properties in the \uicontrol {Bump/Normal} group to simulate
fine geometry displacement across the surface of the material. Set the
\uicontrol {Bump map} property to use a grayscale texture for the
simulation. Brighter pixels indicate raised regions.
To use an image for simulation, set the \uicontrol {Normal map} property.
The RGB channels indicate XYZ normal deviations.
The amount of displacement is controlled by the \uicontrol {Bump amount}
property.
Bump and normal maps do not affect the silhouette of a model. To affect the
silhouette, set the \uicontrol {Displacement map} property. It specifies a
grayscale image used to offset the vertices of geometry across the surface
of the material. The \uicontrol {Displacement amount} property specifies the
offset amount.
\section1 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,
specify it as the value of the \uicontrol {Translucency map} property.
To specify the amount of light wrap for the translucency map, set the
\uicontrol {Diffuse light wrap} property. A value of 0 does not wrap the
light at all, while a value of 1 wraps the light all around the object.
To specify the amount of falloff for the translucency based on
the angle of the normals of the object to the light source, set
the \uicontrol {Translucency falloff} property.
\section1 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
points in the polygon appear in clockwise or counter-clockwise order when
projected onto the screen. If front-facing polygons have a clockwise
winding, but the polygon projected on the screen has a counter-clockwise
winding, the projected polygon is rotated to face away from the camera and
is not rendered. Culling makes rendering objects quicker and more efficient
by reducing the number of polygons to draw.
*/

View File

@@ -61,7 +61,8 @@ public:
QSize captureImageMinimumSize,
QSize captureImageMaximumSize,
qint32 stateInstanceId,
const QList<QColor> &edit3dBackgroundColor)
const QList<QColor> &edit3dBackgroundColor,
const QColor &edit3dGridColor)
: instances(instanceContainer)
, reparentInstances(reparentContainer)
, ids(idVector)
@@ -78,6 +79,7 @@ public:
, captureImageMaximumSize(captureImageMaximumSize)
, stateInstanceId{stateInstanceId}
, edit3dBackgroundColor{edit3dBackgroundColor}
, edit3dGridColor{edit3dGridColor}
{}
friend QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command)
@@ -98,6 +100,7 @@ public:
out << command.captureImageMinimumSize;
out << command.captureImageMaximumSize;
out << command.edit3dBackgroundColor;
out << command.edit3dGridColor;
return out;
}
@@ -120,6 +123,7 @@ public:
in >> command.captureImageMinimumSize;
in >> command.captureImageMaximumSize;
in >> command.edit3dBackgroundColor;
in >> command.edit3dGridColor;
return in;
}
@@ -141,6 +145,7 @@ public:
QSize captureImageMaximumSize;
qint32 stateInstanceId = 0;
QList<QColor> edit3dBackgroundColor;
QColor edit3dGridColor;
};
QDebug operator<<(QDebug debug, const CreateSceneCommand &command);

View File

@@ -57,6 +57,7 @@ public:
ParticlesRestart,
ParticlesSeek,
SelectBackgroundColor,
SelectGridColor,
ResetBackgroundColor,
};

View File

@@ -13,7 +13,7 @@
<file>mockfiles/images/directional@2x.png</file>
<file>mockfiles/images/point.png</file>
<file>mockfiles/images/point@2x.png</file>
<file>mockfiles/images/floor_tex.png</file>
<file>mockfiles/images/static_floor.png</file>
<file>mockfiles/images/spot.png</file>
<file>mockfiles/images/spot@2x.png</file>
<file>mockfiles/qt5/AdjustableArrow.qml</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -47,6 +47,7 @@ Item {
property alias contentItem: contentItem
property color backgroundGradientColorStart: "#222222"
property color backgroundGradientColorEnd: "#999999"
property color gridColor: "#aaaaaa"
enum SelectionMode { Item, Group }
enum TransformMode { Move, Rotate, Scale }
@@ -96,12 +97,14 @@ Item {
{"usePerspective": usePerspective,
"showSceneLight": showEditLight,
"showGrid": showGrid,
"gridColor": gridColor,
"importScene": activeScene,
"cameraZoomFactor": cameraControl._zoomFactor,
"z": 1});
editView.usePerspective = Qt.binding(function() {return usePerspective;});
editView.showSceneLight = Qt.binding(function() {return showEditLight;});
editView.showGrid = Qt.binding(function() {return showGrid;});
editView.gridColor = Qt.binding(function() {return gridColor;});
editView.cameraZoomFactor = Qt.binding(function() {return cameraControl._zoomFactor;});
selectionBoxes.length = 0;
@@ -217,10 +220,19 @@ Item {
function updateViewStates(viewStates)
{
if ("selectBackgroundColor" in viewStates) {
var color = viewStates.selectBackgroundColor
backgroundGradientColorStart = color[0];
backgroundGradientColorEnd = color[1];
if (Array.isArray(viewStates.selectBackgroundColor)) {
var colors = viewStates.selectBackgroundColor
backgroundGradientColorStart = colors[0];
backgroundGradientColorEnd = colors[1];
} else {
var color = viewStates.selectBackgroundColor
backgroundGradientColorStart = color;
backgroundGradientColorEnd = color;
}
}
if ("selectGridColor" in viewStates)
viewRoot.gridColor = viewStates.selectGridColor
}
// If resetToDefault is true, tool states not specifically set to anything will be reset to

View File

@@ -33,6 +33,7 @@ Node {
property alias lines: gridGeometry.lines
property alias step: gridGeometry.step
property alias subdivAlpha: subGridMaterial.opacity
property alias gridColor: mainGridMaterial.diffuseColor
eulerRotation.x: 90

View File

@@ -45,10 +45,6 @@ View3D {
Node {
DirectionalLight {
shadowMapQuality: Light.ShadowMapQualityMedium
shadowFilter: 20
shadowFactor: 21
castsShadow: true
eulerRotation.x: -26
eulerRotation.y: -57
}
@@ -68,25 +64,5 @@ View3D {
source: "#Sphere"
materials: previewMaterial
}
Model {
id: floorModel
source: "#Rectangle"
scale.y: 8
scale.x: 8
eulerRotation.x: -90
materials: floorMaterial
DefaultMaterial {
id: floorMaterial
diffuseMap: floorTex
Texture {
id: floorTex
source: "../images/floor_tex.png"
scaleU: floorModel.scale.x
scaleV: floorModel.scale.y
}
}
}
}
}

View File

@@ -123,14 +123,13 @@ Item {
anchors.fill: parent
}
Rectangle {
// We can use static image in Qt5 as only small previews will be generated
Image {
id: backgroundRect
anchors.fill: parent
z: -1
gradient: Gradient {
GradientStop { position: 1.0; color: "#222222" }
GradientStop { position: 0.0; color: "#999999" }
}
source: "../images/static_floor.png"
fillMode: Image.Stretch
}
}
}

View File

@@ -37,7 +37,7 @@ View3D {
function fitToViewPort(closeUp)
{
// The magic number is the distance from camera default pos to origin
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, sourceModel, root,
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, model, root,
1040, closeUp);
}
@@ -70,7 +70,7 @@ View3D {
materials: [
DefaultMaterial {
diffuseColor: "#4aee45"
diffuseColor: "#999999"
}
]
}

View File

@@ -32,6 +32,7 @@ View3D {
property bool usePerspective: false
property alias showSceneLight: sceneLight.visible
property alias showGrid: helperGrid.visible
property alias gridColor: helperGrid.gridColor
property alias sceneHelpers: sceneHelpers
property alias perspectiveCamera: scenePerspectiveCamera
property alias orthoCamera: sceneOrthoCamera

View File

@@ -48,6 +48,7 @@ Item {
property alias contentItem: contentItem
property color backgroundGradientColorStart: "#222222"
property color backgroundGradientColorEnd: "#999999"
property color gridColor: "#aaaaaa"
enum SelectionMode { Item, Group }
enum TransformMode { Move, Rotate, Scale }
@@ -100,12 +101,14 @@ Item {
{"usePerspective": usePerspective,
"showSceneLight": showEditLight,
"showGrid": showGrid,
"gridColor": gridColor,
"importScene": activeScene,
"cameraZoomFactor": cameraControl._zoomFactor,
"z": 1});
editView.usePerspective = Qt.binding(function() {return usePerspective;});
editView.showSceneLight = Qt.binding(function() {return showEditLight;});
editView.showGrid = Qt.binding(function() {return showGrid;});
editView.gridColor = Qt.binding(function() {return gridColor;});
editView.cameraZoomFactor = Qt.binding(function() {return cameraControl._zoomFactor;});
selectionBoxes.length = 0;
@@ -211,10 +214,19 @@ Item {
function updateViewStates(viewStates)
{
if ("selectBackgroundColor" in viewStates) {
var color = viewStates.selectBackgroundColor
backgroundGradientColorStart = color[0];
backgroundGradientColorEnd = color[1];
if (Array.isArray(viewStates.selectBackgroundColor)) {
var colors = viewStates.selectBackgroundColor
backgroundGradientColorStart = colors[0];
backgroundGradientColorEnd = colors[1];
} else {
var color = viewStates.selectBackgroundColor
backgroundGradientColorStart = color;
backgroundGradientColorEnd = color;
}
}
if ("selectGridColor" in viewStates)
viewRoot.gridColor = viewStates.selectGridColor
}
// If resetToDefault is true, tool states not specifically set to anything will be reset to

View File

@@ -33,6 +33,7 @@ Node {
property alias lines: gridGeometry.lines
property alias step: gridGeometry.step
property alias subdivAlpha: subGridMaterial.opacity
property alias gridColor: mainGridMaterial.diffuseColor
eulerRotation.x: 90

View File

@@ -45,10 +45,6 @@ View3D {
Node {
DirectionalLight {
shadowMapQuality: Light.ShadowMapQualityMedium
shadowFilter: 20
shadowFactor: 21
castsShadow: true
eulerRotation.x: -26
eulerRotation.y: -57
}
@@ -70,24 +66,5 @@ View3D {
materials: previewMaterial
}
Model {
id: floorModel
source: "#Rectangle"
scale.y: 8
scale.x: 8
eulerRotation.x: -90
materials: floorMaterial
DefaultMaterial {
id: floorMaterial
diffuseMap: floorTex
Texture {
id: floorTex
source: "../images/floor_tex.png"
scaleU: floorModel.scale.x
scaleV: floorModel.scale.y
}
}
}
}
}

View File

@@ -126,6 +126,50 @@ Item {
GradientStop { position: 1.0; color: "#222222" }
GradientStop { position: 0.0; color: "#999999" }
}
// Use View3D instead of static image to make background look good on all resolutions
View3D {
anchors.fill: parent
environment: sceneEnv
SceneEnvironment {
id: sceneEnv
antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.High
}
DirectionalLight {
eulerRotation.x: -26
eulerRotation.y: -57
}
PerspectiveCamera {
y: 125
z: 120
eulerRotation.x: -31
clipNear: 1
clipFar: 1000
}
Model {
id: floorModel
source: "#Rectangle"
scale.y: 8
scale.x: 8
eulerRotation.x: -90
materials: floorMaterial
DefaultMaterial {
id: floorMaterial
diffuseMap: floorTex
Texture {
id: floorTex
source: "../images/floor_tex.png"
scaleU: floorModel.scale.x
scaleV: floorModel.scale.y
}
}
}
}
}
}
}

View File

@@ -37,7 +37,7 @@ View3D {
function fitToViewPort(closeUp)
{
// The magic number is the distance from camera default pos to origin
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, sourceModel, root,
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, model, root,
1040, closeUp);
}
@@ -70,7 +70,7 @@ View3D {
materials: [
DefaultMaterial {
diffuseColor: "#4aee45"
diffuseColor: "#999999"
}
]
}

View File

@@ -32,6 +32,7 @@ View3D {
property bool usePerspective: false
property alias showSceneLight: sceneLight.visible
property alias showGrid: helperGrid.visible
property alias gridColor: helperGrid.gridColor
property alias sceneHelpers: sceneHelpers
property alias perspectiveCamera: scenePerspectiveCamera
property alias orthoCamera: sceneOrthoCamera

View File

@@ -812,7 +812,7 @@ QVector3D GeneralHelper::pivotScenePosition(QQuick3DNode *node) const
// Calculate bounds for given node, including all child nodes.
// Returns true if the tree contains at least one Model node.
bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds,
QVector3D &maxBounds, bool recursive)
QVector3D &maxBounds)
{
if (!node) {
const float halfExtent = 100.f;
@@ -825,7 +825,7 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec
auto nodePriv = QQuick3DObjectPrivate::get(node);
auto renderNode = static_cast<QSSGRenderNode *>(nodePriv->spatialNode);
if (recursive && renderNode) {
if (renderNode) {
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty))
renderNode->calculateLocalTransform();
@@ -850,7 +850,7 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec
if (auto childNode = qobject_cast<QQuick3DNode *>(child)) {
QVector3D newMinBounds = minBounds;
QVector3D newMaxBounds = maxBounds;
bool childHasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true);
bool childHasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds);
// Ignore any subtrees that do not have Model in them as we don't need those
// for visual bounds calculations
if (childHasModel) {

View File

@@ -130,7 +130,7 @@ private:
void handlePendingToolStateUpdate();
QVector3D pivotScenePosition(QQuick3DNode *node) const;
bool getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds,
QVector3D &maxBounds, bool recursive = false);
QVector3D &maxBounds);
QTimer m_overlayUpdateTimer;
QTimer m_toolStateUpdateTimer;

View File

@@ -198,8 +198,7 @@ void IconRenderer::finishCreateIcon()
render(saveFile);
// Allow little time for file operations to finish
QTimer::singleShot(1000, qGuiApp, &QGuiApplication::quit);
QTimer::singleShot(0, qGuiApp, &QGuiApplication::quit);
}
void IconRenderer::render(const QString &fileName)

View File

@@ -848,6 +848,18 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D(bool timerC
m_activeSceneIdUpdateTimer.stop();
}
// We may have to substitute another scene to work around QTBUG-103316
// The worked around issue is that if a material is used in multiple scenes, there is some
// kind of ownership for it in the first View3D that uses it, so if that view is not rendered
// first, the material will not be properly initialized for other views using it.
// To make materials work properly, we ensure that views are rendered at least once in the
// order they appear in the scene.
if (!m_priorityView3DsToRender.isEmpty()) {
QObject *sceneRoot = find3DSceneRoot(m_priorityView3DsToRender.first());
if (sceneRoot)
activeSceneVar = objectToVariant(sceneRoot);
}
QMetaObject::invokeMethod(m_editView3DData.rootItem, "setActiveScene", Qt::QueuedConnection,
Q_ARG(QVariant, activeSceneVar),
Q_ARG(QVariant, QVariant::fromValue(sceneId)));
@@ -1012,7 +1024,7 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView()
// If we have only one or no render queued, send the result to the creator side.
// Otherwise, we'll hold on that until we have rendered all pending frames to ensure sent
// results are correct.
if (m_need3DEditViewRender <= 1) {
if (m_priorityView3DsToRender.isEmpty() && m_need3DEditViewRender <= 1) {
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::Render3DView,
QVariant::fromValue(imgContainer)});
#ifdef QUICK3D_PARTICLES_MODULE
@@ -1023,6 +1035,25 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView()
#endif
}
if (!m_priorityView3DsToRender.isEmpty()) {
static int tryCounter = 0;
QObject *sceneRoot = find3DSceneRoot(m_priorityView3DsToRender.first());
bool needAnotherRender = false;
if (sceneRoot) {
// Active scene is updated asynchronously, so verify we are actually rendering
// the correct priority scene
QObject *activeScene = QQmlProperty::read(m_editView3DData.rootItem, "activeScene").value<QObject *>();
needAnotherRender = activeScene != sceneRoot;
}
if (!needAnotherRender || ++tryCounter > 10) {
m_priorityView3DsToRender.removeFirst();
updateActiveSceneToEditView3D();
tryCounter = 0;
}
++m_need3DEditViewRender;
}
if (m_need3DEditViewRender > 0) {
// We queue another render even if the requested render count was one, because another
// render is needed to ensure gizmo geometries are properly updated.
@@ -1059,6 +1090,13 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView()
{
// This crashes on Qt 6.0.x due to QtQuick3D issue, so the preview generation is disabled
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
if (!m_priorityView3DsToRender.isEmpty()) {
// Postpone any preview renders until we have rendered the priority views to ensure
// materials in material library are properly initialized
m_renderModelNodeImageViewTimer.start(17);
return;
}
RequestModelNodePreviewImageCommand cmd = *m_modelNodePreviewImageCommands.begin();
ServerNodeInstance instance;
if (cmd.renderItemId() >= 0)
@@ -1578,6 +1616,8 @@ void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList<ServerNodeInst
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DViewport")) {
QObject *obj = instance.internalObject();
if (!m_editView3DSetupDone)
m_priorityView3DsToRender.append(obj); // Workaround for quick3d bug QTBUG-103316
if (!m_view3Ds.contains(obj)) {
m_view3Ds << obj;
QObject::connect(obj, SIGNAL(widthChanged()), this, SLOT(handleView3DSizeChange()));
@@ -1746,7 +1786,7 @@ QObject *Qt5InformationNodeInstanceServer::find3DSceneRoot(QObject *obj) const
}
void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeInstance> &instanceList,
const QHash<QString, QVariantMap> &toolStates)
const CreateSceneCommand &command)
{
#ifdef QUICK3D_MODULE
if (!m_editView3DData.rootItem)
@@ -1779,6 +1819,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D(true);
});
const QHash<QString, QVariantMap> &toolStates = command.edit3dToolStates;
QString lastSceneId;
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
if (helper) {
@@ -1832,11 +1873,23 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
createCameraAndLightGizmos(instanceList);
if (!command.edit3dBackgroundColor.isEmpty()) {
View3DActionCommand backgroundColorCommand(View3DActionCommand::SelectBackgroundColor,
QVariant::fromValue(command.edit3dBackgroundColor));
view3DAction(backgroundColorCommand);
}
if (command.edit3dGridColor.isValid()) {
View3DActionCommand backgroundColorCommand(View3DActionCommand::SelectGridColor,
QVariant::fromValue(command.edit3dGridColor));
view3DAction(backgroundColorCommand);
}
// Queue two renders to make sure icon gizmos update properly
render3DEditView(2);
#else
Q_UNUSED(instanceList)
Q_UNUSED(toolStates)
Q_UNUSED(command)
#endif
}
@@ -1958,7 +2011,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com
nodeInstanceClient()->componentCompleted(createComponentCompletedCommand(instanceList));
if (ViewConfig::isQuick3DMode()) {
setup3DEditView(instanceList, command.edit3dToolStates);
setup3DEditView(instanceList, command);
updateRotationBlocks(command.auxiliaryChanges);
}
@@ -1967,12 +2020,6 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com
#ifdef IMPORT_QUICK3D_ASSETS
QTimer::singleShot(0, this, &Qt5InformationNodeInstanceServer::resolveImportSupport);
#endif
if (!command.edit3dBackgroundColor.isEmpty()) {
View3DActionCommand backgroundColorCommand(View3DActionCommand::SelectBackgroundColor,
QVariant::fromValue(command.edit3dBackgroundColor));
view3DAction(backgroundColorCommand);
}
}
void Qt5InformationNodeInstanceServer::sendChildrenChangedCommand(const QList<ServerNodeInstance> &childList)
@@ -2237,9 +2284,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
case View3DActionCommand::ShowCameraFrustum:
updatedToolState.insert("showCameraFrustum", command.isEnabled());
break;
case View3DActionCommand::SelectBackgroundColor: {
case View3DActionCommand::SelectBackgroundColor:
updatedViewState.insert("selectBackgroundColor", command.value());
break;
case View3DActionCommand::SelectGridColor: {
updatedViewState.insert("selectGridColor", command.value());
break;
}
#ifdef QUICK3D_PARTICLES_MODULE
case View3DActionCommand::ShowParticleEmitter:

View File

@@ -114,7 +114,7 @@ private:
void createEditView3D();
void create3DPreviewView();
void setup3DEditView(const QList<ServerNodeInstance> &instanceList,
const QHash<QString, QVariantMap> &toolStates);
const CreateSceneCommand &command);
void createCameraAndLightGizmos(const QList<ServerNodeInstance> &instanceList) const;
void add3DViewPorts(const QList<ServerNodeInstance> &instanceList);
void add3DScenes(const QList<ServerNodeInstance> &instanceList);
@@ -165,6 +165,7 @@ private:
QSet<QObject *> m_view3Ds;
QMultiHash<QObject *, QObject *> m_3DSceneMap; // key: scene root, value: node
QObject *m_active3DView = nullptr;
QList<QObject *> m_priorityView3DsToRender;
QObject *m_active3DScene = nullptr;
QSet<ServerNodeInstance> m_parentChangedSet;
QList<ServerNodeInstance> m_completedComponentList;

View File

@@ -405,6 +405,7 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
QQuickItemPrivate *pItem = QQuickItemPrivate::get(item);
const bool renderEffects = qEnvironmentVariableIsSet("QMLPUPPET_RENDER_EFFECTS");
const bool smoothRendering = qEnvironmentVariableIsSet("QMLPUPPET_SMOOTH_RENDERING");
if (renderEffects) {
if (parentEffectItem(item))
@@ -427,17 +428,20 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
ServerNodeInstance instance = instanceForObject(item);
const bool rootIs3DObject = rootIsRenderable3DObject();
// Setting layer enabled to false messes up the bounding rect.
// Therefore we calculate it upfront.
QRectF renderBoundingRect;
if (instance.isValid())
renderBoundingRect = instance.boundingRect();
else if (rootIsRenderable3DObject())
else if (rootIs3DObject)
renderBoundingRect = item->boundingRect();
else
renderBoundingRect = ServerNodeInstance::effectAdjustedBoundingRect(item);
const int scaleFactor = (smoothRendering && !rootIs3DObject) ? 2 : 1;
// Hide immediate children that have instances and are QQuickItems so we get only
// the parent item's content, as compositing is handled on creator side.
QSet<QQuickItem *> layerChildren;
@@ -470,6 +474,8 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
// us to render it to a texture that we can grab to an image.
QSGRenderContext *rc = QQuickWindowPrivate::get(m_viewData.window.data())->context;
QSGLayer *layer = rc->sceneGraphContext()->createLayer(rc);
if (smoothRendering)
layer->setSamples(4);
layer->setItem(pItem->itemNode());
layer->setRect(QRectF(renderBoundingRect.x(),
@@ -478,8 +484,8 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
-renderBoundingRect.height()));
const QSize minSize = rc->sceneGraphContext()->minimumFBOSize();
layer->setSize(QSize(qMax(minSize.width(), int(renderBoundingRect.width())),
qMax(minSize.height(), int(renderBoundingRect.height()))));
layer->setSize(QSize(qMax(minSize.width(), int(renderBoundingRect.width() * scaleFactor)),
qMax(minSize.height(), int(renderBoundingRect.height() * scaleFactor))));
layer->scheduleUpdate();
if (layer->updateTexture())
@@ -489,6 +495,8 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
delete layer;
layer = nullptr;
renderImage.setDevicePixelRatio(scaleFactor);
});
m_viewData.renderControl->render();
@@ -514,7 +522,6 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item)
if (!isLayerEnabled(pItem))
pItem->derefFromEffectItem(false);
#else
Q_UNUSED(item)
#endif

View File

@@ -111,8 +111,6 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
}
}
clearChangedPropertyList();
if (Internal::QuickItemNodeInstance::unifiedRenderPath()) {
if (windowDirty)
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()}));
@@ -134,13 +132,15 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
}
if (rootIsRenderable3DObject() && rootNodeInstance().contentItem()
&& DesignerSupport::isDirty(rootNodeInstance().contentItem(),
DesignerSupport::AllMask)
&& !changedPropertyList().isEmpty()
&& nodeInstanceClient()->bytesToWrite() < 10000) {
Internal::QuickItemNodeInstance::updateDirtyNode(rootNodeInstance().contentItem());
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()}));
}
clearChangedPropertyList();
inFunction = false;
}
}

View File

@@ -504,12 +504,12 @@ QImage QuickItemNodeInstance::renderImage() const
if (s_unifiedRenderPath) {
renderImage = nodeInstanceServer()->grabWindow();
renderImage = renderImage.copy(renderBoundingRect.toRect());
/* When grabbing an offscren window the device pixel ratio is 1 */
renderImage.setDevicePixelRatio(1);
} else {
renderImage = nodeInstanceServer()->grabItem(quickItem());
}
/* When grabbing an offscren window the device pixel ratio is 1 */
renderImage.setDevicePixelRatio(1);
#endif
return renderImage;

View File

@@ -128,7 +128,7 @@ Item {
StudioControls.MenuSeparator {}
StudioControls.MenuItem {
text: qsTr("New Material")
text: qsTr("Create New Material")
onTriggered: materialBrowserModel.addNewMaterial()
}
@@ -169,7 +169,8 @@ Item {
}
Text {
text: qsTr("No materials yet.\nClick '+' above to start.")
text: 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
horizontalAlignment: Text.AlignHCenter
@@ -179,7 +180,8 @@ Item {
}
Text {
text: qsTr("Add QtQuick3D module using the Components view to enable the Material Browser.");
text: qsTr("To use <b>Material Browser</b>, first add the QtQuick3D module in the <b>Components</b> view.");
textFormat: Text.RichText
color: StudioTheme.Values.themeTextColor
font.pixelSize: StudioTheme.Values.mediumFontSize
topPadding: 30

View File

@@ -72,9 +72,12 @@ Rectangle {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
onPressed: (mouse) => {
materialBrowserModel.selectMaterial(index)
if (mouse.button === Qt.RightButton)
if (mouse.button === Qt.LeftButton)
rootView.startDragMaterial(index, mapToGlobal(mouse.x, mouse.y))
else if (mouse.button === Qt.RightButton)
root.showContextMenu()
}

View File

@@ -48,8 +48,9 @@ PropertyEditorPane {
height: 150
Text {
text: hasQuick3DImport ? qsTr("No materials yet.\nClick '+' above to start.")
: qsTr("Add QtQuick3D module using the Components view to enable the Material Editor.")
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.")
textFormat: Text.RichText
color: StudioTheme.Values.themeTextColor
font.pixelSize: StudioTheme.Values.mediumFontSize
horizontalAlignment: Text.AlignHCenter

View File

@@ -63,7 +63,7 @@ Rectangle {
buttonSize: root.height
enabled: hasQuick3DImport
onClicked: root.toolBarAction(ToolBarAction.AddNewMaterial)
tooltip: qsTr("Add a new material.")
tooltip: qsTr("Create new material.")
}
IconButton {

View File

@@ -33,7 +33,7 @@ PropertyEditorPane {
id: itemPane
ComponentSection {
showState: majorVersion >= 6
showState: majorQtQuickVersion >= 6
}
Column {

View File

@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
@@ -36,6 +36,7 @@ StudioControls.ComboBox {
property string fontFilter: "*.ttf *.otf"
property bool showExtendedFunctionButton: true
hasActiveDrag: activeDragSuffix !== "" && root.fontFilter.includes(activeDragSuffix)
labelColor: colorLogic.textColor
editable: true
@@ -47,16 +48,45 @@ StudioControls.ComboBox {
filter: root.fontFilter
}
DropArea {
id: dropArea
anchors.fill: parent
property string assetPath: ""
onEntered: function(drag) {
dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0]
drag.accepted = root.hasActiveDrag
root.hasActiveHoverDrag = drag.accepted
}
onExited: root.hasActiveHoverDrag = false
onDropped: function(drop) {
drop.accepted = root.hasActiveHoverDrag
var fontLoader = root.createFontLoader("file:" + dropArea.assetPath)
if (fontLoader.status === FontLoader.Ready) {
root.backendValue.value = fontLoader.name
root.currentIndex = root.find(root.backendValue.value)
}
root.hasActiveHoverDrag = false
root.backendValue.commitDrop(dropArea.assetPath)
}
}
function createFontLoader(fontUrl) {
return Qt.createQmlObject('import QtQuick 2.0; FontLoader { source: "' + fontUrl + '"; }',
root, "dynamicFontLoader")
}
function setupModel() {
var familyNames = ["Arial", "Times New Roman", "Courier", "Verdana", "Tahoma"] // default fonts
// default fonts
var familyNames = ["Arial", "Times New Roman", "Courier", "Verdana", "Tahoma"]
for (var i = 0; i < fileModel.fullPathModel.length; ++i) { // add custom fonts
var fontLoader = createFontLoader(fileModel.docPath + "/" + fileModel.fullPathModel[i])
for (var i = 0; i < fileModel.model.length; ++i) { // add custom fonts
var fontLoader = root.createFontLoader(fileModel.docPath + "/"
+ fileModel.model[i].relativeFilePath)
familyNames.push(fontLoader.name)
}

View File

@@ -34,10 +34,10 @@ Rectangle {
signal clicked()
property alias icon: icon.text
property alias enabled: mouseArea.enabled
property alias tooltip: toolTip.text
property alias iconSize: icon.font.pixelSize
property bool enabled: true
property int buttonSize: StudioTheme.Values.height
property color normalColor: StudioTheme.Values.themeControlBackground
property color hoverColor: StudioTheme.Values.themeControlBackgroundHover
@@ -46,9 +46,10 @@ Rectangle {
width: buttonSize
height: buttonSize
color: mouseArea.pressed ? pressColor
: mouseArea.containsMouse ? hoverColor
: normalColor
color: !enabled ? normalColor
: mouseArea.pressed ? pressColor
: mouseArea.containsMouse ? hoverColor
: normalColor
Behavior on color {
ColorAnimation {
@@ -71,7 +72,11 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked()
onClicked: {
// We need to keep mouse area enabled even when button is disabled to make tooltip work
if (root.enabled)
root.clicked()
}
}
ToolTip {

View File

@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
@@ -44,6 +44,9 @@ Row {
// by QtQuick3D to add built-in primitives to the model.
property var defaultItems
// Current item
property string absoluteFilePath: ""
FileResourcesModel {
id: fileModel
modelNodeBackendProperty: modelNodeBackend
@@ -60,6 +63,7 @@ Row {
property ListModel listModel: ListModel {}
hasActiveDrag: activeDragSuffix !== "" && root.filter.includes(activeDragSuffix)
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
width: implicitWidth
@@ -69,26 +73,99 @@ Row {
// when the combobox is closed by focusing on some other control.
property int hoverIndex: -1
DropArea {
id: dropArea
anchors.fill: parent
property string assetPath: ""
onEntered: function(drag) {
dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0]
drag.accepted = comboBox.hasActiveDrag
comboBox.hasActiveHoverDrag = drag.accepted
}
onExited: comboBox.hasActiveHoverDrag = false
onDropped: function(drop) {
drop.accepted = comboBox.hasActiveHoverDrag
comboBox.editText = dropArea.assetPath
comboBox.accepted()
comboBox.hasActiveHoverDrag = false
root.backendValue.commitDrop(dropArea.assetPath)
}
}
ToolTip {
id: toolTip
visible: comboBox.hover && toolTip.text !== ""
text: root.backendValue.valueToString
delay: StudioTheme.Values.toolTipDelay
height: StudioTheme.Values.toolTipHeight
background: Rectangle {
color: StudioTheme.Values.themeToolTipBackground
border.color: StudioTheme.Values.themeToolTipOutline
border.width: StudioTheme.Values.border
}
contentItem: Text {
color: StudioTheme.Values.themeToolTipText
text: toolTip.text
verticalAlignment: Text.AlignVCenter
contentItem: RowLayout {
spacing: 10
Item {
visible: thumbnail.status === Image.Ready
Layout.preferredWidth: 100
Layout.preferredHeight: 100
Image {
id: checker
visible: !root.isMesh(root.absoluteFilePath)
anchors.fill: parent
fillMode: Image.Tile
source: "images/checkers.png"
}
Image {
id: thumbnail
asynchronous: true
anchors.fill: parent
fillMode: Image.PreserveAspectFit
source: {
if (root.isBuiltInPrimitive(root.absoluteFilePath))
return "image://qmldesigner_thumbnails/"
+ root.absoluteFilePath.substring(1, root.absoluteFilePath.length)
+ ".builtin"
if (fileModel.isLocal(root.absoluteFilePath))
return "image://qmldesigner_thumbnails/" + root.absoluteFilePath
return root.absoluteFilePath
}
}
}
ColumnLayout {
Text {
text: root.fileName(toolTip.text)
color: StudioTheme.Values.themeToolTipText
font: toolTip.font
}
Text {
Layout.fillWidth: true
text: root.isBuiltInPrimitive(toolTip.text) ? qsTr("Built-in primitive")
: toolTip.text
font: toolTip.font
color: StudioTheme.Values.themeToolTipText
wrapMode: Text.WordWrap
}
}
}
}
delegate: ItemDelegate {
required property string fullPath
required property string absoluteFilePath
required property string relativeFilePath
required property string name
required property int group
required property int index
@@ -150,20 +227,66 @@ Row {
}
ToolTip {
id: itemToolTip
visible: delegateRoot.hovered && comboBox.highlightedIndex === index
text: fullPath
id: delegateToolTip
visible: delegateRoot.hovered
text: delegateRoot.relativeFilePath
delay: StudioTheme.Values.toolTipDelay
height: StudioTheme.Values.toolTipHeight
background: Rectangle {
color: StudioTheme.Values.themeToolTipBackground
border.color: StudioTheme.Values.themeToolTipOutline
border.width: StudioTheme.Values.border
}
contentItem: Text {
color: StudioTheme.Values.themeToolTipText
text: itemToolTip.text
verticalAlignment: Text.AlignVCenter
contentItem: RowLayout {
spacing: 10
Item {
visible: delegateThumbnail.status === Image.Ready
Layout.preferredWidth: 100
Layout.preferredHeight: 100
Image {
id: delegateChecker
visible: !root.isMesh(delegateRoot.absoluteFilePath)
anchors.fill: parent
fillMode: Image.Tile
source: "images/checkers.png"
}
Image {
id: delegateThumbnail
asynchronous: true
anchors.fill: parent
fillMode: Image.PreserveAspectFit
source: {
if (root.isBuiltInPrimitive(delegateRoot.name))
return "image://qmldesigner_thumbnails/"
+ delegateRoot.name.substring(1, delegateRoot.name.length)
+ ".builtin"
return "image://qmldesigner_thumbnails/" + delegateRoot.absoluteFilePath
}
}
}
ColumnLayout {
Text {
text: delegateRoot.name
color: StudioTheme.Values.themeToolTipText
font: delegateToolTip.font
}
Text {
Layout.fillWidth: true
text: root.isBuiltInPrimitive(delegateToolTip.text)
? qsTr("Built-in primitive")
: delegateToolTip.text
font: delegateToolTip.font
color: StudioTheme.Values.themeToolTipText
wrapMode: Text.WordWrap
}
}
}
}
}
@@ -197,8 +320,9 @@ Row {
if (root.backendValue.isBound) {
comboBox.textValue = root.backendValue.expression
} else {
var fullPath = root.backendValue.valueToString
comboBox.textValue = fullPath.substr(fullPath.lastIndexOf('/') + 1)
// Can be absolute or relative file path
var filePath = root.backendValue.valueToString
comboBox.textValue = filePath.substr(filePath.lastIndexOf('/') + 1)
}
comboBox.setCurrentText(comboBox.textValue)
@@ -230,9 +354,13 @@ Row {
// Check if value set by user matches with a name in the model then pick the full path
let index = comboBox.find(inputValue)
if (index !== -1)
inputValue = comboBox.items.get(index).model.fullPath
inputValue = comboBox.items.get(index).model.relativeFilePath
root.backendValue.value = inputValue
if (!root.backendValue.isBound)
root.absoluteFilePath = fileModel.resolve(root.backendValue.value)
comboBox.dirty = false
}
@@ -252,11 +380,14 @@ Row {
let inputValue = comboBox.editText
if (index >= 0)
inputValue = comboBox.items.get(index).model.fullPath
inputValue = comboBox.items.get(index).model.relativeFilePath
if (root.backendValue.value !== inputValue)
root.backendValue.value = inputValue
if (!root.backendValue.isBound)
root.absoluteFilePath = fileModel.resolve(root.backendValue.value)
comboBox.dirty = false
}
@@ -273,6 +404,23 @@ Row {
}
}
function isBuiltInPrimitive(value) {
return value.startsWith('#')
}
function isMesh(value) {
return root.isBuiltInPrimitive(value)
|| root.hasFileExtension(root.fileName(value), "mesh")
}
function hasFileExtension(fileName, extension) {
return fileName.split('.').pop() === extension
}
function fileName(filePath) {
return filePath.substr(filePath.lastIndexOf('/') + 1)
}
function createModel() {
// Build the combobox model
comboBox.listModel.clear()
@@ -284,17 +432,22 @@ Row {
if (root.defaultItems !== undefined) {
for (var i = 0; i < root.defaultItems.length; ++i) {
comboBox.listModel.append({
fullPath: root.defaultItems[i],
absoluteFilePath: "",
relativeFilePath: root.defaultItems[i],
name: root.defaultItems[i],
group: 0
})
}
}
for (var j = 0; j < fileModel.fullPathModel.length; ++j) {
const myModel = fileModel.model
for (var j = 0; j < myModel.length; ++j) {
let item = myModel[j]
comboBox.listModel.append({
fullPath: fileModel.fullPathModel[j],
name: fileModel.fileNameModel[j],
absoluteFilePath: item.absoluteFilePath,
relativeFilePath: item.relativeFilePath,
name: item.fileName,
group: 1
})
}
@@ -304,7 +457,7 @@ Row {
Connections {
target: fileModel
function onFullPathModelChanged() {
function onModelChanged() {
root.createModel()
comboBox.setCurrentText(comboBox.textValue)
}
@@ -315,6 +468,9 @@ Row {
Component.onCompleted: {
root.createModel()
comboBox.updateTextValue()
if (!root.backendValue.isBound)
root.absoluteFilePath = fileModel.resolve(root.backendValue.value)
}
function indexOf(model, criteria) {
@@ -333,7 +489,7 @@ Row {
if (comboBox.popup.opened && !root.backendValue.isBound) {
var index = root.indexOf(comboBox.items,
function(item) {
return item.fullPath === root.backendValue.value
return item.relativeFilePath === root.backendValue.value
})
if (index !== -1) {
@@ -352,8 +508,10 @@ Row {
iconColor: root.textColor
onClicked: {
fileModel.openFileDialog()
if (fileModel.fileName !== "")
if (fileModel.fileName !== "") {
root.backendValue.value = fileModel.fileName
root.absoluteFilePath = fileModel.resolve(root.backendValue.value)
}
}
}
}

View File

@@ -136,7 +136,7 @@ T.AbstractButton {
when: myButton.globalHover && !myButton.hover && !myButton.pressed && myButton.enabled
PropertyChanges {
target: buttonBackground
color: StudioTheme.Values.themeControlBackgroundGlobalHover
color: StudioTheme.Values.themeControlBackground
}
},
State {

View File

@@ -37,6 +37,9 @@ Rectangle {
property bool pressed: checkIndicatorMouseArea.containsPress
property bool checked: false
property bool hasActiveDrag: myControl.hasActiveDrag ?? false
property bool hasActiveHoverDrag: myControl.hasActiveHoverDrag ?? false
color: StudioTheme.Values.themeControlBackground
border.width: 0
@@ -79,12 +82,20 @@ Rectangle {
name: "default"
when: myControl.enabled && checkIndicator.enabled && !myControl.edit
&& !checkIndicator.hover && !myControl.hover && !myControl.drag
&& !checkIndicator.checked
&& !checkIndicator.checked && !checkIndicator.hasActiveDrag
PropertyChanges {
target: checkIndicator
color: StudioTheme.Values.themeControlBackground
}
},
State {
name: "dragHover"
when: myControl.enabled && checkIndicator.hasActiveHoverDrag
PropertyChanges {
target: checkIndicator
color: StudioTheme.Values.themeControlBackgroundInteraction
}
},
State {
name: "globalHover"
when: myControl.enabled && checkIndicator.enabled && !myControl.drag

View File

@@ -50,9 +50,6 @@ T.ComboBox {
property alias textInput: comboBoxInput
property int borderWidth: myComboBox.hasActiveHoverDrag ? StudioTheme.Values.borderHover
: StudioTheme.Values.border
signal compressedActivated(int index, int reason)
enum ActivatedReason { EditingFinished, Other }
@@ -61,7 +58,7 @@ T.ComboBox {
height: StudioTheme.Values.defaultControlHeight
leftPadding: actionIndicator.width
rightPadding: popupIndicator.width + myComboBox.borderWidth
rightPadding: popupIndicator.width + StudioTheme.Values.border
font.pixelSize: StudioTheme.Values.myFontSize
wheelEnabled: false
@@ -91,7 +88,6 @@ T.ComboBox {
myControl: myComboBox
text: myComboBox.editText
borderWidth: myComboBox.borderWidth
onEditingFinished: {
comboBoxInput.deselect()
@@ -113,16 +109,16 @@ T.ComboBox {
myControl: myComboBox
myPopup: myComboBox.popup
x: comboBoxInput.x + comboBoxInput.width
y: myComboBox.borderWidth
width: StudioTheme.Values.checkIndicatorWidth - myComboBox.borderWidth
height: StudioTheme.Values.checkIndicatorHeight - myComboBox.borderWidth * 2
y: StudioTheme.Values.border
width: StudioTheme.Values.checkIndicatorWidth - StudioTheme.Values.border
height: StudioTheme.Values.checkIndicatorHeight - StudioTheme.Values.border * 2
}
background: Rectangle {
id: comboBoxBackground
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeControlOutline
border.width: myComboBox.borderWidth
border.width: StudioTheme.Values.border
x: actionIndicator.width
width: myComboBox.width - actionIndicator.width
height: myComboBox.height
@@ -149,7 +145,7 @@ T.ComboBox {
width: comboBoxPopup.width - comboBoxPopup.leftPadding - comboBoxPopup.rightPadding
- (comboBoxPopupScrollBar.visible ? comboBoxPopupScrollBar.contentItem.implicitWidth
+ 2 : 0) // TODO Magic number
height: StudioTheme.Values.height - 2 * myComboBox.borderWidth
height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
padding: 0
enabled: model.enabled === undefined ? true : model.enabled
@@ -203,9 +199,9 @@ T.ComboBox {
popup: T.Popup {
id: comboBoxPopup
x: actionIndicator.width + myComboBox.borderWidth
x: actionIndicator.width + StudioTheme.Values.border
y: myComboBox.height
width: myComboBox.width - actionIndicator.width - myComboBox.borderWidth * 2
width: myComboBox.width - actionIndicator.width - StudioTheme.Values.border * 2
// TODO Setting the height on the popup solved the problem with the popup of height 0,
// but it has the problem that it sometimes extend over the border of the actual window
// and is then cut off.
@@ -213,7 +209,7 @@ T.ComboBox {
+ comboBoxPopup.bottomPadding,
myComboBox.Window.height - topMargin - bottomMargin,
StudioTheme.Values.maxComboBoxPopupHeight)
padding: myComboBox.borderWidth
padding: StudioTheme.Values.border
margins: 0 // If not defined margin will be -1
closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent
| T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside
@@ -245,7 +241,7 @@ T.ComboBox {
State {
name: "default"
when: myComboBox.enabled && !myComboBox.hover && !myComboBox.edit && !myComboBox.open
&& !myComboBox.activeFocus
&& !myComboBox.activeFocus && !myComboBox.hasActiveDrag
PropertyChanges {
target: myComboBox
wheelEnabled: false
@@ -257,9 +253,23 @@ T.ComboBox {
PropertyChanges {
target: comboBoxBackground
color: StudioTheme.Values.themeControlBackground
border.color: myComboBox.hasActiveDrag ? StudioTheme.Values.themeInteraction
: StudioTheme.Values.themeControlOutline
border.width: myComboBox.borderWidth
}
},
State {
name: "acceptsDrag"
when: myComboBox.enabled && myComboBox.hasActiveDrag && !myComboBox.hasActiveHoverDrag
PropertyChanges {
target: comboBoxBackground
border.color: StudioTheme.Values.themeControlOutlineInteraction
}
},
State {
name: "dragHover"
when: myComboBox.enabled && myComboBox.hasActiveHoverDrag
PropertyChanges {
target: comboBoxBackground
color: StudioTheme.Values.themeControlBackgroundInteraction
border.color: StudioTheme.Values.themeControlOutlineInteraction
}
},
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via

View File

@@ -34,7 +34,6 @@ TextInput {
property bool edit: textInput.activeFocus
property bool hover: mouseArea.containsMouse && textInput.enabled
property int borderWidth: StudioTheme.Values.border
z: 2
font: myControl.font
@@ -56,11 +55,11 @@ TextInput {
Rectangle {
id: textInputBackground
x: textInput.borderWidth
y: textInput.borderWidth
x: StudioTheme.Values.border
y: StudioTheme.Values.border
z: -1
width: textInput.width
height: StudioTheme.Values.height - textInput.borderWidth * 2
height: StudioTheme.Values.height - StudioTheme.Values.border * 2
color: StudioTheme.Values.themeControlBackground
border.width: 0
}
@@ -94,7 +93,7 @@ TextInput {
State {
name: "default"
when: myControl.enabled && !textInput.edit && !textInput.hover && !myControl.hover
&& !myControl.open
&& !myControl.open && !myControl.hasActiveDrag
PropertyChanges {
target: textInputBackground
color: StudioTheme.Values.themeControlBackground
@@ -105,6 +104,14 @@ TextInput {
acceptedButtons: Qt.LeftButton
}
},
State {
name: "dragHover"
when: myControl.enabled && myControl.hasActiveHoverDrag
PropertyChanges {
target: textInputBackground
color: StudioTheme.Values.themeControlBackgroundInteraction
}
},
State {
name: "globalHover"
when: myControl.hover && !textInput.hover && !textInput.edit && !myControl.open

View File

@@ -88,6 +88,9 @@ Item {
property alias popupScrollBar: popupScrollBar
property alias popupMouseArea: popupMouseArea
property bool hasActiveDrag: false // an item that can be dropped here is being dragged
property bool hasActiveHoverDrag: false // an item that can be dropped her is being hovered on top
width: StudioTheme.Values.defaultControlWidth
height: StudioTheme.Values.defaultControlHeight
implicitHeight: StudioTheme.Values.defaultControlHeight
@@ -469,9 +472,11 @@ Item {
State {
name: "default"
when: root.enabled && !textInput.edit && !root.hover && !root.open
&& !root.hasActiveDrag
PropertyChanges {
target: textInputBackground
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeControlOutline
}
PropertyChanges {
target: textInputMouseArea
@@ -479,6 +484,23 @@ Item {
acceptedButtons: Qt.LeftButton
}
},
State {
name: "acceptsDrag"
when: root.enabled && root.hasActiveDrag && !root.hasActiveHoverDrag
PropertyChanges {
target: textInputBackground
border.color: StudioTheme.Values.themeInteraction
}
},
State {
name: "dragHover"
when: root.enabled && root.hasActiveHoverDrag
PropertyChanges {
target: textInputBackground
color: StudioTheme.Values.themeControlBackgroundInteraction
border.color: StudioTheme.Values.themeInteraction
}
},
State {
name: "globalHover"
when: root.hover && !textInput.hover && !textInput.edit && !root.open
@@ -587,12 +609,20 @@ Item {
name: "default"
when: root.enabled && checkIndicator.enabled && !root.edit
&& !checkIndicator.hover && !root.hover
&& !checkIndicator.checked
&& !checkIndicator.checked && !root.hasActiveHoverDrag
PropertyChanges {
target: checkIndicator
color: StudioTheme.Values.themeControlBackground
}
},
State {
name: "dragHover"
when: root.enabled && root.hasActiveHoverDrag
PropertyChanges {
target: checkIndicator
color: StudioTheme.Values.themeControlBackgroundInteraction
}
},
State {
name: "globalHover"
when: root.enabled && checkIndicator.enabled

View File

@@ -0,0 +1,5 @@
import QtQuick 2.15
%{FormClass} {
button.onClicked: console.log("Button Pressed")
}

View File

@@ -0,0 +1,33 @@
/*
This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
It is supposed to be strictly declarative and only uses a subset of QML. If you edit
this file manually, you might introduce QML code that is not supported by Qt Design Studio.
Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
*/
import QtQuick 2.15
@if %{UseQtQuickControls2}
import QtQuick.Controls 2.15
@endif
@if %{UseImport}
import %{ApplicationImport}
@endif
%{RootItem} {
@if %{UseImport}
width: Constants.width
height: Constants.height
@else
width: 1024
height: 768
@endif
property alias button: button
Button {
id: button
x: 64
y: 64
text: qsTr("Button")
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,132 @@
{
"version": 1,
"supportedProjectTypes": [ ],
"id": "Q.QtStudio.QmlUIForm.2",
"category": "B.StudioQtQuickFiles",
"trDescription": "Creates a UI file (.ui.qml) along with a matching QML file for implementation purposes.",
"trDisplayName": "QtQuick UI Form",
"trDisplayCategory": "Qt Quick Files",
"icon": "file_ui.png",
"platformIndependent": true,
"enabled": "%{JS: value('Plugins').indexOf('QmlJSEditor') >= 0}",
"options" : [
{ "key": "QmlFile", "value": "%{Class}.%{JS: Util.preferredSuffix('text/x-qml')}" },
{ "key": "UiFile", "value": "%{FormClass}.%{JS: Util.preferredSuffix('application/x-qt.ui+qml')}" },
{ "key": "ApplicationImport", "value": "%{QmlProjectName} 1.0" },
{ "key": "RootItem", "value": "%{JS: %{RootItemCB}.RootItem}" },
{ "key": "UseImportDefault", "value": "%{JS: false}" },
{ "key": "UseQtQuickControls2Default", "value": "%{JS: true}" }
],
"pages" :
[
{
"trDisplayName": "Define Class",
"trShortTitle": "Details",
"typeId": "Fields",
"data" :
[
{
"name": "Class",
"trDisplayName": "Component name:",
"mandatory": true,
"type": "LineEdit",
"data": {
"validator": "(?:[A-Z_][a-zA-Z_0-9]*|)",
"fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }"
}
},
{
"name": "Sp1",
"type": "Spacer",
"data": { "factor": 2 }
},
{
"name": "FormClass",
"trDisplayName": "Component form name:",
"mandatory": true,
"type": "LineEdit",
"data": {
"validator": "(?:[A-Z_][a-zA-Z_0-9]*|)",
"fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }",
"trText": "%{Class}Form"
}
},
{
"name": "TargetPath",
"type": "PathChooser",
"trDisplayName": "Path:",
"mandatory": true,
"data":
{
"kind": "directory",
"basePath": "%{InitialPath}",
"path": "%{InitialPath}"
}
},
{
"name": "RootItemCB",
"trDisplayName": "Root Item:",
"type": "ComboBox",
"data":
{
"index": 0,
"items":
[
{
"trKey": "Item",
"value":
"({
'RootItem': 'Item'
})"
},
{
"trKey": "Rectangle",
"value":
"({
'RootItem': 'Rectangle'
})"
}
]
}
},
{
"name": "UseImport",
"trDisplayName": "Use Application Import",
"type": "CheckBox",
"data":
{
"checked": "%{UseImportDefault}"
}
},
{
"name": "UseQtQuickControls2",
"trDisplayName": "Use QtQuick Controls 2",
"type": "CheckBox",
"data":
{
"checked": "%{UseQtQuickControls2Default}"
}
}
]
}
],
"generators" :
[
{
"typeId": "File",
"data": [
{
"source": "file.qml.tpl",
"target": "%{TargetPath}/%{QmlFile}",
"openInEditor": true
},
{
"source": "fileForm.ui.qml.tpl",
"target": "%{TargetPath}/%{UiFile}",
"openInEditor": true
}
]
}
]
}

View File

@@ -16,12 +16,6 @@ Rectangle {
color: Constants.backgroundColor
Text {
text: qsTr("Hello %{ProjectName}")
anchors.centerIn: parent
font.family: Constants.font.family
}
View3D {
id: view3D
anchors.fill: parent
@@ -63,4 +57,12 @@ Rectangle {
diffuseColor: "#4aee45"
}
}
Text {
text: qsTr("Hello %{ProjectName}")
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 100
font.family: Constants.font.family
}
}

View File

@@ -90,7 +90,7 @@ Project {
/* Required for deployment */
targetDirectory: "/opt/%{ProjectName}"
qdsVersion: "3.4"
qdsVersion: "3.5"
quickVersion: "%{QtQuickVersion}"

View File

@@ -93,12 +93,12 @@ void Database::activateLogging()
void Database::open(LockingMode lockingMode)
{
m_databaseBackend.open(m_databaseFilePath, m_openMode);
m_databaseBackend.setLockingMode(lockingMode);
m_databaseBackend.setJournalMode(m_journalMode);
if (m_busyTimeout > 0ms)
m_databaseBackend.setBusyTimeout(m_busyTimeout);
else
m_databaseBackend.registerBusyHandler();
m_databaseBackend.setLockingMode(lockingMode);
m_databaseBackend.setJournalMode(m_journalMode);
registerTransactionStatements();
m_isOpen = true;
}

View File

@@ -473,7 +473,7 @@ bool CppEditorDocument::save(QString *errorString, const FilePath &filePath, boo
if (!editedRanges.empty()) {
QTextCursor cursor(document());
cursor.beginEditBlock();
cursor.joinPreviousEditBlock();
indenter()->format(editedRanges);
cursor.endEditBlock();
}

View File

@@ -161,6 +161,7 @@ extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/edit3d
SOURCES
edit3dview.cpp edit3dview.h
edit3dviewconfig.h
edit3dwidget.cpp edit3dwidget.h
edit3dcanvas.cpp edit3dcanvas.h
edit3dactions.cpp edit3dactions.h
@@ -305,6 +306,7 @@ extend_qtc_plugin(QmlDesigner
gradientpresetitem.cpp gradientpresetitem.h
gradientpresetlistmodel.cpp gradientpresetlistmodel.h
propertyeditorcontextobject.cpp propertyeditorcontextobject.h
propertyeditorimageprovider.cpp propertyeditorimageprovider.h
propertyeditorqmlbackend.cpp propertyeditorqmlbackend.h
propertyeditortransaction.cpp propertyeditortransaction.h
propertyeditorvalue.cpp propertyeditorvalue.h
@@ -392,7 +394,8 @@ extend_qtc_plugin(QmlDesigner
SOURCES
explicitimagecacheimageprovider.cpp
explicitimagecacheimageprovider.h
smallimagecacheprovider.cpp
smallimagecacheprovider.h
)
extend_qtc_plugin(QmlDesigner

View File

@@ -81,7 +81,7 @@ bool AssetExporterView::saveQmlFile(QString *error) const
void AssetExporterView::modelAttached(Model *model)
{
if (model->rewriterView() && model->rewriterView()->inErrorState())
if (model->rewriterView() && !model->rewriterView()->errors().isEmpty())
setState(LoadState::QmlErrorState);
AbstractView::modelAttached(model);

View File

@@ -75,7 +75,7 @@ QPixmap AssetsLibraryIconProvider::requestPixmap(const QString &id, QSize *size,
pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png");
if (requestedSize.isValid())
return pixmap.scaled(requestedSize);
return pixmap.scaled(requestedSize, Qt::KeepAspectRatio);
return pixmap;
}

View File

@@ -91,10 +91,6 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event)
m_assetsToDrag.clear();
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_assetsToDrag.clear();
if (m_model)
m_model->endDrag();
}
return QObject::eventFilter(obj, event);

View File

@@ -513,7 +513,7 @@ public:
->currentModel();
if (currentModel->rewriterView()
&& currentModel->rewriterView()->inErrorState()) {
&& !currentModel->rewriterView()->errors().isEmpty()) {
throw DocumentError{};
}

View File

@@ -478,19 +478,16 @@ static void layoutHelperFunction(const SelectionContext &selectionContext,
const QmlItemNode qmlItemNode = QmlItemNode(selectionContext.firstSelectedModelNode());
if (qmlItemNode.hasInstanceParentItem()) {
ModelNode layoutNode;
selectionContext.view()->executeInTransaction("DesignerActionManager|layoutHelperFunction1",[=, &layoutNode](){
selectionContext.view()->executeInTransaction("DesignerActionManager|layoutHelperFunction",[=](){
QmlItemNode parentNode = qmlItemNode.instanceParentItem();
NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(layoutType);
layoutNode = selectionContext.view()->createModelNode(layoutType, metaInfo.majorVersion(), metaInfo.minorVersion());
const ModelNode layoutNode = selectionContext.view()->createModelNode(layoutType, metaInfo.majorVersion(), metaInfo.minorVersion());
reparentTo(layoutNode, parentNode);
});
selectionContext.view()->executeInTransaction("DesignerActionManager|layoutHelperFunction2",[=](){
QList<ModelNode> sortedSelectedNodes = selectionContext.selectedModelNodes();
Utils::sort(sortedSelectedNodes, lessThan);

View File

@@ -110,9 +110,11 @@ void DebugView::nodeCreated(const ModelNode &createdNode)
QString string;
message.setString(&string);
message << createdNode;
message << createdNode.majorVersion() << "." << createdNode.minorVersion();
message << createdNode.nodeSource();
message << "MetaInfo " << createdNode.metaInfo().isValid();
message << "MetaInfo " << createdNode.metaInfo().isValid() << " ";
if (createdNode.metaInfo().isValid()) {
message << createdNode.metaInfo().majorVersion() << "." << createdNode.metaInfo().minorVersion();
message << createdNode.metaInfo().componentFileName();
}
log("::nodeCreated:", message.readAll());

View File

@@ -23,85 +23,56 @@
**
****************************************************************************/
#include "backgroundcolorselection.h"
#pragma once
#include <QColorDialog>
#include <nodeinstanceview.h>
#include <utils/qtcassert.h>
#include <view3dactioncommand.h>
#include <qmldesignerplugin.h>
#include "backgroundcolorselection.h"
#include "edit3dviewconfig.h"
using namespace QmlDesigner;
namespace {
QList<QColor> readBackgroundColorConfiguration()
{
QVariant var = QmlDesigner::DesignerSettings::getValue(
QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR);
if (!var.isValid())
return {};
auto colorNameList = var.value<QList<QString>>();
QTC_ASSERT(colorNameList.size() == 2, return {});
return {colorNameList[0], colorNameList[1]};
}
void setBackgroundColorConfiguration(const QList<QColor> &colorConfig)
{
auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView();
View3DActionCommand cmd(View3DActionCommand::SelectBackgroundColor,
QVariant::fromValue(colorConfig));
view->view3DAction(cmd);
}
void saveBackgroundColorConfiguration(const QList<QColor> &colorConfig)
{
QList<QString> colorsSaved = {colorConfig[0].name(), colorConfig[1].name()};
QmlDesigner::DesignerSettings::setValue(
QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR,
QVariant::fromValue(colorsSaved));
}
} // namespace
QColorDialog *BackgroundColorSelection::createDialog(QWidget *parent)
{
auto dialog = new QColorDialog(parent);
dialog->setModal(true);
dialog->setAttribute(Qt::WA_DeleteOnClose);
const QList<QColor> oldColorConfig = readBackgroundColorConfiguration();
dialog->show();
QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog, [](const QColor &color) {
setBackgroundColorConfiguration({color, color});
});
QObject::connect(dialog, &QColorDialog::colorSelected, dialog, [](const QColor &color) {
saveBackgroundColorConfiguration({color, color});
});
if (!oldColorConfig.isEmpty()) {
QObject::connect(dialog, &QColorDialog::rejected, dialog, [oldColorConfig]() {
setBackgroundColorConfiguration(oldColorConfig);
});
}
return dialog;
}
void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *parent)
void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *parent, const QByteArray &key,
View3DActionCommand::Type cmdType)
{
if (m_dialog)
return;
m_dialog = BackgroundColorSelection::createDialog(parent);
m_dialog = BackgroundColorSelection::createColorDialog(parent, key, cmdType);
QTC_ASSERT(m_dialog, return);
QObject::connect(m_dialog, &QWidget::destroyed, m_dialog, [&]() {
m_dialog = nullptr;
});
}
QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, const QByteArray &key,
View3DActionCommand::Type cmdType)
{
auto dialog = new QColorDialog(parent);
dialog->setModal(true);
dialog->setAttribute(Qt::WA_DeleteOnClose);
QList<QColor> oldColorConfig = Edit3DViewConfig::load(key);
dialog->show();
QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog, [cmdType](const QColor &color) {
Edit3DViewConfig::set(cmdType, color);
});
QObject::connect(dialog, &QColorDialog::colorSelected, dialog, [key](const QColor &color) {
Edit3DViewConfig::save(key, color);
});
if (Edit3DViewConfig::isValid(oldColorConfig)) {
QObject::connect(dialog, &QColorDialog::rejected, dialog, [cmdType, oldColorConfig]() {
Edit3DViewConfig::set(cmdType, oldColorConfig);
});
}
return dialog;
}

View File

@@ -25,9 +25,13 @@
#pragma once
#include <QColorDialog>
#include <QByteArray>
#include <view3dactioncommand.h>
QT_FORWARD_DECLARE_CLASS(QColorDialog)
namespace QmlDesigner {
class BackgroundColorSelection : public QObject
{
Q_OBJECT
@@ -37,10 +41,13 @@ public:
: QObject{parent}
{}
static void showBackgroundColorSelectionWidget(QWidget *parent);
static void showBackgroundColorSelectionWidget(QWidget *parent, const QByteArray &key,
View3DActionCommand::Type cmdType);
private:
static QColorDialog *createDialog(QWidget *parent);
static QColorDialog *createColorDialog(QWidget *parent, const QByteArray &key,
View3DActionCommand::Type cmdType);
inline static QColorDialog *m_dialog = nullptr;
};

View File

@@ -48,7 +48,8 @@ Edit3DActionTemplate::Edit3DActionTemplate(const QString &description,
void Edit3DActionTemplate::actionTriggered(bool b)
{
if (m_type != View3DActionCommand::Empty && m_type != View3DActionCommand::SelectBackgroundColor) {
if (m_type != View3DActionCommand::Empty && m_type != View3DActionCommand::SelectBackgroundColor
&& m_type != View3DActionCommand::SelectGridColor) {
auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView();
View3DActionCommand cmd(m_type, b);
view->view3DAction(cmd);

View File

@@ -186,42 +186,17 @@ void Edit3DCanvas::dragEnterEvent(QDragEnterEvent *e)
void Edit3DCanvas::dropEvent(QDropEvent *e)
{
auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode();
QTC_ASSERT(modelNode.isValid(), return);
m_parent->view()->executeInTransaction(__FUNCTION__, [&] {
auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode();
QTC_ASSERT(modelNode.isValid(), return);
e->accept();
m_parent->view()->setSelectedModelNode(modelNode);
e->accept();
m_parent->view()->setSelectedModelNode(modelNode);
// if added node is a Model, assign it a material
if (modelNode.isSubclassOf("QtQuick3D.Model")) {
ModelNode matLib = m_parent->view()->modelNodeForId(Constants::MATERIAL_LIB_ID);
QTC_ASSERT(matLib.isValid(), return);
const QList<ModelNode> materials = matLib.directSubModelNodes();
ModelNode material;
if (materials.size() > 0) {
for (const ModelNode &mat : materials) {
if (mat.isSubclassOf("QtQuick3D.Material")) {
material = mat;
break;
}
}
}
// if no valid material, create a new default material
if (!material.isValid()) {
NodeMetaInfo metaInfo = m_parent->view()->model()->metaInfo("QtQuick3D.DefaultMaterial");
material = m_parent->view()->createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
metaInfo.minorVersion());
VariantProperty matNameProp = material.variantProperty("objectName");
matNameProp.setValue("New Material");
material.validId();
matLib.defaultNodeListProperty().reparentHere(material);
}
BindingProperty modelMatsProp = modelNode.bindingProperty("materials");
modelMatsProp.setExpression(material.id());
}
// if added node is a Model, assign it a material
if (modelNode.isSubclassOf("QtQuick3D.Model"))
m_parent->view()->assignMaterialTo3dModel(modelNode);
});
}
void Edit3DCanvas::focusOutEvent(QFocusEvent *focusEvent)

View File

@@ -28,6 +28,8 @@
#include "edit3dcanvas.h"
#include "edit3dview.h"
#include "edit3dwidget.h"
#include "edit3dviewconfig.h"
#include "backgroundcolorselection.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
@@ -42,8 +44,6 @@
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <backgroundcolorselection.h>
#include <QDebug>
#include <QToolButton>
@@ -266,6 +266,70 @@ void Edit3DView::setSeeker(SeekerSlider *slider)
m_seeker = slider;
}
Edit3DAction *Edit3DView::createSelectBackgrounColorAction()
{
QString description = QCoreApplication::translate("SelectBackgroundColorAction",
"Select Background Color");
QString tooltip = QCoreApplication::translate("SelectBackgroundColorAction",
"Select a color for the background of the 3D Editor.");
auto operation = [this](const SelectionContext &) {
BackgroundColorSelection::showBackgroundColorSelectionWidget(
edit3DWidget(),
DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR,
View3DActionCommand::SelectBackgroundColor);
};
return new Edit3DAction(
Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, View3DActionCommand::SelectBackgroundColor,
description,
{}, false, false, {}, {}, operation,
tooltip);
}
Edit3DAction *Edit3DView::createGridColorSelectionAction()
{
QString description = QCoreApplication::translate("SelectGridColorAction", "Select Grid Color");
QString tooltip = QCoreApplication::translate("SelectGridColorAction",
"Select a color for the grid lines of the 3D Editor.");
auto operation = [this](const SelectionContext &) {
BackgroundColorSelection::showBackgroundColorSelectionWidget(
edit3DWidget(),
DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR,
View3DActionCommand::SelectGridColor);
};
return new Edit3DAction(
Constants::EDIT3D_EDIT_SELECT_GRID_COLOR, View3DActionCommand::SelectGridColor,
description, {}, false, false, {}, {}, operation,
tooltip);
}
Edit3DAction *Edit3DView::createResetColorAction()
{
QString description = QCoreApplication::translate("ResetEdit3DColorsAction", "Reset Colors");
QString tooltip = QCoreApplication::translate("ResetEdit3DColorsAction",
"Reset the background color and the color of the "
"grid lines of the 3D Editor to the default valus.");
auto operation = [](const SelectionContext &) {
QList<QColor> bgColors = {QRgb(0x222222), QRgb(0x999999)};
Edit3DViewConfig::set(View3DActionCommand::SelectBackgroundColor, bgColors);
Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors);
QColor gridColor{0xaaaaaa};
Edit3DViewConfig::set(View3DActionCommand::SelectGridColor, gridColor);
Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, gridColor);
};
return new Edit3DAction(
QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, View3DActionCommand::ResetBackgroundColor,
description,
{}, false, false, {}, {}, operation,
tooltip);
}
void Edit3DView::createEdit3DActions()
{
m_selectionModeAction
@@ -338,32 +402,6 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_G), true, true, {}, {}, nullptr,
QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid."));
SelectionContextOperation showBackgroundColorSelection = [this](const SelectionContext &) {
BackgroundColorSelection::showBackgroundColorSelectionWidget(edit3DWidget());
};
m_backgroundColorSelectionAction = new Edit3DAction(
QmlDesigner::Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, View3DActionCommand::SelectBackgroundColor,
QCoreApplication::translate("SelectBackgroundColorAction", "Select Background Color"),
{}, false, false, {}, {}, showBackgroundColorSelection,
QCoreApplication::translate("SelectBackgroundColorAction", "Select a color for the background of the 3D Editor."));
m_resetBackgroundColorAction = new Edit3DAction(
QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, View3DActionCommand::ResetBackgroundColor,
QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background Color"),
{}, false, false, {}, {}, [](const SelectionContext &) {
QList<QColor> colors = {QRgb(0x222222), QRgb(0x999999)};
auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView();
View3DActionCommand cmd(View3DActionCommand::SelectBackgroundColor, QVariant::fromValue(colors));
view->view3DAction(cmd);
QList<QString> colorsToSave = {colors[0].name(), colors[1].name()};
QmlDesigner::DesignerSettings::setValue(
QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR,
QVariant::fromValue(colorsToSave));
},
QCoreApplication::translate("ResetBackgroundColorAction", "Reset background color of the 3D Editor to the default value."));
m_showSelectionBoxAction = new Edit3DAction(
QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionCommand::ShowSelectionBox,
QCoreApplication::translate("ShowSelectionBoxAction", "Show Selection Boxes"),
@@ -521,8 +559,9 @@ void Edit3DView::createEdit3DActions()
m_visibilityToggleActions << m_showCameraFrustumAction;
m_visibilityToggleActions << m_showParticleEmitterAction;
m_backgroundColorActions << m_backgroundColorSelectionAction;
m_backgroundColorActions << m_resetBackgroundColorAction;
m_backgroundColorActions << createSelectBackgrounColorAction();
m_backgroundColorActions << createGridColorSelectionAction();
m_backgroundColorActions << createResetColorAction();
}
QVector<Edit3DAction *> Edit3DView::leftActions() const

View File

@@ -85,6 +85,10 @@ private:
void createEdit3DWidget();
void checkImports();
Edit3DAction *createSelectBackgrounColorAction();
Edit3DAction *createGridColorSelectionAction();
Edit3DAction *createResetColorAction();
QPointer<Edit3DWidget> m_edit3DWidget;
QVector<Edit3DAction *> m_leftActions;
QVector<Edit3DAction *> m_rightActions;
@@ -101,8 +105,6 @@ private:
Edit3DAction *m_orientationModeAction = nullptr;
Edit3DAction *m_editLightAction = nullptr;
Edit3DAction *m_showGridAction = nullptr;
Edit3DAction *m_backgroundColorSelectionAction = nullptr;
Edit3DAction *m_resetBackgroundColorAction = nullptr;
Edit3DAction *m_showSelectionBoxAction = nullptr;
Edit3DAction *m_showIconGizmoAction = nullptr;
Edit3DAction *m_showCameraFrustumAction = nullptr;

View File

@@ -0,0 +1,98 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <view3dactioncommand.h>
#include <nodeinstanceview.h>
#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
namespace QmlDesigner {
class Edit3DViewConfig
{
public:
static QList<QColor> load(const char key[])
{
QVariant var = DesignerSettings::getValue(key);
if (!var.isValid())
return {};
auto colorNameList = var.value<QStringList>();
return Utils::transform(colorNameList, [](const QString &colorName) {
return QColor{colorName};
});
}
static void set(View3DActionCommand::Type type, const QList<QColor> &colorConfig)
{
if (colorConfig.size() == 1)
set(type, colorConfig.at(0));
else
setVariant(type, QVariant::fromValue(colorConfig));
}
static void set(View3DActionCommand::Type type, const QColor &color)
{
setVariant(type, QVariant::fromValue(color));
}
static void save(const QByteArray &key, const QList<QColor> &colorConfig)
{
QStringList colorNames = Utils::transform(colorConfig, [](const QColor &color) {
return color.name();
});
saveVariant(key, QVariant::fromValue(colorNames));
}
static void save(const QByteArray &key, const QColor &color)
{
saveVariant(key, QVariant::fromValue(color.name()));
}
static bool isValid(const QList<QColor> &colorConfig) { return !colorConfig.isEmpty(); }
private:
static void setVariant(View3DActionCommand::Type type, const QVariant &colorConfig)
{
auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView();
View3DActionCommand cmd(type, colorConfig);
view->view3DAction(cmd);
}
static void saveVariant(const QByteArray &key, const QVariant &colorConfig)
{
DesignerSettings::setValue(key, colorConfig);
}
};
} // namespace QmlDesigner

View File

@@ -33,6 +33,8 @@
#include <rewritingexception.h>
#include "qmldesignerconstants.h"
#include <utils/qtcassert.h>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QLoggingCategory>
@@ -405,10 +407,23 @@ void DragTool::move(const QPointF &scenePosition, const QList<QGraphicsItem *> &
void DragTool::commitTransaction()
{
try {
handleView3dDrop();
m_rewriterTransaction.commit();
} catch (const RewritingException &e) {
e.showException();
}
}
void DragTool::handleView3dDrop()
{
// If a View3D is dropped, we need to assign material to the included model
for (const QmlItemNode &dragNode : qAsConst(m_dragNodes)) {
if (dragNode.modelNode().isSubclassOf("QtQuick3D.View3D")) {
const QList<ModelNode> models = dragNode.modelNode().subModelNodesOfType("QtQuick3D.Model");
QTC_ASSERT(models.size() == 1, return);
view()->assignMaterialTo3dModel(models.at(0));
}
}
}
} // namespace QmlDesigner

View File

@@ -90,6 +90,7 @@ protected:
void move(const QPointF &scenePos, const QList<QGraphicsItem *> &itemList);
void createDragNodes(const QMimeData *mimeData, const QPointF &scenePosition, const QList<QGraphicsItem *> &itemList);
void commitTransaction();
void handleView3dDrop();
private:
MoveManipulator m_moveManipulator;

View File

@@ -471,7 +471,7 @@ void FormEditorView::documentMessagesChanged(const QList<DocumentMessage> &error
if (!errors.isEmpty() && !model()->rewriterView()->hasIncompleteTypeInformation())
m_formEditorWidget->showErrorMessageBox(errors);
else
else if (rewriterView()->errors().isEmpty())
m_formEditorWidget->hideErrorMessageBox();
checkRootModelNode();

View File

@@ -74,7 +74,7 @@ static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString
formatter->plainTextEdit()->verticalScrollBar()->maximum());
}
static const int rowHeight = 28;
static const int rowHeight = 32;
static const int checkBoxColWidth = 18;
static const int labelMinWidth = 130;
static const int controlMinWidth = 65;
@@ -781,7 +781,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
int &globalOptionsHeight = advanced ? m_advancedData.optionsHeight : m_simpleData.optionsHeight;
globalOptionRows = qMax(globalOptionRows, optionRows);
globalOptionsHeight = qMax(rowHeight * optionRows + 20, globalOptionsHeight);
layout->setContentsMargins(8, 8, 8, 0);
layout->setContentsMargins(8, 6, 8, 0);
return layout;
}

View File

@@ -37,6 +37,7 @@
#include <utils/algorithm.h>
#include <utils/runextensions.h>
#include <utils/qtcassert.h>
#include <QApplication>
#include <QDir>
@@ -90,21 +91,16 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
if (!isCancelled()) {
const auto parseData = m_parseData;
for (const auto &pd : parseData) {
if (!startImportProcess(pd)) {
addError(tr("Failed to start import 3D asset process."),
pd.sourceInfo.absoluteFilePath());
m_parseData.remove(pd.importId);
}
}
for (const auto &pd : parseData)
m_puppetQueue.append(pd.importId);
startNextImportProcess();
}
if (!isCancelled()) {
// Wait for puppet processes to finish
if (m_qmlPuppetProcesses.empty()) {
if (m_puppetQueue.isEmpty() && !m_puppetProcess) {
postImport();
} else {
m_qmlPuppetCount = static_cast<int>(m_qmlPuppetProcesses.size());
const QString progressTitle = tr("Importing 3D assets.");
addInfo(progressTitle);
notifyProgress(0, progressTitle);
@@ -142,21 +138,14 @@ void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &sr
emit infoReported(infoMsg, srcPath);
}
void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus,
int importId)
void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitCode)
++m_qmlImportFinishedCount;
m_puppetProcess.reset();
m_qmlPuppetProcesses.erase(
std::remove_if(m_qmlPuppetProcesses.begin(), m_qmlPuppetProcesses.end(),
[&](const auto &entry) {
return !entry || entry->state() == QProcess::NotRunning;
}));
if (m_parseData.contains(importId)) {
const ParseData &pd = m_parseData[importId];
if (m_parseData.contains(m_currentImportId)) {
const ParseData &pd = m_parseData[m_currentImportId];
QString errStr;
if (exitStatus == QProcess::ExitStatus::CrashExit) {
errStr = tr("Import process crashed.");
@@ -179,15 +168,19 @@ void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::Exi
addError(tr("Asset import process failed: \"%1\".")
.arg(pd.sourceInfo.absoluteFilePath()));
addError(errStr);
m_parseData.remove(importId);
m_parseData.remove(m_currentImportId);
}
}
if (m_qmlImportFinishedCount == m_qmlPuppetCount) {
int finishedCount = m_parseData.size() - m_puppetQueue.size();
if (!m_puppetQueue.isEmpty())
startNextImportProcess();
if (m_puppetQueue.isEmpty() && !m_puppetProcess) {
notifyProgress(100);
QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport);
} else {
notifyProgress(int(100. * (double(m_qmlImportFinishedCount) / double(m_qmlPuppetCount))));
notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size()))));
}
}
@@ -196,17 +189,17 @@ void ItemLibraryAssetImporter::iconProcessFinished(int exitCode, QProcess::ExitS
Q_UNUSED(exitCode)
Q_UNUSED(exitStatus)
m_qmlPuppetProcesses.erase(
std::remove_if(m_qmlPuppetProcesses.begin(), m_qmlPuppetProcesses.end(),
[&](const auto &entry) {
return !entry || entry->state() == QProcess::NotRunning;
}));
m_puppetProcess.reset();
if (m_qmlPuppetProcesses.empty()) {
int finishedCount = m_parseData.size() - m_puppetQueue.size();
if (!m_puppetQueue.isEmpty())
startNextIconProcess();
if (m_puppetQueue.isEmpty() && !m_puppetProcess) {
notifyProgress(100);
QTimer::singleShot(0, this, &ItemLibraryAssetImporter::finalizeQuick3DImport);
} else {
notifyProgress(int(100. * (1. - (double(m_qmlPuppetProcesses.size()) / double(m_qmlPuppetCount)))));
notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size()))));
}
}
@@ -225,11 +218,11 @@ void ItemLibraryAssetImporter::reset()
m_tempDir = new QTemporaryDir;
m_importFiles.clear();
m_overwrittenImports.clear();
m_qmlPuppetProcesses.clear();
m_qmlPuppetCount = 0;
m_qmlImportFinishedCount = 0;
m_puppetProcess.reset();
m_parseData.clear();
m_requiredImports.clear();
m_currentImportId = 0;
m_puppetQueue.clear();
}
void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
@@ -351,7 +344,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa
return true;
}
void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd)
void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd)
{
QDir outDir = pd.outDir;
if (pd.originalAssetName != pd.assetName) {
@@ -452,8 +445,10 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd)
"QtQuick3D", impVersionStr));
}
}
if (impVersionMajor > 0 && impVersionMajor < 6
&& startIconProcess(24, iconFileName, qmlIt.filePath())) {
if (impVersionMajor > 0 && impVersionMajor < 6) {
pd.iconFile = iconFileName;
pd.iconSource = qmlIt.filePath();
m_puppetQueue.append(pd.importId);
// Since icon is generated by external process, the file won't be
// ready for asset gathering below, so assume its generation succeeds
// and add it now.
@@ -589,84 +584,101 @@ ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAsset
return OverwriteResult::Skip;
}
bool ItemLibraryAssetImporter::startImportProcess(const ParseData &pd)
void ItemLibraryAssetImporter::startNextImportProcess()
{
if (m_puppetQueue.isEmpty())
return;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model) {
PuppetCreator puppetCreator(doc->currentTarget(), model);
puppetCreator.createQml2PuppetExecutableIfMissing();
QStringList puppetArgs;
QJsonDocument optDoc(pd.options);
puppetArgs << "--import3dAsset" << pd.sourceInfo.absoluteFilePath()
<< pd.outDir.absolutePath() << QString::fromUtf8(optDoc.toJson());
bool done = false;
while (!m_puppetQueue.isEmpty() && !done) {
const ParseData pd = m_parseData.value(m_puppetQueue.takeLast());
QStringList puppetArgs;
QJsonDocument optDoc(pd.options);
QProcessUniquePointer process = puppetCreator.createPuppetProcess(
"custom",
{},
[&] {},
[&](int exitCode, QProcess::ExitStatus exitStatus) {
importProcessFinished(exitCode, exitStatus, pd.importId);
},
puppetArgs);
puppetArgs << "--import3dAsset" << pd.sourceInfo.absoluteFilePath()
<< pd.outDir.absolutePath() << QString::fromUtf8(optDoc.toJson());
if (process->waitForStarted(5000)) {
m_qmlPuppetProcesses.push_back(std::move(process));
return true;
} else {
process.reset();
m_currentImportId = pd.importId;
m_puppetProcess = puppetCreator.createPuppetProcess(
"custom",
{},
[&] {},
[&](int exitCode, QProcess::ExitStatus exitStatus) {
importProcessFinished(exitCode, exitStatus);
},
puppetArgs);
if (m_puppetProcess->waitForStarted(10000)) {
done = true;
} else {
addError(tr("Failed to start import 3D asset process."),
pd.sourceInfo.absoluteFilePath());
m_parseData.remove(pd.importId);
m_puppetProcess.reset();
}
}
}
return false;
}
bool ItemLibraryAssetImporter::startIconProcess(int size, const QString &iconFile,
const QString &iconSource)
void ItemLibraryAssetImporter::startNextIconProcess()
{
if (m_puppetQueue.isEmpty())
return;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model) {
PuppetCreator puppetCreator(doc->currentTarget(), model);
puppetCreator.createQml2PuppetExecutableIfMissing();
QStringList puppetArgs;
puppetArgs << "--rendericon" << QString::number(size) << iconFile << iconSource;
QProcessUniquePointer process = puppetCreator.createPuppetProcess(
"custom",
{},
[&] {},
[&](int exitCode, QProcess::ExitStatus exitStatus) {
iconProcessFinished(exitCode, exitStatus);
},
puppetArgs);
if (process->waitForStarted(5000)) {
m_qmlPuppetProcesses.push_back(std::move(process));
return true;
} else {
process.reset();
bool done = false;
while (!m_puppetQueue.isEmpty() && !done) {
const ParseData pd = m_parseData.value(m_puppetQueue.takeLast());
QStringList puppetArgs;
puppetArgs << "--rendericon" << QString::number(24) << pd.iconFile << pd.iconSource;
m_puppetProcess = puppetCreator.createPuppetProcess(
"custom",
{},
[&] {},
[&](int exitCode, QProcess::ExitStatus exitStatus) {
iconProcessFinished(exitCode, exitStatus);
},
puppetArgs);
if (m_puppetProcess->waitForStarted(10000)) {
done = true;
} else {
addError(tr("Failed to start icon generation process."),
pd.sourceInfo.absoluteFilePath());
m_puppetProcess.reset();
}
}
}
return false;
}
void ItemLibraryAssetImporter::postImport()
{
Q_ASSERT(m_qmlPuppetProcesses.empty());
QTC_ASSERT(m_puppetQueue.isEmpty() && !m_puppetProcess, return);
if (!isCancelled()) {
for (const auto &pd : qAsConst(m_parseData))
for (auto &pd : m_parseData)
postParseQuick3DAsset(pd);
startNextIconProcess();
}
if (!isCancelled()) {
// Wait for icon generation processes to finish
if (m_qmlPuppetProcesses.empty()) {
if (m_puppetQueue.isEmpty() && !m_puppetProcess) {
finalizeQuick3DImport();
} else {
m_qmlPuppetCount = static_cast<int>(m_qmlPuppetProcesses.size());
const QString progressTitle = tr("Generating icons.");
addInfo(progressTitle);
notifyProgress(0, progressTitle);

View File

@@ -74,7 +74,7 @@ signals:
void importFinished();
private slots:
void importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus, int importId);
void importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void iconProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
@@ -87,6 +87,8 @@ private:
QString assetName;
QString originalAssetName;
int importId;
QString iconFile;
QString iconSource;
};
void notifyFinished();
@@ -96,7 +98,7 @@ private:
const QSet<QString> &preselectedFilesForOverwrite);
bool preParseQuick3DAsset(const QString &file, ParseData &pd,
const QSet<QString> &preselectedFilesForOverwrite);
void postParseQuick3DAsset(const ParseData &pd);
void postParseQuick3DAsset(ParseData &pd);
void copyImportedFiles();
void notifyProgress(int value, const QString &text);
@@ -110,8 +112,8 @@ private:
};
OverwriteResult confirmAssetOverwrite(const QString &assetName);
bool startImportProcess(const ParseData &pd);
bool startIconProcess(int size, const QString &iconFile, const QString &iconSource);
void startNextImportProcess();
void startNextIconProcess();
void postImport();
void finalizeQuick3DImport();
QString sourceSceneTargetFilePath(const ParseData &pd);
@@ -122,12 +124,12 @@ private:
bool m_cancelled = false;
QString m_importPath;
QTemporaryDir *m_tempDir = nullptr;
std::vector<QProcessUniquePointer> m_qmlPuppetProcesses;
int m_qmlPuppetCount = 0;
int m_qmlImportFinishedCount = 0;
QProcessUniquePointer m_puppetProcess;
int m_importIdCounter = 0;
int m_currentImportId = 0;
QHash<int, ParseData> m_parseData;
QString m_progressTitle;
QList<Import> m_requiredImports;
QList<int> m_puppetQueue;
};
} // QmlDesigner

View File

@@ -126,10 +126,6 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event)
m_itemToDrag = {};
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_itemToDrag = {};
if (model)
model->endDrag();
}
return QObject::eventFilter(obj, event);

View File

@@ -120,6 +120,11 @@ void MaterialBrowserModel::setHasModelSelection(bool b)
emit hasModelSelectionChanged();
}
QList<ModelNode> MaterialBrowserModel::materials() const
{
return m_materialList;
}
void MaterialBrowserModel::setSearchText(const QString &searchText)
{
QString lowerSearchText = searchText.toLower();

View File

@@ -58,6 +58,7 @@ public:
bool hasModelSelection() const;
void setHasModelSelection(bool b);
QList<ModelNode> materials() const;
void setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport);
void removeMaterial(const ModelNode &material);
void updateMaterialName(const ModelNode &material);

View File

@@ -96,11 +96,19 @@ void MaterialBrowserView::modelAttached(Model *model)
m_widget->clearSearchFilter();
m_hasQuick3DImport = model->hasImport("QtQuick3D");
QTimer::singleShot(0, this, &MaterialBrowserView::refreshModel);
// Project load is already very busy and may even trigger puppet reset, so let's wait a moment
// before refreshing the model
QTimer::singleShot(1000, this, [this]() {
refreshModel(true);
});
}
void MaterialBrowserView::refreshModel()
void MaterialBrowserView::refreshModel(bool updateImages)
{
if (!model() || !model()->nodeInstanceView())
return;
ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID);
QList <ModelNode> materials;
@@ -114,8 +122,10 @@ void MaterialBrowserView::refreshModel()
m_widget->materialBrowserModel()->setMaterials(materials, m_hasQuick3DImport);
for (const ModelNode &node : std::as_const(materials))
model()->nodeInstanceView()->previewImageDataForGenericNode(node, {});
if (updateImages) {
for (const ModelNode &node : std::as_const(materials))
model()->nodeInstanceView()->previewImageDataForGenericNode(node, {});
}
}
bool MaterialBrowserView::isMaterial(const ModelNode &node) const
@@ -203,8 +213,12 @@ void MaterialBrowserView::nodeReparented(const ModelNode &node,
bool matRemoved = oldParentNode.isValid() && oldParentNode.id() == Constants::MATERIAL_LIB_ID;
if (matAdded || matRemoved) {
refreshModel();
if (matAdded && !m_puppetResetPending) {
// Workaround to fix various material issues all likely caused by QTBUG-103316
resetPuppet();
m_puppetResetPending = true;
}
refreshModel(!matAdded);
int idx = m_widget->materialBrowserModel()->materialIndex(node);
m_widget->materialBrowserModel()->selectMaterial(idx);
}
@@ -251,7 +265,9 @@ void MaterialBrowserView::importsChanged(const QList<Import> &addedImports, cons
return;
m_hasQuick3DImport = hasQuick3DImport;
refreshModel();
// Import change will trigger puppet reset, so we don't want to update previews immediately
refreshModel(false);
}
void MaterialBrowserView::customNotification(const AbstractView *view, const QString &identifier,
@@ -269,4 +285,22 @@ void MaterialBrowserView::customNotification(const AbstractView *view, const QSt
}
}
void MaterialBrowserView::instancesCompleted(const QVector<ModelNode> &completedNodeList)
{
for (const ModelNode &node : completedNodeList) {
// We use root node completion as indication of puppet reset
if (node.isRootNode()) {
m_puppetResetPending = false;
QTimer::singleShot(1000, this, [this]() {
if (!model() || !model()->nodeInstanceView())
return;
const QList<ModelNode> materials = m_widget->materialBrowserModel()->materials();
for (const ModelNode &node : materials)
model()->nodeInstanceView()->previewImageDataForGenericNode(node, {});
});
break;
}
}
}
} // namespace QmlDesigner

View File

@@ -60,14 +60,16 @@ public:
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void instancesCompleted(const QVector<ModelNode> &completedNodeList) override;
private:
void refreshModel();
void refreshModel(bool updateImages);
bool isMaterial(const ModelNode &node) const;
QPointer<MaterialBrowserWidget> m_widget;
bool m_hasQuick3DImport = false;
bool m_autoSelectModelMaterial = false; // TODO: wire this to some action
bool m_puppetResetPending = false;
};
} // namespace QmlDesigner

View File

@@ -32,6 +32,7 @@
#include <designermcumanager.h>
#include <documentmanager.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/stylehelper.h>
@@ -103,6 +104,27 @@ bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event)
if (event->type() == QEvent::FocusOut) {
if (obj == m_quickWidget.data())
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "closeContextMenu");
} else if (event->type() == QMouseEvent::MouseMove) {
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
QTC_ASSERT(document, return false);
Model *model = document->currentModel();
QTC_ASSERT(model, return false);
if (m_materialToDrag.isValid()) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) {
QByteArray data;
QMimeData *mimeData = new QMimeData;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << m_materialToDrag.internalId();
mimeData->setData(Constants::MIME_TYPE_MATERIAL, data);
mimeData->removeFormat("text/plain");
model->startDrag(mimeData, m_previewImageProvider->requestPixmap(
QString::number(m_materialToDrag.internalId()), nullptr, {128, 128}));
m_materialToDrag = {};
}
}
}
return QObject::eventFilter(obj, event);
@@ -166,6 +188,12 @@ void MaterialBrowserWidget::handleSearchfilterChanged(const QString &filterText)
}
}
void MaterialBrowserWidget::startDragMaterial(int index, const QPointF &mousePos)
{
m_materialToDrag = m_materialBrowserModel->materialAt(index);
m_dragStartPoint = mousePos.toPoint();
}
QString MaterialBrowserWidget::qmlSourcesPath()
{
#ifdef SHARE_QML_PATH

View File

@@ -69,6 +69,7 @@ public:
void updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap);
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
Q_INVOKABLE void startDragMaterial(int index, const QPointF &mousePos);
QQuickWidget *quickWidget() const;
@@ -86,6 +87,9 @@ private:
PreviewImageProvider *m_previewImageProvider = nullptr;
QString m_filterText;
ModelNode m_materialToDrag;
QPoint m_dragStartPoint;
};
} // namespace QmlDesigner

View File

@@ -73,52 +73,21 @@ MaterialEditorView::MaterialEditorView(QWidget *parent)
m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F7), m_stackedWidget);
connect(m_updateShortcut, &QShortcut::activated, this, &MaterialEditorView::reloadQml);
m_ensureMatLibTimer.callOnTimeout([this] {
if (model() && model()->rewriterView() && !model()->rewriterView()->hasIncompleteTypeInformation()
&& model()->rewriterView()->errors().isEmpty()) {
executeInTransaction("MaterialEditorView::MaterialEditorView", [this] {
ensureMaterialLibraryNode();
});
m_ensureMatLibTimer.stop();
}
});
m_stackedWidget->setStyleSheet(Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
m_stackedWidget->setMinimumWidth(250);
}
void MaterialEditorView::ensureMaterialLibraryNode()
{
if (!m_hasQuick3DImport)
return;
m_materialLibrary = modelNodeForId(Constants::MATERIAL_LIB_ID);
if (m_materialLibrary.isValid())
return;
// create material library node
TypeName nodeType = rootModelNode().isSubclassOf("QtQuick3D.Node") ? "QtQuick3D.Node" : "QtQuick.Item";
NodeMetaInfo metaInfo = model()->metaInfo(nodeType);
m_materialLibrary = createModelNode(nodeType, metaInfo.majorVersion(), metaInfo.minorVersion());
m_materialLibrary.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID);
rootModelNode().defaultNodeListProperty().reparentHere(m_materialLibrary);
const QList<ModelNode> materials = rootModelNode().subModelNodesOfType("QtQuick3D.Material");
if (materials.isEmpty())
return;
RewriterTransaction transaction = beginRewriterTransaction(
"MaterialEditorView::ensureMaterialLibraryNode");
try {
// move all materials to under material library node
for (const ModelNode &node : materials) {
// if material has no name, set name to id
QString matName = node.variantProperty("objectName").value().toString();
if (matName.isEmpty()) {
VariantProperty objNameProp = node.variantProperty("objectName");
objNameProp.setValue(node.id());
}
m_materialLibrary.defaultNodeListProperty().reparentHere(node);
}
} catch (Exception &e) {
e.showException();
}
}
MaterialEditorView::~MaterialEditorView()
{
qDeleteAll(m_qmlBackendHash);
@@ -447,15 +416,15 @@ void MaterialEditorView::handleToolBarAction(int action)
}
case MaterialEditorContextObject::AddNewMaterial: {
ensureMaterialLibraryNode();
if (!model())
break;
executeInTransaction("MaterialEditorView:handleToolBarAction", [&] {
NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.DefaultMaterial");
ModelNode newMatNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
metaInfo.minorVersion());
renameMaterial(newMatNode, "New Material");
m_materialLibrary.defaultNodeListProperty().reparentHere(newMatNode);
materialLibraryNode().defaultNodeListProperty().reparentHere(newMatNode);
});
break;
}
@@ -567,6 +536,11 @@ void MaterialEditorView::modelAttached(Model *model)
m_hasQuick3DImport = model->hasImport("QtQuick3D");
// Creating the material library node on model attach causes errors as long as the type information
// not complete yet, so we keep checking until type info is complete.
if (m_hasQuick3DImport)
m_ensureMatLibTimer.start(500);
if (!m_setupCompleted) {
reloadQml();
m_setupCompleted = true;
@@ -580,8 +554,6 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
m_qmlBackEnd->materialEditorTransaction()->end();
}
void MaterialEditorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
@@ -589,6 +561,7 @@ void MaterialEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
if (noValidSelection())
return;
bool changed = false;
for (const AbstractProperty &property : propertyList) {
ModelNode node(property.parentModelNode());
@@ -597,8 +570,11 @@ void MaterialEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
changed = true;
}
}
if (changed)
requestPreviewRender();
}
void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
@@ -660,7 +636,7 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node, const Prope
// request render image for the selected material node
void MaterialEditorView::requestPreviewRender()
{
if (m_selectedMaterial.isValid())
if (model() && model()->nodeInstanceView() && m_selectedMaterial.isValid())
model()->nodeInstanceView()->previewImageDataForGenericNode(m_selectedMaterial, {});
}
@@ -707,6 +683,7 @@ void MaterialEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
m_locked = true;
bool changed = false;
for (const QPair<ModelNode, PropertyName> &propertyPair : propertyList) {
const ModelNode modelNode = propertyPair.first;
const QmlObjectNode qmlObjectNode(modelNode);
@@ -718,8 +695,11 @@ void MaterialEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
setValue(modelNode, property.name(), qmlObjectNode.instanceValue(property.name()));
else
setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name()));
changed = true;
}
}
if (changed)
requestPreviewRender();
m_locked = false;
}
@@ -744,6 +724,9 @@ void MaterialEditorView::importsChanged(const QList<Import> &addedImports, const
m_hasQuick3DImport = model()->hasImport("QtQuick3D");
m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
if (m_hasQuick3DImport)
m_ensureMatLibTimer.start(500);
resetView();
}
@@ -763,7 +746,8 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
{
QTC_ASSERT(material.isValid(), return);
ensureMaterialLibraryNode();
if (!model())
return;
TypeName matType = material.type();
QmlObjectNode sourceMat(material);
@@ -790,7 +774,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
duplicateMat.setBindingProperty(prop.name(), prop.toBindingProperty().expression());
}
m_materialLibrary.defaultNodeListProperty().reparentHere(duplicateMat);
materialLibraryNode().defaultNodeListProperty().reparentHere(duplicateMat);
});
}
@@ -816,11 +800,16 @@ void MaterialEditorView::customNotification(const AbstractView *view, const QStr
void QmlDesigner::MaterialEditorView::highlightSupportedProperties(bool highlight)
{
if (!m_selectedMaterial.isValid())
return;
DesignerPropertyMap &propMap = m_qmlBackEnd->backendValuesPropertyMap();
const QStringList propNames = propMap.keys();
NodeMetaInfo metaInfo = m_selectedMaterial.metaInfo();
QTC_ASSERT(metaInfo.isValid(), return);
for (const QString &propName : propNames) {
if (propName.endsWith("Map")) {
if (metaInfo.propertyTypeName(propName.toLatin1()) == "QtQuick3D.Texture") {
QObject *propEditorValObj = propMap.value(propName).value<QObject *>();
PropertyEditorValue *propEditorVal = qobject_cast<PropertyEditorValue *>(propEditorValObj);
propEditorVal->setHasActiveDrag(highlight);
@@ -852,7 +841,6 @@ void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const Prop
{
m_locked = true;
m_qmlBackEnd->setValue(qmlObjectNode, name, value);
requestPreviewRender();
m_locked = false;
}

View File

@@ -99,7 +99,6 @@ private:
void highlightSupportedProperties(bool highlight = true);
QString generateIdFromName(const QString &name);
void ensureMaterialLibraryNode();
void requestPreviewRender();
void applyMaterialToSelectedModels(const ModelNode &material, bool add = false);
@@ -115,7 +114,7 @@ private:
bool noValidSelection() const;
ModelNode m_selectedMaterial;
ModelNode m_materialLibrary;
QTimer m_ensureMatLibTimer;
QShortcut *m_updateShortcut = nullptr;
int m_timerId = 0;
QStackedWidget *m_stackedWidget = nullptr;

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

View File

@@ -56,6 +56,8 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
// ParticleAbstractShape3D
// -> ParticleEmitter3D
// -> Attractor3D
// Material
// -> Model
const TypeName textureType = "QtQuick3D.Texture";
if (insertInfo.isSubclassOf(textureType)) {
@@ -104,6 +106,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
if (parentInfo.isSubclassOf("QtQuick3D.Particles3D.ParticleEmitter3D")
|| parentInfo.isSubclassOf("QtQuick3D.Particles3D.Attractor3D"))
propertyList.append("shape");
} else if (insertInfo.isSubclassOf("QtQuick3D.Material")) {
if (parentInfo.isSubclassOf("QtQuick3D.Particles3D.Model"))
propertyList.append("materials");
}
}

View File

@@ -13,5 +13,6 @@
<file>export_unchecked.png</file>
<file>export_unchecked@2x.png</file>
<file>tooltip_placeholder.png</file>
<file>checkers.png</file>
</qresource>
</RCC>

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