Merge remote-tracking branch 'origin/7.0'

Conflicts:
	cmake/QtCreatorIDEBranding.cmake
	qbs/modules/qtc/qtc.qbs
	qtcreator_ide_branding.pri

Change-Id: Ic02df53b880d0861d9d9ea0df3e0d381ae99f350
This commit is contained in:
Eike Ziller
2022-03-11 09:50:48 +01:00
184 changed files with 3566 additions and 908 deletions

View File

@@ -8,12 +8,12 @@ on:
env: env:
QT_VERSION: 6.2.3 QT_VERSION: 6.2.3
CLANG_VERSION: 130 CLANG_VERSION: 140
ELFUTILS_VERSION: 0.175 ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1 CMAKE_VERSION: 3.21.1
NINJA_VERSION: 1.10.2 NINJA_VERSION: 1.10.2
BUILD_TYPE: Release BUILD_TYPE: Release
CCACHE_VERSION: 4.5 CCACHE_VERSION: 4.6
QT_MIRRORS: download.qt.io;mirrors.ocf.berkeley.edu/qt;ftp.fau.de/qtproject;mirror.bit.edu.cn/qtproject QT_MIRRORS: download.qt.io;mirrors.ocf.berkeley.edu/qt;ftp.fau.de/qtproject;mirror.bit.edu.cn/qtproject
jobs: jobs:
@@ -31,12 +31,14 @@ jobs:
environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat",
is_msvc: true is_msvc: true
} }
# - { - {
# name: "Windows Latest MinGW", artifact: "Windows-MinGW", name: "Windows Latest MinGW", artifact: "Windows-MinGW",
# os: windows-latest, os: windows-latest,
# cc: "gcc", cxx: "g++", toolchain: "https://github.com/cristianadam/mingw-builds/releases/download/v11.2.0-rev1/x86_64-11.2.0-release-posix-seh-rt_v9-rev1.7z",
# is_msvc: false toolchain_path: "mingw64/bin",
# } cc: "gcc", cxx: "g++",
is_msvc: false
}
- { - {
name: "Ubuntu Latest GCC", artifact: "Linux", name: "Ubuntu Latest GCC", artifact: "Linux",
os: ubuntu-latest, os: ubuntu-latest,
@@ -111,6 +113,33 @@ jobs:
) )
endif() endif()
- name: Install system libs
shell: cmake -P {0}
run: |
if ("${{ runner.os }}" STREQUAL "Linux")
execute_process(
COMMAND sudo apt update
)
execute_process(
COMMAND sudo apt install libgl1-mesa-dev libvulkan-dev libxcb-xinput-dev libxcb-xinerama0-dev libxkbcommon-dev libxkbcommon-x11-dev
RESULT_VARIABLE result
)
if (NOT result EQUAL 0)
message(FATAL_ERROR "Failed to install dependencies")
endif()
endif()
if (NOT "x${{ matrix.config.toolchain }}" STREQUAL "x")
foreach(retry RANGE 10)
file(DOWNLOAD "${{ matrix.config.toolchain }}" ./toolchain.7z SHOW_PROGRESS)
file(SIZE ./toolchain.7z fileSize)
if (fileSize GREATER 0)
break()
endif()
endforeach()
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./toolchain.7z)
endif()
- name: Download Qt - name: Download Qt
id: qt id: qt
shell: cmake -P {0} shell: cmake -P {0}
@@ -121,8 +150,8 @@ jobs:
if ("${{ runner.os }}" STREQUAL "Windows") if ("${{ runner.os }}" STREQUAL "Windows")
set(url_os "windows_x86") set(url_os "windows_x86")
if ("x${{ matrix.config.environment_script }}" STREQUAL "x") if ("x${{ matrix.config.environment_script }}" STREQUAL "x")
set(qt_package_arch_suffix "win64_mingw81") set(qt_package_arch_suffix "win64_mingw")
set(qt_dir_prefix "${qt_version}/mingw81_64") set(qt_dir_prefix "${qt_version}/mingw_64")
set(qt_package_suffix "-Windows-Windows_10_21H2-Mingw-Windows-Windows_10_21H2-X86_64") set(qt_package_suffix "-Windows-Windows_10_21H2-Mingw-Windows-Windows_10_21H2-X86_64")
elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat") elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat")
set(qt_package_arch_suffix "win64_msvc2019_64") set(qt_package_arch_suffix "win64_msvc2019_64")
@@ -218,7 +247,7 @@ jobs:
if ("x${{ matrix.config.environment_script }}" STREQUAL "x") if ("x${{ matrix.config.environment_script }}" STREQUAL "x")
# deploy MinGW # deploy MinGW
foreach(file libwinpthread-1.dll libstdc++-6.dll libgcc_s_seh-1.dll) foreach(file libwinpthread-1.dll libstdc++-6.dll libgcc_s_seh-1.dll)
file(INSTALL "C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin/${file}" file(INSTALL "$ENV{GITHUB_WORKSPACE}/${{ matrix.config.toolchain_path }}/${file}"
DESTINATION "qt6/${qt_dir_prefix}/bin" DESTINATION "qt6/${qt_dir_prefix}/bin"
USE_SOURCE_PERMISSIONS) USE_SOURCE_PERMISSIONS)
endforeach() endforeach()
@@ -434,22 +463,6 @@ jobs:
endforeach() endforeach()
endforeach() endforeach()
- name: Install system libs
shell: cmake -P {0}
run: |
if ("${{ runner.os }}" STREQUAL "Linux")
execute_process(
COMMAND sudo apt update
)
execute_process(
COMMAND sudo apt install libgl1-mesa-dev libvulkan-dev libxcb-xinput-dev libxcb-xinerama0-dev libxkbcommon-dev libxkbcommon-x11-dev
RESULT_VARIABLE result
)
if (NOT result EQUAL 0)
message(FATAL_ERROR "Failed to install dependencies")
endif()
endif()
- name: Build - name: Build
shell: cmake -P {0} shell: cmake -P {0}
run: | run: |
@@ -516,6 +529,14 @@ jobs:
unset(NO_DMG) unset(NO_DMG)
endif() endif()
if (NOT "x${{ matrix.config.toolchain_path }}" STREQUAL "x")
set(path_separator ":")
if ("${{ runner.os }}" STREQUAL "Windows")
set(path_separator ";")
endif()
set(ENV{PATH} "$ENV{GITHUB_WORKSPACE}/${{ matrix.config.toolchain_path }}${path_separator}$ENV{PATH}")
endif()
execute_process( execute_process(
COMMAND python COMMAND python
-u -u
@@ -673,10 +694,10 @@ jobs:
name: "Windows Latest MSVC", artifact: "Windows-MSVC", name: "Windows Latest MSVC", artifact: "Windows-MSVC",
os: ubuntu-latest os: ubuntu-latest
} }
# - { - {
# name: "Windows Latest MinGW", artifact: "Windows-MinGW", name: "Windows Latest MinGW", artifact: "Windows-MinGW",
# os: ubuntu-latest os: ubuntu-latest
# } }
- { - {
name: "Ubuntu Latest GCC", artifact: "Linux", name: "Ubuntu Latest GCC", artifact: "Linux",
os: ubuntu-latest os: ubuntu-latest

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the Qt Creator documentation. ** This file is part of the Qt Creator documentation.
@@ -52,7 +52,7 @@
To use Conan, install it by using the Qt installer or the tools provided by To use Conan, install it by using the Qt installer or the tools provided by
your operating system. For example, on Windows, you can use the your operating system. For example, on Windows, you can use the
\c {choco install conan} or \{pip install conan} command. \c {choco install conan} or \c {pip install conan} command.
To enable the experimental Conan plugin, select \uicontrol Help > To enable the experimental Conan plugin, select \uicontrol Help >
\uicontrol {About Plugins} > \uicontrol Utilities > \uicontrol Conan. \uicontrol {About Plugins} > \uicontrol Utilities > \uicontrol Conan.

View File

@@ -97,6 +97,8 @@
between lines can be useful if there is usually not enough space to between lines can be useful if there is usually not enough space to
display annotations next to the text. display annotations next to the text.
\image qtcreator-options-text-editor-display.png "Text Editor Display options"
If you hide the annotations by deselecting the check box, you can move the If you hide the annotations by deselecting the check box, you can move the
mouse pointer over an icon to view them. mouse pointer over an icon to view them.

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the Qt Creator documentation. ** This file is part of the Qt Creator documentation.
@@ -40,12 +40,18 @@
\title Configuring the Editor \title Configuring the Editor
\QC allows you to configure the text editor to suit your specific You can configure the text editor to suit your specific needs by selecting
needs. To configure the editor, select \uicontrol Tools > \uicontrol Tools > \uicontrol Options > \uicontrol{Text Editor}.
\uicontrol Options > \uicontrol{Text Editor}.
\image qtcreator-font-colors.png "Text Editor options"
The settings you specify apply globally to all projects.
To specify editor behavior for an open project, select \uicontrol Projects >
\uicontrol Editor.
\image qtcreator-editor-settings.png "Editor settings"
These settings apply to all projects. To specify editor behavior for an open
project, select \uicontrol Projects > \uicontrol Editor.
\if defined(qtcreator) \if defined(qtcreator)
For more information, see \l{Specifying Editor Settings}. For more information, see \l{Specifying Editor Settings}.
\endif \endif

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the Qt Creator documentation. ** This file is part of the Qt Creator documentation.
@@ -31,19 +31,22 @@
\image qtcreator-meson-build-settings.png "Meson build settings" \image qtcreator-meson-build-settings.png "Meson build settings"
Settings are grouped by category by Meson. All items are user modifiable Meson builds projects in the directory specified in the
except \c backend which is forced to Ninja, \c {buildtype}, \c debug as well \uicontrol {Build directory} field.
as \c optimization to ensure a good compatibility with \QC.
Each setting type has its own editor. To modif any setting, double-click it, Build settings are grouped by category. You can modify all settings,
either edit the field, or select your choice depending on the control. To except \c backend, which is forced to Ninja, \c {buildtype}, \c debug,
apply changes, select \uicontrol {Apply configuration changes}. This will and \c optimization to ensure compatibility with \QC.
trigger a \c {meson configure} command if there were any configuration
changes. If for any reason the build directory configuration is broken,
select \uicontrol {Wipe project}. This should fix any build directory.
\note Any modified setting will remain in bold until \uicontrol To modify a setting, double-click it. Modified settings are formatted in
{Apply configuration changes} is selected. bold until you select \uicontrol {Apply configuration changes} to apply
them. This triggers \c {meson configure}. If problems arise, select
\uicontrol {Wipe Project} to fix the build directory configuration.
Meson supports cross-compiling in addition to native building. \QC
generates a native build file for you. To use a custom native file or a
cross file instead, specify the file name in \uicontrol Parameters.
For example, \c {--cross-file cross_file.txt}.
For more information about using Meson, see \l{Setting Up Meson}. For more information about using Meson, see \l{Setting Up Meson}.
@@ -56,8 +59,7 @@
\image qtcreator-meson-build-steps.png "Meson build steps" \image qtcreator-meson-build-steps.png "Meson build steps"
The build errors and warnings are parsed and displayed in the The build errors and warnings are parsed and displayed in \l Issues.
\uicontrol Issues output pane.
\section1 Meson Clean Steps \section1 Meson Clean Steps

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2020 The Qt Company Ltd. ** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the Qt Creator documentation. ** This file is part of the Qt Creator documentation.
@@ -99,7 +99,6 @@
The following features are not supported yet: The following features are not supported yet:
\list \list
\li Cross compilation.
\li Showing header files in project tree. \li Showing header files in project tree.
\li Configuration change detection, for example when building triggers a \li Configuration change detection, for example when building triggers a
Meson configuration first. Meson configuration first.

View File

@@ -80,7 +80,7 @@
In the first step, you select a template for the project. You can filter In the first step, you select a template for the project. You can filter
templates (1) to view only those that apply to a particular target platform. templates (1) to view only those that apply to a particular target platform.
\image qtcreator-new-qt-quick-project-wizard.png \image qtcreator-new-project.png
Next, you select a location for the project and specify settings for it. Next, you select a location for the project and specify settings for it.
@@ -326,7 +326,7 @@
\uicontrol {New Subproject}. Follow the steps in the \uicontrol {New Subproject}. Follow the steps in the
\uicontrol {New Subproject} wizard to create a subproject. \uicontrol {New Subproject} wizard to create a subproject.
\image qtcreator-new-subproject.png \image qtcreator-new-qt-quick-project.png
To add an existing project as a subproject, select To add an existing project as a subproject, select
\uicontrol {Add Existing Projects} in the context menu. \uicontrol {Add Existing Projects} in the context menu.

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the Qt Creator documentation. ** This file is part of the Qt Creator documentation.
@@ -38,6 +38,18 @@
execute any JavaScript functions. Therefore, you must make sure that the execute any JavaScript functions. Therefore, you must make sure that the
port is properly protected by a firewall. port is properly protected by a firewall.
Optionally, in \uicontrol {Additional startup commands}, you can enter
additional settings for debugging C++:
\list
\li \l{Adding Custom Debugging Helpers}{Custom debugging helpers}
\li \l{Specifying GDB Settings}{GDB commands} to execute after GDB
has started, but before the debugged program is started or
attached, and before the debugging helpers are initialized
\endlist
However, you can usually leave this field empty.
For more information about debugging, see \l{Debugging}. For more information about debugging, see \l{Debugging}.
//! [run settings debugger] //! [run settings debugger]

View File

@@ -36,7 +36,7 @@
\title Creating Qt Quick Projects \title Creating Qt Quick Projects
\image qmldesigner-new-project.png "New Project dialog" \image qtcreator-new-qt-quick-project.png "New Project dialog"
The following table lists the wizard templates for creating a new The following table lists the wizard templates for creating a new
Qt Quick project from scratch. Qt Quick project from scratch.

View File

@@ -299,7 +299,7 @@
\uicontrol {Include Old Entries} and \uicontrol {Include Tags}. \uicontrol {Include Old Entries} and \uicontrol {Include Tags}.
To add a tag to a change in the change log, select \uicontrol Branches > To add a tag to a change in the change log, select \uicontrol Branches >
\uicontrol Log. Select the change, and then select > \uicontrol {Add Tag \uicontrol Log. Select the change, and then select \uicontrol {Add Tag
for Change} in the context menu. for Change} in the context menu.
If you checked out a specific commit, the list of branches displays a If you checked out a specific commit, the list of branches displays a

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the Qt Design Studio documentation. ** This file is part of the Qt Design Studio documentation.
@@ -26,7 +26,7 @@
/*! /*!
\previouspage qtquick-form-editor.html \previouspage qtquick-form-editor.html
\page studio-3d-editor.html \page studio-3d-editor.html
\nextpage quick-library.html \nextpage quick-components-view.html
\title 3D Editor \title 3D Editor
@@ -64,11 +64,11 @@
affect only the local transformations of the component or whether they affect only the local transformations of the component or whether they
transform with respect to the global space. transform with respect to the global space.
Additional helpful features when editing 3D scenes are the \e {edit light}, Another helpful feature when editing 3D scenes is the \e {edit light},
which is a quick way to light the scene, and the grid that helps you to which is a quick way to light the scene.
navigate in 3D space. Select the \inlineimage grid_on.png
(\uicontrol {Toggle Grid Visibility}) or press \key G to show or hide the Additionally, you can toggle the visibility of the grid, selection boxes,
grid. icon gizmos, and camera frustums in the 3D scene.
To refresh the contents of \uicontrol {3D Editor}, press \key P or To refresh the contents of \uicontrol {3D Editor}, press \key P or
select the \inlineimage icons/reset.png select the \inlineimage icons/reset.png
@@ -97,7 +97,7 @@
\li To pan, press \key Alt (or \key Option on \macos) and use the \li To pan, press \key Alt (or \key Option on \macos) and use the
middle mouse button to click and drag anywhere in the rendered middle mouse button to click and drag anywhere in the rendered
view to slide the view around. view to slide the view around.
\note At the moment it is not possible to pan using Magic Mouse. \note It is not possible to pan using Magic Mouse.
\li To orbit, press \key Alt and click and drag anywhere in the rendered \li To orbit, press \key Alt and click and drag anywhere in the rendered
view to rotate the view. view to rotate the view.
\li To zoom, use the mouse wheel or press \key Alt and right-click \li To zoom, use the mouse wheel or press \key Alt and right-click
@@ -109,7 +109,7 @@
select \inlineimage fit_selected.png select \inlineimage fit_selected.png
(\uicontrol {Fit Selected}) or press \key F. (\uicontrol {Fit Selected}) or press \key F.
The world axis helper (1) shows the direction of the world axes in view. The world axis helper (1) shows the direction of the world axes in the view.
To point the camera at the currently selected component in the direction of To point the camera at the currently selected component in the direction of
an axis, click the axis. Clicking the dot at the end of the axis will point an axis, click the axis. Clicking the dot at the end of the axis will point
the camera at the opposite direction of the axis. If no component is the camera at the opposite direction of the axis. If no component is
@@ -179,6 +179,7 @@
{keyboard shortcuts} applicable to your operating system, for example, {keyboard shortcuts} applicable to your operating system, for example,
\key Ctrl+C and \key Ctrl+V on Windows to copy-paste components. \key Ctrl+C and \key Ctrl+V on Windows to copy-paste components.
\target moving components 3d editor
\section1 Moving Components \section1 Moving Components
\image studio-3d-editor-move.png "3D Editor in move mode" \image studio-3d-editor-move.png "3D Editor in move mode"
@@ -233,6 +234,76 @@
gray handle at the center of the component. gray handle at the center of the component.
\endlist \endlist
\section1 Aligning Views and Cameras
To align a camera to the \uicontrol {3D Editor} view:
\list 1
\li Select a camera in \uicontrol {3D Editor} or \uicontrol {Navigator}.
\li In \uicontrol {3D Editor},
select \inlineimage icons/align-camera-on.png
.
\endlist
This moves and rotates the camera so that the camera shows the same view
as the current view in \uicontrol {3D Editor}.
To align the \uicontrol {3D Editor} view to a camera:
\list 1
\li Select a camera in \uicontrol {3D Editor} or \uicontrol {Navigator}.
\li In \uicontrol {3D Editor},
select \inlineimage icons/align-view-on.png
.
\endlist
This copies the position as well as x and y rotation values from the
camera and applies them to \uicontrol {3D Editor}.
\section1 Toggling Visibility
To toggle the visibility of objects in \uicontrol {3D Editor}, select
\inlineimage icons/visibilityon.png
in the toolbar. This opens a menu with the following options:
\table
\row
\li Show Grid
\li Toggles the visibility of the helper grid.
\row
\li Show Selection Boxes
\li Toggles the visibility of selection boxes for selected 3D objects.
\row
\li Show Icon Gizmos
\li Toggles the visibility of icon gizmos for object such as cameras,
lights, and particle systems.
\row
\li Always Show Camera Frustums
\li Toggles between always showing the camera frustum and showing it
only for cameras selected in \uicontrol {3D Editor}.
\endtable
\section1 Particle Editor
The particle editor tools help you preview your particle systems in
\uicontrol {3D Editor}. You can select one particle system to preview at a
time.
To preview a particle system in \uicontrol{3D Editor}:
\list 1
\li Select a particle system in \uicontrol Navigator or
\uicontrol {3D Editor}.
\li In the \uicontrol {3D Editor}, select
\inlineimage icons/particle-animation-on.png
to activate particle animation. Now you can see the particle animation in
\uicontrol{3D Editor}.
\endlist
You can pause the particle animation by selecting
\inlineimage icons/particle-animation-on.png
. When the animation is paused, you can use
\inlineimage icons/particles-seek.png
to manually seek forward or backward in the particle animation.
\section1 Summary of the 3D Editor Toolbar Buttons \section1 Summary of the 3D Editor Toolbar Buttons
The \uicontrol {3D Editor} toolbar contains the following buttons: The \uicontrol {3D Editor} toolbar contains the following buttons:
@@ -254,7 +325,7 @@
\inlineimage move_on.png \inlineimage move_on.png
\li Activate the Move Tool \li Activate the Move Tool
\li \key W \li \key W
\li \l{Moving Components} \li \l{moving components 3d editor}{Moving Components}
\row \row
\li \inlineimage rotate_off.png \li \inlineimage rotate_off.png
\inlineimage rotate_on.png \inlineimage rotate_on.png
@@ -291,17 +362,47 @@
\li \key U \li \key U
\li \l{Using Edit Light} \li \l{Using Edit Light}
\row \row
\li \inlineimage grid_off.png \li \inlineimage icons/align-camera-on.png
\inlineimage grid_on.png \li Align Selected Cameras to View
\li Toggle Grid Visibility
\li \key G
\li \li
\li\l{Aligning Views and Cameras}
\row
\li \inlineimage icons/align-view-on.png
\li Align View to Selected Camera
\li
\li \l{Aligning Views and Cameras}
\row
\li \inlineimage icons/visibilityon.png
\li Visibility Toggles
\li
\li \l{Toggling Visibility}
\row
\li \inlineimage icons/particles-seek.png
\li Seek Particle System Time
\li
\li \l{Particle Editor}
\row
\li \inlineimage icons/particle-animation-on.png
\inlineimage icons/particle-animation-off.png
\li Toggle Particle Animation
\li \key V
\li \l{Particle Editor}
\row
\li \inlineimage icons/particle-play.png
\inlineimage icons/particle-pause.png
\li Play/Pause Particles
\li \key ,
\li \l{Particle Editor}
\row
\li \inlineimage icons/particle-restart.png
\li Restart Particles
\li \key /
\li \l{Particle Editor}
\row \row
\li \inlineimage icons/reset.png \li \inlineimage icons/reset.png
\li Reset View \li Reset View
\li \key R \li \key P
\li \li
\endtable \endtable
*/ */

View File

@@ -229,6 +229,16 @@ def codesign_call():
codesign_call.extend(signing_flags.split()) codesign_call.extend(signing_flags.split())
return codesign_call return codesign_call
def codesign_executable(path):
codesign = codesign_call()
if not codesign:
return
entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
'installer', 'mac', os.path.basename(path) + '.entitlements')
if os.path.exists(entitlements_path):
codesign.extend(['--entitlements', entitlements_path])
subprocess.check_call(codesign + [path])
def os_walk(path, filter, function): def os_walk(path, filter, function):
for r, _, fs in os.walk(path): for r, _, fs in os.walk(path):
for f in fs: for f in fs:
@@ -237,20 +247,21 @@ def os_walk(path, filter, function):
function(ff) function(ff)
def conditional_sign_recursive(path, filter): def conditional_sign_recursive(path, filter):
codesign = codesign_call() if is_mac_platform():
if is_mac_platform() and codesign: os_walk(path, filter, lambda fp: codesign_executable(fp))
os_walk(path, filter, lambda fp: subprocess.check_call(codesign + [fp]))
def codesign(app_path): def codesign(app_path):
codesign = codesign_call()
if not codesign or not is_mac_platform():
return
# sign all executables in Resources # sign all executables in Resources
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Resources'), conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Resources'),
lambda ff: os.access(ff, os.X_OK)) lambda ff: os.access(ff, os.X_OK))
# sign all libraries in Imports # sign all libraries in Imports
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Imports'), conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Imports'),
lambda ff: ff.endswith('.dylib')) lambda ff: ff.endswith('.dylib'))
codesign = codesign_call()
if is_mac_platform() and codesign: # sign the whole bundle
entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist', entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
'installer', 'mac', 'entitlements.plist') 'installer', 'mac', 'entitlements.plist')
# sign the whole bundle
subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path]) subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path])

