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:
QT_VERSION: 6.2.3
CLANG_VERSION: 130
CLANG_VERSION: 140
ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1
NINJA_VERSION: 1.10.2
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
jobs:
@@ -31,12 +31,14 @@ jobs:
environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat",
is_msvc: true
}
# - {
# name: "Windows Latest MinGW", artifact: "Windows-MinGW",
# os: windows-latest,
# cc: "gcc", cxx: "g++",
# is_msvc: false
# }
- {
name: "Windows Latest MinGW", artifact: "Windows-MinGW",
os: windows-latest,
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",
toolchain_path: "mingw64/bin",
cc: "gcc", cxx: "g++",
is_msvc: false
}
- {
name: "Ubuntu Latest GCC", artifact: "Linux",
os: ubuntu-latest,
@@ -111,6 +113,33 @@ jobs:
)
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
id: qt
shell: cmake -P {0}
@@ -121,8 +150,8 @@ jobs:
if ("${{ runner.os }}" STREQUAL "Windows")
set(url_os "windows_x86")
if ("x${{ matrix.config.environment_script }}" STREQUAL "x")
set(qt_package_arch_suffix "win64_mingw81")
set(qt_dir_prefix "${qt_version}/mingw81_64")
set(qt_package_arch_suffix "win64_mingw")
set(qt_dir_prefix "${qt_version}/mingw_64")
set(qt_package_suffix "-Windows-Windows_10_21H2-Mingw-Windows-Windows_10_21H2-X86_64")
elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat")
set(qt_package_arch_suffix "win64_msvc2019_64")
@@ -218,7 +247,7 @@ jobs:
if ("x${{ matrix.config.environment_script }}" STREQUAL "x")
# deploy MinGW
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"
USE_SOURCE_PERMISSIONS)
endforeach()
@@ -434,22 +463,6 @@ jobs:
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
shell: cmake -P {0}
run: |
@@ -516,6 +529,14 @@ jobs:
unset(NO_DMG)
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(
COMMAND python
-u
@@ -673,10 +694,10 @@ jobs:
name: "Windows Latest MSVC", artifact: "Windows-MSVC",
os: ubuntu-latest
}
# - {
# name: "Windows Latest MinGW", artifact: "Windows-MinGW",
# os: ubuntu-latest
# }
- {
name: "Windows Latest MinGW", artifact: "Windows-MinGW",
os: ubuntu-latest
}
- {
name: "Ubuntu Latest GCC", artifact: "Linux",
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/
**
** 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
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 >
\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
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
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/
**
** This file is part of the Qt Creator documentation.
@@ -40,12 +40,18 @@
\title Configuring the Editor
\QC allows you to configure the text editor to suit your specific
needs. To configure the editor, select \uicontrol Tools >
\uicontrol Options > \uicontrol{Text Editor}.
You can configure the text editor to suit your specific needs by selecting
\uicontrol Tools > \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)
For more information, see \l{Specifying Editor Settings}.
\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/
**
** This file is part of the Qt Creator documentation.
@@ -31,19 +31,22 @@
\image qtcreator-meson-build-settings.png "Meson build settings"
Settings are grouped by category by Meson. All items are user modifiable
except \c backend which is forced to Ninja, \c {buildtype}, \c debug as well
as \c optimization to ensure a good compatibility with \QC.
Meson builds projects in the directory specified in the
\uicontrol {Build directory} field.
Each setting type has its own editor. To modif any setting, double-click it,
either edit the field, or select your choice depending on the control. To
apply changes, select \uicontrol {Apply configuration changes}. This will
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.
Build settings are grouped by category. You can modify all settings,
except \c backend, which is forced to Ninja, \c {buildtype}, \c debug,
and \c optimization to ensure compatibility with \QC.
\note Any modified setting will remain in bold until \uicontrol
{Apply configuration changes} is selected.
To modify a setting, double-click it. Modified settings are formatted in
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}.
@@ -56,8 +59,7 @@
\image qtcreator-meson-build-steps.png "Meson build steps"
The build errors and warnings are parsed and displayed in the
\uicontrol Issues output pane.
The build errors and warnings are parsed and displayed in \l Issues.
\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/
**
** This file is part of the Qt Creator documentation.
@@ -99,7 +99,6 @@
The following features are not supported yet:
\list
\li Cross compilation.
\li Showing header files in project tree.
\li Configuration change detection, for example when building triggers a
Meson configuration first.

