forked from qt-creator/qt-creator
Merge "Merge remote-tracking branch 'origin/8.0' into 9.0" into 9.0
This commit is contained in:
@@ -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
|
||||
|
@@ -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 |
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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"
|
||||
|
@@ -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,
|
||||
|
@@ -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}.
|
||||
|
@@ -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
|
||||
|
@@ -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}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
246
share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
Normal file
246
share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
@@ -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") }
|
||||
|
@@ -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 {}
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
Binary file not shown.
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -0,0 +1 @@
|
||||
Imported 3D assets and components imported from bundles will be created in this folder.
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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()
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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());
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 §io
|
||||
|
||||
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 §io
|
||||
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
|
||||
|
@@ -75,6 +75,7 @@ public:
|
||||
struct PropertyCopyData
|
||||
{
|
||||
PropertyName name;
|
||||
TypeName dynamicTypeName;
|
||||
QVariant value;
|
||||
bool isBinding = false;
|
||||
bool isValid = false;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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",
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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")
|
||||
|
Reference in New Issue
Block a user