View File

@@ -50,6 +50,7 @@ public:
ShowSelectionBox, ShowSelectionBox,
ShowIconGizmo, ShowIconGizmo,
ShowCameraFrustum, ShowCameraFrustum,
ShowParticleEmitter,
Edit3DParticleModeToggle, Edit3DParticleModeToggle,
ParticlesPlay, ParticlesPlay,
ParticlesRestart, ParticlesRestart,

View File

@@ -35,7 +35,6 @@
<file>mockfiles/qt6/LightGizmo.qml</file> <file>mockfiles/qt6/LightGizmo.qml</file>
<file>mockfiles/qt6/LightIconGizmo.qml</file> <file>mockfiles/qt6/LightIconGizmo.qml</file>
<file>mockfiles/qt6/LightModel.qml</file> <file>mockfiles/qt6/LightModel.qml</file>
<file>mockfiles/qt6/ParticleSystemGizmo.qml</file>
<file>mockfiles/qt6/Line3D.qml</file> <file>mockfiles/qt6/Line3D.qml</file>
<file>mockfiles/qt6/MaterialNodeView.qml</file> <file>mockfiles/qt6/MaterialNodeView.qml</file>
<file>mockfiles/qt6/ModelNode2DImageView.qml</file> <file>mockfiles/qt6/ModelNode2DImageView.qml</file>
@@ -44,6 +43,8 @@
<file>mockfiles/qt6/MoveGizmo.qml</file> <file>mockfiles/qt6/MoveGizmo.qml</file>
<file>mockfiles/qt6/NodeNodeView.qml</file> <file>mockfiles/qt6/NodeNodeView.qml</file>
<file>mockfiles/qt6/Overlay2D.qml</file> <file>mockfiles/qt6/Overlay2D.qml</file>
<file>mockfiles/qt6/ParticleSystemGizmo.qml</file>
<file>mockfiles/qt6/ParticleEmitterGizmo.qml</file>
<file>mockfiles/qt6/PlanarDraggable.qml</file> <file>mockfiles/qt6/PlanarDraggable.qml</file>
<file>mockfiles/qt6/PlanarMoveHandle.qml</file> <file>mockfiles/qt6/PlanarMoveHandle.qml</file>
<file>mockfiles/qt6/PlanarScaleHandle.qml</file> <file>mockfiles/qt6/PlanarScaleHandle.qml</file>

View File

