Merge "Merge remote-tracking branch 'origin/qds/dev'"
@@ -22,6 +22,9 @@ function(setup_dependencies_component)
|
|||||||
set(_elfutils_arg "--elfutils \"${_elfutils_path}\"")
|
set(_elfutils_arg "--elfutils \"${_elfutils_path}\"")
|
||||||
endif()
|
endif()
|
||||||
install(CODE "
|
install(CODE "
|
||||||
|
if (CMAKE_VERSION GREATER_EQUAL 3.19)
|
||||||
|
set(QTC_COMMAND_ERROR_IS_FATAL COMMAND_ERROR_IS_FATAL ANY)
|
||||||
|
endif()
|
||||||
# DESTDIR is set for e.g. the cpack DEB generator, but is empty in other situations
|
# DESTDIR is set for e.g. the cpack DEB generator, but is empty in other situations
|
||||||
if(DEFINED ENV{DESTDIR})
|
if(DEFINED ENV{DESTDIR})
|
||||||
set(DESTDIR_WITH_SEP \"\$ENV{DESTDIR}/\")
|
set(DESTDIR_WITH_SEP \"\$ENV{DESTDIR}/\")
|
||||||
@@ -48,7 +51,7 @@ function(setup_dependencies_component)
|
|||||||
\"\${_ide_app_target}\"
|
\"\${_ide_app_target}\"
|
||||||
\"${_qmake_binary}\"
|
\"${_qmake_binary}\"
|
||||||
COMMAND_ECHO STDOUT
|
COMMAND_ECHO STDOUT
|
||||||
\${QTC_COMMAND_ERROR_IS_FATAL}
|
${QTC_COMMAND_ERROR_IS_FATAL}
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
COMPONENT Dependencies
|
COMPONENT Dependencies
|
||||||
|
|||||||
@@ -23,16 +23,23 @@
|
|||||||
necessary files to a location in a device where you want to run the
|
necessary files to a location in a device where you want to run the
|
||||||
executable at.
|
executable at.
|
||||||
|
|
||||||
|
\note To preview on a wirelessly connected device, select \preferences > \uicontrol Devices
|
||||||
|
and connect the device.
|
||||||
|
|
||||||
To preview a UI on a device:
|
To preview a UI on a device:
|
||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
|
\if defined(qtcreator)
|
||||||
\li In \uicontrol Projects > \uicontrol {Build & Run}, enable the kit
|
\li In \uicontrol Projects > \uicontrol {Build & Run}, enable the kit
|
||||||
predefined for the device type (1).
|
predefined for the device type (1).
|
||||||
\li Select the kit for the device in the kit selector (2).
|
\li Select the kit for the device in the kit selector (2).
|
||||||
\if defined(qtcreator)
|
|
||||||
\image qtcreator-live-preview-kit.png
|
\image qtcreator-live-preview-kit.png
|
||||||
\else
|
\else
|
||||||
\image studio-kit-selector.png "Kit selector"
|
\li In the bottom toolbar, select \inlineimage icons/settings.png
|
||||||
|
and enable the kit predefined for the device type.
|
||||||
|
\image design-studio-kit-selection.webp.
|
||||||
|
\li Select the kit for the device in the bottom toolbar.
|
||||||
|
\image design-studio-select-kit.webp
|
||||||
\endif
|
\endif
|
||||||
\li Select \uicontrol Build > \uicontrol {QML Preview} or
|
\li Select \uicontrol Build > \uicontrol {QML Preview} or
|
||||||
press \key {Alt+P}.
|
press \key {Alt+P}.
|
||||||
|
|||||||
BIN
doc/qtdesignstudio/images/design-studio-kit-selection.webp
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
doc/qtdesignstudio/images/design-studio-select-kit.webp
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 348 B After Width: | Height: | Size: 1.9 KiB |
BIN
doc/qtdesignstudio/images/icons/bakelights.png
Normal file
|
After Width: | Height: | Size: 502 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
doc/qtdesignstudio/images/icons/split-view.png
Normal file
|
After Width: | Height: | Size: 326 B |
BIN
doc/qtdesignstudio/images/studio-3d-split-view.webp
Normal file
|
After Width: | Height: | Size: 33 KiB |
@@ -9,12 +9,12 @@
|
|||||||
\title Creating UIs for MCUs
|
\title Creating UIs for MCUs
|
||||||
|
|
||||||
As a technical artist or a designer, you can use specialized UI design tools,
|
As a technical artist or a designer, you can use specialized UI design tools,
|
||||||
such as Adobe Photoshop, Sketch, Figma, Blender, or Maya, to create the
|
such as Adobe Photoshop, Sketch, or Figma, to create the original UI design
|
||||||
original UI design files for your MCU application. After the initial design
|
files for your MCU application. After the initial design work, export your
|
||||||
work, export your design from the design tools, and import your 2D and 3D UI
|
design from the design tools, and import your 2D UI design assets into \QDS,
|
||||||
design assets into \QDS, which can convert them into code for developers.
|
which can convert them into code for developers. For more information on
|
||||||
For more information on managing the original assets created with
|
managing the original assets created with specialized UI design tools, see
|
||||||
specialized UI design tools, see \l {Asset Creation with Other Tools}.
|
\l {Asset Creation with Other Tools}.
|
||||||
|
|
||||||
Once your UI design assets are in \QDS, use it to \l {Wireframing} {wireframe}
|
Once your UI design assets are in \QDS, use it to \l {Wireframing} {wireframe}
|
||||||
your MCU application, to visualize its structure. To modify the look and feel
|
your MCU application, to visualize its structure. To modify the look and feel
|
||||||
|
|||||||
@@ -104,6 +104,16 @@
|
|||||||
selected, the camera is pointed at the world origin. This does not affect
|
selected, the camera is pointed at the world origin. This does not affect
|
||||||
the camera zoom level.
|
the camera zoom level.
|
||||||
|
|
||||||
|
To view the scene in a split view of four different point of views, select
|
||||||
|
\inlineimage icons/split-view.png.
|
||||||
|
|
||||||
|
\image studio-3d-split-view.webp "Split view in the 3D view"
|
||||||
|
|
||||||
|
To select one of the four panes, click on it. The selected pane is marked with
|
||||||
|
a blue frame. Use the world axis helper to change the point of view for each pane
|
||||||
|
independently. Navigate each split by panning, rotating, and zooming, as
|
||||||
|
described above.
|
||||||
|
|
||||||
\image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view"
|
\image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view"
|
||||||
|
|
||||||
You can use scene cameras (2) to view the \uicontrol View3D component from a
|
You can use scene cameras (2) to view the \uicontrol View3D component from a
|
||||||
@@ -143,6 +153,13 @@
|
|||||||
For more information about the available scene light types and their
|
For more information about the available scene light types and their
|
||||||
properties, see \l{Lights}.
|
properties, see \l{Lights}.
|
||||||
|
|
||||||
|
\section1 Baking Lights
|
||||||
|
|
||||||
|
Bake lights to light static elements in your scene. To bake lights,
|
||||||
|
select \inlineimage icons/bakelights.png to open the
|
||||||
|
\uicontrol {Lights Baking Setup} dialog. For more information, see
|
||||||
|
\l {Baking Lightmaps}.
|
||||||
|
|
||||||
\section1 Selecting Components
|
\section1 Selecting Components
|
||||||
|
|
||||||
To move, rotate, or scale components in the scene, you need to select them
|
To move, rotate, or scale components in the scene, you need to select them
|
||||||
@@ -358,6 +375,54 @@
|
|||||||
\inlineimage icons/particles-seek.png
|
\inlineimage icons/particles-seek.png
|
||||||
to manually seek forward or backward in the particle animation.
|
to manually seek forward or backward in the particle animation.
|
||||||
|
|
||||||
|
\section1 Using Viewport Shading
|
||||||
|
|
||||||
|
Use \uicontrol {Viewport Shading} to change the rendering of the materials to only
|
||||||
|
reflect a particular aspect of the overall rendering process. Use shading also as a
|
||||||
|
debugging tool to understand why a material looks the way it does. In split view,
|
||||||
|
view the scene using different shading properties in each split.
|
||||||
|
|
||||||
|
To use the \uicontrol {Viewport Shading}, right-click the \uicontrol 3D view to open the
|
||||||
|
context menu, select \uicontrol {Viewport Shading} and then select \uicontrol Wireframe,
|
||||||
|
one of the material properties, or \uicontrol{Reset All Viewports}.
|
||||||
|
|
||||||
|
Select \uicontrol Wireframe to only show the edges of the objects in the scene.
|
||||||
|
|
||||||
|
Select one of the material properties available for shading:
|
||||||
|
\table
|
||||||
|
\header
|
||||||
|
\li Property
|
||||||
|
\li Description
|
||||||
|
\row
|
||||||
|
\li Base Color
|
||||||
|
\li Shows only the base color of a material passed through without any lighting.
|
||||||
|
\row
|
||||||
|
\li Roughness
|
||||||
|
\li Shows only the roughness of a material passed through as an unlit greyscale
|
||||||
|
value.
|
||||||
|
\row
|
||||||
|
\li Metalness
|
||||||
|
\li Shows only the metalness of a material passed through as an unlit greyscale
|
||||||
|
value.
|
||||||
|
\row
|
||||||
|
\li Normals
|
||||||
|
\li Shows only the interpolated world space normal value of the material mapped
|
||||||
|
to an RGB color.
|
||||||
|
\row
|
||||||
|
\li Ambient Occlusion
|
||||||
|
\li Shows only the ambient occlusion of the material.
|
||||||
|
\row
|
||||||
|
\li Diffuse
|
||||||
|
\li Shows only the diffuse contribution of the material after all lighting.
|
||||||
|
\row
|
||||||
|
\li Specular
|
||||||
|
\li Shows only the specular contribution of the material after all
|
||||||
|
lighting.
|
||||||
|
\endtable
|
||||||
|
|
||||||
|
Select \uicontrol{Reset All Viewports} to reset the shading of the scene in all of the
|
||||||
|
splits.
|
||||||
|
|
||||||
\section1 Summary of the 3D View Toolbar Buttons
|
\section1 Summary of the 3D View Toolbar Buttons
|
||||||
|
|
||||||
The \uicontrol{3D} view toolbar contains the following buttons:
|
The \uicontrol{3D} view toolbar contains the following buttons:
|
||||||
@@ -441,6 +506,11 @@
|
|||||||
\li Background Color Actions
|
\li Background Color Actions
|
||||||
\li
|
\li
|
||||||
\li \l{Changing Colors}
|
\li \l{Changing Colors}
|
||||||
|
\row
|
||||||
|
\li \inlineimage icons/split-view.png
|
||||||
|
\li Toggle Split View On/Off
|
||||||
|
\li \key Ctrl + \key Alt + \key Q
|
||||||
|
\li \l{Using Split View}
|
||||||
\row
|
\row
|
||||||
\li \inlineimage icons/particles-seek.png
|
\li \inlineimage icons/particles-seek.png
|
||||||
\li Seek Particle System Time
|
\li Seek Particle System Time
|
||||||
|
|||||||
@@ -213,10 +213,11 @@
|
|||||||
|
|
||||||
\note Lightmaps baking is released as technical preview in \QDS 4.1.
|
\note Lightmaps baking is released as technical preview in \QDS 4.1.
|
||||||
|
|
||||||
Baked lightmaps allow pre-generating the direct lighting from lights such as DirectionalLight,
|
Baked lightmaps allow pre-generating the direct lighting from lights,
|
||||||
PointLight, and SpotLight, including the shadows cast by the lights. At run time, instead of
|
such as DirectionalLight, PointLight, and SpotLight, including the shadows
|
||||||
performing the appropriate calculations in the fragment shader, and, in case of shadows,
|
cast by the lights. At run time, instead of performing the appropriate
|
||||||
generating the potentially costly shadow maps in real time, the pre-generated light map is
|
calculations in the fragment shader, and, in case of shadows, generating
|
||||||
|
the potentially costly shadow maps in real time, the pre-generated light map is
|
||||||
sampled instead.
|
sampled instead.
|
||||||
|
|
||||||
\section2 Baking Lightmaps
|
\section2 Baking Lightmaps
|
||||||
@@ -224,40 +225,49 @@
|
|||||||
To bake lightmaps for models in your 3D scene:
|
To bake lightmaps for models in your 3D scene:
|
||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
\li Right-click anywhere in the \uicontrol 3D view and select \uicontrol {Bake Lights}.
|
\li Select \inlineimage icons/bakelights.png in the \uicontrol 3D view
|
||||||
\li In the \uicontrol {Lights Baking Setup} dialog:
|
toolbar or right-click anywhere in the \uicontrol 3D view and select
|
||||||
|
\uicontrol {Bake Lights} to open the \uicontrol {Lights Baking Setup}
|
||||||
|
dialog to define settings for baking lights.
|
||||||
|
\li Set \uicontrol {Bake Mode} for each light source:
|
||||||
\list
|
\list
|
||||||
\li For every light you want to use to bake lightmaps, set \uicontrol {Bake Mode} to BakeModeAll.
|
\li To bake both direct (diffuse and shadow) and indirect lighting,
|
||||||
\li For every 3D model you want to bake lightmaps, select \uicontrol {In Use} and
|
select \uicontrol {Bake All}.
|
||||||
\uicontrol {Enabled}, and set the desired \uicontrol {Resolution}.
|
\li To bake only indirect lighting, select \uicontrol {Bake Indirect}.
|
||||||
|
\li To not include a light source in the baking of lights, select
|
||||||
|
\uicontrol {Baking Disabled}.
|
||||||
\endlist
|
\endlist
|
||||||
\li Optional. If you have components with unexposed models or lights (for example, imported
|
\li For every 3D model you want to bake into a lightmap, select
|
||||||
3D models created in other software), select \uicontrol {Expose models and lights} to add the
|
\uicontrol {In Use} and \uicontrol {Enabled}, and set the desired
|
||||||
models and light of that component to the \uicontrol Models and \uicontrol Lights sections of
|
\uicontrol {Resolution}.
|
||||||
the dialog.
|
\li Optional. If you have components with unexposed models or lights
|
||||||
|
(for example, imported 3D models created in other software), select
|
||||||
|
\uicontrol {Expose models and lights} to add the models and light of
|
||||||
|
that component to the \uicontrol Models and \uicontrol Lights sections
|
||||||
|
of the dialog.
|
||||||
\li Select \uicontrol Bake.
|
\li Select \uicontrol Bake.
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
\image bake-lights-dialog.png
|
\image bake-lights-dialog.png
|
||||||
|
|
||||||
\section2 Manually Baking Lightmaps for a 3D Model
|
\section2 Manually Baking Lightmaps for a 3D Model
|
||||||
Baked lightmap components are not visible in the \uicontrol Navigator view by default. To make
|
\note Baked lightmap components are not visible in the \uicontrol Navigator view by
|
||||||
them visible, select \inlineimage icons/visibilityon.png
|
default. To make them visible, select \inlineimage icons/visibilityon.png
|
||||||
in the \uicontrol Navigator view.
|
in the \uicontrol Navigator view.
|
||||||
|
|
||||||
To bake lightmaps for a 3D model:
|
To bake lightmaps for a 3D model:
|
||||||
\list 1
|
\list 1
|
||||||
\li From \uicontrol Components, drag a \uicontrol {Baked Lightmap} component to
|
\li From \uicontrol Components, drag a \uicontrol {Baked Lightmap} component
|
||||||
the 3D model in the \uicontrol Navigator view.
|
to the 3D model in the \uicontrol Navigator view.
|
||||||
\image baked-lightmaps-navigator.png
|
\image baked-lightmaps-navigator.png
|
||||||
\li In the \uicontrol Navigator view, select \e bakedLightmap and in the \uicontrol Properties
|
\li In the \uicontrol Navigator view, select \e bakedLightmap, and in the
|
||||||
view:
|
\uicontrol Properties view:
|
||||||
\list
|
\list
|
||||||
\li Select \uicontrol Enabled.
|
\li Select \uicontrol Enabled.
|
||||||
\li In \uicontrol Key, set the filename base for the generated light maps. This must be
|
\li In \uicontrol Key, set the filename base for the generated light maps.
|
||||||
a unique name.
|
This must be a unique name.
|
||||||
\li In \uicontrol {Load Prefix}, set the relative path to the folder where the generated
|
\li In \uicontrol {Load Prefix}, set the relative path to the folder where
|
||||||
light map files are saved.
|
the generated light map files are saved.
|
||||||
\endlist
|
\endlist
|
||||||
\li In the \uicontrol Navigator view, select the 3D model and in the \uicontrol Properties
|
\li In the \uicontrol Navigator view, select the 3D model and in the \uicontrol Properties
|
||||||
view, select \uicontrol {Used in Baked Lighting}.
|
view, select \uicontrol {Used in Baked Lighting}.
|
||||||
|
|||||||
@@ -147,20 +147,20 @@ def copy_qt_libs(target_qt_prefix_path, qt_bin_dir, qt_libs_dir):
|
|||||||
shutil.copy(library, lib_dest)
|
shutil.copy(library, lib_dest)
|
||||||
|
|
||||||
|
|
||||||
def deploy_qtdiag(qtc_binary_path, qt_install):
|
def deploy_binary(binary_name, qtc_binary_path, qt_install):
|
||||||
print("Copying qtdiag")
|
print(f"Copying {binary_name}")
|
||||||
qtdiag_src = os.path.join(qt_install.bin, with_exe_ext('qtdiag'))
|
binary_src = os.path.join(qt_install.bin, with_exe_ext(binary_name))
|
||||||
destdir = (qtc_binary_path if common.is_windows_platform()
|
destdir = (qtc_binary_path if common.is_windows_platform()
|
||||||
else os.path.join(qtc_binary_path, 'Contents', 'MacOS') if common.is_mac_platform()
|
else os.path.join(qtc_binary_path, 'Contents', 'MacOS') if common.is_mac_platform()
|
||||||
else os.path.join(qtc_binary_path, '..', 'lib', 'Qt', 'bin'))
|
else os.path.join(qtc_binary_path, '..', 'lib', 'Qt', 'bin'))
|
||||||
if not os.path.exists(destdir):
|
if not os.path.exists(destdir):
|
||||||
os.makedirs(destdir)
|
os.makedirs(destdir)
|
||||||
shutil.copy(qtdiag_src, destdir)
|
shutil.copy(binary_src, destdir)
|
||||||
if common.is_mac_platform():
|
if common.is_mac_platform():
|
||||||
# fix RPATHs
|
# fix RPATHs
|
||||||
qtdiag_dest = os.path.join(destdir, 'qtdiag')
|
binary_dest = os.path.join(destdir, binary_name)
|
||||||
subprocess.check_call(['xcrun', 'install_name_tool', '-add_rpath', '@loader_path/../Frameworks', qtdiag_dest])
|
subprocess.check_call(['xcrun', 'install_name_tool', '-add_rpath', '@loader_path/../Frameworks', binary_dest])
|
||||||
subprocess.check_call(['xcrun', 'install_name_tool', '-delete_rpath', '@loader_path/../lib', qtdiag_dest])
|
subprocess.check_call(['xcrun', 'install_name_tool', '-delete_rpath', '@loader_path/../lib', binary_dest])
|
||||||
|
|
||||||
|
|
||||||
def deploy_plugins(qtc_binary_path, qt_install):
|
def deploy_plugins(qtc_binary_path, qt_install):
|
||||||
@@ -469,7 +469,8 @@ def main():
|
|||||||
qtcreator_binary_path = (args.qtcreator_binary if common.is_mac_platform()
|
qtcreator_binary_path = (args.qtcreator_binary if common.is_mac_platform()
|
||||||
else os.path.dirname(args.qtcreator_binary))
|
else os.path.dirname(args.qtcreator_binary))
|
||||||
|
|
||||||
deploy_qtdiag(qtcreator_binary_path, qt_install)
|
deploy_binary('qtdiag', qtcreator_binary_path, qt_install)
|
||||||
|
deploy_binary('qsb', qtcreator_binary_path, qt_install)
|
||||||
deploy_plugins(qtcreator_binary_path, qt_install)
|
deploy_plugins(qtcreator_binary_path, qt_install)
|
||||||
deploy_imports(qtcreator_binary_path, qt_install)
|
deploy_imports(qtcreator_binary_path, qt_install)
|
||||||
deploy_translations(qtcreator_binary_path, qt_install)
|
deploy_translations(qtcreator_binary_path, qt_install)
|
||||||
|
|||||||
@@ -99,45 +99,17 @@ Item {
|
|||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
|
||||||
IconButton {
|
IconButton {
|
||||||
icon: StudioTheme.Constants.updateContent_medium
|
icon: StudioTheme.Constants.save_medium
|
||||||
tooltip: qsTr("Update existing file with changes")
|
tooltip: qsTr("Save changes")
|
||||||
enabled: root.model.collectionName !== ""
|
enabled: root.model.collectionName !== ""
|
||||||
onClicked:
|
onClicked: root.model.saveCurrentCollection()
|
||||||
{
|
|
||||||
if (root.backend.selectedSourceAddress().indexOf("json") !== -1)
|
|
||||||
root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "JSON")
|
|
||||||
else
|
|
||||||
root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "CSV")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton {
|
IconButton {
|
||||||
icon: StudioTheme.Constants.export_medium
|
icon: StudioTheme.Constants.export_medium
|
||||||
tooltip: qsTr("Export the model to a new file")
|
tooltip: qsTr("Export model")
|
||||||
enabled: root.model.collectionName !== ""
|
enabled: root.model.collectionName !== ""
|
||||||
onClicked: exportMenu.popup()
|
onClicked: fileDialog.open()
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.Menu {
|
|
||||||
id: exportMenu
|
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
|
||||||
text: qsTr("Export as JSON")
|
|
||||||
onTriggered:
|
|
||||||
{
|
|
||||||
fileDialog.defaultSuffix = "json"
|
|
||||||
fileDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
|
||||||
text: qsTr("Export as CSV")
|
|
||||||
onTriggered:
|
|
||||||
{
|
|
||||||
fileDialog.defaultSuffix = "csv"
|
|
||||||
fileDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,17 +117,18 @@ Item {
|
|||||||
|
|
||||||
PlatformWidgets.FileDialog {
|
PlatformWidgets.FileDialog {
|
||||||
id: fileDialog
|
id: fileDialog
|
||||||
|
|
||||||
fileMode: PlatformWidgets.FileDialog.SaveFile
|
fileMode: PlatformWidgets.FileDialog.SaveFile
|
||||||
onAccepted:
|
|
||||||
{
|
|
||||||
var fileAddress = file.toString()
|
|
||||||
|
|
||||||
if (fileAddress.indexOf("json") !== -1)
|
nameFilters: ["JSON Files (*.json)",
|
||||||
root.model.exportCollection(fileAddress, root.model.collectionName, "JSON")
|
"Comma-Separated Values (*.csv)"
|
||||||
else if (fileAddress.indexOf("csv") !== -1)
|
]
|
||||||
root.model.exportCollection(fileAddress, root.model.collectionName, "CSV")
|
|
||||||
|
|
||||||
fileDialog.reject()
|
selectedNameFilter.index: 0
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
let filePath = fileDialog.file.toString()
|
||||||
|
root.model.exportCollection(filePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import CollectionDetails 1.0 as CollectionDetails
|
||||||
import HelperWidgets 2.0 as HelperWidgets
|
import HelperWidgets 2.0 as HelperWidgets
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme 1.0 as StudioTheme
|
||||||
import StudioControls 1.0 as StudioControls
|
import StudioControls 1.0 as StudioControls
|
||||||
@@ -109,15 +110,9 @@ Rectangle {
|
|||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
delegate: HeaderDelegate {
|
delegate: HeaderDelegate {
|
||||||
id: horizontalHeaderItem
|
|
||||||
|
|
||||||
selectedItem: tableView.model.selectedColumn
|
selectedItem: tableView.model.selectedColumn
|
||||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||||
|
|
||||||
function getGlobalBottomLeft() {
|
|
||||||
return mapToGlobal(0, horizontalHeaderItem.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 5
|
anchors.margins: 5
|
||||||
@@ -125,47 +120,51 @@ Rectangle {
|
|||||||
onClicked: (mouse) => {
|
onClicked: (mouse) => {
|
||||||
tableView.model.selectColumn(index)
|
tableView.model.selectColumn(index)
|
||||||
|
|
||||||
if (mouse.button === Qt.RightButton)
|
if (mouse.button === Qt.RightButton) {
|
||||||
headerMenu.popIndex(index, horizontalHeaderItem.getGlobalBottomLeft())
|
let posX = index === root.model.columnCount() - 1 ? parent.width - editProperyDialog.width : 0
|
||||||
|
|
||||||
|
headerMenu.clickedHeaderIndex = index
|
||||||
|
headerMenu.dialogPos = parent.mapToGlobal(posX, parent.height)
|
||||||
|
headerMenu.popup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HelperWidgets.ToolTipArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
text: root.model.propertyType(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StudioControls.Menu {
|
StudioControls.Menu {
|
||||||
id: headerMenu
|
id: headerMenu
|
||||||
|
|
||||||
property int clickedHeader: -1
|
property int clickedHeaderIndex: -1
|
||||||
property point initialPosition
|
property point dialogPos
|
||||||
|
|
||||||
function popIndex(clickedIndex, clickedRect)
|
|
||||||
{
|
|
||||||
headerMenu.clickedHeader = clickedIndex
|
|
||||||
headerMenu.initialPosition = clickedRect
|
|
||||||
headerMenu.popup()
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosed: {
|
onClosed: {
|
||||||
headerMenu.clickedHeader = -1
|
headerMenu.clickedHeaderIndex = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Edit")
|
text: qsTr("Edit")
|
||||||
onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader, headerMenu.initialPosition)
|
onTriggered: editProperyDialog.openDialog(headerMenu.clickedHeaderIndex,
|
||||||
|
headerMenu.dialogPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader)
|
onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeaderIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Sort Ascending")
|
text: qsTr("Sort Ascending")
|
||||||
onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.AscendingOrder)
|
onTriggered: sortedModel.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Sort Descending")
|
text: qsTr("Sort Descending")
|
||||||
onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.DescendingOrder)
|
onTriggered: sortedModel.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,13 +208,23 @@ Rectangle {
|
|||||||
id: itemCell
|
id: itemCell
|
||||||
implicitWidth: 100
|
implicitWidth: 100
|
||||||
implicitHeight: itemText.height
|
implicitHeight: itemText.height
|
||||||
|
border.color: dataTypeWarning !== CollectionDetails.Warning.None ?
|
||||||
|
StudioTheme.Values.themeWarning : StudioTheme.Values.themeControlBackgroundInteraction
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
|
HelperWidgets.ToolTipArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
text: root.model.warningToString(dataTypeWarning)
|
||||||
|
enabled: dataTypeWarning !== CollectionDetails.Warning.None && text !== ""
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: itemText
|
id: itemText
|
||||||
|
|
||||||
text: display ? display : ""
|
text: display
|
||||||
|
color: StudioTheme.Values.themePlaceholderTextColorInteraction
|
||||||
width: parent.width
|
width: parent.width
|
||||||
leftPadding: 5
|
leftPadding: 5
|
||||||
topPadding: 3
|
topPadding: 3
|
||||||
@@ -241,7 +250,6 @@ Rectangle {
|
|||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: itemCell
|
target: itemCell
|
||||||
color: StudioTheme.Values.themeControlBackground
|
color: StudioTheme.Values.themeControlBackground
|
||||||
border.color: StudioTheme.Values.themeControlBackgroundInteraction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
@@ -306,14 +314,11 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.fill: parent
|
anchors.centerIn: parent
|
||||||
text: qsTr("Select a model to continue")
|
text: qsTr("Select a model to continue")
|
||||||
visible: !topRow.visible
|
visible: !topRow.visible
|
||||||
textFormat: Text.RichText
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
font.pixelSize: StudioTheme.Values.mediumFontSize
|
font.pixelSize: StudioTheme.Values.mediumFontSize
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
implicitWidth: 300
|
implicitWidth: 300
|
||||||
implicitHeight: innerRect.height + 3
|
implicitHeight: boundingRect.height + 3
|
||||||
|
|
||||||
property color textColor
|
property color textColor
|
||||||
property string sourceType
|
property string sourceType
|
||||||
|
property bool hasSelectedTarget
|
||||||
|
|
||||||
signal selectItem(int itemIndex)
|
signal selectItem(int itemIndex)
|
||||||
signal deleteItem()
|
signal deleteItem()
|
||||||
@@ -23,9 +24,8 @@ Item {
|
|||||||
Item {
|
Item {
|
||||||
id: boundingRect
|
id: boundingRect
|
||||||
|
|
||||||
anchors.centerIn: root
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: nameHolder.height
|
height: itemLayout.height
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -49,39 +49,24 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
id: itemLayout
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
Text {
|
|
||||||
id: moveTool
|
|
||||||
|
|
||||||
property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
|
|
||||||
|
|
||||||
Layout.preferredWidth: moveTool.style.squareControlSize.width
|
|
||||||
Layout.preferredHeight: nameHolder.height
|
|
||||||
Layout.leftMargin: 12
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: StudioTheme.Constants.dragmarks
|
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
|
||||||
font.pixelSize: moveTool.style.baseIconFontSize
|
|
||||||
color: root.textColor
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: nameHolder
|
id: nameHolder
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: StudioTheme.Values.collectionItemTextSideMargin
|
||||||
|
Layout.topMargin: StudioTheme.Values.collectionItemTextMargin
|
||||||
|
Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin
|
||||||
|
|
||||||
text: collectionName
|
text: collectionName
|
||||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
color: root.textColor
|
color: root.textColor
|
||||||
leftPadding: 5
|
topPadding: StudioTheme.Values.collectionItemTextPadding
|
||||||
topPadding: 8
|
bottomPadding: StudioTheme.Values.collectionItemTextPadding
|
||||||
rightPadding: 8
|
|
||||||
bottomPadding: 8
|
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
@@ -90,13 +75,16 @@ Item {
|
|||||||
id: threeDots
|
id: threeDots
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
Layout.topMargin: StudioTheme.Values.collectionItemTextMargin
|
||||||
|
Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin
|
||||||
|
Layout.rightMargin: StudioTheme.Values.collectionItemTextSideMargin
|
||||||
|
|
||||||
text: StudioTheme.Constants.more_medium
|
text: StudioTheme.Constants.more_medium
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
||||||
color: root.textColor
|
color: root.textColor
|
||||||
rightPadding: 12
|
padding: StudioTheme.Values.collectionItemTextPadding
|
||||||
topPadding: nameHolder.topPadding
|
|
||||||
bottomPadding: nameHolder.bottomPadding
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
@@ -125,6 +113,12 @@ Item {
|
|||||||
shortcut: StandardKey.Replace
|
shortcut: StandardKey.Replace
|
||||||
onTriggered: renameDialog.open()
|
onTriggered: renameDialog.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
text: qsTr("Assign to the selected node")
|
||||||
|
enabled: root.hasSelectedTarget
|
||||||
|
onTriggered: rootView.assignCollectionToSelectedNode(collectionName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component Spacer: Item {
|
component Spacer: Item {
|
||||||
@@ -137,6 +131,7 @@ Item {
|
|||||||
|
|
||||||
title: qsTr("Deleting the model")
|
title: qsTr("Deleting the model")
|
||||||
clip: true
|
clip: true
|
||||||
|
implicitWidth: 300
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
|||||||
@@ -24,15 +24,8 @@ Item {
|
|||||||
warningDialog.open()
|
warningDialog.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonImport {
|
ImportDialog {
|
||||||
id: jsonImporter
|
id: importDialog
|
||||||
|
|
||||||
backendValue: root.rootView
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
CsvImport {
|
|
||||||
id: csvImporter
|
|
||||||
|
|
||||||
backendValue: root.rootView
|
backendValue: root.rootView
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -62,46 +55,37 @@ Item {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: collectionsSideBar
|
id: collectionsSideBar
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
Layout.minimumWidth: 300
|
Layout.minimumWidth: 300
|
||||||
Layout.fillWidth: !grid.isHorizontal
|
Layout.fillWidth: !grid.isHorizontal
|
||||||
|
|
||||||
RowLayout {
|
Rectangle {
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
color: StudioTheme.Values.themeToolbarBackground
|
||||||
|
Layout.preferredHeight: StudioTheme.Values.toolbarHeight
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 50
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
Layout.fillWidth: true
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin
|
||||||
|
|
||||||
text: qsTr("Data Models")
|
text: qsTr("Data Models")
|
||||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
leftPadding: 15
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IconTextButton {
|
HelperWidgets.AbstractButton {
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin
|
||||||
|
|
||||||
icon: StudioTheme.Constants.import_medium
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
text: qsTr("JSON")
|
buttonIcon: StudioTheme.Constants.import_medium
|
||||||
tooltip: qsTr("Import JSON")
|
tooltip: qsTr("Import a model")
|
||||||
radius: StudioTheme.Values.smallRadius
|
|
||||||
|
|
||||||
onClicked: jsonImporter.open()
|
onClicked: importDialog.open()
|
||||||
}
|
|
||||||
|
|
||||||
IconTextButton {
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
icon: StudioTheme.Constants.import_medium
|
|
||||||
text: qsTr("CSV")
|
|
||||||
tooltip: qsTr("Import CSV")
|
|
||||||
radius: StudioTheme.Values.smallRadius
|
|
||||||
|
|
||||||
onClicked: csvImporter.open()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,9 +112,7 @@ Item {
|
|||||||
|
|
||||||
delegate: ModelSourceItem {
|
delegate: ModelSourceItem {
|
||||||
implicitWidth: sourceListView.width
|
implicitWidth: sourceListView.width
|
||||||
onDeleteItem: root.model.removeRow(index)
|
|
||||||
hasSelectedTarget: root.rootView.targetNodeSelected
|
hasSelectedTarget: root.rootView.targetNodeSelected
|
||||||
onAssignToSelected: root.rootView.assignSourceNodeToSelectedItem(sourceNode)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,26 +12,22 @@ StudioControls.Dialog {
|
|||||||
|
|
||||||
required property var model
|
required property var model
|
||||||
property int __propertyIndex: -1
|
property int __propertyIndex: -1
|
||||||
property string __oldName
|
property string __currentName
|
||||||
|
|
||||||
title: qsTr("Edit Property")
|
title: qsTr("Edit Column")
|
||||||
clip: true
|
|
||||||
|
|
||||||
function editProperty(index, initialPosition) {
|
function openDialog(index, initialPosition) {
|
||||||
root.__propertyIndex = index
|
root.__propertyIndex = index
|
||||||
|
|
||||||
if (root.__propertyIndex < 0)
|
if (root.__propertyIndex < 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
let previousName = root.model.propertyName(root.__propertyIndex)
|
root.__currentName = root.model.propertyName(root.__propertyIndex)
|
||||||
let previousType = root.model.propertyType(root.__propertyIndex)
|
nameTextField.text = root.__currentName
|
||||||
|
nameTextField.selectAll()
|
||||||
|
nameTextField.forceActiveFocus()
|
||||||
|
|
||||||
root.__oldName = previousName
|
typeComboBox.initialType = root.model.propertyType(root.__propertyIndex)
|
||||||
newNameField.text = previousName
|
|
||||||
|
|
||||||
propertyType.initialType = previousType
|
|
||||||
|
|
||||||
forceChangeType.checked = false
|
|
||||||
|
|
||||||
let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y)
|
let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y)
|
||||||
x = newPoint.x
|
x = newPoint.x
|
||||||
@@ -41,35 +37,31 @@ StudioControls.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (newNameField.text !== "" && newNameField.text !== root.__oldName)
|
if (nameTextField.text !== "" && nameTextField.text !== root.__currentName)
|
||||||
root.model.renameColumn(root.__propertyIndex, newNameField.text)
|
root.model.renameColumn(root.__propertyIndex, nameTextField.text)
|
||||||
|
|
||||||
if (propertyType.changed || forceChangeType.checked)
|
if (typeComboBox.initialType !== typeComboBox.currentText)
|
||||||
root.model.setPropertyType(root.__propertyIndex, propertyType.currentText, forceChangeType.checked)
|
root.model.setPropertyType(root.__propertyIndex, typeComboBox.currentText)
|
||||||
}
|
}
|
||||||
|
|
||||||
onRejected: {
|
contentItem: Column {
|
||||||
let currentDatatype = propertyType.initialType
|
spacing: 5
|
||||||
propertyType.currentIndex = propertyType.find(currentDatatype)
|
|
||||||
}
|
|
||||||
|
|
||||||
component Spacer: Item {
|
Grid {
|
||||||
implicitWidth: 1
|
columns: 2
|
||||||
implicitHeight: StudioTheme.Values.columnGap
|
rows: 2
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
verticalItemAlignment: Grid.AlignVCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("Name")
|
text: qsTr("Name")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
width: 50
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.TextField {
|
StudioControls.TextField {
|
||||||
id: newNameField
|
id: nameTextField
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
actionIndicator.visible: false
|
actionIndicator.visible: false
|
||||||
translationIndicator.visible: false
|
translationIndicator.visible: false
|
||||||
@@ -81,13 +73,7 @@ StudioControls.Dialog {
|
|||||||
validator: RegularExpressionValidator {
|
validator: RegularExpressionValidator {
|
||||||
regularExpression: /^\w+$/
|
regularExpression: /^\w+$/
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
editButton.enabled = newNameField.text !== ""
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Spacer {}
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("Type")
|
text: qsTr("Type")
|
||||||
@@ -95,90 +81,72 @@ StudioControls.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.ComboBox {
|
StudioControls.ComboBox {
|
||||||
id: propertyType
|
id: typeComboBox
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
property string initialType
|
property string initialType
|
||||||
readonly property bool changed: propertyType.initialType !== propertyType.currentText
|
|
||||||
|
|
||||||
model: root.model.typesList()
|
model: root.model.typesList()
|
||||||
actionIndicatorVisible: false
|
actionIndicatorVisible: false
|
||||||
|
|
||||||
onInitialTypeChanged: propertyType.currentIndex = propertyType.find(initialType)
|
onInitialTypeChanged: typeComboBox.currentIndex = typeComboBox.find(initialType)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer {}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
StudioControls.CheckBox {
|
|
||||||
id: forceChangeType
|
|
||||||
actionIndicatorVisible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: qsTr("Force update values")
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer {
|
|
||||||
visible: warningBox.visible
|
|
||||||
implicitHeight: StudioTheme.Values.controlLabelGap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: warningBox
|
id: warningBox
|
||||||
|
|
||||||
Layout.fillWidth: true
|
visible: typeComboBox.initialType !== typeComboBox.currentText
|
||||||
Layout.preferredHeight: warning.height
|
|
||||||
|
|
||||||
visible: propertyType.initialType !== propertyType.currentText
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
clip: true
|
clip: true
|
||||||
border.color: StudioTheme.Values.themeWarning
|
border.color: StudioTheme.Values.themeWarning
|
||||||
|
width: parent.width
|
||||||
|
height: warning.height
|
||||||
|
|
||||||
RowLayout {
|
Row {
|
||||||
id: warning
|
id: warning
|
||||||
|
|
||||||
width: parent.width
|
padding: 5
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
HelperWidgets.IconLabel {
|
HelperWidgets.IconLabel {
|
||||||
icon: StudioTheme.Constants.warning_medium
|
icon: StudioTheme.Constants.warning_medium
|
||||||
Layout.leftMargin: 10
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("Conversion from %1 to %2 may lead to irreversible data loss")
|
text: qsTr("Conversion from %1 to %2 may lead to data loss")
|
||||||
.arg(propertyType.initialType)
|
.arg(typeComboBox.initialType)
|
||||||
.arg(propertyType.currentText)
|
.arg(typeComboBox.currentText)
|
||||||
|
|
||||||
|
width: warningBox.width - 20
|
||||||
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.margins: 8
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer {}
|
Row {
|
||||||
|
height: 40
|
||||||
RowLayout {
|
spacing: 5
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
anchors.right: parent.right
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
id: editButton
|
id: editButton
|
||||||
|
|
||||||
text: qsTr("Edit")
|
text: qsTr("Apply")
|
||||||
|
enabled: nameTextField.text !== ""
|
||||||
|
width: 70
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
onClicked: root.accept()
|
onClicked: root.accept()
|
||||||
}
|
}
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
width: 70
|
||||||
|
|
||||||
onClicked: root.reject()
|
onClicked: root.reject()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import StudioTheme as StudioTheme
|
|||||||
StudioControls.Dialog {
|
StudioControls.Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
title: qsTr("Import A CSV File")
|
title: qsTr("Import a model")
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
closePolicy: Popup.CloseOnEscape
|
closePolicy: Popup.CloseOnEscape
|
||||||
modal: true
|
modal: true
|
||||||
@@ -23,8 +23,8 @@ StudioControls.Dialog {
|
|||||||
property bool fileExists: false
|
property bool fileExists: false
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
collectionName.text = "Collection_"
|
collectionName.text = "Model"
|
||||||
fileName.text = qsTr("New CSV File")
|
fileName.text = qsTr("Model path")
|
||||||
fileName.selectAll()
|
fileName.selectAll()
|
||||||
fileName.forceActiveFocus()
|
fileName.forceActiveFocus()
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,14 @@ StudioControls.Dialog {
|
|||||||
|
|
||||||
PlatformWidgets.FileDialog {
|
PlatformWidgets.FileDialog {
|
||||||
id: fileDialog
|
id: fileDialog
|
||||||
|
nameFilters : ["All Model Files (*.json *.csv)",
|
||||||
|
"JSON Files (*.json)",
|
||||||
|
"Comma-Separated Values (*.csv)"]
|
||||||
|
|
||||||
|
title: qsTr("Select a model file")
|
||||||
|
fileMode: PlatformWidgets.FileDialog.OpenFile
|
||||||
|
acceptLabel: qsTr("Open")
|
||||||
|
|
||||||
onAccepted: fileName.text = fileDialog.file
|
onAccepted: fileName.text = fileDialog.file
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +69,7 @@ StudioControls.Dialog {
|
|||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("File name: ")
|
text: qsTr("File name")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +88,11 @@ StudioControls.Dialog {
|
|||||||
translationIndicator.visible: false
|
translationIndicator.visible: false
|
||||||
validator: fileNameValidator
|
validator: fileNameValidator
|
||||||
|
|
||||||
Keys.onEnterPressed: btnCreate.onClicked()
|
Keys.onEnterPressed: btnImport.onClicked()
|
||||||
Keys.onReturnPressed: btnCreate.onClicked()
|
Keys.onReturnPressed: btnImport.onClicked()
|
||||||
Keys.onEscapePressed: root.reject()
|
Keys.onEscapePressed: root.reject()
|
||||||
|
|
||||||
onTextChanged: root.fileExists = root.backendValue.isCsvFile(fileName.text)
|
onTextChanged: root.fileExists = root.backendValue.isValidUrlToImport(fileName.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
@@ -100,7 +108,7 @@ StudioControls.Dialog {
|
|||||||
Spacer {}
|
Spacer {}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("The model name: ")
|
text: qsTr("The model name")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +123,8 @@ StudioControls.Dialog {
|
|||||||
regularExpression: /^\w+$/
|
regularExpression: /^\w+$/
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: btnCreate.onClicked()
|
Keys.onEnterPressed: btnImport.onClicked()
|
||||||
Keys.onReturnPressed: btnCreate.onClicked()
|
Keys.onReturnPressed: btnImport.onClicked()
|
||||||
Keys.onEscapePressed: root.reject()
|
Keys.onEscapePressed: root.reject()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,15 +187,17 @@ StudioControls.Dialog {
|
|||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
id: btnCreate
|
id: btnImport
|
||||||
|
|
||||||
text: qsTr("Import")
|
text: qsTr("Import")
|
||||||
enabled: root.fileExists && collectionName.text !== ""
|
enabled: root.fileExists && collectionName.text !== ""
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let csvLoaded = root.backendValue.loadCsvFile(fileName.text, collectionName.text)
|
let collectionImported = root.backendValue.importCollectionToDataStore(
|
||||||
|
collectionName.text,
|
||||||
|
fileName.text)
|
||||||
|
|
||||||
if (csvLoaded)
|
if (collectionImported)
|
||||||
root.accept()
|
root.accept()
|
||||||
else
|
else
|
||||||
creationFailedDialog.open()
|
creationFailedDialog.open()
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
// Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuickDesignerTheme 1.0
|
|
||||||
import Qt.labs.platform as PlatformWidgets
|
|
||||||
import HelperWidgets 2.0 as HelperWidgets
|
|
||||||
import StudioControls 1.0 as StudioControls
|
|
||||||
import StudioTheme as StudioTheme
|
|
||||||
|
|
||||||
StudioControls.Dialog {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
title: qsTr("Import Models")
|
|
||||||
anchors.centerIn: parent
|
|
||||||
closePolicy: Popup.CloseOnEscape
|
|
||||||
modal: true
|
|
||||||
|
|
||||||
required property var backendValue
|
|
||||||
property bool fileExists: false
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
fileName.text = qsTr("New JSON File")
|
|
||||||
fileName.selectAll()
|
|
||||||
fileName.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
onRejected: {
|
|
||||||
fileName.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
RegularExpressionValidator {
|
|
||||||
id: fileNameValidator
|
|
||||||
regularExpression: /^(\w[^*><?|]*)[^/\\:*><?|]$/
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformWidgets.FileDialog {
|
|
||||||
id: fileDialog
|
|
||||||
onAccepted: fileName.text = fileDialog.file
|
|
||||||
}
|
|
||||||
|
|
||||||
Message {
|
|
||||||
id: creationFailedDialog
|
|
||||||
|
|
||||||
title: qsTr("Could not load the file")
|
|
||||||
message: qsTr("An error occurred while trying to load the file.")
|
|
||||||
|
|
||||||
onClosed: root.reject()
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: qsTr("File name: ")
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
StudioControls.TextField {
|
|
||||||
id: fileName
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
actionIndicator.visible: false
|
|
||||||
translationIndicator.visible: false
|
|
||||||
validator: fileNameValidator
|
|
||||||
|
|
||||||
Keys.onEnterPressed: btnCreate.onClicked()
|
|
||||||
Keys.onReturnPressed: btnCreate.onClicked()
|
|
||||||
Keys.onEscapePressed: root.reject()
|
|
||||||
|
|
||||||
onTextChanged: root.fileExists = root.backendValue.isJsonFile(fileName.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
id: fileDialogButton
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: qsTr("Open")
|
|
||||||
onClicked: fileDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { // spacer
|
|
||||||
implicitWidth: 1
|
|
||||||
implicitHeight: StudioTheme.Values.controlLabelGap
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
padding: 5
|
|
||||||
text: qsTr("File name cannot be empty.")
|
|
||||||
wrapMode: Label.WordWrap
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
visible: fileName.text === ""
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
border.width: StudioTheme.Values.border
|
|
||||||
border.color: StudioTheme.Values.themeWarning
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { // spacer
|
|
||||||
implicitWidth: 1
|
|
||||||
implicitHeight: StudioTheme.Values.sectionColumnSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
id: btnCreate
|
|
||||||
|
|
||||||
text: qsTr("Import")
|
|
||||||
enabled: root.fileExists
|
|
||||||
onClicked: {
|
|
||||||
let jsonLoaded = root.backendValue.loadJsonFile(fileName.text)
|
|
||||||
|
|
||||||
if (jsonLoaded)
|
|
||||||
root.accept()
|
|
||||||
else
|
|
||||||
creationFailedDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: root.reject()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,342 +12,25 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
implicitWidth: 300
|
implicitWidth: 300
|
||||||
implicitHeight: wholeColumn.height
|
implicitHeight: collectionListView.height
|
||||||
|
|
||||||
property bool hasSelectedTarget
|
property bool hasSelectedTarget
|
||||||
|
|
||||||
property color textColor
|
|
||||||
property var collectionModel
|
|
||||||
|
|
||||||
property bool expanded: false
|
|
||||||
|
|
||||||
signal selectItem(int itemIndex)
|
|
||||||
signal deleteItem()
|
|
||||||
signal assignToSelected()
|
|
||||||
|
|
||||||
function toggleExpanded() {
|
|
||||||
if (collectionListView.count > 0)
|
|
||||||
root.expanded = !root.expanded || sourceIsSelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: wholeColumn
|
|
||||||
width: parent.width
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: boundingRect
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: nameHolder.height
|
|
||||||
Layout.leftMargin: 6
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: itemMouse
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
propagateComposedEvents: true
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onClicked: (event) => {
|
|
||||||
if (!sourceIsSelected) {
|
|
||||||
sourceIsSelected = true
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDoubleClicked: (event) => {
|
|
||||||
root.toggleExpanded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: innerRect
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: expandButton
|
|
||||||
|
|
||||||
property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
|
|
||||||
|
|
||||||
Layout.preferredWidth: expandButton.style.squareControlSize.width
|
|
||||||
Layout.preferredHeight: nameHolder.height
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: StudioTheme.Constants.startNode
|
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
|
||||||
font.pixelSize: 6
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: textColor
|
|
||||||
|
|
||||||
rotation: root.expanded ? 90 : 0
|
|
||||||
visible: collectionListView.count > 0
|
|
||||||
|
|
||||||
Behavior on rotation {
|
|
||||||
SpringAnimation { spring: 2; damping: 0.2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
|
||||||
onClicked: root.toggleExpanded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: nameHolder
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: sourceName
|
|
||||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
|
||||||
color: textColor
|
|
||||||
leftPadding: 5
|
|
||||||
topPadding: 8
|
|
||||||
rightPadding: 8
|
|
||||||
bottomPadding: 8
|
|
||||||
elide: Text.ElideMiddle
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: threeDots
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: StudioTheme.Constants.more_medium
|
|
||||||
font.family: StudioTheme.Constants.iconFont.family
|
|
||||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
|
||||||
color: textColor
|
|
||||||
rightPadding: 12
|
|
||||||
topPadding: nameHolder.topPadding
|
|
||||||
bottomPadding: nameHolder.bottomPadding
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
|
||||||
onClicked: collectionMenu.popup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: collectionListView
|
id: collectionListView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
width: parent.width
|
||||||
Layout.preferredHeight: root.expanded ? contentHeight : 0
|
implicitHeight: contentHeight
|
||||||
Layout.leftMargin: 6
|
leftMargin: 6
|
||||||
|
|
||||||
model: internalModels
|
model: internalModels
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Behavior on Layout.preferredHeight {
|
|
||||||
NumberAnimation {duration: 500}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: CollectionItem {
|
delegate: CollectionItem {
|
||||||
width: collectionListView.width
|
width: collectionListView.width
|
||||||
sourceType: collectionListView.model.sourceType
|
sourceType: collectionListView.model.sourceType
|
||||||
|
hasSelectedTarget: root.hasSelectedTarget
|
||||||
onDeleteItem: collectionListView.model.removeRow(index)
|
onDeleteItem: collectionListView.model.removeRow(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.Menu {
|
|
||||||
id: collectionMenu
|
|
||||||
|
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
|
||||||
text: qsTr("Delete")
|
|
||||||
shortcut: StandardKey.Delete
|
|
||||||
onTriggered: deleteDialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
|
||||||
text: qsTr("Rename")
|
|
||||||
shortcut: StandardKey.Replace
|
|
||||||
onTriggered: renameDialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
|
||||||
text: qsTr("Assign to the selected node")
|
|
||||||
enabled: root.hasSelectedTarget
|
|
||||||
onTriggered: root.assignToSelected()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component Spacer: Item {
|
|
||||||
implicitWidth: 1
|
|
||||||
implicitHeight: StudioTheme.Values.sectionColumnSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.Dialog {
|
|
||||||
id: deleteDialog
|
|
||||||
|
|
||||||
title: qsTr("Deleting source")
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: StudioTheme.Values.sectionColumnSpacing
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: qsTr("Are you sure that you want to delete source \"" + sourceName + "\"?")
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
id: btnDelete
|
|
||||||
|
|
||||||
text: qsTr("Delete")
|
|
||||||
onClicked: root.deleteItem(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: deleteDialog.reject()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.Dialog {
|
|
||||||
id: renameDialog
|
|
||||||
|
|
||||||
title: qsTr("Rename source")
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
if (newNameField.text !== "")
|
|
||||||
sourceName = newNameField.text
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
newNameField.text = sourceName
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: qsTr("Previous name: " + sourceName)
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer {}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: qsTr("New name:")
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.TextField {
|
|
||||||
id: newNameField
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
actionIndicator.visible: false
|
|
||||||
translationIndicator.visible: false
|
|
||||||
validator: newNameValidator
|
|
||||||
|
|
||||||
Keys.onEnterPressed: renameDialog.accept()
|
|
||||||
Keys.onReturnPressed: renameDialog.accept()
|
|
||||||
Keys.onEscapePressed: renameDialog.reject()
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
btnRename.enabled = newNameField.text !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer {}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
spacing: StudioTheme.Values.sectionRowSpacing
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
id: btnRename
|
|
||||||
|
|
||||||
text: qsTr("Rename")
|
|
||||||
onClicked: renameDialog.accept()
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: renameDialog.reject()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RegularExpressionValidator {
|
|
||||||
id: newNameValidator
|
|
||||||
regularExpression: /^\w+$/
|
|
||||||
}
|
|
||||||
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "default"
|
|
||||||
when: !sourceIsSelected && !itemMouse.containsMouse
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: innerRect
|
|
||||||
opacity: 0.4
|
|
||||||
color: StudioTheme.Values.themeControlBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: root
|
|
||||||
textColor: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "hovered"
|
|
||||||
when: !sourceIsSelected && itemMouse.containsMouse
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: innerRect
|
|
||||||
opacity: 0.5
|
|
||||||
color: StudioTheme.Values.themeControlBackgroundHover
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: root
|
|
||||||
textColor: StudioTheme.Values.themeTextColor
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "selected"
|
|
||||||
when: sourceIsSelected
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: innerRect
|
|
||||||
opacity: 0.6
|
|
||||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: root
|
|
||||||
textColor: StudioTheme.Values.themeIconColorSelected
|
|
||||||
expanded: true
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: expandButton
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,15 +14,12 @@ import CollectionEditor 1.0
|
|||||||
StudioControls.Dialog {
|
StudioControls.Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
enum SourceType { NewJson, NewCsv, ExistingCollection, NewCollectionToJson }
|
enum SourceType { NewJson, NewCsv, ExistingCollection }
|
||||||
|
|
||||||
required property var backendValue
|
required property var backendValue
|
||||||
required property var sourceModel
|
required property var sourceModel
|
||||||
|
|
||||||
readonly property alias collectionType: typeMode.collectionType
|
|
||||||
readonly property bool isValid: collectionName.isValid
|
readonly property bool isValid: collectionName.isValid
|
||||||
&& jsonCollections.isValid
|
|
||||||
&& newCollectionPath.isValid
|
|
||||||
|
|
||||||
title: qsTr("Add a new Model")
|
title: qsTr("Add a new Model")
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -31,8 +28,6 @@ StudioControls.Dialog {
|
|||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
collectionName.text = qsTr("Model")
|
collectionName.text = qsTr("Model")
|
||||||
updateType()
|
|
||||||
updateJsonSourceIndex()
|
|
||||||
updateCollectionExists()
|
updateCollectionExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,57 +36,12 @@ StudioControls.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (root.isValid) {
|
if (root.isValid)
|
||||||
root.backendValue.addCollection(collectionName.text,
|
root.backendValue.addCollectionToDataStore(collectionName.text);
|
||||||
root.collectionType,
|
|
||||||
newCollectionPath.text,
|
|
||||||
jsonCollections.currentValue)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateType() {
|
|
||||||
newCollectionPath.text = ""
|
|
||||||
if (typeMode.currentValue === NewCollectionDialog.SourceType.NewJson) {
|
|
||||||
newCollectionFileDialog.nameFilters = ["JSON Files (*.json)"]
|
|
||||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
|
|
||||||
newCollectionPath.enabled = true
|
|
||||||
jsonCollections.enabled = false
|
|
||||||
typeMode.collectionType = "json"
|
|
||||||
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCsv) {
|
|
||||||
newCollectionFileDialog.nameFilters = ["Comma-Separated Values (*.csv)"]
|
|
||||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
|
|
||||||
newCollectionPath.enabled = true
|
|
||||||
jsonCollections.enabled = false
|
|
||||||
typeMode.collectionType = "csv"
|
|
||||||
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.ExistingCollection) {
|
|
||||||
newCollectionFileDialog.nameFilters = ["All Model Group Files (*.json *.csv)",
|
|
||||||
"JSON Files (*.json)",
|
|
||||||
"Comma-Separated Values (*.csv)"]
|
|
||||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.OpenFile
|
|
||||||
newCollectionPath.enabled = true
|
|
||||||
jsonCollections.enabled = false
|
|
||||||
typeMode.collectionType = "existing"
|
|
||||||
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCollectionToJson) {
|
|
||||||
newCollectionFileDialog.nameFilters = [""]
|
|
||||||
newCollectionPath.enabled = false
|
|
||||||
jsonCollections.enabled = true
|
|
||||||
typeMode.collectionType = "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateJsonSourceIndex() {
|
|
||||||
if (!jsonCollections.enabled) {
|
|
||||||
jsonCollections.currentIndex = -1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonCollections.currentIndex === -1 && jsonCollections.model.rowCount())
|
|
||||||
jsonCollections.currentIndex = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCollectionExists() {
|
function updateCollectionExists() {
|
||||||
collectionName.alreadyExists = sourceModel.collectionExists(jsonCollections.currentValue,
|
collectionName.alreadyExists = sourceModel.collectionExists(backendValue.dataStoreNode(),
|
||||||
collectionName.text)
|
collectionName.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,118 +69,6 @@ StudioControls.Dialog {
|
|||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: 5
|
spacing: 5
|
||||||
|
|
||||||
NameField {
|
|
||||||
text: qsTr("Type")
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.ComboBox {
|
|
||||||
id: typeMode
|
|
||||||
|
|
||||||
property string collectionType
|
|
||||||
|
|
||||||
Layout.minimumWidth: 300
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
model: ListModel {
|
|
||||||
ListElement { text: qsTr("New JSON model group"); value: NewCollectionDialog.SourceType.NewJson}
|
|
||||||
ListElement { text: qsTr("New CSV model"); value: NewCollectionDialog.SourceType.NewCsv}
|
|
||||||
ListElement { text: qsTr("Import an existing model group"); value: NewCollectionDialog.SourceType.ExistingCollection}
|
|
||||||
ListElement { text: qsTr("Add a model to an available JSON model group"); value: NewCollectionDialog.SourceType.NewCollectionToJson}
|
|
||||||
}
|
|
||||||
|
|
||||||
textRole: "text"
|
|
||||||
valueRole: "value"
|
|
||||||
actionIndicatorVisible: false
|
|
||||||
|
|
||||||
onCurrentValueChanged: root.updateType()
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer {}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
visible: newCollectionPath.enabled
|
|
||||||
|
|
||||||
NameField {
|
|
||||||
text: qsTr("File location")
|
|
||||||
visible: newCollectionPath.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: newCollectionPath
|
|
||||||
|
|
||||||
readonly property bool isValid: !newCollectionPath.enabled || newCollectionPath.text !== ""
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font.family: StudioTheme.Constants.font.family
|
|
||||||
font.pixelSize: StudioTheme.Values.baseIconFontSize
|
|
||||||
color: StudioTheme.Values.themePlaceholderTextColor
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.Button {
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
text: qsTr("Select")
|
|
||||||
|
|
||||||
onClicked: newCollectionFileDialog.open()
|
|
||||||
|
|
||||||
PlatformWidgets.FileDialog {
|
|
||||||
id: newCollectionFileDialog
|
|
||||||
|
|
||||||
title: qsTr("Select source file")
|
|
||||||
fileMode: PlatformWidgets.FileDialog.OpenFile
|
|
||||||
acceptLabel: newCollectionFileDialog.fileMode === PlatformWidgets.FileDialog.OpenFile
|
|
||||||
? qsTr("Open")
|
|
||||||
: qsTr("Add")
|
|
||||||
|
|
||||||
onAccepted: newCollectionPath.text = newCollectionFileDialog.currentFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorField {
|
|
||||||
visible: !newCollectionPath.isValid
|
|
||||||
text: qsTr("Select a file to continue")
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer { visible: newCollectionPath.enabled }
|
|
||||||
|
|
||||||
NameField {
|
|
||||||
text: qsTr("JSON model group")
|
|
||||||
visible: jsonCollections.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
StudioControls.ComboBox {
|
|
||||||
id: jsonCollections
|
|
||||||
|
|
||||||
readonly property bool isValid: !jsonCollections.enabled || jsonCollections.currentIndex !== -1
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
implicitWidth: 300
|
|
||||||
textRole: "sourceName"
|
|
||||||
valueRole: "sourceNode"
|
|
||||||
visible: jsonCollections.enabled
|
|
||||||
actionIndicatorVisible: false
|
|
||||||
|
|
||||||
model: CollectionJsonSourceFilterModel {
|
|
||||||
sourceModel: root.sourceModel
|
|
||||||
onRowsInserted: root.updateJsonSourceIndex()
|
|
||||||
onModelReset: root.updateJsonSourceIndex()
|
|
||||||
onRowsRemoved: root.updateJsonSourceIndex()
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnabledChanged: root.updateJsonSourceIndex()
|
|
||||||
onCurrentValueChanged: root.updateCollectionExists()
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorField {
|
|
||||||
visible: !jsonCollections.isValid
|
|
||||||
text: qsTr("Add a JSON resource to continue")
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer {visible: jsonCollections.visible }
|
|
||||||
|
|
||||||
NameField {
|
NameField {
|
||||||
text: qsTr("The model name")
|
text: qsTr("The model name")
|
||||||
visible: collectionName.enabled
|
visible: collectionName.enabled
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ StudioControls.PopupDialog {
|
|||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
function onPopupShouldOpen() {
|
function onPopupShouldOpen() {
|
||||||
root.showGlobal()
|
Qt.callLater(root.showGlobal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// This file should match the BlurHelper.qml in qtquickdesigner repository, except for shader paths
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: rootItem
|
||||||
|
property alias blurSrc1: blurredItemSource1
|
||||||
|
property alias blurSrc2: blurredItemSource2
|
||||||
|
property alias blurSrc3: blurredItemSource3
|
||||||
|
property alias blurSrc4: blurredItemSource4
|
||||||
|
property alias blurSrc5: blurredItemSource5
|
||||||
|
|
||||||
|
component BlurItem: ShaderEffect {
|
||||||
|
property vector2d offset: Qt.vector2d((1.0 + rootItem.blurMultiplier) / width,
|
||||||
|
(1.0 + rootItem.blurMultiplier) / height)
|
||||||
|
visible: false
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
vertexShader: g_propertyData.blur_vs_path
|
||||||
|
fragmentShader: g_propertyData.blur_fs_path
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: priv
|
||||||
|
property bool useBlurItem1: true
|
||||||
|
property bool useBlurItem2: rootItem.blurMax > 2
|
||||||
|
property bool useBlurItem3: rootItem.blurMax > 8
|
||||||
|
property bool useBlurItem4: rootItem.blurMax > 16
|
||||||
|
property bool useBlurItem5: rootItem.blurMax > 32
|
||||||
|
}
|
||||||
|
|
||||||
|
BlurItem {
|
||||||
|
id: blurredItemSource1
|
||||||
|
property Item src: priv.useBlurItem1 ? source : null
|
||||||
|
// Size of the first blurred item is by default half of the source.
|
||||||
|
// Increase for quality and decrease for performance & more blur.
|
||||||
|
readonly property int blurItemSize: 8
|
||||||
|
width: src ? Math.ceil(src.width / 16) * blurItemSize : 0
|
||||||
|
height: src ? Math.ceil(src.height / 16) * blurItemSize : 0
|
||||||
|
}
|
||||||
|
BlurItem {
|
||||||
|
id: blurredItemSource2
|
||||||
|
property Item src: priv.useBlurItem2 ? blurredItemSource1 : null
|
||||||
|
width: blurredItemSource1.width * 0.5
|
||||||
|
height: blurredItemSource1.height * 0.5
|
||||||
|
}
|
||||||
|
BlurItem {
|
||||||
|
id: blurredItemSource3
|
||||||
|
property Item src: priv.useBlurItem3 ? blurredItemSource2 : null
|
||||||
|
width: blurredItemSource2.width * 0.5
|
||||||
|
height: blurredItemSource2.height * 0.5
|
||||||
|
}
|
||||||
|
BlurItem {
|
||||||
|
id: blurredItemSource4
|
||||||
|
property Item src: priv.useBlurItem4 ? blurredItemSource3 : null
|
||||||
|
width: blurredItemSource3.width * 0.5
|
||||||
|
height: blurredItemSource3.height * 0.5
|
||||||
|
}
|
||||||
|
BlurItem {
|
||||||
|
id: blurredItemSource5
|
||||||
|
property Item src: priv.useBlurItem5 ? blurredItemSource4 : null
|
||||||
|
width: blurredItemSource4.width * 0.5
|
||||||
|
height: blurredItemSource4.height * 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,19 +12,22 @@ import EffectMakerBackend
|
|||||||
HelperWidgets.Section {
|
HelperWidgets.Section {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property int modelIndex: 0
|
||||||
|
|
||||||
caption: nodeName
|
caption: nodeName
|
||||||
category: "EffectMaker"
|
category: "EffectMaker"
|
||||||
|
|
||||||
draggable: true
|
draggable: !isDependency
|
||||||
fillBackground: true
|
fillBackground: true
|
||||||
showCloseButton: true
|
showCloseButton: !isDependency
|
||||||
closeButtonToolTip: qsTr("Remove")
|
closeButtonToolTip: qsTr("Remove")
|
||||||
|
visible: repeater.count > 0 || !isDependency
|
||||||
|
|
||||||
onCloseButtonClicked: {
|
onCloseButtonClicked: {
|
||||||
EffectMakerBackend.effectMakerModel.removeNode(root.index)
|
EffectMakerBackend.effectMakerModel.removeNode(root.modelIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
showEyeButton: true
|
showEyeButton: !isDependency
|
||||||
eyeEnabled: nodeEnabled
|
eyeEnabled: nodeEnabled
|
||||||
eyeButtonToolTip: qsTr("Enable/Disable Node")
|
eyeButtonToolTip: qsTr("Enable/Disable Node")
|
||||||
|
|
||||||
@@ -36,6 +39,7 @@ HelperWidgets.Section {
|
|||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: repeater
|
||||||
model: nodeUniformsModel
|
model: nodeUniformsModel
|
||||||
|
|
||||||
EffectCompositionNodeUniform {
|
EffectCompositionNodeUniform {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Item {
|
|||||||
|
|
||||||
height: layout.implicitHeight
|
height: layout.implicitHeight
|
||||||
|
|
||||||
|
visible: !uniformUseCustomValue
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (uniformType === "int")
|
if (uniformType === "int")
|
||||||
valueLoader.source = "ValueInt.qml"
|
valueLoader.source = "ValueInt.qml"
|
||||||
|
|||||||
@@ -16,14 +16,45 @@ Item {
|
|||||||
property int moveToIdx: 0
|
property int moveToIdx: 0
|
||||||
property bool previewAnimationRunning: false
|
property bool previewAnimationRunning: false
|
||||||
|
|
||||||
SaveDialog {
|
// Invoked after save changes is done
|
||||||
id: saveDialog
|
property var onSaveChangesCallback: () => {}
|
||||||
compositionName: EffectMakerBackend.effectMakerModel.currentComposition
|
|
||||||
|
// Invoked from C++ side when open composition is requested and there are unsaved changes
|
||||||
|
function promptToSaveBeforeOpen() {
|
||||||
|
root.onSaveChangesCallback = () => { EffectMakerBackend.rootView.doOpenComposition() }
|
||||||
|
|
||||||
|
saveChangesDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: EffectMakerBackend.effectMakerModel
|
||||||
|
function onIsEmptyChanged() {
|
||||||
|
if (EffectMakerBackend.effectMakerModel.isEmpty)
|
||||||
|
saveAsDialog.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveAsDialog {
|
||||||
|
id: saveAsDialog
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
onAccepted: {
|
}
|
||||||
let name = saveDialog.compositionName
|
|
||||||
EffectMakerBackend.effectMakerModel.exportComposition(name)
|
SaveChangesDialog {
|
||||||
EffectMakerBackend.effectMakerModel.exportResources(name)
|
id: saveChangesDialog
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
onSave: {
|
||||||
|
if (EffectMakerBackend.effectMakerModel.currentComposition === "") {
|
||||||
|
// if current composition is unsaved, show save as dialog and clear afterwards
|
||||||
|
saveAsDialog.clearOnClose = true
|
||||||
|
saveAsDialog.open()
|
||||||
|
} else {
|
||||||
|
root.onSaveChangesCallback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDiscard: {
|
||||||
|
root.onSaveChangesCallback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +64,29 @@ Item {
|
|||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
EffectMakerTopBar {
|
EffectMakerTopBar {
|
||||||
onSaveClicked: saveDialog.open()
|
onAddClicked: {
|
||||||
|
root.onSaveChangesCallback = () => { EffectMakerBackend.effectMakerModel.clear() }
|
||||||
|
|
||||||
|
if (EffectMakerBackend.effectMakerModel.hasUnsavedChanges)
|
||||||
|
saveChangesDialog.open()
|
||||||
|
else
|
||||||
|
EffectMakerBackend.effectMakerModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSaveClicked: {
|
||||||
|
let name = EffectMakerBackend.effectMakerModel.currentComposition
|
||||||
|
|
||||||
|
if (name === "")
|
||||||
|
saveAsDialog.open()
|
||||||
|
else
|
||||||
|
EffectMakerBackend.effectMakerModel.saveComposition(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSaveAsClicked: saveAsDialog.open()
|
||||||
|
|
||||||
|
onAssignToSelectedClicked: {
|
||||||
|
EffectMakerBackend.effectMakerModel.assignToSelected()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectMakerPreview {
|
EffectMakerPreview {
|
||||||
@@ -55,6 +108,21 @@ Item {
|
|||||||
mainRoot: root
|
mainRoot: root
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
x: 5
|
||||||
|
width: parent.width - 50
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperWidgets.AbstractButton {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 5
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.clearList_medium
|
||||||
|
tooltip: qsTr("Remove all effect nodes.")
|
||||||
|
enabled: !EffectMakerBackend.effectMakerModel.isEmpty
|
||||||
|
|
||||||
|
onClicked: EffectMakerBackend.effectMakerModel.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
HelperWidgets.AbstractButton {
|
||||||
@@ -64,7 +132,7 @@ Item {
|
|||||||
|
|
||||||
style: StudioTheme.Values.viewBarButtonStyle
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
buttonIcon: StudioTheme.Constants.code
|
buttonIcon: StudioTheme.Constants.code
|
||||||
tooltip: qsTr("Open Shader in Code Editor")
|
tooltip: qsTr("Open Shader in Code Editor.")
|
||||||
visible: false // TODO: to be implemented
|
visible: false // TODO: to be implemented
|
||||||
|
|
||||||
onClicked: {} // TODO
|
onClicked: {} // TODO
|
||||||
@@ -96,6 +164,7 @@ Item {
|
|||||||
|
|
||||||
delegate: EffectCompositionNode {
|
delegate: EffectCompositionNode {
|
||||||
width: root.width
|
width: root.width
|
||||||
|
modelIndex: index
|
||||||
|
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
@@ -144,7 +213,8 @@ Item {
|
|||||||
currItem.y = root.secsY[i]
|
currItem.y = root.secsY[i]
|
||||||
}
|
}
|
||||||
} else if (i < root.moveFromIdx) {
|
} else if (i < root.moveFromIdx) {
|
||||||
if (root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) {
|
if (!repeater.model.isDependencyNode(i)
|
||||||
|
&& root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) {
|
||||||
currItem.y = root.secsY[i] + root.draggedSec.height
|
currItem.y = root.secsY[i] + root.draggedSec.height
|
||||||
root.moveToIdx = Math.min(root.moveToIdx, i)
|
root.moveToIdx = Math.min(root.moveToIdx, i)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -84,17 +84,6 @@ Column {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
|
||||||
enabled: sourceImage.scale > .4
|
|
||||||
style: StudioTheme.Values.viewBarButtonStyle
|
|
||||||
buttonIcon: StudioTheme.Constants.zoomOut_medium
|
|
||||||
tooltip: qsTr("Zoom out")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
sourceImage.scale -= .2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
HelperWidgets.AbstractButton {
|
||||||
enabled: sourceImage.scale < 2
|
enabled: sourceImage.scale < 2
|
||||||
style: StudioTheme.Values.viewBarButtonStyle
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
@@ -106,6 +95,17 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HelperWidgets.AbstractButton {
|
||||||
|
enabled: sourceImage.scale > .4
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.zoomOut_medium
|
||||||
|
tooltip: qsTr("Zoom out")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
sourceImage.scale -= .2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
HelperWidgets.AbstractButton {
|
||||||
enabled: sourceImage.scale !== 1
|
enabled: sourceImage.scale !== 1
|
||||||
style: StudioTheme.Values.viewBarButtonStyle
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
@@ -194,6 +194,13 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlurHelper {
|
||||||
|
id: blurHelper
|
||||||
|
anchors.fill: parent
|
||||||
|
property int blurMax: g_propertyData.blur_helper_max_level ? g_propertyData.blur_helper_max_level : 64
|
||||||
|
property real blurMultiplier: g_propertyData.blurMultiplier ? g_propertyData.blurMultiplier : 0
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: componentParent
|
id: componentParent
|
||||||
width: source.width
|
width: source.width
|
||||||
|
|||||||
@@ -15,17 +15,61 @@ Rectangle {
|
|||||||
height: StudioTheme.Values.toolbarHeight
|
height: StudioTheme.Values.toolbarHeight
|
||||||
color: StudioTheme.Values.themeToolbarBackground
|
color: StudioTheme.Values.themeToolbarBackground
|
||||||
|
|
||||||
|
signal addClicked
|
||||||
signal saveClicked
|
signal saveClicked
|
||||||
|
signal saveAsClicked
|
||||||
|
signal assignToSelectedClicked
|
||||||
|
|
||||||
HelperWidgets.Button {
|
Row {
|
||||||
|
spacing: 5
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
x: 5
|
|
||||||
|
|
||||||
text: qsTr("Save in Library")
|
HelperWidgets.AbstractButton {
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.add_medium
|
||||||
|
tooltip: qsTr("Add new composition")
|
||||||
|
|
||||||
|
onClicked: root.addClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperWidgets.AbstractButton {
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.save_medium
|
||||||
|
tooltip: qsTr("Save current composition")
|
||||||
|
enabled: EffectMakerBackend.effectMakerModel.hasUnsavedChanges
|
||||||
|
|| EffectMakerBackend.effectMakerModel.currentComposition === ""
|
||||||
|
|
||||||
onClicked: root.saveClicked()
|
onClicked: root.saveClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HelperWidgets.AbstractButton {
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.saveAs_medium
|
||||||
|
tooltip: qsTr("Save current composition with a new name")
|
||||||
|
enabled: !EffectMakerBackend.effectMakerModel.isEmpty
|
||||||
|
|
||||||
|
onClicked: root.saveAsClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperWidgets.AbstractButton {
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.assignTo_medium
|
||||||
|
tooltip: qsTr("Assign current composition to selected item")
|
||||||
|
enabled: EffectMakerBackend.effectMakerModel.currentComposition !== ""
|
||||||
|
|
||||||
|
onClicked: root.assignToSelectedClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
readonly property string compName: EffectMakerBackend.effectMakerModel.currentComposition
|
||||||
|
|
||||||
|
text: compName !== "" ? compName : qsTr("Untitled")
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
HelperWidgets.AbstractButton {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.rightMargin: 5
|
anchors.rightMargin: 5
|
||||||
|
|||||||
@@ -15,19 +15,21 @@ Rectangle {
|
|||||||
width: 140
|
width: 140
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction
|
color: mouseArea.containsMouse && modelData.canBeAdded
|
||||||
: "transparent"
|
? StudioTheme.Values.themeControlBackgroundInteraction : "transparent"
|
||||||
|
|
||||||
signal addEffectNode(var nodeQenPath)
|
signal addEffectNode(var nodeQenPath)
|
||||||
|
|
||||||
MouseArea {
|
ToolTipArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
|
|
||||||
|
tooltip: modelData.canBeAdded ? "" : qsTr("Existing effect has conflicting properties, this effect cannot be added.")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (modelData.canBeAdded)
|
||||||
root.addEffectNode(modelData.nodeQenPath)
|
root.addEffectNode(modelData.nodeQenPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,13 +43,15 @@ Rectangle {
|
|||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: modelData.canBeAdded ? StudioTheme.Values.themeTextColor
|
||||||
|
: StudioTheme.Values.themeTextColorDisabled
|
||||||
source: modelData.nodeIcon
|
source: modelData.nodeIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: modelData.nodeName
|
text: modelData.nodeName
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: modelData.canBeAdded ? StudioTheme.Values.themeTextColor
|
||||||
|
: StudioTheme.Values.themeTextColorDisabled
|
||||||
font.pointSize: StudioTheme.Values.smallFontSize
|
font.pointSize: StudioTheme.Values.smallFontSize
|
||||||
anchors.verticalCenter: nodeIcon.verticalCenter
|
anchors.verticalCenter: nodeIcon.verticalCenter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ StudioControls.ComboBox {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
actionIndicatorVisible: false
|
actionIndicatorVisible: false
|
||||||
x: 5
|
|
||||||
width: parent.width - 50
|
|
||||||
|
|
||||||
model: [qsTr("+ Add Effect")]
|
model: [qsTr("+ Add Effect")]
|
||||||
|
|
||||||
@@ -23,18 +21,48 @@ StudioControls.ComboBox {
|
|||||||
|
|
||||||
required property Item mainRoot
|
required property Item mainRoot
|
||||||
|
|
||||||
|
readonly property int popupHeight: Math.min(800, row.height + 2)
|
||||||
|
|
||||||
|
function calculateWindowGeometry() {
|
||||||
|
var globalPos = EffectMakerBackend.rootView.globalPos(mainRoot.mapFromItem(root, 0, 0))
|
||||||
|
var screenRect = EffectMakerBackend.rootView.screenRect();
|
||||||
|
|
||||||
|
window.width = row.width + 2 // 2: scrollView left and right 1px margins
|
||||||
|
|
||||||
|
var newX = globalPos.x + root.width - window.width
|
||||||
|
if (newX < screenRect.x)
|
||||||
|
newX = globalPos.x
|
||||||
|
|
||||||
|
var newY = Math.min(screenRect.y + screenRect.height,
|
||||||
|
Math.max(screenRect.y, globalPos.y + root.height - 1))
|
||||||
|
|
||||||
|
// Check if we have more space above or below the control, and put control on that side,
|
||||||
|
// unless we have enough room for maximum size popup under the control
|
||||||
|
var newHeight
|
||||||
|
var screenY = newY - screenRect.y
|
||||||
|
if (screenRect.height - screenY > screenY || screenRect.height - screenY > root.popupHeight) {
|
||||||
|
newHeight = Math.min(root.popupHeight, screenRect.height - screenY)
|
||||||
|
} else {
|
||||||
|
newHeight = Math.min(root.popupHeight, screenY - root.height)
|
||||||
|
newY = newY - newHeight - root.height + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
window.height = newHeight
|
||||||
|
window.x = newX
|
||||||
|
window.y = newY
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.popup
|
target: root.popup
|
||||||
|
|
||||||
function onAboutToShow() {
|
function onAboutToShow() {
|
||||||
var a = mainRoot.mapToGlobal(0, 0)
|
root.calculateWindowGeometry()
|
||||||
var b = root.mapToItem(mainRoot, 0, 0)
|
|
||||||
|
|
||||||
window.x = a.x + b.x + root.width - window.width
|
|
||||||
window.y = a.y + b.y + root.height - 1
|
|
||||||
|
|
||||||
window.show()
|
window.show()
|
||||||
window.requestActivate()
|
window.requestActivate()
|
||||||
|
|
||||||
|
// Geometry can get corrupted by first show after screen change, so recalc it
|
||||||
|
root.calculateWindowGeometry()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAboutToHide() {
|
function onAboutToHide() {
|
||||||
@@ -45,12 +73,10 @@ StudioControls.ComboBox {
|
|||||||
Window {
|
Window {
|
||||||
id: window
|
id: window
|
||||||
|
|
||||||
width: row.width + 2 // 2: scrollView left and right 1px margins
|
|
||||||
height: Math.min(800, Math.min(row.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar
|
|
||||||
flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
|
flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
|
||||||
|
|
||||||
onActiveFocusItemChanged: {
|
onActiveFocusItemChanged: {
|
||||||
if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened)
|
if (!window.activeFocusItem && !root.hovered && root.popup.opened)
|
||||||
root.popup.close()
|
root.popup.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +85,7 @@ StudioControls.ComboBox {
|
|||||||
color: StudioTheme.Values.themePanelBackground
|
color: StudioTheme.Values.themePanelBackground
|
||||||
border.color: StudioTheme.Values.themeInteraction
|
border.color: StudioTheme.Values.themeInteraction
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
focus: true
|
||||||
|
|
||||||
HelperWidgets.ScrollView {
|
HelperWidgets.ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -67,16 +94,6 @@ StudioControls.ComboBox {
|
|||||||
Row {
|
Row {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
onWidthChanged: {
|
|
||||||
// Needed to update on first window showing, as row.width only gets
|
|
||||||
// correct value after the window is shown, so first showing is off
|
|
||||||
|
|
||||||
var a = mainRoot.mapToGlobal(0, 0)
|
|
||||||
var b = root.mapToItem(mainRoot, 0, 0)
|
|
||||||
|
|
||||||
window.x = a.x + b.x + root.width - row.width
|
|
||||||
}
|
|
||||||
|
|
||||||
padding: 10
|
padding: 10
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
@@ -108,6 +125,11 @@ StudioControls.ComboBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function(event) {
|
||||||
|
if (event.key === Qt.Key_Escape && root.popup.opened)
|
||||||
|
root.popup.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,18 +30,48 @@ StudioControls.ComboBox {
|
|||||||
"images/preview4.png"]
|
"images/preview4.png"]
|
||||||
property string selectedImage: images[0]
|
property string selectedImage: images[0]
|
||||||
|
|
||||||
|
readonly property int popupHeight: Math.min(800, col.height + 2)
|
||||||
|
|
||||||
|
function calculateWindowGeometry() {
|
||||||
|
var globalPos = EffectMakerBackend.rootView.globalPos(mainRoot.mapFromItem(root, 0, 0))
|
||||||
|
var screenRect = EffectMakerBackend.rootView.screenRect();
|
||||||
|
|
||||||
|
window.width = col.width + 2 // 2: scrollView left and right 1px margins
|
||||||
|
|
||||||
|
var newX = globalPos.x + root.width - window.width
|
||||||
|
if (newX < screenRect.x)
|
||||||
|
newX = globalPos.x
|
||||||
|
|
||||||
|
var newY = Math.min(screenRect.y + screenRect.height,
|
||||||
|
Math.max(screenRect.y, globalPos.y + root.height - 1))
|
||||||
|
|
||||||
|
// Check if we have more space above or below the control, and put control on that side,
|
||||||
|
// unless we have enough room for maximum size popup under the control
|
||||||
|
var newHeight
|
||||||
|
var screenY = newY - screenRect.y
|
||||||
|
if (screenRect.height - screenY > screenY || screenRect.height - screenY > root.popupHeight) {
|
||||||
|
newHeight = Math.min(root.popupHeight, screenRect.height - screenY)
|
||||||
|
} else {
|
||||||
|
newHeight = Math.min(root.popupHeight, screenY - root.height)
|
||||||
|
newY = newY - newHeight - root.height + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
window.height = newHeight
|
||||||
|
window.x = newX
|
||||||
|
window.y = newY
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.popup
|
target: root.popup
|
||||||
|
|
||||||
function onAboutToShow() {
|
function onAboutToShow() {
|
||||||
var a = mainRoot.mapToGlobal(0, 0)
|
root.calculateWindowGeometry()
|
||||||
var b = root.mapToItem(mainRoot, 0, 0)
|
|
||||||
|
|
||||||
window.x = a.x + b.x + root.width - window.width
|
|
||||||
window.y = a.y + b.y + root.height - 1
|
|
||||||
|
|
||||||
window.show()
|
window.show()
|
||||||
window.requestActivate()
|
window.requestActivate()
|
||||||
|
|
||||||
|
// Geometry can get corrupted by first show after screen change, so recalc it
|
||||||
|
root.calculateWindowGeometry()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAboutToHide() {
|
function onAboutToHide() {
|
||||||
@@ -60,17 +90,29 @@ StudioControls.ComboBox {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 1
|
anchors.margins: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: (mouse) => {
|
||||||
|
if (root.popup.opened)
|
||||||
|
root.popup.close()
|
||||||
|
else
|
||||||
|
root.popup.open()
|
||||||
|
mouse.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: window
|
id: window
|
||||||
|
|
||||||
width: col.width + 2 // 2: scrollView left and right 1px margins
|
|
||||||
height: Math.min(800, Math.min(col.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar
|
|
||||||
flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
|
flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
|
||||||
|
|
||||||
onActiveFocusItemChanged: {
|
onActiveFocusItemChanged: {
|
||||||
if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened)
|
if (!window.activeFocusItem && !root.hovered && root.popup.opened)
|
||||||
root.popup.close()
|
root.popup.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +121,7 @@ StudioControls.ComboBox {
|
|||||||
color: StudioTheme.Values.themePanelBackground
|
color: StudioTheme.Values.themePanelBackground
|
||||||
border.color: StudioTheme.Values.themeInteraction
|
border.color: StudioTheme.Values.themeInteraction
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
focus: true
|
||||||
|
|
||||||
HelperWidgets.ScrollView {
|
HelperWidgets.ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -88,16 +131,6 @@ StudioControls.ComboBox {
|
|||||||
Column {
|
Column {
|
||||||
id: col
|
id: col
|
||||||
|
|
||||||
onWidthChanged: {
|
|
||||||
// Needed to update on first window showing, as row.width only gets
|
|
||||||
// correct value after the window is shown, so first showing is off
|
|
||||||
|
|
||||||
var a = mainRoot.mapToGlobal(0, 0)
|
|
||||||
var b = root.mapToItem(mainRoot, 0, 0)
|
|
||||||
|
|
||||||
window.x = a.x + b.x + root.width - col.width
|
|
||||||
}
|
|
||||||
|
|
||||||
padding: 10
|
padding: 10
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
@@ -135,6 +168,11 @@ StudioControls.ComboBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function(event) {
|
||||||
|
if (event.key === Qt.Key_Escape && root.popup.opened)
|
||||||
|
root.popup.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import QtQuick.Controls
|
|||||||
import HelperWidgets as HelperWidgets
|
import HelperWidgets as HelperWidgets
|
||||||
import StudioControls as StudioControls
|
import StudioControls as StudioControls
|
||||||
import StudioTheme as StudioTheme
|
import StudioTheme as StudioTheme
|
||||||
import AssetsLibraryBackend
|
import EffectMakerBackend
|
||||||
|
|
||||||
StudioControls.Dialog {
|
StudioControls.Dialog {
|
||||||
id: root
|
id: root
|
||||||
@@ -18,12 +18,13 @@ StudioControls.Dialog {
|
|||||||
implicitWidth: 250
|
implicitWidth: 250
|
||||||
implicitHeight: 160
|
implicitHeight: 160
|
||||||
|
|
||||||
property string compositionName: ""
|
property bool clearOnClose: false // clear the effect maker after saving
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
nameText.text = compositionName //TODO: Generate unique name
|
nameText.text = EffectMakerBackend.effectMakerModel.getUniqueEffectName()
|
||||||
emptyText.text = ""
|
nameText.selectAll()
|
||||||
nameText.forceActiveFocus()
|
nameText.forceActiveFocus()
|
||||||
|
emptyText.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
@@ -83,14 +84,28 @@ StudioControls.Dialog {
|
|||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
enabled: nameText.text !== ""
|
enabled: nameText.text !== ""
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.compositionName = nameText.text
|
EffectMakerBackend.effectMakerModel.saveComposition(nameText.text)
|
||||||
root.accept() //TODO: Check if name is unique
|
|
||||||
|
if (root.clearOnClose) {
|
||||||
|
EffectMakerBackend.effectMakerModel.clear()
|
||||||
|
root.clearOnClose = false
|
||||||
|
}
|
||||||
|
|
||||||
|
root.accept() // TODO: confirm before overriding effect with same name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HelperWidgets.Button {
|
HelperWidgets.Button {
|
||||||
text: qsTr("Cancel")
|
text: qsTr("Cancel")
|
||||||
onClicked: root.reject()
|
|
||||||
|
onClicked: {
|
||||||
|
if (root.clearOnClose) {
|
||||||
|
EffectMakerBackend.effectMakerModel.clear()
|
||||||
|
root.clearOnClose = false
|
||||||
|
}
|
||||||
|
|
||||||
|
root.reject()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import HelperWidgets as HelperWidgets
|
||||||
|
import StudioControls as StudioControls
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
import EffectMakerBackend
|
||||||
|
|
||||||
|
StudioControls.Dialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
title: qsTr("Save Changes")
|
||||||
|
|
||||||
|
closePolicy: Popup.CloseOnEscape
|
||||||
|
modal: true
|
||||||
|
implicitWidth: 300
|
||||||
|
implicitHeight: 130
|
||||||
|
|
||||||
|
signal save()
|
||||||
|
signal discard()
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
Text {
|
||||||
|
text: qsTr("Current composition has unsaved changes.")
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperWidgets.Button {
|
||||||
|
width: 60
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
onClicked: root.reject()
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
HelperWidgets.Button {
|
||||||
|
width: 50
|
||||||
|
text: qsTr("Save")
|
||||||
|
onClicked: {
|
||||||
|
let name = EffectMakerBackend.effectMakerModel.currentComposition
|
||||||
|
if (name !== "")
|
||||||
|
EffectMakerBackend.effectMakerModel.saveComposition(name)
|
||||||
|
|
||||||
|
root.save()
|
||||||
|
root.accept()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperWidgets.Button {
|
||||||
|
width: 110
|
||||||
|
text: qsTr("Discard Changes")
|
||||||
|
onClicked: {
|
||||||
|
root.discard()
|
||||||
|
root.accept()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,5 +19,19 @@ Row {
|
|||||||
actionIndicatorVisible: false
|
actionIndicatorVisible: false
|
||||||
|
|
||||||
onAbsoluteFilePathChanged: uniformValue = absoluteFilePath
|
onAbsoluteFilePathChanged: uniformValue = absoluteFilePath
|
||||||
|
|
||||||
|
function defaultAsString() {
|
||||||
|
let urlStr = uniformDefaultValue.toString()
|
||||||
|
urlStr = urlStr.replace(/^(file:\/{3})/, "")
|
||||||
|
|
||||||
|
// Prepend slash if there is no drive letter
|
||||||
|
if (urlStr.length > 1 && urlStr[1] !== ':')
|
||||||
|
urlStr = '/' + urlStr;
|
||||||
|
|
||||||
|
return urlStr
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultItems: [uniformDefaultValue.split('/').pop()]
|
||||||
|
defaultPaths: [defaultAsString(uniformDefaultValue)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
HelperWidgets.Section {
|
HelperWidgets.Section {
|
||||||
id: predefinedSection
|
id: predefinedSection
|
||||||
caption: qsTr("Predefined Categories")
|
caption: qsTr("Predefined Categories")
|
||||||
@@ -412,6 +412,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,8 +183,9 @@ SecondColumnLayout {
|
|||||||
property QtObject loaderItem: loader.item
|
property QtObject loaderItem: loader.item
|
||||||
property string gradientPropertyName
|
property string gradientPropertyName
|
||||||
|
|
||||||
|
keepOpen: loader.item?.eyeDropperActive ?? false
|
||||||
|
|
||||||
width: 260
|
width: 260
|
||||||
maximumHeight: Screen.desktopAvailableHeight * 0.7
|
|
||||||
|
|
||||||
function commitToGradient() {
|
function commitToGradient() {
|
||||||
if (!loader.active)
|
if (!loader.active)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import QtQuickDesignerColorPalette
|
|||||||
Column {
|
Column {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property bool eyeDropperActive: ColorPaletteBackend.eyeDropperActive
|
||||||
|
|
||||||
property bool supportGradient: false
|
property bool supportGradient: false
|
||||||
property bool shapeGradients: false
|
property bool shapeGradients: false
|
||||||
property alias gradientLine: gradientLine
|
property alias gradientLine: gradientLine
|
||||||
@@ -552,9 +554,9 @@ Column {
|
|||||||
textRole: "text"
|
textRole: "text"
|
||||||
valueRole: "value"
|
valueRole: "value"
|
||||||
model: [
|
model: [
|
||||||
{ value: ColorPicker.Mode.HSVA, text: "HSVA" },
|
{ value: StudioControls.ColorPicker.Mode.HSVA, text: "HSVA" },
|
||||||
{ value: ColorPicker.Mode.RGBA, text: "RGBA" },
|
{ value: StudioControls.ColorPicker.Mode.RGBA, text: "RGBA" },
|
||||||
{ value: ColorPicker.Mode.HSLA, text: "HSLA" }
|
{ value: StudioControls.ColorPicker.Mode.HSLA, text: "HSLA" }
|
||||||
]
|
]
|
||||||
|
|
||||||
onActivated: colorPicker.mode = colorMode.currentValue
|
onActivated: colorPicker.mode = colorMode.currentValue
|
||||||
@@ -597,7 +599,7 @@ Column {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: rgbaRow
|
id: rgbaRow
|
||||||
visible: colorPicker.mode === ColorPicker.Mode.RGBA
|
visible: colorPicker.mode === StudioControls.ColorPicker.Mode.RGBA
|
||||||
spacing: StudioTheme.Values.controlGap
|
spacing: StudioTheme.Values.controlGap
|
||||||
|
|
||||||
DoubleSpinBox {
|
DoubleSpinBox {
|
||||||
@@ -683,7 +685,7 @@ Column {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: hslaRow
|
id: hslaRow
|
||||||
visible: colorPicker.mode === ColorPicker.Mode.HSLA
|
visible: colorPicker.mode === StudioControls.ColorPicker.Mode.HSLA
|
||||||
spacing: StudioTheme.Values.controlGap
|
spacing: StudioTheme.Values.controlGap
|
||||||
|
|
||||||
DoubleSpinBox {
|
DoubleSpinBox {
|
||||||
@@ -749,7 +751,7 @@ Column {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: hsvaRow
|
id: hsvaRow
|
||||||
visible: colorPicker.mode === ColorPicker.Mode.HSVA
|
visible: colorPicker.mode === StudioControls.ColorPicker.Mode.HSVA
|
||||||
spacing: StudioTheme.Values.controlGap
|
spacing: StudioTheme.Values.controlGap
|
||||||
|
|
||||||
DoubleSpinBox {
|
DoubleSpinBox {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (C) 2020 The Qt Company Ltd.
|
// Copyright (C) 2020 The Qt Company Ltd.
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls.Basic
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
ScrollBar {
|
ScrollBar {
|
||||||
id: scrollBar
|
id: scrollBar
|
||||||
@@ -13,9 +13,10 @@ ScrollBar {
|
|||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||||
implicitContentHeight + topPadding + bottomPadding)
|
implicitContentHeight + topPadding + bottomPadding)
|
||||||
|
|
||||||
|
|
||||||
property bool scrollBarVisible: parent.childrenRect.width > parent.width
|
property bool scrollBarVisible: parent.childrenRect.width > parent.width
|
||||||
|
|
||||||
|
minimumSize: orientation == Qt.Horizontal ? height / width : width / height
|
||||||
|
|
||||||
orientation: Qt.Horizontal
|
orientation: Qt.Horizontal
|
||||||
policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||||
x: 0
|
x: 0
|
||||||
@@ -24,8 +25,6 @@ ScrollBar {
|
|||||||
- (parent.bothVisible ? parent.verticalThickness : 0)
|
- (parent.bothVisible ? parent.verticalThickness : 0)
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
minimumSize: orientation == Qt.Horizontal ? height / width : width / height
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: StudioTheme.Values.themeScrollBarTrack
|
color: StudioTheme.Values.themeScrollBarTrack
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ Section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SectionLayout {
|
SectionLayout {
|
||||||
|
/*
|
||||||
PropertyLabel { text: qsTr("Category") }
|
PropertyLabel { text: qsTr("Category") }
|
||||||
|
|
||||||
SecondColumnLayout {
|
SecondColumnLayout {
|
||||||
@@ -84,7 +85,7 @@ Section {
|
|||||||
|
|
||||||
ExpandingSpacer {}
|
ExpandingSpacer {}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
PropertyLabel {
|
PropertyLabel {
|
||||||
text: qsTr("Object name")
|
text: qsTr("Object name")
|
||||||
tooltip: qsTr("Sets the object name of the component.")
|
tooltip: qsTr("Sets the object name of the component.")
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls
|
||||||
import HelperWidgets 2.0
|
import HelperWidgets
|
||||||
import StudioControls 1.0 as StudioControls
|
import StudioControls as StudioControls
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme as StudioTheme
|
||||||
import QtQuickDesignerTheme 1.0
|
import QtQuickDesignerTheme
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: root
|
id: root
|
||||||
@@ -22,6 +22,10 @@ Row {
|
|||||||
// by QtQuick3D to add built-in primitives to the model.
|
// by QtQuick3D to add built-in primitives to the model.
|
||||||
property var defaultItems
|
property var defaultItems
|
||||||
|
|
||||||
|
// These paths will be used for default items if they are defined. Otherwise, default item
|
||||||
|
// itself is used as the path.
|
||||||
|
property var defaultPaths
|
||||||
|
|
||||||
// Current item
|
// Current item
|
||||||
property string absoluteFilePath: ""
|
property string absoluteFilePath: ""
|
||||||
|
|
||||||
@@ -40,6 +44,96 @@ Row {
|
|||||||
backendValue: root.backendValue
|
backendValue: root.backendValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component ThumbnailToolTip: ToolTip {
|
||||||
|
id: toolTip
|
||||||
|
|
||||||
|
property alias checkerVisible: checker.visible
|
||||||
|
property alias thumbnailSource: thumbnail.source
|
||||||
|
|
||||||
|
property alias titleText: title.text
|
||||||
|
property alias descriptionText: description.text
|
||||||
|
|
||||||
|
property int maximumWidth: 420
|
||||||
|
|
||||||
|
delay: StudioTheme.Values.toolTipDelay
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: StudioTheme.Values.themeToolTipBackground
|
||||||
|
border.color: StudioTheme.Values.themeToolTipOutline
|
||||||
|
border.width: StudioTheme.Values.border
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Row {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
readonly property real __epsilon: 2
|
||||||
|
|
||||||
|
height: Math.max(wrapper.visible ? wrapper.height : 0, column.height)
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: wrapper
|
||||||
|
visible: thumbnail.status === Image.Ready
|
||||||
|
width: 96
|
||||||
|
height: 96
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: checker
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.Tile
|
||||||
|
source: "images/checkers.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: thumbnail
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceSize.width: wrapper.width
|
||||||
|
sourceSize.height: wrapper.height
|
||||||
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
property int thumbnailSize: wrapper.visible ? wrapper.width + row.spacing : 0
|
||||||
|
|
||||||
|
spacing: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Math.min(toolTip.maximumWidth - column.thumbnailSize,
|
||||||
|
Math.max(titleTextMetrics.width + row.__epsilon,
|
||||||
|
descriptionTextMetrics.width + row.__epsilon))
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: title
|
||||||
|
font: toolTip.font
|
||||||
|
color: StudioTheme.Values.themeToolTipText
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: titleTextMetrics
|
||||||
|
text: title.text
|
||||||
|
font: title.font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: description
|
||||||
|
width: column.width
|
||||||
|
font: toolTip.font
|
||||||
|
color: StudioTheme.Values.themeToolTipText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: descriptionTextMetrics
|
||||||
|
text: description.text
|
||||||
|
font: description.font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StudioControls.FilterComboBox {
|
StudioControls.FilterComboBox {
|
||||||
id: comboBox
|
id: comboBox
|
||||||
|
|
||||||
@@ -86,41 +180,14 @@ Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip {
|
ThumbnailToolTip {
|
||||||
id: toolTip
|
id: rootToolTip
|
||||||
visible: comboBox.hover && toolTip.text !== ""
|
|
||||||
|
visible: comboBox.hover && rootToolTip.text !== ""
|
||||||
text: root.backendValue?.valueToString ?? ""
|
text: root.backendValue?.valueToString ?? ""
|
||||||
delay: StudioTheme.Values.toolTipDelay
|
|
||||||
|
|
||||||
background: Rectangle {
|
checkerVisible: !root.isMesh(root.absoluteFilePath)
|
||||||
color: StudioTheme.Values.themeToolTipBackground
|
thumbnailSource: {
|
||||||
border.color: StudioTheme.Values.themeToolTipOutline
|
|
||||||
border.width: StudioTheme.Values.border
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
Item {
|
|
||||||
visible: thumbnail.status === Image.Ready
|
|
||||||
Layout.preferredWidth: 96
|
|
||||||
Layout.preferredHeight: 96
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: checker
|
|
||||||
visible: !root.isMesh(root.absoluteFilePath)
|
|
||||||
anchors.fill: parent
|
|
||||||
fillMode: Image.Tile
|
|
||||||
source: "images/checkers.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: thumbnail
|
|
||||||
asynchronous: true
|
|
||||||
height: 96
|
|
||||||
width: 96
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: {
|
|
||||||
if (root.isBuiltInPrimitive(root.absoluteFilePath))
|
if (root.isBuiltInPrimitive(root.absoluteFilePath))
|
||||||
return "image://qmldesigner_thumbnails/"
|
return "image://qmldesigner_thumbnails/"
|
||||||
+ root.absoluteFilePath.substring(1, root.absoluteFilePath.length)
|
+ root.absoluteFilePath.substring(1, root.absoluteFilePath.length)
|
||||||
@@ -131,26 +198,10 @@ Row {
|
|||||||
|
|
||||||
return root.absoluteFilePath
|
return root.absoluteFilePath
|
||||||
}
|
}
|
||||||
}
|
titleText: root.fileName(rootToolTip.text)
|
||||||
}
|
descriptionText: root.isBuiltInPrimitive(rootToolTip.text)
|
||||||
|
? qsTr("Built-in primitive")
|
||||||
ColumnLayout {
|
: rootToolTip.text
|
||||||
Text {
|
|
||||||
text: root.fileName(toolTip.text)
|
|
||||||
color: StudioTheme.Values.themeToolTipText
|
|
||||||
font: toolTip.font
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.isBuiltInPrimitive(toolTip.text) ? qsTr("Built-in primitive")
|
|
||||||
: toolTip.text
|
|
||||||
font: toolTip.font
|
|
||||||
color: StudioTheme.Values.themeToolTipText
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
@@ -213,43 +264,14 @@ Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip {
|
ThumbnailToolTip {
|
||||||
id: delegateToolTip
|
id: delegateToolTip
|
||||||
|
|
||||||
visible: delegateRoot.hovered
|
visible: delegateRoot.hovered
|
||||||
text: delegateRoot.relativeFilePath
|
text: delegateRoot.relativeFilePath
|
||||||
delay: StudioTheme.Values.toolTipDelay
|
|
||||||
|
|
||||||
background: Rectangle {
|
checkerVisible: !root.isMesh(delegateRoot.absoluteFilePath)
|
||||||
color: StudioTheme.Values.themeToolTipBackground
|
thumbnailSource: {
|
||||||
border.color: StudioTheme.Values.themeToolTipOutline
|
|
||||||
border.width: StudioTheme.Values.border
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
Item {
|
|
||||||
visible: delegateThumbnail.status === Image.Ready
|
|
||||||
Layout.preferredWidth: 96
|
|
||||||
Layout.preferredHeight: 96
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: delegateChecker
|
|
||||||
visible: !root.isMesh(delegateRoot.absoluteFilePath)
|
|
||||||
anchors.fill: parent
|
|
||||||
fillMode: Image.Tile
|
|
||||||
source: "images/checkers.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: delegateThumbnail
|
|
||||||
asynchronous: true
|
|
||||||
sourceSize.height: 96
|
|
||||||
sourceSize.width: 96
|
|
||||||
height: 96
|
|
||||||
width: 96
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: {
|
|
||||||
if (root.isBuiltInPrimitive(delegateRoot.name))
|
if (root.isBuiltInPrimitive(delegateRoot.name))
|
||||||
return "image://qmldesigner_thumbnails/"
|
return "image://qmldesigner_thumbnails/"
|
||||||
+ delegateRoot.name.substring(1, delegateRoot.name.length)
|
+ delegateRoot.name.substring(1, delegateRoot.name.length)
|
||||||
@@ -257,27 +279,10 @@ Row {
|
|||||||
|
|
||||||
return "image://qmldesigner_thumbnails/" + delegateRoot.absoluteFilePath
|
return "image://qmldesigner_thumbnails/" + delegateRoot.absoluteFilePath
|
||||||
}
|
}
|
||||||
}
|
titleText: delegateRoot.name
|
||||||
}
|
descriptionText: root.isBuiltInPrimitive(delegateToolTip.text)
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Text {
|
|
||||||
text: delegateRoot.name
|
|
||||||
color: StudioTheme.Values.themeToolTipText
|
|
||||||
font: delegateToolTip.font
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.isBuiltInPrimitive(delegateToolTip.text)
|
|
||||||
? qsTr("Built-in primitive")
|
? qsTr("Built-in primitive")
|
||||||
: delegateToolTip.text
|
: delegateToolTip.text
|
||||||
font: delegateToolTip.font
|
|
||||||
color: StudioTheme.Values.themeToolTipText
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,8 +427,10 @@ Row {
|
|||||||
if (root.defaultItems !== undefined) {
|
if (root.defaultItems !== undefined) {
|
||||||
for (var i = 0; i < root.defaultItems.length; ++i) {
|
for (var i = 0; i < root.defaultItems.length; ++i) {
|
||||||
comboBox.listModel.append({
|
comboBox.listModel.append({
|
||||||
absoluteFilePath: "",
|
absoluteFilePath: root.defaultPaths ? root.defaultPaths[i]
|
||||||
relativeFilePath: root.defaultItems[i],
|
: "",
|
||||||
|
relativeFilePath: root.defaultPaths ? root.defaultPaths[i]
|
||||||
|
: root.defaultItems[i],
|
||||||
name: root.defaultItems[i],
|
name: root.defaultItems[i],
|
||||||
group: 0
|
group: 0
|
||||||
})
|
})
|
||||||
@@ -454,6 +461,7 @@ Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDefaultItemsChanged: root.createModel()
|
onDefaultItemsChanged: root.createModel()
|
||||||
|
onDefaultPathsChanged: root.createModel()
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
root.createModel()
|
root.createModel()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (C) 2020 The Qt Company Ltd.
|
// Copyright (C) 2020 The Qt Company Ltd.
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls.Basic
|
||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
ScrollBar {
|
ScrollBar {
|
||||||
id: scrollBar
|
id: scrollBar
|
||||||
|
|||||||
@@ -25,19 +25,21 @@ QtObject {
|
|||||||
property alias flags: window.flags
|
property alias flags: window.flags
|
||||||
property alias visible: window.visible
|
property alias visible: window.visible
|
||||||
|
|
||||||
|
property int anchorGap: 10
|
||||||
property int edge: Qt.LeftEdge
|
property int edge: Qt.LeftEdge
|
||||||
property int actualEdge: root.edge
|
property int actualEdge: root.edge
|
||||||
property alias chevronVisible: chevron.visible
|
//property alias chevronVisible: chevron.visible
|
||||||
|
|
||||||
property rect __itemGlobal: Qt.rect(0, 0, 100, 100)
|
property rect __itemGlobal: Qt.rect(0, 0, 100, 100)
|
||||||
|
|
||||||
|
property bool keepOpen: false
|
||||||
|
|
||||||
signal closing(close: var)
|
signal closing(close: var)
|
||||||
|
|
||||||
function showGlobal()
|
function showGlobal() {
|
||||||
{
|
var pos = WindowManager.globalCursorPosition()
|
||||||
var pos = WindowManager.globalCursorPosition();
|
|
||||||
root.__itemGlobal = Qt.rect(pos.x, pos.y, 300, 20)
|
root.__itemGlobal = Qt.rect(pos.x, pos.y, 300, 20)
|
||||||
root.chevronVisible = false
|
//root.chevronVisible = false
|
||||||
root.layout()
|
root.layout()
|
||||||
window.show()
|
window.show()
|
||||||
window.raise()
|
window.raise()
|
||||||
@@ -46,7 +48,7 @@ QtObject {
|
|||||||
function show(target: Item) {
|
function show(target: Item) {
|
||||||
var originGlobal = target.mapToGlobal(0, 0)
|
var originGlobal = target.mapToGlobal(0, 0)
|
||||||
root.__itemGlobal = Qt.rect(originGlobal.x, originGlobal.y, target.width, target.height)
|
root.__itemGlobal = Qt.rect(originGlobal.x, originGlobal.y, target.width, target.height)
|
||||||
root.chevronVisible = true
|
//root.chevronVisible = true
|
||||||
root.layout()
|
root.layout()
|
||||||
window.show()
|
window.show()
|
||||||
window.raise()
|
window.raise()
|
||||||
@@ -57,11 +59,8 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function layout() {
|
function layout() {
|
||||||
// Setup
|
let position = Qt.point(root.__itemGlobal.x, root.__itemGlobal.y)
|
||||||
var screen = Qt.rect(0, //Screen.virtualX, // TODO
|
var screen = WindowManager.getScreenGeometry(position)
|
||||||
0, //Screen.virtualY, // TODO
|
|
||||||
Screen.desktopAvailableWidth,
|
|
||||||
Screen.desktopAvailableHeight)
|
|
||||||
|
|
||||||
// Collect region information
|
// Collect region information
|
||||||
let edges = window.getRegions(screen, root.__itemGlobal)
|
let edges = window.getRegions(screen, root.__itemGlobal)
|
||||||
@@ -78,8 +77,8 @@ QtObject {
|
|||||||
let anchor = edges[edge].anchor
|
let anchor = edges[edge].anchor
|
||||||
let popoverRect = window.popoverGeometry(edge, anchor, edges[edge].region)
|
let popoverRect = window.popoverGeometry(edge, anchor, edges[edge].region)
|
||||||
|
|
||||||
if (chevron.visible)
|
//if (chevron.visible)
|
||||||
chevron.layout(edge, popoverRect, anchor)
|
// chevron.layout(edge, popoverRect, anchor)
|
||||||
|
|
||||||
window.x = popoverRect.x
|
window.x = popoverRect.x
|
||||||
window.y = popoverRect.y
|
window.y = popoverRect.y
|
||||||
@@ -88,7 +87,7 @@ QtObject {
|
|||||||
property Window window: Window {
|
property Window window: Window {
|
||||||
id: window
|
id: window
|
||||||
|
|
||||||
property int margin: 20
|
property int margin: 0 //20
|
||||||
|
|
||||||
width: root.width + (2 * window.margin)
|
width: root.width + (2 * window.margin)
|
||||||
height: root.height + (2 * window.margin)
|
height: root.height + (2 * window.margin)
|
||||||
@@ -139,7 +138,7 @@ QtObject {
|
|||||||
return Qt.LeftEdge // Default
|
return Qt.LeftEdge // Default
|
||||||
}
|
}
|
||||||
|
|
||||||
function contains(a: rect, b: rect): boolean {
|
function contains(a: rect, b: rect): bool {
|
||||||
let halfSizeA = Qt.size(a.width * 0.5, a.height * 0.5)
|
let halfSizeA = Qt.size(a.width * 0.5, a.height * 0.5)
|
||||||
let halfSizeB = Qt.size(b.width * 0.5, b.height * 0.5)
|
let halfSizeB = Qt.size(b.width * 0.5, b.height * 0.5)
|
||||||
|
|
||||||
@@ -165,9 +164,18 @@ QtObject {
|
|||||||
var targetCenter = Qt.point(target.x + (target.width * 0.5),
|
var targetCenter = Qt.point(target.x + (target.width * 0.5),
|
||||||
target.y + (target.height * 0.5))
|
target.y + (target.height * 0.5))
|
||||||
|
|
||||||
|
// Just as a reminder why calculating custom right and bottom:
|
||||||
|
// > Note that for historical reasons this function returns top() + height() - 1;
|
||||||
|
// > use y() + height() to retrieve the true y-coordinate.
|
||||||
|
let sourceRight = source.x + source.width
|
||||||
|
let sourceBottom = source.y + source.height
|
||||||
|
|
||||||
// TOP
|
// TOP
|
||||||
let topAnchor = Qt.point(targetCenter.x, target.y)
|
let topAnchor = Qt.point(targetCenter.x, target.y - root.anchorGap)
|
||||||
let topRegion = Qt.rect(source.x, source.y, source.width, Math.max(0, topAnchor.y))
|
let topRegion = Qt.rect(source.x,
|
||||||
|
source.y,
|
||||||
|
source.width,
|
||||||
|
(topAnchor.y < source.top) ? 0 : Math.abs(topAnchor.y - source.top))
|
||||||
|
|
||||||
edges[Qt.TopEdge] = {
|
edges[Qt.TopEdge] = {
|
||||||
anchor: topAnchor,
|
anchor: topAnchor,
|
||||||
@@ -177,10 +185,10 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RIGHT
|
// RIGHT
|
||||||
let rightAnchor = Qt.point(target.x + target.width, targetCenter.y)
|
let rightAnchor = Qt.point(target.x + target.width + root.anchorGap, targetCenter.y)
|
||||||
let rightRegion = Qt.rect(rightAnchor.x,
|
let rightRegion = Qt.rect(rightAnchor.x,
|
||||||
source.y,
|
source.y,
|
||||||
Math.max(0, source.width - rightAnchor.x),
|
(rightAnchor.x > sourceRight) ? 0 : Math.abs(sourceRight - rightAnchor.x),
|
||||||
source.height)
|
source.height)
|
||||||
|
|
||||||
edges[Qt.RightEdge] = {
|
edges[Qt.RightEdge] = {
|
||||||
@@ -191,11 +199,11 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BOTTOM
|
// BOTTOM
|
||||||
let bottomAnchor = Qt.point(targetCenter.x, target.y + target.height)
|
let bottomAnchor = Qt.point(targetCenter.x, target.y + target.height + root.anchorGap)
|
||||||
let bottomRegion = Qt.rect(source.x,
|
let bottomRegion = Qt.rect(source.x,
|
||||||
bottomAnchor.y,
|
bottomAnchor.y,
|
||||||
source.width,
|
source.width,
|
||||||
Math.max(0, source.height - bottomAnchor.y))
|
(bottomAnchor.y > sourceBottom) ? 0 : Math.abs(sourceBottom - bottomAnchor.y))
|
||||||
|
|
||||||
edges[Qt.BottomEdge] = {
|
edges[Qt.BottomEdge] = {
|
||||||
anchor: bottomAnchor,
|
anchor: bottomAnchor,
|
||||||
@@ -205,8 +213,11 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LEFT
|
// LEFT
|
||||||
let leftAnchor = Qt.point(target.x, targetCenter.y)
|
let leftAnchor = Qt.point(target.x - root.anchorGap, targetCenter.y)
|
||||||
let leftRegion = Qt.rect(source.x, source.y, Math.max(0, leftAnchor.x), source.height)
|
let leftRegion = Qt.rect(source.x,
|
||||||
|
source.y,
|
||||||
|
(leftAnchor.x < source.left) ? 0 : Math.abs(leftAnchor.x - source.left),
|
||||||
|
source.height)
|
||||||
|
|
||||||
edges[Qt.LeftEdge] = {
|
edges[Qt.LeftEdge] = {
|
||||||
anchor: leftAnchor,
|
anchor: leftAnchor,
|
||||||
@@ -221,7 +232,9 @@ QtObject {
|
|||||||
function popoverGeometry(edge: int, anchor: point, region: rect) {
|
function popoverGeometry(edge: int, anchor: point, region: rect) {
|
||||||
if (edge === Qt.TopEdge) {
|
if (edge === Qt.TopEdge) {
|
||||||
let height = Math.min(window.height, region.height)
|
let height = Math.min(window.height, region.height)
|
||||||
return Qt.rect(Math.max(0, Math.min(anchor.x - (window.width * 0.5), region.width - window.width)),
|
return Qt.rect(Math.max(region.x,
|
||||||
|
Math.min(anchor.x - (window.width * 0.5),
|
||||||
|
region.x + region.width - window.width)),
|
||||||
anchor.y - height,
|
anchor.y - height,
|
||||||
Math.min(window.width, region.width),
|
Math.min(window.width, region.width),
|
||||||
height)
|
height)
|
||||||
@@ -230,14 +243,18 @@ QtObject {
|
|||||||
if (edge === Qt.RightEdge) {
|
if (edge === Qt.RightEdge) {
|
||||||
let width = Math.min(window.width, region.width)
|
let width = Math.min(window.width, region.width)
|
||||||
return Qt.rect(anchor.x,
|
return Qt.rect(anchor.x,
|
||||||
Math.max(0, Math.min(anchor.y - (window.height * 0.5), region.height - window.height)),
|
Math.max(region.y,
|
||||||
|
Math.min(anchor.y - (window.height * 0.5),
|
||||||
|
region.y + region.height - window.height)),
|
||||||
width,
|
width,
|
||||||
Math.min(window.height, region.height))
|
Math.min(window.height, region.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edge === Qt.BottomEdge) {
|
if (edge === Qt.BottomEdge) {
|
||||||
let height = Math.min(window.height, region.height)
|
let height = Math.min(window.height, region.height)
|
||||||
return Qt.rect(Math.max(0, Math.min(anchor.x - (window.width * 0.5), region.width - window.width)),
|
return Qt.rect(Math.max(region.x,
|
||||||
|
Math.min(anchor.x - (window.width * 0.5),
|
||||||
|
region.x + region.width - window.width)),
|
||||||
anchor.y,
|
anchor.y,
|
||||||
Math.min(window.width, region.width),
|
Math.min(window.width, region.width),
|
||||||
height)
|
height)
|
||||||
@@ -246,7 +263,9 @@ QtObject {
|
|||||||
if (edge === Qt.LeftEdge) {
|
if (edge === Qt.LeftEdge) {
|
||||||
let width = Math.min(window.width, region.width)
|
let width = Math.min(window.width, region.width)
|
||||||
return Qt.rect(anchor.x - width,
|
return Qt.rect(anchor.x - width,
|
||||||
Math.max(0, Math.min(anchor.y - (window.height * 0.5), region.height - window.height)),
|
Math.max(region.y,
|
||||||
|
Math.min(anchor.y - (window.height * 0.5),
|
||||||
|
region.y + region.height - window.height)),
|
||||||
width,
|
width,
|
||||||
Math.min(window.height, region.height))
|
Math.min(window.height, region.height))
|
||||||
}
|
}
|
||||||
@@ -270,6 +289,9 @@ QtObject {
|
|||||||
if (!focusWindow)
|
if (!focusWindow)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if (root.keepOpen)
|
||||||
|
return
|
||||||
|
|
||||||
if (focusWindow !== window && focusWindow.transientParent !== window)
|
if (focusWindow !== window && focusWindow.transientParent !== window)
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
@@ -304,7 +326,10 @@ QtObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
// The chevron will be reactivated when we fixed all the issues that where found during testing.
|
||||||
|
// * Potential Qt bug: black background instead of transparent border due to GPU selection on Windows
|
||||||
|
// * Ghost chevron on macOS after dragging the window
|
||||||
Shape {
|
Shape {
|
||||||
id: chevron
|
id: chevron
|
||||||
|
|
||||||
@@ -381,7 +406,7 @@ QtObject {
|
|||||||
PathLine { id: end; x: 0; y: 0 }
|
PathLine { id: end; x: 0; y: 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
Column {
|
Column {
|
||||||
id: column
|
id: column
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -392,6 +417,17 @@ QtObject {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: StudioTheme.Values.titleBarHeight
|
height: StudioTheme.Values.titleBarHeight
|
||||||
|
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
|
||||||
|
target: null
|
||||||
|
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||||
|
onActiveChanged: {
|
||||||
|
if (dragHandler.active)
|
||||||
|
window.startSystemMove() // QTBUG-102488
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: row
|
id: row
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Item {
|
|||||||
|
|
||||||
SectionLabel {
|
SectionLabel {
|
||||||
id: arrow
|
id: arrow
|
||||||
style: control.style
|
controlStyle: control.style
|
||||||
width: control.style.smallIconSize.width
|
width: control.style.smallIconSize.width
|
||||||
height: control.style.smallIconSize.height
|
height: control.style.smallIconSize.height
|
||||||
text: StudioTheme.Constants.sectionToggle
|
text: StudioTheme.Constants.sectionToggle
|
||||||
@@ -56,7 +56,7 @@ Item {
|
|||||||
|
|
||||||
SectionLabel {
|
SectionLabel {
|
||||||
id: label
|
id: label
|
||||||
style: control.style
|
controlStyle: control.style
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: control.style.text.idle
|
color: control.style.text.idle
|
||||||
x: 22
|
x: 22
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import StudioTheme 1.0 as StudioTheme
|
|||||||
T.Label {
|
T.Label {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
property StudioTheme.ControlStyle controlStyle: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
width: Math.max(Math.min(240, parent.width - 220), 90)
|
width: Math.max(Math.min(240, parent.width - 220), 90)
|
||||||
color: control.style.text.idle
|
color: control.controlStyle.text.idle
|
||||||
font.pixelSize: control.style.baseFontSize
|
font.pixelSize: control.controlStyle.baseFontSize
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
|
||||||
Layout.preferredWidth: width
|
Layout.preferredWidth: width
|
||||||
|
|||||||
@@ -79,300 +79,304 @@ QtObject {
|
|||||||
readonly property string centerHorizontal: "\u0060"
|
readonly property string centerHorizontal: "\u0060"
|
||||||
readonly property string centerVertical: "\u0061"
|
readonly property string centerVertical: "\u0061"
|
||||||
readonly property string cleanLogs_medium: "\u0062"
|
readonly property string cleanLogs_medium: "\u0062"
|
||||||
readonly property string closeCross: "\u0063"
|
readonly property string clearList_large: "\u0063"
|
||||||
readonly property string closeFile_large: "\u0064"
|
readonly property string clearList_medium: "\u0064"
|
||||||
readonly property string closeLink: "\u0065"
|
readonly property string closeCross: "\u0065"
|
||||||
readonly property string close_small: "\u0066"
|
readonly property string closeFile_large: "\u0066"
|
||||||
readonly property string code: "\u0067"
|
readonly property string closeLink: "\u0067"
|
||||||
readonly property string codeEditor_medium: "\u0068"
|
readonly property string close_small: "\u0068"
|
||||||
readonly property string codeview_medium: "\u0069"
|
readonly property string code: "\u0069"
|
||||||
readonly property string colorPopupClose: "\u006A"
|
readonly property string codeEditor_medium: "\u006A"
|
||||||
readonly property string colorSelection_medium: "\u006B"
|
readonly property string codeview_medium: "\u006B"
|
||||||
readonly property string columnsAndRows: "\u006C"
|
readonly property string colorPopupClose: "\u006C"
|
||||||
readonly property string comboBox_medium: "\u006D"
|
readonly property string colorSelection_medium: "\u006D"
|
||||||
readonly property string cone_medium: "\u006E"
|
readonly property string columnsAndRows: "\u006E"
|
||||||
readonly property string cone_small: "\u006F"
|
readonly property string comboBox_medium: "\u006F"
|
||||||
readonly property string connection_small: "\u0070"
|
readonly property string cone_medium: "\u0070"
|
||||||
readonly property string connections_medium: "\u0071"
|
readonly property string cone_small: "\u0071"
|
||||||
readonly property string copyLink: "\u0072"
|
readonly property string connection_small: "\u0072"
|
||||||
readonly property string copyStyle: "\u0073"
|
readonly property string connections_medium: "\u0073"
|
||||||
readonly property string copy_small: "\u0074"
|
readonly property string copyLink: "\u0074"
|
||||||
readonly property string cornerA: "\u0075"
|
readonly property string copyStyle: "\u0075"
|
||||||
readonly property string cornerB: "\u0076"
|
readonly property string copy_small: "\u0076"
|
||||||
readonly property string cornersAll: "\u0077"
|
readonly property string cornerA: "\u0077"
|
||||||
readonly property string createComponent_large: "\u0078"
|
readonly property string cornerB: "\u0078"
|
||||||
readonly property string createComponent_small: "\u0079"
|
readonly property string cornersAll: "\u0079"
|
||||||
readonly property string createObject_medium: "\u007A"
|
readonly property string createComponent_large: "\u007A"
|
||||||
readonly property string create_medium: "\u007B"
|
readonly property string createComponent_small: "\u007B"
|
||||||
readonly property string create_small: "\u007C"
|
readonly property string createObject_medium: "\u007C"
|
||||||
readonly property string cube_medium: "\u007D"
|
readonly property string create_medium: "\u007D"
|
||||||
readonly property string cube_small: "\u007E"
|
readonly property string create_small: "\u007E"
|
||||||
readonly property string curveDesigner: "\u007F"
|
readonly property string cube_medium: "\u007F"
|
||||||
readonly property string curveDesigner_medium: "\u0080"
|
readonly property string cube_small: "\u0080"
|
||||||
readonly property string curveEditor: "\u0081"
|
readonly property string curveDesigner: "\u0081"
|
||||||
readonly property string customMaterialEditor: "\u0082"
|
readonly property string curveDesigner_medium: "\u0082"
|
||||||
readonly property string cylinder_medium: "\u0083"
|
readonly property string curveEditor: "\u0083"
|
||||||
readonly property string cylinder_small: "\u0084"
|
readonly property string customMaterialEditor: "\u0084"
|
||||||
readonly property string decisionNode: "\u0085"
|
readonly property string cylinder_medium: "\u0085"
|
||||||
readonly property string deleteColumn: "\u0086"
|
readonly property string cylinder_small: "\u0086"
|
||||||
readonly property string deleteMaterial: "\u0087"
|
readonly property string decisionNode: "\u0087"
|
||||||
readonly property string deleteRow: "\u0088"
|
readonly property string deleteColumn: "\u0088"
|
||||||
readonly property string deleteTable: "\u0089"
|
readonly property string deleteMaterial: "\u0089"
|
||||||
readonly property string delete_medium: "\u008A"
|
readonly property string deleteRow: "\u008A"
|
||||||
readonly property string delete_small: "\u008B"
|
readonly property string deleteTable: "\u008B"
|
||||||
readonly property string deletecolumn_medium: "\u008C"
|
readonly property string delete_medium: "\u008C"
|
||||||
readonly property string deletepermanently_medium: "\u008D"
|
readonly property string delete_small: "\u008D"
|
||||||
readonly property string deleterow_medium: "\u008E"
|
readonly property string deletecolumn_medium: "\u008E"
|
||||||
readonly property string designMode_large: "\u008F"
|
readonly property string deletepermanently_medium: "\u008F"
|
||||||
readonly property string detach: "\u0090"
|
readonly property string deleterow_medium: "\u0090"
|
||||||
readonly property string directionalLight_small: "\u0091"
|
readonly property string designMode_large: "\u0091"
|
||||||
readonly property string distributeBottom: "\u0092"
|
readonly property string detach: "\u0092"
|
||||||
readonly property string distributeCenterHorizontal: "\u0093"
|
readonly property string directionalLight_small: "\u0093"
|
||||||
readonly property string distributeCenterVertical: "\u0094"
|
readonly property string distributeBottom: "\u0094"
|
||||||
readonly property string distributeLeft: "\u0095"
|
readonly property string distributeCenterHorizontal: "\u0095"
|
||||||
readonly property string distributeOriginBottomRight: "\u0096"
|
readonly property string distributeCenterVertical: "\u0096"
|
||||||
readonly property string distributeOriginCenter: "\u0097"
|
readonly property string distributeLeft: "\u0097"
|
||||||
readonly property string distributeOriginNone: "\u0098"
|
readonly property string distributeOriginBottomRight: "\u0098"
|
||||||
readonly property string distributeOriginTopLeft: "\u0099"
|
readonly property string distributeOriginCenter: "\u0099"
|
||||||
readonly property string distributeRight: "\u009A"
|
readonly property string distributeOriginNone: "\u009A"
|
||||||
readonly property string distributeSpacingHorizontal: "\u009B"
|
readonly property string distributeOriginTopLeft: "\u009B"
|
||||||
readonly property string distributeSpacingVertical: "\u009D"
|
readonly property string distributeRight: "\u009D"
|
||||||
readonly property string distributeTop: "\u009E"
|
readonly property string distributeSpacingHorizontal: "\u009E"
|
||||||
readonly property string download: "\u009F"
|
readonly property string distributeSpacingVertical: "\u009F"
|
||||||
readonly property string downloadUnavailable: "\u00A0"
|
readonly property string distributeTop: "\u00A0"
|
||||||
readonly property string downloadUpdate: "\u00A1"
|
readonly property string download: "\u00A1"
|
||||||
readonly property string downloaded: "\u00A2"
|
readonly property string downloadUnavailable: "\u00A2"
|
||||||
readonly property string dragmarks: "\u00A3"
|
readonly property string downloadUpdate: "\u00A3"
|
||||||
readonly property string duplicate_small: "\u00A4"
|
readonly property string downloaded: "\u00A4"
|
||||||
readonly property string edit: "\u00A5"
|
readonly property string dragmarks: "\u00A5"
|
||||||
readonly property string editComponent_large: "\u00A6"
|
readonly property string duplicate_small: "\u00A6"
|
||||||
readonly property string editComponent_small: "\u00A7"
|
readonly property string edit: "\u00A7"
|
||||||
readonly property string editLightOff_medium: "\u00A8"
|
readonly property string editComponent_large: "\u00A8"
|
||||||
readonly property string editLightOn_medium: "\u00A9"
|
readonly property string editComponent_small: "\u00A9"
|
||||||
readonly property string edit_medium: "\u00AA"
|
readonly property string editLightOff_medium: "\u00AA"
|
||||||
readonly property string edit_small: "\u00AB"
|
readonly property string editLightOn_medium: "\u00AB"
|
||||||
readonly property string effects: "\u00AC"
|
readonly property string edit_medium: "\u00AC"
|
||||||
readonly property string events_small: "\u00AE"
|
readonly property string edit_small: "\u00AE"
|
||||||
readonly property string export_medium: "\u00AF"
|
readonly property string effects: "\u00AF"
|
||||||
readonly property string eyeDropper: "\u00B0"
|
readonly property string events_small: "\u00B0"
|
||||||
readonly property string favorite: "\u00B1"
|
readonly property string export_medium: "\u00B1"
|
||||||
readonly property string fitAll_medium: "\u00B2"
|
readonly property string eyeDropper: "\u00B2"
|
||||||
readonly property string fitSelected_small: "\u00B3"
|
readonly property string favorite: "\u00B3"
|
||||||
readonly property string fitSelection_medium: "\u00B4"
|
readonly property string fitAll_medium: "\u00B4"
|
||||||
readonly property string fitToView_medium: "\u00B5"
|
readonly property string fitSelected_small: "\u00B5"
|
||||||
readonly property string flowAction: "\u00B6"
|
readonly property string fitSelection_medium: "\u00B6"
|
||||||
readonly property string flowTransition: "\u00B7"
|
readonly property string fitToView_medium: "\u00B7"
|
||||||
readonly property string fontStyleBold: "\u00B8"
|
readonly property string flowAction: "\u00B8"
|
||||||
readonly property string fontStyleItalic: "\u00B9"
|
readonly property string flowTransition: "\u00B9"
|
||||||
readonly property string fontStyleStrikethrough: "\u00BA"
|
readonly property string fontStyleBold: "\u00BA"
|
||||||
readonly property string fontStyleUnderline: "\u00BB"
|
readonly property string fontStyleItalic: "\u00BB"
|
||||||
readonly property string forward_medium: "\u00BC"
|
readonly property string fontStyleStrikethrough: "\u00BC"
|
||||||
readonly property string globalOrient_medium: "\u00BD"
|
readonly property string fontStyleUnderline: "\u00BD"
|
||||||
readonly property string gradient: "\u00BE"
|
readonly property string forward_medium: "\u00BE"
|
||||||
readonly property string gridView: "\u00BF"
|
readonly property string globalOrient_medium: "\u00BF"
|
||||||
readonly property string grid_medium: "\u00C0"
|
readonly property string gradient: "\u00C0"
|
||||||
readonly property string group_small: "\u00C1"
|
readonly property string gridView: "\u00C1"
|
||||||
readonly property string help: "\u00C2"
|
readonly property string grid_medium: "\u00C2"
|
||||||
readonly property string home_large: "\u00C3"
|
readonly property string group_small: "\u00C3"
|
||||||
readonly property string idAliasOff: "\u00C4"
|
readonly property string help: "\u00C4"
|
||||||
readonly property string idAliasOn: "\u00C5"
|
readonly property string home_large: "\u00C5"
|
||||||
readonly property string import_medium: "\u00C6"
|
readonly property string idAliasOff: "\u00C6"
|
||||||
readonly property string imported: "\u00C7"
|
readonly property string idAliasOn: "\u00C7"
|
||||||
readonly property string importedModels_small: "\u00C8"
|
readonly property string import_medium: "\u00C8"
|
||||||
readonly property string infinity: "\u00C9"
|
readonly property string imported: "\u00C9"
|
||||||
readonly property string invisible_medium: "\u00CA"
|
readonly property string importedModels_small: "\u00CA"
|
||||||
readonly property string invisible_small: "\u00CB"
|
readonly property string infinity: "\u00CB"
|
||||||
readonly property string jumpToCode_medium: "\u00CC"
|
readonly property string invisible_medium: "\u00CC"
|
||||||
readonly property string jumpToCode_small: "\u00CD"
|
readonly property string invisible_small: "\u00CD"
|
||||||
readonly property string keyframe: "\u00CE"
|
readonly property string jumpToCode_medium: "\u00CE"
|
||||||
readonly property string languageList_medium: "\u00CF"
|
readonly property string jumpToCode_small: "\u00CF"
|
||||||
readonly property string layouts_small: "\u00D0"
|
readonly property string keyframe: "\u00D0"
|
||||||
readonly property string lights_small: "\u00D1"
|
readonly property string languageList_medium: "\u00D1"
|
||||||
readonly property string linear_medium: "\u00D2"
|
readonly property string layouts_small: "\u00D2"
|
||||||
readonly property string linkTriangle: "\u00D3"
|
readonly property string lights_small: "\u00D3"
|
||||||
readonly property string linked: "\u00D4"
|
readonly property string linear_medium: "\u00D4"
|
||||||
readonly property string listView: "\u00D5"
|
readonly property string linkTriangle: "\u00D5"
|
||||||
readonly property string listView_medium: "\u00D6"
|
readonly property string linked: "\u00D6"
|
||||||
readonly property string list_medium: "\u00D7"
|
readonly property string listView: "\u00D7"
|
||||||
readonly property string localOrient_medium: "\u00D8"
|
readonly property string listView_medium: "\u00D8"
|
||||||
readonly property string lockOff: "\u00D9"
|
readonly property string list_medium: "\u00D9"
|
||||||
readonly property string lockOn: "\u00DA"
|
readonly property string localOrient_medium: "\u00DA"
|
||||||
readonly property string loopPlayback_medium: "\u00DB"
|
readonly property string lockOff: "\u00DB"
|
||||||
readonly property string materialBrowser_medium: "\u00DC"
|
readonly property string lockOn: "\u00DC"
|
||||||
readonly property string materialPreviewEnvironment: "\u00DD"
|
readonly property string loopPlayback_medium: "\u00DD"
|
||||||
readonly property string materialPreviewModel: "\u00DE"
|
readonly property string materialBrowser_medium: "\u00DE"
|
||||||
readonly property string material_medium: "\u00DF"
|
readonly property string materialPreviewEnvironment: "\u00DF"
|
||||||
readonly property string maxBar_small: "\u00E0"
|
readonly property string materialPreviewModel: "\u00E0"
|
||||||
readonly property string mergeCells: "\u00E1"
|
readonly property string material_medium: "\u00E1"
|
||||||
readonly property string merge_small: "\u00E2"
|
readonly property string maxBar_small: "\u00E2"
|
||||||
readonly property string minus: "\u00E3"
|
readonly property string mergeCells: "\u00E3"
|
||||||
readonly property string mirror: "\u00E4"
|
readonly property string merge_small: "\u00E4"
|
||||||
readonly property string more_medium: "\u00E5"
|
readonly property string minus: "\u00E5"
|
||||||
readonly property string mouseArea_small: "\u00E6"
|
readonly property string mirror: "\u00E6"
|
||||||
readonly property string moveDown_medium: "\u00E7"
|
readonly property string more_medium: "\u00E7"
|
||||||
readonly property string moveInwards_medium: "\u00E8"
|
readonly property string mouseArea_small: "\u00E8"
|
||||||
readonly property string moveUp_medium: "\u00E9"
|
readonly property string moveDown_medium: "\u00E9"
|
||||||
readonly property string moveUpwards_medium: "\u00EA"
|
readonly property string moveInwards_medium: "\u00EA"
|
||||||
readonly property string move_medium: "\u00EB"
|
readonly property string moveUp_medium: "\u00EB"
|
||||||
readonly property string newMaterial: "\u00EC"
|
readonly property string moveUpwards_medium: "\u00EC"
|
||||||
readonly property string nextFile_large: "\u00ED"
|
readonly property string move_medium: "\u00ED"
|
||||||
readonly property string normalBar_small: "\u00EE"
|
readonly property string newMaterial: "\u00EE"
|
||||||
readonly property string openLink: "\u00EF"
|
readonly property string nextFile_large: "\u00EF"
|
||||||
readonly property string openMaterialBrowser: "\u00F0"
|
readonly property string normalBar_small: "\u00F0"
|
||||||
readonly property string orientation: "\u00F1"
|
readonly property string openLink: "\u00F1"
|
||||||
readonly property string orthCam_medium: "\u00F2"
|
readonly property string openMaterialBrowser: "\u00F2"
|
||||||
readonly property string orthCam_small: "\u00F3"
|
readonly property string orientation: "\u00F3"
|
||||||
readonly property string paddingEdge: "\u00F4"
|
readonly property string orthCam_medium: "\u00F4"
|
||||||
readonly property string paddingFrame: "\u00F5"
|
readonly property string orthCam_small: "\u00F5"
|
||||||
readonly property string particleAnimation_medium: "\u00F6"
|
readonly property string paddingEdge: "\u00F6"
|
||||||
readonly property string pasteStyle: "\u00F7"
|
readonly property string paddingFrame: "\u00F7"
|
||||||
readonly property string paste_small: "\u00F8"
|
readonly property string particleAnimation_medium: "\u00F8"
|
||||||
readonly property string pause: "\u00F9"
|
readonly property string pasteStyle: "\u00F9"
|
||||||
readonly property string pause_medium: "\u00FA"
|
readonly property string paste_small: "\u00FA"
|
||||||
readonly property string perspectiveCam_medium: "\u00FB"
|
readonly property string pause: "\u00FB"
|
||||||
readonly property string perspectiveCam_small: "\u00FC"
|
readonly property string pause_medium: "\u00FC"
|
||||||
readonly property string pin: "\u00FD"
|
readonly property string perspectiveCam_medium: "\u00FD"
|
||||||
readonly property string plane_medium: "\u00FE"
|
readonly property string perspectiveCam_small: "\u00FE"
|
||||||
readonly property string plane_small: "\u00FF"
|
readonly property string pin: "\u00FF"
|
||||||
readonly property string play: "\u0100"
|
readonly property string plane_medium: "\u0100"
|
||||||
readonly property string playFill_medium: "\u0101"
|
readonly property string plane_small: "\u0101"
|
||||||
readonly property string playOutline_medium: "\u0102"
|
readonly property string play: "\u0102"
|
||||||
readonly property string plus: "\u0103"
|
readonly property string playFill_medium: "\u0103"
|
||||||
readonly property string pointLight_small: "\u0104"
|
readonly property string playOutline_medium: "\u0104"
|
||||||
readonly property string positioners_small: "\u0105"
|
readonly property string plus: "\u0105"
|
||||||
readonly property string previewEnv_medium: "\u0106"
|
readonly property string pointLight_small: "\u0106"
|
||||||
readonly property string previousFile_large: "\u0107"
|
readonly property string positioners_small: "\u0107"
|
||||||
readonly property string promote: "\u0108"
|
readonly property string previewEnv_medium: "\u0108"
|
||||||
readonly property string properties_medium: "\u0109"
|
readonly property string previousFile_large: "\u0109"
|
||||||
readonly property string readOnly: "\u010A"
|
readonly property string promote: "\u010A"
|
||||||
readonly property string recent_medium: "\u010B"
|
readonly property string properties_medium: "\u010B"
|
||||||
readonly property string recordFill_medium: "\u010C"
|
readonly property string readOnly: "\u010C"
|
||||||
readonly property string recordOutline_medium: "\u010D"
|
readonly property string recent_medium: "\u010D"
|
||||||
readonly property string redo: "\u010E"
|
readonly property string recordFill_medium: "\u010E"
|
||||||
readonly property string reload_medium: "\u010F"
|
readonly property string recordOutline_medium: "\u010F"
|
||||||
readonly property string remove_medium: "\u0110"
|
readonly property string redo: "\u0110"
|
||||||
readonly property string remove_small: "\u0111"
|
readonly property string reload_medium: "\u0111"
|
||||||
readonly property string rename_small: "\u0112"
|
readonly property string remove_medium: "\u0112"
|
||||||
readonly property string replace_small: "\u0113"
|
readonly property string remove_small: "\u0113"
|
||||||
readonly property string resetView_small: "\u0114"
|
readonly property string rename_small: "\u0114"
|
||||||
readonly property string restartParticles_medium: "\u0115"
|
readonly property string replace_small: "\u0115"
|
||||||
readonly property string reverseOrder_medium: "\u0116"
|
readonly property string resetView_small: "\u0116"
|
||||||
readonly property string roatate_medium: "\u0117"
|
readonly property string restartParticles_medium: "\u0117"
|
||||||
readonly property string rotationFill: "\u0118"
|
readonly property string reverseOrder_medium: "\u0118"
|
||||||
readonly property string rotationOutline: "\u0119"
|
readonly property string roatate_medium: "\u0119"
|
||||||
readonly property string runProjFill_large: "\u011A"
|
readonly property string rotationFill: "\u011A"
|
||||||
readonly property string runProjOutline_large: "\u011B"
|
readonly property string rotationOutline: "\u011B"
|
||||||
readonly property string s_anchors: "\u011C"
|
readonly property string runProjFill_large: "\u011C"
|
||||||
readonly property string s_annotations: "\u011D"
|
readonly property string runProjOutline_large: "\u011D"
|
||||||
readonly property string s_arrange: "\u011E"
|
readonly property string s_anchors: "\u011E"
|
||||||
readonly property string s_boundingBox: "\u011F"
|
readonly property string s_annotations: "\u011F"
|
||||||
readonly property string s_component: "\u0120"
|
readonly property string s_arrange: "\u0120"
|
||||||
readonly property string s_connections: "\u0121"
|
readonly property string s_boundingBox: "\u0121"
|
||||||
readonly property string s_edit: "\u0122"
|
readonly property string s_component: "\u0122"
|
||||||
readonly property string s_enterComponent: "\u0123"
|
readonly property string s_connections: "\u0123"
|
||||||
readonly property string s_eventList: "\u0124"
|
readonly property string s_edit: "\u0124"
|
||||||
readonly property string s_group: "\u0125"
|
readonly property string s_enterComponent: "\u0125"
|
||||||
readonly property string s_layouts: "\u0126"
|
readonly property string s_eventList: "\u0126"
|
||||||
readonly property string s_merging: "\u0127"
|
readonly property string s_group: "\u0127"
|
||||||
readonly property string s_mouseArea: "\u0128"
|
readonly property string s_layouts: "\u0128"
|
||||||
readonly property string s_positioners: "\u0129"
|
readonly property string s_merging: "\u0129"
|
||||||
readonly property string s_selection: "\u012A"
|
readonly property string s_mouseArea: "\u012A"
|
||||||
readonly property string s_snapping: "\u012B"
|
readonly property string s_positioners: "\u012B"
|
||||||
readonly property string s_timeline: "\u012C"
|
readonly property string s_selection: "\u012C"
|
||||||
readonly property string s_visibility: "\u012D"
|
readonly property string s_snapping: "\u012D"
|
||||||
readonly property string saveLogs_medium: "\u012E"
|
readonly property string s_timeline: "\u012E"
|
||||||
readonly property string scale_medium: "\u012F"
|
readonly property string s_visibility: "\u012F"
|
||||||
readonly property string search: "\u0130"
|
readonly property string saveAs_medium: "\u0130"
|
||||||
readonly property string search_small: "\u0131"
|
readonly property string saveLogs_medium: "\u0131"
|
||||||
readonly property string sectionToggle: "\u0132"
|
readonly property string save_medium: "\u0132"
|
||||||
readonly property string selectFill_medium: "\u0133"
|
readonly property string scale_medium: "\u0133"
|
||||||
readonly property string selectOutline_medium: "\u0134"
|
readonly property string search: "\u0134"
|
||||||
readonly property string selectParent_small: "\u0135"
|
readonly property string search_small: "\u0135"
|
||||||
readonly property string selection_small: "\u0136"
|
readonly property string sectionToggle: "\u0136"
|
||||||
readonly property string settings_medium: "\u0137"
|
readonly property string selectFill_medium: "\u0137"
|
||||||
readonly property string signal_small: "\u0138"
|
readonly property string selectOutline_medium: "\u0138"
|
||||||
readonly property string snapping_conf_medium: "\u0139"
|
readonly property string selectParent_small: "\u0139"
|
||||||
readonly property string snapping_medium: "\u013A"
|
readonly property string selection_small: "\u013A"
|
||||||
readonly property string snapping_small: "\u013B"
|
readonly property string settings_medium: "\u013B"
|
||||||
readonly property string sortascending_medium: "\u013C"
|
readonly property string signal_small: "\u013C"
|
||||||
readonly property string sortdescending_medium: "\u013D"
|
readonly property string snapping_conf_medium: "\u013D"
|
||||||
readonly property string sphere_medium: "\u013E"
|
readonly property string snapping_medium: "\u013E"
|
||||||
readonly property string sphere_small: "\u013F"
|
readonly property string snapping_small: "\u013F"
|
||||||
readonly property string splitColumns: "\u0140"
|
readonly property string sortascending_medium: "\u0140"
|
||||||
readonly property string splitRows: "\u0141"
|
readonly property string sortdescending_medium: "\u0141"
|
||||||
readonly property string splitScreen_medium: "\u0142"
|
readonly property string sphere_medium: "\u0142"
|
||||||
readonly property string spotLight_small: "\u0143"
|
readonly property string sphere_small: "\u0143"
|
||||||
readonly property string stackedContainer_small: "\u0144"
|
readonly property string splitColumns: "\u0144"
|
||||||
readonly property string startNode: "\u0145"
|
readonly property string splitRows: "\u0145"
|
||||||
readonly property string step_medium: "\u0146"
|
readonly property string splitScreen_medium: "\u0146"
|
||||||
readonly property string stop_medium: "\u0147"
|
readonly property string spotLight_small: "\u0147"
|
||||||
readonly property string tableView_medium: "\u0148"
|
readonly property string stackedContainer_small: "\u0148"
|
||||||
readonly property string testIcon: "\u0149"
|
readonly property string startNode: "\u0149"
|
||||||
readonly property string textAlignBottom: "\u014A"
|
readonly property string step_medium: "\u014A"
|
||||||
readonly property string textAlignCenter: "\u014B"
|
readonly property string stop_medium: "\u014B"
|
||||||
readonly property string textAlignJustified: "\u014C"
|
readonly property string tableView_medium: "\u014C"
|
||||||
readonly property string textAlignLeft: "\u014D"
|
readonly property string testIcon: "\u014D"
|
||||||
readonly property string textAlignMiddle: "\u014E"
|
readonly property string textAlignBottom: "\u014E"
|
||||||
readonly property string textAlignRight: "\u014F"
|
readonly property string textAlignCenter: "\u014F"
|
||||||
readonly property string textAlignTop: "\u0150"
|
readonly property string textAlignJustified: "\u0150"
|
||||||
readonly property string textBulletList: "\u0151"
|
readonly property string textAlignLeft: "\u0151"
|
||||||
readonly property string textFullJustification: "\u0152"
|
readonly property string textAlignMiddle: "\u0152"
|
||||||
readonly property string textNumberedList: "\u0153"
|
readonly property string textAlignRight: "\u0153"
|
||||||
readonly property string textures_medium: "\u0154"
|
readonly property string textAlignTop: "\u0154"
|
||||||
readonly property string tickIcon: "\u0155"
|
readonly property string textBulletList: "\u0155"
|
||||||
readonly property string tickMark_small: "\u0156"
|
readonly property string textFullJustification: "\u0156"
|
||||||
readonly property string timeline_small: "\u0157"
|
readonly property string textNumberedList: "\u0157"
|
||||||
readonly property string toEndFrame_medium: "\u0158"
|
readonly property string textures_medium: "\u0158"
|
||||||
readonly property string toNextFrame_medium: "\u0159"
|
readonly property string tickIcon: "\u0159"
|
||||||
readonly property string toPrevFrame_medium: "\u015A"
|
readonly property string tickMark_small: "\u015A"
|
||||||
readonly property string toStartFrame_medium: "\u015B"
|
readonly property string timeline_small: "\u015B"
|
||||||
readonly property string topToolbar_annotations: "\u015C"
|
readonly property string toEndFrame_medium: "\u015C"
|
||||||
readonly property string topToolbar_closeFile: "\u015D"
|
readonly property string toNextFrame_medium: "\u015D"
|
||||||
readonly property string topToolbar_designMode: "\u015E"
|
readonly property string toPrevFrame_medium: "\u015E"
|
||||||
readonly property string topToolbar_enterComponent: "\u015F"
|
readonly property string toStartFrame_medium: "\u015F"
|
||||||
readonly property string topToolbar_home: "\u0160"
|
readonly property string topToolbar_annotations: "\u0160"
|
||||||
readonly property string topToolbar_makeComponent: "\u0161"
|
readonly property string topToolbar_closeFile: "\u0161"
|
||||||
readonly property string topToolbar_navFile: "\u0162"
|
readonly property string topToolbar_designMode: "\u0162"
|
||||||
readonly property string topToolbar_runProject: "\u0163"
|
readonly property string topToolbar_enterComponent: "\u0163"
|
||||||
readonly property string translationCreateFiles: "\u0164"
|
readonly property string topToolbar_home: "\u0164"
|
||||||
readonly property string translationCreateReport: "\u0165"
|
readonly property string topToolbar_makeComponent: "\u0165"
|
||||||
readonly property string translationExport: "\u0166"
|
readonly property string topToolbar_navFile: "\u0166"
|
||||||
readonly property string translationImport: "\u0167"
|
readonly property string topToolbar_runProject: "\u0167"
|
||||||
readonly property string translationSelectLanguages: "\u0168"
|
readonly property string translationCreateFiles: "\u0168"
|
||||||
readonly property string translationTest: "\u0169"
|
readonly property string translationCreateReport: "\u0169"
|
||||||
readonly property string transparent: "\u016A"
|
readonly property string translationExport: "\u016A"
|
||||||
readonly property string triState: "\u016B"
|
readonly property string translationImport: "\u016B"
|
||||||
readonly property string triangleArcA: "\u016C"
|
readonly property string translationSelectLanguages: "\u016C"
|
||||||
readonly property string triangleArcB: "\u016D"
|
readonly property string translationTest: "\u016D"
|
||||||
readonly property string triangleCornerA: "\u016E"
|
readonly property string transparent: "\u016E"
|
||||||
readonly property string triangleCornerB: "\u016F"
|
readonly property string triState: "\u016F"
|
||||||
readonly property string unLinked: "\u0170"
|
readonly property string triangleArcA: "\u0170"
|
||||||
readonly property string undo: "\u0171"
|
readonly property string triangleArcB: "\u0171"
|
||||||
readonly property string unify_medium: "\u0172"
|
readonly property string triangleCornerA: "\u0172"
|
||||||
readonly property string unpin: "\u0173"
|
readonly property string triangleCornerB: "\u0173"
|
||||||
readonly property string upDownIcon: "\u0174"
|
readonly property string unLinked: "\u0174"
|
||||||
readonly property string upDownSquare2: "\u0175"
|
readonly property string undo: "\u0175"
|
||||||
readonly property string updateAvailable_medium: "\u0176"
|
readonly property string unify_medium: "\u0176"
|
||||||
readonly property string updateContent_medium: "\u0177"
|
readonly property string unpin: "\u0177"
|
||||||
readonly property string visibilityOff: "\u0178"
|
readonly property string upDownIcon: "\u0178"
|
||||||
readonly property string visibilityOn: "\u0179"
|
readonly property string upDownSquare2: "\u0179"
|
||||||
readonly property string visible_medium: "\u017A"
|
readonly property string updateAvailable_medium: "\u017A"
|
||||||
readonly property string visible_small: "\u017B"
|
readonly property string updateContent_medium: "\u017B"
|
||||||
readonly property string warning_medium: "\u017C"
|
readonly property string visibilityOff: "\u017C"
|
||||||
readonly property string wildcard: "\u017D"
|
readonly property string visibilityOn: "\u017D"
|
||||||
readonly property string wizardsAutomotive: "\u017E"
|
readonly property string visible_medium: "\u017E"
|
||||||
readonly property string wizardsDesktop: "\u017F"
|
readonly property string visible_small: "\u017F"
|
||||||
readonly property string wizardsGeneric: "\u0180"
|
readonly property string warning_medium: "\u0180"
|
||||||
readonly property string wizardsMcuEmpty: "\u0181"
|
readonly property string wildcard: "\u0181"
|
||||||
readonly property string wizardsMcuGraph: "\u0182"
|
readonly property string wizardsAutomotive: "\u0182"
|
||||||
readonly property string wizardsMobile: "\u0183"
|
readonly property string wizardsDesktop: "\u0183"
|
||||||
readonly property string wizardsUnknown: "\u0184"
|
readonly property string wizardsGeneric: "\u0184"
|
||||||
readonly property string zoomAll: "\u0185"
|
readonly property string wizardsMcuEmpty: "\u0185"
|
||||||
readonly property string zoomIn: "\u0186"
|
readonly property string wizardsMcuGraph: "\u0186"
|
||||||
readonly property string zoomIn_medium: "\u0187"
|
readonly property string wizardsMobile: "\u0187"
|
||||||
readonly property string zoomOut: "\u0188"
|
readonly property string wizardsUnknown: "\u0188"
|
||||||
readonly property string zoomOut_medium: "\u0189"
|
readonly property string zoomAll: "\u0189"
|
||||||
readonly property string zoomSelection: "\u018A"
|
readonly property string zoomIn: "\u018A"
|
||||||
|
readonly property string zoomIn_medium: "\u018B"
|
||||||
|
readonly property string zoomOut: "\u018C"
|
||||||
|
readonly property string zoomOut_medium: "\u018D"
|
||||||
|
readonly property string zoomSelection: "\u018E"
|
||||||
|
|
||||||
readonly property font iconFont: Qt.font({
|
readonly property font iconFont: Qt.font({
|
||||||
"family": controlIcons.name,
|
"family": controlIcons.name,
|
||||||
|
|||||||
@@ -242,6 +242,11 @@ QtObject {
|
|||||||
property real dialogButtonSpacing: 10
|
property real dialogButtonSpacing: 10
|
||||||
property real dialogButtonPadding: 4
|
property real dialogButtonPadding: 4
|
||||||
|
|
||||||
|
// Collection Editor
|
||||||
|
property real collectionItemTextSideMargin: 10
|
||||||
|
property real collectionItemTextMargin: 5
|
||||||
|
property real collectionItemTextPadding: 5
|
||||||
|
|
||||||
// NEW NEW NEW
|
// NEW NEW NEW
|
||||||
readonly property int flowMargin: 7
|
readonly property int flowMargin: 7
|
||||||
readonly property int flowSpacing: 7 // Odd so cursor has a center location
|
readonly property int flowSpacing: 7 // Odd so cursor has a center location
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ Item {
|
|||||||
model: backend.kits
|
model: backend.kits
|
||||||
onActivated: backend.setCurrentKit(kits.currentIndex)
|
onActivated: backend.setCurrentKit(kits.currentIndex)
|
||||||
openUpwards: true
|
openUpwards: true
|
||||||
enabled: (backend.isInDesignMode || (backend.isInEditMode && backend.projectOpened)) && backend.isQt6
|
enabled: (backend.isInDesignMode || (backend.isInEditMode && backend.projectOpened))
|
||||||
|
&& backend.isQt6 && !backend.isMCUs
|
||||||
property int kitIndex: backend.currentKit
|
property int kitIndex: backend.currentKit
|
||||||
onKitIndexChanged: kits.currentIndex = backend.currentKit
|
onKitIndexChanged: kits.currentIndex = backend.currentKit
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,6 +382,22 @@
|
|||||||
{
|
{
|
||||||
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,6 +395,22 @@
|
|||||||
{
|
{
|
||||||
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ Project {
|
|||||||
filter: "*.qsb"
|
filter: "*.qsb"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Files {
|
||||||
|
filter: "*.json"
|
||||||
|
}
|
||||||
|
|
||||||
Files {
|
Files {
|
||||||
filter: "*.mesh"
|
filter: "*.mesh"
|
||||||
directory: "asset_imports"
|
directory: "asset_imports"
|
||||||
@@ -110,7 +114,7 @@ Project {
|
|||||||
/* Required for deployment */
|
/* Required for deployment */
|
||||||
targetDirectory: "/opt/%{ProjectName}"
|
targetDirectory: "/opt/%{ProjectName}"
|
||||||
|
|
||||||
qdsVersion: "4.4"
|
qdsVersion: "4.3"
|
||||||
|
|
||||||
quickVersion: "%{QtQuickVersion}"
|
quickVersion: "%{QtQuickVersion}"
|
||||||
|
|
||||||
|
|||||||
@@ -382,6 +382,22 @@
|
|||||||
{
|
{
|
||||||
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,6 +341,22 @@
|
|||||||
{
|
{
|
||||||
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,6 +343,22 @@
|
|||||||
{
|
{
|
||||||
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,6 +343,22 @@
|
|||||||
{
|
{
|
||||||
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
"source": "../shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,12 @@ qt6_add_qml_module(%{ImportModuleName}
|
|||||||
RESOURCE_PREFIX "/qt/qml"
|
RESOURCE_PREFIX "/qt/qml"
|
||||||
QML_FILES
|
QML_FILES
|
||||||
Constants.qml
|
Constants.qml
|
||||||
|
DataStore.qml
|
||||||
DirectoryFontLoader.qml
|
DirectoryFontLoader.qml
|
||||||
EventListModel.qml
|
EventListModel.qml
|
||||||
EventListSimulator.qml
|
EventListSimulator.qml
|
||||||
|
JsonData.qml
|
||||||
|
RESOURCES
|
||||||
|
data.json
|
||||||
|
models.json
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
import QtQuick 6.5
|
||||||
|
import QtQuick.Studio.Utils 1.0
|
||||||
|
|
||||||
|
JsonListModel {
|
||||||
|
id: models
|
||||||
|
source: Qt.resolvedUrl("models.json")
|
||||||
|
|
||||||
|
property ChildListModel book: ChildListModel {
|
||||||
|
modelName: "book"
|
||||||
|
}
|
||||||
|
|
||||||
|
property JsonData backend: JsonData {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Studio.Utils 1.0
|
||||||
|
|
||||||
|
JsonBackend {
|
||||||
|
property string name: "someName"
|
||||||
|
property int number: 1
|
||||||
|
source: Qt.resolvedUrl("data.json")
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Christen Anderson",
|
||||||
|
"number": "+3455641"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
Module %{ImportModuleName}
|
Module %{ImportModuleName}
|
||||||
|
singleton DataStore 1.0 DataStore.qml
|
||||||
singleton Constants 1.0 Constants.qml
|
singleton Constants 1.0 Constants.qml
|
||||||
EventListSimulator 1.0 EventListSimulator.qml
|
EventListSimulator 1.0 EventListSimulator.qml
|
||||||
EventListModel 1.0 EventListModel.qml
|
EventListModel 1.0 EventListModel.qml
|
||||||
DirectoryFontLoader 1.0 DirectoryFontLoader.qml
|
DirectoryFontLoader 1.0 DirectoryFontLoader.qml
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"book": [
|
||||||
|
{
|
||||||
|
"author": "Nigel Rees",
|
||||||
|
"category": "reference",
|
||||||
|
"price": 8.95,
|
||||||
|
"title": "Sayings of the Century"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Evelyn Waugh",
|
||||||
|
"category": "fiction",
|
||||||
|
"price": 12.99,
|
||||||
|
"title": "Sword of Honor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Herman Melville",
|
||||||
|
"category": "fiction",
|
||||||
|
"isbn": "0-553-21311-3",
|
||||||
|
"price": 8.99,
|
||||||
|
"title": "Moby Dick"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "J. R. R. Tolkien",
|
||||||
|
"category": "fiction",
|
||||||
|
"isbn": "0-395-19395-8",
|
||||||
|
"price": 22.99,
|
||||||
|
"title": "The Lord of the Rings"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
singleton Constants 1.0 Constants.qml
|
singleton Constants 1.0 Constants.qml
|
||||||
|
singleton DataStore 1.0 DataStore.qml
|
||||||
EventListModel 1.0 EventListModel.qml
|
EventListModel 1.0 EventListModel.qml
|
||||||
EventListSimulator 1.0 EventListSimulator.qml
|
EventListSimulator 1.0 EventListSimulator.qml
|
||||||
DirectoryFontLoader 1.0 DirectoryFontLoader.qml
|
DirectoryFontLoader 1.0 DirectoryFontLoader.qml
|
||||||
|
|||||||
@@ -261,6 +261,22 @@
|
|||||||
"source": "%{QdsWizardPath}/shared-plugin/name/designer/plugin.metainfo",
|
"source": "%{QdsWizardPath}/shared-plugin/name/designer/plugin.metainfo",
|
||||||
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo",
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo",
|
||||||
"condition": "%{QdsProjectStyle}"
|
"condition": "%{QdsProjectStyle}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/JsonData.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/JsonData.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/DataStore.qml.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DataStore.qml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/models.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/models.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "../shared-plugin/name/data.json.tpl",
|
||||||
|
"target": "%{ProjectDirectory}/imports/%{ImportModuleName}/data.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ enum eTitleBarButton {
|
|||||||
TitleBarButtonTabsMenu,
|
TitleBarButtonTabsMenu,
|
||||||
TitleBarButtonUndock,
|
TitleBarButtonUndock,
|
||||||
TitleBarButtonClose,
|
TitleBarButtonClose,
|
||||||
TitleBarButtonAutoHide
|
TitleBarButtonAutoHide,
|
||||||
|
TitleBarButtonMinimize
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,6 +89,7 @@ enum eIcon {
|
|||||||
DockAreaMenuIcon, //!< DockAreaMenuIcon
|
DockAreaMenuIcon, //!< DockAreaMenuIcon
|
||||||
DockAreaUndockIcon, //!< DockAreaUndockIcon
|
DockAreaUndockIcon, //!< DockAreaUndockIcon
|
||||||
DockAreaCloseIcon, //!< DockAreaCloseIcon
|
DockAreaCloseIcon, //!< DockAreaCloseIcon
|
||||||
|
DockAreaMinimizeIcon,
|
||||||
FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon
|
FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon
|
||||||
FloatingWidgetNormalIcon, //!< FloatingWidgetNormalIcon
|
FloatingWidgetNormalIcon, //!< FloatingWidgetNormalIcon
|
||||||
FloatingWidgetMaximizeIcon, //!< FloatingWidgetMaximizeIcon
|
FloatingWidgetMaximizeIcon, //!< FloatingWidgetMaximizeIcon
|
||||||
@@ -161,8 +163,8 @@ bool isSideBarArea(DockWidgetArea area);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for the parent widget of the given type. Returns the parent widget of the given
|
* Searches for the parent widget of the given type. Returns the parent widget of the given
|
||||||
* widget or 0 if the widget is not child of any widget of type T.
|
* widget or nullptr if the widget is not child of any widget of type T.
|
||||||
* It is not safe to use this function in in DockWidget because only the current dock widget has a
|
* It is not safe to use this function in DockWidget, because only the current dock widget has a
|
||||||
* parent. All dock widgets that are not the current dock widget in a dock area have no parent.
|
* parent. All dock widgets that are not the current dock widget in a dock area have no parent.
|
||||||
*/
|
*/
|
||||||
template<class T>
|
template<class T>
|
||||||
@@ -171,12 +173,12 @@ T findParent(const QWidget *widget)
|
|||||||
QWidget *parentWidget = widget->parentWidget();
|
QWidget *parentWidget = widget->parentWidget();
|
||||||
while (parentWidget) {
|
while (parentWidget) {
|
||||||
T parentImpl = qobject_cast<T>(parentWidget);
|
T parentImpl = qobject_cast<T>(parentWidget);
|
||||||
if (parentImpl) {
|
if (parentImpl)
|
||||||
return parentImpl;
|
return parentImpl;
|
||||||
}
|
|
||||||
parentWidget = parentWidget->parentWidget();
|
parentWidget = parentWidget->parentWidget();
|
||||||
}
|
}
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,10 +46,11 @@ public:
|
|||||||
QPointer<TitleBarButton> m_autoHideButton;
|
QPointer<TitleBarButton> m_autoHideButton;
|
||||||
QPointer<TitleBarButton> m_undockButton;
|
QPointer<TitleBarButton> m_undockButton;
|
||||||
QPointer<TitleBarButton> m_closeButton;
|
QPointer<TitleBarButton> m_closeButton;
|
||||||
|
QPointer<TitleBarButton> m_minimizeButton;
|
||||||
QBoxLayout *m_layout = nullptr;
|
QBoxLayout *m_layout = nullptr;
|
||||||
DockAreaWidget *m_dockArea = nullptr;
|
DockAreaWidget *m_dockArea = nullptr;
|
||||||
DockAreaTabBar *m_tabBar = nullptr;
|
DockAreaTabBar *m_tabBar = nullptr;
|
||||||
ElidingLabel *m_autoHideTitleLabel;
|
ElidingLabel *m_autoHideTitleLabel = nullptr;
|
||||||
bool m_menuOutdated = true;
|
bool m_menuOutdated = true;
|
||||||
QMenu *m_tabsMenu;
|
QMenu *m_tabsMenu;
|
||||||
QList<TitleBarButtonType *> m_dockWidgetActionsButtons;
|
QList<TitleBarButtonType *> m_dockWidgetActionsButtons;
|
||||||
@@ -198,6 +199,23 @@ void DockAreaTitleBarPrivate::createButtons()
|
|||||||
q,
|
q,
|
||||||
&DockAreaTitleBar::onAutoHideButtonClicked);
|
&DockAreaTitleBar::onAutoHideButtonClicked);
|
||||||
|
|
||||||
|
// Minimize button
|
||||||
|
m_minimizeButton = new TitleBarButton(
|
||||||
|
testAutoHideConfigFlag(DockManager::AutoHideHasMinimizeButton));
|
||||||
|
m_minimizeButton->setObjectName("dockAreaMinimizeButton");
|
||||||
|
//m_minimizeButton->setAutoRaise(true);
|
||||||
|
m_minimizeButton->setVisible(false);
|
||||||
|
internal::setButtonIcon(m_minimizeButton,
|
||||||
|
QStyle::SP_TitleBarMinButton,
|
||||||
|
ADS::DockAreaMinimizeIcon);
|
||||||
|
internal::setToolTip(m_minimizeButton, QObject::tr("Minimize"));
|
||||||
|
m_minimizeButton->setSizePolicy(sizePolicy);
|
||||||
|
m_layout->addWidget(m_minimizeButton, 0);
|
||||||
|
QObject::connect(m_minimizeButton,
|
||||||
|
&QToolButton::clicked,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::minimizeAutoHideContainer);
|
||||||
|
|
||||||
// Close button
|
// Close button
|
||||||
m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton));
|
m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton));
|
||||||
m_closeButton->setObjectName("dockAreaCloseButton");
|
m_closeButton->setObjectName("dockAreaCloseButton");
|
||||||
@@ -224,7 +242,10 @@ void DockAreaTitleBarPrivate::createAutoHideTitleLabel()
|
|||||||
{
|
{
|
||||||
m_autoHideTitleLabel = new ElidingLabel("");
|
m_autoHideTitleLabel = new ElidingLabel("");
|
||||||
m_autoHideTitleLabel->setObjectName("autoHideTitleLabel");
|
m_autoHideTitleLabel->setObjectName("autoHideTitleLabel");
|
||||||
m_layout->addWidget(m_autoHideTitleLabel);
|
// At position 0 is the tab bar - insert behind tab bar
|
||||||
|
m_layout->insertWidget(1, m_autoHideTitleLabel);
|
||||||
|
m_autoHideTitleLabel->setVisible(false); // Default hidden
|
||||||
|
m_layout->insertWidget(2, new SpacerWidget(q));
|
||||||
}
|
}
|
||||||
void DockAreaTitleBarPrivate::createTabBar()
|
void DockAreaTitleBarPrivate::createTabBar()
|
||||||
{
|
{
|
||||||
@@ -366,10 +387,8 @@ DockAreaTitleBar::DockAreaTitleBar(DockAreaWidget *parent)
|
|||||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||||
|
|
||||||
d->createTabBar();
|
d->createTabBar();
|
||||||
d->createAutoHideTitleLabel();
|
|
||||||
d->m_autoHideTitleLabel->setVisible(false); // Default hidden
|
|
||||||
d->m_layout->addWidget(new SpacerWidget(this));
|
|
||||||
d->createButtons();
|
d->createButtons();
|
||||||
|
d->createAutoHideTitleLabel();
|
||||||
setFocusPolicy(Qt::NoFocus);
|
setFocusPolicy(Qt::NoFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +467,18 @@ void DockAreaTitleBar::onCloseButtonClicked()
|
|||||||
d->m_dockArea->closeArea();
|
d->m_dockArea->closeArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::onAutoHideCloseActionTriggered()
|
||||||
|
{
|
||||||
|
d->m_dockArea->closeArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::minimizeAutoHideContainer()
|
||||||
|
{
|
||||||
|
auto autoHideContainer = d->m_dockArea->autoHideDockContainer();
|
||||||
|
if (autoHideContainer)
|
||||||
|
autoHideContainer->collapseView(true);
|
||||||
|
}
|
||||||
|
|
||||||
void DockAreaTitleBar::onUndockButtonClicked()
|
void DockAreaTitleBar::onUndockButtonClicked()
|
||||||
{
|
{
|
||||||
if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
|
if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||||
@@ -533,6 +564,8 @@ TitleBarButton *DockAreaTitleBar::button(eTitleBarButton which) const
|
|||||||
return d->m_closeButton;
|
return d->m_closeButton;
|
||||||
case TitleBarButtonAutoHide:
|
case TitleBarButtonAutoHide:
|
||||||
return d->m_autoHideButton;
|
return d->m_autoHideButton;
|
||||||
|
case TitleBarButtonMinimize:
|
||||||
|
return d->m_minimizeButton;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -676,12 +709,28 @@ void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
}
|
}
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
}
|
}
|
||||||
QAction *closeAction = menu.addAction(isAutoHide ? Tr::tr("Close") : Tr::tr("Close Group"));
|
|
||||||
|
if (isAutoHide) {
|
||||||
|
QAction *minimizeAction = menu.addAction(Tr::tr("Minimize"));
|
||||||
|
minimizeAction->connect(minimizeAction,
|
||||||
|
&QAction::triggered,
|
||||||
|
this,
|
||||||
|
&DockAreaTitleBar::minimizeAutoHideContainer);
|
||||||
|
|
||||||
|
QAction *closeAction = menu.addAction(Tr::tr("Close"));
|
||||||
|
closeAction->connect(closeAction,
|
||||||
|
&QAction::triggered,
|
||||||
|
this,
|
||||||
|
&DockAreaTitleBar::onAutoHideCloseActionTriggered);
|
||||||
|
closeAction->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
|
||||||
|
} else {
|
||||||
|
QAction *closeAction = menu.addAction(Tr::tr("Close Group"));
|
||||||
closeAction->connect(closeAction,
|
closeAction->connect(closeAction,
|
||||||
&QAction::triggered,
|
&QAction::triggered,
|
||||||
this,
|
this,
|
||||||
&DockAreaTitleBar::onCloseButtonClicked);
|
&DockAreaTitleBar::onCloseButtonClicked);
|
||||||
closeAction->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
|
closeAction->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAutoHide && !isTopLevelArea) {
|
if (!isAutoHide && !isTopLevelArea) {
|
||||||
QAction *closeOthersAction = menu.addAction(Tr::tr("Close Other Groups"));
|
QAction *closeOthersAction = menu.addAction(Tr::tr("Close Other Groups"));
|
||||||
@@ -718,8 +767,11 @@ QString DockAreaTitleBar::titleBarButtonToolTip(eTitleBarButton button) const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TitleBarButtonClose:
|
case TitleBarButtonClose:
|
||||||
if (d->m_dockArea->isAutoHide())
|
if (d->m_dockArea->isAutoHide()) {
|
||||||
return Tr::tr("Close");
|
bool minimize = DockManager::testAutoHideConfigFlag(
|
||||||
|
DockManager::AutoHideCloseButtonCollapsesDock);
|
||||||
|
return minimize ? Tr::tr("Minimize") : Tr::tr("Close");
|
||||||
|
}
|
||||||
|
|
||||||
if (DockManager::testConfigFlag(DockManager::DockAreaCloseButtonClosesTab))
|
if (DockManager::testConfigFlag(DockManager::DockAreaCloseButtonClosesTab))
|
||||||
return Tr::tr("Close Active Tab");
|
return Tr::tr("Close Active Tab");
|
||||||
@@ -748,4 +800,11 @@ void DockAreaTitleBar::setAreaFloating()
|
|||||||
d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
|
d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::showAutoHideControls(bool show)
|
||||||
|
{
|
||||||
|
d->m_tabBar->setVisible(!show); // Auto hide toolbar never has tabs
|
||||||
|
d->m_minimizeButton->setVisible(show);
|
||||||
|
d->m_autoHideTitleLabel->setVisible(show);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ADS
|
} // namespace ADS
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ private:
|
|||||||
|
|
||||||
void onTabsMenuAboutToShow();
|
void onTabsMenuAboutToShow();
|
||||||
void onCloseButtonClicked();
|
void onCloseButtonClicked();
|
||||||
|
void onAutoHideCloseActionTriggered();
|
||||||
|
void minimizeAutoHideContainer();
|
||||||
void onUndockButtonClicked();
|
void onUndockButtonClicked();
|
||||||
void onTabsMenuActionTriggered(QAction *action);
|
void onTabsMenuActionTriggered(QAction *action);
|
||||||
void onCurrentTabChanged(int index);
|
void onCurrentTabChanged(int index);
|
||||||
@@ -190,6 +192,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setAreaFloating();
|
void setAreaFloating();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function, to create all the required auto hide controls
|
||||||
|
*/
|
||||||
|
void showAutoHideControls(bool show);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* This signal is emitted if a tab in the tab bar is clicked by the user
|
* This signal is emitted if a tab in the tab bar is clicked by the user
|
||||||
|
|||||||
@@ -295,13 +295,20 @@ void DockAreaWidgetPrivate::updateTitleBarButtonVisibility(bool isTopLevel)
|
|||||||
if (!container)
|
if (!container)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isTopLevel) {
|
bool isAutoHide = q->isAutoHide();
|
||||||
|
if (isAutoHide) {
|
||||||
|
bool showCloseButton = DockManager::autoHideConfigFlags().testFlag(
|
||||||
|
DockManager::AutoHideHasCloseButton);
|
||||||
|
m_titleBar->button(TitleBarButtonClose)->setVisible(showCloseButton);
|
||||||
|
m_titleBar->button(TitleBarButtonAutoHide)->setVisible(true);
|
||||||
|
m_titleBar->button(TitleBarButtonUndock)->setVisible(false);
|
||||||
|
m_titleBar->button(TitleBarButtonTabsMenu)->setVisible(false);
|
||||||
|
} else if (isTopLevel) {
|
||||||
m_titleBar->button(TitleBarButtonClose)->setVisible(!container->isFloating());
|
m_titleBar->button(TitleBarButtonClose)->setVisible(!container->isFloating());
|
||||||
m_titleBar->button(TitleBarButtonAutoHide)->setVisible(!container->isFloating());
|
m_titleBar->button(TitleBarButtonAutoHide)->setVisible(!container->isFloating());
|
||||||
// Undock and tabs should never show when auto hidden
|
// Undock and tabs should never show when auto hidden
|
||||||
m_titleBar->button(TitleBarButtonUndock)
|
m_titleBar->button(TitleBarButtonUndock)->setVisible(!container->isFloating());
|
||||||
->setVisible(!container->isFloating() && !q->isAutoHide());
|
m_titleBar->button(TitleBarButtonTabsMenu)->setVisible(true);
|
||||||
m_titleBar->button(TitleBarButtonTabsMenu)->setVisible(!q->isAutoHide());
|
|
||||||
} else {
|
} else {
|
||||||
m_titleBar->button(TitleBarButtonClose)->setVisible(true);
|
m_titleBar->button(TitleBarButtonClose)->setVisible(true);
|
||||||
m_titleBar->button(TitleBarButtonAutoHide)->setVisible(true);
|
m_titleBar->button(TitleBarButtonAutoHide)->setVisible(true);
|
||||||
@@ -650,10 +657,7 @@ void DockAreaWidget::updateTitleBarVisibility()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isAutoHideFeatureEnabled()) {
|
if (isAutoHideFeatureEnabled()) {
|
||||||
auto tabBar = d->m_titleBar->tabBar();
|
d->m_titleBar->showAutoHideControls(autoHide);
|
||||||
tabBar->setVisible(!autoHide); // Never show tab bar when auto hidden
|
|
||||||
// Always show when auto hidden, never otherwise
|
|
||||||
d->m_titleBar->autoHideTitleLabel()->setVisible(autoHide);
|
|
||||||
updateTitleBarButtonVisibility(container->topLevelDockArea() == this);
|
updateTitleBarButtonVisibility(container->topLevelDockArea() == this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -484,9 +484,10 @@ void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floating
|
|||||||
|
|
||||||
if (!targetAreaSplitter) {
|
if (!targetAreaSplitter) {
|
||||||
auto splitter = createSplitter(insertParam.orientation());
|
auto splitter = createSplitter(insertParam.orientation());
|
||||||
m_layout->replaceWidget(targetArea, splitter);
|
QLayoutItem *layoutItem = m_layout->replaceWidget(targetArea, splitter);
|
||||||
splitter->addWidget(targetArea);
|
splitter->addWidget(targetArea);
|
||||||
targetAreaSplitter = splitter;
|
targetAreaSplitter = splitter;
|
||||||
|
delete layoutItem;
|
||||||
}
|
}
|
||||||
int areaIndex = targetAreaSplitter->indexOf(targetArea);
|
int areaIndex = targetAreaSplitter->indexOf(targetArea);
|
||||||
auto floatingSplitter = floatingContainer->rootSplitter();
|
auto floatingSplitter = floatingContainer->rootSplitter();
|
||||||
@@ -1532,10 +1533,11 @@ bool DockContainerWidget::restoreState(DockingStateReader &stateReader, bool tes
|
|||||||
if (!newRootSplitter)
|
if (!newRootSplitter)
|
||||||
newRootSplitter = d->createSplitter(Qt::Horizontal);
|
newRootSplitter = d->createSplitter(Qt::Horizontal);
|
||||||
|
|
||||||
d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter);
|
QLayoutItem *layoutItem = d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter);
|
||||||
auto oldRoot = d->m_rootSplitter;
|
auto oldRoot = d->m_rootSplitter;
|
||||||
d->m_rootSplitter = qobject_cast<DockSplitter *>(newRootSplitter);
|
d->m_rootSplitter = qobject_cast<DockSplitter *>(newRootSplitter);
|
||||||
oldRoot->deleteLater();
|
oldRoot->deleteLater();
|
||||||
|
delete layoutItem;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ public:
|
|||||||
DockWidget *m_centralWidget = nullptr;
|
DockWidget *m_centralWidget = nullptr;
|
||||||
bool m_isLeavingMinimized = false;
|
bool m_isLeavingMinimized = false;
|
||||||
|
|
||||||
|
Qt::ToolButtonStyle m_toolBarStyleDocked = Qt::ToolButtonIconOnly;
|
||||||
|
Qt::ToolButtonStyle m_toolBarStyleFloating = Qt::ToolButtonTextUnderIcon;
|
||||||
|
QSize m_toolBarIconSizeDocked = QSize(16, 16);
|
||||||
|
QSize m_toolBarIconSizeFloating = QSize(24, 24);
|
||||||
|
|
||||||
QString m_workspacePresetsPath;
|
QString m_workspacePresetsPath;
|
||||||
QList<Workspace> m_workspaces;
|
QList<Workspace> m_workspaces;
|
||||||
Workspace m_workspace;
|
Workspace m_workspace;
|
||||||
@@ -94,6 +99,7 @@ public:
|
|||||||
|
|
||||||
QtcSettings *m_settings = nullptr;
|
QtcSettings *m_settings = nullptr;
|
||||||
bool m_modeChangeState = false;
|
bool m_modeChangeState = false;
|
||||||
|
bool m_wasShown = false;
|
||||||
bool m_workspaceOrderDirty = false;
|
bool m_workspaceOrderDirty = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -364,8 +370,10 @@ DockManager::DockManager(QWidget *parent)
|
|||||||
|
|
||||||
DockManager::~DockManager()
|
DockManager::~DockManager()
|
||||||
{
|
{
|
||||||
|
if (d->m_wasShown) {
|
||||||
emit aboutToUnloadWorkspace(d->m_workspace.fileName());
|
emit aboutToUnloadWorkspace(d->m_workspace.fileName());
|
||||||
save();
|
save();
|
||||||
|
}
|
||||||
saveStartupWorkspace();
|
saveStartupWorkspace();
|
||||||
saveLockWorkspace();
|
saveLockWorkspace();
|
||||||
|
|
||||||
@@ -766,6 +774,38 @@ QString DockManager::floatingContainersTitle()
|
|||||||
return g_floatingContainersTitle;
|
return g_floatingContainersTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockManager::setDockWidgetToolBarStyle(Qt::ToolButtonStyle style, DockWidget::eState state)
|
||||||
|
{
|
||||||
|
if (DockWidget::StateFloating == state)
|
||||||
|
d->m_toolBarStyleFloating = style;
|
||||||
|
else
|
||||||
|
d->m_toolBarStyleDocked = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ToolButtonStyle DockManager::dockWidgetToolBarStyle(DockWidget::eState state) const
|
||||||
|
{
|
||||||
|
if (DockWidget::StateFloating == state)
|
||||||
|
return d->m_toolBarStyleFloating;
|
||||||
|
else
|
||||||
|
return d->m_toolBarStyleDocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::setDockWidgetToolBarIconSize(const QSize &iconSize, DockWidget::eState state)
|
||||||
|
{
|
||||||
|
if (DockWidget::StateFloating == state)
|
||||||
|
d->m_toolBarIconSizeFloating = iconSize;
|
||||||
|
else
|
||||||
|
d->m_toolBarIconSizeDocked = iconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize DockManager::dockWidgetToolBarIconSize(DockWidget::eState state) const
|
||||||
|
{
|
||||||
|
if (DockWidget::StateFloating == state)
|
||||||
|
return d->m_toolBarIconSizeFloating;
|
||||||
|
else
|
||||||
|
return d->m_toolBarIconSizeDocked;
|
||||||
|
}
|
||||||
|
|
||||||
DockWidget *DockManager::centralWidget() const
|
DockWidget *DockManager::centralWidget() const
|
||||||
{
|
{
|
||||||
return d->m_centralWidget;
|
return d->m_centralWidget;
|
||||||
@@ -1324,6 +1364,11 @@ bool DockManager::isModeChangeState() const
|
|||||||
return d->m_modeChangeState;
|
return d->m_modeChangeState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockManager::aboutToShow()
|
||||||
|
{
|
||||||
|
d->m_wasShown = true;
|
||||||
|
}
|
||||||
|
|
||||||
expected_str<QString> DockManager::importWorkspace(const QString &filePath)
|
expected_str<QString> DockManager::importWorkspace(const QString &filePath)
|
||||||
{
|
{
|
||||||
qCInfo(adsLog) << "Import workspace" << filePath;
|
qCInfo(adsLog) << "Import workspace" << filePath;
|
||||||
|
|||||||
@@ -186,10 +186,13 @@ public:
|
|||||||
= 0x20, ///< show the auto hide window on mouse over tab and hide it if mouse leaves auto hide container
|
= 0x20, ///< show the auto hide window on mouse over tab and hide it if mouse leaves auto hide container
|
||||||
AutoHideCloseButtonCollapsesDock
|
AutoHideCloseButtonCollapsesDock
|
||||||
= 0x40, ///< Close button of an auto hide container collapses the dock instead of hiding it completely
|
= 0x40, ///< Close button of an auto hide container collapses the dock instead of hiding it completely
|
||||||
|
AutoHideHasCloseButton
|
||||||
|
= 0x80, //< If the flag is set an auto hide title bar has a close button
|
||||||
|
AutoHideHasMinimizeButton
|
||||||
|
= 0x100, ///< if this flag is set, the auto hide title bar has a minimize button to collapse the dock widget
|
||||||
|
|
||||||
DefaultAutoHideConfig
|
DefaultAutoHideConfig = AutoHideFeatureEnabled | DockAreaHasAutoHideButton
|
||||||
= AutoHideFeatureEnabled | DockAreaHasAutoHideButton
|
| AutoHideCloseButtonCollapsesDock | AutoHideHasCloseButton
|
||||||
| AutoHideCloseButtonCollapsesDock ///< the default configuration for left and right side bars
|
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(AutoHideFlags, eAutoHideFlag)
|
Q_DECLARE_FLAGS(AutoHideFlags, eAutoHideFlag)
|
||||||
|
|
||||||
@@ -452,6 +455,31 @@ public:
|
|||||||
*/
|
*/
|
||||||
static QString floatingContainersTitle();
|
static QString floatingContainersTitle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the tool button style for the given dock widget state. It is possible to
|
||||||
|
* switch the tool button style depending on the state. If a dock widget is floating, then here
|
||||||
|
* are more space and it is possible to select a style that requires more space like
|
||||||
|
* Qt::ToolButtonTextUnderIcon. For the docked state Qt::ToolButtonIconOnly might be better.
|
||||||
|
*/
|
||||||
|
void setDockWidgetToolBarStyle(Qt::ToolButtonStyle style, DockWidget::eState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tool button style for the given docking state. \see setToolBarStyle()
|
||||||
|
*/
|
||||||
|
Qt::ToolButtonStyle dockWidgetToolBarStyle(DockWidget::eState state) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the tool button icon size for the given state. If a dock widget is
|
||||||
|
* floating, there is more space and increasing the icon size is possible. For docked widgets,
|
||||||
|
* small icon sizes, eg. 16 x 16 might be better.
|
||||||
|
*/
|
||||||
|
void setDockWidgetToolBarIconSize(const QSize &iconSize, DockWidget::eState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon size for a given docking state. \see setToolBarIconSize()
|
||||||
|
*/
|
||||||
|
QSize dockWidgetToolBarIconSize(DockWidget::eState state) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function returns managers central widget or nullptr if no central widget is set.
|
* This function returns managers central widget or nullptr if no central widget is set.
|
||||||
*/
|
*/
|
||||||
@@ -733,6 +761,13 @@ public:
|
|||||||
static QString readDisplayName(const Utils::FilePath &filePath);
|
static QString readDisplayName(const Utils::FilePath &filePath);
|
||||||
static bool writeDisplayName(const Utils::FilePath &filePath, const QString &displayName);
|
static bool writeDisplayName(const Utils::FilePath &filePath, const QString &displayName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to limit saving of workspaces to only when they were actually presented ones,
|
||||||
|
* otherwise it could lead to distorted workspace due to the correct windows sizes not being
|
||||||
|
* set when never presented/rendered.
|
||||||
|
*/
|
||||||
|
void aboutToShow();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void aboutToUnloadWorkspace(QString fileName);
|
void aboutToUnloadWorkspace(QString fileName);
|
||||||
void aboutToLoadWorkspace(QString fileName);
|
void aboutToLoadWorkspace(QString fileName);
|
||||||
|
|||||||
@@ -50,10 +50,9 @@ bool DockSplitter::hasVisibleContent() const
|
|||||||
{
|
{
|
||||||
// TODO Cache or precalculate this to speed up
|
// TODO Cache or precalculate this to speed up
|
||||||
for (int i = 0; i < count(); ++i) {
|
for (int i = 0; i < count(); ++i) {
|
||||||
if (!widget(i)->isHidden()) {
|
if (!widget(i)->isHidden())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public:
|
|||||||
= DockWidget::MinimumSizeHintFromDockWidget;
|
= DockWidget::MinimumSizeHintFromDockWidget;
|
||||||
WidgetFactory *m_factory = nullptr;
|
WidgetFactory *m_factory = nullptr;
|
||||||
QPointer<AutoHideTab> m_sideTabWidget;
|
QPointer<AutoHideTab> m_sideTabWidget;
|
||||||
|
DockWidget::eToolBarStyleSource m_toolBarStyleSource = DockWidget::ToolBarStyleFromDockManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private data constructor
|
* Private data constructor
|
||||||
@@ -106,6 +107,11 @@ public:
|
|||||||
* Creates the content widget with the registered widget factory and returns true on success.
|
* Creates the content widget with the registered widget factory and returns true on success.
|
||||||
*/
|
*/
|
||||||
bool createWidgetFromFactory();
|
bool createWidgetFromFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the dock manager toolbar style and icon size for the different states
|
||||||
|
*/
|
||||||
|
void setToolBarStyleFromDockManager();
|
||||||
}; // class DockWidgetPrivate
|
}; // class DockWidgetPrivate
|
||||||
|
|
||||||
DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
|
DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
|
||||||
@@ -246,6 +252,19 @@ bool DockWidgetPrivate::createWidgetFromFactory()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockWidgetPrivate::setToolBarStyleFromDockManager()
|
||||||
|
{
|
||||||
|
if (!m_dockManager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto state = DockWidget::StateDocked;
|
||||||
|
q->setToolBarIconSize(m_dockManager->dockWidgetToolBarIconSize(state), state);
|
||||||
|
q->setToolBarStyle(m_dockManager->dockWidgetToolBarStyle(state), state);
|
||||||
|
state = DockWidget::StateFloating;
|
||||||
|
q->setToolBarIconSize(m_dockManager->dockWidgetToolBarIconSize(state), state);
|
||||||
|
q->setToolBarStyle(m_dockManager->dockWidgetToolBarStyle(state), state);
|
||||||
|
}
|
||||||
|
|
||||||
DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
|
DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
|
||||||
: QFrame(parent)
|
: QFrame(parent)
|
||||||
, d(new DockWidgetPrivate(this))
|
, d(new DockWidgetPrivate(this))
|
||||||
@@ -290,8 +309,11 @@ void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode)
|
|||||||
auto scrollAreaWidget = qobject_cast<QAbstractScrollArea *>(widget);
|
auto scrollAreaWidget = qobject_cast<QAbstractScrollArea *>(widget);
|
||||||
if (scrollAreaWidget || ForceNoScrollArea == insertMode) {
|
if (scrollAreaWidget || ForceNoScrollArea == insertMode) {
|
||||||
d->m_layout->addWidget(widget);
|
d->m_layout->addWidget(widget);
|
||||||
if (scrollAreaWidget && scrollAreaWidget->viewport())
|
if (scrollAreaWidget) {
|
||||||
|
if (scrollAreaWidget->viewport())
|
||||||
scrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
|
scrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
|
||||||
|
scrollAreaWidget->setProperty("focused", isFocused());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
d->setupScrollArea();
|
d->setupScrollArea();
|
||||||
d->m_scrollArea->setWidget(widget);
|
d->m_scrollArea->setWidget(widget);
|
||||||
@@ -381,6 +403,12 @@ DockManager *DockWidget::dockManager() const
|
|||||||
void DockWidget::setDockManager(DockManager *dockManager)
|
void DockWidget::setDockManager(DockManager *dockManager)
|
||||||
{
|
{
|
||||||
d->m_dockManager = dockManager;
|
d->m_dockManager = dockManager;
|
||||||
|
|
||||||
|
if (!dockManager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ToolBarStyleFromDockManager == d->m_toolBarStyleSource)
|
||||||
|
d->setToolBarStyleFromDockManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
DockContainerWidget *DockWidget::dockContainer() const
|
DockContainerWidget *DockWidget::dockContainer() const
|
||||||
@@ -457,6 +485,11 @@ void DockWidget::setFocused(bool focused)
|
|||||||
if (d->m_scrollArea)
|
if (d->m_scrollArea)
|
||||||
d->m_scrollArea->setProperty("focused", focused);
|
d->m_scrollArea->setProperty("focused", focused);
|
||||||
|
|
||||||
|
QList<QAbstractScrollArea *> scrollAreas = d->m_widget->findChildren<QAbstractScrollArea *>(QString(),
|
||||||
|
Qt::FindDirectChildrenOnly);
|
||||||
|
for (QAbstractScrollArea *scrollArea : scrollAreas)
|
||||||
|
scrollArea->setProperty("focused", focused);
|
||||||
|
|
||||||
const QString customObjectName = QString("__mainSrollView");
|
const QString customObjectName = QString("__mainSrollView");
|
||||||
|
|
||||||
QList<QQuickWidget *> quickWidgets = d->m_widget->findChildren<QQuickWidget *>();
|
QList<QQuickWidget *> quickWidgets = d->m_widget->findChildren<QQuickWidget *>();
|
||||||
@@ -491,6 +524,18 @@ QAction *DockWidget::toggleViewAction() const
|
|||||||
return d->m_toggleViewAction;
|
return d->m_toggleViewAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToggleViewAction(QAction *action)
|
||||||
|
{
|
||||||
|
if (!action)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->m_toggleViewAction->setParent(nullptr);
|
||||||
|
delete d->m_toggleViewAction;
|
||||||
|
d->m_toggleViewAction = action;
|
||||||
|
d->m_toggleViewAction->setParent(this);
|
||||||
|
connect(d->m_toggleViewAction, &QAction::triggered, this, &DockWidget::toggleView);
|
||||||
|
}
|
||||||
|
|
||||||
void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
|
void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
|
||||||
{
|
{
|
||||||
if (ActionModeToggle == mode) {
|
if (ActionModeToggle == mode) {
|
||||||
@@ -693,6 +738,18 @@ void DockWidget::setToolBar(QToolBar *toolBar)
|
|||||||
setToolbarFloatingStyle(isFloating());
|
setToolbarFloatingStyle(isFloating());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToolBarStyleSource(eToolBarStyleSource source)
|
||||||
|
{
|
||||||
|
d->m_toolBarStyleSource = source;
|
||||||
|
if (ToolBarStyleFromDockManager == d->m_toolBarStyleSource)
|
||||||
|
d->setToolBarStyleFromDockManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget::eToolBarStyleSource DockWidget::toolBarStyleSource() const
|
||||||
|
{
|
||||||
|
return d->m_toolBarStyleSource;
|
||||||
|
}
|
||||||
|
|
||||||
void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
|
void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
|
||||||
{
|
{
|
||||||
if (StateFloating == state)
|
if (StateFloating == state)
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ public:
|
|||||||
|
|
||||||
enum eState { StateHidden, StateDocked, StateFloating };
|
enum eState { StateHidden, StateDocked, StateFloating };
|
||||||
|
|
||||||
|
enum eToolBarStyleSource { ToolBarStyleFromDockManager, ToolBarStyleFromDockWidget };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the widget for the dock widget to widget.
|
* Sets the widget for the dock widget to widget.
|
||||||
* The InsertMode defines how the widget is inserted into the dock widget.
|
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||||
@@ -385,6 +387,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
QAction *toggleViewAction() const;
|
QAction *toggleViewAction() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use provided action to be the default toggle view action for this dock widget.
|
||||||
|
* This dock widget now owns the action.
|
||||||
|
*/
|
||||||
|
void setToggleViewAction(QAction *action);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the behavior of the toggle view action.
|
* Configures the behavior of the toggle view action.
|
||||||
* \see eToggleViewActionMode for a detailed description
|
* \see eToggleViewActionMode for a detailed description
|
||||||
@@ -443,6 +451,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setToolBar(QToolBar *toolBar);
|
void setToolBar(QToolBar *toolBar);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures, if the dock widget uses the global tool bar styles from
|
||||||
|
* dock manager or if it uses its own tool bar style
|
||||||
|
*/
|
||||||
|
void setToolBarStyleSource(eToolBarStyleSource source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configured tool bar style source
|
||||||
|
*/
|
||||||
|
eToolBarStyleSource toolBarStyleSource() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function sets the tool button style for the given dock widget state.
|
* This function sets the tool button style for the given dock widget state.
|
||||||
* It is possible to switch the tool button style depending on the state.
|
* It is possible to switch the tool button style depending on the state.
|
||||||
|
|||||||
@@ -374,10 +374,12 @@ void DockWidgetTab::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
|
|
||||||
d->focusController()->setDockWidgetTabPressed(false);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
|
||||||
|
d->focusController()->setDockWidgetTabPressed(false);
|
||||||
|
|
||||||
} else if (event->button() == Qt::MiddleButton) {
|
} else if (event->button() == Qt::MiddleButton) {
|
||||||
if (DockManager::testConfigFlag(DockManager::MiddleMouseButtonClosesTab)
|
if (DockManager::testConfigFlag(DockManager::MiddleMouseButtonClosesTab)
|
||||||
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable)) {
|
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable)) {
|
||||||
|
|||||||
@@ -41,16 +41,6 @@ const QString &Workspace::name() const
|
|||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::setLocked(bool value)
|
|
||||||
{
|
|
||||||
m_locked = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Workspace::isLocked() const
|
|
||||||
{
|
|
||||||
return m_locked;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Utils::FilePath &Workspace::filePath() const
|
const Utils::FilePath &Workspace::filePath() const
|
||||||
{
|
{
|
||||||
return m_filePath;
|
return m_filePath;
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ public:
|
|||||||
void setName(const QString &name);
|
void setName(const QString &name);
|
||||||
const QString &name() const;
|
const QString &name() const;
|
||||||
|
|
||||||
void setLocked(bool value);
|
|
||||||
bool isLocked() const;
|
|
||||||
|
|
||||||
const Utils::FilePath &filePath() const;
|
const Utils::FilePath &filePath() const;
|
||||||
|
|
||||||
QString fileName() const;
|
QString fileName() const;
|
||||||
@@ -53,7 +50,6 @@ private:
|
|||||||
QString m_name;
|
QString m_name;
|
||||||
Utils::FilePath m_filePath;
|
Utils::FilePath m_filePath;
|
||||||
bool m_preset = false;
|
bool m_preset = false;
|
||||||
bool m_locked = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ADS
|
} // namespace ADS
|
||||||
|
|||||||
@@ -1485,8 +1485,10 @@ QList<const CppComponentValue *> CppQmlTypes::createObjectsForImport(const QStri
|
|||||||
|
|
||||||
// if it already exists, skip
|
// if it already exists, skip
|
||||||
const QString key = qualifiedName(package, fmo->className(), version);
|
const QString key = qualifiedName(package, fmo->className(), version);
|
||||||
if (m_objectsByQualifiedName.contains(key))
|
if (m_objectsByQualifiedName.contains(key)) {
|
||||||
|
exportedObjects.insert(key, m_objectsByQualifiedName.value(key));
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ComponentVersion cppVersion;
|
ComponentVersion cppVersion;
|
||||||
for (const FakeMetaObject::Export &bestExport : std::as_const(bestExports)) {
|
for (const FakeMetaObject::Export &bestExport : std::as_const(bestExports)) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "cmaketoolmanager.h"
|
#include "cmaketoolmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
|
||||||
#include <utils/detailswidget.h>
|
#include <utils/detailswidget.h>
|
||||||
@@ -457,6 +458,9 @@ void CMakeToolItemConfigWidget::onBinaryPathEditingFinished()
|
|||||||
|
|
||||||
void CMakeToolItemConfigWidget::updateQchFilePath()
|
void CMakeToolItemConfigWidget::updateQchFilePath()
|
||||||
{
|
{
|
||||||
|
// QDS does not want automatic detection of cmake help file
|
||||||
|
if (Core::ICore::isQtDesignStudio())
|
||||||
|
return;
|
||||||
if (m_qchFileChooser->filePath().isEmpty())
|
if (m_qchFileChooser->filePath().isEmpty())
|
||||||
m_qchFileChooser->setFilePath(CMakeTool::searchQchFile(m_binaryChooser->filePath()));
|
m_qchFileChooser->setFilePath(CMakeTool::searchQchFile(m_binaryChooser->filePath()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
|
|
||||||
CompositionNode::CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &jsonObject)
|
CompositionNode::CompositionNode(const QString &effectName, const QString &qenPath,
|
||||||
|
const QJsonObject &jsonObject)
|
||||||
{
|
{
|
||||||
QJsonObject json;
|
QJsonObject json;
|
||||||
if (jsonObject.isEmpty()) {
|
if (jsonObject.isEmpty()) {
|
||||||
@@ -58,6 +59,11 @@ QString CompositionNode::description() const
|
|||||||
return m_description;
|
return m_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CompositionNode::id() const
|
||||||
|
{
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
QObject *CompositionNode::uniformsModel()
|
QObject *CompositionNode::uniformsModel()
|
||||||
{
|
{
|
||||||
return &m_unifomrsModel;
|
return &m_unifomrsModel;
|
||||||
@@ -81,6 +87,11 @@ void CompositionNode::setIsEnabled(bool newIsEnabled)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CompositionNode::isDependency() const
|
||||||
|
{
|
||||||
|
return m_refCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
CompositionNode::NodeType CompositionNode::type() const
|
CompositionNode::NodeType CompositionNode::type() const
|
||||||
{
|
{
|
||||||
return m_type;
|
return m_type;
|
||||||
@@ -102,6 +113,13 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
|
|||||||
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray());
|
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray());
|
||||||
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray());
|
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray());
|
||||||
|
|
||||||
|
m_id = json.value("id").toString();
|
||||||
|
if (m_id.isEmpty() && !qenPath.isEmpty()) {
|
||||||
|
QString fileName = qenPath.split('/').last();
|
||||||
|
fileName.chop(4); // remove ".qen"
|
||||||
|
m_id = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
// parse properties
|
// parse properties
|
||||||
QJsonArray jsonProps = json.value("properties").toArray();
|
QJsonArray jsonProps = json.value("properties").toArray();
|
||||||
for (const auto /*QJsonValueRef*/ &prop : jsonProps) {
|
for (const auto /*QJsonValueRef*/ &prop : jsonProps) {
|
||||||
@@ -119,7 +137,6 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
|
|||||||
QString trimmedLine = codeLine.trimmed();
|
QString trimmedLine = codeLine.trimmed();
|
||||||
if (trimmedLine.startsWith("@requires")) {
|
if (trimmedLine.startsWith("@requires")) {
|
||||||
// Get the required node, remove "@requires "
|
// Get the required node, remove "@requires "
|
||||||
QString l = trimmedLine.sliced(9).trimmed();
|
|
||||||
QString nodeName = trimmedLine.sliced(10);
|
QString nodeName = trimmedLine.sliced(10);
|
||||||
if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName))
|
if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName))
|
||||||
m_requiredNodes << nodeName;
|
m_requiredNodes << nodeName;
|
||||||
@@ -132,6 +149,36 @@ QList<Uniform *> CompositionNode::uniforms() const
|
|||||||
return m_uniforms;
|
return m_uniforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CompositionNode::incRefCount()
|
||||||
|
{
|
||||||
|
++m_refCount;
|
||||||
|
|
||||||
|
if (m_refCount == 1)
|
||||||
|
emit isDepencyChanged();
|
||||||
|
|
||||||
|
return m_refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CompositionNode::decRefCount()
|
||||||
|
{
|
||||||
|
--m_refCount;
|
||||||
|
|
||||||
|
if (m_refCount == 0)
|
||||||
|
emit isDepencyChanged();
|
||||||
|
|
||||||
|
return m_refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompositionNode::setRefCount(int count)
|
||||||
|
{
|
||||||
|
bool notifyChange = (m_refCount > 0 && count == 0) || (m_refCount <= 0 && count > 0);
|
||||||
|
|
||||||
|
m_refCount = count;
|
||||||
|
|
||||||
|
if (notifyChange)
|
||||||
|
emit isDepencyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QString CompositionNode::name() const
|
QString CompositionNode::name() const
|
||||||
{
|
{
|
||||||
return m_name;
|
return m_name;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class CompositionNode : public QObject
|
|||||||
|
|
||||||
Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT)
|
Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT)
|
||||||
Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
|
Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
|
||||||
|
Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged)
|
||||||
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
|
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -30,6 +31,7 @@ public:
|
|||||||
QString fragmentCode() const;
|
QString fragmentCode() const;
|
||||||
QString vertexCode() const;
|
QString vertexCode() const;
|
||||||
QString description() const;
|
QString description() const;
|
||||||
|
QString id() const;
|
||||||
|
|
||||||
QObject *uniformsModel();
|
QObject *uniformsModel();
|
||||||
|
|
||||||
@@ -40,13 +42,20 @@ public:
|
|||||||
bool isEnabled() const;
|
bool isEnabled() const;
|
||||||
void setIsEnabled(bool newIsEnabled);
|
void setIsEnabled(bool newIsEnabled);
|
||||||
|
|
||||||
|
bool isDependency() const;
|
||||||
|
|
||||||
QString name() const;
|
QString name() const;
|
||||||
|
|
||||||
QList<Uniform *> uniforms() const;
|
QList<Uniform *> uniforms() const;
|
||||||
|
|
||||||
|
int incRefCount();
|
||||||
|
int decRefCount();
|
||||||
|
void setRefCount(int count);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void uniformsModelChanged();
|
void uniformsModelChanged();
|
||||||
void isEnabledChanged();
|
void isEnabledChanged();
|
||||||
|
void isDepencyChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
|
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
|
||||||
@@ -57,7 +66,9 @@ private:
|
|||||||
QString m_vertexCode;
|
QString m_vertexCode;
|
||||||
QString m_description;
|
QString m_description;
|
||||||
QStringList m_requiredNodes;
|
QStringList m_requiredNodes;
|
||||||
|
QString m_id;
|
||||||
bool m_isEnabled = true;
|
bool m_isEnabled = true;
|
||||||
|
int m_refCount = 0;
|
||||||
|
|
||||||
QList<Uniform*> m_uniforms;
|
QList<Uniform*> m_uniforms;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "effectmakermodel.h"
|
#include "effectmakermodel.h"
|
||||||
|
|
||||||
#include "compositionnode.h"
|
#include "compositionnode.h"
|
||||||
|
#include "effectutils.h"
|
||||||
|
#include "propertyhandler.h"
|
||||||
#include "syntaxhighlighterdata.h"
|
#include "syntaxhighlighterdata.h"
|
||||||
#include "uniform.h"
|
#include "uniform.h"
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
#include <modelnodeoperations.h>
|
#include <modelnodeoperations.h>
|
||||||
|
|
||||||
#include <QByteArrayView>
|
#include <QByteArrayView>
|
||||||
|
#include <QLibraryInfo>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
@@ -57,6 +60,7 @@ QHash<int, QByteArray> EffectMakerModel::roleNames() const
|
|||||||
roles[NameRole] = "nodeName";
|
roles[NameRole] = "nodeName";
|
||||||
roles[EnabledRole] = "nodeEnabled";
|
roles[EnabledRole] = "nodeEnabled";
|
||||||
roles[UniformsRole] = "nodeUniformsModel";
|
roles[UniformsRole] = "nodeUniformsModel";
|
||||||
|
roles[Dependency] = "isDependency";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,14 +107,45 @@ void EffectMakerModel::setIsEmpty(bool val)
|
|||||||
|
|
||||||
void EffectMakerModel::addNode(const QString &nodeQenPath)
|
void EffectMakerModel::addNode(const QString &nodeQenPath)
|
||||||
{
|
{
|
||||||
beginInsertRows({}, m_nodes.size(), m_nodes.size());
|
beginResetModel();
|
||||||
auto *node = new CompositionNode("", nodeQenPath);
|
auto *node = new CompositionNode({}, nodeQenPath);
|
||||||
|
connect(qobject_cast<EffectMakerUniformsModel *>(node->uniformsModel()),
|
||||||
|
&EffectMakerUniformsModel::dataChanged, this, [this] {
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const QList<QString> requiredNodes = node->requiredNodes();
|
||||||
|
if (requiredNodes.size() > 0) {
|
||||||
|
for (const QString &requiredId : requiredNodes) {
|
||||||
|
if (auto reqNode = findNodeById(requiredId)) {
|
||||||
|
reqNode->incRefCount();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString path = EffectUtils::nodesSourcesPath() + "/common/" + requiredId + ".qen";
|
||||||
|
auto requiredNode = new CompositionNode({}, path);
|
||||||
|
requiredNode->setRefCount(1);
|
||||||
|
m_nodes.prepend(requiredNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_nodes.append(node);
|
m_nodes.append(node);
|
||||||
endInsertRows();
|
endResetModel();
|
||||||
|
|
||||||
setIsEmpty(false);
|
setIsEmpty(false);
|
||||||
|
|
||||||
bakeShaders();
|
bakeShaders();
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
|
|
||||||
|
emit nodesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionNode *EffectMakerModel::findNodeById(const QString &id) const
|
||||||
|
{
|
||||||
|
for (CompositionNode *node : std::as_const(m_nodes)) {
|
||||||
|
if (node->id() == id)
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::moveNode(int fromIdx, int toIdx)
|
void EffectMakerModel::moveNode(int fromIdx, int toIdx)
|
||||||
@@ -123,21 +158,67 @@ void EffectMakerModel::moveNode(int fromIdx, int toIdx)
|
|||||||
m_nodes.move(fromIdx, toIdx);
|
m_nodes.move(fromIdx, toIdx);
|
||||||
endMoveRows();
|
endMoveRows();
|
||||||
|
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
bakeShaders();
|
bakeShaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::removeNode(int idx)
|
void EffectMakerModel::removeNode(int idx)
|
||||||
{
|
{
|
||||||
beginRemoveRows({}, idx, idx);
|
beginResetModel();
|
||||||
CompositionNode *node = m_nodes.at(idx);
|
CompositionNode *node = m_nodes.takeAt(idx);
|
||||||
m_nodes.removeAt(idx);
|
|
||||||
|
const QStringList reqNodes = node->requiredNodes();
|
||||||
|
for (const QString &reqId : reqNodes) {
|
||||||
|
CompositionNode *depNode = findNodeById(reqId);
|
||||||
|
if (depNode && depNode->decRefCount() <= 0) {
|
||||||
|
m_nodes.removeOne(depNode);
|
||||||
|
delete depNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete node;
|
delete node;
|
||||||
endRemoveRows();
|
endResetModel();
|
||||||
|
|
||||||
if (m_nodes.isEmpty())
|
if (m_nodes.isEmpty())
|
||||||
setIsEmpty(true);
|
setIsEmpty(true);
|
||||||
else
|
else
|
||||||
bakeShaders();
|
bakeShaders();
|
||||||
|
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
|
emit nodesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectMakerModel::clear()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
qDeleteAll(m_nodes);
|
||||||
|
m_nodes.clear();
|
||||||
|
endResetModel();
|
||||||
|
setHasUnsavedChanges(!m_currentComposition.isEmpty());
|
||||||
|
setCurrentComposition("");
|
||||||
|
|
||||||
|
setIsEmpty(true);
|
||||||
|
emit nodesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectMakerModel::assignToSelected()
|
||||||
|
{
|
||||||
|
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
|
||||||
|
const QString path = effectsAssetsDir + QDir::separator() + m_currentComposition + ".qep";
|
||||||
|
emit assignToSelectedTriggered(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EffectMakerModel::getUniqueEffectName() const
|
||||||
|
{
|
||||||
|
const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
|
||||||
|
const QString path = effectsDir + QDir::separator() + "Effect%1.qep";
|
||||||
|
|
||||||
|
int num = 0;
|
||||||
|
|
||||||
|
while (QFile::exists(path.arg(++num, 2, 10, QChar('0'))))
|
||||||
|
; // empty body
|
||||||
|
|
||||||
|
return QString("Effect%1").arg(num, 2, 10, QChar('0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EffectMakerModel::fragmentShader() const
|
QString EffectMakerModel::fragmentShader() const
|
||||||
@@ -171,25 +252,7 @@ const QString &EffectMakerModel::qmlComponentString() const
|
|||||||
return m_qmlComponentString;
|
return m_qmlComponentString;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::clear()
|
const QList<Uniform *> EffectMakerModel::allUniforms() const
|
||||||
{
|
|
||||||
if (m_nodes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
beginRemoveRows({}, 0, m_nodes.count());
|
|
||||||
|
|
||||||
for (CompositionNode *node : std::as_const(m_nodes))
|
|
||||||
delete node;
|
|
||||||
|
|
||||||
m_nodes.clear();
|
|
||||||
|
|
||||||
endRemoveRows();
|
|
||||||
|
|
||||||
setIsEmpty(true);
|
|
||||||
bakeShaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<Uniform *> EffectMakerModel::allUniforms()
|
|
||||||
{
|
{
|
||||||
QList<Uniform *> uniforms = {};
|
QList<Uniform *> uniforms = {};
|
||||||
for (const auto &node : std::as_const(m_nodes))
|
for (const auto &node : std::as_const(m_nodes))
|
||||||
@@ -416,6 +479,8 @@ QJsonObject nodeToJson(const CompositionNode &node)
|
|||||||
nodeObject.insert("description", node.description());
|
nodeObject.insert("description", node.description());
|
||||||
nodeObject.insert("enabled", node.isEnabled());
|
nodeObject.insert("enabled", node.isEnabled());
|
||||||
nodeObject.insert("version", 1);
|
nodeObject.insert("version", 1);
|
||||||
|
nodeObject.insert("id", node.id());
|
||||||
|
|
||||||
// Add properties
|
// Add properties
|
||||||
QJsonArray propertiesArray;
|
QJsonArray propertiesArray;
|
||||||
const QList<Uniform *> uniforms = node.uniforms();
|
const QList<Uniform *> uniforms = node.uniforms();
|
||||||
@@ -523,14 +588,24 @@ QString EffectMakerModel::getQmlEffectString()
|
|||||||
s += '\n';
|
s += '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Blue stuff goes here
|
if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) {
|
||||||
|
s += " BlurHelper {\n";
|
||||||
|
s += " id: blurHelper\n";
|
||||||
|
s += " anchors.fill: parent\n";
|
||||||
|
int blurMax = 32;
|
||||||
|
if (g_propertyData.contains("BLUR_HELPER_MAX_LEVEL"))
|
||||||
|
blurMax = g_propertyData["BLUR_HELPER_MAX_LEVEL"].toInt();
|
||||||
|
s += QString(" property int blurMax: %1\n").arg(blurMax);
|
||||||
|
s += " property real blurMultiplier: rootItem.blurMultiplier\n";
|
||||||
|
s += " }\n";
|
||||||
|
}
|
||||||
|
|
||||||
s += getQmlComponentString(true);
|
s += getQmlComponentString(true);
|
||||||
s += "}\n";
|
s += "}\n";
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::exportComposition(const QString &name)
|
void EffectMakerModel::saveComposition(const QString &name)
|
||||||
{
|
{
|
||||||
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
|
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
|
||||||
const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
|
const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
|
||||||
@@ -561,12 +636,19 @@ void EffectMakerModel::exportComposition(const QString &name)
|
|||||||
|
|
||||||
saveFile.write(jsonDoc.toJson());
|
saveFile.write(jsonDoc.toJson());
|
||||||
saveFile.close();
|
saveFile.close();
|
||||||
|
setCurrentComposition(name);
|
||||||
|
setHasUnsavedChanges(false);
|
||||||
|
|
||||||
|
saveResources(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::openComposition(const QString &path)
|
void EffectMakerModel::openComposition(const QString &path)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
|
const QString effectName = QFileInfo(path).baseName();
|
||||||
|
setCurrentComposition(effectName);
|
||||||
|
|
||||||
QFile compFile(path);
|
QFile compFile(path);
|
||||||
if (!compFile.open(QIODevice::ReadOnly)) {
|
if (!compFile.open(QIODevice::ReadOnly)) {
|
||||||
QString error = QString("Couldn't open composition file: '%1'").arg(path);
|
QString error = QString("Couldn't open composition file: '%1'").arg(path);
|
||||||
@@ -576,6 +658,10 @@ void EffectMakerModel::openComposition(const QString &path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QByteArray data = compFile.readAll();
|
QByteArray data = compFile.readAll();
|
||||||
|
|
||||||
|
if (data.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
QJsonParseError parseError;
|
QJsonParseError parseError;
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data, &parseError));
|
QJsonDocument jsonDoc(QJsonDocument::fromJson(data, &parseError));
|
||||||
if (parseError.error != QJsonParseError::NoError) {
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
@@ -605,28 +691,40 @@ void EffectMakerModel::openComposition(const QString &path)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get effects dir
|
|
||||||
const QString effectName = QFileInfo(path).baseName();
|
|
||||||
const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory();
|
|
||||||
const QString effectsResPath = effectsResDir.pathAppended(effectName).toString();
|
|
||||||
|
|
||||||
if (json.contains("nodes") && json["nodes"].isArray()) {
|
if (json.contains("nodes") && json["nodes"].isArray()) {
|
||||||
|
beginResetModel();
|
||||||
|
QHash<QString, int> refCounts;
|
||||||
const QJsonArray nodesArray = json["nodes"].toArray();
|
const QJsonArray nodesArray = json["nodes"].toArray();
|
||||||
|
|
||||||
for (const auto &nodeElement : nodesArray) {
|
for (const auto &nodeElement : nodesArray) {
|
||||||
beginInsertRows({}, m_nodes.size(), m_nodes.size());
|
auto *node = new CompositionNode(effectName, {}, nodeElement.toObject());
|
||||||
auto *node = new CompositionNode(effectName, "", nodeElement.toObject());
|
connect(qobject_cast<EffectMakerUniformsModel *>(node->uniformsModel()),
|
||||||
|
&EffectMakerUniformsModel::dataChanged, this, [this] {
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
|
});
|
||||||
m_nodes.append(node);
|
m_nodes.append(node);
|
||||||
endInsertRows();
|
const QStringList reqIds = node->requiredNodes();
|
||||||
|
for (const QString &reqId : reqIds)
|
||||||
|
++refCounts[reqId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto it = refCounts.cbegin(), end = refCounts.cend(); it != end; ++it) {
|
||||||
|
CompositionNode *depNode = findNodeById(it.key());
|
||||||
|
if (depNode)
|
||||||
|
depNode->setRefCount(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
setIsEmpty(m_nodes.isEmpty());
|
setIsEmpty(m_nodes.isEmpty());
|
||||||
bakeShaders();
|
bakeShaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentComposition(effectName);
|
setHasUnsavedChanges(false);
|
||||||
|
emit nodesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::exportResources(const QString &name)
|
void EffectMakerModel::saveResources(const QString &name)
|
||||||
{
|
{
|
||||||
// Make sure that uniforms are up-to-date
|
// Make sure that uniforms are up-to-date
|
||||||
updateCustomUniforms();
|
updateCustomUniforms();
|
||||||
@@ -692,7 +790,7 @@ void EffectMakerModel::exportResources(const QString &name)
|
|||||||
QString qmlFilePath = effectsResPath + qmlFilename;
|
QString qmlFilePath = effectsResPath + qmlFilename;
|
||||||
writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text);
|
writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text);
|
||||||
|
|
||||||
// Export shaders and images
|
// Save shaders and images
|
||||||
QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename};
|
QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename};
|
||||||
QStringList dests = {vsFilename, fsFilename};
|
QStringList dests = {vsFilename, fsFilename};
|
||||||
|
|
||||||
@@ -702,11 +800,28 @@ void EffectMakerModel::exportResources(const QString &name)
|
|||||||
QString imagePath = uniform->value().toString();
|
QString imagePath = uniform->value().toString();
|
||||||
QFileInfo fi(imagePath);
|
QFileInfo fi(imagePath);
|
||||||
QString imageFilename = fi.fileName();
|
QString imageFilename = fi.fileName();
|
||||||
sources.append(imagePath.remove(0, 7)); // Removes "file://"
|
if (imagePath.startsWith("file:")) {
|
||||||
|
QUrl url(imagePath);
|
||||||
|
imagePath = url.toLocalFile();
|
||||||
|
}
|
||||||
|
sources.append(imagePath);
|
||||||
dests.append(imageFilename);
|
dests.append(imageFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) {
|
||||||
|
QString blurHelperFilename("BlurHelper.qml");
|
||||||
|
QString blurFsFilename("bluritems.frag.qsb");
|
||||||
|
QString blurVsFilename("bluritems.vert.qsb");
|
||||||
|
QString blurHelperPath(EffectUtils::nodesSourcesPath() + "/common/");
|
||||||
|
sources.append(blurHelperPath + blurHelperFilename);
|
||||||
|
sources.append(blurHelperPath + blurFsFilename);
|
||||||
|
sources.append(blurHelperPath + blurVsFilename);
|
||||||
|
dests.append(blurHelperFilename);
|
||||||
|
dests.append(blurFsFilename);
|
||||||
|
dests.append(blurVsFilename);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < sources.count(); ++i) {
|
for (int i = 0; i < sources.count(); ++i) {
|
||||||
Utils::FilePath source = Utils::FilePath::fromString(sources[i]);
|
Utils::FilePath source = Utils::FilePath::fromString(sources[i]);
|
||||||
Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]);
|
Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]);
|
||||||
@@ -716,6 +831,8 @@ void EffectMakerModel::exportResources(const QString &name)
|
|||||||
if (!source.copyFile(target))
|
if (!source.copyFile(target))
|
||||||
qWarning() << __FUNCTION__ << " Failed to copy file: " << source;
|
qWarning() << __FUNCTION__ << " Failed to copy file: " << source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::resetEffectError(int type)
|
void EffectMakerModel::resetEffectError(int type)
|
||||||
@@ -737,16 +854,18 @@ QString EffectMakerModel::valueAsString(const Uniform &uniform)
|
|||||||
return QString::number(uniform.value().toDouble());
|
return QString::number(uniform.value().toDouble());
|
||||||
} else if (uniform.type() == Uniform::Type::Vec2) {
|
} else if (uniform.type() == Uniform::Type::Vec2) {
|
||||||
QVector2D v2 = uniform.value().value<QVector2D>();
|
QVector2D v2 = uniform.value().value<QVector2D>();
|
||||||
return QString("Qt.point(%1, %2)").arg(v2.x(), v2.y());
|
return QString("Qt.point(%1, %2)").arg(v2.x()).arg(v2.y());
|
||||||
} else if (uniform.type() == Uniform::Type::Vec3) {
|
} else if (uniform.type() == Uniform::Type::Vec3) {
|
||||||
QVector3D v3 = uniform.value().value<QVector3D>();
|
QVector3D v3 = uniform.value().value<QVector3D>();
|
||||||
return QString("Qt.vector3d(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z());
|
return QString("Qt.vector3d(%1, %2, %3)").arg(v3.x()).arg(v3.y()).arg(v3.z());
|
||||||
} else if (uniform.type() == Uniform::Type::Vec4) {
|
} else if (uniform.type() == Uniform::Type::Vec4) {
|
||||||
QVector4D v4 = uniform.value().value<QVector4D>();
|
QVector4D v4 = uniform.value().value<QVector4D>();
|
||||||
return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w());
|
return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x()).arg(v4.y()).arg(v4.z()).arg(v4.w());
|
||||||
} else if (uniform.type() == Uniform::Type::Sampler) {
|
} else if (uniform.type() == Uniform::Type::Sampler) {
|
||||||
return getImageElementName(uniform);
|
return getImageElementName(uniform);
|
||||||
} else if (uniform.type() == Uniform::Type::Define || uniform.type() == Uniform::Type::Color) {
|
} else if (uniform.type() == Uniform::Type::Color) {
|
||||||
|
return QString("\"%1\"").arg(uniform.value().toString());
|
||||||
|
} else if (uniform.type() == Uniform::Type::Define) {
|
||||||
return uniform.value().toString();
|
return uniform.value().toString();
|
||||||
} else {
|
} else {
|
||||||
qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1();
|
qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1();
|
||||||
@@ -1085,18 +1204,25 @@ QString EffectMakerModel::generateFragmentShader(bool includeUniforms)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader)
|
void EffectMakerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, bool preview)
|
||||||
{
|
{
|
||||||
--m_remainingQsbTargets;
|
--m_remainingQsbTargets;
|
||||||
|
|
||||||
const QString errStr = qsbProcess->errorString();
|
const QString errStr = qsbProcess->errorString();
|
||||||
const QByteArray errStd = qsbProcess->readAllRawStandardError();
|
const QByteArray errStd = qsbProcess->readAllRawStandardError();
|
||||||
if (!errStr.isEmpty())
|
QString previewStr;
|
||||||
qWarning() << QString("Failed to generate QSB file for: %1 %2").arg(shader, errStr);
|
if (preview)
|
||||||
|
previewStr = QStringLiteral("preview");
|
||||||
|
|
||||||
if (!errStd.isEmpty())
|
if (!errStr.isEmpty()) {
|
||||||
qWarning() << QString("Failed to generate QSB file for: %1 %2")
|
qWarning() << QString("Failed to generate %3 QSB file for: %1 %2")
|
||||||
.arg(shader, QString::fromUtf8(errStd));
|
.arg(shader, errStr, previewStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errStd.isEmpty()) {
|
||||||
|
qWarning() << QString("Failed to generate %3 QSB file for: %1 %2")
|
||||||
|
.arg(shader, QString::fromUtf8(errStd), previewStr);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_remainingQsbTargets <= 0) {
|
if (m_remainingQsbTargets <= 0) {
|
||||||
Q_EMIT shadersBaked();
|
Q_EMIT shadersBaked();
|
||||||
@@ -1182,21 +1308,30 @@ void EffectMakerModel::createFiles()
|
|||||||
QFile(m_vertexShaderFilename).remove();
|
QFile(m_vertexShaderFilename).remove();
|
||||||
if (QFileInfo(m_fragmentShaderFilename).exists())
|
if (QFileInfo(m_fragmentShaderFilename).exists())
|
||||||
QFile(m_fragmentShaderFilename).remove();
|
QFile(m_fragmentShaderFilename).remove();
|
||||||
|
if (QFileInfo(m_vertexShaderPreviewFilename).exists())
|
||||||
|
QFile(m_vertexShaderPreviewFilename).remove();
|
||||||
|
if (QFileInfo(m_fragmentShaderPreviewFilename).exists())
|
||||||
|
QFile(m_fragmentShaderPreviewFilename).remove();
|
||||||
|
|
||||||
auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb");
|
auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb");
|
||||||
auto fragmentShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb");
|
auto fragmentShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb");
|
||||||
|
auto vertexShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.vert.qsb");
|
||||||
|
auto fragmentShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.frag.qsb");
|
||||||
|
|
||||||
m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert");
|
m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert");
|
||||||
m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag");
|
m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag");
|
||||||
|
|
||||||
if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open()
|
if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open()
|
||||||
|| !vertexShaderFile.open() || !fragmentShaderFile.open()) {
|
|| !vertexShaderFile.open() || !fragmentShaderFile.open()
|
||||||
|
|| !vertexShaderPreviewFile.open() || !fragmentShaderPreviewFile.open()) {
|
||||||
qWarning() << "Unable to open temporary files";
|
qWarning() << "Unable to open temporary files";
|
||||||
} else {
|
} else {
|
||||||
m_vertexSourceFilename = m_vertexSourceFile.fileName();
|
m_vertexSourceFilename = m_vertexSourceFile.fileName();
|
||||||
m_fragmentSourceFilename = m_fragmentSourceFile.fileName();
|
m_fragmentSourceFilename = m_fragmentSourceFile.fileName();
|
||||||
m_vertexShaderFilename = vertexShaderFile.fileName();
|
m_vertexShaderFilename = vertexShaderFile.fileName();
|
||||||
m_fragmentShaderFilename = fragmentShaderFile.fileName();
|
m_fragmentShaderFilename = fragmentShaderFile.fileName();
|
||||||
|
m_vertexShaderPreviewFilename = vertexShaderPreviewFile.fileName();
|
||||||
|
m_fragmentShaderPreviewFilename = fragmentShaderPreviewFile.fileName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1243,13 +1378,24 @@ void EffectMakerModel::bakeShaders()
|
|||||||
|
|
||||||
Utils::FilePath qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix();
|
Utils::FilePath qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix();
|
||||||
if (!qsbPath.exists()) {
|
if (!qsbPath.exists()) {
|
||||||
qWarning() << failMessage << "QSB tool not found";
|
qWarning() << failMessage << "QSB tool for target kit not found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::FilePath binPath = Utils::FilePath::fromString(
|
||||||
|
QLibraryInfo::path(QLibraryInfo::BinariesPath));
|
||||||
|
Utils::FilePath qsbPrevPath = binPath.pathAppended("qsb").withExecutableSuffix();
|
||||||
|
if (!qsbPrevPath.exists()) {
|
||||||
|
qWarning() << failMessage << "QSB tool for preview shaders not found";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_remainingQsbTargets = 2; // We only have 2 shaders
|
m_remainingQsbTargets = 2; // We only have 2 shaders
|
||||||
const QStringList srcPaths = {m_vertexSourceFilename, m_fragmentSourceFilename};
|
const QStringList srcPaths = {m_vertexSourceFilename, m_fragmentSourceFilename};
|
||||||
const QStringList outPaths = {m_vertexShaderFilename, m_fragmentShaderFilename};
|
const QStringList outPaths = {m_vertexShaderFilename, m_fragmentShaderFilename};
|
||||||
|
const QStringList outPrevPaths = {m_vertexShaderPreviewFilename, m_fragmentShaderPreviewFilename};
|
||||||
|
|
||||||
|
auto runQsb = [this, srcPaths](const Utils::FilePath &qsbPath, const QStringList &outPaths, bool preview) {
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
const auto workDir = Utils::FilePath::fromString(outPaths[i]);
|
const auto workDir = Utils::FilePath::fromString(outPaths[i]);
|
||||||
// TODO: Optional legacy glsl support like standalone effect maker needs to add "100es,120"
|
// TODO: Optional legacy glsl support like standalone effect maker needs to add "100es,120"
|
||||||
@@ -1258,12 +1404,17 @@ void EffectMakerModel::bakeShaders()
|
|||||||
|
|
||||||
auto qsbProcess = new Utils::Process(this);
|
auto qsbProcess = new Utils::Process(this);
|
||||||
connect(qsbProcess, &Utils::Process::done, this, [=] {
|
connect(qsbProcess, &Utils::Process::done, this, [=] {
|
||||||
handleQsbProcessExit(qsbProcess, srcPaths[i]);
|
handleQsbProcessExit(qsbProcess, srcPaths[i], preview);
|
||||||
});
|
});
|
||||||
qsbProcess->setWorkingDirectory(workDir.absolutePath());
|
qsbProcess->setWorkingDirectory(workDir.absolutePath());
|
||||||
qsbProcess->setCommand({qsbPath, args});
|
qsbProcess->setCommand({qsbPath, args});
|
||||||
qsbProcess->start();
|
qsbProcess->start();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runQsb(qsbPath, outPaths, false);
|
||||||
|
runQsb(qsbPrevPath, outPrevPaths, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EffectMakerModel::shadersUpToDate() const
|
bool EffectMakerModel::shadersUpToDate() const
|
||||||
@@ -1375,8 +1526,10 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles)
|
|||||||
s += '\n' + customImagesString;
|
s += '\n' + customImagesString;
|
||||||
|
|
||||||
s += '\n';
|
s += '\n';
|
||||||
s += l2 + "vertexShader: 'file:///" + m_vertexShaderFilename + "'\n";
|
const QString vertFile = localFiles ? m_vertexShaderFilename : m_vertexShaderPreviewFilename;
|
||||||
s += l2 + "fragmentShader: 'file:///" + m_fragmentShaderFilename + "'\n";
|
const QString fragFile = localFiles ? m_fragmentShaderFilename : m_fragmentShaderPreviewFilename;
|
||||||
|
s += l2 + "vertexShader: 'file:///" + vertFile + "'\n";
|
||||||
|
s += l2 + "fragmentShader: 'file:///" + fragFile + "'\n";
|
||||||
s += l2 + "anchors.fill: parent\n";
|
s += l2 + "anchors.fill: parent\n";
|
||||||
if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) {
|
if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) {
|
||||||
QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth())
|
QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth())
|
||||||
@@ -1398,10 +1551,41 @@ void EffectMakerModel::setCurrentComposition(const QString &newCurrentCompositio
|
|||||||
{
|
{
|
||||||
if (m_currentComposition == newCurrentComposition)
|
if (m_currentComposition == newCurrentComposition)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_currentComposition = newCurrentComposition;
|
m_currentComposition = newCurrentComposition;
|
||||||
emit currentCompositionChanged();
|
emit currentCompositionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EffectMakerModel::hasUnsavedChanges() const
|
||||||
|
{
|
||||||
|
return m_hasUnsavedChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectMakerModel::setHasUnsavedChanges(bool val)
|
||||||
|
{
|
||||||
|
if (m_hasUnsavedChanges == val)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_hasUnsavedChanges = val;
|
||||||
|
emit hasUnsavedChangesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList EffectMakerModel::uniformNames() const
|
||||||
|
{
|
||||||
|
QStringList usedList;
|
||||||
|
const QList<Uniform *> uniforms = allUniforms();
|
||||||
|
for (const auto uniform : uniforms)
|
||||||
|
usedList.append(uniform->name());
|
||||||
|
return usedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectMakerModel::isDependencyNode(int index) const
|
||||||
|
{
|
||||||
|
if (m_nodes.size() > index)
|
||||||
|
return m_nodes[index]->isDependency();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void EffectMakerModel::updateQmlComponent()
|
void EffectMakerModel::updateQmlComponent()
|
||||||
{
|
{
|
||||||
// Clear possible QML runtime errors
|
// Clear possible QML runtime errors
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QStandardItemModel>
|
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
@@ -44,6 +44,7 @@ class EffectMakerModel : public QAbstractListModel
|
|||||||
|
|
||||||
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||||
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
||||||
|
Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged)
|
||||||
Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged)
|
Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged)
|
||||||
Q_PROPERTY(QString qmlComponentString READ qmlComponentString)
|
Q_PROPERTY(QString qmlComponentString READ qmlComponentString)
|
||||||
Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged)
|
Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged)
|
||||||
@@ -61,8 +62,13 @@ public:
|
|||||||
|
|
||||||
void addNode(const QString &nodeQenPath);
|
void addNode(const QString &nodeQenPath);
|
||||||
|
|
||||||
|
CompositionNode *findNodeById(const QString &id) const;
|
||||||
|
|
||||||
Q_INVOKABLE void moveNode(int fromIdx, int toIdx);
|
Q_INVOKABLE void moveNode(int fromIdx, int toIdx);
|
||||||
Q_INVOKABLE void removeNode(int idx);
|
Q_INVOKABLE void removeNode(int idx);
|
||||||
|
Q_INVOKABLE void clear();
|
||||||
|
Q_INVOKABLE void assignToSelected();
|
||||||
|
Q_INVOKABLE QString getUniqueEffectName() const;
|
||||||
|
|
||||||
bool shadersUpToDate() const;
|
bool shadersUpToDate() const;
|
||||||
void setShadersUpToDate(bool newShadersUpToDate);
|
void setShadersUpToDate(bool newShadersUpToDate);
|
||||||
@@ -75,35 +81,43 @@ public:
|
|||||||
|
|
||||||
const QString &qmlComponentString() const;
|
const QString &qmlComponentString() const;
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
Q_INVOKABLE void updateQmlComponent();
|
Q_INVOKABLE void updateQmlComponent();
|
||||||
|
|
||||||
Q_INVOKABLE void resetEffectError(int type);
|
Q_INVOKABLE void resetEffectError(int type);
|
||||||
Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1);
|
Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1);
|
||||||
|
|
||||||
Q_INVOKABLE void exportComposition(const QString &name);
|
Q_INVOKABLE void saveComposition(const QString &name);
|
||||||
Q_INVOKABLE void exportResources(const QString &name);
|
|
||||||
|
|
||||||
void openComposition(const QString &path);
|
void openComposition(const QString &path);
|
||||||
|
|
||||||
QString currentComposition() const;
|
QString currentComposition() const;
|
||||||
void setCurrentComposition(const QString &newCurrentComposition);
|
void setCurrentComposition(const QString &newCurrentComposition);
|
||||||
|
|
||||||
|
bool hasUnsavedChanges() const;
|
||||||
|
void setHasUnsavedChanges(bool val);
|
||||||
|
|
||||||
|
QStringList uniformNames() const;
|
||||||
|
|
||||||
|
Q_INVOKABLE bool isDependencyNode(int index) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void isEmptyChanged();
|
void isEmptyChanged();
|
||||||
void selectedIndexChanged(int idx);
|
void selectedIndexChanged(int idx);
|
||||||
void effectErrorChanged();
|
void effectErrorChanged();
|
||||||
void shadersUpToDateChanged();
|
void shadersUpToDateChanged();
|
||||||
void shadersBaked();
|
void shadersBaked();
|
||||||
|
|
||||||
void currentCompositionChanged();
|
void currentCompositionChanged();
|
||||||
|
void nodesChanged();
|
||||||
|
void resourcesSaved(const QByteArray &type, const Utils::FilePath &path);
|
||||||
|
void hasUnsavedChangesChanged();
|
||||||
|
void assignToSelectedTriggered(const QString &effectPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
NameRole = Qt::UserRole + 1,
|
NameRole = Qt::UserRole + 1,
|
||||||
EnabledRole,
|
EnabledRole,
|
||||||
UniformsRole
|
UniformsRole,
|
||||||
|
Dependency
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ErrorTypes {
|
enum ErrorTypes {
|
||||||
@@ -117,7 +131,7 @@ private:
|
|||||||
|
|
||||||
bool isValidIndex(int idx) const;
|
bool isValidIndex(int idx) const;
|
||||||
|
|
||||||
const QList<Uniform *> allUniforms();
|
const QList<Uniform *> allUniforms() const;
|
||||||
|
|
||||||
const QString getBufUniform();
|
const QString getBufUniform();
|
||||||
const QString getVSUniforms();
|
const QString getVSUniforms();
|
||||||
@@ -142,13 +156,14 @@ private:
|
|||||||
QString getCustomShaderVaryings(bool outState);
|
QString getCustomShaderVaryings(bool outState);
|
||||||
QString generateVertexShader(bool includeUniforms = true);
|
QString generateVertexShader(bool includeUniforms = true);
|
||||||
QString generateFragmentShader(bool includeUniforms = true);
|
QString generateFragmentShader(bool includeUniforms = true);
|
||||||
void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader);
|
void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, bool preview);
|
||||||
QString stripFileFromURL(const QString &urlString) const;
|
QString stripFileFromURL(const QString &urlString) const;
|
||||||
QString getQmlEffectString();
|
QString getQmlEffectString();
|
||||||
|
|
||||||
void updateCustomUniforms();
|
void updateCustomUniforms();
|
||||||
void createFiles();
|
void createFiles();
|
||||||
void bakeShaders();
|
void bakeShaders();
|
||||||
|
void saveResources(const QString &name);
|
||||||
|
|
||||||
QString mipmapPropertyName(const QString &name) const;
|
QString mipmapPropertyName(const QString &name) const;
|
||||||
QString getQmlImagesString(bool localFiles);
|
QString getQmlImagesString(bool localFiles);
|
||||||
@@ -158,6 +173,7 @@ private:
|
|||||||
|
|
||||||
int m_selectedIndex = -1;
|
int m_selectedIndex = -1;
|
||||||
bool m_isEmpty = true;
|
bool m_isEmpty = true;
|
||||||
|
bool m_hasUnsavedChanges = false;
|
||||||
// True when shaders haven't changed since last baking
|
// True when shaders haven't changed since last baking
|
||||||
bool m_shadersUpToDate = true;
|
bool m_shadersUpToDate = true;
|
||||||
int m_remainingQsbTargets = 0;
|
int m_remainingQsbTargets = 0;
|
||||||
@@ -175,6 +191,8 @@ private:
|
|||||||
QString m_vertexSourceFilename;
|
QString m_vertexSourceFilename;
|
||||||
QString m_fragmentShaderFilename;
|
QString m_fragmentShaderFilename;
|
||||||
QString m_vertexShaderFilename;
|
QString m_vertexShaderFilename;
|
||||||
|
QString m_fragmentShaderPreviewFilename;
|
||||||
|
QString m_vertexShaderPreviewFilename;
|
||||||
// Used in exported QML, at root of the file
|
// Used in exported QML, at root of the file
|
||||||
QString m_exportedRootPropertiesString;
|
QString m_exportedRootPropertiesString;
|
||||||
// Used in exported QML, at ShaderEffect component of the file
|
// Used in exported QML, at ShaderEffect component of the file
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include "effectmakernodesmodel.h"
|
#include "effectmakernodesmodel.h"
|
||||||
|
#include "effectutils.h"
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@@ -38,44 +40,21 @@ QVariant EffectMakerNodesModel::data(const QModelIndex &index, int role) const
|
|||||||
return m_categories.at(index.row())->property(roleNames().value(role));
|
return m_categories.at(index.row())->property(roleNames().value(role));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectMakerNodesModel::findNodesPath()
|
|
||||||
{
|
|
||||||
if (m_nodesPath.exists() || m_probeNodesDir)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QDir nodesDir;
|
|
||||||
|
|
||||||
if (!qEnvironmentVariable("EFFECT_MAKER_NODES_PATH").isEmpty())
|
|
||||||
nodesDir.setPath(qEnvironmentVariable("EFFECT_MAKER_NODES_PATH"));
|
|
||||||
else if (Utils::HostOsInfo::isMacHost())
|
|
||||||
nodesDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/effect_maker_nodes");
|
|
||||||
|
|
||||||
// search for nodesDir from exec dir and up
|
|
||||||
if (nodesDir.dirName() == ".") {
|
|
||||||
m_probeNodesDir = true; // probe only once
|
|
||||||
nodesDir.setPath(QCoreApplication::applicationDirPath());
|
|
||||||
while (!nodesDir.cd("effect_maker_nodes") && nodesDir.cdUp())
|
|
||||||
; // do nothing
|
|
||||||
|
|
||||||
if (nodesDir.dirName() != "effect_maker_nodes") // bundlePathDir not found
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_nodesPath = Utils::FilePath::fromString(nodesDir.path());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EffectMakerNodesModel::loadModel()
|
void EffectMakerNodesModel::loadModel()
|
||||||
{
|
{
|
||||||
findNodesPath();
|
if (m_modelLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!m_nodesPath.exists()) {
|
auto nodesPath = Utils::FilePath::fromString(EffectUtils::nodesSourcesPath());
|
||||||
|
|
||||||
|
if (!nodesPath.exists()) {
|
||||||
qWarning() << __FUNCTION__ << "Effects not found.";
|
qWarning() << __FUNCTION__ << "Effects not found.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_categories = {};
|
m_categories = {};
|
||||||
|
|
||||||
QDirIterator itCategories(m_nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot);
|
QDirIterator itCategories(nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
while (itCategories.hasNext()) {
|
while (itCategories.hasNext()) {
|
||||||
itCategories.next();
|
itCategories.next();
|
||||||
|
|
||||||
@@ -85,7 +64,7 @@ void EffectMakerNodesModel::loadModel()
|
|||||||
QString catName = itCategories.fileName();
|
QString catName = itCategories.fileName();
|
||||||
|
|
||||||
QList<EffectNode *> effects = {};
|
QList<EffectNode *> effects = {};
|
||||||
Utils::FilePath categoryPath = m_nodesPath.resolvePath(itCategories.fileName());
|
Utils::FilePath categoryPath = nodesPath.resolvePath(itCategories.fileName());
|
||||||
QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files);
|
QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files);
|
||||||
while (itEffects.hasNext()) {
|
while (itEffects.hasNext()) {
|
||||||
itEffects.next();
|
itEffects.next();
|
||||||
@@ -102,6 +81,8 @@ void EffectMakerNodesModel::loadModel()
|
|||||||
return a->name() < b->name();
|
return a->name() < b->name();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_modelLoaded = true;
|
||||||
|
|
||||||
resetModel();
|
resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,5 +92,20 @@ void EffectMakerNodesModel::resetModel()
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace EffectMaker
|
void EffectMakerNodesModel::updateCanBeAdded(const QStringList &uniforms)
|
||||||
|
{
|
||||||
|
for (const EffectNodesCategory *cat : std::as_const(m_categories)) {
|
||||||
|
const QList<EffectNode *> nodes = cat->nodes();
|
||||||
|
for (EffectNode *node : nodes) {
|
||||||
|
bool match = false;
|
||||||
|
for (const QString &uniform : uniforms) {
|
||||||
|
match = node->hasUniform(uniform);
|
||||||
|
if (match)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node->setCanBeAdded(!match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace EffectMaker
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
#include "effectnodescategory.h"
|
#include "effectnodescategory.h"
|
||||||
|
|
||||||
#include <utils/filepath.h>
|
|
||||||
|
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
@@ -32,12 +30,14 @@ public:
|
|||||||
|
|
||||||
QList<EffectNodesCategory *> categories() const { return m_categories; }
|
QList<EffectNodesCategory *> categories() const { return m_categories; }
|
||||||
|
|
||||||
|
void updateCanBeAdded(const QStringList &uniforms);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void findNodesPath();
|
QString nodesSourcesPath() const;
|
||||||
|
|
||||||
QList<EffectNodesCategory *> m_categories;
|
QList<EffectNodesCategory *> m_categories;
|
||||||
Utils::FilePath m_nodesPath;
|
|
||||||
bool m_probeNodesDir = false;
|
bool m_probeNodesDir = false;
|
||||||
|
bool m_modelLoaded = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "effectmakerview.h"
|
#include "effectmakerview.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
|
|
||||||
#include <viewmanager.h>
|
#include <viewmanager.h>
|
||||||
@@ -10,6 +11,14 @@
|
|||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
|
|
||||||
|
static bool enableEffectMaker()
|
||||||
|
{
|
||||||
|
Utils::QtcSettings *settings = Core::ICore::settings();
|
||||||
|
const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44";
|
||||||
|
|
||||||
|
return settings->value(enableModelManagerKey, false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
class EffectMakerPlugin final : public ExtensionSystem::IPlugin
|
class EffectMakerPlugin final : public ExtensionSystem::IPlugin
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -17,11 +26,13 @@ class EffectMakerPlugin final : public ExtensionSystem::IPlugin
|
|||||||
|
|
||||||
bool delayedInitialize() final
|
bool delayedInitialize() final
|
||||||
{
|
{
|
||||||
auto designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
|
if (enableEffectMaker()) {
|
||||||
|
auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
|
||||||
auto &viewManager = designerPlugin->viewManager();
|
auto &viewManager = designerPlugin->viewManager();
|
||||||
|
|
||||||
viewManager.registerView(std::make_unique<EffectMakerView>(
|
viewManager.registerView(std::make_unique<EffectMakerView>(
|
||||||
QmlDesigner::QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly()));
|
QmlDesigner::QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly()));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ QHash<int, QByteArray> EffectMakerUniformsModel::roleNames() const
|
|||||||
roles[MinValueRole] = "uniformMinValue";
|
roles[MinValueRole] = "uniformMinValue";
|
||||||
roles[MaxValueRole] = "uniformMaxValue";
|
roles[MaxValueRole] = "uniformMaxValue";
|
||||||
roles[TypeRole] = "uniformType";
|
roles[TypeRole] = "uniformType";
|
||||||
|
roles[UseCustomValueRole] = "uniformUseCustomValue";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ private:
|
|||||||
MaxValueRole,
|
MaxValueRole,
|
||||||
MinValueRole,
|
MinValueRole,
|
||||||
TypeRole,
|
TypeRole,
|
||||||
|
UseCustomValueRole
|
||||||
};
|
};
|
||||||
|
|
||||||
QList<Uniform *> m_uniforms;
|
QList<Uniform *> m_uniforms;
|
||||||
|
|||||||
@@ -7,16 +7,11 @@
|
|||||||
#include "effectmakernodesmodel.h"
|
#include "effectmakernodesmodel.h"
|
||||||
#include "effectmakerwidget.h"
|
#include "effectmakerwidget.h"
|
||||||
|
|
||||||
#include "nodeinstanceview.h"
|
|
||||||
#include "qmldesignerconstants.h"
|
#include "qmldesignerconstants.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <modelnodeoperations.h>
|
||||||
|
|
||||||
#include <QQmlContext>
|
#include <coreplugin/icore.h>
|
||||||
#include <QQmlEngine>
|
|
||||||
#include <QQuickItem>
|
|
||||||
#include <QQuickView>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
|
|
||||||
@@ -51,6 +46,15 @@ QmlDesigner::WidgetInfo EffectMakerView::widgetInfo()
|
|||||||
if (m_widget.isNull()) {
|
if (m_widget.isNull()) {
|
||||||
m_widget = new EffectMakerWidget{this};
|
m_widget = new EffectMakerWidget{this};
|
||||||
|
|
||||||
|
connect(m_widget->effectMakerModel(), &EffectMakerModel::assignToSelectedTriggered, this,
|
||||||
|
[&] (const QString &effectPath) {
|
||||||
|
executeInTransaction("EffectMakerView::widgetInfo", [&] {
|
||||||
|
const QList<QmlDesigner::ModelNode> selectedNodes = selectedModelNodes();
|
||||||
|
for (const QmlDesigner::ModelNode &node : selectedNodes)
|
||||||
|
QmlDesigner::ModelNodeOperations::handleItemLibraryEffectDrop(effectPath, node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
auto context = new EffectMakerContext(m_widget.data());
|
auto context = new EffectMakerContext(m_widget.data());
|
||||||
Core::ICore::addContextObject(context);
|
Core::ICore::addContextObject(context);
|
||||||
}
|
}
|
||||||
@@ -66,7 +70,7 @@ void EffectMakerView::customNotification([[maybe_unused]] const AbstractView *vi
|
|||||||
{
|
{
|
||||||
if (identifier == "open_effectmaker_composition" && data.count() > 0) {
|
if (identifier == "open_effectmaker_composition" && data.count() > 0) {
|
||||||
const QString compositionPath = data[0].toString();
|
const QString compositionPath = data[0].toString();
|
||||||
m_widget->effectMakerModel()->openComposition(compositionPath);
|
m_widget->openComposition(compositionPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,4 +89,3 @@ void EffectMakerView::modelAboutToBeDetached(QmlDesigner::Model *model)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|
||||||
|
|||||||
@@ -43,4 +43,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|
||||||
|
|||||||
@@ -7,26 +7,33 @@
|
|||||||
#include "effectmakermodel.h"
|
#include "effectmakermodel.h"
|
||||||
#include "effectmakernodesmodel.h"
|
#include "effectmakernodesmodel.h"
|
||||||
#include "effectmakerview.h"
|
#include "effectmakerview.h"
|
||||||
|
#include "effectutils.h"
|
||||||
#include "propertyhandler.h"
|
#include "propertyhandler.h"
|
||||||
|
|
||||||
//#include "qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h"
|
//#include "qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h"
|
||||||
#include "qmldesignerconstants.h"
|
#include "qmldesignerconstants.h"
|
||||||
#include "qmldesignerplugin.h"
|
#include "qmldesignerplugin.h"
|
||||||
#include "qqmlcontext.h"
|
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <qmldesigner/documentmanager.h>
|
||||||
#include <qmldesigner/qmldesignerconstants.h>
|
#include <qmldesigner/qmldesignerconstants.h>
|
||||||
#include <qmldesigner/qmldesignerplugin.h>
|
#include <qmldesigner/qmldesignerplugin.h>
|
||||||
#include <studioquickwidget.h>
|
#include <studioquickwidget.h>
|
||||||
|
|
||||||
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QQmlContext>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
|
|
||||||
@@ -55,6 +62,7 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view)
|
|||||||
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
QmlDesigner::Theme::setupTheme(m_quickWidget->engine());
|
QmlDesigner::Theme::setupTheme(m_quickWidget->engine());
|
||||||
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||||
|
m_quickWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common");
|
||||||
m_quickWidget->setClearColor(QmlDesigner::Theme::getColor(
|
m_quickWidget->setClearColor(QmlDesigner::Theme::getColor(
|
||||||
QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
|
QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
|
||||||
|
|
||||||
@@ -70,12 +78,38 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view)
|
|||||||
|
|
||||||
m_quickWidget->rootContext()->setContextProperty("g_propertyData", &g_propertyData);
|
m_quickWidget->rootContext()->setContextProperty("g_propertyData", &g_propertyData);
|
||||||
|
|
||||||
|
QString blurPath = "file:" + EffectUtils::nodesSourcesPath() + "/common/";
|
||||||
|
g_propertyData.insert(QString("blur_vs_path"), QString(blurPath + "bluritems.vert.qsb"));
|
||||||
|
g_propertyData.insert(QString("blur_fs_path"), QString(blurPath + "bluritems.frag.qsb"));
|
||||||
|
|
||||||
auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend");
|
auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend");
|
||||||
map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())},
|
map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())},
|
||||||
{"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())},
|
{"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())},
|
||||||
{"rootView", QVariant::fromValue(this)}});
|
{"rootView", QVariant::fromValue(this)}});
|
||||||
QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(
|
QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(
|
||||||
this, QmlDesigner::Constants::EVENT_NEWEFFECTMAKER_TIME);
|
this, QmlDesigner::Constants::EVENT_NEWEFFECTMAKER_TIME);
|
||||||
|
|
||||||
|
connect(m_effectMakerModel.data(), &EffectMakerModel::nodesChanged, this, [this]() {
|
||||||
|
m_effectMakerNodesModel->updateCanBeAdded(m_effectMakerModel->uniformNames());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_effectMakerModel.data(), &EffectMakerModel::resourcesSaved,
|
||||||
|
this, [this](const QmlDesigner::TypeName &type, const Utils::FilePath &path) {
|
||||||
|
if (!m_importScan.timer) {
|
||||||
|
m_importScan.timer = new QTimer(this);
|
||||||
|
connect(m_importScan.timer, &QTimer::timeout,
|
||||||
|
this, &EffectMakerWidget::handleImportScanTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_importScan.timer->isActive() && !m_importScan.future.isFinished())
|
||||||
|
m_importScan.future.cancel();
|
||||||
|
|
||||||
|
m_importScan.counter = 0;
|
||||||
|
m_importScan.type = type;
|
||||||
|
m_importScan.path = path;
|
||||||
|
|
||||||
|
m_importScan.timer->start(100);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -119,6 +153,20 @@ void EffectMakerWidget::focusSection(int section)
|
|||||||
Q_UNUSED(section)
|
Q_UNUSED(section)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect EffectMakerWidget::screenRect() const
|
||||||
|
{
|
||||||
|
if (m_quickWidget && m_quickWidget->screen())
|
||||||
|
return m_quickWidget->screen()->availableGeometry();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint EffectMakerWidget::globalPos(const QPoint &point) const
|
||||||
|
{
|
||||||
|
if (m_quickWidget)
|
||||||
|
return m_quickWidget->mapToGlobal(point);
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
QSize EffectMakerWidget::sizeHint() const
|
QSize EffectMakerWidget::sizeHint() const
|
||||||
{
|
{
|
||||||
return {420, 420};
|
return {420, 420};
|
||||||
@@ -152,6 +200,21 @@ void EffectMakerWidget::initView()
|
|||||||
reloadQmlSource();
|
reloadQmlSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectMakerWidget::openComposition(const QString &path)
|
||||||
|
{
|
||||||
|
m_compositionPath = path;
|
||||||
|
|
||||||
|
if (effectMakerModel()->hasUnsavedChanges())
|
||||||
|
QMetaObject::invokeMethod(quickWidget()->rootObject(), "promptToSaveBeforeOpen");
|
||||||
|
else
|
||||||
|
doOpenComposition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectMakerWidget::doOpenComposition()
|
||||||
|
{
|
||||||
|
effectMakerModel()->openComposition(m_compositionPath);
|
||||||
|
}
|
||||||
|
|
||||||
void EffectMakerWidget::reloadQmlSource()
|
void EffectMakerWidget::reloadQmlSource()
|
||||||
{
|
{
|
||||||
const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml";
|
const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml";
|
||||||
@@ -159,5 +222,65 @@ void EffectMakerWidget::reloadQmlSource()
|
|||||||
m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath));
|
m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectMakerWidget::handleImportScanTimer()
|
||||||
|
{
|
||||||
|
++m_importScan.counter;
|
||||||
|
|
||||||
|
if (m_importScan.counter == 1) {
|
||||||
|
// Rescan the effect import to update code model
|
||||||
|
auto modelManager = QmlJS::ModelManagerInterface::instance();
|
||||||
|
if (modelManager) {
|
||||||
|
QmlJS::PathsAndLanguages pathToScan;
|
||||||
|
pathToScan.maybeInsert(m_importScan.path);
|
||||||
|
m_importScan.future = ::Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan,
|
||||||
|
modelManager->workingCopy(),
|
||||||
|
pathToScan, modelManager, true, true, true);
|
||||||
|
}
|
||||||
|
} else if (m_importScan.counter < 100) {
|
||||||
|
// We have to wait a while to ensure qmljs detects new files and updates its
|
||||||
|
// internal model. Then we force amend on rewriter to trigger qmljs snapshot update.
|
||||||
|
if (m_importScan.future.isCanceled() || m_importScan.future.isFinished())
|
||||||
|
m_importScan.counter = 100; // skip the timeout step
|
||||||
|
} else if (m_importScan.counter == 100) {
|
||||||
|
// Scanning is taking too long, abort
|
||||||
|
m_importScan.future.cancel();
|
||||||
|
m_importScan.timer->stop();
|
||||||
|
m_importScan.counter = 0;
|
||||||
|
} else if (m_importScan.counter == 101) {
|
||||||
|
if (m_effectMakerView->model() && m_effectMakerView->model()->rewriterView()) {
|
||||||
|
QmlDesigner::QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
|
||||||
|
m_effectMakerView->model()->rewriterView()->forceAmend();
|
||||||
|
}
|
||||||
|
} else if (m_importScan.counter == 102) {
|
||||||
|
if (m_effectMakerView->model()) {
|
||||||
|
// If type is in use, we have to reset puppet to update 2D view
|
||||||
|
if (!m_effectMakerView->allModelNodesOfType(
|
||||||
|
m_effectMakerView->model()->metaInfo(m_importScan.type)).isEmpty()) {
|
||||||
|
m_effectMakerView->resetPuppet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_importScan.counter >= 103) {
|
||||||
|
// Refresh property view by resetting selection if any selected node is of updated type
|
||||||
|
if (m_effectMakerView->model() && m_effectMakerView->hasSelectedModelNodes()) {
|
||||||
|
const auto nodes = m_effectMakerView->selectedModelNodes();
|
||||||
|
QmlDesigner::MetaInfoType metaType
|
||||||
|
= m_effectMakerView->model()->metaInfo(m_importScan.type).type();
|
||||||
|
bool match = false;
|
||||||
|
for (const QmlDesigner::ModelNode &node : nodes) {
|
||||||
|
if (node.metaInfo().type() == metaType) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
m_effectMakerView->clearSelectedModelNodes();
|
||||||
|
m_effectMakerView->setSelectedModelNodes(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_importScan.timer->stop();
|
||||||
|
m_importScan.counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,14 @@
|
|||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
|
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
|
#include <QFuture>
|
||||||
|
|
||||||
class StudioQuickWidget;
|
class StudioQuickWidget;
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QTimer;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
|
|
||||||
class EffectMakerView;
|
class EffectMakerView;
|
||||||
@@ -34,6 +39,7 @@ public:
|
|||||||
void delayedUpdateModel();
|
void delayedUpdateModel();
|
||||||
void updateModel();
|
void updateModel();
|
||||||
void initView();
|
void initView();
|
||||||
|
void openComposition(const QString &path);
|
||||||
|
|
||||||
StudioQuickWidget *quickWidget() const;
|
StudioQuickWidget *quickWidget() const;
|
||||||
QPointer<EffectMakerModel> effectMakerModel() const;
|
QPointer<EffectMakerModel> effectMakerModel() const;
|
||||||
@@ -41,6 +47,9 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void addEffectNode(const QString &nodeQenPath);
|
Q_INVOKABLE void addEffectNode(const QString &nodeQenPath);
|
||||||
Q_INVOKABLE void focusSection(int section);
|
Q_INVOKABLE void focusSection(int section);
|
||||||
|
Q_INVOKABLE void doOpenComposition();
|
||||||
|
Q_INVOKABLE QRect screenRect() const;
|
||||||
|
Q_INVOKABLE QPoint globalPos(const QPoint &point) const;
|
||||||
|
|
||||||
QSize sizeHint() const override;
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
@@ -49,6 +58,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void reloadQmlSource();
|
void reloadQmlSource();
|
||||||
|
void handleImportScanTimer();
|
||||||
|
|
||||||
QPointer<EffectMakerModel> m_effectMakerModel;
|
QPointer<EffectMakerModel> m_effectMakerModel;
|
||||||
QPointer<EffectMakerNodesModel> m_effectMakerNodesModel;
|
QPointer<EffectMakerNodesModel> m_effectMakerNodesModel;
|
||||||
@@ -56,6 +66,17 @@ private:
|
|||||||
QPointer<StudioQuickWidget> m_quickWidget;
|
QPointer<StudioQuickWidget> m_quickWidget;
|
||||||
QmlDesigner::QmlModelNodeProxy m_backendModelNode;
|
QmlDesigner::QmlModelNodeProxy m_backendModelNode;
|
||||||
QmlDesigner::QmlAnchorBindingProxy m_backendAnchorBinding;
|
QmlDesigner::QmlAnchorBindingProxy m_backendAnchorBinding;
|
||||||
|
|
||||||
|
struct ImportScanData {
|
||||||
|
QFuture<void> future;
|
||||||
|
int counter = 0;
|
||||||
|
QTimer *timer = nullptr;
|
||||||
|
QmlDesigner::TypeName type;
|
||||||
|
Utils::FilePath path;
|
||||||
|
};
|
||||||
|
|
||||||
|
ImportScanData m_importScan;
|
||||||
|
QString m_compositionPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include "effectnode.h"
|
#include "effectnode.h"
|
||||||
|
#include "compositionnode.h"
|
||||||
|
#include "uniform.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -22,6 +24,12 @@ EffectNode::EffectNode(const QString &qenPath)
|
|||||||
iconPath = QStringLiteral("%1/%2").arg(parentDir.path(), "placeholder.svg");
|
iconPath = QStringLiteral("%1/%2").arg(parentDir.path(), "placeholder.svg");
|
||||||
}
|
}
|
||||||
m_iconPath = QUrl::fromLocalFile(iconPath);
|
m_iconPath = QUrl::fromLocalFile(iconPath);
|
||||||
|
|
||||||
|
CompositionNode node({}, qenPath);
|
||||||
|
const QList<Uniform *> uniforms = node.uniforms();
|
||||||
|
|
||||||
|
for (const Uniform *uniform : uniforms)
|
||||||
|
m_uniformNames.insert(uniform->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EffectNode::name() const
|
QString EffectNode::name() const
|
||||||
@@ -39,5 +47,18 @@ QString EffectNode::qenPath() const
|
|||||||
return m_qenPath;
|
return m_qenPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectNode::setCanBeAdded(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled != m_canBeAdded) {
|
||||||
|
m_canBeAdded = enabled;
|
||||||
|
emit canBeAddedChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectNode::hasUniform(const QString &name)
|
||||||
|
{
|
||||||
|
return m_uniformNames.contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
@@ -16,6 +17,7 @@ class EffectNode : public QObject
|
|||||||
Q_PROPERTY(QString nodeDescription MEMBER m_description CONSTANT)
|
Q_PROPERTY(QString nodeDescription MEMBER m_description CONSTANT)
|
||||||
Q_PROPERTY(QUrl nodeIcon MEMBER m_iconPath CONSTANT)
|
Q_PROPERTY(QUrl nodeIcon MEMBER m_iconPath CONSTANT)
|
||||||
Q_PROPERTY(QString nodeQenPath MEMBER m_qenPath CONSTANT)
|
Q_PROPERTY(QString nodeQenPath MEMBER m_qenPath CONSTANT)
|
||||||
|
Q_PROPERTY(bool canBeAdded MEMBER m_canBeAdded NOTIFY canBeAddedChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EffectNode(const QString &qenPath);
|
EffectNode(const QString &qenPath);
|
||||||
@@ -24,11 +26,20 @@ public:
|
|||||||
QString description() const;
|
QString description() const;
|
||||||
QString qenPath() const;
|
QString qenPath() const;
|
||||||
|
|
||||||
|
void setCanBeAdded(bool enabled);
|
||||||
|
|
||||||
|
bool hasUniform(const QString &name);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void canBeAddedChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_description;
|
QString m_description;
|
||||||
QString m_qenPath;
|
QString m_qenPath;
|
||||||
QUrl m_iconPath;
|
QUrl m_iconPath;
|
||||||
|
bool m_canBeAdded = true;
|
||||||
|
QSet<QString> m_uniformNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "effectutils.h"
|
#include "effectutils.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
||||||
namespace EffectMaker {
|
namespace EffectMaker {
|
||||||
@@ -20,5 +22,14 @@ QString EffectUtils::codeFromJsonArray(const QJsonArray &codeArray)
|
|||||||
return codeString;
|
return codeString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString EffectUtils::nodesSourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/effectMakerNodes";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/effectMakerNodes").toString();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public:
|
|||||||
EffectUtils() = delete;
|
EffectUtils() = delete;
|
||||||
|
|
||||||
static QString codeFromJsonArray(const QJsonArray &codeArray);
|
static QString codeFromJsonArray(const QJsonArray &codeArray);
|
||||||
|
|
||||||
|
static QString nodesSourcesPath();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace EffectMaker
|
} // namespace EffectMaker
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class Uniform : public QObject
|
|||||||
Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged)
|
Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged)
|
||||||
Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT)
|
Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT)
|
||||||
Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT)
|
Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT)
|
||||||
|
Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue CONSTANT)
|
||||||
|
Q_PROPERTY(QVariant uniformUseCustomValue MEMBER m_useCustomValue CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type
|
enum class Type
|
||||||
|
|||||||
@@ -839,10 +839,12 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
|
collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
|
||||||
collectioneditorconstants.h
|
collectioneditorconstants.h
|
||||||
collectioneditorutils.cpp collectioneditorutils.h
|
collectioneditorutils.cpp collectioneditorutils.h
|
||||||
|
collectionimporttools.cpp collectionimporttools.h
|
||||||
collectionlistmodel.cpp collectionlistmodel.h
|
collectionlistmodel.cpp collectionlistmodel.h
|
||||||
collectionsourcemodel.cpp collectionsourcemodel.h
|
collectionsourcemodel.cpp collectionsourcemodel.h
|
||||||
collectionview.cpp collectionview.h
|
collectionview.cpp collectionview.h
|
||||||
collectionwidget.cpp collectionwidget.h
|
collectionwidget.cpp collectionwidget.h
|
||||||
|
datastoremodelnode.cpp datastoremodelnode.h
|
||||||
)
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(QmlDesigner
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
|||||||
@@ -13,19 +13,12 @@
|
|||||||
#include "qmldesignerplugin.h"
|
#include "qmldesignerplugin.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
|
||||||
#include <extensionsystem/pluginspec.h>
|
|
||||||
|
|
||||||
#include <studioquickwidget.h>
|
#include <studioquickwidget.h>
|
||||||
|
|
||||||
#include <coreplugin/fileutils.h>
|
#include <coreplugin/fileutils.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/messagebox.h>
|
#include <coreplugin/messagebox.h>
|
||||||
|
|
||||||
#include <projectexplorer/projecttree.h>
|
|
||||||
#include <projectexplorer/target.h>
|
|
||||||
#include <projectexplorer/project.h>
|
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
@@ -192,12 +185,12 @@ QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, co
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openEffectMaker)
|
bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectMaker)
|
||||||
{
|
{
|
||||||
bool created = QFile(effectPath).open(QIODevice::WriteOnly);
|
bool created = QFile(effectPath).open(QIODevice::WriteOnly);
|
||||||
|
|
||||||
if (created && openEffectMaker) {
|
if (created && openInEffectMaker) {
|
||||||
ModelNodeOperations::openEffectMaker(effectPath);
|
openEffectMaker(effectPath);
|
||||||
emit directoryCreated(QFileInfo(effectPath).absolutePath());
|
emit directoryCreated(QFileInfo(effectPath).absolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,21 +360,8 @@ QSet<QString> AssetsLibraryWidget::supportedAssetSuffixes(bool complex)
|
|||||||
return suffixes;
|
return suffixes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEffectMakerActivated()
|
|
||||||
{
|
|
||||||
const QVector<ExtensionSystem::PluginSpec *> specs = ExtensionSystem::PluginManager::plugins();
|
|
||||||
return std::find_if(specs.begin(), specs.end(),
|
|
||||||
[](ExtensionSystem::PluginSpec *spec) {
|
|
||||||
return spec->name() == "EffectMakerNew" && spec->isEffectivelyEnabled();
|
|
||||||
})
|
|
||||||
!= specs.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetsLibraryWidget::openEffectMaker(const QString &filePath)
|
void AssetsLibraryWidget::openEffectMaker(const QString &filePath)
|
||||||
{
|
{
|
||||||
if (isEffectMakerActivated())
|
|
||||||
m_assetsView->emitCustomNotification("open_effectmaker_composition", {}, {filePath});
|
|
||||||
else
|
|
||||||
ModelNodeOperations::openEffectMaker(filePath);
|
ModelNodeOperations::openEffectMaker(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||