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

This commit is contained in:
The Qt Project
2022-10-18 09:51:56 +00:00
74 changed files with 1782 additions and 1113 deletions

View File

@@ -299,22 +299,26 @@
users, but always place example values also in the text.
\list
\li Use the screen resolution of 1366x768 (available on the most
\li Use the screen resolution of 1920x1080 (available on the most
commonly used screens, as of this writing).
\li Use the aspect ratio of 16:9.
\li Open the application in the maximum size on full screen.
\note Use display scaling 100%.
\li Use your favorite tool to take the screen shot.
\li Include only the part of the screen that you need (you can crop the
image also in the screen capture tool).
image also in the screen capture tool). In \QDS, close all
unnecessary views to avoid clutter.
\li Do not scale or resize the images in the tool because it causes
reduced visual quality and bigger file size. Also, the CSS we use
online scales down images if needed (their width is larger than 800
pixels).
\li To highlight parts of the screen shot, use the images of numbers
that are stored in \c{doc/images/numbers} in the \QC repository.
that are stored in \c{qtcreator/doc/qtcreator/images/numbers} in
the \QC repository.
\li Before you submit the images to the repository, optimize them to
\li Before you submit PNG images to the repository, optimize them to
save space.
\endlist
@@ -322,9 +326,9 @@
You can use number icons in screenshots to highlight parts of the screenshot
(instead of using red arrows or borders, or something similar). You can then
refer to the numbers in text. For and example, see the
\l{https://doc.qt.io/qt/topics-app-development.html}{Development Tools}
topic in the Qt reference documentation.
refer to the numbers in text. For an example, see the
\l{https://doc.qt.io/qtcreator/creator-quick-tour.html}{User Interface}
topic in the \QC Manual.
This improves the consistency of the look and feel of Qt documentation,
and eliminates the need to describe parts of the UI in the text because
@@ -332,8 +336,8 @@
brackets.
You can find a set of images that show the numbers from 1 to 10 in the
\c doc/images/numbers directory (or in the \c qtdoc module sources in
\c doc/images/numbers).
\c qtcreator/doc/qtcreator/images/numbers directory (or in the \c qtdoc
module sources in \c doc/images/numbers).
To use the numbers, copy-paste the number images on the screenshot to the
places that you want to refer to from text.
@@ -358,6 +362,13 @@
recolors icons in \c qtcreator/doc/qtcreator/images/icons. Use the
\c -docspath option to specify the path to another icon source directory.
For example, if you saved the new icons in \c {C:\iconconversions}, switch to
the \c {qtcreator\src\tools\icons} folder and enter:
\badcode
recolordocsicons.py -docspath C:\iconconversions
\endcode
To run the script, you will need to install the following tools and add them
to the PATH:
@@ -367,20 +378,26 @@
\li optipng
\endlist
\section2 Saving Images
Save images in PNG or WebP format in the \QC project folder in the
\c doc/qtcreator/images or \c doc/qtdesignstudio/images folder. Binary
images can easily add megabytes to the Git
history. To keep the history as small as possible, the Git post-commit hooks
remind you to try to keep image file size below 50 kilobytes. To achieve this
goal, crop images so that only relevant information is visible in them.
If your screenshot contains lots of colorful content or a photo, for example,
consider saving it in WebP format for a smaller image file size.
\section2 Optimizing Images
Save images in the PNG format in the \QC project folder in the
\c {doc/images} folder. Binary images can easily add megabytes to the Git
history. To keep the history as small as possible, the Git post-commit hooks
remind you to try to keep image size below 50 kilobytes. To achieve this
goal, crop images so that only relevant information is visible in them.
Before committing images, optimize them by using an image optimization tool.
Optimization should not visibly reduce image quality. If it does, do not do
it.
Before committing PNG images, optimize them by using an image optimization
tool. Optimization should not visibly reduce image quality. If it does, try
saving the image as WebP instead.
You can use a web service, such as \l{https://tinypng.com}, or an image
optimization tool to shrink the images. For example, you can use the Radical
optimization tool to shrink PNG images. For example, you can use the Radical
Image Optimization Tool (RIOT) or OptiPNG on Windows, ImageOptim on
\macos, or some other tool available on Linux.
@@ -425,6 +442,16 @@
optipng -o 7 -strip all doc/images/<screenshot_name>
\endcode
\section2 Creating GIF Videos
Sometimes it is easier to explain how something works by recording
a short GIF video. You can use any tool you like, for example
\l {https://www.screentogif.com/}{ScreenToGif}. GIF videos are typically
bigger than screenshots, so try to make them as short and to the point as
you can.
Use the \c {\image} command to add GIF files to the documentation.
\section2 Linking to Youtube Videos
You can use the \c {\youtube} macro to link to a video on Youtube. The HTML
@@ -477,11 +504,7 @@
\section2 Setting Up Documentation Builds
For more information about setting up the build environment with a
self-built Qt if you do not want to build the whole Qt, see
\l{https://wiki.qt.io/Building_Qt_Documentation}{Building Qt Documentation}
on the Qt wiki.
You can use an installed Qt version to build the documentation.
The content and formatting of documentation are separated in QDoc.
The documentation configuration, style sheets, and templates have
changed over time, so they differ between Qt and \QC versions.
@@ -532,7 +555,7 @@
For example (all on one line):
\badcode
C:\dev\qtc-doc-build>cmake -DWITH_DOCS=ON
"-DCMAKE_PREFIX_PATH=C:\Qt\5.15.1\msvc2019_64"
"-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64"
C:\dev\qtc-super\qtcreator
\endcode
\li To also build Extending \QC Manual, add the following option:
@@ -544,7 +567,7 @@
\badcode
C:\dev\qtc-doc-build>cmake -DWITH_DOCS=ON -DBUILD_DEVELOPER_DOCS=ON
"-DCMAKE_MODULE_PATH=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding"
"-DCMAKE_PREFIX_PATH=C:\Qt\5.15.1\msvc2019_64"
"-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64"
C:\dev\qtc-super\qtcreator
\endcode
\li To build the docs using the online style, use the following option
@@ -556,7 +579,7 @@
C:\dev\qtc-doc-build>cmake -DWITH_ONLINE_DOCS=ON
-DBUILD_DEVELOPER_DOCS=ON
"-DCMAKE_MODULE_PATH=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding"
"-DCMAKE_PREFIX_PATH=C:\Qt\5.15.1\msvc2019_64"
"-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64"
C:\dev\qtc-super\qtcreator
\endcode
\note If you already ran CMake \c {-DWITH_DOCS=ON} in a folder and

View File

@@ -4,7 +4,7 @@
/*!
\page state-transition-animations.html
\ingroup gstutorials
\sa States, {Transitions}, {Adding States}
\sa States, {Transitions}, {Working with States}
\title Animated State Transitions
\brief Illustrates how to create animated state transitions.
@@ -12,7 +12,7 @@
\image animated-state-transitions.jpg
The \e{Animated State Transitions} tutorial illustrates how you can animate
the transition between \l{Adding States}{states}.
the transition between \l{Working with States}{states}.
The starting point of this tutorial is the Car Demo project, you can
download it from

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -8,23 +8,18 @@
\title Log In UI - Components
\brief Illustrates how to use wizard templates to create a simple UI
wireframe that contains a text label, images, and push buttons.
that contains a text label, images, and push buttons.
\image loginui1.jpg
\e{Log In UI - Components} is the first in a series of tutorials that build
on each other to illustrate how to use \QDS to create a simple UI with some
basic UI components, such as pages, buttons, and fields. The first tutorial
in the series describes how to use the \QDS wizard templates to create a
project and a button UI control, and how to modify the files generated by
the wizard templates to wireframe the UI.
\e{Log In UI - Components} is the first tutorial in a series of tutorials
that describes how to use the \QDS wizard templates to create a project and
a button UI control, and how to modify the files generated by the wizard
templates to design the UI.
You can donwnload the completed project from
\l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui1}{here}.
The \e {Learn More} sections provide additional information about the
tasks performed by the wizards and about other basic tasks and concepts.
\section1 Creating the UI Project
For the purposes of this tutorial, you will use the empty wizard template.
@@ -47,6 +42,7 @@
\li Set \uicontrol Width to 720 and \uicontrol Height to 1280.
You can change the screen size later in \l Properties.
\endlist
\li Select \uicontrol Create to create the project.
\endlist
@@ -57,7 +53,7 @@
The wizard constructs the \e Screen01 \l{glossary-component}{component}
using instances of a \l{basic-rectangle}{Rectangle} component that forms
the background and a \l Text component that displays some text.
the background, a \l Button, and a \l Text component that displays some text.
\note The visibility of views depends on the selected workspace,
so your \QDS might look somewhat different from the above image.
@@ -65,53 +61,14 @@
in the \uicontrol Design mode. For more information about moving
views around, see \l {Managing Workspaces}.
\section2 Learn More - Projects and Files
You should remove this \l Button for now from the UI
to have a clean workspace. You'll add this later in the course of the tutorial.
Then you shall know how to modify and adjust it as you need.
\QDS creates a set of boilerplate files and folders that you need to create
a UI. The files are listed in the \l{File System} view.
\image loginui1-project-files.png
\list
\li The \e {loginui1.qmlproject} project file defines that all
component, JavaScript, and image files in the project folder belong
to the project. Therefore, you do not need to individually list new
files when you add them to the project.
\li The \e {loginui1.qml} file defines the functionality of
the UI. For the time being, it does not do anything.
\li The \e {Screen01.ui.qml} file is a custom component created by
the wizard template. For more information, see \l {UI Files}.
By default, this is the main file in the project, but you can
change that in the .qmlproject file. While the custom component
is a good starting point for new users, you don't have to use it.
Specifically, if you export and import designs using \QB, your main
file is most likely called something else. For more information,
see \l {Exporting from Design Tools}.
\li The \e CMakeLists.txt project configuration file allowing you to
share your project as a fully working C++ application with
developers.
\li The \e {qtquickcontrols2.conf} file specifies the selected
\l {Styling Qt Quick Controls}{UI style} and some style-specific
arguments.
\li The \e imports folder contains \e {Constants.qml} and
\e {DirectoryFontLoader.qml} files that specify a font loader
and a \e qmldir module definition file that declares the Constant
component. For more information, see
\l {Module Definition qmldir Files}. The \e EventListModel.qml and
\e EventListSimulator.qml files are not used in this example, so
you can ignore them for now.
\endlist
\l{UI Files}{UI files} define a hierarchy of components with a
highly-readable, structured layout. Every UI file consists of two parts:
an imports section and an component declaration section. The components and
functionality most common to UIs are provided in the \c QtQuick import. You
can view the code of a \e .ui.qml file in the \l{Code} view.
To remove this Button, just select it and press \key {Backspace}.
Next, you will edit the values of the properties of the component instances
to create the main page of the UI.
\section1 Creating the Main Page
You will now change the values of the properties of the \l Text component
@@ -123,9 +80,9 @@
You can download the logo and the background image from here:
\list
\li \l{https://doc.qt.io/qtdesignstudio/images/used-in-examples/loginui1/images/qt_logo_green_128x128px.png}
\li \l{https://git.qt.io/public-demos/qtdesign-studio/-/blob/master/tutorial%20projects/Loginui1/content/images/qt_logo_green_128x128px.png}
{qt_logo_green_128x128px.png}
\li \l{https://doc.qt.io/qtdesignstudio/images/used-in-examples/loginui1/images/adventurePage.jpg}
\li \l{https://git.qt.io/public-demos/qtdesign-studio/-/blob/master/tutorial%20projects/Loginui1/content/images/adventurePage.jpg}
{Background image} (\e adventurePage.jpg)
Photo by \l{https://unsplash.com/photos/a2MgJdG6UvE}
@@ -139,7 +96,8 @@
To add the assets:
\list 1
\li Select \uicontrol Assets > \inlineimage icons/plus.png
.
(Select \uicontrol View > \uicontrol Views > \uicontrol Assets to enable it,
if you can't find it).
\li Select the asset files, and then select \uicontrol Open.
\li Select the location where the files will be saved in the
\uicontrol {Add Resources} dialog.
@@ -158,8 +116,8 @@
To modify the \e Screen01 component in the \uicontrol {2D} view:
\list 1
\li Drag-and-drop the background image from \uicontrol Assets to the
rectangle in \l Navigator.
\li Drag-and-drop the background image (1) from \uicontrol Assets to the
\l{basic-rectangle}{Rectangle} (2) in \l Navigator.
\image loginui1-library-assets.jpg "Assets view"
\li \QDS automatically creates an instance of the \l{Images}{Image}
component for you with the path to the image file set as the
@@ -167,7 +125,7 @@
\image loginui1-image-properties.png "Image properties"
\li Drag-and-drop the Qt logo from \uicontrol Assets to the rectangle
in \uicontrol Navigator and move it to the top-center of the
background image in \uicontrol the {2D} view.
background image in the \uicontrol {2D} view.
\li Select \e Text in \uicontrol Navigator and drag it below the logo
in the \uicontrol {2D} view. If the text is hidden behind the
background, select \inlineimage icons/navigator-arrowdown.png
@@ -182,8 +140,8 @@
line: \e {Are you ready to explore?}.
\image loginui1-text-properties.png "Text properties"
\li In \uicontrol Font, select \e {Titillium Web ExtraLight}.
\li In \uicontrol Size, set the font size of the tag line to
\e 50 pixels (\uicontrol px).
\li In \uicontrol Size, first select the scale to pixels (\uicontrol px),
then set font size of the tag line to \e 50 (\uicontrol px).
\li In \uicontrol {Text color}, set the text color to white
(\e #ffffff).
\endlist
@@ -197,74 +155,6 @@
\image loginui1-main-page.jpg "Modified UI in the Design mode"
You can resize the preview dialog to display the whole screen.
\section2 Learn More - Components
\QDS provides preset \l{glossary-component}{components} for creating
UIs, including components for creating and animating visual components,
receiving user input, and creating data models and views.
To be able to use the functionality of preset components, the wizard template
adds the following \e import statements to the UI files (.ui.qml) that it
creates:
\quotefromfile Loginui1/content/Screen01.ui.qml
\skipto import
\printuntil Controls
You can view the import statements in the \uicontrol {Code} view.
The \l Components view lists the components in each module that are
supported by \QDS. You can use the basic components to create your own
components, and they will be listed in \uicontrol {My Components}.
This section is only visible if you have created custom components.
The \l {basic-rectangle}{Rectangle}, \l Text, and \l {Images}{Image}
components used in this tutorial are based on the \l Item component.
It is the base component for all visual elements, with implementation
of basic functions and properties, such as component type, ID, position,
size, and visibility.
For more information, see \l{Use Case - Visual Elements In QML}. For
descriptions of all components, see \l{All QML Types} in the Qt reference
documentation.
\section3 Regtangle Properties
The default \l {basic-rectangle}{Rectangle} component is used for drawing
shapes with four sides and four corners. You can fill rectangles either with
a solid fill color or a gradient. You can specify the border color separately.
By setting the value of the radius property, you can create shapes with
rounded corners.
If you want to specify the radius of each corner separately, you can use the
\l{studio-rectangle}{Rectangle} component from the
\uicontrol {Qt Quick Studio Components} module instead of the basic rectangle
component. It is available in \uicontrol Components
> \uicontrol {Qt Quick Studio Components}.
\section3 Text Properties
The \l Text component is used for adding static text to the UI, such as
titles and labels. You can select the font to use and specify extensive
properties for each text component, such as size in points or pixels,
weight, style, and spacing.
If you want to create a label with a background, use the \l Label component
from the \uicontrol {Qt Quick Controls} module instead of the Text component.
\section3 Image Properties
The \l {Images}{Image} component is used for adding images to the UI in several
supported formats, including bitmap formats such as PNG and JPEG and vector
graphics formats such as SVG. To add an image to \uicontrol Assets, select
\inlineimage icons/plus.png
, and then select the image file.
If you need to display animated images, use the \l {Animated Image}
component, also available in \uicontrol Components >
\uicontrol {Default Components} > \uicontrol Basic.
\section1 Creating a Push Button
You can use another wizard template to create a push button and to add it to
@@ -298,21 +188,9 @@
\image loginui1-button.png "Button in the Design mode."
\section2 Learn More - UI Controls
The \e {Custom Button} wizard template creates a button component
based on the \l {Button} control in the \l {Qt Quick Controls} module. It
is a push-button control that can be pushed or clicked by the user. Buttons
are normally used to perform an action or to answer a question. The
properties and functionality inherited from the Button component enable
you to set text, display an icon, react to mouse clicks, and so on.
To be able to use the functionality of the Button control, the wizard template
adds the following \e import statements to the \e EntryField.ui.qml file:
\quotefromfile Loginui1/content/EntryField.ui.qml
\skipto import
\printuntil Controls
\note To open the \uicontrol States view, select it from
\uicontrol View > \uicontrol Views > \uicontrol States, if
it is not available by default.
Next, you will change the appearance of the EntryField component by
modifying its properties.
@@ -353,8 +231,8 @@
properties in \uicontrol Properties.
\li In \uicontrol Character > \uicontrol Font, select
\e {Titillium Web ExtraLight}.
\li In \uicontrol Size, set the font size to \e 34 pixels
(\uicontrol px).
\li In \uicontrol Size, first select the scale to pixels (\uicontrol px),
then set font size to \e 34 (\uicontrol px).
\li In \uicontrol {Text color}, set the text color to white
(\e #ffffff).
\li In \uicontrol {Alignment H}, select the \uicontrol Left button to
@@ -374,6 +252,10 @@
\image loginui1-entry-field-styled.jpg "Modified button in the 2D view"
\note Do not edit the the value of \uicontrol Text in the \uicontrol Character
property, because this will break the connection, and later you won't be able
to change the text in \uicontrol {Button Content} > \uicontrol Text.
Next, you will add instances of the \e EntryField component to the
\e Screen01 component and modify their properties.
@@ -437,7 +319,8 @@
its properties in \uicontrol Properties.
\li In \uicontrol Character > \uicontrol Font, select
\e {Titillium Web ExtraLight}.
\li In \uicontrol Size, set the font size to \e 34 pixels.
\li In \uicontrol Size, first select the scale to pixels (\uicontrol px),
then set font size to \e 34 (\uicontrol px).
\li In \uicontrol {Text color}, set the text color to \e #41cd52.
\li In the \uicontrol States view, select the \e normal state and repeat
the changes, as necessary.
@@ -481,7 +364,136 @@
\image loginui1-ready.jpg "The finished UI in the 2D view"
\section2 Learn More - Component IDs
\section1 Learn More
The \e {Learn More} sections provide additional information about the
tasks performed by the wizards and about other basic tasks and concepts.
\section2 Projects and Files
\QDS creates a set of files and folders that you need to create
a UI. The files are listed in the \l{File System} view.
\image loginui1-project-files.png
\list
\li The \e {loginui1.qmlproject} project file defines that all
component, JavaScript, and image files in the project folder belong
to the project. Therefore, you do not need to individually list new
files when you add them to the project.
\li The \e {loginui1.qml} file defines the functionality of
the UI. For the time being, it does not do anything.
\li The \e {Screen01.ui.qml} file is a custom component created by
the wizard template. For more information, see \l {UI Files}.
By default, this is the main file in the project, but you can
change that in the .qmlproject file. While the custom component
is a good starting point for new users, you don't have to use it.
Specifically, if you export and import designs using \QB, your main
file is most likely called something else. For more information,
see \l {Exporting from Design Tools}.
\li The \e CMakeLists.txt project configuration file allowing you to
share your project as a fully working C++ application with
developers.
\li The \e {qtquickcontrols2.conf} file specifies the selected
\l {Styling Qt Quick Controls}{UI style} and some style-specific
arguments.
\li The \e imports folder contains \e {Constants.qml} and
\e {DirectoryFontLoader.qml} files that specify a font loader
and a \e qmldir module definition file that declares the Constant
component. For more information, see
\l {Module Definition qmldir Files}. The \e EventListModel.qml and
\e EventListSimulator.qml files are not used in this example, so
you can ignore them for now.
\endlist
\l{UI Files}{UI files} define a hierarchy of components with a
highly-readable, structured layout. Every UI file consists of two parts:
an imports section and an component declaration section. The components and
functionality most common to UIs are provided in the \c QtQuick import. You
can view the code of a \e .ui.qml file in the \l{Code} view.
\section2 Components
\QDS provides preset \l{glossary-component}{components} for creating
UIs, including components for creating and animating visual components,
receiving user input, and creating data models and views.
To be able to use the functionality of preset components, the wizard template
adds the following \e import statements to the UI files (.ui.qml) that it
creates:
\quotefromfile Loginui1/content/Screen01.ui.qml
\skipto import
\printuntil Controls
You can view the import statements in the \uicontrol {Code} view.
The \l Components view lists the components in each module that are
supported by \QDS. You can use the basic components to create your own
components, and they will be listed in \uicontrol {My Components}.
This section is only visible if you have created custom components.
The \l {basic-rectangle}{Rectangle}, \l Text, and \l {Images}{Image}
components used in this tutorial are based on the \l Item component.
It is the base component for all visual elements, with implementation
of basic functions and properties, such as component type, ID, position,
size, and visibility.
For more information, see \l{Use Case - Visual Elements In QML}. For
descriptions of all components, see \l{All QML Types} in the Qt reference
documentation.
\section3 Regtangle Properties
The default \l {basic-rectangle}{Rectangle} component is used for drawing
shapes with four sides and four corners. You can fill rectangles either with
a solid fill color or a gradient. You can specify the border color separately.
By setting the value of the radius property, you can create shapes with
rounded corners.
If you want to specify the radius of each corner separately, you can use the
\l{studio-rectangle}{Rectangle} component from the
\uicontrol {Qt Quick Studio Components} module instead of the basic rectangle
component. It is available in \uicontrol Components
> \uicontrol {Qt Quick Studio Components}.
\section3 Text Properties
The \l Text component is used for adding static text to the UI, such as
titles and labels. You can select the font to use and specify extensive
properties for each text component, such as size in points or pixels,
weight, style, and spacing.
If you want to create a label with a background, use the \l Label component
from the \uicontrol {Qt Quick Controls} module instead of the Text component.
\section3 Image Properties
The \l {Images}{Image} component is used for adding images to the UI in several
supported formats, including bitmap formats such as PNG and JPEG and vector
graphics formats such as SVG. To add an image to \uicontrol Assets, select
\inlineimage icons/plus.png
, and then select the image file.
If you need to display animated images, use the \l {Animated Image}
component, also available in \uicontrol Components >
\uicontrol {Default Components} > \uicontrol Basic.
\section2 UI Controls
The \e {Custom Button} wizard template creates a button component
based on the \l {Button} control in the \l {Qt Quick Controls} module. It
is a push-button control that can be pushed or clicked by the user. Buttons
are normally used to perform an action or to answer a question. The
properties and functionality inherited from the Button component enable
you to set text, display an icon, react to mouse clicks, and so on.
To be able to use the functionality of the Button control, the wizard template
adds the following \e import statements to the \e EntryField.ui.qml file:
\quotefromfile Loginui1/content/EntryField.ui.qml
\skipto import
\printuntil Controls
\section2 Component IDs
Each component and each instance of a component has an \e ID that uniquely
identifies it and enables other components' properties to be bound to it.

View File

@@ -76,13 +76,11 @@
rectangle at the top, while keeping its horizontal center aligned
with that of the rectangle.
\li Select \e tagLine in \uicontrol Navigator.
\li In \uicontrol Properties > \uicontrol Layout, deselect the
\inlineimage icons/anchor-center-vertical.png
button to remove the vertical center anchor, and then select the
\inlineimage icons/anchor-top.png
button to anchor the tag line to
\e qt_logo_green_128x128px in the \uicontrol Target field with a
40-pixel margin. This attaches the top of the tag line to the
\li In \uicontrol Properties > \uicontrol Layout,
select the \inlineimage icons/anchor-top.png
button and then select \e qt_logo_green_128x128px
as \uicontrol Target to anchor \e tagLine with a 40-pixel margin.
This attaches the top of the tag line to the
bottom of the logo, while keeping its horizontal center aligned
with that of the rectangle.
\image loginui2-layout-text.png "Text Layout properties"
@@ -101,20 +99,6 @@
\image loginui2-loginpage.jpg "Login page in the Design mode and live preview"
\section2 Learn More - Anchors
In an anchor-based layout, each component instance can be thought of as
having a set of invisible \e anchor lines: top, bottom, left, right, fill,
horizontal center, vertical center, and baseline.
Anchors enable placing a component instance either adjacent to or inside of
another component instance, by attaching one or more of the instance's
anchor lines to the anchor lines of the other component instance. If a
component instance changes, the instances that are anchored to it will
adjust automatically to maintain the anchoring.
For more information, see \l{Positioning with Anchors}.
\section1 Using Column Positioners
You will now position the entry fields and buttons in columns
@@ -150,7 +134,7 @@
\li Select \e fields in \uicontrol Navigator.
\li In \uicontrol Properties > \uicontrol Layout, select the
\inlineimage icons/anchor-top.png
button to anchor the top of the field column to
button to anchor the top of the fields column to
the bottom of \e tagLine with a 170-pixel margin.
\li Select the \inlineimage icons/anchor-center-horizontal.png
button to anchor the column horizontally to its parent.
@@ -170,7 +154,20 @@
\image loginui2-loginpage-ready.jpg "Login page in the Design mode and live preview"
\section1 Learn More - Positioners
\section1 Learn More
\section2 Anchors
In an anchor-based layout, each component instance can be thought of as
having a set of invisible \e anchor lines: top, bottom, left, right, fill,
horizontal center, vertical center, and baseline.
Anchors enable placing a component instance either adjacent to or inside of
another component instance, by attaching one or more of the instance's
anchor lines to the anchor lines of the other component instance. If a
component instance changes, the instances that are anchored to it will
adjust automatically to maintain the anchoring.
For more information, see \l{Positioning with Anchors}.
\section2 Positioners
For many use cases, the best positioner to use is a simple grid, row, or
column, and \QDS provides components that will position children in these

View File

@@ -77,7 +77,7 @@
\section1 Using States to Simulate Page Changes
You will now add \l{Adding States}{states} to the UI to show and hide UI
You will now add \l{Working with States}{states} to the UI to show and hide UI
components in the \uicontrol {2D} view, depending on the current page:
\list 1
@@ -115,29 +115,6 @@
\image loginui3-login-state-preview.jpg "Preview of the login state"
\section2 Learn More - States
The \l{Adding States}{state} of a particular visual component is the set of
information which describes how and where the individual parts of the visual
component are displayed within it, and all the data associated with that
state. Most visual components in a UI will have a limited number of states,
each with well-defined properties.
For example, a list item may be either selected or not, and if
selected, it may either be the currently active single selection or it
may be part of a selection group. Each of those states may have certain
associated visual appearance (neutral, highlighted, expanded, and so on).
Youn can apply states to trigger behavior or animations. UI components
typically have a default state that contains all of a component's initial
property values and is therefore useful for managing property values before
state changes.
You can specify additional states by adding new states. Each state within a
component has a unique name. To change the current state of an component,
the state property is set to the name of the state. State changes can be
bound to conditions by using the \c when property.
Next, you will create connections to specify that clicking the
\uicontrol {Create Account} button on the login page triggers a
transition to the account creation page.
@@ -162,9 +139,13 @@
\e createAccount should apply.
\li Double-click the value \uicontrol Action column and select
\uicontrol {Change state to createAccount} in the drop-down menu.
\image loginui3-connections.png "Connections tab"
\note Or, you can right-click the \e createAccount button in \l Navigator.
Then select \uicontrol {Connections} > \uicontrol {Add signal handler} >
\uicontrol {clicked} > \uicontrol {Change State to createAccount}.
\image loginui3-connections.png "Connections tab"
\li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S}
to save your changes.
\endlist
In the live preview, you can now click the \uicontrol {Create Account}
@@ -172,8 +153,30 @@
\image loginui3.gif "Moving between login page and account creation page"
\section1 Learn More
\section2 States
The \l{Working with States}{state} of a particular visual component is the set of
information which describes how and where the individual parts of the visual
component are displayed within it, and all the data associated with that
state. Most visual components in a UI will have a limited number of states,
each with well-defined properties.
\section2 Learn More - Signal and Event Handlers
For example, a list item may be either selected or not, and if
selected, it may either be the currently active single selection or it
may be part of a selection group. Each of those states may have certain
associated visual appearance (neutral, highlighted, expanded, and so on).
Youn can apply states to trigger behavior or animations. UI components
typically have a default state that contains all of a component's initial
property values and is therefore useful for managing property values before
state changes.
You can specify additional states by adding new states. Each state within a
component has a unique name. To change the current state of an component,
the state property is set to the name of the state. State changes can be
bound to conditions by using the \c when property.
\section2 Signal and Event Handlers
UI components need to communicate with each other. For example, a button
needs to know that the user has clicked on it. In response, the button may

View File

@@ -240,7 +240,7 @@
\section1 Binding Animation to States
You will now bring back the \l{Adding States}{states} in the
You will now bring back the \l{Working with States}{states} in the
\uicontrol States view and bind them to the animation settings
in \uicontrol Timeline:

View File

@@ -31,7 +31,7 @@
The button can have the following states: checked, hover, pressed, and
normal. We construct the button using different images for the button
background, frame, and front. We then add \l{Adding States}{states} in
background, frame, and front. We then add \l{Working with States}{states} in
the \l States view for each of the button states. In each state, we turn
the visibility of the appropriate images on or off in the button properties,
to change the appearance of the button.

View File

@@ -166,7 +166,7 @@
in the top-left corner of the root component.
Then, we open the \uicontrol States view to create the \e start,
\e settings, \e presets, and \e running \l{Adding States}{states} for
\e settings, \e presets, and \e running \l{Working with States}{states} for
displaying a particular screen by selecting \uicontrol {Create New State}.
\image washingmachineui-states.png "States view"

View File

@@ -117,7 +117,7 @@
PNG file, as a border and a background.
Use two border images and suitable graphics to change the appearance of
a button when it is clicked. You can use use \l{Adding States}{states}
a button when it is clicked. You can use use \l{Working with States}{states}
to determine which image is visible depending on whether the mouse
button is pressed down. You could add more images and states to
change the appearance of the button depending on other mouse events,

View File

@@ -43,7 +43,7 @@
\image qmldesigner-bindings.png "Connections view Bindings tab"
\li Add states to apply sets of changes to the property values of one
or several component instances in the \uicontrol States view.
For more information, see \l{Adding States}.
For more information, see \l{Working with States}.
\li Animate the properties of component instances in the
\uicontrol Timeline view. For more information, see
\l{Creating Timeline Animations}.

View File

@@ -63,7 +63,7 @@
your UI.
\li Use as few components as necessary. To minimize the number of
components, use \l{Adding Property Aliases}{alias properties} and
\l{Adding States}{states} to create the differences in your
\l{Working with States}{states} to create the differences in your
component instances. We recommend reusing components
instead of duplicating them, so the components do not need to be
processed as completely new component types. This reduces loading

View File

@@ -32,7 +32,7 @@
exported as a public property of the relevant component. For example,
a speedometer should have a property for speed to which the UI is bound.
You can declare various \l{Adding States}{UI states} that describe how
You can declare various \l{Working with States}{UI states} that describe how
property values change from a base state. States can be a useful way of
organizing your UI logic. You can associate transitions with components
to define how their properties will animate when they change due to a
@@ -100,7 +100,7 @@
\li \l{Adding Property Aliases}
\row
\li Referencing a state from within a specific component
\li \l{Adding States}
\li \l{Working with States}
\row
\li Switching to a state when a particular property changes
\li \l{Applying States}

View File

@@ -49,7 +49,7 @@
\uicontrol {Flow Decision} components from
\uicontrol Components > \uicontrol {Flow View}, as described in
\l{Simulating Conditions}.
\li Use \l{Adding States}{states} in flows to modify the appearance
\li Use \l{Working with States}{states} in flows to modify the appearance
of components on screens in response to user interaction, as
described in \l{Applying States in Flows}.
\li Use \uicontrol {Flow Wildcard} components from
@@ -620,7 +620,7 @@
\title Applying States in Flows
You can use \l{Adding States}{states} in flows to modify the appearance
You can use \l{Working with States}{states} in flows to modify the appearance
of \l{glossary-component}{components} in flow items in response to user
interaction, for example. For this purpose, you use the
\uicontrol {Flow Item} components available in

View File

@@ -28,7 +28,7 @@
\endtable
You can edit the properties of the controls in all the preset
\l{Adding States}{states} to apply your own style to them.
\l{Working with States}{states} to apply your own style to them.
\note For buttons and check boxes, you can disable the misbehaving hover
effects by selecting \l Properties > \uicontrol Control, and then disabling

View File

@@ -121,7 +121,7 @@
\list
\li \l{Connections}
\li \l{Adding Connections}
\li \l{Working with Connections}
\endlist
\section1 Device
@@ -249,7 +249,7 @@
\list
\li \l{States}
\li \l{Adding States}
\li \l{Working with States}
\endlist
\section1 Transition

View File

@@ -118,13 +118,13 @@
\endlist
\li \l{Dynamic Behaviors}
\list
\li \l{Adding Connections}
\li \l{Working with Connections}
\list
\li\l{Connecting Components to Signals}
\li\l{Adding Bindings Between Properties}
\li\l{Specifying Custom Properties}
\endlist
\li \l{Adding States}
\li \l{Working with States}
\endlist
\li \l{Validating with Target Hardware}
\list

View File

@@ -13,7 +13,7 @@
when the values of other components or the UI state change.
\list
\li \l {Adding Connections}
\li \l {Working with Connections}
You can create connections between the UI components and
the application to enable them to communicate with each other. For
@@ -25,7 +25,7 @@
binding their properties together. This way, when the value of a
property changes in a parent component, it can be automatically
changed in all the child components, for example.
\li \l {Adding States}
\li \l {Working with States}
You can declare various UI states that describe how component
properties change from a base state. Therefore, states can be

View File

@@ -6,7 +6,7 @@
\previouspage qtquick-adding-dynamics.html
\nextpage quick-signals.html
\title Adding Connections
\title Working with Connections
\list
\li \l{Connecting Components to Signals}

View File

@@ -75,14 +75,14 @@
\li \l{Connections}
\li Enables you to add functionality to the UI by creating
connections between components, signals, and component properties.
\li \l{Adding Connections}
\li \l{Working with Connections}
\row
\li \l States
\li Displays the different states that can be applied to a component.
Typically, states describe UI configurations, such as the
visibility and behavior of components and the available user
actions.
\li \l{Adding States}
\li \l{Working with States}
\row
\li \l{Transitions}
\li Enables you to make movement between states smooth by animating

View File

@@ -124,7 +124,7 @@
the \l {3D} view nor access their properties in
\uicontrol Properties.
If you attempt to \l{Adding States}{remove a state} that changes the
If you attempt to \l{Working with States}{remove a state} that changes the
properties of a locked component, you are prompted to confirm the removal.
If you have \l{Editing Animation Curves}{added easing curves} to keyframe
@@ -212,7 +212,7 @@
\image qmldesigner-export-item.png
You can then use the property alias in other components to
\l{Adding Connections}{create connections} to this component.
\l{Working with Connections}{create connections} to this component.
\section1 Moving Within Components

View File

@@ -86,7 +86,7 @@
The default values of properties are displayed in white color, while the
values that you specify explicitly are highlighted with blue color. In
addition, property changes in \l{Adding States}{states} are highlighted
addition, property changes in \l{Working with States}{states} are highlighted
with blue.
This allows you to easily see which values are set in the component

View File

@@ -153,7 +153,7 @@
If the \uicontrol Clip check box is selected, the component and its children
are clipped to the bounding rectangle of the component.
in the \uicontrol State field, select the \l{Adding States}{state} to
in the \uicontrol State field, select the \l{Working with States}{state} to
change the value of a property in that state.
\section1 Picking Colors

View File

@@ -8,8 +8,8 @@
\title States
The \uicontrol States view displays the different \l{Adding States}{states}
of a UI.
The \uicontrol States view displays the different
\l{Working with States}{states} of a UI.
\image qmldesigner-transitions.png "States view"

View File

@@ -11,7 +11,7 @@
\nextpage exporting-3d-assets.html
\endif
\title Adding States
\title Working with States
You can define states for components and component instances in the
\l States view by selecting \inlineimage icons/plus.png

View File

@@ -12,7 +12,7 @@
\uicontrol {Transitions} to animate the changes between
states.
First, you need to \l{Adding States}{add states} in the \l States view
First, you need to \l{Working with States}{add states} in the \l States view
and \l{Specifying Component Properties}{edit some properties} that can be
animated, such as colors or numbers, in the \l Properties view. For example,
you can animate the changes in the position of a component.

View File

@@ -24,6 +24,10 @@ Item {
// Array of supported externally dropped files that trigger custom import process
property var dropComplexExtFiles: []
AssetsContextMenu {
id: contextMenu
}
function clearSearchFilter()
{
searchBox.clear();
@@ -103,307 +107,11 @@ Item {
root.selectedAssetsChanged()
}
StudioControls.Menu {
id: contextMenu
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
onOpened: {
var numSelected = Object.values(root.selectedAssets).filter(p => p).length
deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File")
}
StudioControls.MenuItem {
text: qsTr("Expand All")
enabled: root.allExpandedState !== 1
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(true)
}
StudioControls.MenuItem {
text: qsTr("Collapse All")
enabled: root.allExpandedState !== 2
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(false)
}
StudioControls.MenuSeparator {
visible: root.isDirContextMenu
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
id: deleteFileItem
text: qsTr("Delete File")
visible: root.contextFilePath
height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
onTriggered: {
assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
}
}
StudioControls.MenuSeparator {
visible: root.contextFilePath
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
text: qsTr("Rename Folder")
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: renameFolderDialog.open()
}
StudioControls.MenuItem {
text: qsTr("New Folder")
onTriggered: newFolderDialog.open()
}
StudioControls.MenuItem {
text: qsTr("Delete Folder")
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: {
var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
&& !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
if (dirEmpty)
assetsModel.deleteFolder(root.contextDir.dirPath)
else
confirmDeleteFolderDialog.open()
}
}
}
RegExpValidator {
id: folderNameValidator
regExp: /^(\w[^*/><?\\|:]*)$/
}
Dialog {
id: renameFolderDialog
title: qsTr("Rename Folder")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
implicitWidth: 280
modal: true
property bool renameError: false
contentItem: Column {
spacing: 2
StudioControls.TextField {
id: folderRename
actionIndicator.visible: false
translationIndicator.visible: false
width: renameFolderDialog.width - 12
validator: folderNameValidator
onEditChanged: renameFolderDialog.renameError = false
Keys.onEnterPressed: btnRename.onClicked()
Keys.onReturnPressed: btnRename.onClicked()
}
Text {
text: qsTr("Folder name cannot be empty.")
color: "#ff0000"
visible: folderRename.text === "" && !renameFolderDialog.renameError
}
Text {
text: qsTr("Could not rename folder. Make sure no folder with the same name exists.")
wrapMode: Text.WordWrap
width: renameFolderDialog.width - 12
color: "#ff0000"
visible: renameFolderDialog.renameError
}
Item { // spacer
width: 1
height: 10
}
Text {
text: qsTr("If the folder has assets in use, renaming it might cause the project to not work correctly.")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: renameFolderDialog.width
leftPadding: 10
rightPadding: 10
}
Item { // spacer
width: 1
height: 20
}
Row {
anchors.right: parent.right
Button {
id: btnRename
text: qsTr("Rename")
enabled: folderRename.text !== ""
onClicked: {
var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
if (success)
renameFolderDialog.accept()
renameFolderDialog.renameError = !success
}
}
Button {
text: qsTr("Cancel")
onClicked: renameFolderDialog.reject()
}
}
}
onOpened: {
folderRename.text = root.contextDir.dirName
folderRename.selectAll()
folderRename.forceActiveFocus()
renameFolderDialog.renameError = false
}
}
Dialog {
id: newFolderDialog
title: qsTr("Create New Folder")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
modal: true
contentItem: Column {
spacing: 2
Row {
Text {
text: qsTr("Folder name: ")
anchors.verticalCenter: parent.verticalCenter
color: StudioTheme.Values.themeTextColor
}
StudioControls.TextField {
id: folderName
actionIndicator.visible: false
translationIndicator.visible: false
validator: folderNameValidator
Keys.onEnterPressed: btnCreate.onClicked()
Keys.onReturnPressed: btnCreate.onClicked()
}
}
Text {
text: qsTr("Folder name cannot be empty.")
color: "#ff0000"
anchors.right: parent.right
visible: folderName.text === ""
}
Item { // spacer
width: 1
height: 20
}
Row {
anchors.right: parent.right
Button {
id: btnCreate
text: qsTr("Create")
enabled: folderName.text !== ""
onClicked: {
assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
newFolderDialog.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: newFolderDialog.reject()
}
}
}
onOpened: {
folderName.text = "New folder"
folderName.selectAll()
folderName.forceActiveFocus()
}
}
Dialog {
id: confirmDeleteFolderDialog
title: qsTr("Folder Not Empty")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
implicitWidth: 300
modal: true
contentItem: Column {
spacing: 20
width: parent.width
Text {
id: folderNotEmpty
text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
.arg(root.contextDir ? root.contextDir.dirName : "")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: confirmDeleteFolderDialog.width
leftPadding: 10
rightPadding: 10
Keys.onEnterPressed: btnDelete.onClicked()
Keys.onReturnPressed: btnDelete.onClicked()
}
Text {
text: qsTr("If the folder has assets in use, deleting it might cause the project to not work correctly.")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: confirmDeleteFolderDialog.width
leftPadding: 10
rightPadding: 10
}
Row {
anchors.right: parent.right
Button {
id: btnDelete
text: qsTr("Delete")
onClicked: {
assetsModel.deleteFolder(root.contextDir.dirPath)
confirmDeleteFolderDialog.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: confirmDeleteFolderDialog.reject()
}
}
}
onOpened: folderNotEmpty.forceActiveFocus()
}
Column {
anchors.fill: parent
anchors.topMargin: 5
@@ -507,220 +215,10 @@ Item {
}
}
ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
AssetsView {
id: assetsView
width: parent.width
height: parent.height - y
clip: true
interactive: assetsView.verticalScrollBarVisible && !contextMenu.opened
Column {
Repeater {
model: assetsModel // context property
delegate: dirSection
}
Component {
id: dirSection
Section {
id: section
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
caption: dirName
sectionHeight: 30
sectionFontSize: 15
leftPadding: 0
topPadding: dirDepth > 0 ? 5 : 0
bottomPadding: 0
hideHeader: dirDepth === 0
showLeftBorder: dirDepth > 0
expanded: dirExpanded
visible: dirVisible
expandOnClick: false
useDefaulContextMenu: false
dropEnabled: true
onToggleExpand: {
dirExpanded = !dirExpanded
}
onDropEnter: (drag)=> {
root.updateDropExtFiles(drag)
section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0
}
onDropExit: {
section.highlight = false
}
onDrop: {
section.highlight = false
rootView.handleExtFilesDrop(root.dropSimpleExtFiles,
root.dropComplexExtFiles,
dirPath)
}
onShowContextMenu: {
root.contextFilePath = ""
root.contextDir = model
root.isDirContextMenu = true
root.allExpandedState = assetsModel.getAllExpandedState()
contextMenu.popup()
}
Column {
spacing: 5
leftPadding: 5
Repeater {
model: dirsModel
delegate: dirSection
}
Repeater {
model: filesModel
delegate: fileSection
}
Text {
text: qsTr("Empty folder")
color: StudioTheme.Values.themeTextColorDisabled
font.pixelSize: 12
visible: !(dirsModel && dirsModel.rowCount() > 0)
&& !(filesModel && filesModel.rowCount() > 0)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
root.contextFilePath = ""
root.contextDir = model
root.isDirContextMenu = true
contextMenu.popup()
}
}
}
}
}
}
Component {
id: fileSection
Rectangle {
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
height: img.height
color: root.selectedAssets[filePath]
? StudioTheme.Values.themeInteraction
: (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
: "transparent")
Row {
spacing: 5
Image {
id: img
asynchronous: true
fillMode: Image.PreserveAspectFit
width: 48
height: 48
source: "image://qmldesigner_assets/" + filePath
}
Text {
text: fileName
color: StudioTheme.Values.themeTextColor
font.pixelSize: 14
anchors.verticalCenter: parent.verticalCenter
}
}
readonly property string suffix: fileName.substr(-4)
readonly property bool isFont: suffix === ".ttf" || suffix === ".otf"
property bool currFileSelected: false
MouseArea {
id: mouseArea
property bool allowTooltip: true
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onExited: tooltipBackend.hideTooltip()
onEntered: allowTooltip = true
onCanceled: {
tooltipBackend.hideTooltip()
allowTooltip = true
}
onPositionChanged: tooltipBackend.reposition()
onPressed: (mouse)=> {
forceActiveFocus()
allowTooltip = false
tooltipBackend.hideTooltip()
var ctrlDown = mouse.modifiers & Qt.ControlModifier
if (mouse.button === Qt.LeftButton) {
if (!root.selectedAssets[filePath] && !ctrlDown)
root.selectedAssets = {}
currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
if (currFileSelected) {
rootView.startDragAsset(
Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
mapToGlobal(mouse.x, mouse.y))
}
} else {
if (!root.selectedAssets[filePath] && !ctrlDown)
root.selectedAssets = {}
currFileSelected = root.selectedAssets[filePath] || !ctrlDown
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
root.contextFilePath = filePath
root.contextDir = model.fileDir
root.isDirContextMenu = false
contextMenu.popup()
}
}
onReleased: (mouse)=> {
allowTooltip = true
if (mouse.button === Qt.LeftButton) {
if (!(mouse.modifiers & Qt.ControlModifier))
root.selectedAssets = {}
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
}
}
ToolTip {
visible: !isFont && mouseArea.containsMouse && !contextMenu.visible
text: filePath
delay: 1000
}
Timer {
interval: 1000
running: mouseArea.containsMouse && mouseArea.allowTooltip
onTriggered: {
if (suffix === ".ttf" || suffix === ".otf") {
tooltipBackend.name = fileName
tooltipBackend.path = filePath
tooltipBackend.showTooltip()
}
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,120 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
StudioControls.Menu {
id: contextMenu
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
onOpened: {
var numSelected = Object.values(root.selectedAssets).filter(p => p).length
deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File")
}
StudioControls.MenuItem {
text: qsTr("Expand All")
enabled: root.allExpandedState !== 1
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(true)
}
StudioControls.MenuItem {
text: qsTr("Collapse All")
enabled: root.allExpandedState !== 2
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(false)
}
StudioControls.MenuSeparator {
visible: root.isDirContextMenu
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
id: deleteFileItem
text: qsTr("Delete File")
visible: root.contextFilePath
height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
onTriggered: {
assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
}
}
StudioControls.MenuSeparator {
visible: root.contextFilePath
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
text: qsTr("Rename Folder")
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: renameFolderDialog.open()
RenameFolderDialog {
id: renameFolderDialog
}
}
StudioControls.MenuItem {
text: qsTr("New Folder")
NewFolderDialog {
id: newFolderDialog
}
onTriggered: newFolderDialog.open()
}
StudioControls.MenuItem {
text: qsTr("Delete Folder")
visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
ConfirmDeleteFolderDialog {
id: confirmDeleteFolderDialog
}
onTriggered: {
var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
&& !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
if (dirEmpty)
assetsModel.deleteFolder(root.contextDir.dirPath)
else
confirmDeleteFolderDialog.open()
}
}
}

View File

@@ -0,0 +1,246 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
id: assetsView
clip: true
interactive: assetsView.verticalScrollBarVisible && !contextMenu.opened
Column {
Repeater {
model: assetsModel // context property
delegate: dirSection
}
Component {
id: dirSection
Section {
id: section
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
caption: dirName
sectionHeight: 30
sectionFontSize: 15
leftPadding: 0
topPadding: dirDepth > 0 ? 5 : 0
bottomPadding: 0
hideHeader: dirDepth === 0
showLeftBorder: dirDepth > 0
expanded: dirExpanded
visible: dirVisible
expandOnClick: false
useDefaulContextMenu: false
dropEnabled: true
onToggleExpand: {
dirExpanded = !dirExpanded
}
onDropEnter: (drag)=> {
root.updateDropExtFiles(drag)
section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0
}
onDropExit: {
section.highlight = false
}
onDrop: {
section.highlight = false
rootView.handleExtFilesDrop(root.dropSimpleExtFiles,
root.dropComplexExtFiles,
dirPath)
}
onShowContextMenu: {
root.contextFilePath = ""
root.contextDir = model
root.isDirContextMenu = true
root.allExpandedState = assetsModel.getAllExpandedState()
contextMenu.popup()
}
Column {
spacing: 5
leftPadding: 5
Repeater {
model: dirsModel
delegate: dirSection
}
Repeater {
model: filesModel
delegate: fileSection
}
Text {
text: qsTr("Empty folder")
color: StudioTheme.Values.themeTextColorDisabled
font.pixelSize: 12
visible: !(dirsModel && dirsModel.rowCount() > 0)
&& !(filesModel && filesModel.rowCount() > 0)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
root.contextFilePath = ""
root.contextDir = model
root.isDirContextMenu = true
contextMenu.popup()
}
}
}
}
}
}
Component {
id: fileSection
Rectangle {
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
height: img.height
color: root.selectedAssets[filePath]
? StudioTheme.Values.themeInteraction
: (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
: "transparent")
Row {
spacing: 5
Image {
id: img
asynchronous: true
fillMode: Image.PreserveAspectFit
width: 48
height: 48
source: "image://qmldesigner_assets/" + filePath
}
Text {
text: fileName
color: StudioTheme.Values.themeTextColor
font.pixelSize: 14
anchors.verticalCenter: parent.verticalCenter
}
}
readonly property string suffix: fileName.substr(-4)
readonly property bool isFont: suffix === ".ttf" || suffix === ".otf"
property bool currFileSelected: false
MouseArea {
id: mouseArea
property bool allowTooltip: true
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onExited: tooltipBackend.hideTooltip()
onEntered: allowTooltip = true
onCanceled: {
tooltipBackend.hideTooltip()
allowTooltip = true
}
onPositionChanged: tooltipBackend.reposition()
onPressed: (mouse)=> {
forceActiveFocus()
allowTooltip = false
tooltipBackend.hideTooltip()
var ctrlDown = mouse.modifiers & Qt.ControlModifier
if (mouse.button === Qt.LeftButton) {
if (!root.selectedAssets[filePath] && !ctrlDown)
root.selectedAssets = {}
currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
if (currFileSelected) {
rootView.startDragAsset(
Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
mapToGlobal(mouse.x, mouse.y))
}
} else {
if (!root.selectedAssets[filePath] && !ctrlDown)
root.selectedAssets = {}
currFileSelected = root.selectedAssets[filePath] || !ctrlDown
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
root.contextFilePath = filePath
root.contextDir = model.fileDir
root.isDirContextMenu = false
contextMenu.popup()
}
}
onReleased: (mouse)=> {
allowTooltip = true
if (mouse.button === Qt.LeftButton) {
if (!(mouse.modifiers & Qt.ControlModifier))
root.selectedAssets = {}
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
}
}
ToolTip {
visible: !isFont && mouseArea.containsMouse && !contextMenu.visible
text: filePath
delay: 1000
}
Timer {
interval: 1000
running: mouseArea.containsMouse && mouseArea.allowTooltip
onTriggered: {
if (suffix === ".ttf" || suffix === ".otf") {
tooltipBackend.name = fileName
tooltipBackend.path = filePath
tooltipBackend.showTooltip()
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
Dialog {
id: confirmDeleteFolderDialog
title: qsTr("Folder Not Empty")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
implicitWidth: 300
modal: true
contentItem: Column {
spacing: 20
width: parent.width
Text {
id: folderNotEmpty
text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
.arg(root.contextDir ? root.contextDir.dirName : "")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: confirmDeleteFolderDialog.width
leftPadding: 10
rightPadding: 10
Keys.onEnterPressed: btnDelete.onClicked()
Keys.onReturnPressed: btnDelete.onClicked()
}
Text {
text: qsTr("If the folder has assets in use, deleting it might cause the project to not work correctly.")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: confirmDeleteFolderDialog.width
leftPadding: 10
rightPadding: 10
}
Row {
anchors.right: parent.right
Button {
id: btnDelete
text: qsTr("Delete")
onClicked: {
assetsModel.deleteFolder(root.contextDir.dirPath)
confirmDeleteFolderDialog.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: confirmDeleteFolderDialog.reject()
}
}
}
onOpened: folderNotEmpty.forceActiveFocus()
}

View File

@@ -0,0 +1,102 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
Dialog {
id: newFolderDialog
title: qsTr("Create New Folder")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
modal: true
contentItem: Column {
spacing: 2
Row {
Text {
text: qsTr("Folder name: ")
anchors.verticalCenter: parent.verticalCenter
color: StudioTheme.Values.themeTextColor
}
StudioControls.TextField {
id: folderName
actionIndicator.visible: false
translationIndicator.visible: false
validator: folderNameValidator
Keys.onEnterPressed: btnCreate.onClicked()
Keys.onReturnPressed: btnCreate.onClicked()
}
}
Text {
text: qsTr("Folder name cannot be empty.")
color: "#ff0000"
anchors.right: parent.right
visible: folderName.text === ""
}
Item { // spacer
width: 1
height: 20
}
Row {
anchors.right: parent.right
Button {
id: btnCreate
text: qsTr("Create")
enabled: folderName.text !== ""
onClicked: {
assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
newFolderDialog.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: newFolderDialog.reject()
}
}
}
onOpened: {
folderName.text = qsTr("New folder")
folderName.selectAll()
folderName.forceActiveFocus()
}
}

View File

@@ -0,0 +1,124 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
Dialog {
id: renameFolderDialog
title: qsTr("Rename Folder")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
implicitWidth: 280
modal: true
property bool renameError: false
contentItem: Column {
spacing: 2
StudioControls.TextField {
id: folderRename
actionIndicator.visible: false
translationIndicator.visible: false
width: renameFolderDialog.width - 12
validator: folderNameValidator
onEditChanged: renameFolderDialog.renameError = false
Keys.onEnterPressed: btnRename.onClicked()
Keys.onReturnPressed: btnRename.onClicked()
}
Text {
text: qsTr("Folder name cannot be empty.")
color: "#ff0000"
visible: folderRename.text === "" && !renameFolderDialog.renameError
}
Text {
text: qsTr("Could not rename folder. Make sure no folder with the same name exists.")
wrapMode: Text.WordWrap
width: renameFolderDialog.width - 12
color: "#ff0000"
visible: renameFolderDialog.renameError
}
Item { // spacer
width: 1
height: 10
}
Text {
text: qsTr("If the folder has assets in use, renaming it might cause the project to not work correctly.")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: renameFolderDialog.width
leftPadding: 10
rightPadding: 10
}
Item { // spacer
width: 1
height: 20
}
Row {
anchors.right: parent.right
Button {
id: btnRename
text: qsTr("Rename")
enabled: folderRename.text !== ""
onClicked: {
var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
if (success)
renameFolderDialog.accept()
renameFolderDialog.renameError = !success
}
}
Button {
text: qsTr("Cancel")
onClicked: renameFolderDialog.reject()
}
}
}
onOpened: {
folderRename.text = root.contextDir.dirName
folderRename.selectAll()
folderRename.forceActiveFocus()
renameFolderDialog.renameError = false
}
}

View File

@@ -27,6 +27,8 @@ import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuickDesignerTheme 1.0
import HelperWidgets 2.0
import QtQuick.Controls
import StudioTheme 1.0 as StudioTheme
Item {
@@ -43,7 +45,7 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: (mouse) => {
if (mouse.button === Qt.LeftButton)
if (mouse.button === Qt.LeftButton && !materialBrowserBundleModel.importerRunning)
rootView.startDragBundleMaterial(modelData, mapToGlobal(mouse.x, mouse.y))
else if (mouse.button === Qt.RightButton)
root.showContextMenu()
@@ -64,6 +66,48 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
source: modelData.bundleMaterialIcon
cache: false
Rectangle { // circular indicator for imported bundle materials
width: 10
height: 10
radius: 5
anchors.right: img.right
anchors.top: img.top
anchors.margins: 5
color: "#00ff00"
border.color: "#555555"
border.width: 1
visible: modelData.bundleMaterialImported
ToolTip {
visible: indicatorMouseArea.containsMouse
text: qsTr("Material is imported to project")
delay: 1000
}
MouseArea {
id: indicatorMouseArea
anchors.fill: parent
hoverEnabled: true
}
}
IconButton {
icon: StudioTheme.Constants.plus
tooltip: qsTr("Add an instance to project")
buttonSize: 22
property color c: StudioTheme.Values.themeIconColor
normalColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .2)
hoverColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .3)
pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4)
anchors.right: img.right
anchors.bottom: img.bottom
enabled: !materialBrowserBundleModel.importerRunning
onClicked: {
materialBrowserBundleModel.addToProject(modelData)
}
}
}
TextInput {

View File

@@ -24,8 +24,8 @@ Item {
// Called also from C++ to close context menu on focus out
function closeContextMenu()
{
cxtMenu.close()
cxtMenuBundle.close()
ctxMenu.close()
ctxMenuBundle.close()
}
// Called from C++ to refresh a preview material after it changes
@@ -57,7 +57,7 @@ Item {
if (!materialBrowserModel.hasMaterialRoot && (!materialBrowserBundleModel.matBundleExists
|| mouse.y < userMatsSecBottom)) {
root.currentMaterial = null
cxtMenu.popup()
ctxMenu.popup()
}
}
}
@@ -75,8 +75,12 @@ Item {
}
}
UnimportBundleMaterialDialog {
id: unimportBundleMaterialDialog
}
StudioControls.Menu {
id: cxtMenu
id: ctxMenu
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
@@ -103,7 +107,10 @@ Item {
width: parent.width
onAboutToShow: {
root.matSectionsModel = ["All"];
if (root.currentMaterial.hasDynamicProperties)
root.matSectionsModel = ["All", "Custom"];
else
root.matSectionsModel = ["All"];
switch (root.currentMaterial.materialType) {
case "DefaultMaterial":
@@ -180,7 +187,7 @@ Item {
}
StudioControls.Menu {
id: cxtMenuBundle
id: ctxMenuBundle
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
@@ -199,9 +206,22 @@ Item {
StudioControls.MenuSeparator {}
StudioControls.MenuItem {
text: qsTr("Add to project")
enabled: !materialBrowserBundleModel.importerRunning
text: qsTr("Add an instance to project")
onTriggered: materialBrowserBundleModel.addMaterial(root.currentBundleMaterial)
onTriggered: {
materialBrowserBundleModel.addToProject(root.currentBundleMaterial)
}
}
StudioControls.MenuItem {
enabled: !materialBrowserBundleModel.importerRunning && root.currentBundleMaterial.bundleMaterialImported
text: qsTr("Remove from project")
onTriggered: {
unimportBundleMaterialDialog.targetBundleMaterial = root.currentBundleMaterial
unimportBundleMaterialDialog.open()
}
}
}
@@ -276,13 +296,14 @@ Item {
height: root.height - searchBox.height
clip: true
visible: materialBrowserModel.hasQuick3DImport && !materialBrowserModel.hasMaterialRoot
interactive: !ctxMenu.opened && !ctxMenuBundle.opened
Column {
Section {
id: userMaterialsSection
width: root.width
caption: qsTr("User materials")
caption: qsTr("Materials")
hideHeader: !materialBrowserBundleModel.matBundleExists
Grid {
@@ -304,7 +325,7 @@ Item {
onShowContextMenu: {
root.currentMaterial = model
cxtMenu.popup()
ctxMenu.popup()
}
}
}
@@ -367,7 +388,7 @@ Item {
onShowContextMenu: {
root.currentBundleMaterial = modelData
cxtMenuBundle.popup()
ctxMenuBundle.popup()
}
}
}

View File

@@ -0,0 +1,85 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
Dialog {
id: root
title: qsTr("Bundle material might be in use")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
implicitWidth: 300
modal: true
property var targetBundleMaterial
contentItem: Column {
spacing: 20
width: parent.width
Text {
id: folderNotEmpty
text: qsTr("If the material you are removing is in use, it might cause the project to malfunction.\n\nAre you sure you want to remove the material?")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
anchors.right: parent.right
anchors.left: parent.left
leftPadding: 10
rightPadding: 10
Keys.onEnterPressed: btnRemove.onClicked()
Keys.onReturnPressed: btnRemove.onClicked()
}
Row {
anchors.right: parent.right
Button {
id: btnRemove
text: qsTr("Remove")
onClicked: {
materialBrowserBundleModel.removeFromProject(root.targetBundleMaterial)
root.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: root.reject()
}
}
}
onOpened: folderNotEmpty.forceActiveFocus()
}

View File

@@ -175,6 +175,7 @@ Column {
// Section with hidden header is used so properties are aligned with the other sections' properties
hideHeader: true
width: parent.width
collapsible: false
SectionLayout {
PropertyLabel { text: qsTr("Name") }

View File

@@ -0,0 +1,29 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.15
import "../QtQuick" as Original
Original.QtObjectPane {}

View File

@@ -29,6 +29,7 @@ Item {
property int level: 0
property int levelShift: 10
property bool hideHeader: false
property bool collapsible: true
property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead
property bool addTopPadding: true
property bool addBottomPadding: true
@@ -48,7 +49,10 @@ Item {
Connections {
target: Controller
function onCollapseAll() { section.expanded = false }
function onCollapseAll() {
if (collapsible)
section.expanded = false
}
function onExpandAll() { section.expanded = true }
}
@@ -120,6 +124,9 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: function(mouse) {
if (mouse.button === Qt.LeftButton) {
if (!section.collapsible && section.expanded)
return
transition.enabled = true
if (section.expandOnClick)
section.expanded = !section.expanded

View File

@@ -521,20 +521,27 @@ Item {
]
Text {
visible: root.autocompleteString !== ""
id: tmpSelectionName
visible: root.autocompleteString !== "" && root.open
text: root.autocompleteString
x: textInput.leftPadding + textMetrics.advanceWidth
y: (textInput.height - Math.ceil(textMetrics.height)) / 2
y: (textInput.height - Math.ceil(tmpSelectionTextMetrics.height)) / 2
color: "gray" // TODO proper color value
font: textInput.font
renderType: textInput.renderType
TextMetrics {
id: textMetrics
font: textInput.font
text: textInput.text
}
TextMetrics {
id: tmpSelectionTextMetrics
font: tmpSelectionName.font
text: "Xq"
}
}
TextMetrics {
id: textMetrics
font: textInput.font
text: textInput.text
}
Rectangle {
id: checkIndicator

View File

@@ -48,6 +48,17 @@ T.MenuItem {
}
}
arrow: T.Label {
id: arrow
x: parent.width - (StudioTheme.Values.height + arrow.width) / 2
y: (parent.height - arrow.height) / 2
visible: control.subMenu
text: StudioTheme.Constants.startNode
color: StudioTheme.Values.themeTextColor
font.pixelSize: 8
font.family: StudioTheme.Constants.iconFont.family
}
background: Rectangle {
implicitWidth: textLabel.implicitWidth + control.labelSpacing + shortcutLabel.implicitWidth
+ control.leftPadding + control.rightPadding

View File

@@ -49,128 +49,126 @@ QtObject {
readonly property string centerHorizontal: "\u0042"
readonly property string centerVertical: "\u0043"
readonly property string closeCross: "\u0044"
readonly property string closeLink: "\u0045"
readonly property string colorPopupClose: "\u0046"
readonly property string columnsAndRows: "\u0047"
readonly property string copyLink: "\u0048"
readonly property string copyStyle: "\u0049"
readonly property string cornerA: "\u004A"
readonly property string cornerB: "\u004B"
readonly property string cornersAll: "\u004C"
readonly property string curveDesigner: "\u004D"
readonly property string curveEditor: "\u004E"
readonly property string customMaterialEditor: "\u004F"
readonly property string decisionNode: "\u0050"
readonly property string deleteColumn: "\u0051"
readonly property string deleteMaterial: "\u0052"
readonly property string deleteRow: "\u0053"
readonly property string deleteTable: "\u0054"
readonly property string detach: "\u0055"
readonly property string distributeBottom: "\u0056"
readonly property string distributeCenterHorizontal: "\u0057"
readonly property string distributeCenterVertical: "\u0058"
readonly property string distributeLeft: "\u0059"
readonly property string distributeOriginBottomRight: "\u005A"
readonly property string distributeOriginCenter: "\u005B"
readonly property string distributeOriginNone: "\u005C"
readonly property string distributeOriginTopLeft: "\u005D"
readonly property string distributeRight: "\u005E"
readonly property string distributeSpacingHorizontal: "\u005F"
readonly property string distributeSpacingVertical: "\u0060"
readonly property string distributeTop: "\u0061"
readonly property string download: "\u0062"
readonly property string downloadUnavailable: "\u0063"
readonly property string downloadUpdate: "\u0064"
readonly property string downloaded: "\u0065"
readonly property string edit: "\u0066"
readonly property string eyeDropper: "\u0067"
readonly property string favorite: "\u0068"
readonly property string flowAction: "\u0069"
readonly property string flowTransition: "\u006A"
readonly property string fontStyleBold: "\u006B"
readonly property string fontStyleItalic: "\u006C"
readonly property string fontStyleStrikethrough: "\u006D"
readonly property string fontStyleUnderline: "\u006E"
readonly property string gradient: "\u006F"
readonly property string gridView: "\u0070"
readonly property string idAliasOff: "\u0071"
readonly property string idAliasOn: "\u0072"
readonly property string infinity: "\u0073"
readonly property string keyframe: "\u0074"
readonly property string linkTriangle: "\u0075"
readonly property string linked: "\u0076"
readonly property string listView: "\u0077"
readonly property string lockOff: "\u0078"
readonly property string lockOn: "\u0079"
readonly property string materialPreviewEnvironment: "\u007A"
readonly property string materialPreviewModel: "\u007B"
readonly property string mergeCells: "\u007C"
readonly property string minus: "\u007D"
readonly property string mirror: "\u007E"
readonly property string newMaterial: "\u007F"
readonly property string openLink: "\u0080"
readonly property string openMaterialBrowser: "\u0081"
readonly property string orientation: "\u0082"
readonly property string paddingEdge: "\u0083"
readonly property string paddingFrame: "\u0084"
readonly property string pasteStyle: "\u0085"
readonly property string pause: "\u0086"
readonly property string pin: "\u0087"
readonly property string play: "\u0088"
readonly property string plus: "\u0089"
readonly property string promote: "\u008A"
readonly property string readOnly: "\u008B"
readonly property string redo: "\u008C"
readonly property string rotationFill: "\u008D"
readonly property string rotationOutline: "\u008E"
readonly property string search: "\u008F"
readonly property string sectionToggle: "\u0090"
readonly property string splitColumns: "\u0091"
readonly property string splitRows: "\u0092"
readonly property string startNode: "\u0093"
readonly property string testIcon: "\u0094"
readonly property string textAlignBottom: "\u0095"
readonly property string textAlignCenter: "\u0096"
readonly property string textAlignJustified: "\u0097"
readonly property string textAlignLeft: "\u0098"
readonly property string textAlignMiddle: "\u0099"
readonly property string textAlignRight: "\u009A"
readonly property string textAlignTop: "\u009B"
readonly property string textBulletList: "\u009D"
readonly property string textFullJustification: "\u009E"
readonly property string textNumberedList: "\u009F"
readonly property string tickIcon: "\u00A0"
readonly property string translationCreateFiles: "\u00A1"
readonly property string translationCreateReport: "\u00A2"
readonly property string translationExport: "\u00A3"
readonly property string translationImport: "\u00A4"
readonly property string translationSelectLanguages: "\u00A5"
readonly property string translationTest: "\u00A6"
readonly property string transparent: "\u00A7"
readonly property string triState: "\u00A8"
readonly property string triangleArcA: "\u00A9"
readonly property string triangleArcB: "\u00AA"
readonly property string triangleCornerA: "\u00AB"
readonly property string triangleCornerB: "\u00AC"
readonly property string unLinked: "\u00AE"
readonly property string undo: "\u00AF"
readonly property string unpin: "\u00B0"
readonly property string upDownIcon: "\u00B1"
readonly property string upDownSquare2: "\u00B2"
readonly property string visibilityOff: "\u00B3"
readonly property string visibilityOn: "\u00B4"
readonly property string wildcard: "\u00B5"
readonly property string wizardsAutomotive: "\u00B6"
readonly property string wizardsDesktop: "\u00B7"
readonly property string wizardsGeneric: "\u00B8"
readonly property string wizardsMcuEmpty: "\u00B9"
readonly property string wizardsMcuGraph: "\u00BA"
readonly property string wizardsMobile: "\u00BB"
readonly property string wizardsUnknown: "\u00BC"
readonly property string zoomAll: "\u00BD"
readonly property string zoomIn: "\u00BE"
readonly property string zoomOut: "\u00BF"
readonly property string zoomSelection: "\u00C0"
readonly property string colorPopupClose: "\u0045"
readonly property string columnsAndRows: "\u0046"
readonly property string copyStyle: "\u0047"
readonly property string cornerA: "\u0048"
readonly property string cornerB: "\u0049"
readonly property string cornersAll: "\u004A"
readonly property string curveDesigner: "\u004B"
readonly property string curveEditor: "\u004C"
readonly property string customMaterialEditor: "\u004D"
readonly property string decisionNode: "\u004E"
readonly property string deleteColumn: "\u004F"
readonly property string deleteMaterial: "\u0050"
readonly property string deleteRow: "\u0051"
readonly property string deleteTable: "\u0052"
readonly property string detach: "\u0053"
readonly property string distributeBottom: "\u0054"
readonly property string distributeCenterHorizontal: "\u0055"
readonly property string distributeCenterVertical: "\u0056"
readonly property string distributeLeft: "\u0057"
readonly property string distributeOriginBottomRight: "\u0058"
readonly property string distributeOriginCenter: "\u0059"
readonly property string distributeOriginNone: "\u005A"
readonly property string distributeOriginTopLeft: "\u005B"
readonly property string distributeRight: "\u005C"
readonly property string distributeSpacingHorizontal: "\u005D"
readonly property string distributeSpacingVertical: "\u005E"
readonly property string distributeTop: "\u005F"
readonly property string download: "\u0060"
readonly property string downloadUnavailable: "\u0061"
readonly property string downloadUpdate: "\u0062"
readonly property string downloaded: "\u0063"
readonly property string edit: "\u0064"
readonly property string eyeDropper: "\u0065"
readonly property string favorite: "\u0066"
readonly property string flowAction: "\u0067"
readonly property string flowTransition: "\u0068"
readonly property string fontStyleBold: "\u0069"
readonly property string fontStyleItalic: "\u006A"
readonly property string fontStyleStrikethrough: "\u006B"
readonly property string fontStyleUnderline: "\u006C"
readonly property string gradient: "\u006D"
readonly property string gridView: "\u006E"
readonly property string idAliasOff: "\u006F"
readonly property string idAliasOn: "\u0070"
readonly property string imported: "\u0071"
readonly property string infinity: "\u0072"
readonly property string keyframe: "\u0073"
readonly property string linkTriangle: "\u0074"
readonly property string linked: "\u0075"
readonly property string listView: "\u0076"
readonly property string lockOff: "\u0077"
readonly property string lockOn: "\u0078"
readonly property string materialPreviewEnvironment: "\u0079"
readonly property string materialPreviewModel: "\u007A"
readonly property string mergeCells: "\u007B"
readonly property string minus: "\u007C"
readonly property string mirror: "\u007D"
readonly property string newMaterial: "\u007E"
readonly property string openMaterialBrowser: "\u007F"
readonly property string orientation: "\u0080"
readonly property string paddingEdge: "\u0081"
readonly property string paddingFrame: "\u0082"
readonly property string pasteStyle: "\u0083"
readonly property string pause: "\u0084"
readonly property string pin: "\u0085"
readonly property string play: "\u0086"
readonly property string plus: "\u0087"
readonly property string promote: "\u0088"
readonly property string readOnly: "\u0089"
readonly property string redo: "\u008A"
readonly property string rotationFill: "\u008B"
readonly property string rotationOutline: "\u008C"
readonly property string search: "\u008D"
readonly property string sectionToggle: "\u008E"
readonly property string splitColumns: "\u008F"
readonly property string splitRows: "\u0090"
readonly property string startNode: "\u0091"
readonly property string testIcon: "\u0092"
readonly property string textAlignBottom: "\u0093"
readonly property string textAlignCenter: "\u0094"
readonly property string textAlignJustified: "\u0095"
readonly property string textAlignLeft: "\u0096"
readonly property string textAlignMiddle: "\u0097"
readonly property string textAlignRight: "\u0098"
readonly property string textAlignTop: "\u0099"
readonly property string textBulletList: "\u009A"
readonly property string textFullJustification: "\u009B"
readonly property string textNumberedList: "\u009D"
readonly property string tickIcon: "\u009E"
readonly property string translationCreateFiles: "\u009F"
readonly property string translationCreateReport: "\u00A0"
readonly property string translationExport: "\u00A1"
readonly property string translationImport: "\u00A2"
readonly property string translationSelectLanguages: "\u00A3"
readonly property string translationTest: "\u00A4"
readonly property string transparent: "\u00A5"
readonly property string triState: "\u00A6"
readonly property string triangleArcA: "\u00A7"
readonly property string triangleArcB: "\u00A8"
readonly property string triangleCornerA: "\u00A9"
readonly property string triangleCornerB: "\u00AA"
readonly property string unLinked: "\u00AB"
readonly property string undo: "\u00AC"
readonly property string unpin: "\u00AE"
readonly property string upDownIcon: "\u00AF"
readonly property string upDownSquare2: "\u00B0"
readonly property string visibilityOff: "\u00B1"
readonly property string visibilityOn: "\u00B2"
readonly property string wildcard: "\u00B3"
readonly property string wizardsAutomotive: "\u00B4"
readonly property string wizardsDesktop: "\u00B5"
readonly property string wizardsGeneric: "\u00B6"
readonly property string wizardsMcuEmpty: "\u00B7"
readonly property string wizardsMcuGraph: "\u00B8"
readonly property string wizardsMobile: "\u00B9"
readonly property string wizardsUnknown: "\u00BA"
readonly property string zoomAll: "\u00BB"
readonly property string zoomIn: "\u00BC"
readonly property string zoomOut: "\u00BD"
readonly property string zoomSelection: "\u00BE"
readonly property font iconFont: Qt.font({
"family": controlIcons.name,

View File

@@ -328,6 +328,10 @@
"source": "../common/fonts.txt",
"target": "%{ProjectDirectory}/content/fonts/fonts.txt"
},
{
"source": "../common/asset_imports.txt",
"target": "%{ProjectDirectory}/asset_imports/asset_imports.txt"
},
{
"source": "../common/CMakeLists.imports.txt.tpl",
"target": "%{ProjectDirectory}/imports/CMakeLists.txt"

View File

@@ -324,6 +324,10 @@
"source": "../common/fonts.txt",
"target": "%{ProjectDirectory}/content/fonts/fonts.txt"
},
{
"source": "../common/asset_imports.txt",
"target": "%{ProjectDirectory}/asset_imports/asset_imports.txt"
},
{
"source": "../common/CMakeLists.imports.txt.tpl",
"target": "%{ProjectDirectory}/imports/CMakeLists.txt"

View File

@@ -0,0 +1 @@
Imported 3D assets and components imported from bundles will be created in this folder.

View File

@@ -323,6 +323,10 @@
"source": "../common/fonts.txt",
"target": "%{ProjectDirectory}/content/fonts/fonts.txt"
},
{
"source": "../common/asset_imports.txt",
"target": "%{ProjectDirectory}/asset_imports/asset_imports.txt"
},
{
"source": "../common/CMakeLists.imports.txt.tpl",
"target": "%{ProjectDirectory}/imports/CMakeLists.txt"

View File

@@ -280,6 +280,10 @@
"source": "../common/fonts.txt",
"target": "%{ProjectDirectory}/content/fonts/fonts.txt"
},
{
"source": "../common/asset_imports.txt",
"target": "%{ProjectDirectory}/asset_imports/asset_imports.txt"
},
{
"source": "../common/CMakeLists.imports.txt.tpl",
"target": "%{ProjectDirectory}/imports/CMakeLists.txt"

View File

@@ -284,6 +284,10 @@
"source": "../common/fonts.txt",
"target": "%{ProjectDirectory}/content/fonts/fonts.txt"
},
{
"source": "../common/asset_imports.txt",
"target": "%{ProjectDirectory}/asset_imports/asset_imports.txt"
},
{
"source": "../common/CMakeLists.imports.txt.tpl",
"target": "%{ProjectDirectory}/imports/CMakeLists.txt"

View File

@@ -284,6 +284,10 @@
"source": "../common/fonts.txt",
"target": "%{ProjectDirectory}/content/fonts/fonts.txt"
},
{
"source": "../common/asset_imports.txt",
"target": "%{ProjectDirectory}/asset_imports/asset_imports.txt"
},
{
"source": "../common/CMakeLists.imports.txt.tpl",
"target": "%{ProjectDirectory}/imports/CMakeLists.txt"

View File

@@ -220,22 +220,35 @@ static void setupInstallSettings(QString &installSettingspath)
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), QLatin1String(Core::Constants::IDE_CASED_ID)));
installSettingspath.clear();
}
// Check if the default install settings contain a setting for the actual install settings.
// This can be an absolute path, or a path relative to applicationDirPath().
// The result is interpreted like -settingspath, but for SystemScope
static const char kInstallSettingsKey[] = "Settings/InstallSettings";
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope,
installSettingspath.isEmpty() ? resourcePath() : installSettingspath);
QSettings installSettings(QSettings::IniFormat, QSettings::UserScope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
if (installSettings.contains(kInstallSettingsKey)) {
QString installSettingsPath = installSettings.value(kInstallSettingsKey).toString();
if (QDir::isRelativePath(installSettingsPath))
installSettingsPath = applicationDirPath() + '/' + installSettingsPath;
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, installSettingsPath);
}
// Check if the default install settings contain a setting for the actual install settings.
// This can be an absolute path, or a path relative to applicationDirPath().
// The result is interpreted like -settingspath, but for SystemScope.
//
// Through the sdktool split that is upcoming, the new install settings might redirect
// yet a second time. So try this a few times.
// (Only the first time with QSettings::UserScope, to allow setting the install settings path
// in the user settings.)
QSettings::Scope scope = QSettings::UserScope;
int count = 0;
bool containsInstallSettingsKey = false;
do {
QSettings installSettings(QSettings::IniFormat, scope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
containsInstallSettingsKey = installSettings.contains(kInstallSettingsKey);
if (containsInstallSettingsKey) {
QString newInstallSettingsPath = installSettings.value(kInstallSettingsKey).toString();
if (QDir::isRelativePath(newInstallSettingsPath))
newInstallSettingsPath = applicationDirPath() + '/' + newInstallSettingsPath;
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, newInstallSettingsPath);
}
scope = QSettings::SystemScope; // UserScope only the first time we check
++count;
} while (containsInstallSettingsKey && count < 3);
}
static Utils::QtcSettings *createUserSettings()

View File

@@ -538,7 +538,7 @@ static McuAbstractTargetFactory::Ptr createFactory(bool isLegacy,
{"arm-greenhills",
McuPackagePtr{new McuPackage{settingsHandler,
{},
toolchainFilePrefix / "arm-ghs.cmake",
toolchainFilePrefix / "ghs-arm.cmake",
{},
{},
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,

View File

@@ -35,6 +35,25 @@ static Q_LOGGING_CATEGORY(assetsLibraryBenchmark, "qtc.assetsLibrary.setRoot", Q
namespace QmlDesigner {
AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent)
: QAbstractListModel(parent)
, m_fileSystemWatcher(fileSystemWatcher)
{
// add role names
int role = 0;
const QMetaObject meta = AssetsLibraryDir::staticMetaObject;
for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
m_roleNames.insert(role++, meta.property(i).name());
}
void AssetsLibraryModel::setSearchText(const QString &searchText)
{
if (m_searchText != searchText) {
m_searchText = searchText;
refresh();
}
}
void AssetsLibraryModel::saveExpandedState(bool expanded, const QString &assetPath)
{
m_expandedStateHash.insert(assetPath, expanded);
@@ -186,66 +205,18 @@ QObject *AssetsLibraryModel::rootDir() const
return m_assetsDir;
}
const QStringList &AssetsLibraryModel::supportedImageSuffixes()
bool AssetsLibraryModel::isEmpty() const
{
static QStringList retList;
if (retList.isEmpty()) {
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
for (const QByteArray &suffix : suffixes)
retList.append("*." + QString::fromUtf8(suffix));
return m_isEmpty;
};
void AssetsLibraryModel::setIsEmpty(bool empty)
{
if (m_isEmpty != empty) {
m_isEmpty = empty;
emit isEmptyChanged();
}
return retList;
}
const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.vert",
"*.glsl", "*.glslv", "*.glslf",
"*.vsh", "*.fsh"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedFontSuffixes()
{
static const QStringList retList {"*.ttf", "*.otf"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedAudioSuffixes()
{
static const QStringList retList {"*.wav", "*.mp3"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedVideoSuffixes()
{
static const QStringList retList {"*.mp4"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes()
{
// These are file types only supported by 3D textures
static QStringList retList {"*.hdr", "*.ktx"};
return retList;
}
AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent)
: QAbstractListModel(parent)
, m_fileSystemWatcher(fileSystemWatcher)
{
// add role names
int role = 0;
const QMetaObject meta = AssetsLibraryDir::staticMetaObject;
for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
m_roleNames.insert(role++, meta.property(i).name());
}
};
QVariant AssetsLibraryModel::data(const QModelIndex &index, int role) const
{
@@ -353,12 +324,54 @@ void AssetsLibraryModel::setRootPath(const QString &path)
qCInfo(assetsLibraryBenchmark) << "model reset:" << time.elapsed();
}
void AssetsLibraryModel::setSearchText(const QString &searchText)
const QStringList &AssetsLibraryModel::supportedImageSuffixes()
{
if (m_searchText != searchText) {
m_searchText = searchText;
refresh();
static QStringList retList;
if (retList.isEmpty()) {
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
for (const QByteArray &suffix : suffixes)
retList.append("*." + QString::fromUtf8(suffix));
}
return retList;
}
const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.vert",
"*.glsl", "*.glslv", "*.glslf",
"*.vsh", "*.fsh"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedFontSuffixes()
{
static const QStringList retList {"*.ttf", "*.otf"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedAudioSuffixes()
{
static const QStringList retList {"*.wav", "*.mp3"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedVideoSuffixes()
{
static const QStringList retList {"*.mp4"};
return retList;
}
const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes()
{
// These are file types only supported by 3D textures
static QStringList retList {"*.hdr", "*.ktx"};
return retList;
}
const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
@@ -379,19 +392,6 @@ const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
return allSuffixes;
}
bool AssetsLibraryModel::isEmpty() const
{
return m_isEmpty;
};
void AssetsLibraryModel::setIsEmpty(bool empty)
{
if (m_isEmpty != empty) {
m_isEmpty = empty;
emit isEmptyChanged();
}
};
const QSet<QString> &AssetsLibraryModel::previewableSuffixes() const
{
static QSet<QString> previewableSuffixes;

View File

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

View File

@@ -57,10 +57,8 @@ public:
centerHorizontal,
centerVertical,
closeCross,
closeLink,
colorPopupClose,
columnsAndRows,
copyLink,
copyStyle,
cornerA,
cornerB,
@@ -103,6 +101,7 @@ public:
gridView,
idAliasOff,
idAliasOn,
imported,
infinity,
keyframe,
linkTriangle,
@@ -116,7 +115,6 @@ public:
minus,
mirror,
newMaterial,
openLink,
openMaterialBrowser,
orientation,
paddingEdge,

View File

@@ -14,6 +14,7 @@
#include "viewmanager.h"
#include <seekerslider.h>
#include <nodeinstanceview.h>
#include <import.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -208,14 +209,21 @@ void Edit3DWidget::updateCreateSubMenu(const QStringList &keys,
void Edit3DWidget::onCreateAction()
{
// QAction *action = qobject_cast<QAction *>(sender());
// if (!action)
// if (!action || !m_view || !m_view->model())
// return;
// m_view->executeInTransaction(__FUNCTION__, [&] {
// int activeScene = m_view->rootModelNode().auxiliaryData("active3dScene@Internal").toInt();
// ItemLibraryEntry entry = m_nameToEntry.value(action->data().toString());
// Import import = Import::createLibraryImport(entry.requiredImport(),
// QString::number(entry.majorVersion())
// + QLatin1Char('.')
// + QString::number(entry.minorVersion()));
// if (!m_view->model()->hasImport(import))
// m_view->model()->changeImports({import}, {});
// auto modelNode = QmlVisualNode::createQml3DNode(m_view, m_nameToEntry.value(action->data().toString()),
// activeScene, m_contextMenuPos3d).modelNode();
// int activeScene = m_view->rootModelNode().auxiliaryData("active3dScene@Internal").toInt();
// auto modelNode = QmlVisualNode::createQml3DNode(m_view, entry,
// activeScene, m_contextMenuPos3d).modelNode();
// QTC_ASSERT(modelNode.isValid(), return);
// m_view->setSelectedModelNode(modelNode);

View File

@@ -273,8 +273,10 @@ void ItemLibraryWidget::delayedUpdateModel()
void ItemLibraryWidget::setModel(Model *model)
{
m_model = model;
if (!model)
if (!model) {
m_itemToDrag = {};
return;
}
setItemLibraryInfo(model->metaInfo().itemLibraryInfo());

View File

@@ -97,9 +97,10 @@ QString BundleImporter::importComponent(const QString &qmlFile,
FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile));
const bool qmlFileExists = qmlSourceFile.exists();
const QString qmlType = qmlSourceFile.baseName();
m_pendingTypes.append(QStringLiteral("%1.%2.%3")
.arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
m_bundleId, qmlType));
const QString fullTypeName = QStringLiteral("%1.%2.%3")
.arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType);
if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName])
return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName);
if (!qmldirContent.contains(qmlFile)) {
qmldirContent.append(qmlType);
qmldirContent.append(" 1.0 ");
@@ -162,6 +163,7 @@ QString BundleImporter::importComponent(const QString &qmlFile,
m_importAddPending = true;
}
}
m_pendingTypes.insert(fullTypeName, true);
m_importTimerCount = 0;
m_importTimer.start();
@@ -175,8 +177,16 @@ void BundleImporter::handleImportTimer()
m_fullReset = false;
m_importAddPending = false;
m_importTimerCount = 0;
m_pendingTypes.clear();
emit importFinished({});
// Emit dummy finished signals for all pending types
const QStringList pendingTypes = m_pendingTypes.keys();
for (const QString &pendingType : pendingTypes) {
m_pendingTypes.remove(pendingType);
if (m_pendingTypes[pendingType])
emit importFinished({});
else
emit unimportFinished({});
}
};
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -210,12 +220,17 @@ void BundleImporter::handleImportTimer()
}
// Detect when the code model has the new material(s) fully available
const QStringList pendingTypes = m_pendingTypes;
const QStringList pendingTypes = m_pendingTypes.keys();
for (const QString &pendingType : pendingTypes) {
NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8());
if (metaInfo.isValid() && !metaInfo.superClasses().empty()) {
m_pendingTypes.removeAll(pendingType);
emit importFinished(metaInfo);
const bool isImport = m_pendingTypes[pendingType];
const bool typeComplete = metaInfo.isValid() && !metaInfo.superClasses().empty();
if (isImport == typeComplete) {
m_pendingTypes.remove(pendingType);
if (isImport)
emit importFinished(metaInfo);
else
emit unimportFinished(metaInfo);
}
}
@@ -257,14 +272,14 @@ QString BundleImporter::unimportComponent(const QString &qmlFile)
{
FilePath bundleImportPath = resolveBundleImportPath();
if (bundleImportPath.isEmpty())
return "Failed to resolve bundle import folder";
return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile);
if (!bundleImportPath.exists())
return {};
return QStringLiteral("Unable to find bundle path: '%1'").arg(bundleImportPath.toString());
FilePath qmlFilePath = bundleImportPath.resolvePath(qmlFile);
if (!qmlFilePath.exists())
return {};
return QStringLiteral("Unable to find specified file: '%1'").arg(qmlFilePath.toString());
QStringList removedFiles;
removedFiles.append(qmlFile);
@@ -272,9 +287,15 @@ QString BundleImporter::unimportComponent(const QString &qmlFile)
FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir"));
const std::optional<QByteArray> qmldirContent = qmldirPath.fileContents();
QByteArray newContent;
QString qmlType = qmlFilePath.baseName();
const QString fullTypeName = QStringLiteral("%1.%2.%3")
.arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType);
if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName])
return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName);
if (qmldirContent) {
QByteArray qmlType = qmlFilePath.baseName().toUtf8();
int typeIndex = qmldirContent->indexOf(qmlType);
int typeIndex = qmldirContent->indexOf(qmlType.toUtf8());
if (typeIndex != -1) {
int newLineIndex = qmldirContent->indexOf('\n', typeIndex);
newContent = qmldirContent->left(typeIndex);
@@ -287,6 +308,8 @@ QString BundleImporter::unimportComponent(const QString &qmlFile)
}
}
m_pendingTypes.insert(fullTypeName, false);
QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath);
bool writeAssetRefs = false;
const auto keys = assetRefMap.keys();

View File

@@ -51,18 +51,19 @@ public:
QString importComponent(const QString &qmlFile,
const QStringList &files);
QString unimportComponent(const QString &qmlFile);
Utils::FilePath resolveBundleImportPath();
signals:
// The metaInfo parameter will be invalid if an error was encountered during
// asynchronous part of the import. In this case all remaining pending imports have been
// terminated, and will not receive separate importFinished notifications.
void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
private:
void handleImportTimer();
QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath);
void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap);
Utils::FilePath resolveBundleImportPath();
Utils::FilePath m_bundleDir;
QString m_bundleId;
@@ -72,7 +73,7 @@ private:
int m_importTimerCount = 0;
bool m_importAddPending = false;
bool m_fullReset = false;
QStringList m_pendingTypes;
QHash<QString, bool> m_pendingTypes; // <type, isImport>
};
} // namespace QmlDesigner::Internal

View File

@@ -70,4 +70,20 @@ bool BundleMaterial::visible() const
return m_visible;
}
bool BundleMaterial::setImported(bool imported)
{
if (m_imported != imported) {
m_imported = imported;
emit materialImportedChanged();
return true;
}
return false;
}
bool BundleMaterial::imported() const
{
return m_imported;
}
} // namespace QmlDesigner

View File

@@ -40,6 +40,7 @@ class BundleMaterial : public QObject
Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT)
Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged)
Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged)
public:
BundleMaterial(QObject *parent,
@@ -57,8 +58,12 @@ public:
QStringList files() const;
bool visible() const;
bool setImported(bool imported);
bool imported() const;
signals:
void materialVisibleChanged();
void materialImportedChanged();
private:
QString m_name;
@@ -68,6 +73,7 @@ private:
QStringList m_files;
bool m_visible = true;
bool m_imported = false;
};
} // namespace QmlDesigner

View File

@@ -37,6 +37,16 @@ void BundleMaterialCategory::addBundleMaterial(BundleMaterial *bundleMat)
m_categoryMaterials.append(bundleMat);
}
bool BundleMaterialCategory::updateImportedState(const QStringList &importedMats)
{
bool changed = false;
for (BundleMaterial *mat : std::as_const(m_categoryMaterials))
changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4)));
return changed;
}
bool BundleMaterialCategory::filter(const QString &searchText)
{
bool visible = false;

View File

@@ -42,6 +42,7 @@ public:
BundleMaterialCategory(QObject *parent, const QString &name);
void addBundleMaterial(BundleMaterial *bundleMat);
bool updateImportedState(const QStringList &importedMats);
bool filter(const QString &searchText);
QString name() const;

View File

@@ -122,7 +122,7 @@ void MaterialBrowserBundleModel::loadMaterialBundle()
m_matBundleExists = true;
const QString bundleId = m_matBundleObj.value("id").toString();
QString bundleId = m_matBundleObj.value("id").toString();
const QJsonObject catsObj = m_matBundleObj.value("categories").toObject();
const QStringList categories = catsObj.keys();
@@ -160,8 +160,17 @@ void MaterialBrowserBundleModel::loadMaterialBundle()
m_importer = new Internal::BundleImporter(matBundleDir.path(), bundleId, sharedFiles);
connect(m_importer, &Internal::BundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
m_importerRunning = false;
emit importerRunningChanged();
if (metaInfo.isValid())
emit addBundleMaterialToProjectRequested(metaInfo);
emit bundleMaterialImported(metaInfo);
});
connect(m_importer, &Internal::BundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
Q_UNUSED(metaInfo)
m_importerRunning = false;
emit importerRunningChanged();
emit bundleMaterialUnimported(metaInfo);
});
}
@@ -193,6 +202,11 @@ void MaterialBrowserBundleModel::setHasMaterialRoot(bool b)
emit hasMaterialRootChanged();
}
Internal::BundleImporter *MaterialBrowserBundleModel::bundleImporter() const
{
return m_importer;
}
void MaterialBrowserBundleModel::setSearchText(const QString &searchText)
{
QString lowerSearchText = searchText.toLower();
@@ -219,6 +233,16 @@ void MaterialBrowserBundleModel::setSearchText(const QString &searchText)
resetModel();
}
void MaterialBrowserBundleModel::updateImportedState(const QStringList &importedMats)
{
bool changed = false;
for (BundleMaterialCategory *cat : std::as_const(m_bundleCategories))
changed |= cat->updateImportedState(importedMats);
if (changed)
resetModel();
}
void MaterialBrowserBundleModel::resetModel()
{
beginResetModel();
@@ -230,12 +254,30 @@ void MaterialBrowserBundleModel::applyToSelected(BundleMaterial *mat, bool add)
emit applyToSelectedTriggered(mat, add);
}
void MaterialBrowserBundleModel::addMaterial(BundleMaterial *mat)
void MaterialBrowserBundleModel::addToProject(BundleMaterial *mat)
{
QString err = m_importer->importComponent(mat->qml(), mat->files());
if (!err.isEmpty())
if (err.isEmpty()) {
m_importerRunning = true;
emit importerRunningChanged();
} else {
qWarning() << __FUNCTION__ << err;
}
}
void MaterialBrowserBundleModel::removeFromProject(BundleMaterial *mat)
{
emit bundleMaterialAboutToUnimport(mat->type());
QString err = m_importer->unimportComponent(mat->qml());
if (err.isEmpty()) {
m_importerRunning = true;
emit importerRunningChanged();
} else {
qWarning() << __FUNCTION__ << err;
}
}
} // namespace QmlDesigner

View File

@@ -50,6 +50,7 @@ class MaterialBrowserBundleModel : public QAbstractListModel
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged)
Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
public:
MaterialBrowserBundleModel(QObject *parent = nullptr);
@@ -59,6 +60,7 @@ public:
QHash<int, QByteArray> roleNames() const override;
void setSearchText(const QString &searchText);
void updateImportedState(const QStringList &importedMats);
bool hasQuick3DImport() const;
void setHasQuick3DImport(bool b);
@@ -66,10 +68,13 @@ public:
bool hasMaterialRoot() const;
void setHasMaterialRoot(bool b);
Internal::BundleImporter *bundleImporter() const;
void resetModel();
Q_INVOKABLE void applyToSelected(QmlDesigner::BundleMaterial *mat, bool add = false);
Q_INVOKABLE void addMaterial(QmlDesigner::BundleMaterial *mat);
Q_INVOKABLE void addToProject(QmlDesigner::BundleMaterial *mat);
Q_INVOKABLE void removeFromProject(QmlDesigner::BundleMaterial *mat);
signals:
void isEmptyChanged();
@@ -77,7 +82,10 @@ signals:
void hasMaterialRootChanged();
void materialVisibleChanged();
void applyToSelectedTriggered(QmlDesigner::BundleMaterial *mat, bool add = false);
void addBundleMaterialToProjectRequested(const QmlDesigner::NodeMetaInfo &metaInfo);
void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo);
void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type);
void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
void importerRunningChanged();
private:
void loadMaterialBundle();
@@ -93,6 +101,7 @@ private:
bool m_hasMaterialRoot = false;
bool m_matBundleExists = false;
bool m_probeMatBundleDir = false;
bool m_importerRunning = false;
};
} // namespace QmlDesigner

View File

@@ -51,6 +51,9 @@ QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
return matType;
}
if (roleName == "hasDynamicProperties")
return !m_materialList.at(index.row()).dynamicProperties().isEmpty();
return {};
}
@@ -121,7 +124,8 @@ QHash<int, QByteArray> MaterialBrowserModel::roleNames() const
{Qt::UserRole + 1, "materialName"},
{Qt::UserRole + 2, "materialInternalId"},
{Qt::UserRole + 3, "materialVisible"},
{Qt::UserRole + 4, "materialType"}
{Qt::UserRole + 4, "materialType"},
{Qt::UserRole + 5, "hasDynamicProperties"}
};
return roles;
}
@@ -338,33 +342,47 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString &sectio
setCopiedMaterialType(matType);
m_allPropsCopied = section == "All";
bool dynamicPropsCopied = section == "Custom";
QmlObjectNode mat(m_copiedMaterial);
QSet<PropertyName> validProps;
QHash<PropertyName, TypeName> dynamicProps;
PropertyNameList copiedProps;
// Base state properties are always valid
const auto baseProps = m_copiedMaterial.propertyNames();
for (const auto &baseProp : baseProps)
validProps.insert(baseProp);
if (!mat.isInBaseState()) {
QmlPropertyChanges changes = mat.propertyChangeForCurrentState();
if (changes.isValid()) {
const QList<AbstractProperty> changedProps = changes.targetProperties();
for (const auto &changedProp : changedProps)
validProps.insert(changedProp.name());
if (dynamicPropsCopied || m_allPropsCopied) {
// Dynamic properties must always be set in base state
const QList<AbstractProperty> dynProps = m_copiedMaterial.dynamicProperties();
for (const auto &prop : dynProps) {
dynamicProps.insert(prop.name(), prop.dynamicTypeName());
validProps.insert(prop.name());
}
}
if (mat.timelineIsActive()) {
const QList<QmlTimelineKeyframeGroup> keyframeGroups
= mat.currentTimeline().keyframeGroupsForTarget(m_copiedMaterial);
for (const auto &kfg : keyframeGroups)
validProps.insert(kfg.propertyName());
}
if (!dynamicPropsCopied) {
// Base state properties are always valid
const auto baseProps = m_copiedMaterial.propertyNames();
for (const auto &baseProp : baseProps)
validProps.insert(baseProp);
if (m_allPropsCopied || m_propertyGroupsObj.empty()) {
if (!mat.isInBaseState()) {
QmlPropertyChanges changes = mat.propertyChangeForCurrentState();
if (changes.isValid()) {
const QList<AbstractProperty> changedProps = changes.targetProperties();
for (const auto &changedProp : changedProps)
validProps.insert(changedProp.name());
}
}
if (mat.timelineIsActive()) {
const QList<QmlTimelineKeyframeGroup> keyframeGroups
= mat.currentTimeline().keyframeGroupsForTarget(m_copiedMaterial);
for (const auto &kfg : keyframeGroups)
validProps.insert(kfg.propertyName());
}
}
validProps.remove("objectName");
if (m_allPropsCopied || dynamicPropsCopied || m_propertyGroupsObj.empty()) {
copiedProps = validProps.values();
} else {
QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject();
@@ -389,8 +407,10 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString &sectio
PropertyCopyData data;
data.name = propName;
data.isValid = m_allPropsCopied || validProps.contains(propName);
data.isBinding = mat.hasBindingProperty(propName);
if (data.isValid) {
if (dynamicProps.contains(propName))
data.dynamicTypeName = dynamicProps[propName];
data.isBinding = mat.hasBindingProperty(propName);
if (data.isBinding)
data.value = mat.expression(propName);
else

View File

@@ -75,6 +75,7 @@ public:
struct PropertyCopyData
{
PropertyName name;
TypeName dynamicTypeName;
QVariant value;
bool isBinding = false;
bool isValid = false;

View File

@@ -2,8 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "materialbrowserview.h"
#include "bindingproperty.h"
#include "bundlematerial.h"
#include "bundleimporter.h"
#include "materialbrowsermodel.h"
#include "materialbrowserwidget.h"
#include "materialbrowserbundlemodel.h"
@@ -18,6 +20,7 @@
#include <nodemetainfo.h>
#include <nodelistproperty.h>
#include <qmldesignerconstants.h>
#include <utils/algorithm.h>
#include <QQuickItem>
#include <QRegularExpression>
@@ -81,13 +84,19 @@ WidgetInfo MaterialBrowserView::widgetInfo()
// remove current properties
PropertyNameList propNames;
if (mat.isInBaseState()) {
propNames = material.propertyNames();
const QList<AbstractProperty> baseProps = material.properties();
for (const auto &baseProp : baseProps) {
if (!baseProp.isDynamic())
propNames.append(baseProp.name());
}
} else {
QmlPropertyChanges changes = mat.propertyChangeForCurrentState();
if (changes.isValid()) {
const QList<AbstractProperty> changedProps = changes.targetProperties();
for (const auto &changedProp : changedProps)
propNames.append(changedProp.name());
for (const auto &changedProp : changedProps) {
if (!changedProp.isDynamic())
propNames.append(changedProp.name());
}
}
}
for (const PropertyName &propName : std::as_const(propNames)) {
@@ -98,14 +107,29 @@ WidgetInfo MaterialBrowserView::widgetInfo()
// apply pasted properties
for (const QmlDesigner::MaterialBrowserModel::PropertyCopyData &propData : propDatas) {
if (propData.name == "objectName")
continue;
if (propData.isValid) {
if (propData.isBinding)
const bool isDynamic = !propData.dynamicTypeName.isEmpty();
const bool isBaseState = currentState().isBaseState();
const bool hasProperty = mat.hasProperty(propData.name);
if (propData.isBinding) {
if (isDynamic && (!hasProperty || isBaseState)) {
mat.modelNode().bindingProperty(propData.name)
.setDynamicTypeNameAndExpression(
propData.dynamicTypeName, propData.value.toString());
continue;
}
mat.setBindingProperty(propData.name, propData.value.toString());
else
} else {
const bool isRecording = mat.timelineIsActive()
&& mat.currentTimeline().isRecording();
if (isDynamic && (!hasProperty || (isBaseState && !isRecording))) {
mat.modelNode().variantProperty(propData.name)
.setDynamicTypeNameAndValue(
propData.dynamicTypeName, propData.value);
continue;
}
mat.setVariantProperty(propData.name, propData.value);
}
} else {
mat.removeProperty(propData.name);
}
@@ -122,23 +146,38 @@ WidgetInfo MaterialBrowserView::widgetInfo()
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::applyToSelectedTriggered, this,
[&] (BundleMaterial *bundleMat, bool add) {
if (!m_selectedModel.isValid())
if (m_selectedModels.isEmpty())
return;
m_bundleMaterialDropTarget = m_selectedModel;
m_bundleMaterialTargets = m_selectedModels;
m_bundleMaterialAddToSelected = add;
ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
if (defaultMat.isValid())
applyBundleMaterialToDropTarget(defaultMat);
else
m_widget->materialBrowserBundleModel()->addMaterial(bundleMat);
m_widget->materialBrowserBundleModel()->addToProject(bundleMat);
});
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::addBundleMaterialToProjectRequested, this,
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::bundleMaterialImported, this,
[&] (const QmlDesigner::NodeMetaInfo &metaInfo) {
applyBundleMaterialToDropTarget({}, metaInfo);
updateBundleMaterialsImportedState();
});
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::bundleMaterialAboutToUnimport, this,
[&] (const QmlDesigner::TypeName &type) {
// delete instances of the bundle material that is about to be unimported
executeInTransaction("MaterialBrowserView::widgetInfo", [&] {
Utils::reverseForeach(m_widget->materialBrowserModel()->materials(), [&](const ModelNode &mat) {
if (mat.isValid() && mat.type() == type)
QmlObjectNode(mat).destroy();
});
});
});
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::bundleMaterialUnimported, this,
&MaterialBrowserView::updateBundleMaterialsImportedState);
}
return createWidgetInfo(m_widget.data(),
@@ -178,40 +217,42 @@ void MaterialBrowserView::applyBundleMaterialToDropTarget(const ModelNode &bundl
newMatNode = bundleMat;
}
if (m_bundleMaterialDropTarget.isValid()
&& m_bundleMaterialDropTarget.metaInfo().isQtQuick3DModel()) {
QmlObjectNode qmlObjNode(m_bundleMaterialDropTarget);
if (m_bundleMaterialAddToSelected) {
// TODO: unify this logic as it exist elsewhere also
auto expToList = [](const QString &exp) {
QString copy = exp;
copy = copy.remove("[").remove("]");
// TODO: unify this logic as it exist elsewhere also
auto expToList = [](const QString &exp) {
QString copy = exp;
copy = copy.remove("[").remove("]");
QStringList tmp = copy.split(',', Qt::SkipEmptyParts);
for (QString &str : tmp)
str = str.trimmed();
QStringList tmp = copy.split(',', Qt::SkipEmptyParts);
for (QString &str : tmp)
str = str.trimmed();
return tmp;
};
return tmp;
};
auto listToExp = [](QStringList &stringList) {
if (stringList.size() > 1)
return QString("[" + stringList.join(",") + "]");
auto listToExp = [](QStringList &stringList) {
if (stringList.size() > 1)
return QString("[" + stringList.join(",") + "]");
if (stringList.size() == 1)
return stringList.first();
if (stringList.size() == 1)
return stringList.first();
return QString();
};
QStringList matList = expToList(qmlObjNode.expression("materials"));
matList.append(newMatNode.id());
QString updatedExp = listToExp(matList);
qmlObjNode.setBindingProperty("materials", updatedExp);
} else {
qmlObjNode.setBindingProperty("materials", newMatNode.id());
return QString();
};
for (const ModelNode &target : std::as_const(m_bundleMaterialTargets)) {
if (target.isValid() && target.metaInfo().isQtQuick3DModel()) {
QmlObjectNode qmlObjNode(target);
if (m_bundleMaterialAddToSelected) {
QStringList matList = expToList(qmlObjNode.expression("materials"));
matList.append(newMatNode.id());
QString updatedExp = listToExp(matList);
qmlObjNode.setBindingProperty("materials", updatedExp);
} else {
qmlObjNode.setBindingProperty("materials", newMatNode.id());
}
}
m_bundleMaterialDropTarget = {};
m_bundleMaterialTargets = {};
m_bundleMaterialAddToSelected = false;
}
});
@@ -226,6 +267,7 @@ void MaterialBrowserView::modelAttached(Model *model)
rootModelNode().metaInfo().isQtQuick3DMaterial());
m_hasQuick3DImport = model->hasImport("QtQuick3D");
updateBundleMaterialsImportedState();
// Project load is already very busy and may even trigger puppet reset, so let's wait a moment
// before refreshing the model
@@ -274,24 +316,20 @@ void MaterialBrowserView::modelAboutToBeDetached(Model *model)
void MaterialBrowserView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
{
m_selectedModel = {};
m_selectedModels = Utils::filtered(selectedNodeList, [](const ModelNode &node) {
return node.metaInfo().isQtQuick3DModel();
});
for (const ModelNode &node : selectedNodeList) {
if (node.metaInfo().isQtQuick3DModel()) {
m_selectedModel = node;
break;
}
}
m_widget->materialBrowserModel()->setHasModelSelection(m_selectedModel.isValid());
m_widget->materialBrowserModel()->setHasModelSelection(!m_selectedModels.isEmpty());
// the logic below selects the material of the first selected model if auto selection is on
if (!m_autoSelectModelMaterial)
return;
if (selectedNodeList.size() > 1 || !m_selectedModel.isValid())
if (selectedNodeList.size() > 1 || m_selectedModels.isEmpty())
return;
QmlObjectNode qmlObjNode(m_selectedModel);
QmlObjectNode qmlObjNode(m_selectedModels.at(0));
QString matExp = qmlObjNode.expression("materials");
if (matExp.isEmpty())
return;
@@ -385,6 +423,25 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath);
}
void MaterialBrowserView::updateBundleMaterialsImportedState()
{
using namespace Utils;
if (!m_widget->materialBrowserBundleModel()->bundleImporter())
return;
QStringList importedBundleMats;
FilePath materialBundlePath = m_widget->materialBrowserBundleModel()->bundleImporter()->resolveBundleImportPath();
if (materialBundlePath.exists()) {
importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}),
[](const FilePath &f) { return f.fileName().chopped(4); });
}
m_widget->materialBrowserBundleModel()->updateImportedState(importedBundleMats);
}
ModelNode MaterialBrowserView::getBundleMaterialDefaultInstance(const TypeName &type)
{
const QList<ModelNode> materials = m_widget->materialBrowserModel()->materials();
@@ -442,13 +499,13 @@ void MaterialBrowserView::customNotification(const AbstractView *view,
} else if (identifier == "delete_selected_material") {
m_widget->materialBrowserModel()->deleteSelectedMaterial();
} else if (identifier == "drop_bundle_material") {
m_bundleMaterialDropTarget = nodeList.first();
m_bundleMaterialTargets = nodeList;
ModelNode defaultMat = getBundleMaterialDefaultInstance(m_draggedBundleMaterial->type());
if (defaultMat.isValid())
applyBundleMaterialToDropTarget(defaultMat);
else
m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial);
m_widget->materialBrowserBundleModel()->addToProject(m_draggedBundleMaterial);
m_draggedBundleMaterial = nullptr;
}

View File

@@ -46,13 +46,15 @@ private:
void refreshModel(bool updateImages);
bool isMaterial(const ModelNode &node) const;
void loadPropertyGroups();
void updateBundleMaterialsImportedState();
void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo = {});
ModelNode getBundleMaterialDefaultInstance(const TypeName &type);
QPointer<MaterialBrowserWidget> m_widget;
ModelNode m_bundleMaterialDropTarget;
ModelNode m_selectedModel; // first selected 3D model node
QList<ModelNode> m_bundleMaterialTargets;
QList<ModelNode> m_selectedModels; // selected 3D model nodes
BundleMaterial *m_draggedBundleMaterial = nullptr;
bool m_bundleMaterialAddToSelected = false;
bool m_hasQuick3DImport = false;
bool m_autoSelectModelMaterial = false; // TODO: wire this to some action

View File

@@ -129,6 +129,7 @@ public:
QList<NodeListProperty> nodeListProperties() const;
QList<BindingProperty> bindingProperties() const;
QList<SignalHandlerProperty> signalProperties() const;
QList<AbstractProperty> dynamicProperties() const;
PropertyNameList propertyNames() const;
bool hasProperties() const;

View File

@@ -623,6 +623,18 @@ QList<SignalHandlerProperty> ModelNode::signalProperties() const
return propertyList;
}
QList<AbstractProperty> ModelNode::dynamicProperties() const
{
QList<AbstractProperty> propertyList;
const QList<AbstractProperty> abstractProperties = properties();
for (const AbstractProperty &abstractProperty : abstractProperties) {
if (abstractProperty.isDynamic())
propertyList.append(abstractProperty);
}
return propertyList;
}
/*!
\brief removes a property from this node
\param name name of the property

View File

@@ -912,9 +912,9 @@ void QtOptionsPageWidget::apply()
&QtOptionsPageWidget::updateQtVersions);
}
// TODO whenever we move the output of sdktool to a different location in the installer,
// this needs to be adapted accordingly
const QStringList kSubdirsToCheck = {"",
"Tools/sdktool", // macOS
"Tools/sdktool/share/qtcreator", // Windows/Linux
"Qt Creator.app/Contents/Resources",
"Contents/Resources",
"Tools/QtCreator/share/qtcreator",

View File

@@ -5706,8 +5706,8 @@ void TextEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
}
}
QTextCursor oldCursor = multiTextCursor().mainCursor();
const int oldPosition = oldCursor.position();
QTextCursor eventCursor = cursorForPosition(QPoint(e->pos().x(), e->pos().y()));
const int eventDocumentPosition = eventCursor.position();
QPlainTextEdit::mouseDoubleClickEvent(e);
@@ -5715,19 +5715,19 @@ void TextEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
// event is triggered on a position that is inbetween two whitespaces this event selects the
// previous word or nothing if the whitespaces are at the block start. Replace this behavior
// with selecting the whitespaces starting from the previous word end to the next word.
const QChar character = characterAt(oldPosition);
const QChar prevCharacter = characterAt(oldPosition - 1);
const QChar character = characterAt(eventDocumentPosition);
const QChar prevCharacter = characterAt(eventDocumentPosition - 1);
if (character.isSpace() && prevCharacter.isSpace()) {
if (prevCharacter != QChar::ParagraphSeparator) {
oldCursor.movePosition(QTextCursor::PreviousWord);
oldCursor.movePosition(QTextCursor::EndOfWord);
eventCursor.movePosition(QTextCursor::PreviousWord);
eventCursor.movePosition(QTextCursor::EndOfWord);
} else if (character == QChar::ParagraphSeparator) {
return; // no special handling for empty lines
}
oldCursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
eventCursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
MultiTextCursor cursor = multiTextCursor();
cursor.replaceMainCursor(oldCursor);
cursor.replaceMainCursor(eventCursor);
setMultiTextCursor(cursor);
}
}

View File

@@ -47,8 +47,8 @@ def verifyVersionControlView(targetDir, canceled):
"Searching for target directory in clone log")
test.verify(" ".join(["clone", "--progress", cloneUrl, cloneDir]) in vcsLog,
"Searching for git parameters in clone log")
test.verify(canceled == (" terminated abnormally" in vcsLog),
"Searching for result in clone log")
test.compare(canceled, " terminated abnormally" in vcsLog,
"Searching for result in clone log")
clickButton(waitForObject(":*Qt Creator.Clear_QToolButton"))
def verifyFiles(targetDir):
@@ -117,5 +117,5 @@ def main():
test.fail("The checked out project was not being opened.",
str(waitForObject(":Cannot Open Project_QTextEdit").plainText))
clickButton(waitForObject(":Cannot Open Project.OK_QPushButton"))
verifyVersionControlView(targetDir, button != ":Git Repository Clone.Finish_QPushButton")
verifyVersionControlView(targetDir, button == "Cancel immediately")
invokeMenuItem("File", "Exit")