@@ -385,14 +385,12 @@ Item {
lightIconGizmos[slotFound].targetNode = obj; lightIconGizmos[slotFound].targetNode = obj;
lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj);
lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
_generalHelper.registerGizmoTarget(obj);
return; return;
} }
// No free gizmos available, create a new one // No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("LightIconGizmo.qml"); var gizmoComponent = Qt.createComponent("LightIconGizmo.qml");
if (gizmoComponent.status === Component.Ready) { if (gizmoComponent.status === Component.Ready) {
_generalHelper.registerGizmoTarget(obj);
var gizmo = gizmoComponent.createObject(overlayView, var gizmo = gizmoComponent.createObject(overlayView,
{"view3D": overlayView, "targetNode": obj, {"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes, "scene": scene, "selectedNodes": selectedNodes, "scene": scene,
@@ -426,7 +424,6 @@ Item {
cameraGizmos[slotFound].targetNode = obj; cameraGizmos[slotFound].targetNode = obj;
cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj); cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj);
cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj); cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
_generalHelper.registerGizmoTarget(obj);
return; return;
} }
@@ -434,7 +431,6 @@ Item {
var gizmoComponent = Qt.createComponent("CameraGizmo.qml"); var gizmoComponent = Qt.createComponent("CameraGizmo.qml");
var frustumComponent = Qt.createComponent("CameraFrustum.qml"); var frustumComponent = Qt.createComponent("CameraFrustum.qml");
if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) { if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) {
_generalHelper.registerGizmoTarget(obj);
var geometryName = _generalHelper.generateUniqueName("CameraGeometry"); var geometryName = _generalHelper.generateUniqueName("CameraGeometry");
var frustum = frustumComponent.createObject( var frustum = frustumComponent.createObject(
overlayScene, overlayScene,
@@ -463,7 +459,6 @@ Item {
if (lightIconGizmos[i].targetNode === obj) { if (lightIconGizmos[i].targetNode === obj) {
lightIconGizmos[i].scene = null; lightIconGizmos[i].scene = null;
lightIconGizmos[i].targetNode = null; lightIconGizmos[i].targetNode = null;
_generalHelper.unregisterGizmoTarget(obj);
return; return;
} }
} }
@@ -475,7 +470,6 @@ Item {
if (cameraGizmos[i].targetNode === obj) { if (cameraGizmos[i].targetNode === obj) {
cameraGizmos[i].scene = null; cameraGizmos[i].scene = null;
cameraGizmos[i].targetNode = null; cameraGizmos[i].targetNode = null;
_generalHelper.unregisterGizmoTarget(obj);
return; return;
} }
} }

View File

@@ -42,6 +42,7 @@ Item {
property bool showSelectionBox: true property bool showSelectionBox: true
property bool showIconGizmo: true property bool showIconGizmo: true
property bool showCameraFrustum: false property bool showCameraFrustum: false
property bool showParticleEmitter: false
property bool usePerspective: true property bool usePerspective: true
property bool globalOrientation: false property bool globalOrientation: false
property alias contentItem: contentItem property alias contentItem: contentItem
@@ -58,9 +59,10 @@ Item {
property var lightIconGizmos: [] property var lightIconGizmos: []
property var cameraGizmos: [] property var cameraGizmos: []
property var particleSystemIconGizmos: [] property var particleSystemIconGizmos: []
property var particleEmitterGizmos: []
property var selectionBoxes: [] property var selectionBoxes: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000) property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
property Node activeParticleSystem: null
property bool shuttingDown: false property bool shuttingDown: false
property real fps: 0 property real fps: 0
@@ -77,6 +79,7 @@ Item {
onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox); onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox);
onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo); onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo);
onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum); onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum);
onShowParticleEmitterChanged: _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter);
onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
@@ -232,6 +235,11 @@ Item {
else if (resetToDefault) else if (resetToDefault)
showCameraFrustum = false; showCameraFrustum = false;
if ("showParticleEmitter" in toolStates)
showParticleEmitter = toolStates.showParticleEmitter;
else if (resetToDefault)
showParticleEmitter = false;
if ("usePerspective" in toolStates) if ("usePerspective" in toolStates)
usePerspective = toolStates.usePerspective; usePerspective = toolStates.usePerspective;
else if (resetToDefault) else if (resetToDefault)
@@ -265,6 +273,7 @@ Item {
_generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox) _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox)
_generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo) _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo)
_generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum) _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum)
_generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter)
_generalHelper.storeToolState(sceneId, "usePerspective", usePerspective) _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
_generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
_generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
@@ -376,14 +385,12 @@ Item {
lightIconGizmos[slotFound].targetNode = obj; lightIconGizmos[slotFound].targetNode = obj;
lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj);
lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
_generalHelper.registerGizmoTarget(obj);
return; return;
} }
// No free gizmos available, create a new one // No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("LightIconGizmo.qml"); var gizmoComponent = Qt.createComponent("LightIconGizmo.qml");
if (gizmoComponent.status === Component.Ready) { if (gizmoComponent.status === Component.Ready) {
_generalHelper.registerGizmoTarget(obj);
var gizmo = gizmoComponent.createObject(overlayView, var gizmo = gizmoComponent.createObject(overlayView,
{"view3D": overlayView, "targetNode": obj, {"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes, "scene": scene, "selectedNodes": selectedNodes, "scene": scene,
@@ -417,7 +424,6 @@ Item {
cameraGizmos[slotFound].targetNode = obj; cameraGizmos[slotFound].targetNode = obj;
cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj); cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj);
cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj); cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
_generalHelper.registerGizmoTarget(obj);
return; return;
} }
@@ -425,7 +431,6 @@ Item {
var gizmoComponent = Qt.createComponent("CameraGizmo.qml"); var gizmoComponent = Qt.createComponent("CameraGizmo.qml");
var frustumComponent = Qt.createComponent("CameraFrustum.qml"); var frustumComponent = Qt.createComponent("CameraFrustum.qml");
if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) { if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) {
_generalHelper.registerGizmoTarget(obj);
var geometryName = _generalHelper.generateUniqueName("CameraGeometry"); var geometryName = _generalHelper.generateUniqueName("CameraGeometry");
var frustum = frustumComponent.createObject( var frustum = frustumComponent.createObject(
overlayScene, overlayScene,
@@ -466,26 +471,66 @@ Item {
particleSystemIconGizmos[slotFound].targetNode = obj; particleSystemIconGizmos[slotFound].targetNode = obj;
particleSystemIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); particleSystemIconGizmos[slotFound].locked = _generalHelper.isLocked(obj);
particleSystemIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); particleSystemIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
_generalHelper.registerGizmoTarget(obj);
return; return;
} }
// No free gizmos available, create a new one // No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("ParticleSystemGizmo.qml"); var gizmoComponent = Qt.createComponent("ParticleSystemGizmo.qml");
if (gizmoComponent.status === Component.Ready) { if (gizmoComponent.status === Component.Ready) {
_generalHelper.registerGizmoTarget(obj);
var gizmo = gizmoComponent.createObject(overlayView, var gizmo = gizmoComponent.createObject(overlayView,
{"view3D": overlayView, "targetNode": obj, {"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes, "scene": scene, "selectedNodes": selectedNodes, "scene": scene,
"activeScene": activeScene, "activeScene": activeScene,
"locked": _generalHelper.isLocked(obj), "locked": _generalHelper.isLocked(obj),
"hidden": _generalHelper.isHidden(obj), "hidden": _generalHelper.isHidden(obj),
"globalShow": showIconGizmo}); "globalShow": showIconGizmo,
"activeParticleSystem": activeParticleSystem});
particleSystemIconGizmos[particleSystemIconGizmos.length] = gizmo; particleSystemIconGizmos[particleSystemIconGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked); gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
gizmo.activeScene = Qt.binding(function() {return activeScene;}); gizmo.activeScene = Qt.binding(function() {return activeScene;});
gizmo.globalShow = Qt.binding(function() {return showIconGizmo;}); gizmo.globalShow = Qt.binding(function() {return showIconGizmo;});
gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;});
}
}
function addParticleEmitterGizmo(scene, obj)
{
// Insert into first available gizmo if we don't already have gizmo for this object
var slotFound = -1;
for (var i = 0; i < particleEmitterGizmos.length; ++i) {
if (!particleEmitterGizmos[i].targetNode) {
slotFound = i;
} else if (particleEmitterGizmos[i].targetNode === obj) {
particleEmitterGizmos[i].scene = scene;
return;
}
}
if (slotFound !== -1) {
particleEmitterGizmos[slotFound].scene = scene;
particleEmitterGizmos[slotFound].targetNode = obj;
particleEmitterGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
particleEmitterGizmos[slotFound].systemHidden = _generalHelper.isHidden(obj.system);
return;
}
// No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("ParticleEmitterGizmo.qml");
if (gizmoComponent.status === Component.Ready) {
var gizmo = gizmoComponent.createObject(
overlayScene,
{"targetNode": obj, "selectedNodes": selectedNodes,
"activeParticleSystem": activeParticleSystem, "scene": scene,
"activeScene": activeScene, "hidden": _generalHelper.isHidden(obj),
"systemHidden": _generalHelper.isHidden(obj.system),
"globalShow": showParticleEmitter});
particleEmitterGizmos[particleEmitterGizmos.length] = gizmo;
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;});
gizmo.globalShow = Qt.binding(function() {return showParticleEmitter;});
gizmo.activeScene = Qt.binding(function() {return activeScene;});
} }
} }
@@ -495,7 +540,6 @@ Item {
if (lightIconGizmos[i].targetNode === obj) { if (lightIconGizmos[i].targetNode === obj) {
lightIconGizmos[i].scene = null; lightIconGizmos[i].scene = null;
lightIconGizmos[i].targetNode = null; lightIconGizmos[i].targetNode = null;
_generalHelper.unregisterGizmoTarget(obj);
return; return;
} }
} }
@@ -507,7 +551,6 @@ Item {
if (cameraGizmos[i].targetNode === obj) { if (cameraGizmos[i].targetNode === obj) {
cameraGizmos[i].scene = null; cameraGizmos[i].scene = null;
cameraGizmos[i].targetNode = null; cameraGizmos[i].targetNode = null;
_generalHelper.unregisterGizmoTarget(obj);
return; return;
} }
} }
@@ -519,7 +562,17 @@ Item {
if (particleSystemIconGizmos[i].targetNode === obj) { if (particleSystemIconGizmos[i].targetNode === obj) {
particleSystemIconGizmos[i].scene = null; particleSystemIconGizmos[i].scene = null;
particleSystemIconGizmos[i].targetNode = null; particleSystemIconGizmos[i].targetNode = null;
_generalHelper.unregisterGizmoTarget(obj); return;
}
}
}
function releaseParticleEmitterGizmo(obj)
{
for (var i = 0; i < particleEmitterGizmos.length; ++i) {
if (particleEmitterGizmos[i].targetNode === obj) {
particleEmitterGizmos[i].scene = null;
particleEmitterGizmos[i].targetNode = null;
return; return;
} }
} }
@@ -555,6 +608,16 @@ Item {
} }
} }
function updateParticleEmitterGizmoScene(scene, obj)
{
for (var i = 0; i < particleEmitterGizmos.length; ++i) {
if (particleEmitterGizmos[i].targetNode === obj) {
particleEmitterGizmos[i].scene = scene;
return;
}
}
}
Component.onCompleted: { Component.onCompleted: {
createEditView(); createEditView();
selectObjects([]); selectObjects([]);
@@ -588,7 +651,6 @@ Item {
return; return;
} }
} }
} }
function onHiddenStateChanged(node) function onHiddenStateChanged(node)
{ {
@@ -610,6 +672,15 @@ Item {
return; return;
} }
} }
for (var i = 0; i < particleEmitterGizmos.length; ++i) {
if (particleEmitterGizmos[i].targetNode === node) {
particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node);
return;
} else if (particleEmitterGizmos[i].targetNode && particleEmitterGizmos[i].targetNode.system === node) {
particleEmitterGizmos[i].systemHidden = _generalHelper.isHidden(node);
return;
}
}
} }
} }
@@ -810,9 +881,16 @@ Item {
onPressed: (mouse)=> { onPressed: (mouse)=> {
if (viewRoot.editView) { if (viewRoot.editView) {
var pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y); // First pick overlay to check for hits there
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), var pickResult = _generalHelper.pickViewAt(overlayView, mouse.x, mouse.y);
mouse.modifiers & Qt.ControlModifier); var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
if (!resolvedResult) {
// No hits from overlay view, pick the main scene
pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y);
resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
}
handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier);
if (pickResult.objectHit) { if (pickResult.objectHit) {
if (transformMode === EditView3D.TransformMode.Move) if (transformMode === EditView3D.TransformMode.Move)

View File

@@ -47,6 +47,7 @@ Item {
property bool locked: false property bool locked: false
property bool globalShow: true property bool globalShow: true
property bool canBeVisible: activeScene === scene && !hidden && (targetNode ? targetNode.visible : false) property bool canBeVisible: activeScene === scene && !hidden && (targetNode ? targetNode.visible : false)
property real iconOpacity: selected ? 0.2 : 1
property alias iconSource: iconImage.source property alias iconSource: iconImage.source
@@ -76,7 +77,7 @@ Item {
border.color: "#7777ff" border.color: "#7777ff"
border.width: !iconGizmo.locked && iconGizmo.highlightOnHover && iconGizmo.hasMouse ? 2 : 0 border.width: !iconGizmo.locked && iconGizmo.highlightOnHover && iconGizmo.hasMouse ? 2 : 0
radius: 5 radius: 5
opacity: iconGizmo.selected ? 0.2 : 1 opacity: iconGizmo.iconOpacity
Image { Image {
id: iconImage id: iconImage
fillMode: Image.Pad fillMode: Image.Pad

View File

@@ -0,0 +1,127 @@
/****************************************************************************
**
** 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 QtQuick3D
import QtQuick3D.Particles3D
Node {
id: root
property Node targetNode: null
property var selectedNodes: []
property Node activeParticleSystem: null
property Node scene: null
property Node activeScene: null
property bool hidden: false
property bool systemHidden: false
property Node shapeModel: null
property bool globalShow: false
property bool canBeVisible: activeScene === scene && targetNode && !hidden && !systemHidden
property bool partOfActiveSystem: root.targetNode && root.targetNode.system === activeParticleSystem
opacity: 0.15
readonly property bool selected: selectedNodes.includes(targetNode)
visible: canBeVisible && (globalShow || selected)
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
rotation: targetNode ? targetNode.sceneRotation : Qt.quaternion(1, 0, 0, 0)
scale: targetNode ? targetNode.sceneScale : Qt.vector3d(1, 1, 1)
function basicShape()
{
if (targetNode && targetNode.shape && targetNode.shape instanceof ParticleShape3D) {
if (targetNode.shape.type === ParticleShape3D.Cube)
return "#Cube";
else if (targetNode.shape.type === ParticleShape3D.Cylinder)
return "#Cylinder";
}
return "#Sphere";
}
function updateShape()
{
if (shapeModel)
shapeModel.destroy();
if (!targetNode)
return;
if (targetNode.shape instanceof ParticleModelShape3D) {
shapeModel = _generalHelper.createParticleEmitterGizmoModel(targetNode, defaultMaterial);
shapeModel.parent = root;
}
}
Component.onCompleted: {
updateShape();
}
Connections {
target: targetNode
function onSystemChanged() { systemHidden = _generalHelper.isHidden(system); }
}
Connections {
target: targetNode
function onShapeChanged() { updateShape(); }
}
Connections {
target: targetNode.shape instanceof ParticleModelShape3D ? targetNode.shape
:null
function onDelegateChanged() { updateShape(); }
}
Connections {
target: targetNode.shape instanceof ParticleModelShape3D ? targetNode.shape.delegate
: null
function onSourceChanged() { updateShape(); }
}
Model {
readonly property Node _pickTarget: root.targetNode
materials: [defaultMaterial]
source: basicShape()
scale: root.targetNode && root.targetNode.shape && targetNode.shape instanceof ParticleShape3D
? root.targetNode.shape.extents.times(0.02) // default extent is 50
: autoScale.getScale(Qt.vector3d(0.1, 0.1, 0.1))
visible: !shapeModel
}
AutoScaleHelper {
id: autoScale
view3D: overlayView
}
DefaultMaterial {
id: defaultMaterial
diffuseColor: root.selected ? "#FF0000" : partOfActiveSystem ? "#FFFF00" : "#AAAAAA"
lighting: DefaultMaterial.NoLighting
cullMode: Material.NoCulling
}
}

View File

@@ -28,5 +28,9 @@ import QtQuick3D 6.0
IconGizmo { IconGizmo {
id: particleSystemGizmo id: particleSystemGizmo
property Node activeParticleSystem: null
iconSource: "qrc:///qtquickplugin/mockfiles/images/editor_particlesystem.png" iconSource: "qrc:///qtquickplugin/mockfiles/images/editor_particlesystem.png"
iconOpacity: selected || activeParticleSystem == targetNode ? 0.2 : 1
} }

View File

@@ -54,6 +54,7 @@ Model {
Model { Model {
id: pickModel id: pickModel
readonly property bool _edit3dLocked: true // Make this non-pickable in main picking handling
objectName: "PickModel for " + rotateRing.objectName objectName: "PickModel for " + rotateRing.objectName
source: "../meshes/ringselect.mesh" source: "../meshes/ringselect.mesh"
pickable: true pickable: true

View File

@@ -46,6 +46,12 @@
#include <QtQuick/qquickitem.h> #include <QtQuick/qquickitem.h>
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#ifdef QUICK3D_PARTICLES_MODULE
#include <QtQuick3DParticles/private/qquick3dparticlemodelshape_p.h>
#include <QtQuick3DParticles/private/qquick3dparticleemitter_p.h>
#include <QtQuick3DParticles/private/qquick3dparticletrailemitter_p.h>
#endif
#include <limits> #include <limits>
namespace QmlDesigner { namespace QmlDesigner {
@@ -404,22 +410,6 @@ QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *pickNode)
return pickNode; return pickNode;
} }
void GeneralHelper::registerGizmoTarget(QQuick3DNode *node)
{
if (!m_gizmoTargets.contains(node)) {
m_gizmoTargets.insert(node);
node->installEventFilter(this);
}
}
void GeneralHelper::unregisterGizmoTarget(QQuick3DNode *node)
{
if (m_gizmoTargets.contains(node)) {
m_gizmoTargets.remove(node);
node->removeEventFilter(this);
}
}
bool GeneralHelper::isLocked(QQuick3DNode *node) const bool GeneralHelper::isLocked(QQuick3DNode *node) const
{ {
if (node) { if (node) {
@@ -460,6 +450,31 @@ bool GeneralHelper::isPickable(QQuick3DNode *node) const
return true; return true;
} }
// Emitter gizmo model creation is done in C++ as creating dynamic properties and
// assigning materials to dynamically created models is lot simpler in C++
QQuick3DNode *GeneralHelper::createParticleEmitterGizmoModel(QQuick3DNode *emitter,
QQuick3DMaterial *material) const
{
#ifdef QUICK3D_PARTICLES_MODULE
auto e = qobject_cast<QQuick3DParticleEmitter *>(emitter);
if (!e || qobject_cast<QQuick3DParticleTrailEmitter *>(e) || !material)
return nullptr;
auto shape = qobject_cast<QQuick3DParticleModelShape *>(e->shape());
if (shape && shape->delegate()) {
if (auto model = qobject_cast<QQuick3DModel *>(
shape->delegate()->create(shape->delegate()->creationContext()))) {
QQmlEngine::setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
model->setProperty("_pickTarget", QVariant::fromValue(emitter));
QQmlListReference matRef(model, "materials");
matRef.append(material);
return model;
}
}
#endif
return nullptr;
}
void GeneralHelper::storeToolState(const QString &sceneId, const QString &tool, const QVariant &state, void GeneralHelper::storeToolState(const QString &sceneId, const QString &tool, const QVariant &state,
int delay) int delay)
{ {
@@ -713,21 +728,6 @@ bool GeneralHelper::isRotationBlocked(QQuick3DNode *node) const
return m_rotationBlockedNodes.contains(node); return m_rotationBlockedNodes.contains(node);
} }
bool GeneralHelper::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::DynamicPropertyChange) {
auto node = qobject_cast<QQuick3DNode *>(obj);
if (m_gizmoTargets.contains(node)) {
auto de = static_cast<QDynamicPropertyChangeEvent *>(event);
if (de->propertyName() == "_edit3dLocked")
emit lockedStateChanged(node);
else if (de->propertyName() == "_edit3dHidden")
emit hiddenStateChanged(node);
}
}
return QObject::eventFilter(obj, event);
}
void GeneralHelper::handlePendingToolStateUpdate() void GeneralHelper::handlePendingToolStateUpdate()
{ {
m_toolStateUpdateTimer.stop(); m_toolStateUpdateTimer.stop();

View File

@@ -41,6 +41,7 @@ QT_BEGIN_NAMESPACE
class QQuick3DCamera; class QQuick3DCamera;
class QQuick3DNode; class QQuick3DNode;
class QQuick3DViewport; class QQuick3DViewport;
class QQuick3DMaterial;
class QQuickItem; class QQuickItem;
QT_END_NAMESPACE QT_END_NAMESPACE
@@ -84,11 +85,11 @@ public:
Q_INVOKABLE QQuick3DPickResult pickViewAt(QQuick3DViewport *view, float posX, float posY); Q_INVOKABLE QQuick3DPickResult pickViewAt(QQuick3DViewport *view, float posX, float posY);
Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode); Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode);
Q_INVOKABLE void registerGizmoTarget(QQuick3DNode *node);
Q_INVOKABLE void unregisterGizmoTarget(QQuick3DNode *node);
Q_INVOKABLE bool isLocked(QQuick3DNode *node) const; Q_INVOKABLE bool isLocked(QQuick3DNode *node) const;
Q_INVOKABLE bool isHidden(QQuick3DNode *node) const; Q_INVOKABLE bool isHidden(QQuick3DNode *node) const;
Q_INVOKABLE bool isPickable(QQuick3DNode *node) const; Q_INVOKABLE bool isPickable(QQuick3DNode *node) const;
Q_INVOKABLE QQuick3DNode *createParticleEmitterGizmoModel(QQuick3DNode *emitter,
QQuick3DMaterial *material) const;
Q_INVOKABLE void storeToolState(const QString &sceneId, const QString &tool, Q_INVOKABLE void storeToolState(const QString &sceneId, const QString &tool,
const QVariant &state, int delayEmit = 0); const QVariant &state, int delayEmit = 0);
@@ -123,9 +124,6 @@ signals:
void lockedStateChanged(QQuick3DNode *node); void lockedStateChanged(QQuick3DNode *node);
void rotationBlocksChanged(); void rotationBlocksChanged();
protected:
bool eventFilter(QObject *obj, QEvent *event) final;
private: private:
void handlePendingToolStateUpdate(); void handlePendingToolStateUpdate();
QVector3D pivotScenePosition(QQuick3DNode *node) const; QVector3D pivotScenePosition(QQuick3DNode *node) const;
@@ -136,7 +134,6 @@ private:
QTimer m_toolStateUpdateTimer; QTimer m_toolStateUpdateTimer;
QHash<QString, QVariantMap> m_toolStates; QHash<QString, QVariantMap> m_toolStates;
QHash<QString, QVariantMap> m_toolStatesPending; QHash<QString, QVariantMap> m_toolStatesPending;
QSet<QQuick3DNode *> m_gizmoTargets;
QSet<QQuick3DNode *> m_rotationBlockedNodes; QSet<QQuick3DNode *> m_rotationBlockedNodes;
struct MultiSelData { struct MultiSelData {

View File

@@ -113,6 +113,7 @@
#include <QtQuick3DParticles/private/qquick3dparticle_p.h> #include <QtQuick3DParticles/private/qquick3dparticle_p.h>
#include <QtQuick3DParticles/private/qquick3dparticleaffector_p.h> #include <QtQuick3DParticles/private/qquick3dparticleaffector_p.h>
#include <QtQuick3DParticles/private/qquick3dparticleemitter_p.h> #include <QtQuick3DParticles/private/qquick3dparticleemitter_p.h>
#include <QtQuick3DParticles/private/qquick3dparticletrailemitter_p.h>
#endif #endif
#ifdef IMPORT_QUICK3D_ASSETS #ifdef IMPORT_QUICK3D_ASSETS
@@ -427,16 +428,25 @@ void Qt5InformationNodeInstanceServer::resetParticleSystem()
void Qt5InformationNodeInstanceServer::handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem) void Qt5InformationNodeInstanceServer::handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem)
{ {
if (!m_particleAnimationDriver || targetParticleSystem == m_targetParticleSystem) if (targetParticleSystem == m_targetParticleSystem)
return; return;
m_particleAnimationDriver->reset();
// stop the previously selected from animating // stop the previously selected from animating
resetParticleSystem(); resetParticleSystem();
m_targetParticleSystem = targetParticleSystem; m_targetParticleSystem = targetParticleSystem;
if (m_editView3DData.rootItem) {
QQmlProperty systemProperty(m_editView3DData.rootItem, "activeParticleSystem", context());
systemProperty.write(objectToVariant(m_targetParticleSystem));
}
if (!m_particleAnimationDriver)
return;
// Ensure clean slate for newly selected system
resetParticleSystem(); resetParticleSystem();
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 2) #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 2)
QObject::disconnect(m_particleAnimationConnection); QObject::disconnect(m_particleAnimationConnection);
m_particleAnimationConnection = connect(m_particleAnimationDriver, &AnimationDriver::advanced, [this] () { m_particleAnimationConnection = connect(m_particleAnimationDriver, &AnimationDriver::advanced, [this] () {
@@ -507,9 +517,17 @@ static QQuick3DParticleSystem *parentParticleSystem(QObject *selectedObject)
return nullptr; return nullptr;
} }
void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected(QObject *selectedObject) void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected()
{ {
resetParticleSystem();
m_targetParticleSystem = nullptr; m_targetParticleSystem = nullptr;
if (m_editView3DData.rootItem) {
QQmlProperty systemProperty(m_editView3DData.rootItem, "activeParticleSystem", context());
systemProperty.write(objectToVariant(nullptr));
}
const auto anim = animations(); const auto anim = animations();
int i = 0; int i = 0;
for (auto a : anim) { for (auto a : anim) {
@@ -526,7 +544,7 @@ void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected(QObject *s
void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &objs) void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &objs)
{ {
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE
resetParticleSystem(); bool skipSystemDeselect = m_targetParticleSystem == nullptr;
#endif #endif
QList<ServerNodeInstance> instanceList; QList<ServerNodeInstance> instanceList;
const QVariantList varObjs = objs.value<QVariantList>(); const QVariantList varObjs = objs.value<QVariantList>();
@@ -535,8 +553,20 @@ void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &ob
if (obj) { if (obj) {
ServerNodeInstance instance = instanceForObject(obj); ServerNodeInstance instance = instanceForObject(obj);
instanceList << instance; instanceList << instance;
#ifdef QUICK3D_PARTICLES_MODULE
if (!skipSystemDeselect) {
auto particleSystem = parentParticleSystem(instance.internalObject());
skipSystemDeselect = particleSystem == m_targetParticleSystem;
}
#endif
} }
} }
#ifdef QUICK3D_PARTICLES_MODULE
if (m_targetParticleSystem && !skipSystemDeselect)
handleParticleSystemDeselected();
#endif
selectInstances(instanceList); selectInstances(instanceList);
// Hold selection changes reflected back from designer for a bit // Hold selection changes reflected back from designer for a bit
m_selectionChangeTimer.start(500); m_selectionChangeTimer.start(500);
@@ -745,6 +775,10 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed(QObject *obj)
} else if (qobject_cast<QQuick3DParticleSystem *>(obj)) { } else if (qobject_cast<QQuick3DParticleSystem *>(obj)) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleSystemGizmo", QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleSystemGizmo",
Q_ARG(QVariant, objectToVariant(obj))); Q_ARG(QVariant, objectToVariant(obj)));
} else if (qobject_cast<QQuick3DParticleEmitter *>(obj)
&& !qobject_cast<QQuick3DParticleTrailEmitter *>(obj)) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleEmitterGizmo",
Q_ARG(QVariant, objectToVariant(obj)));
#endif #endif
} }
removeNode3D(obj); removeNode3D(obj);
@@ -853,6 +887,11 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots()
QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleSystemGizmoScene", QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleSystemGizmoScene",
Q_ARG(QVariant, objectToVariant(newRoot)), Q_ARG(QVariant, objectToVariant(newRoot)),
Q_ARG(QVariant, objectToVariant(node))); Q_ARG(QVariant, objectToVariant(node)));
} else if (qobject_cast<QQuick3DParticleEmitter *>(node)
&& !qobject_cast<QQuick3DParticleTrailEmitter *>(node)) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleEmitterGizmoScene",
Q_ARG(QVariant, objectToVariant(newRoot)),
Q_ARG(QVariant, objectToVariant(node)));
#endif #endif
} }
} }
@@ -1415,15 +1454,19 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
QHash<QObject *, QObjectList> cameras; QHash<QObject *, QObjectList> cameras;
QHash<QObject *, QObjectList> lights; QHash<QObject *, QObjectList> lights;
QHash<QObject *, QObjectList> particleSystems; QHash<QObject *, QObjectList> particleSystems;
QHash<QObject *, QObjectList> particleEmitters;
for (const ServerNodeInstance &instance : instanceList) { for (const ServerNodeInstance &instance : instanceList) {
if (instance.isSubclassOf("QQuick3DCamera")) if (instance.isSubclassOf("QQuick3DCamera")) {
cameras[find3DSceneRoot(instance)] << instance.internalObject(); cameras[find3DSceneRoot(instance)] << instance.internalObject();
else if (instance.isSubclassOf("QQuick3DAbstractLight")) } else if (instance.isSubclassOf("QQuick3DAbstractLight")) {
lights[find3DSceneRoot(instance)] << instance.internalObject(); lights[find3DSceneRoot(instance)] << instance.internalObject();
else if (instance.isSubclassOf("QQuick3DParticleSystem")) } else if (instance.isSubclassOf("QQuick3DParticleSystem")) {
particleSystems[find3DSceneRoot(instance)] << instance.internalObject(); particleSystems[find3DSceneRoot(instance)] << instance.internalObject();
} else if (instance.isSubclassOf("QQuick3DParticleEmitter")
&& !instance.isSubclassOf("QQuick3DParticleTrailEmitter")) {
particleEmitters[find3DSceneRoot(instance)] << instance.internalObject();
}
} }
auto cameraIt = cameras.constBegin(); auto cameraIt = cameras.constBegin();
@@ -1456,6 +1499,17 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
} }
++particleIt; ++particleIt;
} }
auto emitterIt = particleEmitters.constBegin();
while (emitterIt != particleEmitters.constEnd()) {
const auto emitterObjs = emitterIt.value();
for (auto &obj : emitterObjs) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "addParticleEmitterGizmo",
Q_ARG(QVariant, objectToVariant(emitterIt.key())),
Q_ARG(QVariant, objectToVariant(obj)));
}
++emitterIt;
}
} }
void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList<ServerNodeInstance> &instanceList) void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList<ServerNodeInstance> &instanceList)
@@ -1924,6 +1978,9 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
QVariantList selectedObjs; QVariantList selectedObjs;
QObject *firstSceneRoot = nullptr; QObject *firstSceneRoot = nullptr;
ServerNodeInstance firstInstance; ServerNodeInstance firstInstance;
#ifdef QUICK3D_PARTICLES_MODULE
QList<QQuick3DParticleSystem *> selectedParticleSystems;
#endif
for (qint32 id : instanceIds) { for (qint32 id : instanceIds) {
if (hasInstanceForId(id)) { if (hasInstanceForId(id)) {
ServerNodeInstance instance = instanceForId(id); ServerNodeInstance instance = instanceForId(id);
@@ -1937,17 +1994,12 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
object = instance.internalObject(); object = instance.internalObject();
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE
auto particlesystem = qobject_cast<QQuick3DParticleSystem *>(instance.internalObject()); if (selectedParticleSystems.size() <= 1) {
if (particlesystem) { auto particleSystem = qobject_cast<QQuick3DParticleSystem *>(instance.internalObject());
handleParticleSystemSelected(particlesystem); if (!particleSystem)
} else { particleSystem = parentParticleSystem(instance.internalObject());
particlesystem = parentParticleSystem(instance.internalObject()); if (particleSystem && !selectedParticleSystems.contains(particleSystem))
if (particlesystem) { selectedParticleSystems.append(particleSystem);
if (particlesystem != m_targetParticleSystem)
handleParticleSystemSelected(particlesystem);
} else {
handleParticleSystemDeselected(instance.internalObject());
}
} }
#endif #endif
auto isSelectableAsRoot = [&]() -> bool { auto isSelectableAsRoot = [&]() -> bool {
@@ -1957,6 +2009,8 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
|| qobject_cast<QQuick3DAbstractLight *>(object) || qobject_cast<QQuick3DAbstractLight *>(object)
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE
|| qobject_cast<QQuick3DParticleSystem *>(object) || qobject_cast<QQuick3DParticleSystem *>(object)
|| qobject_cast<QQuick3DParticleEmitter *>(object)
#endif #endif
) { ) {
return true; return true;
@@ -1978,6 +2032,14 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
} }
} }
#ifdef QUICK3D_PARTICLES_MODULE
// We only support exactly one active particle systems at a time
if (selectedParticleSystems.size() == 1)
handleParticleSystemSelected(selectedParticleSystems[0]);
else
handleParticleSystemDeselected();
#endif
if (firstSceneRoot && m_active3DScene != firstSceneRoot) { if (firstSceneRoot && m_active3DScene != firstSceneRoot) {
m_active3DScene = firstSceneRoot; m_active3DScene = firstSceneRoot;
m_active3DView = findView3DForInstance(firstInstance); m_active3DView = findView3DForInstance(firstInstance);
@@ -2105,6 +2167,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
updatedState.insert("showCameraFrustum", command.isEnabled()); updatedState.insert("showCameraFrustum", command.isEnabled());
break; break;
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE
case View3DActionCommand::ShowParticleEmitter:
updatedState.insert("showParticleEmitter", command.isEnabled());
break;
case View3DActionCommand::ParticlesPlay: case View3DActionCommand::ParticlesPlay:
m_particleAnimationPlaying = command.isEnabled(); m_particleAnimationPlaying = command.isEnabled();
updatedState.insert("particlePlay", command.isEnabled()); updatedState.insert("particlePlay", command.isEnabled());
@@ -2220,8 +2285,12 @@ void Qt5InformationNodeInstanceServer::handleInstanceLocked(const ServerNodeInst
QObject *obj = instance.internalObject(); QObject *obj = instance.internalObject();
auto node = qobject_cast<QQuick3DNode *>(obj); auto node = qobject_cast<QQuick3DNode *>(obj);
if (node) if (node) {
node->setProperty("_edit3dLocked", edit3dLocked); node->setProperty("_edit3dLocked", edit3dLocked);
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
if (helper)
emit helper->lockedStateChanged(node);
}
const auto children = obj->children(); const auto children = obj->children();
for (auto child : children) { for (auto child : children) {
if (hasInstanceForObject(child)) { if (hasInstanceForObject(child)) {
@@ -2276,6 +2345,9 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst
// as changes in the node tree (reparenting, adding new nodes) can make the previously set // as changes in the node tree (reparenting, adding new nodes) can make the previously set
// hide status based on ancestor unreliable. // hide status based on ancestor unreliable.
node->setProperty("_edit3dHidden", edit3dHidden); node->setProperty("_edit3dHidden", edit3dHidden);
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
if (helper)
emit helper->hiddenStateChanged(node);
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) #if QT_VERSION < QT_VERSION_CHECK(6, 2, 1)
if (auto model = qobject_cast<QQuick3DModel *>(node)) if (auto model = qobject_cast<QQuick3DModel *>(node))
model->setPickable(!edit3dHidden); // allow 3D objects to receive mouse clicks model->setPickable(!edit3dHidden); // allow 3D objects to receive mouse clicks

View File

@@ -151,7 +151,7 @@ private:
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE
void handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem); void handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem);
void resetParticleSystem(); void resetParticleSystem();
void handleParticleSystemDeselected(QObject *selectedObject); void handleParticleSystemDeselected();
#endif #endif
RenderViewData m_editView3DData; RenderViewData m_editView3DData;

View File

@@ -49,7 +49,7 @@ protected:
private: private:
ServerNodeInstance m_currentState; ServerNodeInstance m_currentState;
QSize m_previewSize{160, 160}; QSize m_previewSize{320, 320};
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -32,45 +32,67 @@ import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
Item { Item {
id: rootItem id: root
property var selectedAssets: ({}) property var selectedAssets: ({})
property int allExpandedState: 0 property int allExpandedState: 0
property string contextFilePath: "" property string contextFilePath: ""
property var contextDir: undefined property var contextDir: undefined
property bool isDirContextMenu: false property bool isDirContextMenu: false
property var dropExtFiles: [] // array of supported externally dropped files
function clearSearchFilter() function clearSearchFilter()
{ {
searchBox.text = ""; searchBox.text = "";
} }
DropArea { function updateDropExtFiles(drag)
id: dropArea {
root.dropExtFiles = []
property var files // list of supported dropped files for (const u of drag.urls) {
var url = u.toString();
enabled: true
anchors.fill: parent
onEntered: (drag)=> {
files = []
for (var i = 0; i < drag.urls.length; ++i) {
var url = drag.urls[i].toString();
if (url.startsWith("file:///")) // remove file scheme (happens on Windows) if (url.startsWith("file:///")) // remove file scheme (happens on Windows)
url = url.substr(8) url = url.substr(8)
var ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase() var ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase()
if (rootView.supportedDropSuffixes().includes('*.' + ext)) if (rootView.supportedDropSuffixes().includes('*.' + ext))
files.push(url) root.dropExtFiles.push(url)
} }
if (files.length === 0) drag.accepted = root.dropExtFiles.length > 0
drag.accepted = false; }
DropArea { // handles external drop on empty area of the view (goes to root folder)
id: dropArea
y: assetsView.y + assetsView.contentHeight + 5
width: parent.width
height: parent.height - y
onEntered: (drag)=> {
root.updateDropExtFiles(drag)
} }
onDropped: { onDropped: {
if (files.length > 0) rootView.handleExtFilesDrop(root.dropExtFiles, assetsModel.rootDir().dirPath)
rootView.handleFilesDrop(files) }
Canvas { // marker for the drop area
id: dropCanvas
anchors.fill: parent
visible: dropArea.containsDrag
onWidthChanged: dropCanvas.requestPaint()
onHeightChanged: dropCanvas.requestPaint()
onPaint: {
var ctx = getContext("2d")
ctx.reset()
ctx.strokeStyle = StudioTheme.Values.themeInteraction
ctx.lineWidth = 2
ctx.setLineDash([4, 4])
ctx.rect(5, 5, dropCanvas.width - 10, dropCanvas.height - 10)
ctx.stroke()
}
} }
} }
@@ -79,9 +101,9 @@ Item {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onClicked: { onClicked: {
if (!assetsModel.isEmpty) { if (!assetsModel.isEmpty) {
contextFilePath = "" root.contextFilePath = ""
contextDir = assetsModel.rootDir() root.contextDir = assetsModel.rootDir()
isDirContextMenu = false root.isDirContextMenu = false
contextMenu.popup() contextMenu.popup()
} }
} }
@@ -91,8 +113,8 @@ Item {
function handleViewFocusOut() function handleViewFocusOut()
{ {
contextMenu.close() contextMenu.close()
selectedAssets = {} root.selectedAssets = {}
selectedAssetsChanged() root.selectedAssetsChanged()
} }
StudioControls.Menu { StudioControls.Menu {
@@ -100,42 +122,50 @@ Item {
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape 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 { StudioControls.MenuItem {
text: qsTr("Expand All") text: qsTr("Expand All")
enabled: allExpandedState !== 1 enabled: root.allExpandedState !== 1
visible: isDirContextMenu visible: root.isDirContextMenu
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(true) onTriggered: assetsModel.toggleExpandAll(true)
} }
StudioControls.MenuItem { StudioControls.MenuItem {
text: qsTr("Collapse All") text: qsTr("Collapse All")
enabled: allExpandedState !== 2 enabled: root.allExpandedState !== 2
visible: isDirContextMenu visible: root.isDirContextMenu
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(false) onTriggered: assetsModel.toggleExpandAll(false)
} }
StudioControls.MenuSeparator { StudioControls.MenuSeparator {
visible: isDirContextMenu visible: root.isDirContextMenu
height: visible ? StudioTheme.Values.border : 0 height: visible ? StudioTheme.Values.border : 0
} }
StudioControls.MenuItem { StudioControls.MenuItem {
id: deleteFileItem
text: qsTr("Delete File") text: qsTr("Delete File")
visible: contextFilePath visible: root.contextFilePath
height: visible ? implicitHeight : 0 height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
onTriggered: assetsModel.deleteFile(contextFilePath) onTriggered: {
assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
}
} }
StudioControls.MenuSeparator { StudioControls.MenuSeparator {
visible: contextFilePath visible: root.contextFilePath
height: visible ? StudioTheme.Values.border : 0 height: visible ? StudioTheme.Values.border : 0
} }
StudioControls.MenuItem { StudioControls.MenuItem {
text: qsTr("Rename Folder") text: qsTr("Rename Folder")
visible: isDirContextMenu visible: root.isDirContextMenu
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: renameFolderDialog.open() onTriggered: renameFolderDialog.open()
} }
@@ -147,14 +177,14 @@ Item {
StudioControls.MenuItem { StudioControls.MenuItem {
text: qsTr("Delete Folder") text: qsTr("Delete Folder")
visible: isDirContextMenu visible: root.isDirContextMenu
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: { onTriggered: {
var dirEmpty = !(contextDir.dirsModel && contextDir.dirsModel.rowCount() > 0) var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
&& !(contextDir.filesModel && contextDir.filesModel.rowCount() > 0); && !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
if (dirEmpty) if (dirEmpty)
assetsModel.deleteFolder(contextDir.dirPath) assetsModel.deleteFolder(root.contextDir.dirPath)
else else
confirmDeleteFolderDialog.open() confirmDeleteFolderDialog.open()
} }
@@ -235,7 +265,7 @@ Item {
text: qsTr("Rename") text: qsTr("Rename")
enabled: folderRename.text !== "" enabled: folderRename.text !== ""
onClicked: { onClicked: {
var success = assetsModel.renameFolder(contextDir.dirPath, folderRename.text) var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
if (success) if (success)
renameFolderDialog.accept() renameFolderDialog.accept()
@@ -251,7 +281,7 @@ Item {
} }
onOpened: { onOpened: {
folderRename.text = contextDir.dirName folderRename.text = root.contextDir.dirName
folderRename.selectAll() folderRename.selectAll()
folderRename.forceActiveFocus() folderRename.forceActiveFocus()
renameFolderDialog.renameError = false renameFolderDialog.renameError = false
@@ -309,7 +339,7 @@ Item {
text: qsTr("Create") text: qsTr("Create")
enabled: folderName.text !== "" enabled: folderName.text !== ""
onClicked: { onClicked: {
assetsModel.addNewFolder(contextDir.dirPath + '/' + folderName.text) assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
newFolderDialog.accept() newFolderDialog.accept()
} }
} }
@@ -345,7 +375,7 @@ Item {
id: folderNotEmpty id: folderNotEmpty
text: qsTr("Folder \"%1\" is not empty. Delete it anyway?") text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
.arg(contextDir ? contextDir.dirName : "") .arg(root.contextDir ? root.contextDir.dirName : "")
color: StudioTheme.Values.themeTextColor color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: confirmDeleteFolderDialog.width width: confirmDeleteFolderDialog.width
@@ -373,7 +403,7 @@ Item {
text: qsTr("Delete") text: qsTr("Delete")
onClicked: { onClicked: {
assetsModel.deleteFolder(contextDir.dirPath) assetsModel.deleteFolder(root.contextDir.dirPath)
confirmDeleteFolderDialog.accept() confirmDeleteFolderDialog.accept()
} }
} }
@@ -433,7 +463,7 @@ Item {
spacing: 20 spacing: 20
x: 20 x: 20
width: rootItem.width - 2 * x width: root.width - 2 * x
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
Text { Text {
@@ -492,6 +522,8 @@ Item {
id: dirSection id: dirSection
Section { Section {
id: section
width: assetsView.width - width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5 (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
caption: dirName caption: dirName
@@ -506,16 +538,31 @@ Item {
visible: dirVisible visible: dirVisible
expandOnClick: false expandOnClick: false
useDefaulContextMenu: false useDefaulContextMenu: false
dropEnabled: true
onToggleExpand: { onToggleExpand: {
dirExpanded = !dirExpanded dirExpanded = !dirExpanded
} }
onDropEnter: (drag)=> {
root.updateDropExtFiles(drag)
section.highlight = drag.accepted
}
onDropExit: {
section.highlight = false
}
onDrop: {
section.highlight = false
rootView.handleExtFilesDrop(root.dropExtFiles, dirPath)
}
onShowContextMenu: { onShowContextMenu: {
contextFilePath = "" root.contextFilePath = ""
contextDir = model root.contextDir = model
isDirContextMenu = true root.isDirContextMenu = true
allExpandedState = assetsModel.getAllExpandedState() root.allExpandedState = assetsModel.getAllExpandedState()
contextMenu.popup() contextMenu.popup()
} }
@@ -544,9 +591,9 @@ Item {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onClicked: { onClicked: {
contextFilePath = "" root.contextFilePath = ""
contextDir = model root.contextDir = model
isDirContextMenu = true root.isDirContextMenu = true
contextMenu.popup() contextMenu.popup()
} }
} }
@@ -562,7 +609,8 @@ Item {
width: assetsView.width - width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
height: img.height height: img.height
color: selectedAssets[filePath] ? StudioTheme.Values.themeInteraction color: root.selectedAssets[filePath]
? StudioTheme.Values.themeInteraction
: (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
: "transparent") : "transparent")
@@ -601,28 +649,31 @@ Item {
onPositionChanged: tooltipBackend.reposition() onPositionChanged: tooltipBackend.reposition()
onPressed: (mouse)=> { onPressed: (mouse)=> {
forceActiveFocus() forceActiveFocus()
if (mouse.button === Qt.LeftButton) {
var ctrlDown = mouse.modifiers & Qt.ControlModifier var ctrlDown = mouse.modifiers & Qt.ControlModifier
if (!selectedAssets[filePath] && !ctrlDown) if (mouse.button === Qt.LeftButton) {
selectedAssets = {} if (!root.selectedAssets[filePath] && !ctrlDown)
currFileSelected = ctrlDown ? !selectedAssets[filePath] : true root.selectedAssets = {}
selectedAssets[filePath] = currFileSelected currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
selectedAssetsChanged() root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
var selectedAssetsArr = [] if (currFileSelected) {
for (var assetPath in selectedAssets) { rootView.startDragAsset(
if (selectedAssets[assetPath]) Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
selectedAssetsArr.push(assetPath) mapToGlobal(mouse.x, mouse.y))
} }
if (currFileSelected)
rootView.startDragAsset(selectedAssetsArr, mapToGlobal(mouse.x, mouse.y))
} else { } else {
contextFilePath = filePath if (!root.selectedAssets[filePath] && !ctrlDown)
contextDir = model.fileDir root.selectedAssets = {}
currFileSelected = root.selectedAssets[filePath] || !ctrlDown
root.selectedAssets[filePath] = currFileSelected
root.selectedAssetsChanged()
root.contextFilePath = filePath
root.contextDir = model.fileDir
root.isDirContextMenu = false
tooltipBackend.hideTooltip() tooltipBackend.hideTooltip()
isDirContextMenu = false
contextMenu.popup() contextMenu.popup()
} }
} }
@@ -630,9 +681,9 @@ Item {
onReleased: (mouse)=> { onReleased: (mouse)=> {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (!(mouse.modifiers & Qt.ControlModifier)) if (!(mouse.modifiers & Qt.ControlModifier))
selectedAssets = {} root.selectedAssets = {}
selectedAssets[filePath] = currFileSelected root.selectedAssets[filePath] = currFileSelected
selectedAssetsChanged() root.selectedAssetsChanged()
} }
} }

View File

@@ -32,6 +32,7 @@ import StudioTheme as StudioTheme
import StudioControls as SC import StudioControls as SC
import NewProjectDialog import NewProjectDialog
import BackendApi
Item { Item {
id: rootDialog id: rootDialog
@@ -161,17 +162,43 @@ Item {
readonly property int animDur: 500 readonly property int animDur: 500
id: tabBar id: tabBar
x: 10 // left padding x: 10 // left padding
width: parent.width - 64 // right padding width: parent.width - 20 // right padding
height: DialogValues.projectViewHeaderHeight height: DialogValues.presetViewHeaderHeight
color: DialogValues.lightPaneColor color: DialogValues.lightPaneColor
function selectTab(tabIndex, selectLast = false) {
var item = repeater.itemAt(tabIndex)
tabBarRow.currIndex = tabIndex
presetView.selectLast = selectLast
BackendApi.presetModel.setPage(tabIndex) // NOTE: it resets preset model
}
Connections {
target: BackendApi
function onUserPresetSaved() {
var customTabIndex = repeater.count - 1
tabBar.selectTab(customTabIndex, true)
}
function onLastUserPresetRemoved() {
tabBar.selectTab(0, false)
}
}
Row { Row {
id: tabBarRow id: tabBarRow
spacing: 20 spacing: 20
property int currIndex: 0 property int currIndex: 0
readonly property string currentTabName:
repeater.count > 0 && repeater.itemAt(currIndex)
? repeater.itemAt(currIndex).text
: ''
Repeater { Repeater {
model: categoryModel id: repeater
model: BackendApi.categoryModel
Text { Text {
text: name text: name
font.weight: Font.DemiBold font.weight: Font.DemiBold
@@ -184,13 +211,7 @@ Item {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
tabBarRow.currIndex = index tabBar.selectTab(index)
presetModel.setPage(index)
projectView.currentIndex = 0
projectView.currentIndexChanged()
strip.x = parent.x
strip.width = parent.width
} }
} }
@@ -199,8 +220,19 @@ Item {
} // tabBarRow } // tabBarRow
Rectangle { Rectangle {
function computeX() {
var item = tabBarRow.children[tabBarRow.currIndex] ?? tabBarRow.children[0]
return item.x;
}
function computeWidth() {
var item = tabBarRow.children[tabBarRow.currIndex] ?? tabBarRow.children[0]
return item.width;
}
id: strip id: strip
width: tabBarRow.children[0].width x: computeX()
width: computeWidth()
height: 5 height: 5
radius: 2 radius: 2
color: DialogValues.textColorInteraction color: DialogValues.textColorInteraction
@@ -209,35 +241,40 @@ Item {
Behavior on x { SmoothedAnimation { duration: tabBar.animDur } } Behavior on x { SmoothedAnimation { duration: tabBar.animDur } }
Behavior on width { SmoothedAnimation { duration: strip.width === 0 ? 0 : tabBar.animDur } } // do not animate initial width Behavior on width { SmoothedAnimation { duration: strip.width === 0 ? 0 : tabBar.animDur } } // do not animate initial width
} }
Connections {
target: rootDialog
function onWidthChanged() {
if (rootDialog.width < 1200) { // 1200 = the width threshold
tabBar.width = tabBar.parent.width - 20
projectView.width = projectView.parent.width - 20
} else {
tabBar.width = tabBar.parent.width - 64
projectView.width = projectView.parent.width - 64
}
}
}
} // Rectangle } // Rectangle
NewProjectView { Rectangle {
id: projectView id: presetViewFrame
x: 10 // left padding x: 10 // left padding
width: parent.width - 64 // right padding width: parent.width - 20 // right padding
height: DialogValues.projectViewHeight height: DialogValues.presetViewHeight
color: DialogValues.darkPaneColor
Item {
anchors.fill: parent
anchors.margins: DialogValues.gridMargins
NewProjectView {
id: presetView
anchors.fill: parent
loader: projectDetailsLoader loader: projectDetailsLoader
currentTabName: tabBarRow.currentTabName
Connections { Connections {
target: rootDialog target: rootDialog
function onHeightChanged() { function onHeightChanged() {
if (rootDialog.height < 700) { // 700 = minimum height big dialog if (rootDialog.height < 720) { // 720 = minimum height big dialog
projectView.height = DialogValues.projectViewHeight / 2 DialogValues.presetViewHeight =
DialogValues.presetItemHeight
+ 2 * DialogValues.gridMargins
} else { } else {
projectView.height = DialogValues.projectViewHeight DialogValues.presetViewHeight =
DialogValues.presetItemHeight * 2
+ DialogValues.gridSpacing
+ 2 * DialogValues.gridMargins
}
}
} }
} }
} }
@@ -247,12 +284,12 @@ Item {
Text { Text {
id: descriptionText id: descriptionText
text: dialogBox.projectDescription text: BackendApi.projectDescription
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
leftPadding: 14 leftPadding: 14
width: projectView.width width: presetViewFrame.width
color: DialogValues.textColor color: DialogValues.textColor
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
maximumLineCount: 4 maximumLineCount: 4
@@ -298,7 +335,7 @@ Item {
iconFont: StudioTheme.Constants.font iconFont: StudioTheme.Constants.font
onClicked: { onClicked: {
dialogBox.reject(); BackendApi.reject();
} }
} }
@@ -310,11 +347,11 @@ Item {
visible: true visible: true
buttonIcon: qsTr("Create") buttonIcon: qsTr("Create")
iconSize: DialogValues.defaultPixelSize iconSize: DialogValues.defaultPixelSize
enabled: dialogBox.fieldsValid enabled: BackendApi.fieldsValid
iconFont: StudioTheme.Constants.font iconFont: StudioTheme.Constants.font
onClicked: { onClicked: {
dialogBox.accept(); BackendApi.accept();
} }
} }
} // RowLayout } // RowLayout

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -31,16 +31,13 @@ import QtQuick.Layouts
import StudioControls as SC import StudioControls as SC
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import BackendApi
Item { Item {
width: DialogValues.detailsPaneWidth width: DialogValues.detailsPaneWidth
Component.onCompleted: { Component.onCompleted: BackendApi.detailsLoaded = true
dialogBox.detailsLoaded = true; Component.onDestruction: BackendApi.detailsLoaded = false
}
Component.onDestruction: {
dialogBox.detailsLoaded = false;
}
Rectangle { Rectangle {
color: DialogValues.darkPaneColor color: DialogValues.darkPaneColor
@@ -53,13 +50,13 @@ Item {
Column { Column {
anchors.fill: parent anchors.fill: parent
spacing: DialogValues.defaultPadding spacing: 5
Text { Text {
id: detailsHeading id: detailsHeading
text: qsTr("Details") text: qsTr("Details")
height: DialogValues.paneTitleTextHeight height: DialogValues.paneTitleTextHeight
width: parent.width; width: parent.width
font.weight: Font.DemiBold font.weight: Font.DemiBold
font.pixelSize: DialogValues.paneTitlePixelSize font.pixelSize: DialogValues.paneTitlePixelSize
lineHeight: DialogValues.paneTitleLineHeight lineHeight: DialogValues.paneTitleLineHeight
@@ -71,39 +68,36 @@ Item {
Flickable { Flickable {
width: parent.width width: parent.width
height: parent.height - detailsHeading.height - DialogValues.defaultPadding height: parent.height - detailsHeading.height - DialogValues.defaultPadding
- savePresetButton.height
contentWidth: parent.width contentWidth: parent.width
contentHeight: scrollContent.height contentHeight: scrollContent.height
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
clip: true clip: true
ScrollBar.vertical: SC.VerticalScrollBar { ScrollBar.vertical: SC.VerticalScrollBar {}
}
Column { Column {
id: scrollContent id: scrollContent
width: parent.width - DialogValues.detailsPanePadding width: parent.width - DialogValues.detailsPanePadding
height: DialogValues.detailsScrollableContentHeight
spacing: DialogValues.defaultPadding spacing: DialogValues.defaultPadding
SC.TextField { SC.TextField {
id: projectNameTextField id: projectNameTextField
actionIndicatorVisible: false actionIndicatorVisible: false
translationIndicatorVisible: false translationIndicatorVisible: false
text: dialogBox.projectName text: BackendApi.projectName
width: parent.width width: parent.width
color: DialogValues.textColor color: DialogValues.textColor
selectByMouse: true selectByMouse: true
font.pixelSize: DialogValues.defaultPixelSize
onEditingFinished: { onEditingFinished: {
text = text.charAt(0).toUpperCase() + text.slice(1) text = text.charAt(0).toUpperCase() + text.slice(1)
} }
font.pixelSize: DialogValues.defaultPixelSize
} }
Binding { Binding {
target: dialogBox target: BackendApi
property: "projectName" property: "projectName"
value: projectNameTextField.text value: projectNameTextField.text
} }
@@ -118,14 +112,14 @@ Item {
id: projectLocationTextField id: projectLocationTextField
actionIndicatorVisible: false actionIndicatorVisible: false
translationIndicatorVisible: false translationIndicatorVisible: false
text: dialogBox.projectLocation text: BackendApi.projectLocation
color: DialogValues.textColor color: DialogValues.textColor
selectByMouse: true selectByMouse: true
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
} }
Binding { Binding {
target: dialogBox target: BackendApi
property: "projectLocation" property: "projectLocation"
value: projectLocationTextField.text value: projectLocationTextField.text
} }
@@ -138,7 +132,7 @@ Item {
iconFont: StudioTheme.Constants.font iconFont: StudioTheme.Constants.font
onClicked: { onClicked: {
var newLocation = dialogBox.chooseProjectLocation() var newLocation = BackendApi.chooseProjectLocation()
if (newLocation) if (newLocation)
projectLocationTextField.text = newLocation projectLocationTextField.text = newLocation
} }
@@ -159,7 +153,7 @@ Item {
Text { Text {
id: statusMessage id: statusMessage
text: dialogBox.statusMessage text: BackendApi.statusMessage
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
@@ -172,7 +166,7 @@ Item {
states: [ states: [
State { State {
name: "warning" name: "warning"
when: dialogBox.statusType === "warning" when: BackendApi.statusType === "warning"
PropertyChanges { PropertyChanges {
target: statusMessage target: statusMessage
color: DialogValues.textWarning color: DialogValues.textWarning
@@ -185,7 +179,7 @@ Item {
State { State {
name: "error" name: "error"
when: dialogBox.statusType === "error" when: BackendApi.statusType === "error"
PropertyChanges { PropertyChanges {
target: statusMessage target: statusMessage
color: DialogValues.textError color: DialogValues.textError
@@ -208,7 +202,7 @@ Item {
} }
Binding { Binding {
target: dialogBox target: BackendApi
property: "saveAsDefaultLocation" property: "saveAsDefaultLocation"
value: defaultLocationCheckbox.checked value: defaultLocationCheckbox.checked
} }
@@ -219,25 +213,25 @@ Item {
id: screenSizeComboBox id: screenSizeComboBox
actionIndicatorVisible: false actionIndicatorVisible: false
currentIndex: -1 currentIndex: -1
model: screenSizeModel model: BackendApi.screenSizeModel
textRole: "display" textRole: "display"
width: parent.width width: parent.width
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
onActivated: (index) => { onActivated: (index) => {
dialogBox.setScreenSizeIndex(index); BackendApi.setScreenSizeIndex(index);
var size = screenSizeModel.screenSizes(index); var size = BackendApi.screenSizeModel.screenSizes(index);
widthField.realValue = size.width; widthField.realValue = size.width;
heightField.realValue = size.height; heightField.realValue = size.height;
} }
Connections { Connections {
target: screenSizeModel target: BackendApi.screenSizeModel
function onModelReset() { function onModelReset() {
var newIndex = screenSizeComboBox.currentIndex > -1 var newIndex = screenSizeComboBox.currentIndex > -1
? screenSizeComboBox.currentIndex ? screenSizeComboBox.currentIndex
: dialogBox.screenSizeIndex() : BackendApi.screenSizeIndex()
screenSizeComboBox.currentIndex = newIndex screenSizeComboBox.currentIndex = newIndex
screenSizeComboBox.activated(newIndex) screenSizeComboBox.activated(newIndex)
@@ -248,10 +242,8 @@ Item {
GridLayout { // orientation + width + height GridLayout { // orientation + width + height
width: parent.width width: parent.width
height: 85 height: 85
columns: 4 columns: 4
rows: 2 rows: 2
columnSpacing: 10 columnSpacing: 10
rowSpacing: 10 rowSpacing: 10
@@ -295,10 +287,7 @@ Item {
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
onRealValueChanged: { onRealValueChanged: {
var height = heightField.realValue if (widthField.realValue >= heightField.realValue)
var width = realValue
if (width >= height)
orientationButton.setHorizontal() orientationButton.setHorizontal()
else else
orientationButton.setVertical() orientationButton.setVertical()
@@ -306,7 +295,7 @@ Item {
} // Width Text Field } // Width Text Field
Binding { Binding {
target: dialogBox target: BackendApi
property: "customWidth" property: "customWidth"
value: widthField.realValue value: widthField.realValue
} }
@@ -323,10 +312,7 @@ Item {
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
onRealValueChanged: { onRealValueChanged: {
var height = realValue if (widthField.realValue >= heightField.realValue)
var width = widthField.realValue
if (width >= height)
orientationButton.setHorizontal() orientationButton.setHorizontal()
else else
orientationButton.setVertical() orientationButton.setVertical()
@@ -334,7 +320,7 @@ Item {
} // Height Text Field } // Height Text Field
Binding { Binding {
target: dialogBox target: BackendApi
property: "customHeight" property: "customHeight"
value: heightField.realValue value: heightField.realValue
} }
@@ -345,7 +331,6 @@ Item {
id: orientationButton id: orientationButton
implicitWidth: 100 implicitWidth: 100
implicitHeight: 50 implicitHeight: 50
checked: false checked: false
hoverEnabled: false hoverEnabled: false
background: Rectangle { background: Rectangle {
@@ -384,19 +369,22 @@ Item {
onClicked: { onClicked: {
if (widthField.realValue && heightField.realValue) { if (widthField.realValue && heightField.realValue) {
[widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue]; [widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue]
checked = !checked orientationButton.checked = !orientationButton.checked
if (widthField.realValue === heightField.realValue)
orientationButton.checked ? setVertical() : setHorizontal()
} }
} }
function setHorizontal() { function setHorizontal() {
checked = false orientationButton.checked = false
horizontalBar.color = DialogValues.textColorInteraction horizontalBar.color = DialogValues.textColorInteraction
verticalBar.color = "white" verticalBar.color = "white"
} }
function setVertical() { function setVertical() {
checked = true orientationButton.checked = true
horizontalBar.color = "white" horizontalBar.color = "white"
verticalBar.color = DialogValues.textColorInteraction verticalBar.color = DialogValues.textColorInteraction
} }
@@ -404,23 +392,27 @@ Item {
} // GridLayout: orientation + width + height } // GridLayout: orientation + width + height
Rectangle { width: parent.width; height: 1; color: DialogValues.dividerlineColor } Rectangle {
width: parent.width
height: 1
color: DialogValues.dividerlineColor
}
SC.CheckBox { SC.CheckBox {
id: useQtVirtualKeyboard id: useQtVirtualKeyboard
actionIndicatorVisible: false actionIndicatorVisible: false
text: qsTr("Use Qt Virtual Keyboard") text: qsTr("Use Qt Virtual Keyboard")
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
checked: dialogBox.useVirtualKeyboard checked: BackendApi.useVirtualKeyboard
visible: dialogBox.haveVirtualKeyboard visible: BackendApi.haveVirtualKeyboard
} }
RowLayout { // Target Qt Version RowLayout { // Target Qt Version
width: parent.width width: parent.width
visible: dialogBox.haveTargetQtVersion visible: BackendApi.haveTargetQtVersion
Text { Text {
text: "Target Qt Version:" text: qsTr("Target Qt Version:")
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
@@ -432,33 +424,98 @@ Item {
actionIndicatorVisible: false actionIndicatorVisible: false
implicitWidth: 70 implicitWidth: 70
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
currentIndex: 1 currentIndex: BackendApi.targetQtVersionIndex
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
model: ListModel { model: ListModel {
ListElement { ListElement { name: "Qt 5" }
name: "Qt 5" ListElement { name: "Qt 6" }
}
ListElement {
name: "Qt 6"
}
} }
onActivated: (index) => { onActivated: (index) => {
dialogBox.setTargetQtVersion(index) BackendApi.targetQtVersionIndex = index
} }
} // Target Qt Version ComboBox } // Target Qt Version ComboBox
Binding {
target: BackendApi
property: "targetQtVersionIndex"
value: qtVersionComboBox.currentIndex
}
} // RowLayout } // RowLayout
Binding { Binding {
target: dialogBox target: BackendApi
property: "useVirtualKeyboard" property: "useVirtualKeyboard"
value: useQtVirtualKeyboard.checked value: useQtVirtualKeyboard.checked
} }
} // ScrollContent Column } // ScrollContent Column
} // ScrollView } // ScrollView
} // Column } // Column
} // Item
SC.AbstractButton {
id: savePresetButton
width: StudioTheme.Values.singleControlColumnWidth
buttonIcon: qsTr("Save Custom Preset")
iconFont: StudioTheme.Constants.font
iconSize: DialogValues.defaultPixelSize
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
onClicked: savePresetDialog.open()
} }
}
PopupDialog {
id: savePresetDialog
title: qsTr("Save Preset")
standardButtons: Dialog.Save | Dialog.Cancel
modal: true
closePolicy: Popup.CloseOnEscape
anchors.centerIn: parent
width: DialogValues.popupDialogWidth
onAccepted: BackendApi.savePresetDialogAccept()
onOpened: {
presetNameTextField.selectAll()
presetNameTextField.forceActiveFocus()
}
ColumnLayout {
width: parent.width
spacing: 10
Text {
text: qsTr("Preset name")
font.pixelSize: DialogValues.defaultPixelSize
color: DialogValues.textColor
}
SC.TextField {
id: presetNameTextField
actionIndicatorVisible: false
translationIndicatorVisible: false
text: qsTr("MyPreset")
color: DialogValues.textColor
font.pixelSize: DialogValues.defaultPixelSize
Layout.fillWidth: true
maximumLength: 30
validator: RegularExpressionValidator { regularExpression: /\w[\w ]*/ }
onEditingFinished: {
presetNameTextField.text = text.trim()
presetNameTextField.text = text.replace(/\s+/g, " ")
}
}
Binding {
target: BackendApi
property: "presetName"
value: presetNameTextField.text
}
}
}
} // Item
} // Rectangle
} // root Item

