Merge remote-tracking branch 'origin/7.0'
Conflicts: cmake/QtCreatorIDEBranding.cmake qbs/modules/qtc/qtc.qbs qtcreator_ide_branding.pri Change-Id: Ic02df53b880d0861d9d9ea0df3e0d381ae99f350
83
.github/workflows/build_cmake.yml
vendored
@@ -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
|
||||
|
8
dist/installer/mac/disclaim.entitlements
vendored
Normal 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>
|
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
BIN
doc/qtcreator/images/qtcreator-new-project.png
Normal file
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
BIN
doc/qtcreator/images/qtcreator-new-qt-quick-project.png
Normal file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -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.
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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]
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
BIN
doc/qtdesignstudio/images/icons/align-camera-on.png
Normal file
After Width: | Height: | Size: 309 B |
BIN
doc/qtdesignstudio/images/icons/align-view-on.png
Normal file
After Width: | Height: | Size: 321 B |
BIN
doc/qtdesignstudio/images/icons/particle-animation-off.png
Normal file
After Width: | Height: | Size: 549 B |
BIN
doc/qtdesignstudio/images/icons/particle-animation-on.png
Normal file
After Width: | Height: | Size: 316 B |
BIN
doc/qtdesignstudio/images/icons/particle-pause.png
Normal file
After Width: | Height: | Size: 120 B |
BIN
doc/qtdesignstudio/images/icons/particle-play.png
Normal file
After Width: | Height: | Size: 159 B |
BIN
doc/qtdesignstudio/images/icons/particle-restart.png
Normal file
After Width: | Height: | Size: 302 B |
BIN
doc/qtdesignstudio/images/icons/particles-seek.png
Normal file
After Width: | Height: | Size: 311 B |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 40 KiB |
@@ -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
|
||||
|
||||
|
||||
*/
|
||||
|
@@ -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])
|
||||
|
@@ -50,6 +50,7 @@ public:
|
||||
ShowSelectionBox,
|
||||
ShowIconGizmo,
|
||||
ShowCameraFrustum,
|
||||
ShowParticleEmitter,
|
||||
Edit3DParticleModeToggle,
|
||||
ParticlesPlay,
|
||||
ParticlesRestart,
|
||||
|
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -49,7 +49,7 @@ protected:
|
||||
|
||||
private:
|
||||
ServerNodeInstance m_currentState;
|
||||
QSize m_previewSize{160, 160};
|
||||
QSize m_previewSize{320, 320};
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -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
|
||||
} // Item
|
||||
|
||||
SC.AbstractButton {
|
||||
id: savePresetButton
|
||||
width: StudioTheme.Values.singleControlColumnWidth
|
||||
buttonIcon: qsTr("Save Custom Preset")
|
||||
iconFont: StudioTheme.Constants.font
|
||||
iconSize: DialogValues.defaultPixelSize
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
onClicked: savePresetDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
PopupDialog {
|
||||
id: savePresetDialog
|
||||
title: qsTr("Save Preset")
|
||||
standardButtons: Dialog.Save | Dialog.Cancel
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
anchors.centerIn: parent
|
||||
width: DialogValues.popupDialogWidth
|
||||
|
||||
onAccepted: BackendApi.savePresetDialogAccept()
|
||||
|
||||
onOpened: {
|
||||
presetNameTextField.selectAll()
|
||||
presetNameTextField.forceActiveFocus()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: qsTr("Preset name")
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
color: DialogValues.textColor
|
||||
}
|
||||
|
||||
SC.TextField {
|
||||
id: presetNameTextField
|
||||
actionIndicatorVisible: false
|
||||
translationIndicatorVisible: false
|
||||
text: qsTr("MyPreset")
|
||||
color: DialogValues.textColor
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
Layout.fillWidth: true
|
||||
maximumLength: 30
|
||||
validator: RegularExpressionValidator { regularExpression: /\w[\w ]*/ }
|
||||
|
||||
onEditingFinished: {
|
||||
presetNameTextField.text = text.trim()
|
||||
presetNameTextField.text = text.replace(/\s+/g, " ")
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: BackendApi
|
||||
property: "presetName"
|
||||
value: presetNameTextField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Item
|
||||
} // Rectangle
|
||||
} // root Item
|
||||
|
@@ -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
|
||||
@@ -91,6 +111,6 @@ QtObject {
|
||||
item and spacing after it). So we have to subtract 2 x layout spacing before setting
|
||||
our own, narrower, spacing.
|
||||
*/
|
||||
return -layoutSpacing -layoutSpacing + value
|
||||
return -layoutSpacing - layoutSpacing + value
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
} // GridView
|
||||
|
||||
PopupDialog {
|
||||
id: removePresetDialog
|
||||
|
||||
property string presetName
|
||||
|
||||
title: qsTr("Delete Custom Preset")
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
anchors.centerIn: parent
|
||||
width: DialogValues.popupDialogWidth
|
||||
|
||||
onAccepted: BackendApi.removeCurrentPreset()
|
||||
|
||||
Text {
|
||||
text: qsTr("Are you sure you want to delete \"" + removePresetDialog.presetName + "\" ?")
|
||||
color: DialogValues.textColor
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
width: DialogValues.popupDialogWidth - 2 * DialogValues.popupDialogPadding
|
||||
}
|
||||
|
||||
} // Dialog
|
||||
} // GridView
|
||||
} // ScrollView
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|