Merge "Merge remote-tracking branch 'origin/7.0' into 8.0" into 8.0
BIN
doc/qtdesignstudio/images/icons/apply-material.png
Normal file
|
After Width: | Height: | Size: 779 B |
BIN
doc/qtdesignstudio/images/material-editor-browser.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
doc/qtdesignstudio/images/materials-remove-material.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/qtdesignstudio/images/navigator-material-texture.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 13 KiB |
BIN
doc/qtdesignstudio/images/studio-qtquick-3d-material.webp
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/qtdesignstudio/images/timeline-bind-animation-state.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
doc/qtdesignstudio/images/timeline-insert-keyframe.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
doc/qtdesignstudio/images/timeline-per-property-recording.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
doc/qtdesignstudio/images/timeline-settings-dialog-second.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
doc/qtdesignstudio/images/timeline-settings-dialog.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
doc/qtdesignstudio/images/timeline-settings-property-binding.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/qtdesignstudio/images/timeline-states.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}.
|
||||
|
||||
266
doc/qtdesignstudio/src/views/studio-material-editor.qdoc
Normal 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.
|
||||
|
||||
*/
|
||||
@@ -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);
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
ParticlesRestart,
|
||||
ParticlesSeek,
|
||||
SelectBackgroundColor,
|
||||
SelectGridColor,
|
||||
ResetBackgroundColor,
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
BIN
share/qtcreator/qml/qmlpuppet/mockfiles/images/static_floor.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -33,7 +33,7 @@ PropertyEditorPane {
|
||||
id: itemPane
|
||||
|
||||
ComponentSection {
|
||||
showState: majorVersion >= 6
|
||||
showState: majorQtQuickVersion >= 6
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
%{FormClass} {
|
||||
button.onClicked: console.log("Button Pressed")
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ Project {
|
||||
/* Required for deployment */
|
||||
targetDirectory: "/opt/%{ProjectName}"
|
||||
|
||||
qdsVersion: "3.4"
|
||||
qdsVersion: "3.5"
|
||||
|
||||
quickVersion: "%{QtQuickVersion}"
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -513,7 +513,7 @@ public:
|
||||
->currentModel();
|
||||
|
||||
if (currentModel->rewriterView()
|
||||
&& currentModel->rewriterView()->inErrorState()) {
|
||||
&& !currentModel->rewriterView()->errors().isEmpty()) {
|
||||
throw DocumentError{};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
98
src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
BIN
src/plugins/qmldesigner/components/navigator/checkers.png
Normal file
|
After Width: | Height: | Size: 80 B |
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||