View File

@@ -29,39 +29,57 @@ import QtQml
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
QtObject { QtObject {
id: root
readonly property int dialogWidth: 1522 readonly property int dialogWidth: 1522
readonly property int dialogHeight: 940 readonly property int dialogHeight: 940
readonly property int projectViewMinimumWidth: 600 readonly property int presetViewMinimumWidth: 600
readonly property int projectViewMinimumHeight: projectViewHeight readonly property int presetViewMinimumHeight: root.gridCellHeight
readonly property int dialogContentHeight: projectViewHeight + 300 // i.e. dialog without header and footer readonly property int dialogContentHeight: root.presetViewHeight + 300 // i.e. dialog without header and footer
readonly property int loadedPanesWidth: detailsPaneWidth + stylesPaneWidth readonly property int loadedPanesWidth: root.detailsPaneWidth + root.stylesPaneWidth
readonly property int detailsPaneWidth: 330 + detailsPanePadding * 2 readonly property int detailsPaneWidth: 330 + root.detailsPanePadding * 2
readonly property int dialogTitleTextHeight: 85 readonly property int dialogTitleTextHeight: 85
readonly property int paneTitleTextHeight: 47 readonly property int paneTitleTextHeight: 47
readonly property int logoWidth: 85 readonly property int logoWidth: 85
readonly property int logoHeight: 85 readonly property int logoHeight: 85
/* detailsScrollableContentHeight - the full height that may need to be scrolled to be fully readonly property int stylesPaneWidth: root.styleImageWidth + root.stylesPanePadding * 2
visible, if the dialog box is too small. */ + root.styleImageBorderWidth * 2 // i.e. 240px
readonly property int detailsScrollableContentHeight: 428
readonly property int stylesPaneWidth: styleImageWidth + stylesPanePadding * 2 + styleImageBorderWidth * 2 // i.e. 240px
readonly property int detailsPanePadding: 18 readonly property int detailsPanePadding: 18
readonly property int stylesPanePadding: 18 readonly property int stylesPanePadding: 18
readonly property int defaultPadding: 18 readonly property int defaultPadding: 18
readonly property int dialogLeftPadding: 35 readonly property int dialogLeftPadding: 35
readonly property int styleListItemHeight: root.styleImageHeight + root.styleTextHeight
+ 2 * root.styleImageBorderWidth
+ root.styleListItemBottomMargin
+ root.styleListItemSpacing
readonly property int styleListItemBottomMargin: 10
readonly property int styleListItemSpacing: 4
readonly property int styleImageWidth: 200 readonly property int styleImageWidth: 200
readonly property int styleImageHeight: 262
readonly property int styleImageBorderWidth: 2 readonly property int styleImageBorderWidth: 2
readonly property int styleTextHeight: 18
readonly property int footerHeight: 73 readonly property int footerHeight: 73
readonly property int projectItemWidth: 90 readonly property int presetItemWidth: 136
readonly property int projectItemHeight: 144 readonly property int presetItemHeight: 110
readonly property int projectViewHeight: projectItemHeight * 2 property int presetViewHeight: root.presetItemHeight * 2 + root.gridSpacing + root.gridMargins * 2
readonly property int projectViewHeaderHeight: 38 readonly property int presetViewHeaderHeight: 38
readonly property int gridMargins: 20
readonly property int gridCellWidth: root.presetItemWidth + root.gridSpacing
readonly property int gridCellHeight: root.presetItemHeight + root.gridSpacing
readonly property int gridSpacing: 2
readonly property int dialogButtonWidth: 100 readonly property int dialogButtonWidth: 100
readonly property int loadedPanesHeight: dialogContentHeight // This is for internal popup dialogs
readonly property int detailsPaneHeight: dialogContentHeight readonly property int popupDialogWidth: 270
readonly property int popupDialogPadding: 12
readonly property int loadedPanesHeight: root.dialogContentHeight
readonly property int detailsPaneHeight: root.dialogContentHeight
readonly property string darkPaneColor: StudioTheme.Values.themeBackgroundColorNormal readonly property string darkPaneColor: StudioTheme.Values.themeBackgroundColorNormal
readonly property string lightPaneColor: StudioTheme.Values.themeBackgroundColorAlternate readonly property string lightPaneColor: StudioTheme.Values.themeBackgroundColorAlternate
@@ -71,6 +89,8 @@ QtObject {
readonly property string dividerlineColor: StudioTheme.Values.themeTextColorDisabled readonly property string dividerlineColor: StudioTheme.Values.themeTextColorDisabled
readonly property string textError: StudioTheme.Values.themeError readonly property string textError: StudioTheme.Values.themeError
readonly property string textWarning: StudioTheme.Values.themeWarning readonly property string textWarning: StudioTheme.Values.themeWarning
readonly property string presetItemBackgroundHover: StudioTheme.Values.themeControlBackgroundGlobalHover
readonly property string presetItemBackgroundHoverInteraction: StudioTheme.Values.themeControlBackgroundInteraction
readonly property real defaultPixelSize: 14 readonly property real defaultPixelSize: 14
readonly property real defaultLineHeight: 21 readonly property real defaultLineHeight: 21
@@ -91,6 +111,6 @@ QtObject {
item and spacing after it). So we have to subtract 2 x layout spacing before setting item and spacing after it). So we have to subtract 2 x layout spacing before setting
our own, narrower, spacing. our own, narrower, spacing.
*/ */
return -layoutSpacing -layoutSpacing + value return -layoutSpacing - layoutSpacing + value
} }
} }