View File

@@ -80,7 +80,7 @@
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.
\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.
@@ -326,7 +326,7 @@
\uicontrol {New Subproject}. Follow the steps in the
\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
\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/
**
** This file is part of the Qt Creator documentation.
@@ -38,6 +38,18 @@
execute any JavaScript functions. Therefore, you must make sure that the
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}.
//! [run settings debugger]

View File

@@ -36,7 +36,7 @@
\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
Qt Quick project from scratch.

View File

@@ -299,7 +299,7 @@
\uicontrol {Include Old Entries} and \uicontrol {Include Tags}.
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.
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/
**
** This file is part of the Qt Design Studio documentation.
@@ -26,7 +26,7 @@
/*!
\previouspage qtquick-form-editor.html
\page studio-3d-editor.html
\nextpage quick-library.html
\nextpage quick-components-view.html
\title 3D Editor
@@ -64,11 +64,11 @@
affect only the local transformations of the component or whether they
transform with respect to the global space.
Additional helpful features when editing 3D scenes are the \e {edit light},
which is a quick way to light the scene, and the grid that helps you to
navigate in 3D space. Select the \inlineimage grid_on.png
(\uicontrol {Toggle Grid Visibility}) or press \key G to show or hide the
grid.
Another helpful feature when editing 3D scenes is the \e {edit light},
which is a quick way to light the scene.
Additionally, you can toggle the visibility of the grid, selection boxes,
icon gizmos, and camera frustums in the 3D scene.
To refresh the contents of \uicontrol {3D Editor}, press \key P or
select the \inlineimage icons/reset.png
@@ -97,7 +97,7 @@
\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
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
view to rotate the view.
\li To zoom, use the mouse wheel or press \key Alt and right-click
@@ -109,7 +109,7 @@
select \inlineimage fit_selected.png
(\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
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
@@ -179,6 +179,7 @@
{keyboard shortcuts} applicable to your operating system, for example,
\key Ctrl+C and \key Ctrl+V on Windows to copy-paste components.
\target moving components 3d editor
\section1 Moving Components
\image studio-3d-editor-move.png "3D Editor in move mode"
@@ -233,6 +234,76 @@
gray handle at the center of the component.
\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
The \uicontrol {3D Editor} toolbar contains the following buttons:
@@ -254,7 +325,7 @@
\inlineimage move_on.png
\li Activate the Move Tool
\li \key W
\li \l{Moving Components}
\li \l{moving components 3d editor}{Moving Components}
\row
\li \inlineimage rotate_off.png
\inlineimage rotate_on.png
@@ -291,17 +362,47 @@
\li \key U
\li \l{Using Edit Light}
\row
\li \inlineimage grid_off.png
\inlineimage grid_on.png
\li Toggle Grid Visibility
\li \key G
\li \inlineimage icons/align-camera-on.png
\li Align Selected Cameras to View
\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
\li \inlineimage icons/reset.png
\li Reset View
\li \key R
\li \key P
\li
\endtable
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,6 +47,7 @@ Item {
property bool locked: false
property bool globalShow: true
property bool canBeVisible: activeScene === scene && !hidden && (targetNode ? targetNode.visible : false)
property real iconOpacity: selected ? 0.2 : 1
property alias iconSource: iconImage.source
@@ -76,7 +77,7 @@ Item {
border.color: "#7777ff"
border.width: !iconGizmo.locked && iconGizmo.highlightOnHover && iconGizmo.hasMouse ? 2 : 0
radius: 5
opacity: iconGizmo.selected ? 0.2 : 1
opacity: iconGizmo.iconOpacity
Image {
id: iconImage
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 {
id: particleSystemGizmo
property Node activeParticleSystem: null
iconSource: "qrc:///qtquickplugin/mockfiles/images/editor_particlesystem.png"
iconOpacity: selected || activeParticleSystem == targetNode ? 0.2 : 1
}

View File

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

View File

@@ -46,6 +46,12 @@
#include <QtQuick/qquickitem.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>
namespace QmlDesigner {
@@ -404,22 +410,6 @@ QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *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
{
if (node) {
@@ -460,6 +450,31 @@ bool GeneralHelper::isPickable(QQuick3DNode *node) const
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,
int delay)
{
@@ -713,21 +728,6 @@ bool GeneralHelper::isRotationBlocked(QQuick3DNode *node) const
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()
{
m_toolStateUpdateTimer.stop();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,7 @@ import StudioTheme as StudioTheme
import StudioControls as SC
import NewProjectDialog
import BackendApi
Item {
id: rootDialog
@@ -161,17 +162,43 @@ Item {
readonly property int animDur: 500
id: tabBar
x: 10 // left padding
width: parent.width - 64 // right padding
height: DialogValues.projectViewHeaderHeight
width: parent.width - 20 // right padding
height: DialogValues.presetViewHeaderHeight
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 {
id: tabBarRow
spacing: 20
property int currIndex: 0
readonly property string currentTabName:
repeater.count > 0 && repeater.itemAt(currIndex)
? repeater.itemAt(currIndex).text
: ''
Repeater {
model: categoryModel
id: repeater
model: BackendApi.categoryModel
Text {
text: name
font.weight: Font.DemiBold
@@ -184,13 +211,7 @@ Item {
MouseArea {
anchors.fill: parent
onClicked: {
tabBarRow.currIndex = index
presetModel.setPage(index)
projectView.currentIndex = 0
projectView.currentIndexChanged()
strip.x = parent.x
strip.width = parent.width
tabBar.selectTab(index)
}
}
@@ -199,8 +220,19 @@ Item {
} // tabBarRow
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
width: tabBarRow.children[0].width
x: computeX()
width: computeWidth()
height: 5
radius: 2
color: DialogValues.textColorInteraction
@@ -209,35 +241,40 @@ Item {
Behavior on x { SmoothedAnimation { duration: tabBar.animDur } }
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
NewProjectView {
id: projectView
Rectangle {
id: presetViewFrame
x: 10 // left padding
width: parent.width - 64 // right padding
height: DialogValues.projectViewHeight
width: parent.width - 20 // right padding
height: DialogValues.presetViewHeight
color: DialogValues.darkPaneColor
Item {
anchors.fill: parent
anchors.margins: DialogValues.gridMargins
NewProjectView {
id: presetView
anchors.fill: parent
loader: projectDetailsLoader
currentTabName: tabBarRow.currentTabName
Connections {
target: rootDialog
function onHeightChanged() {
if (rootDialog.height < 700) { // 700 = minimum height big dialog
projectView.height = DialogValues.projectViewHeight / 2
if (rootDialog.height < 720) { // 720 = minimum height big dialog
DialogValues.presetViewHeight =
DialogValues.presetItemHeight
+ 2 * DialogValues.gridMargins
} else {
projectView.height = DialogValues.projectViewHeight
DialogValues.presetViewHeight =
DialogValues.presetItemHeight * 2
+ DialogValues.gridSpacing
+ 2 * DialogValues.gridMargins
}
}
}
}
}
@@ -247,12 +284,12 @@ Item {
Text {
id: descriptionText
text: dialogBox.projectDescription
text: BackendApi.projectDescription
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
leftPadding: 14
width: projectView.width
width: presetViewFrame.width
color: DialogValues.textColor
wrapMode: Text.WordWrap
maximumLineCount: 4
@@ -298,7 +335,7 @@ Item {
iconFont: StudioTheme.Constants.font
onClicked: {
dialogBox.reject();
BackendApi.reject();
}
}
@@ -310,11 +347,11 @@ Item {
visible: true
buttonIcon: qsTr("Create")
iconSize: DialogValues.defaultPixelSize
enabled: dialogBox.fieldsValid
enabled: BackendApi.fieldsValid
iconFont: StudioTheme.Constants.font
onClicked: {
dialogBox.accept();
BackendApi.accept();
}
}
} // 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 StudioTheme as StudioTheme
import BackendApi
Item {
width: DialogValues.detailsPaneWidth
Component.onCompleted: {
dialogBox.detailsLoaded = true;
}
Component.onDestruction: {
dialogBox.detailsLoaded = false;
}
Component.onCompleted: BackendApi.detailsLoaded = true
Component.onDestruction: BackendApi.detailsLoaded = false
Rectangle {
color: DialogValues.darkPaneColor
@@ -53,13 +50,13 @@ Item {
Column {
anchors.fill: parent
spacing: DialogValues.defaultPadding
spacing: 5
Text {
id: detailsHeading
text: qsTr("Details")
height: DialogValues.paneTitleTextHeight
width: parent.width;
width: parent.width
font.weight: Font.DemiBold
font.pixelSize: DialogValues.paneTitlePixelSize
lineHeight: DialogValues.paneTitleLineHeight
@@ -71,39 +68,36 @@ Item {
Flickable {
width: parent.width
height: parent.height - detailsHeading.height - DialogValues.defaultPadding
- savePresetButton.height
contentWidth: parent.width
contentHeight: scrollContent.height
boundsBehavior: Flickable.StopAtBounds
clip: true
ScrollBar.vertical: SC.VerticalScrollBar {
}
ScrollBar.vertical: SC.VerticalScrollBar {}
Column {
id: scrollContent
width: parent.width - DialogValues.detailsPanePadding
height: DialogValues.detailsScrollableContentHeight
spacing: DialogValues.defaultPadding
SC.TextField {
id: projectNameTextField
actionIndicatorVisible: false
translationIndicatorVisible: false
text: dialogBox.projectName
text: BackendApi.projectName
width: parent.width
color: DialogValues.textColor
selectByMouse: true
font.pixelSize: DialogValues.defaultPixelSize
onEditingFinished: {
text = text.charAt(0).toUpperCase() + text.slice(1)
}
font.pixelSize: DialogValues.defaultPixelSize
}
Binding {
target: dialogBox
target: BackendApi
property: "projectName"
value: projectNameTextField.text
}
@@ -118,14 +112,14 @@ Item {
id: projectLocationTextField
actionIndicatorVisible: false
translationIndicatorVisible: false
text: dialogBox.projectLocation
text: BackendApi.projectLocation
color: DialogValues.textColor
selectByMouse: true
font.pixelSize: DialogValues.defaultPixelSize
}
Binding {
target: dialogBox
target: BackendApi
property: "projectLocation"
value: projectLocationTextField.text
}
@@ -138,7 +132,7 @@ Item {
iconFont: StudioTheme.Constants.font
onClicked: {
var newLocation = dialogBox.chooseProjectLocation()
var newLocation = BackendApi.chooseProjectLocation()
if (newLocation)
projectLocationTextField.text = newLocation
}
@@ -159,7 +153,7 @@ Item {
Text {
id: statusMessage
text: dialogBox.statusMessage
text: BackendApi.statusMessage
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
@@ -172,7 +166,7 @@ Item {
states: [
State {
name: "warning"
when: dialogBox.statusType === "warning"
when: BackendApi.statusType === "warning"
PropertyChanges {
target: statusMessage
color: DialogValues.textWarning
@@ -185,7 +179,7 @@ Item {
State {
name: "error"
when: dialogBox.statusType === "error"
when: BackendApi.statusType === "error"
PropertyChanges {
target: statusMessage
color: DialogValues.textError
@@ -208,7 +202,7 @@ Item {
}
Binding {
target: dialogBox
target: BackendApi
property: "saveAsDefaultLocation"
value: defaultLocationCheckbox.checked
}
@@ -219,25 +213,25 @@ Item {
id: screenSizeComboBox
actionIndicatorVisible: false
currentIndex: -1
model: screenSizeModel
model: BackendApi.screenSizeModel
textRole: "display"
width: parent.width
font.pixelSize: DialogValues.defaultPixelSize
onActivated: (index) => {
dialogBox.setScreenSizeIndex(index);
BackendApi.setScreenSizeIndex(index);
var size = screenSizeModel.screenSizes(index);
var size = BackendApi.screenSizeModel.screenSizes(index);
widthField.realValue = size.width;
heightField.realValue = size.height;
}
Connections {
target: screenSizeModel
target: BackendApi.screenSizeModel
function onModelReset() {
var newIndex = screenSizeComboBox.currentIndex > -1
? screenSizeComboBox.currentIndex
: dialogBox.screenSizeIndex()
: BackendApi.screenSizeIndex()
screenSizeComboBox.currentIndex = newIndex
screenSizeComboBox.activated(newIndex)
@@ -248,10 +242,8 @@ Item {
GridLayout { // orientation + width + height
width: parent.width
height: 85
columns: 4
rows: 2
columnSpacing: 10
rowSpacing: 10
@@ -295,10 +287,7 @@ Item {
font.pixelSize: DialogValues.defaultPixelSize
onRealValueChanged: {
var height = heightField.realValue
var width = realValue
if (width >= height)
if (widthField.realValue >= heightField.realValue)
orientationButton.setHorizontal()
else
orientationButton.setVertical()
@@ -306,7 +295,7 @@ Item {
} // Width Text Field
Binding {
target: dialogBox
target: BackendApi
property: "customWidth"
value: widthField.realValue
}
@@ -323,10 +312,7 @@ Item {
font.pixelSize: DialogValues.defaultPixelSize
onRealValueChanged: {
var height = realValue
var width = widthField.realValue
if (width >= height)
if (widthField.realValue >= heightField.realValue)
orientationButton.setHorizontal()
else
orientationButton.setVertical()
@@ -334,7 +320,7 @@ Item {
} // Height Text Field
Binding {
target: dialogBox
target: BackendApi
property: "customHeight"
value: heightField.realValue
}
@@ -345,7 +331,6 @@ Item {
id: orientationButton
implicitWidth: 100
implicitHeight: 50
checked: false
hoverEnabled: false
background: Rectangle {
@@ -384,19 +369,22 @@ Item {
onClicked: {
if (widthField.realValue && heightField.realValue) {
[widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue];
checked = !checked
[widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue]
orientationButton.checked = !orientationButton.checked
if (widthField.realValue === heightField.realValue)
orientationButton.checked ? setVertical() : setHorizontal()
}
}
function setHorizontal() {
checked = false
orientationButton.checked = false
horizontalBar.color = DialogValues.textColorInteraction
verticalBar.color = "white"
}
function setVertical() {
checked = true
orientationButton.checked = true
horizontalBar.color = "white"
verticalBar.color = DialogValues.textColorInteraction
}
@@ -404,23 +392,27 @@ Item {
} // GridLayout: orientation + width + height
Rectangle { width: parent.width; height: 1; color: DialogValues.dividerlineColor }
Rectangle {
width: parent.width
height: 1
color: DialogValues.dividerlineColor
}
SC.CheckBox {
id: useQtVirtualKeyboard
actionIndicatorVisible: false
text: qsTr("Use Qt Virtual Keyboard")
font.pixelSize: DialogValues.defaultPixelSize
checked: dialogBox.useVirtualKeyboard
visible: dialogBox.haveVirtualKeyboard
checked: BackendApi.useVirtualKeyboard
visible: BackendApi.haveVirtualKeyboard
}
RowLayout { // Target Qt Version
width: parent.width
visible: dialogBox.haveTargetQtVersion
visible: BackendApi.haveTargetQtVersion
Text {
text: "Target Qt Version:"
text: qsTr("Target Qt Version:")
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
@@ -432,33 +424,98 @@ Item {
actionIndicatorVisible: false
implicitWidth: 70
Layout.alignment: Qt.AlignRight
currentIndex: 1
currentIndex: BackendApi.targetQtVersionIndex
font.pixelSize: DialogValues.defaultPixelSize
model: ListModel {
ListElement {
name: "Qt 5"
}
ListElement {
name: "Qt 6"
}
ListElement { name: "Qt 5" }
ListElement { name: "Qt 6" }
}
onActivated: (index) => {
dialogBox.setTargetQtVersion(index)
BackendApi.targetQtVersionIndex = index
}
} // Target Qt Version ComboBox
Binding {
target: BackendApi
property: "targetQtVersionIndex"
value: qtVersionComboBox.currentIndex
}
} // RowLayout
Binding {
target: dialogBox
target: BackendApi
property: "useVirtualKeyboard"
value: useQtVirtualKeyboard.checked
}
} // ScrollContent Column
} // ScrollView
} // Column
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
QtObject {
id: root
readonly property int dialogWidth: 1522
readonly property int dialogHeight: 940
readonly property int projectViewMinimumWidth: 600
readonly property int projectViewMinimumHeight: projectViewHeight
readonly property int dialogContentHeight: projectViewHeight + 300 // i.e. dialog without header and footer
readonly property int loadedPanesWidth: detailsPaneWidth + stylesPaneWidth
readonly property int detailsPaneWidth: 330 + detailsPanePadding * 2
readonly property int presetViewMinimumWidth: 600
readonly property int presetViewMinimumHeight: root.gridCellHeight
readonly property int dialogContentHeight: root.presetViewHeight + 300 // i.e. dialog without header and footer
readonly property int loadedPanesWidth: root.detailsPaneWidth + root.stylesPaneWidth
readonly property int detailsPaneWidth: 330 + root.detailsPanePadding * 2
readonly property int dialogTitleTextHeight: 85
readonly property int paneTitleTextHeight: 47
readonly property int logoWidth: 85
readonly property int logoHeight: 85
/* detailsScrollableContentHeight - the full height that may need to be scrolled to be fully
visible, if the dialog box is too small. */
readonly property int detailsScrollableContentHeight: 428
readonly property int stylesPaneWidth: styleImageWidth + stylesPanePadding * 2 + styleImageBorderWidth * 2 // i.e. 240px
readonly property int stylesPaneWidth: root.styleImageWidth + root.stylesPanePadding * 2
+ root.styleImageBorderWidth * 2 // i.e. 240px
readonly property int detailsPanePadding: 18
readonly property int stylesPanePadding: 18
readonly property int defaultPadding: 18
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 styleImageHeight: 262
readonly property int styleImageBorderWidth: 2
readonly property int styleTextHeight: 18
readonly property int footerHeight: 73
readonly property int projectItemWidth: 90
readonly property int projectItemHeight: 144
readonly property int projectViewHeight: projectItemHeight * 2
readonly property int projectViewHeaderHeight: 38
readonly property int presetItemWidth: 136
readonly property int presetItemHeight: 110
property int presetViewHeight: root.presetItemHeight * 2 + root.gridSpacing + root.gridMargins * 2
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 loadedPanesHeight: dialogContentHeight
readonly property int detailsPaneHeight: dialogContentHeight
// This is for internal popup dialogs
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 lightPaneColor: StudioTheme.Values.themeBackgroundColorAlternate
@@ -71,6 +89,8 @@ QtObject {
readonly property string dividerlineColor: StudioTheme.Values.themeTextColorDisabled
readonly property string textError: StudioTheme.Values.themeError
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 defaultLineHeight: 21

View File

@@ -28,111 +28,252 @@ import QtQuick.Controls
import QtQuick
import QtQuick.Layouts
import StudioControls as SC
import StudioTheme as StudioTheme
GridView {
id: projectView
import BackendApi
ScrollView {
id: scrollView
required property Item loader
required property string currentTabName
cellWidth: DialogValues.projectItemWidth
cellHeight: DialogValues.projectItemHeight
clip: true
property string backgroundHoverColor: DialogValues.presetItemBackgroundHover
boundsBehavior: Flickable.StopAtBounds
// selectLast: if true, it will select last item in the model after a model reset.
property bool selectLast: false
children: [
Rectangle {
color: DialogValues.darkPaneColor
anchors.fill: parent
z: -1
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical: SC.VerticalScrollBar {
parent: scrollView
x: scrollView.width + (DialogValues.gridMargins
- 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.
onCurrentIndexChanged: {
dialogBox.selectedPreset = projectView.currentIndex
var source = dialogBox.currentPresetQmlPath()
loader.source = source
BackendApi.selectedPreset = gridView.currentIndex
var source = BackendApi.currentPresetQmlPath()
scrollView.loader.source = source
}
Connections {
target: presetModel
target: BackendApi.presetModel
// called when data is set (setWizardFactories)
function onModelReset() {
currentIndex = 0
currentIndexChanged()
if (scrollView.selectLast) {
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 {
id: delegate
width: DialogValues.projectItemWidth
height: DialogValues.projectItemHeight
background: null
property bool hover: delegate.hovered || removeMouseArea.containsMouse
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) {
var code = presetModel.fontIconCode(index)
var code = BackendApi.presetModel.fontIconCode(index)
return code ? code : StudioTheme.Constants.wizardsUnknown
}
Column {
width: parent.width
height: parent.height
contentItem: Item {
anchors.fill: parent
ColumnLayout {
spacing: 0
anchors.top: parent.top
anchors.topMargin: -1
anchors.horizontalCenter: parent.horizontalCenter
Label {
id: projectTypeIcon
text: fontIconCode(index)
id: presetIcon
text: delegate.fontIconCode(index)
color: DialogValues.textColor
width: parent.width
height: DialogValues.projectItemHeight / 2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignBottom
renderType: Text.NativeRendering
font.pixelSize: 65
font.family: StudioTheme.Constants.iconFont.family
}
Layout.alignment: Qt.AlignHCenter
} // Preset type icon Label
Text {
id: projectTypeLabel
id: presetName
color: DialogValues.textColor
text: name
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
width: parent.width
height: DialogValues.projectItemHeight / 2
width: DialogValues.presetItemWidth - 16
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
horizontalAlignment: Text.AlignHCenter
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 {
id: removeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: removeMouseArea.containsMouse ? Qt.PointingHandCursor
: Qt.ArrowCursor
onClicked: {
delegate.GridView.view.currentIndex = index
removePresetDialog.presetName = presetName.text
removePresetDialog.open()
}
}
} // Delete preset button Item
} // Item
states: [
State {
name: "current"
when: delegate.GridView.isCurrentItem
PropertyChanges {
target: projectTypeLabel
color: DialogValues.textColorInteraction
}
PropertyChanges {
target: projectTypeIcon
target: presetName
color: DialogValues.textColorInteraction
}
PropertyChanges {
target: presetResolution
color: DialogValues.textColorInteraction
}
PropertyChanges {
target: presetIcon
color: DialogValues.textColorInteraction
}
PropertyChanges {
target: scrollView
backgroundHoverColor: DialogValues.presetItemBackgroundHoverInteraction
}
} // State
]
} // ItemDelegate
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.Controls
import QtQuick
import QtQuick.Layouts
import StudioControls as SC
import StudioTheme as StudioTheme
import BackendApi
Item {
width: DialogValues.stylesPaneWidth
Component.onCompleted: {
dialogBox.stylesLoaded = true;
BackendApi.stylesLoaded = true
/*
* TODO: roleNames is called before the backend model (in the proxy class StyleModel) is
@@ -47,7 +48,7 @@ Item {
}
Component.onDestruction: {
dialogBox.stylesLoaded = false;
BackendApi.stylesLoaded = false
}
Rectangle {
@@ -57,35 +58,40 @@ Item {
Item {
x: DialogValues.stylesPanePadding
width: parent.width - DialogValues.stylesPanePadding * 2 + styleScrollBar.width
width: parent.width - DialogValues.stylesPanePadding * 2
height: parent.height
ColumnLayout {
anchors.fill: parent
spacing: 5
Text {
id: styleTitleText
text: qsTr("Style")
Layout.minimumHeight: DialogValues.paneTitleTextHeight
height: DialogValues.paneTitleTextHeight
width: parent.width
font.weight: Font.DemiBold
font.pixelSize: DialogValues.paneTitlePixelSize
lineHeight: DialogValues.paneTitleLineHeight
lineHeightMode: Text.FixedHeight
color: DialogValues.textColor
verticalAlignment: Qt.AlignVCenter
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
function refresh() {
text = qsTr("Style") + " (" + styleModel.rowCount() + ")"
styleTitleText.text = qsTr("Style") + " (" + BackendApi.styleModel.rowCount() + ")"
}
}
SC.ComboBox { // Style Filter ComboBox
id: styleComboBox
actionIndicatorVisible: false
currentIndex: 0
textRole: "text"
valueRole: "value"
font.pixelSize: DialogValues.defaultPixelSize
width: parent.width
anchors.top: styleTitleText.bottom
anchors.topMargin: 5
model: ListModel {
ListElement { text: qsTr("All"); value: "all" }
@@ -93,80 +99,79 @@ Item {
ListElement { text: qsTr("Dark"); value: "dark" }
}
implicitWidth: parent.width
onActivated: (index) => {
styleModel.filter(currentValue.toLowerCase());
styleTitleText.refresh();
BackendApi.styleModel.filter(currentValue.toLowerCase())
styleTitleText.refresh()
}
} // 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 {
id: stylesList
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: styleModel
MouseArea {
id: listViewMouseArea
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
}
focus: true
clip: true
model: BackendApi.styleModel
boundsBehavior: Flickable.StopAtBounds
highlightFollowsCurrentItem: false
ScrollBar.vertical: SC.VerticalScrollBar {
id: styleScrollBar
property int extraPadding: 0
bottomInset: extraPadding
bottomPadding: bottomInset + 16
viewMouseArea: listViewMouseArea
} // ScrollBar
bottomMargin: -DialogValues.styleListItemBottomMargin
onCurrentIndexChanged: {
if (styleModel.rowCount() > 0)
dialogBox.styleIndex = stylesList.currentIndex;
if (BackendApi.styleModel.rowCount() > 0)
BackendApi.styleIndex = stylesList.currentIndex
}
delegate: ItemDelegate {
id: delegateId
height: styleImage.height + DialogValues.styleImageBorderWidth + styleText.height + extraPadding.height + 1
width: stylesList.width - styleScrollBar.width
width: stylesList.width
height: DialogValues.styleListItemHeight
Component.onCompleted: {
styleScrollBar.extraPadding = styleText.height + extraPadding.height
}
onClicked: stylesList.currentIndex = index
Rectangle {
background: Rectangle {
anchors.fill: parent
color: DialogValues.lightPaneColor
}
Column {
spacing: 0
contentItem: Item {
anchors.fill: parent
Column {
anchors.fill: parent
spacing: DialogValues.styleListItemSpacing
Rectangle {
border.color: index == stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent"
border.width: index == stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0
width: DialogValues.styleImageWidth
+ 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"
width: parent.width
height: parent.height - styleText.height - extraPadding.height
Image {
id: styleImage
asynchronous: false
source: "image://newprojectdialog_library/" + styleModel.iconId(model.index)
width: 200
height: 262
x: DialogValues.styleImageBorderWidth
y: DialogValues.styleImageBorderWidth
width: DialogValues.styleImageWidth
height: DialogValues.styleImageHeight
asynchronous: false
source: "image://newprojectdialog_library/" + BackendApi.styleModel.iconId(model.index)
}
} // Rectangle
@@ -175,35 +180,26 @@ Item {
text: model.display
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
height: 18
height: DialogValues.styleTextHeight
lineHeightMode: Text.FixedHeight
horizontalAlignment: Text.AlignHCenter
width: parent.width
color: DialogValues.textColor
}
Item { id: extraPadding; width: 1; height: 10 }
} // Column
} // Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
stylesList.currentIndex = index
}
}
}
Connections {
target: styleModel
target: BackendApi.styleModel
function onModelReset() {
stylesList.currentIndex = dialogBox.styleIndex;
stylesList.currentIndexChanged();
styleTitleText.refresh();
stylesList.currentIndex = BackendApi.styleIndex
stylesList.currentIndexChanged()
styleTitleText.refresh()
}
}
} // ListView
} // ColumnLayout
} // ScrollView
} // Parent Item
} // 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
Styles 1.0 Styles.qml
singleton DialogValues 1.0 DialogValues.qml
NewProjectView 1.0 NewProjectView.qml
Styles 1.0 Styles.qml

View File

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

View File

@@ -54,6 +54,8 @@ Item {
property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead
property bool addTopPadding: true
property bool addBottomPadding: true
property bool dropEnabled: false
property bool highlight: false
property bool useDefaulContextMenu: true
@@ -72,9 +74,22 @@ Item {
function onExpandAll() { section.expanded = true }
}
signal drop(var drag)
signal dropEnter(var drag)
signal dropExit()
signal showContextMenu()
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 {
id: header
@@ -82,7 +97,9 @@ Item {
visible: !section.hideHeader
anchors.left: parent.left
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 {
StudioControls.Menu {

View File

@@ -35,37 +35,26 @@ ScrollBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
property bool scrollBarVisible: parent.childrenRect.height > parent.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
property bool scrollBarVisible: parent.contentHeight > scrollBar.height
minimumSize: scrollBar.width / scrollBar.height
orientation: Qt.Vertical
policy: computePolicy()
x: parent.width - width
y: 0
policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
height: parent.availableHeight
- (parent.bothVisible ? parent.horizontalThickness : 0)
padding: 0
padding: scrollBar.active ? StudioTheme.Values.scrollBarActivePadding
: StudioTheme.Values.scrollBarInactivePadding
background: Rectangle {
implicitWidth: StudioTheme.Values.scrollBarThickness
implicitHeight: StudioTheme.Values.scrollBarThickness
color: StudioTheme.Values.themeScrollBarTrack
}
contentItem: Rectangle {
implicitWidth: StudioTheme.Values.scrollBarThickness
implicitWidth: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
implicitHeight: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
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 scrollBarThickness: 10
property real scrollBarActivePadding: 1
property real scrollBarInactivePadding: 2
property real toolTipHeight: 25
property int toolTipDelay: 1000

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