View File

@@ -28,111 +28,252 @@ import QtQuick.Controls
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import StudioControls as SC
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
GridView { import BackendApi
id: projectView
ScrollView {
id: scrollView
required property Item loader required property Item loader
required property string currentTabName
cellWidth: DialogValues.projectItemWidth property string backgroundHoverColor: DialogValues.presetItemBackgroundHover
cellHeight: DialogValues.projectItemHeight
clip: true
boundsBehavior: Flickable.StopAtBounds // selectLast: if true, it will select last item in the model after a model reset.
property bool selectLast: false
children: [ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Rectangle { ScrollBar.vertical: SC.VerticalScrollBar {
color: DialogValues.darkPaneColor parent: scrollView
anchors.fill: parent x: scrollView.width + (DialogValues.gridMargins
z: -1 - StudioTheme.Values.scrollBarThickness) * 0.5
y: scrollView.topPadding
height: scrollView.availableHeight
} }
]
model: presetModel contentWidth: gridView.contentItem.childrenRect.width
contentHeight: gridView.contentItem.childrenRect.height
GridView {
id: gridView
clip: true
anchors.fill: parent
cellWidth: DialogValues.gridCellWidth
cellHeight: DialogValues.gridCellHeight
rightMargin: -DialogValues.gridSpacing
bottomMargin: -DialogValues.gridSpacing
boundsBehavior: Flickable.StopAtBounds
model: BackendApi.presetModel
// called by onModelReset and when user clicks on an item, or when the header item is changed. // called by onModelReset and when user clicks on an item, or when the header item is changed.
onCurrentIndexChanged: { onCurrentIndexChanged: {
dialogBox.selectedPreset = projectView.currentIndex BackendApi.selectedPreset = gridView.currentIndex
var source = dialogBox.currentPresetQmlPath() var source = BackendApi.currentPresetQmlPath()
loader.source = source scrollView.loader.source = source
} }
Connections { Connections {
target: presetModel target: BackendApi.presetModel
// called when data is set (setWizardFactories) // called when data is set (setWizardFactories)
function onModelReset() { function onModelReset() {
currentIndex = 0 if (scrollView.selectLast) {
currentIndexChanged() gridView.currentIndex = BackendApi.presetModel.rowCount() - 1
scrollView.selectLast = false
} else {
gridView.currentIndex = 0
}
// This will load Details.qml and Styles.qml by setting "source" on the Loader.
gridView.currentIndexChanged()
} }
} }
delegate: ItemDelegate { delegate: ItemDelegate {
id: delegate id: delegate
width: DialogValues.projectItemWidth property bool hover: delegate.hovered || removeMouseArea.containsMouse
height: DialogValues.projectItemHeight
background: null width: DialogValues.presetItemWidth
height: DialogValues.presetItemHeight
onClicked: delegate.GridView.view.currentIndex = index
background: Rectangle {
id: delegateBackground
width: parent.width
height: parent.height
color: delegate.hover ? scrollView.backgroundHoverColor : "transparent"
border.color: delegate.hover ? presetName.color : "transparent"
}
function fontIconCode(index) { function fontIconCode(index) {
var code = presetModel.fontIconCode(index) var code = BackendApi.presetModel.fontIconCode(index)
return code ? code : StudioTheme.Constants.wizardsUnknown return code ? code : StudioTheme.Constants.wizardsUnknown
} }
Column { contentItem: Item {
width: parent.width anchors.fill: parent
height: parent.height
ColumnLayout {
spacing: 0
anchors.top: parent.top
anchors.topMargin: -1
anchors.horizontalCenter: parent.horizontalCenter
Label { Label {
id: projectTypeIcon id: presetIcon
text: fontIconCode(index) text: delegate.fontIconCode(index)
color: DialogValues.textColor color: DialogValues.textColor
width: parent.width
height: DialogValues.projectItemHeight / 2
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignBottom verticalAlignment: Text.AlignBottom
renderType: Text.NativeRendering renderType: Text.NativeRendering
font.pixelSize: 65 font.pixelSize: 65
font.family: StudioTheme.Constants.iconFont.family font.family: StudioTheme.Constants.iconFont.family
} Layout.alignment: Qt.AlignHCenter
} // Preset type icon Label
Text { Text {
id: projectTypeLabel id: presetName
color: DialogValues.textColor color: DialogValues.textColor
text: name text: name
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
width: parent.width width: DialogValues.presetItemWidth - 16
height: DialogValues.projectItemHeight / 2 elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: presetName.width
Layout.minimumWidth: presetName.width
Layout.maximumWidth: presetName.width
ToolTip {
id: toolTip
y: -toolTip.height
visible: delegate.hovered && presetName.truncated
text: name
delay: 1000
height: 20
background: Rectangle {
color: StudioTheme.Values.themeToolTipBackground
border.color: StudioTheme.Values.themeToolTipOutline
border.width: StudioTheme.Values.border
}
contentItem: Text {
color: StudioTheme.Values.themeToolTipText
text: toolTip.text
font.pixelSize: DialogValues.defaultPixelSize
verticalAlignment: Text.AlignVCenter
}
}
}
Text {
id: presetResolution
color: DialogValues.textColor
text: resolution
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
wrapMode: Text.Wrap wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop verticalAlignment: Text.AlignTop
Layout.alignment: Qt.AlignHCenter
}
} // ColumnLayout
Item {
id: removePresetButton
width: 20
height: 20
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 4
visible: isUserPreset === true
&& delegate.hover
&& scrollView.currentTabName !== BackendApi.recentsTabName()
Text {
anchors.fill: parent
text: StudioTheme.Constants.closeCross
color: DialogValues.textColor
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
font.family: StudioTheme.Constants.iconFont.family
font.pixelSize: StudioTheme.Values.myIconFontSize
} }
} // Column
MouseArea { MouseArea {
id: removeMouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true
cursorShape: removeMouseArea.containsMouse ? Qt.PointingHandCursor
: Qt.ArrowCursor
onClicked: { onClicked: {
delegate.GridView.view.currentIndex = index removePresetDialog.presetName = presetName.text
removePresetDialog.open()
} }
} }
} // Delete preset button Item
} // Item
states: [ states: [
State { State {
name: "current"
when: delegate.GridView.isCurrentItem when: delegate.GridView.isCurrentItem
PropertyChanges {
target: projectTypeLabel
color: DialogValues.textColorInteraction
}
PropertyChanges { PropertyChanges {
target: projectTypeIcon target: presetName
color: DialogValues.textColorInteraction color: DialogValues.textColorInteraction
} }
PropertyChanges {
target: presetResolution
color: DialogValues.textColorInteraction
}
PropertyChanges {
target: presetIcon
color: DialogValues.textColorInteraction
}
PropertyChanges {
target: scrollView
backgroundHoverColor: DialogValues.presetItemBackgroundHoverInteraction
}
} // State } // State
] ]
} // ItemDelegate } // ItemDelegate
} // GridView
PopupDialog {
id: removePresetDialog
property string presetName
title: qsTr("Delete Custom Preset")
standardButtons: Dialog.Yes | Dialog.No
modal: true
closePolicy: Popup.CloseOnEscape
anchors.centerIn: parent
width: DialogValues.popupDialogWidth
onAccepted: BackendApi.removeCurrentPreset()
Text {
text: qsTr("Are you sure you want to delete \"" + removePresetDialog.presetName + "\" ?")
color: DialogValues.textColor
font.pixelSize: DialogValues.defaultPixelSize
wrapMode: Text.WordWrap
width: DialogValues.popupDialogWidth - 2 * DialogValues.popupDialogPadding
}
} // Dialog
} // GridView
} // ScrollView

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** 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 StudioTheme as StudioTheme
import BackendApi
Dialog {
id: root
padding: DialogValues.popupDialogPadding
background: Rectangle {
color: DialogValues.darkPaneColor
border.color: StudioTheme.Values.themeInteraction
border.width: StudioTheme.Values.border
}
header: Label {
text: root.title
visible: root.title
elide: Label.ElideRight
font.bold: true
font.pixelSize: DialogValues.defaultPixelSize
padding: DialogValues.popupDialogPadding
color: DialogValues.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
x: 1
y: 1
width: parent.width - 2
height: parent.height - 1
color: DialogValues.darkPaneColor
}
}
footer: PopupDialogButtonBox {
visible: count > 0
}
}

View File

@@ -0,0 +1,107 @@
/****************************************************************************
**
** 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 StudioTheme as StudioTheme
Button {
id: root
implicitWidth: Math.max(
background ? background.implicitWidth : 0,
textItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(
background ? background.implicitHeight : 0,
textItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 4
rightPadding: 4
background: Rectangle {
id: background
implicitWidth: 80
implicitHeight: StudioTheme.Values.height
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeControlOutline
anchors.fill: parent
}
contentItem: Text {
id: textItem
text: root.text
font.family: StudioTheme.Constants.font.family
font.pixelSize: DialogValues.defaultPixelSize
color: StudioTheme.Values.themeTextColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
renderType: Text.QtRendering
anchors.fill: parent
}
states: [
State {
name: "default"
when: !root.hovered && !root.checked && !root.pressed
PropertyChanges {
target: background
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeControlOutline
}
PropertyChanges {
target: textItem
color: StudioTheme.Values.themeTextColor
}
},
State {
name: "hover"
when: root.hovered && !root.checked && !root.pressed
PropertyChanges {
target: background
color: StudioTheme.Values.themeControlBackgroundHover
border.color: StudioTheme.Values.themeControlOutline
}
PropertyChanges {
target: textItem
color: StudioTheme.Values.themeTextColor
}
},
State {
name: "press"
when: root.hovered && root.pressed
PropertyChanges {
target: background
color: StudioTheme.Values.themeInteraction
border.color: StudioTheme.Values.themeInteraction
}
PropertyChanges {
target: textItem
color: StudioTheme.Values.themeIconColor
}
}
]
}

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** 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
DialogButtonBox {
id: root
padding: DialogValues.popupDialogPadding
alignment: Qt.AlignRight | Qt.AlignBottom
background: Rectangle {
implicitHeight: 40
x: 1
y: 1
width: parent.width - 2
height: parent.height - 2
color: DialogValues.darkPaneColor
}
delegate: PopupDialogButton {
width: root.count === 1 ? root.availableWidth / 2 : undefined
}
}

View File

@@ -23,20 +23,21 @@
** **
****************************************************************************/ ****************************************************************************/
import QtQuick
import QtQuick.Window import QtQuick.Window
import QtQuick.Controls import QtQuick.Controls
import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import StudioControls as SC import StudioControls as SC
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import BackendApi
Item { Item {
width: DialogValues.stylesPaneWidth width: DialogValues.stylesPaneWidth
Component.onCompleted: { Component.onCompleted: {
dialogBox.stylesLoaded = true; BackendApi.stylesLoaded = true
/* /*
* TODO: roleNames is called before the backend model (in the proxy class StyleModel) is * TODO: roleNames is called before the backend model (in the proxy class StyleModel) is
@@ -47,7 +48,7 @@ Item {
} }
Component.onDestruction: { Component.onDestruction: {
dialogBox.stylesLoaded = false; BackendApi.stylesLoaded = false
} }
Rectangle { Rectangle {
@@ -57,35 +58,40 @@ Item {
Item { Item {
x: DialogValues.stylesPanePadding x: DialogValues.stylesPanePadding
width: parent.width - DialogValues.stylesPanePadding * 2 + styleScrollBar.width width: parent.width - DialogValues.stylesPanePadding * 2
height: parent.height height: parent.height
ColumnLayout {
anchors.fill: parent
spacing: 5
Text { Text {
id: styleTitleText id: styleTitleText
text: qsTr("Style") text: qsTr("Style")
Layout.minimumHeight: DialogValues.paneTitleTextHeight height: DialogValues.paneTitleTextHeight
width: parent.width
font.weight: Font.DemiBold font.weight: Font.DemiBold
font.pixelSize: DialogValues.paneTitlePixelSize font.pixelSize: DialogValues.paneTitlePixelSize
lineHeight: DialogValues.paneTitleLineHeight lineHeight: DialogValues.paneTitleLineHeight
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
color: DialogValues.textColor color: DialogValues.textColor
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
function refresh() { function refresh() {
text = qsTr("Style") + " (" + styleModel.rowCount() + ")" styleTitleText.text = qsTr("Style") + " (" + BackendApi.styleModel.rowCount() + ")"
} }
} }
SC.ComboBox { // Style Filter ComboBox SC.ComboBox { // Style Filter ComboBox
id: styleComboBox
actionIndicatorVisible: false actionIndicatorVisible: false
currentIndex: 0 currentIndex: 0
textRole: "text" textRole: "text"
valueRole: "value" valueRole: "value"
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
width: parent.width
anchors.top: styleTitleText.bottom
anchors.topMargin: 5
model: ListModel { model: ListModel {
ListElement { text: qsTr("All"); value: "all" } ListElement { text: qsTr("All"); value: "all" }
@@ -93,80 +99,79 @@ Item {
ListElement { text: qsTr("Dark"); value: "dark" } ListElement { text: qsTr("Dark"); value: "dark" }
} }
implicitWidth: parent.width
onActivated: (index) => { onActivated: (index) => {
styleModel.filter(currentValue.toLowerCase()); BackendApi.styleModel.filter(currentValue.toLowerCase())
styleTitleText.refresh(); styleTitleText.refresh()
} }
} // Style Filter ComboBox } // Style Filter ComboBox
Item { implicitWidth: 1; implicitHeight: 9 } ScrollView {
id: scrollView
anchors.top: styleComboBox.bottom
anchors.topMargin: 11
anchors.bottom: parent.bottom
anchors.bottomMargin: DialogValues.stylesPanePadding
width: parent.width
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical: SC.VerticalScrollBar {
id: styleScrollBar
x: stylesList.width + (DialogValues.stylesPanePadding
- StudioTheme.Values.scrollBarThickness) * 0.5
}
ListView { ListView {
id: stylesList id: stylesList
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: styleModel
MouseArea {
id: listViewMouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
}
focus: true focus: true
clip: true
model: BackendApi.styleModel
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
bottomMargin: -DialogValues.styleListItemBottomMargin
ScrollBar.vertical: SC.VerticalScrollBar {
id: styleScrollBar
property int extraPadding: 0
bottomInset: extraPadding
bottomPadding: bottomInset + 16
viewMouseArea: listViewMouseArea
} // ScrollBar
onCurrentIndexChanged: { onCurrentIndexChanged: {
if (styleModel.rowCount() > 0) if (BackendApi.styleModel.rowCount() > 0)
dialogBox.styleIndex = stylesList.currentIndex; BackendApi.styleIndex = stylesList.currentIndex
} }
delegate: ItemDelegate { delegate: ItemDelegate {
id: delegateId id: delegateId
height: styleImage.height + DialogValues.styleImageBorderWidth + styleText.height + extraPadding.height + 1 width: stylesList.width
width: stylesList.width - styleScrollBar.width height: DialogValues.styleListItemHeight
Component.onCompleted: { onClicked: stylesList.currentIndex = index
styleScrollBar.extraPadding = styleText.height + extraPadding.height
}
Rectangle { background: Rectangle {
anchors.fill: parent anchors.fill: parent
color: DialogValues.lightPaneColor color: DialogValues.lightPaneColor
}
Column { contentItem: Item {
spacing: 0
anchors.fill: parent anchors.fill: parent
Column {
anchors.fill: parent
spacing: DialogValues.styleListItemSpacing
Rectangle { Rectangle {
border.color: index == stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent" width: DialogValues.styleImageWidth
border.width: index == stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0 + 2 * DialogValues.styleImageBorderWidth
height: DialogValues.styleImageHeight
+ 2 * DialogValues.styleImageBorderWidth
border.color: index === stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent"
border.width: index === stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0
color: "transparent" color: "transparent"
width: parent.width
height: parent.height - styleText.height - extraPadding.height
Image { Image {
id: styleImage id: styleImage
asynchronous: false
source: "image://newprojectdialog_library/" + styleModel.iconId(model.index)
width: 200
height: 262
x: DialogValues.styleImageBorderWidth x: DialogValues.styleImageBorderWidth
y: DialogValues.styleImageBorderWidth y: DialogValues.styleImageBorderWidth
width: DialogValues.styleImageWidth
height: DialogValues.styleImageHeight
asynchronous: false
source: "image://newprojectdialog_library/" + BackendApi.styleModel.iconId(model.index)
} }
} // Rectangle } // Rectangle
@@ -175,35 +180,26 @@ Item {
text: model.display text: model.display
font.pixelSize: DialogValues.defaultPixelSize font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight lineHeight: DialogValues.defaultLineHeight
height: 18 height: DialogValues.styleTextHeight
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
width: parent.width width: parent.width
color: DialogValues.textColor color: DialogValues.textColor
} }
Item { id: extraPadding; width: 1; height: 10 }
} // Column } // Column
} // Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
stylesList.currentIndex = index
}
} }
} }
Connections { Connections {
target: styleModel target: BackendApi.styleModel
function onModelReset() { function onModelReset() {
stylesList.currentIndex = dialogBox.styleIndex; stylesList.currentIndex = BackendApi.styleIndex
stylesList.currentIndexChanged(); stylesList.currentIndexChanged()
styleTitleText.refresh(); styleTitleText.refresh()
} }
} }
} // ListView } // ListView
} // ColumnLayout } // ScrollView
} // Parent Item } // Parent Item
} // Rectangle } // Rectangle
} }

View File

@@ -1,4 +1,7 @@
singleton DialogValues 1.0 DialogValues.qml PopupDialog 1.0 PopupDialog.qml
PopupDialogButton 1.0 PopupDialogButton.qml
PopupDialogButtonBox 1.0 PopupDialogButtonBox.qml
Details 1.0 Details.qml Details 1.0 Details.qml
Styles 1.0 Styles.qml singleton DialogValues 1.0 DialogValues.qml
NewProjectView 1.0 NewProjectView.qml NewProjectView 1.0 NewProjectView.qml
Styles 1.0 Styles.qml

View File

@@ -140,7 +140,7 @@ StudioControls.TextField {
+ 2 : 0) + 2 : 0)
height: StudioTheme.Values.height - 2 * StudioTheme.Values.border height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
color: StudioTheme.Values.themeInteraction color: StudioTheme.Values.themeInteraction
y: listView.currentItem.y y: listView.currentItem?.y ?? 0
} }
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
} }

View File

@@ -54,6 +54,8 @@ Item {
property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead
property bool addTopPadding: true property bool addTopPadding: true
property bool addBottomPadding: true property bool addBottomPadding: true
property bool dropEnabled: false
property bool highlight: false
property bool useDefaulContextMenu: true property bool useDefaulContextMenu: true
@@ -72,9 +74,22 @@ Item {
function onExpandAll() { section.expanded = true } function onExpandAll() { section.expanded = true }
} }
signal drop(var drag)
signal dropEnter(var drag)
signal dropExit()
signal showContextMenu() signal showContextMenu()
signal toggleExpand() signal toggleExpand()
DropArea {
id: dropArea
enabled: section.dropEnabled
anchors.fill: parent
onEntered: (drag)=> section.dropEnter(drag)
onDropped: (drag)=> section.drop(drag)
onExited: section.dropExit()
}
Rectangle { Rectangle {
id: header id: header
@@ -82,7 +97,9 @@ Item {
visible: !section.hideHeader visible: !section.hideHeader
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.0 + (0.2 * section.level)) color: section.highlight ? StudioTheme.Values.themeInteraction
: Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.0
+ (0.2 * section.level))
Item { Item {
StudioControls.Menu { StudioControls.Menu {

View File

@@ -35,37 +35,26 @@ ScrollBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding) implicitContentHeight + topPadding + bottomPadding)
property bool scrollBarVisible: parent.childrenRect.height > parent.height property bool scrollBarVisible: parent.contentHeight > scrollBar.height
// viewMouseArea: if set, the scrollbar will be visible only on hover over the view containing
// the mouse area item.
property MouseArea viewMouseArea: null
minimumSize: orientation == Qt.Horizontal ? height / width : width / height
minimumSize: scrollBar.width / scrollBar.height
orientation: Qt.Vertical orientation: Qt.Vertical
policy: computePolicy() policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
x: parent.width - width
y: 0
height: parent.availableHeight height: parent.availableHeight
- (parent.bothVisible ? parent.horizontalThickness : 0) - (parent.bothVisible ? parent.horizontalThickness : 0)
padding: 0 padding: scrollBar.active ? StudioTheme.Values.scrollBarActivePadding
: StudioTheme.Values.scrollBarInactivePadding
background: Rectangle { background: Rectangle {
implicitWidth: StudioTheme.Values.scrollBarThickness
implicitHeight: StudioTheme.Values.scrollBarThickness
color: StudioTheme.Values.themeScrollBarTrack color: StudioTheme.Values.themeScrollBarTrack
} }
contentItem: Rectangle { contentItem: Rectangle {
implicitWidth: StudioTheme.Values.scrollBarThickness implicitWidth: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
implicitHeight: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
color: StudioTheme.Values.themeScrollBarHandle color: StudioTheme.Values.themeScrollBarHandle
} }
function computePolicy() {
if (!scrollBar.scrollBarVisible)
return ScrollBar.AlwaysOff;
if (scrollBar.viewMouseArea)
return scrollBar.viewMouseArea.containsMouse ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
else
return ScrollBar.AlwaysOn;
}
} }

View File

@@ -89,6 +89,8 @@ QtObject {
property real typeLabelVerticalShift: Math.round(6 * values.scaleFactor) property real typeLabelVerticalShift: Math.round(6 * values.scaleFactor)
property real scrollBarThickness: 10 property real scrollBarThickness: 10
property real scrollBarActivePadding: 1
property real scrollBarInactivePadding: 2
property real toolTipHeight: 25 property real toolTipHeight: 25
property int toolTipDelay: 1000 property int toolTipDelay: 1000

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