forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/qds/dev'
Conflicts: src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp src/plugins/updateinfo/updateinfoplugin.cpp src/tools/qml2puppet/CMakeLists.txt src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp Change-Id: I6b65ce2d603453fcebc2291574d55db80853f247
This commit is contained in:
@@ -31,6 +31,13 @@
|
||||
can be built with CMake. You can open the \e CMakeLists.txt project file in
|
||||
Qt Creator to continue developing the project.
|
||||
|
||||
\target wizard-template-note
|
||||
\note Since \QDS 3.9.0, \QDS project wizard templates generate projects that
|
||||
automatically checkout and build the Qt Quick Studio Components from
|
||||
\l{https://code.qt.io/cgit/qt-labs/qtquickdesigner-components.git/} {Qt Code Review},
|
||||
using CMake. To turn off this feature, use the option \e BUILD_QDS_COMPONENTS
|
||||
in the CMake configuration.
|
||||
|
||||
\if defined(qtdesignstudio)
|
||||
For more information, see \l{Designer-Developer Workflow}.
|
||||
\else
|
||||
@@ -149,10 +156,8 @@
|
||||
|
||||
\section1 Adding Qt Quick Designer Components to Qt Installations
|
||||
|
||||
If you use Qt Quick Studio Components or Effects in your project, you have
|
||||
to check out and install the \e {Qt Quick Designer Components} module from
|
||||
\l{https://code.qt.io/cgit/qt-labs/qtquickdesigner-components.git/}
|
||||
{Qt Code Review}.
|
||||
Since \QDS 3.9, the Qt Quick Studio Components module is installed by default
|
||||
as part of the application. You can also install the module manually.
|
||||
|
||||
For example:
|
||||
\list 1
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
your project as a fully working C++ application with developers.
|
||||
|
||||
If you add or remove QML files in \QDS, you have to regenerate the
|
||||
\e CMakeLists.txt project configuration file by selecting \uicontrol Build
|
||||
> \uicontrol Run > \uicontrol {Generate CMakeLists.txt Files}.
|
||||
\e CMakeLists.txt project configuration file by selecting \uicontrol File
|
||||
> \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}.
|
||||
|
||||
If you use Git, you can clone an example project
|
||||
\l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/playground/AuroraCluster0}
|
||||
@@ -99,6 +99,8 @@
|
||||
This isn't mandatory.
|
||||
|
||||
\li Generate CMake files and C++ source files that are used to compile the application into
|
||||
an executable file by selecting \uicontrol Build > \uicontrol{Generate CMakeLists.txt files}.
|
||||
an executable file by selecting \uicontrol File > \uicontrol {Export Project} >
|
||||
\uicontrol {Generate CMake Build Files}.
|
||||
|
||||
\endlist
|
||||
*/
|
||||
|
||||
@@ -10,11 +10,19 @@
|
||||
\note \QBF is included in the
|
||||
\l{https://www.qt.io/pricing}{\QDS Enterprise license}.
|
||||
|
||||
\QBF is delivered with \QDS as a developer plugin that you can install to
|
||||
the Desktop version of Figma. To install the plugin, open the Plugin Manager
|
||||
of Figma and press the plus button to create a new plugin. Then choose the
|
||||
\e Manifest.json file that comes with \QDS.
|
||||
You need both Figma and Qt accounts to use \QBF in \QDS.
|
||||
To use \QBF in \QDS:
|
||||
\list 1
|
||||
\li Go to \l {https://www.figma.com/community/plugin/1167809465162924409/Qt-Bridge-for-Figma}
|
||||
{\QBF plugin page}.
|
||||
\li Select \uicontrol {Try it out}.
|
||||
\li Select your logged in Figma account.
|
||||
\li After the plugin loads, select \uicontrol Run.
|
||||
\li Select \uicontrol Export, to get the \e {.qtbridge} file in your
|
||||
local drive.
|
||||
\li In \QDS, drag the file to the \uicontrol 2D, \uicontrol 3D, \uicontrol Assets,
|
||||
or \uicontrol Navigator view in an open project .
|
||||
\endlist
|
||||
|
||||
You can launch the Figma plugin from \uicontrol Plugins >
|
||||
\uicontrol Development > \uicontrol {\QBF} in Figma.
|
||||
You can launch the installed Figma plugin from \uicontrol Plugins > \uicontrol {\QBF} in Figma.
|
||||
*/
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
set of files (icons, translation files, and so on) and you don't want
|
||||
to run the risk of losing the files.
|
||||
|
||||
To package your application, select \uicontrol Build >
|
||||
\uicontrol {Generate QRC Resource File}. Then select the
|
||||
files to package in the \uicontrol {Add Resources} dialog.
|
||||
To package your application, select \uicontrol File >
|
||||
\uicontrol {Export Project} > \uicontrol {Generate QRC Resource File}.
|
||||
Then select the files to package in the \uicontrol {Add Resources} dialog.
|
||||
|
||||
\image studio-add-resources.png "Add Resources dialog"
|
||||
|
||||
@@ -44,10 +44,11 @@
|
||||
|
||||
\section1 Embedding Resources into Applications
|
||||
|
||||
Alternatively, you can embedd the resources into your application by
|
||||
selecting \uicontrol Build > \uicontrol {Generate Deployable Package}.
|
||||
Select the location for the .qmlrc file, and then select the files to
|
||||
embedd in the \uicontrol {Add Resources} dialog.
|
||||
Alternatively, you can embed the resources into your application by
|
||||
selecting \uicontrol File > \uicontrol {Export Project} >
|
||||
\uicontrol {Generate Deployable Package}. Select the location for
|
||||
the .qmlrc file, and then select the files to embed in the
|
||||
\uicontrol {Add Resources} dialog.
|
||||
|
||||
When you select \uicontrol OK, \QDS creates a resource collection file
|
||||
(.qmlrc) in the location you selected.
|
||||
|
||||
@@ -23,6 +23,7 @@ Image {
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: (mouse) => {
|
||||
if (mouse.button === Qt.LeftButton)
|
||||
@@ -31,4 +32,20 @@ Image {
|
||||
root.showContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
visible: mouseArea.containsMouse
|
||||
// contentWidth is not calculated correctly by the toolTip (resulting in a wider tooltip than
|
||||
// needed). Using a helper Text to calculate the correct width
|
||||
contentWidth: helperText.width
|
||||
bottomInset: -2
|
||||
text: modelData.textureToolTip
|
||||
delay: 1000
|
||||
|
||||
Text {
|
||||
id: helperText
|
||||
text: modelData.textureToolTip
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ TreeViewDelegate {
|
||||
required property Item assetsRoot
|
||||
|
||||
property bool hasChildWithDropHover: false
|
||||
property bool isHoveringDrop: false
|
||||
property bool isHighlighted: false
|
||||
readonly property string suffix: model.fileName.substr(-4)
|
||||
readonly property bool isFont: root.suffix === ".ttf" || root.suffix === ".otf"
|
||||
readonly property bool isEffect: root.suffix === ".qep"
|
||||
@@ -26,7 +26,12 @@ TreeViewDelegate {
|
||||
readonly property int __dirItemHeight: 21
|
||||
|
||||
implicitHeight: root.__isDirectory ? root.__dirItemHeight : root.__fileItemHeight
|
||||
implicitWidth: root.assetsView.width > 0 ? root.assetsView.width : 10
|
||||
implicitWidth: {
|
||||
if (root.assetsView.verticalScrollBar.scrollBarVisible)
|
||||
return root.assetsView.width - root.indentation - root.assetsView.verticalScrollBar.width
|
||||
else
|
||||
return root.assetsView.width - root.indentation
|
||||
}
|
||||
|
||||
leftMargin: root.__isDirectory ? 0 : thumbnailImage.width
|
||||
|
||||
@@ -54,17 +59,6 @@ TreeViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
onImplicitWidthChanged: {
|
||||
// a small hack, to fix a glitch: when resizing the width of the tree view,
|
||||
// the widths of the delegate items remain the same as before, unless we re-set
|
||||
// that width explicitly.
|
||||
var newWidth = root.implicitWidth - (root.assetsView.verticalScrollBar.scrollBarVisible
|
||||
? root.assetsView.verticalScrollBar.width
|
||||
: 0)
|
||||
bg.width = newWidth
|
||||
bg.implicitWidth = newWidth
|
||||
}
|
||||
|
||||
onDepthChanged: {
|
||||
if (root.depth > root.initialDepth && root.initialDepth >= 0)
|
||||
root.depth = root.initialDepth
|
||||
@@ -73,8 +67,10 @@ TreeViewDelegate {
|
||||
background: Rectangle {
|
||||
id: bg
|
||||
|
||||
width: root.implicitWidth
|
||||
|
||||
color: {
|
||||
if (root.__isDirectory && (root.isHoveringDrop || root.hasChildWithDropHover))
|
||||
if (root.__isDirectory && (root.isHighlighted || root.hasChildWithDropHover))
|
||||
return StudioTheme.Values.themeInteraction
|
||||
|
||||
if (!root.__isDirectory && root.assetsView.selectedAssets[root.__itemPath])
|
||||
@@ -120,40 +116,6 @@ TreeViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: treeDropArea
|
||||
|
||||
enabled: true
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: (drag) => {
|
||||
root.assetsRoot.updateDropExtFiles(drag)
|
||||
root.isHoveringDrop = drag.accepted && root.assetsRoot.dropSimpleExtFiles.length > 0
|
||||
if (root.isHoveringDrop)
|
||||
root.assetsView.startDropHoverOver(root.__currentRow)
|
||||
}
|
||||
|
||||
onDropped: (drag) => {
|
||||
root.isHoveringDrop = false
|
||||
root.assetsView.endDropHover(root.__currentRow)
|
||||
|
||||
let dirPath = root.__isDirectory
|
||||
? model.filePath
|
||||
: assetsModel.parentDirPath(model.filePath);
|
||||
|
||||
rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles,
|
||||
root.assetsRoot.dropComplexExtFiles,
|
||||
dirPath)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (root.isHoveringDrop) {
|
||||
root.isHoveringDrop = false
|
||||
root.assetsView.endDropHover(root.__currentRow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
@@ -247,6 +209,14 @@ TreeViewDelegate {
|
||||
}
|
||||
} // MouseArea
|
||||
|
||||
function getDirPath()
|
||||
{
|
||||
if (root.__isDirectory)
|
||||
return model.filePath
|
||||
else
|
||||
return assetsModel.parentDirPath(model.filePath)
|
||||
}
|
||||
|
||||
function __openContextMenuForCurrentRow()
|
||||
{
|
||||
let modelIndex = assetsModel.indexForPath(model.filePath)
|
||||
|
||||
@@ -183,4 +183,17 @@ StudioControls.Menu {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("New Effect")
|
||||
visible: assetsModel.canCreateEffects()
|
||||
|
||||
NewEffectDialog {
|
||||
id: newEffectDialog
|
||||
parent: root.assetsView
|
||||
dirPath: root.__dirPath
|
||||
}
|
||||
|
||||
onTriggered: newEffectDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,8 +250,12 @@ TreeView {
|
||||
function startDropHoverOver(row)
|
||||
{
|
||||
let index = root.__modelIndex(row)
|
||||
if (assetsModel.isDirectory(index))
|
||||
if (assetsModel.isDirectory(index)) {
|
||||
let item = root.__getDelegateItemForIndex(index)
|
||||
if (item)
|
||||
item.isHighlighted = true
|
||||
return
|
||||
}
|
||||
|
||||
let parentItem = root.__getDelegateParentForIndex(index)
|
||||
if (parentItem)
|
||||
@@ -261,8 +265,12 @@ TreeView {
|
||||
function endDropHover(row)
|
||||
{
|
||||
let index = root.__modelIndex(row)
|
||||
if (assetsModel.isDirectory(index))
|
||||
if (assetsModel.isDirectory(index)) {
|
||||
let item = root.__getDelegateItemForIndex(index)
|
||||
if (item)
|
||||
item.isHighlighted = false
|
||||
return
|
||||
}
|
||||
|
||||
let parentItem = root.__getDelegateParentForIndex(index)
|
||||
if (parentItem)
|
||||
@@ -292,6 +300,12 @@ TreeView {
|
||||
return root.itemAtCell(parentCell)
|
||||
}
|
||||
|
||||
function __getDelegateItemForIndex(index)
|
||||
{
|
||||
let cell = root.cellAtIndex(index)
|
||||
return root.itemAtCell(cell)
|
||||
}
|
||||
|
||||
function __modelIndex(row)
|
||||
{
|
||||
// The modelIndex() function exists since 6.3. In Qt 6.3, this modelIndex() function was a
|
||||
@@ -303,6 +317,76 @@ TreeView {
|
||||
return root.modelIndex(row, 0)
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: dropArea
|
||||
enabled: true
|
||||
anchors.fill: parent
|
||||
|
||||
property bool __isHoveringDrop: false
|
||||
property int __rowHoveringOver: -1
|
||||
|
||||
function __rowAndItem(drag)
|
||||
{
|
||||
let pos = dropArea.mapToItem(root, drag.x, drag.y)
|
||||
let cell = root.cellAtPos(pos.x, pos.y, true)
|
||||
let item = root.itemAtCell(cell)
|
||||
|
||||
return [cell.y, item]
|
||||
}
|
||||
|
||||
onEntered: (drag) => {
|
||||
root.assetsRoot.updateDropExtFiles(drag)
|
||||
|
||||
let [row, item] = dropArea.__rowAndItem(drag)
|
||||
dropArea.__isHoveringDrop = drag.accepted && root.assetsRoot.dropSimpleExtFiles.length > 0
|
||||
|
||||
if (item && dropArea.__isHoveringDrop)
|
||||
root.startDropHoverOver(row)
|
||||
|
||||
dropArea.__rowHoveringOver = row
|
||||
}
|
||||
|
||||
onDropped: (drag) => {
|
||||
let [row, item] = dropArea.__rowAndItem(drag)
|
||||
|
||||
if (item) {
|
||||
root.endDropHover(row)
|
||||
|
||||
let dirPath = item.getDirPath()
|
||||
|
||||
rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles,
|
||||
root.assetsRoot.dropComplexExtFiles,
|
||||
dirPath)
|
||||
}
|
||||
|
||||
dropArea.__isHoveringDrop = false
|
||||
dropArea.__rowHoveringOver = -1
|
||||
}
|
||||
|
||||
onPositionChanged: (drag) => {
|
||||
let [row, item] = dropArea.__rowAndItem(drag)
|
||||
|
||||
if (dropArea.__rowHoveringOver !== row && dropArea.__rowHoveringOver > -1) {
|
||||
root.endDropHover(dropArea.__rowHoveringOver)
|
||||
|
||||
if (item)
|
||||
root.startDropHoverOver(row)
|
||||
}
|
||||
|
||||
dropArea.__rowHoveringOver = row
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (!dropArea.__isHoveringDrop || dropArea.__rowHoveringOver === -1)
|
||||
return
|
||||
|
||||
root.endDropHover(dropArea.__rowHoveringOver)
|
||||
|
||||
dropArea.__isHoveringDrop = false
|
||||
dropArea.__rowHoveringOver = -1
|
||||
}
|
||||
}
|
||||
|
||||
delegate: AssetDelegate {
|
||||
assetsView: root
|
||||
assetsRoot: root.assetsRoot
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import HelperWidgets as HelperWidgets
|
||||
import StudioControls as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
title: qsTr("Create New Effect")
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
modal: true
|
||||
|
||||
required property string dirPath
|
||||
readonly property int __maxPath: 32
|
||||
|
||||
HelperWidgets.RegExpValidator {
|
||||
id: effectNameValidator
|
||||
regExp: /^[A-Z]\w[A-Za-z0-9_]*$/
|
||||
}
|
||||
|
||||
ErrorDialog {
|
||||
id: creationFailedDialog
|
||||
title: qsTr("Could not create effect")
|
||||
message: qsTr("An error occurred while trying to create the effect.")
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
spacing: 2
|
||||
|
||||
Row {
|
||||
Text {
|
||||
text: qsTr("Effect name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
|
||||
StudioControls.TextField {
|
||||
id: effectName
|
||||
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: effectNameValidator
|
||||
|
||||
Keys.onEnterPressed: btnCreate.onClicked()
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Effect name cannot be empty.")
|
||||
color: "#ff0000"
|
||||
anchors.right: parent.right
|
||||
visible: effectName.text === ""
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Effect path is too long.")
|
||||
color: "#ff0000"
|
||||
anchors.right: parent.right
|
||||
visible: effectName.text.length > root.__maxPath
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnCreate
|
||||
|
||||
text: qsTr("Create")
|
||||
enabled: effectName.text !== ""
|
||||
&& effectName.length >=3
|
||||
&& effectName.text.length <= root.__maxPath
|
||||
onClicked: {
|
||||
const path = assetsModel.getUniqueEffectPath(root.dirPath, effectName.text)
|
||||
if (assetsModel.createNewEffect(path))
|
||||
root.accept()
|
||||
else
|
||||
creationFailedDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
const path = assetsModel.getUniqueEffectPath(root.dirPath, "Effect01")
|
||||
effectName.text = path.split('/').pop().replace(".qep", '')
|
||||
effectName.selectAll()
|
||||
effectName.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
@@ -40,5 +40,13 @@ T.Menu {
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
border.width: StudioTheme.Values.border
|
||||
MouseArea {
|
||||
// This mouse area is here to eat clicks that are not handled by menu items
|
||||
// to prevent them going through to the underlying view.
|
||||
// This is primarily problem with disabled menu items, but right clicks would go
|
||||
// through enabled menu items as well.
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.AllButtons
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"supportedProjectTypes": [ ],
|
||||
"id": "J.QEP",
|
||||
"category": "U.QEP",
|
||||
"trDescription": "Creates an Effect Maker file.",
|
||||
"trDisplayName": "Effect File (Effect Maker)",
|
||||
"trDisplayCategory": "Effects",
|
||||
"iconText": "qep",
|
||||
"platformIndependent": true,
|
||||
"enabled": "%{JS: value('Features').indexOf('QmlDesigner.Wizards.Enterprise') >= 0}",
|
||||
"featuresRequired": [ "QmlDesigner.Wizards.Enterprise" ],
|
||||
|
||||
"options": [
|
||||
{ "key": "EffectFile", "value": "%{Class}.qep" },
|
||||
{ "key": "DoNotOpenFile", "value": "true" }
|
||||
],
|
||||
|
||||
"pages" :
|
||||
[
|
||||
{
|
||||
"trDisplayName": "Define Class",
|
||||
"trShortTitle": "Details",
|
||||
"typeId": "Fields",
|
||||
"data" :
|
||||
[
|
||||
{
|
||||
"name": "Class",
|
||||
"trDisplayName": "Effect name:",
|
||||
"mandatory": true,
|
||||
"type": "LineEdit",
|
||||
"data": {
|
||||
"validator": "(?:[A-Z_][a-zA-Z_0-9]*|)",
|
||||
"fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "TargetPath",
|
||||
"type": "PathChooser",
|
||||
"trDisplayName": "Path:",
|
||||
"mandatory": true,
|
||||
"data":
|
||||
{
|
||||
"kind": "existingDirectory",
|
||||
"basePath": "%{InitialPath}",
|
||||
"path": "%{InitialPath}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"generators" :
|
||||
[
|
||||
{
|
||||
"typeId": "File",
|
||||
"data":
|
||||
{
|
||||
"source": "file.qep",
|
||||
"target": "%{TargetPath}/%{EffectFile}",
|
||||
"openInEditor": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,6 +10,11 @@ set(CMAKE_AUTOMOC ON)
|
||||
find_package(QT NAMES Qt6 COMPONENTS Gui Qml Quick)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick)
|
||||
|
||||
# To build this application you need Qt 6.2.0 or higher
|
||||
if (Qt6_VERSION VERSION_LESS 6.2.0)
|
||||
message(FATAL_ERROR "You need Qt 6.2.0 or newer to build the application.")
|
||||
endif()
|
||||
|
||||
qt_add_executable(${CMAKE_PROJECT_NAME} src/main.cpp)
|
||||
|
||||
# qt_standard_project_setup() requires Qt 6.3 or higher. See https://doc.qt.io/qt-6/qt-standard-project-setup.html for details.
|
||||
|
||||
@@ -63,7 +63,7 @@ static const char *qtQuickUISuffix = "ui.qml";
|
||||
|
||||
static void maybeAddPath(ViewerContext &context, const Utils::FilePath &path)
|
||||
{
|
||||
if (!path.isEmpty() && !(context.paths.count(path) > 0))
|
||||
if (!path.isEmpty() && (context.paths.count(path) <= 0))
|
||||
context.paths.insert(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@ add_qtc_library(QmlDesignerUtils STATIC
|
||||
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils
|
||||
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils
|
||||
SOURCES
|
||||
asset.cpp asset.h
|
||||
designersettings.cpp designersettings.h
|
||||
hdrimage.cpp hdrimage.h
|
||||
imageutils.cpp imageutils.h
|
||||
qmldesignerutils_global.h
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "assetslibraryiconprovider.h"
|
||||
#include "assetslibrarymodel.h"
|
||||
#include "asset.h"
|
||||
#include "modelnodeoperations.h"
|
||||
|
||||
#include <theme.h>
|
||||
@@ -25,12 +25,16 @@ QPixmap AssetsLibraryIconProvider::requestPixmap(const QString &id, QSize *size,
|
||||
pixmap = m_thumbnails[id];
|
||||
} else {
|
||||
pixmap = fetchPixmap(id, requestedSize);
|
||||
if (pixmap.isNull())
|
||||
bool haveValidImage = true;
|
||||
if (pixmap.isNull()) {
|
||||
pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png");
|
||||
haveValidImage = false;
|
||||
}
|
||||
|
||||
if (requestedSize.isValid())
|
||||
pixmap = pixmap.scaled(requestedSize, Qt::KeepAspectRatio);
|
||||
|
||||
if (haveValidImage)
|
||||
m_thumbnails[id] = pixmap;
|
||||
}
|
||||
|
||||
@@ -53,24 +57,25 @@ QPixmap AssetsLibraryIconProvider::generateFontIcons(const QString &filePath, co
|
||||
|
||||
QPixmap AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &requestedSize) const
|
||||
{
|
||||
const QString suffix = "*." + id.split('.').last().toLower();
|
||||
Asset asset(id);
|
||||
|
||||
if (id == "browse") {
|
||||
return Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/browse.png");
|
||||
} else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) {
|
||||
} else if (asset.isFont()) {
|
||||
return generateFontIcons(id, requestedSize);
|
||||
} else if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) {
|
||||
} else if (asset.isImage()) {
|
||||
return Utils::StyleHelper::dpiSpecificImageFile(id);
|
||||
} else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) {
|
||||
} else if (asset.isTexture3D()) {
|
||||
return HdrImage{id}.toPixmap();
|
||||
} else {
|
||||
QString type;
|
||||
if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix))
|
||||
if (asset.isShader())
|
||||
type = "shader";
|
||||
else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix))
|
||||
else if (asset.isAudio())
|
||||
type = "sound";
|
||||
else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix))
|
||||
else if (asset.isVideo())
|
||||
type = "video";
|
||||
else if (AssetsLibraryModel::supportedEffectMakerSuffixes().contains(suffix))
|
||||
else if (asset.isEffect())
|
||||
type = QmlDesigner::ModelNodeOperations::getEffectIcon(id);
|
||||
|
||||
QString pathTemplate = QString(":/AssetsLibrary/images/asset_%1%2.png").arg(type);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemModel>
|
||||
#include <QImageReader>
|
||||
#include <QMessageBox>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "asset.h"
|
||||
#include "assetslibrarymodel.h"
|
||||
|
||||
#include <modelnodeoperations.h>
|
||||
@@ -134,32 +134,10 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &
|
||||
bool AssetsLibraryModel::addNewFolder(const QString &folderPath)
|
||||
{
|
||||
QString iterPath = folderPath;
|
||||
static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
|
||||
QDir dir{folderPath};
|
||||
|
||||
while (dir.exists()) {
|
||||
// if the folder name ends with a number, increment it
|
||||
QRegularExpressionMatch match = rgx.match(iterPath);
|
||||
if (match.hasMatch()) { // ends with a number
|
||||
QString numStr = match.captured(0);
|
||||
int num = match.captured(0).toInt();
|
||||
|
||||
// get number of padding zeros, ex: for "005" = 2
|
||||
int nPaddingZeros = 0;
|
||||
for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros);
|
||||
|
||||
++num;
|
||||
|
||||
// if the incremented number's digits increased, decrease the padding zeros
|
||||
if (std::fmod(std::log10(num), 1.0) == 0)
|
||||
--nPaddingZeros;
|
||||
|
||||
iterPath = folderPath.mid(0, match.capturedStart())
|
||||
+ QString('0').repeated(nPaddingZeros)
|
||||
+ QString::number(num);
|
||||
} else {
|
||||
iterPath = folderPath + '1';
|
||||
}
|
||||
iterPath = getUniqueName(iterPath);
|
||||
|
||||
dir.setPath(iterPath);
|
||||
}
|
||||
@@ -180,12 +158,49 @@ bool AssetsLibraryModel::deleteFolderRecursively(const QModelIndex &folderIndex)
|
||||
bool AssetsLibraryModel::allFilePathsAreImages(const QStringList &filePaths) const
|
||||
{
|
||||
return Utils::allOf(filePaths, [](const QString &path) {
|
||||
const QString suffix = "*." + path.split('.').last().toLower();
|
||||
|
||||
return AssetsLibraryModel::supportedImageSuffixes().contains(suffix);
|
||||
return Asset(path).isImage();
|
||||
});
|
||||
}
|
||||
|
||||
QString AssetsLibraryModel::getUniqueEffectPath(const QString &parentFolder, const QString &effectName)
|
||||
{
|
||||
auto genEffectPath = [=](const QString &name) {
|
||||
return QString(parentFolder + "/" + name + ".qep");
|
||||
};
|
||||
|
||||
QString uniqueName = effectName;
|
||||
QString path = genEffectPath(uniqueName);
|
||||
QFileInfo file{path};
|
||||
|
||||
while (file.exists()) {
|
||||
uniqueName = getUniqueName(uniqueName);
|
||||
|
||||
path = genEffectPath(uniqueName);
|
||||
file.setFile(path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool AssetsLibraryModel::createNewEffect(const QString &effectPath, bool openEffectMaker)
|
||||
{
|
||||
bool created = QFile(effectPath).open(QIODevice::WriteOnly);
|
||||
|
||||
if (created && openEffectMaker)
|
||||
ModelNodeOperations::openEffectMaker(effectPath);
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
bool AssetsLibraryModel::canCreateEffects() const
|
||||
{
|
||||
#ifdef LICENSECHECKER
|
||||
return checkLicense() == FoundLicense::enterprise;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QString path = m_sourceFsModel->filePath(sourceParent);
|
||||
@@ -242,6 +257,36 @@ void AssetsLibraryModel::syncHaveFiles()
|
||||
setHaveFiles(checkHaveFiles());
|
||||
}
|
||||
|
||||
QString AssetsLibraryModel::getUniqueName(const QString &oldName) {
|
||||
static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
|
||||
|
||||
QString uniqueName = oldName;
|
||||
// if the folder name ends with a number, increment it
|
||||
QRegularExpressionMatch match = rgx.match(uniqueName);
|
||||
if (match.hasMatch()) { // ends with a number
|
||||
QString numStr = match.captured(0);
|
||||
int num = match.captured(0).toInt();
|
||||
|
||||
// get number of padding zeros, ex: for "005" = 2
|
||||
int nPaddingZeros = 0;
|
||||
for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros);
|
||||
|
||||
++num;
|
||||
|
||||
// if the incremented number's digits increased, decrease the padding zeros
|
||||
if (std::fmod(std::log10(num), 1.0) == 0)
|
||||
--nPaddingZeros;
|
||||
|
||||
uniqueName = oldName.mid(0, match.capturedStart())
|
||||
+ QString('0').repeated(nPaddingZeros)
|
||||
+ QString::number(num);
|
||||
} else {
|
||||
uniqueName = oldName + '1';
|
||||
}
|
||||
|
||||
return uniqueName;
|
||||
}
|
||||
|
||||
void AssetsLibraryModel::setRootPath(const QString &newPath)
|
||||
{
|
||||
beginResetModel();
|
||||
@@ -252,7 +297,7 @@ void AssetsLibraryModel::setRootPath(const QString &newPath)
|
||||
m_rootPath = newPath;
|
||||
m_sourceFsModel->setRootPath(newPath);
|
||||
|
||||
m_sourceFsModel->setNameFilters(supportedSuffixes().values());
|
||||
m_sourceFsModel->setNameFilters(Asset::supportedSuffixes().values());
|
||||
m_sourceFsModel->setNameFilterDisables(false);
|
||||
|
||||
endResetModel();
|
||||
@@ -327,80 +372,4 @@ QString AssetsLibraryModel::parentDirPath(const QString &path) const
|
||||
return filePath(parentIdx);
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedImageSuffixes()
|
||||
{
|
||||
static QStringList retList;
|
||||
if (retList.isEmpty()) {
|
||||
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
|
||||
for (const QByteArray &suffix : suffixes)
|
||||
retList.append("*." + QString::fromUtf8(suffix));
|
||||
}
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedShaderSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.frag", "*.vert",
|
||||
"*.glsl", "*.glslv", "*.glslf",
|
||||
"*.vsh", "*.fsh"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedFontSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.ttf", "*.otf"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedAudioSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.wav", "*.mp3"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedVideoSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.mp4"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes()
|
||||
{
|
||||
// These are file types only supported by 3D textures
|
||||
static QStringList retList {"*.hdr", "*.ktx"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &AssetsLibraryModel::supportedEffectMakerSuffixes()
|
||||
{
|
||||
// These are file types only supported by Effect Maker
|
||||
static QStringList retList {"*.qep"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QSet<QString> &AssetsLibraryModel::supportedSuffixes()
|
||||
{
|
||||
static QSet<QString> allSuffixes;
|
||||
if (allSuffixes.isEmpty()) {
|
||||
auto insertSuffixes = [](const QStringList &suffixes) {
|
||||
for (const auto &suffix : suffixes)
|
||||
allSuffixes.insert(suffix);
|
||||
};
|
||||
insertSuffixes(supportedImageSuffixes());
|
||||
insertSuffixes(supportedShaderSuffixes());
|
||||
insertSuffixes(supportedFontSuffixes());
|
||||
insertSuffixes(supportedAudioSuffixes());
|
||||
insertSuffixes(supportedVideoSuffixes());
|
||||
insertSuffixes(supportedTexture3DSuffixes());
|
||||
insertSuffixes(supportedEffectMakerSuffixes());
|
||||
}
|
||||
return allSuffixes;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/filesystemwatcher.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -47,6 +47,11 @@ public:
|
||||
Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex);
|
||||
Q_INVOKABLE bool allFilePathsAreImages(const QStringList &filePaths) const;
|
||||
|
||||
Q_INVOKABLE QString getUniqueEffectPath(const QString &parentFolder, const QString &effectName);
|
||||
Q_INVOKABLE bool createNewEffect(const QString &effectPath, bool openEffectMaker = true);
|
||||
|
||||
Q_INVOKABLE bool canCreateEffects() const;
|
||||
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
int result = QSortFilterProxyModel::columnCount(parent);
|
||||
@@ -55,16 +60,6 @@ public:
|
||||
|
||||
bool haveFiles() const { return m_haveFiles; }
|
||||
|
||||
static const QStringList &supportedImageSuffixes();
|
||||
static const QStringList &supportedFragmentShaderSuffixes();
|
||||
static const QStringList &supportedShaderSuffixes();
|
||||
static const QStringList &supportedFontSuffixes();
|
||||
static const QStringList &supportedAudioSuffixes();
|
||||
static const QStringList &supportedVideoSuffixes();
|
||||
static const QStringList &supportedTexture3DSuffixes();
|
||||
static const QStringList &supportedEffectMakerSuffixes();
|
||||
static const QSet<QString> &supportedSuffixes();
|
||||
|
||||
signals:
|
||||
void directoryLoaded(const QString &path);
|
||||
void rootPathChanged();
|
||||
@@ -79,6 +74,7 @@ private:
|
||||
void destroyBackendModel();
|
||||
bool checkHaveFiles(const QModelIndex &parentIdx) const;
|
||||
bool checkHaveFiles() const;
|
||||
QString getUniqueName(const QString &oldName);
|
||||
|
||||
QString m_searchText;
|
||||
QString m_rootPath;
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
#include "assetslibrarywidget.h"
|
||||
|
||||
#include "assetslibrarymodel.h"
|
||||
#include "asset.h"
|
||||
#include "assetslibraryiconprovider.h"
|
||||
#include "assetslibrarymodel.h"
|
||||
|
||||
#include <theme.h>
|
||||
|
||||
@@ -229,7 +230,7 @@ QSet<QString> AssetsLibraryWidget::supportedAssetSuffixes(bool complex)
|
||||
|
||||
QSet<QString> suffixes;
|
||||
for (const AddResourceHandler &handler : handlers) {
|
||||
if (AssetsLibraryModel::supportedSuffixes().contains(handler.filter) != complex)
|
||||
if (Asset(handler.filter).isSupported() != complex)
|
||||
suffixes.insert(handler.filter);
|
||||
}
|
||||
|
||||
@@ -290,32 +291,32 @@ void AssetsLibraryWidget::startDragAsset(const QStringList &assetPaths, const QP
|
||||
|
||||
QPair<QString, QByteArray> AssetsLibraryWidget::getAssetTypeAndData(const QString &assetPath)
|
||||
{
|
||||
QString suffix = "*." + assetPath.split('.').last().toLower();
|
||||
if (!suffix.isEmpty()) {
|
||||
if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) {
|
||||
Asset asset(assetPath);
|
||||
if (asset.hasSuffix()) {
|
||||
if (asset.isImage()) {
|
||||
// Data: Image format (suffix)
|
||||
return {Constants::MIME_TYPE_ASSET_IMAGE, suffix.toUtf8()};
|
||||
} else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) {
|
||||
return {Constants::MIME_TYPE_ASSET_IMAGE, asset.suffix().toUtf8()};
|
||||
} else if (asset.isFont()) {
|
||||
// Data: Font family name
|
||||
QRawFont font(assetPath, 10);
|
||||
QString fontFamily = font.isValid() ? font.familyName() : "";
|
||||
return {Constants::MIME_TYPE_ASSET_FONT, fontFamily.toUtf8()};
|
||||
} else if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix)) {
|
||||
} else if (asset.isShader()) {
|
||||
// Data: shader type, frament (f) or vertex (v)
|
||||
return {Constants::MIME_TYPE_ASSET_SHADER,
|
||||
AssetsLibraryModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"};
|
||||
} else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix)) {
|
||||
asset.isFragmentShader() ? "f" : "v"};
|
||||
} else if (asset.isAudio()) {
|
||||
// No extra data for sounds
|
||||
return {Constants::MIME_TYPE_ASSET_SOUND, {}};
|
||||
} else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix)) {
|
||||
} else if (asset.isVideo()) {
|
||||
// No extra data for videos
|
||||
return {Constants::MIME_TYPE_ASSET_VIDEO, {}};
|
||||
} else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) {
|
||||
} else if (asset.isTexture3D()) {
|
||||
// Data: Image format (suffix)
|
||||
return {Constants::MIME_TYPE_ASSET_TEXTURE3D, suffix.toUtf8()};
|
||||
} else if (AssetsLibraryModel::supportedEffectMakerSuffixes().contains(suffix)) {
|
||||
return {Constants::MIME_TYPE_ASSET_TEXTURE3D, asset.suffix().toUtf8()};
|
||||
} else if (asset.isEffect()) {
|
||||
// Data: Effect Maker format (suffix)
|
||||
return {Constants::MIME_TYPE_ASSET_EFFECT, suffix.toUtf8()};
|
||||
return {Constants::MIME_TYPE_ASSET_EFFECT, asset.suffix().toUtf8()};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -1525,8 +1525,11 @@ void mergeWithTemplate(const SelectionContext &selectionContext, ExternalDepende
|
||||
|
||||
const QString templateFile = getTemplateDialog(projectPath);
|
||||
|
||||
if (QFileInfo::exists(templateFile))
|
||||
StylesheetMerger::styleMerge(selectionContext.view()->model(), templateFile, externalDependencies);
|
||||
if (QFileInfo::exists(templateFile)) {
|
||||
StylesheetMerger::styleMerge(Utils::FilePath::fromString(templateFile),
|
||||
selectionContext.view()->model(),
|
||||
externalDependencies);
|
||||
}
|
||||
}
|
||||
|
||||
void removeGroup(const SelectionContext &selectionContext)
|
||||
@@ -1674,7 +1677,7 @@ void openEffectMaker(const QString &filePath)
|
||||
}
|
||||
}
|
||||
|
||||
Utils::FilePath getEffectsDirectory()
|
||||
Utils::FilePath getEffectsImportDirectory()
|
||||
{
|
||||
QString defaultDir = "asset_imports/Effects";
|
||||
Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
|
||||
@@ -1688,6 +1691,11 @@ Utils::FilePath getEffectsDirectory()
|
||||
return effectsPath;
|
||||
}
|
||||
|
||||
QString getEffectsDefaultDirectory(const QString &defaultDir)
|
||||
{
|
||||
return getAssetDefaultDirectory("effects", defaultDir);
|
||||
}
|
||||
|
||||
QString getEffectIcon(const QString &effectPath)
|
||||
{
|
||||
const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget();
|
||||
@@ -1715,7 +1723,7 @@ bool useLayerEffect()
|
||||
bool validateEffect(const QString &effectPath)
|
||||
{
|
||||
const QString effectName = QFileInfo(effectPath).baseName();
|
||||
Utils::FilePath effectsResDir = ModelNodeOperations::getEffectsDirectory();
|
||||
Utils::FilePath effectsResDir = ModelNodeOperations::getEffectsImportDirectory();
|
||||
Utils::FilePath qmlPath = effectsResDir.resolvePath(effectName + "/" + effectName + ".qml");
|
||||
if (!qmlPath.exists()) {
|
||||
QMessageBox msgBox;
|
||||
|
||||
@@ -119,7 +119,8 @@ void addMouseAreaFill(const SelectionContext &selectionContext);
|
||||
void openSignalDialog(const SelectionContext &selectionContext);
|
||||
void updateImported3DAsset(const SelectionContext &selectionContext);
|
||||
|
||||
QMLDESIGNERCORE_EXPORT Utils::FilePath getEffectsDirectory();
|
||||
QMLDESIGNERCORE_EXPORT Utils::FilePath getEffectsImportDirectory();
|
||||
QMLDESIGNERCORE_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir);
|
||||
void openEffectMaker(const QString &filePath);
|
||||
QString getEffectIcon(const QString &effectPath);
|
||||
bool useLayerEffect();
|
||||
|
||||
@@ -76,19 +76,19 @@ bool Navigation2dFilter::wheelEvent(QWheelEvent *event)
|
||||
bool zoomChangedConnected = QObject::isSignalConnected(zoomChangedSignal);
|
||||
|
||||
if (zoomChangedConnected) {
|
||||
const double globalMouseSpeed =
|
||||
QmlDesignerPlugin::settings().value(DesignerSettingsKey::EDITOR_ZOOM_FACTOR).toDouble();
|
||||
|
||||
double speed = globalMouseSpeed/20.;
|
||||
if (Utils::HostOsInfo::isMacHost())
|
||||
speed = 1.0/200.;
|
||||
|
||||
if (QPointF delta = event->pixelDelta(); !delta.isNull()) {
|
||||
double speed = 1.0 / 200.0;
|
||||
bool isMac = Utils::HostOsInfo::isMacHost();
|
||||
if (QPointF delta = event->pixelDelta(); !delta.isNull() && isMac) {
|
||||
double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y();
|
||||
emit zoomChanged(dist * speed, event->position());
|
||||
event->accept();
|
||||
return true;
|
||||
} else if (QPointF delta = event->angleDelta(); !delta.isNull()) {
|
||||
|
||||
const double globalMouseSpeed =
|
||||
QmlDesignerPlugin::settings().value(DesignerSettingsKey::EDITOR_ZOOM_FACTOR).toDouble();
|
||||
speed = globalMouseSpeed / 20.0;
|
||||
|
||||
constexpr double degreePerStep = 15.;
|
||||
constexpr double stepCount = 8.;
|
||||
double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y();
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
|
||||
#include "contentlibrarytexture.h"
|
||||
|
||||
#include "imageutils.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QString &path, const QUrl &icon)
|
||||
: QObject(parent)
|
||||
, m_path(path)
|
||||
, m_icon(icon) {}
|
||||
, m_icon(icon)
|
||||
{
|
||||
m_toolTip = QLatin1String("%1\n%2").arg(path.split('/').last(), ImageUtils::imageInfo(path));
|
||||
}
|
||||
|
||||
bool ContentLibraryTexture::filter(const QString &searchText)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ class ContentLibraryTexture : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString texturePath MEMBER m_path CONSTANT)
|
||||
Q_PROPERTY(QString textureToolTip MEMBER m_toolTip CONSTANT)
|
||||
Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT)
|
||||
Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged)
|
||||
|
||||
@@ -29,6 +30,7 @@ signals:
|
||||
|
||||
private:
|
||||
QString m_path;
|
||||
QString m_toolTip;
|
||||
QUrl m_icon;
|
||||
|
||||
bool m_visible = true;
|
||||
|
||||
@@ -65,19 +65,27 @@ ModelNode CreateTexture::createTextureFromImage(const QString &assetPath, AddTex
|
||||
|
||||
NodeMetaInfo metaInfo = m_view->model()->qtQuick3DTextureMetaInfo();
|
||||
|
||||
Utils::FilePath imagePath = ModelNodeOperations::getImagesDefaultDirectory()
|
||||
.pathAppended(Utils::FilePath::fromString(assetPath).fileName());
|
||||
QString sourceVal = imagePath.relativePathFrom(
|
||||
QmlDesigner::DocumentManager::currentFilePath()).toString();
|
||||
Utils::FilePath currentDocumentPath = QmlDesigner::DocumentManager::currentFilePath();
|
||||
Utils::FilePath imageTargetPath;
|
||||
if (m_importFile) {
|
||||
QString assetName = Utils::FilePath::fromString(assetPath).fileName();
|
||||
// if the asset had to be imported from somewhere else, then assetPath is the source where
|
||||
// the asset was taken from, and we have to compute where it was placed in the project.
|
||||
imageTargetPath = ModelNodeOperations::getImagesDefaultDirectory().pathAppended(assetName);
|
||||
} else {
|
||||
imageTargetPath = Utils::FilePath::fromString(assetPath);
|
||||
}
|
||||
|
||||
ModelNode newTexNode = m_view->getTextureDefaultInstance(sourceVal);
|
||||
QString textureSource = imageTargetPath.relativePathFrom(currentDocumentPath).toString();
|
||||
|
||||
ModelNode newTexNode = m_view->getTextureDefaultInstance(textureSource);
|
||||
if (!newTexNode.isValid()) {
|
||||
newTexNode = m_view->createModelNode("QtQuick3D.Texture",
|
||||
metaInfo.majorVersion(),
|
||||
metaInfo.minorVersion());
|
||||
newTexNode.validId();
|
||||
VariantProperty sourceProp = newTexNode.variantProperty("source");
|
||||
sourceProp.setValue(sourceVal);
|
||||
sourceProp.setValue(textureSource);
|
||||
matLib.defaultNodeListProperty().reparentHere(newTexNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent,
|
||||
|
||||
QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog,
|
||||
[actionType, view](const QColor &color) {
|
||||
Edit3DViewConfig::setColor(view, actionType, color);
|
||||
Edit3DViewConfig::setColors(view, actionType, {color});
|
||||
});
|
||||
|
||||
QObject::connect(dialog, &QColorDialog::colorSelected, dialog,
|
||||
@@ -52,13 +52,13 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent,
|
||||
if (colorSelected)
|
||||
colorSelected();
|
||||
|
||||
Edit3DViewConfig::saveColor(key, color);
|
||||
Edit3DViewConfig::saveColors(key, {color});
|
||||
});
|
||||
|
||||
if (Edit3DViewConfig::isColorValid(oldColorConfig)) {
|
||||
if (Edit3DViewConfig::colorsValid(oldColorConfig)) {
|
||||
QObject::connect(dialog, &QColorDialog::rejected, dialog,
|
||||
[actionType, oldColorConfig, view]() {
|
||||
Edit3DViewConfig::setColor(view, actionType, oldColorConfig);
|
||||
Edit3DViewConfig::setColors(view, actionType, oldColorConfig);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "edit3dactions.h"
|
||||
#include "edit3dview.h"
|
||||
#include "edit3dwidget.h"
|
||||
|
||||
#include <viewmanager.h>
|
||||
#include <nodeinstanceview.h>
|
||||
@@ -18,7 +17,7 @@ namespace QmlDesigner {
|
||||
|
||||
Edit3DActionTemplate::Edit3DActionTemplate(const QString &description,
|
||||
SelectionContextOperation action,
|
||||
AbstractView *view,
|
||||
Edit3DView *view,
|
||||
View3DActionType type)
|
||||
: DefaultAction(description)
|
||||
, m_action(action)
|
||||
@@ -45,12 +44,14 @@ Edit3DAction::Edit3DAction(const QByteArray &menuId,
|
||||
bool checked,
|
||||
const QIcon &iconOff,
|
||||
const QIcon &iconOn,
|
||||
AbstractView *view,
|
||||
Edit3DView *view,
|
||||
SelectionContextOperation selectionAction,
|
||||
const QString &toolTip)
|
||||
: AbstractAction(new Edit3DActionTemplate(description, selectionAction, view, type))
|
||||
, m_menuId(menuId)
|
||||
, m_actionTemplate(qobject_cast<Edit3DActionTemplate *>(defaultAction()))
|
||||
{
|
||||
view->registerEdit3DAction(this);
|
||||
action()->setShortcut(key);
|
||||
action()->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
action()->setCheckable(checkable);
|
||||
@@ -73,11 +74,21 @@ Edit3DAction::Edit3DAction(const QByteArray &menuId,
|
||||
}
|
||||
}
|
||||
|
||||
Edit3DAction::~Edit3DAction()
|
||||
{
|
||||
m_actionTemplate->m_view->unregisterEdit3DAction(this);
|
||||
}
|
||||
|
||||
QByteArray Edit3DAction::category() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
View3DActionType Edit3DAction::actionType() const
|
||||
{
|
||||
return m_actionTemplate->m_type;
|
||||
}
|
||||
|
||||
bool Edit3DAction::isVisible([[maybe_unused]] const SelectionContext &selectionContext) const
|
||||
{
|
||||
return true;
|
||||
@@ -96,7 +107,7 @@ Edit3DCameraAction::Edit3DCameraAction(const QByteArray &menuId,
|
||||
bool checked,
|
||||
const QIcon &iconOff,
|
||||
const QIcon &iconOn,
|
||||
AbstractView *view,
|
||||
Edit3DView *view,
|
||||
SelectionContextOperation selectionAction)
|
||||
: Edit3DAction(menuId, type, description, key, checkable, checked, iconOff, iconOn, view, selectionAction)
|
||||
{
|
||||
|
||||
@@ -10,19 +10,22 @@
|
||||
namespace QmlDesigner {
|
||||
|
||||
using SelectionContextOperation = std::function<void(const SelectionContext &)>;
|
||||
class Edit3DView;
|
||||
|
||||
class Edit3DActionTemplate : public DefaultAction
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Edit3DActionTemplate(const QString &description,
|
||||
SelectionContextOperation action,
|
||||
AbstractView *view,
|
||||
Edit3DView *view,
|
||||
View3DActionType type);
|
||||
|
||||
void actionTriggered(bool b) override;
|
||||
|
||||
SelectionContextOperation m_action;
|
||||
AbstractView *m_view;
|
||||
Edit3DView *m_view = nullptr;
|
||||
View3DActionType m_type;
|
||||
};
|
||||
|
||||
@@ -37,10 +40,12 @@ public:
|
||||
bool checked,
|
||||
const QIcon &iconOff,
|
||||
const QIcon &iconOn,
|
||||
AbstractView *view,
|
||||
Edit3DView *view,
|
||||
SelectionContextOperation selectionAction = nullptr,
|
||||
const QString &toolTip = {});
|
||||
|
||||
virtual ~Edit3DAction();
|
||||
|
||||
QByteArray category() const override;
|
||||
|
||||
int priority() const override
|
||||
@@ -58,12 +63,15 @@ public:
|
||||
return m_menuId;
|
||||
}
|
||||
|
||||
View3DActionType actionType() const;
|
||||
|
||||
protected:
|
||||
bool isVisible(const SelectionContext &selectionContext) const override;
|
||||
bool isEnabled(const SelectionContext &selectionContext) const override;
|
||||
|
||||
private:
|
||||
QByteArray m_menuId;
|
||||
Edit3DActionTemplate *m_actionTemplate = nullptr;
|
||||
};
|
||||
|
||||
class Edit3DCameraAction : public Edit3DAction
|
||||
@@ -77,7 +85,7 @@ public:
|
||||
bool checked,
|
||||
const QIcon &iconOff,
|
||||
const QIcon &iconOn,
|
||||
AbstractView *view,
|
||||
Edit3DView *view,
|
||||
SelectionContextOperation selectionAction = nullptr);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -38,7 +38,9 @@ Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies)
|
||||
}
|
||||
|
||||
Edit3DView::~Edit3DView()
|
||||
{}
|
||||
{
|
||||
qDeleteAll(m_edit3DActions);
|
||||
}
|
||||
|
||||
void Edit3DView::createEdit3DWidget()
|
||||
{
|
||||
@@ -197,6 +199,31 @@ void Edit3DView::onEntriesChanged()
|
||||
m_compressionTimer.start();
|
||||
}
|
||||
|
||||
void Edit3DView::registerEdit3DAction(Edit3DAction *action)
|
||||
{
|
||||
View3DActionType actionType = action->actionType();
|
||||
if (actionType == View3DActionType::Empty)
|
||||
return;
|
||||
|
||||
if (m_edit3DActions.contains(actionType)) {
|
||||
Edit3DAction *formerAction = m_edit3DActions.value(actionType);
|
||||
if (formerAction == action)
|
||||
return;
|
||||
|
||||
qWarning() << Q_FUNC_INFO << __LINE__ << "Reregistering action for" << int(actionType);
|
||||
delete formerAction;
|
||||
}
|
||||
|
||||
m_edit3DActions.insert(actionType, action);
|
||||
}
|
||||
|
||||
void Edit3DView::unregisterEdit3DAction(Edit3DAction *action)
|
||||
{
|
||||
View3DActionType actionType = action->actionType();
|
||||
if (m_edit3DActions.value(actionType, nullptr) == action)
|
||||
m_edit3DActions.remove(actionType);
|
||||
}
|
||||
|
||||
void Edit3DView::handleEntriesChanged()
|
||||
{
|
||||
if (!model())
|
||||
@@ -389,12 +416,12 @@ Edit3DAction *Edit3DView::createResetColorAction(QAction *syncBackgroundColorAct
|
||||
|
||||
auto operation = [this, syncBackgroundColorAction](const SelectionContext &) {
|
||||
QList<QColor> bgColors = {QRgb(0x222222), QRgb(0x999999)};
|
||||
Edit3DViewConfig::setColor(this, View3DActionType::SelectBackgroundColor, bgColors);
|
||||
Edit3DViewConfig::saveColor(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors);
|
||||
Edit3DViewConfig::setColors(this, View3DActionType::SelectBackgroundColor, bgColors);
|
||||
Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors);
|
||||
|
||||
QColor gridColor{0xaaaaaa};
|
||||
Edit3DViewConfig::setColor(this, View3DActionType::SelectGridColor, gridColor);
|
||||
Edit3DViewConfig::saveColor(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, gridColor);
|
||||
Edit3DViewConfig::setColors(this, View3DActionType::SelectGridColor, {gridColor});
|
||||
Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, {gridColor});
|
||||
|
||||
if (syncBackgroundColorAction->isChecked()) {
|
||||
Edit3DViewConfig::set(this, View3DActionType::SyncBackgroundColor, false);
|
||||
@@ -812,6 +839,11 @@ QVector<Edit3DAction *> Edit3DView::backgroundColorActions() const
|
||||
return m_backgroundColorActions;
|
||||
}
|
||||
|
||||
Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const
|
||||
{
|
||||
return m_edit3DActions.value(type, nullptr);
|
||||
}
|
||||
|
||||
void Edit3DView::addQuick3DImport()
|
||||
{
|
||||
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
QVector<Edit3DAction *> rightActions() const;
|
||||
QVector<Edit3DAction *> visibilityToggleActions() const;
|
||||
QVector<Edit3DAction *> backgroundColorActions() const;
|
||||
Edit3DAction *edit3DAction(View3DActionType type) const;
|
||||
void setSeeker(SeekerSlider *slider);
|
||||
|
||||
void addQuick3DImport();
|
||||
@@ -77,6 +78,9 @@ private:
|
||||
None
|
||||
};
|
||||
|
||||
void registerEdit3DAction(Edit3DAction *action);
|
||||
void unregisterEdit3DAction(Edit3DAction *action);
|
||||
|
||||
void createEdit3DWidget();
|
||||
void checkImports();
|
||||
void handleEntriesChanged();
|
||||
@@ -92,6 +96,7 @@ private:
|
||||
QVector<Edit3DAction *> m_rightActions;
|
||||
QVector<Edit3DAction *> m_visibilityToggleActions;
|
||||
QVector<Edit3DAction *> m_backgroundColorActions;
|
||||
QMap<View3DActionType, Edit3DAction *> m_edit3DActions;
|
||||
Edit3DAction *m_selectionModeAction = nullptr;
|
||||
Edit3DAction *m_moveToolAction = nullptr;
|
||||
Edit3DAction *m_rotateToolAction = nullptr;
|
||||
@@ -120,6 +125,8 @@ private:
|
||||
NodeAtPosReqType m_nodeAtPosReqType;
|
||||
QPoint m_contextMenuPos;
|
||||
QTimer m_compressionTimer;
|
||||
|
||||
friend class Edit3DAction;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
@@ -28,26 +28,18 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
static void setColor(AbstractView *view, View3DActionType type, const QList<QColor> &colorConfig)
|
||||
static void setColors(AbstractView *view, View3DActionType type, const QList<QColor> &colorConfig)
|
||||
{
|
||||
if (colorConfig.size() == 1)
|
||||
setColor(view, type, colorConfig.at(0));
|
||||
else
|
||||
setVariant(view, type, QVariant::fromValue(colorConfig));
|
||||
}
|
||||
|
||||
static void setColor(AbstractView *view, View3DActionType type, const QColor &color)
|
||||
{
|
||||
setVariant(view, type, QVariant::fromValue(color));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void set(AbstractView *view, View3DActionType type, const T &value)
|
||||
{
|
||||
setVariant(view, type, QVariant::fromValue(value));
|
||||
}
|
||||
|
||||
static void saveColor(const QByteArray &key, const QList<QColor> &colorConfig)
|
||||
static void saveColors(const QByteArray &key, const QList<QColor> &colorConfig)
|
||||
{
|
||||
QStringList colorNames = Utils::transform(colorConfig, [](const QColor &color) {
|
||||
return color.name();
|
||||
@@ -56,12 +48,7 @@ public:
|
||||
saveVariant(key, QVariant::fromValue(colorNames));
|
||||
}
|
||||
|
||||
static void saveColor(const QByteArray &key, const QColor &color)
|
||||
{
|
||||
saveVariant(key, QVariant::fromValue(color.name()));
|
||||
}
|
||||
|
||||
static bool isColorValid(const QList<QColor> &colorConfig) { return !colorConfig.isEmpty(); }
|
||||
static bool colorsValid(const QList<QColor> &colorConfig) { return !colorConfig.isEmpty(); }
|
||||
|
||||
private:
|
||||
static void setVariant(AbstractView *view, View3DActionType type, const QVariant &colorConfig)
|
||||
|
||||
@@ -232,6 +232,14 @@ void Edit3DWidget::createContextMenu()
|
||||
view()->setSelectedModelNode(parentNode);
|
||||
});
|
||||
|
||||
QAction *defaultToggleGroupAction = view()->edit3DAction(View3DActionType::SelectionModeToggle)->action();
|
||||
m_toggleGroupAction = m_contextMenu->addAction(tr("Group Selection Mode"), [&](const bool &mode) {
|
||||
view()->edit3DAction(View3DActionType::SelectionModeToggle)->action()->trigger();
|
||||
});
|
||||
connect(defaultToggleGroupAction, &QAction::toggled, m_toggleGroupAction, &QAction::setChecked);
|
||||
m_toggleGroupAction->setCheckable(true);
|
||||
m_toggleGroupAction->setChecked(defaultToggleGroupAction->isChecked());
|
||||
|
||||
m_contextMenu->addSeparator();
|
||||
}
|
||||
|
||||
@@ -375,6 +383,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
|
||||
m_alignCameraAction->setEnabled(isCamera);
|
||||
m_alignViewAction->setEnabled(isCamera);
|
||||
m_selectParentAction->setEnabled(selectionExcludingRoot);
|
||||
m_toggleGroupAction->setEnabled(true);
|
||||
|
||||
m_contextMenu->popup(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ private:
|
||||
QPointer<QAction> m_alignCameraAction;
|
||||
QPointer<QAction> m_alignViewAction;
|
||||
QPointer<QAction> m_selectParentAction;
|
||||
QPointer<QAction> m_toggleGroupAction;
|
||||
QPointer<QMenu> m_createSubMenu;
|
||||
ModelNode m_contextMenuTarget;
|
||||
QVector3D m_contextMenuPos3d;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include "materialbrowsertexturesmodel.h"
|
||||
|
||||
#include "designeractionmanager.h"
|
||||
#include "designmodewidget.h"
|
||||
#include "imageutils.h"
|
||||
#include "qmldesignerplugin.h"
|
||||
#include "qmlobjectnode.h"
|
||||
#include "variantproperty.h"
|
||||
@@ -52,24 +52,18 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role)
|
||||
return m_textureList.at(index.row()).internalId();
|
||||
|
||||
if (role == RoleTexToolTip) {
|
||||
QString source = QmlObjectNode(m_textureList.at(index.row())).modelValue("source").toString();
|
||||
QString source = data(index, RoleTexSource).toString(); // absolute path
|
||||
if (source.isEmpty())
|
||||
return tr("Texture has no source image.");
|
||||
|
||||
const QString noData = tr("Texture has no data.");
|
||||
ModelNode texNode = m_textureList.at(index.row());
|
||||
QString info = ImageUtils::imageInfo(source);
|
||||
|
||||
auto op = QmlDesignerPlugin::instance()->viewManager().designerActionManager()
|
||||
.modelNodePreviewOperation(m_textureList.at(index.row()));
|
||||
if (!op)
|
||||
return noData;
|
||||
if (info.isEmpty())
|
||||
return tr("Texture has no data.");
|
||||
|
||||
QVariantMap imgMap = op(m_textureList.at(index.row())).toMap();
|
||||
if (imgMap.isEmpty())
|
||||
return noData;
|
||||
|
||||
return QLatin1String("%1\n%2\n%3").arg(imgMap.value("id").toString(),
|
||||
source.split('/').last(),
|
||||
imgMap.value("info").toString());
|
||||
QString sourceRelative = QmlObjectNode(texNode).modelValue("source").toString();
|
||||
return QLatin1String("%1\n%2\n%3").arg(texNode.id(), sourceRelative, info);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -797,10 +797,7 @@ void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, int rowNu
|
||||
if (!targetNode.metaInfo().isQtQuick3DModel())
|
||||
return;
|
||||
|
||||
QByteArray data = mimeData->data(Constants::MIME_TYPE_MATERIAL);
|
||||
QDataStream stream(data);
|
||||
qint32 internalId;
|
||||
stream >> internalId;
|
||||
qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt();
|
||||
ModelNode matNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
m_view->executeInTransaction(__FUNCTION__, [&] {
|
||||
|
||||
@@ -253,10 +253,7 @@ void NavigatorView::dragStarted(QMimeData *mimeData)
|
||||
m_widget->setDragType(itemLibraryEntry.typeName());
|
||||
m_widget->update();
|
||||
} else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) {
|
||||
QByteArray data = mimeData->data(Constants::MIME_TYPE_MATERIAL);
|
||||
QDataStream stream(data);
|
||||
qint32 internalId;
|
||||
stream >> internalId;
|
||||
qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt();
|
||||
ModelNode matNode = modelNodeForInternalId(internalId);
|
||||
|
||||
m_widget->setDragType(matNode.metaInfo().typeName());
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "propertyeditorimageprovider.h"
|
||||
#include "assetslibrarymodel.h"
|
||||
#include "asset.h"
|
||||
|
||||
#include <projectexplorer/target.h>
|
||||
#include <utils/hdrimage.h>
|
||||
@@ -16,12 +16,12 @@ namespace QmlDesigner {
|
||||
QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QString &id,
|
||||
const QSize &requestedSize)
|
||||
{
|
||||
const QString suffix = "*." + id.split('.').last().toLower();
|
||||
Asset asset(id);
|
||||
|
||||
if (suffix == "*.mesh")
|
||||
if (asset.suffix() == "*.mesh")
|
||||
return m_smallImageCacheProvider.requestImageResponse(id, requestedSize);
|
||||
|
||||
if (suffix == "*.builtin")
|
||||
if (asset.suffix() == "*.builtin")
|
||||
return m_smallImageCacheProvider.requestImageResponse("#" + id.split('.').first(),
|
||||
requestedSize);
|
||||
|
||||
@@ -29,15 +29,15 @@ QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QSt
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
response.get(),
|
||||
[response = QPointer<QmlDesigner::ImageResponse>(response.get()), suffix, id, requestedSize] {
|
||||
if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) {
|
||||
QImage image = QImage(Utils::StyleHelper::dpiSpecificImageFile(id));
|
||||
[response = QPointer<QmlDesigner::ImageResponse>(response.get()), asset, requestedSize] {
|
||||
if (asset.isImage()) {
|
||||
QImage image = QImage(Utils::StyleHelper::dpiSpecificImageFile(asset.id()));
|
||||
if (!image.isNull()) {
|
||||
response->setImage(image.scaled(requestedSize, Qt::KeepAspectRatio));
|
||||
return;
|
||||
}
|
||||
} else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) {
|
||||
HdrImage hdr{id};
|
||||
} else if (asset.isTexture3D()) {
|
||||
HdrImage hdr{asset.id()};
|
||||
if (!hdr.image().isNull()) {
|
||||
response->setImage(hdr.image().scaled(requestedSize, Qt::KeepAspectRatio));
|
||||
return;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "qmldesignercorelib_global.h"
|
||||
|
||||
#include "utils/filepath.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
#include <modelnode.h>
|
||||
@@ -26,7 +28,12 @@ class QMLDESIGNERCORE_EXPORT StylesheetMerger
|
||||
public:
|
||||
StylesheetMerger(AbstractView*, AbstractView*);
|
||||
void merge();
|
||||
static void styleMerge(Model *model, const QString &templateFile, class ExternalDependenciesInterface &externalDependencies);
|
||||
static void styleMerge(const Utils::FilePath &templateFile,
|
||||
Model *model,
|
||||
class ExternalDependenciesInterface &ed);
|
||||
static void styleMerge(const QString &qmlTemplateString,
|
||||
Model *model,
|
||||
class ExternalDependenciesInterface &externalDependencies);
|
||||
|
||||
private:
|
||||
void preprocessStyleSheet();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "createscenecommand.h"
|
||||
#include "debugoutputcommand.h"
|
||||
#include "informationchangedcommand.h"
|
||||
#include "imageutils.h"
|
||||
#include "inputeventcommand.h"
|
||||
#include "nodeabstractproperty.h"
|
||||
#include "nodeinstanceserverproxy.h"
|
||||
@@ -83,6 +84,8 @@
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QImageReader>
|
||||
#include <QLocale>
|
||||
#include <QMultiHash>
|
||||
#include <QPainter>
|
||||
#include <QPicture>
|
||||
@@ -135,12 +138,13 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
|
||||
// related to a single event to be received before we act.
|
||||
m_resetTimer.setSingleShot(true);
|
||||
m_resetTimer.setInterval(100);
|
||||
QObject::connect(&m_resetTimer, &QTimer::timeout, [this] {
|
||||
QObject::connect(&m_resetTimer, &QTimer::timeout, this, [this] {
|
||||
if (isAttached())
|
||||
resetPuppet();
|
||||
});
|
||||
m_updateWatcherTimer.setSingleShot(true);
|
||||
m_updateWatcherTimer.setInterval(100);
|
||||
QObject::connect(&m_updateWatcherTimer, &QTimer::timeout, [this] {
|
||||
QObject::connect(&m_updateWatcherTimer, &QTimer::timeout, this, [this] {
|
||||
for (const auto &path : std::as_const(m_pendingUpdateDirs))
|
||||
updateWatcher(path);
|
||||
m_pendingUpdateDirs.clear();
|
||||
@@ -151,11 +155,11 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
|
||||
// unnecessary generation when project with multiple shaders is opened.
|
||||
m_generateQsbFilesTimer.setSingleShot(true);
|
||||
m_generateQsbFilesTimer.setInterval(100);
|
||||
QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, [this] {
|
||||
QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, this, [this] {
|
||||
handleShaderChanges();
|
||||
});
|
||||
|
||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged,
|
||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this,
|
||||
[this](const QString &path) {
|
||||
const QSet<QString> pendingDirs = m_pendingUpdateDirs;
|
||||
for (const auto &pendingPath : pendingDirs) {
|
||||
@@ -171,7 +175,7 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
|
||||
m_updateWatcherTimer.start();
|
||||
|
||||
});
|
||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) {
|
||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) {
|
||||
if (m_qsbTargets.contains(path)) {
|
||||
m_qsbTargets.insert(path, true);
|
||||
m_generateQsbFilesTimer.start();
|
||||
@@ -1909,23 +1913,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
|
||||
imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio);
|
||||
imageData.pixmap.setDevicePixelRatio(ratio);
|
||||
imageData.time = modified;
|
||||
|
||||
double imgSize = double(imageFi.size());
|
||||
static QStringList units({::QmlDesigner::NodeInstanceView::tr("B"),
|
||||
::QmlDesigner::NodeInstanceView::tr("KB"),
|
||||
::QmlDesigner::NodeInstanceView::tr("MB"),
|
||||
::QmlDesigner::NodeInstanceView::tr("GB")});
|
||||
int unitIndex = 0;
|
||||
while (imgSize > 1024. && unitIndex < units.size() - 1) {
|
||||
++unitIndex;
|
||||
imgSize /= 1024.;
|
||||
}
|
||||
imageData.info = QStringLiteral("%1 x %2\n%3%4 (%5)")
|
||||
.arg(originalPixmap.width())
|
||||
.arg(originalPixmap.height())
|
||||
.arg(QString::number(imgSize, 'g', 3))
|
||||
.arg(units[unitIndex])
|
||||
.arg(imageFi.suffix());
|
||||
imageData.info = ImageUtils::imageInfo(imageSource);
|
||||
m_imageDataMap.insert(imageData.id, imageData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ QProcessUniquePointer puppetProcess(const QString &puppetPath,
|
||||
processFinishCallback);
|
||||
|
||||
if (forwardOutput == puppetMode || forwardOutput == "all") {
|
||||
puppetProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
puppetProcess->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
QObject::connect(puppetProcess.get(), &QProcess::readyRead, processOutputCallback);
|
||||
}
|
||||
puppetProcess->setWorkingDirectory(workingDirectory);
|
||||
@@ -46,7 +46,6 @@ QProcessUniquePointer puppetProcess(const QString &puppetPath,
|
||||
else
|
||||
processArguments = {socketToken, puppetMode};
|
||||
|
||||
processArguments.push_back("-graphicssystem raster");
|
||||
processArguments.push_back(freeTypeOption);
|
||||
|
||||
puppetProcess->start(puppetPath, processArguments);
|
||||
|
||||
@@ -562,9 +562,9 @@ QVector<PropertyInfo> getObjectTypes(const ObjectValue *objectValue, const Conte
|
||||
const CppComponentValue * qmlObjectValue = value_cast<CppComponentValue>(prototype);
|
||||
|
||||
if (qmlObjectValue)
|
||||
propertyList.append(getQmlTypes(qmlObjectValue, context, local, rec));
|
||||
propertyList.append(getQmlTypes(qmlObjectValue, context, local, rec + 1));
|
||||
else
|
||||
propertyList.append(getObjectTypes(prototype, context, local, rec));
|
||||
propertyList.append(getObjectTypes(prototype, context, local, rec + 1));
|
||||
}
|
||||
|
||||
return propertyList;
|
||||
|
||||
@@ -559,6 +559,7 @@ void AbstractView::resetView()
|
||||
|
||||
void AbstractView::resetPuppet()
|
||||
{
|
||||
QTC_ASSERT(isAttached(), return);
|
||||
emitCustomNotification(QStringLiteral("reset QmlPuppet"));
|
||||
}
|
||||
|
||||
@@ -689,6 +690,7 @@ void AbstractView::emitCustomNotification(const QString &identifier, const QList
|
||||
|
||||
void AbstractView::emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data)
|
||||
{
|
||||
if (model())
|
||||
model()->d->notifyCustomNotification(this, identifier, nodeList, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -515,11 +515,24 @@ void StylesheetMerger::merge()
|
||||
}
|
||||
}
|
||||
|
||||
void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, ExternalDependenciesInterface &externalDependencies)
|
||||
void StylesheetMerger::styleMerge(const Utils::FilePath &templateFile,
|
||||
Model *model,
|
||||
ExternalDependenciesInterface &externalDependencies)
|
||||
{
|
||||
Utils::FileReader reader;
|
||||
|
||||
QTC_ASSERT(reader.fetch(templateFile), return );
|
||||
const QString qmlTemplateString = QString::fromUtf8(reader.data());
|
||||
StylesheetMerger::styleMerge(qmlTemplateString, model, externalDependencies);
|
||||
}
|
||||
|
||||
void StylesheetMerger::styleMerge(const QString &qmlTemplateString,
|
||||
Model *model,
|
||||
ExternalDependenciesInterface &externalDependencies)
|
||||
{
|
||||
Model *parentModel = model;
|
||||
|
||||
QTC_ASSERT(parentModel, return);
|
||||
QTC_ASSERT(parentModel, return );
|
||||
|
||||
auto templateModel(Model::create("QtQuick.Item", 2, 1, parentModel));
|
||||
Q_ASSERT(templateModel.get());
|
||||
@@ -527,10 +540,6 @@ void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, Ext
|
||||
templateModel->setFileUrl(parentModel->fileUrl());
|
||||
|
||||
QPlainTextEdit textEditTemplate;
|
||||
Utils::FileReader reader;
|
||||
|
||||
QTC_ASSERT(reader.fetch(Utils::FilePath::fromString(templateFile)), return);
|
||||
QString qmlTemplateString = QString::fromUtf8(reader.data());
|
||||
QString imports;
|
||||
|
||||
for (const Import &import : parentModel->imports()) {
|
||||
@@ -541,13 +550,14 @@ void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, Ext
|
||||
textEditTemplate.setPlainText(imports + qmlTemplateString);
|
||||
NotIndentingTextEditModifier textModifierTemplate(&textEditTemplate);
|
||||
|
||||
QScopedPointer<RewriterView> templateRewriterView(new RewriterView(externalDependencies, RewriterView::Amend));
|
||||
QScopedPointer<RewriterView> templateRewriterView(
|
||||
new RewriterView(externalDependencies, RewriterView::Amend));
|
||||
templateRewriterView->setTextModifier(&textModifierTemplate);
|
||||
templateModel->attachView(templateRewriterView.data());
|
||||
templateRewriterView->setCheckSemanticErrors(false);
|
||||
|
||||
ModelNode templateRootNode = templateRewriterView->rootModelNode();
|
||||
QTC_ASSERT(templateRootNode.isValid(), return);
|
||||
QTC_ASSERT(templateRootNode.isValid(), return );
|
||||
|
||||
auto styleModel(Model::create("QtQuick.Item", 2, 1, parentModel));
|
||||
Q_ASSERT(styleModel.get());
|
||||
@@ -556,11 +566,12 @@ void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, Ext
|
||||
|
||||
QPlainTextEdit textEditStyle;
|
||||
RewriterView *parentRewriterView = parentModel->rewriterView();
|
||||
QTC_ASSERT(parentRewriterView, return);
|
||||
QTC_ASSERT(parentRewriterView, return );
|
||||
textEditStyle.setPlainText(parentRewriterView->textModifierContent());
|
||||
NotIndentingTextEditModifier textModifierStyle(&textEditStyle);
|
||||
|
||||
QScopedPointer<RewriterView> styleRewriterView(new RewriterView(externalDependencies, RewriterView::Amend));
|
||||
QScopedPointer<RewriterView> styleRewriterView(
|
||||
new RewriterView(externalDependencies, RewriterView::Amend));
|
||||
styleRewriterView->setTextModifier(&textModifierStyle);
|
||||
styleModel->attachView(styleRewriterView.data());
|
||||
|
||||
|
||||
182
src/plugins/qmldesigner/utils/asset.cpp
Normal file
182
src/plugins/qmldesigner/utils/asset.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QImageReader>
|
||||
|
||||
#include "asset.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
Asset::Asset(const QString &filePath)
|
||||
: m_filePath(filePath)
|
||||
{
|
||||
m_suffix = "*." + filePath.split('.').last().toLower();
|
||||
}
|
||||
|
||||
|
||||
const QStringList &Asset::supportedImageSuffixes()
|
||||
{
|
||||
static QStringList retList;
|
||||
if (retList.isEmpty()) {
|
||||
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
|
||||
for (const QByteArray &suffix : suffixes)
|
||||
retList.append("*." + QString::fromUtf8(suffix));
|
||||
}
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedFragmentShaderSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedShaderSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.frag", "*.vert",
|
||||
"*.glsl", "*.glslv", "*.glslf",
|
||||
"*.vsh", "*.fsh"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedFontSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.ttf", "*.otf"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedAudioSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.wav", "*.mp3"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedVideoSuffixes()
|
||||
{
|
||||
static const QStringList retList {"*.mp4"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedTexture3DSuffixes()
|
||||
{
|
||||
// These are file types only supported by 3D textures
|
||||
static QStringList retList {"*.hdr", "*.ktx"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QStringList &Asset::supportedEffectMakerSuffixes()
|
||||
{
|
||||
// These are file types only supported by Effect Maker
|
||||
static QStringList retList {"*.qep"};
|
||||
return retList;
|
||||
}
|
||||
|
||||
const QSet<QString> &Asset::supportedSuffixes()
|
||||
{
|
||||
static QSet<QString> allSuffixes;
|
||||
if (allSuffixes.isEmpty()) {
|
||||
auto insertSuffixes = [](const QStringList &suffixes) {
|
||||
for (const auto &suffix : suffixes)
|
||||
allSuffixes.insert(suffix);
|
||||
};
|
||||
insertSuffixes(supportedImageSuffixes());
|
||||
insertSuffixes(supportedShaderSuffixes());
|
||||
insertSuffixes(supportedFontSuffixes());
|
||||
insertSuffixes(supportedAudioSuffixes());
|
||||
insertSuffixes(supportedVideoSuffixes());
|
||||
insertSuffixes(supportedTexture3DSuffixes());
|
||||
insertSuffixes(supportedEffectMakerSuffixes());
|
||||
}
|
||||
return allSuffixes;
|
||||
}
|
||||
|
||||
Asset::Type Asset::type() const
|
||||
{
|
||||
if (supportedImageSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Image;
|
||||
|
||||
if (supportedFragmentShaderSuffixes().contains(m_suffix))
|
||||
return Asset::Type::FragmentShader;
|
||||
|
||||
if (supportedShaderSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Shader;
|
||||
|
||||
if (supportedFontSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Font;
|
||||
|
||||
if (supportedAudioSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Audio;
|
||||
|
||||
if (supportedVideoSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Video;
|
||||
|
||||
if (supportedTexture3DSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Texture3D;
|
||||
|
||||
if (supportedEffectMakerSuffixes().contains(m_suffix))
|
||||
return Asset::Type::Effect;
|
||||
|
||||
return Asset::Type::Unknown;
|
||||
}
|
||||
|
||||
bool Asset::isImage() const
|
||||
{
|
||||
return type() == Asset::Type::Image;
|
||||
}
|
||||
|
||||
bool Asset::isFragmentShader() const
|
||||
{
|
||||
return type() == Asset::Type::FragmentShader;
|
||||
}
|
||||
|
||||
bool Asset::isShader() const
|
||||
{
|
||||
return type() == Asset::Type::Shader;
|
||||
}
|
||||
|
||||
bool Asset::isFont() const
|
||||
{
|
||||
return type() == Asset::Type::Font;
|
||||
}
|
||||
|
||||
bool Asset::isAudio() const
|
||||
{
|
||||
return type() == Asset::Type::Audio;
|
||||
}
|
||||
|
||||
bool Asset::isVideo() const
|
||||
{
|
||||
return type() == Asset::Type::Video;
|
||||
}
|
||||
|
||||
bool Asset::isTexture3D() const
|
||||
{
|
||||
return type() == Asset::Type::Texture3D;
|
||||
}
|
||||
|
||||
bool Asset::isEffect() const
|
||||
{
|
||||
return type() == Asset::Type::Effect;
|
||||
}
|
||||
|
||||
const QString Asset::suffix() const
|
||||
{
|
||||
return m_suffix;
|
||||
}
|
||||
|
||||
const QString Asset::id() const
|
||||
{
|
||||
return m_filePath;
|
||||
}
|
||||
|
||||
bool Asset::isSupported() const
|
||||
{
|
||||
return supportedSuffixes().contains(m_filePath);
|
||||
}
|
||||
|
||||
bool Asset::hasSuffix() const
|
||||
{
|
||||
return !m_suffix.isEmpty();
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
45
src/plugins/qmldesigner/utils/asset.h
Normal file
45
src/plugins/qmldesigner/utils/asset.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class Asset
|
||||
{
|
||||
public:
|
||||
enum Type { Unknown, Image, FragmentShader, Font, Audio, Video, Texture3D, Effect, Shader };
|
||||
|
||||
Asset(const QString &filePath);
|
||||
|
||||
static const QStringList &supportedImageSuffixes();
|
||||
static const QStringList &supportedFragmentShaderSuffixes();
|
||||
static const QStringList &supportedShaderSuffixes();
|
||||
static const QStringList &supportedFontSuffixes();
|
||||
static const QStringList &supportedAudioSuffixes();
|
||||
static const QStringList &supportedVideoSuffixes();
|
||||
static const QStringList &supportedTexture3DSuffixes();
|
||||
static const QStringList &supportedEffectMakerSuffixes();
|
||||
static const QSet<QString> &supportedSuffixes();
|
||||
|
||||
const QString suffix() const;
|
||||
const QString id() const;
|
||||
bool hasSuffix() const;
|
||||
|
||||
Type type() const;
|
||||
bool isImage() const;
|
||||
bool isFragmentShader() const;
|
||||
bool isShader() const;
|
||||
bool isFont() const;
|
||||
bool isAudio() const;
|
||||
bool isVideo() const;
|
||||
bool isTexture3D() const;
|
||||
bool isEffect() const;
|
||||
bool isSupported() const;
|
||||
|
||||
private:
|
||||
QString m_filePath;
|
||||
QString m_suffix;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
46
src/plugins/qmldesigner/utils/imageutils.cpp
Normal file
46
src/plugins/qmldesigner/utils/imageutils.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "imageutils.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QImageReader>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
|
||||
{
|
||||
QFileInfo info(path);
|
||||
if (!info.exists())
|
||||
return {};
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
if (info.suffix() == "hdr") {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return {};
|
||||
|
||||
while (!file.atEnd()) {
|
||||
QByteArray line = file.readLine();
|
||||
if (sscanf(line.constData(), "-Y %d +X %d", &height, &width))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
QSize size = QImageReader(path).size();
|
||||
width = size.width();
|
||||
height = size.height();
|
||||
}
|
||||
|
||||
if (width == 0 && height == 0)
|
||||
return {};
|
||||
|
||||
return QLatin1String("%1 x %2\n%3 (%4)")
|
||||
.arg(QString::number(width),
|
||||
QString::number(height),
|
||||
QLocale::system().formattedDataSize(info.size(), 2, QLocale::DataSizeTraditionalFormat),
|
||||
info.suffix());
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
17
src/plugins/qmldesigner/utils/imageutils.h
Normal file
17
src/plugins/qmldesigner/utils/imageutils.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ImageUtils
|
||||
{
|
||||
public:
|
||||
ImageUtils();
|
||||
|
||||
static QString imageInfo(const QString &path);
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
@@ -75,13 +75,13 @@ void FileDownloader::start()
|
||||
auto request = QNetworkRequest(m_url);
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
|
||||
QNetworkRequest::UserVerifiedRedirectPolicy);
|
||||
QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request);
|
||||
m_reply = Utils::NetworkAccessManager::instance()->get(request);
|
||||
|
||||
QNetworkReply::connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
|
||||
m_tempFile.write(reply->readAll());
|
||||
QNetworkReply::connect(m_reply, &QNetworkReply::readyRead, this, [this]() {
|
||||
m_tempFile.write(m_reply->readAll());
|
||||
});
|
||||
|
||||
QNetworkReply::connect(reply,
|
||||
QNetworkReply::connect(m_reply,
|
||||
&QNetworkReply::downloadProgress,
|
||||
this,
|
||||
[this](qint64 current, qint64 max) {
|
||||
@@ -92,16 +92,21 @@ void FileDownloader::start()
|
||||
emit progressChanged();
|
||||
});
|
||||
|
||||
QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &) {
|
||||
emit reply->redirectAllowed();
|
||||
QNetworkReply::connect(m_reply, &QNetworkReply::redirected, [this](const QUrl &) {
|
||||
emit m_reply->redirectAllowed();
|
||||
});
|
||||
|
||||
QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||
if (reply->error()) {
|
||||
QNetworkReply::connect(m_reply, &QNetworkReply::finished, this, [this]() {
|
||||
if (m_reply->error()) {
|
||||
if (m_tempFile.exists())
|
||||
m_tempFile.remove();
|
||||
qWarning() << Q_FUNC_INFO << m_url << reply->errorString();
|
||||
|
||||
if (m_reply->error() != QNetworkReply::OperationCanceledError) {
|
||||
qWarning() << Q_FUNC_INFO << m_url << m_reply->errorString();
|
||||
emit downloadFailed();
|
||||
} else {
|
||||
emit downloadCanceled();
|
||||
}
|
||||
} else {
|
||||
m_tempFile.flush();
|
||||
m_tempFile.close();
|
||||
@@ -109,9 +114,17 @@ void FileDownloader::start()
|
||||
emit tempFileChanged();
|
||||
emit finishedChanged();
|
||||
}
|
||||
|
||||
m_reply = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void FileDownloader::cancel()
|
||||
{
|
||||
if (m_reply)
|
||||
m_reply->abort();
|
||||
}
|
||||
|
||||
void FileDownloader::setUrl(const QUrl &url)
|
||||
{
|
||||
m_url = url;
|
||||
|
||||
@@ -112,6 +112,7 @@ public:
|
||||
bool available() const;
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
Q_INVOKABLE void cancel();
|
||||
|
||||
signals:
|
||||
void finishedChanged();
|
||||
@@ -123,6 +124,8 @@ signals:
|
||||
void lastModifiedChanged();
|
||||
void availableChanged();
|
||||
|
||||
void downloadCanceled();
|
||||
|
||||
private:
|
||||
void probeUrl();
|
||||
|
||||
@@ -133,6 +136,8 @@ private:
|
||||
QFile m_tempFile;
|
||||
QDateTime m_lastModified;
|
||||
bool m_available = false;
|
||||
|
||||
QNetworkReply *m_reply = nullptr;
|
||||
};
|
||||
|
||||
class DataModelDownloader : public QObject
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QMetaEnum>
|
||||
#include <QTimer>
|
||||
#include <QVersionNumber>
|
||||
#include <QScrollArea>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -223,7 +224,12 @@ static void showUpdateInfo(const QList<Update> &updates,
|
||||
label->setText("<qt><p>" + UpdateInfoPlugin::tr("Available updates:") + "<ul><li>"
|
||||
+ qtText + updateText + "</li></ul></p></qt>");
|
||||
label->setContentsMargins(0, 0, 0, 8);
|
||||
return label;
|
||||
auto scrollArea = new QScrollArea;
|
||||
scrollArea->setWidget(label);
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
scrollArea->viewport()->setAutoFillBackground(false);
|
||||
label->setAutoFillBackground(false);
|
||||
return scrollArea;
|
||||
});
|
||||
}
|
||||
ICore::infoBar()->removeInfo(InstallUpdates); // remove any existing notifications
|
||||
|
||||
@@ -23,6 +23,8 @@ if (NOT QT_CREATOR_API_DEFINED)
|
||||
)
|
||||
endif()
|
||||
|
||||
configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES)
|
||||
|
||||
if (NOT TARGET QmlPuppetCommunication)
|
||||
include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake)
|
||||
endif()
|
||||
@@ -34,13 +36,34 @@ add_qtc_executable(qml2puppet
|
||||
Qt5::CorePrivate Qt5::Widgets Qt5::QmlPrivate
|
||||
Qt5::QuickPrivate Qt5::Network Qt5::GuiPrivate
|
||||
QmlPuppetCommunication
|
||||
INCLUDES
|
||||
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
SOURCES
|
||||
qml2puppet/qml2puppetmain.cpp
|
||||
qml2puppet/main.cpp
|
||||
qml2puppet/qmlbase.h qml2puppet/appmetadata.h
|
||||
qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h
|
||||
qmlpuppet.qrc
|
||||
PROPERTIES
|
||||
OUTPUT_NAME qml2puppet-${IDE_VERSION}
|
||||
)
|
||||
|
||||
if(TARGET qml2puppet)
|
||||
execute_process(
|
||||
COMMAND git describe --tags --always --dirty=+
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_SHA_RESULT
|
||||
OUTPUT_VARIABLE GIT_SHA_OUTPUT
|
||||
ERROR_VARIABLE GIT_SHA_ERROR
|
||||
)
|
||||
|
||||
#if we are not a git repository use the .tag file
|
||||
if(NOT GIT_SHA_OUTPUT)
|
||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA_OUTPUT)
|
||||
endif()
|
||||
|
||||
add_definitions( -D GIT_SHA=${GIT_SHA_OUTPUT} )
|
||||
endif()
|
||||
|
||||
extend_qtc_executable(qml2puppet
|
||||
CONDITION Qt5_VERSION VERSION_GREATER_EQUAL 6.0.0
|
||||
SOURCES
|
||||
@@ -191,10 +214,27 @@ extend_qtc_executable(qml2puppet
|
||||
DEPENDS Nanotrace
|
||||
)
|
||||
|
||||
if (QTC_STATIC_BUILD AND Qt5_VERSION VERSION_GREATER_EQUAL 6.0.0)
|
||||
qt6_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR})
|
||||
if (Qt5_VERSION VERSION_GREATER_EQUAL 6.4.0)
|
||||
extend_qtc_executable(qml2puppet
|
||||
DEFINES ENABLE_INTERNAL_QML_RUNTIME
|
||||
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/qml2puppet/runner
|
||||
SOURCES_PREFIX qml2puppet/runner
|
||||
SOURCES
|
||||
qmlruntime.h qmlruntime.cpp
|
||||
qmlconfiguration.h loadwatcher.h
|
||||
)
|
||||
|
||||
# Turn the tool into its own self-contained qml module
|
||||
qt_add_qml_module(qml2puppet
|
||||
URI QmlRuntime.QmlConfiguration
|
||||
VERSION 1.0
|
||||
)
|
||||
if (QTC_STATIC_BUILD)
|
||||
qt_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Crashpad
|
||||
# only windows requires separate crashpad client per process until client->SetHandlerIPCPipe()
|
||||
# is implemented (check the TODO inside startCrashpad())
|
||||
|
||||
@@ -194,7 +194,6 @@ Item {
|
||||
function updateViewStates(viewStates)
|
||||
{
|
||||
if ("selectBackgroundColor" in viewStates) {
|
||||
if (Array.isArray(viewStates.selectBackgroundColor)) {
|
||||
var colors = viewStates.selectBackgroundColor
|
||||
if (colors.length === 1) {
|
||||
backgroundGradientColorStart = colors[0];
|
||||
@@ -203,15 +202,10 @@ Item {
|
||||
backgroundGradientColorStart = colors[0];
|
||||
backgroundGradientColorEnd = colors[1];
|
||||
}
|
||||
} else {
|
||||
var color = viewStates.selectBackgroundColor
|
||||
backgroundGradientColorStart = color;
|
||||
backgroundGradientColorEnd = color;
|
||||
}
|
||||
}
|
||||
|
||||
if ("selectGridColor" in viewStates)
|
||||
viewRoot.gridColor = viewStates.selectGridColor
|
||||
if ("selectGridColor" in viewStates && viewStates.selectGridColor.length === 1)
|
||||
viewRoot.gridColor = viewStates.selectGridColor[0]
|
||||
}
|
||||
|
||||
// If resetToDefault is true, tool states not specifically set to anything will be reset to
|
||||
|
||||
96
src/tools/qml2puppet/qml2puppet/appmetadata.h
Normal file
96
src/tools/qml2puppet/qml2puppet/appmetadata.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH
|
||||
// Qt-GPL-exception-1.0
|
||||
#pragma once
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include <app/app_version.h>
|
||||
|
||||
// Common functions can be used in all QDS apps
|
||||
namespace QDSMeta {
|
||||
|
||||
namespace Logging {
|
||||
inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated");
|
||||
inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1");
|
||||
inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2");
|
||||
|
||||
inline void registerMessageHandler()
|
||||
{
|
||||
qInstallMessageHandler(
|
||||
[](QtMsgType type, const QMessageLogContext &context, const QString &msg) {
|
||||
auto tPrinter = [&](const QString &msgPrefix) {
|
||||
fprintf(stderr,
|
||||
"%s: %s (%s:%u, %s)\n",
|
||||
msgPrefix.toLocal8Bit().constData(),
|
||||
msg.toLocal8Bit().constData(),
|
||||
context.file,
|
||||
context.line,
|
||||
context.function);
|
||||
};
|
||||
|
||||
if (type == QtDebugMsg)
|
||||
tPrinter("Debug");
|
||||
else if (type == QtInfoMsg)
|
||||
tPrinter("Info");
|
||||
else if (type == QtWarningMsg)
|
||||
tPrinter("Warning");
|
||||
else if (type == QtCriticalMsg)
|
||||
tPrinter("Critical");
|
||||
else if (type == QtFatalMsg) {
|
||||
tPrinter("Fatal");
|
||||
abort();
|
||||
}
|
||||
});
|
||||
}
|
||||
} // namespace Logging
|
||||
|
||||
namespace AppInfo {
|
||||
|
||||
#define STRINGIFY_INTERNAL(x) #x
|
||||
#define QDS_STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
||||
|
||||
inline void printAppInfo()
|
||||
{
|
||||
qInfo() << Qt::endl
|
||||
<< "<< QDS Meta Info >>" << Qt::endl
|
||||
<< "App Info" << Qt::endl
|
||||
<< " - Name :" << Core::Constants::IDE_ID << Qt::endl
|
||||
<< " - Version :" << Core::Constants::IDE_VERSION_DISPLAY << Qt::endl
|
||||
<< " - Author :" << Core::Constants::IDE_AUTHOR << Qt::endl
|
||||
<< " - Year :" << Core::Constants::IDE_YEAR << Qt::endl
|
||||
<< " - App :" << QCoreApplication::applicationName() << Qt::endl
|
||||
<< "Build Info " << Qt::endl
|
||||
<< " - Date :" << __DATE__ << Qt::endl
|
||||
<< " - Commit :" << QStringLiteral(QDS_STRINGIFY(GIT_SHA)) << Qt::endl
|
||||
<< " - Qt Version :" << QT_VERSION_STR << Qt::endl
|
||||
<< "Compiler Info " << Qt::endl
|
||||
#if defined(__GNUC__)
|
||||
<< " - GCC :" << __GNUC__ << Qt::endl
|
||||
<< " - GCC Minor :" << __GNUC_MINOR__ << Qt::endl
|
||||
<< " - GCC Patch :" << __GNUC_PATCHLEVEL__ << Qt::endl
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
<< " - MSC Short :" << _MSC_VER << Qt::endl
|
||||
<< " - MSC Full :" << _MSC_FULL_VER << Qt::endl
|
||||
#endif
|
||||
#if defined(__clang__)
|
||||
<< " - clang maj :" << __clang_major__ << Qt::endl
|
||||
<< " - clang min :" << __clang_minor__ << Qt::endl
|
||||
<< " - clang patch :" << __clang_patchlevel__ << Qt::endl
|
||||
#endif
|
||||
<< "<< End Of QDS Meta Info >>" << Qt::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
inline void registerAppInfo(const QString &appName)
|
||||
{
|
||||
QCoreApplication::setOrganizationName(Core::Constants::IDE_AUTHOR);
|
||||
QCoreApplication::setOrganizationDomain("qt-project.org");
|
||||
QCoreApplication::setApplicationName(appName);
|
||||
QCoreApplication::setApplicationVersion(Core::Constants::IDE_VERSION_LONG);
|
||||
}
|
||||
|
||||
} // namespace AppInfo
|
||||
} // namespace QDSMeta
|
||||
66
src/tools/qml2puppet/qml2puppet/configcrashpad.h
Normal file
66
src/tools/qml2puppet/qml2puppet/configcrashpad.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
#pragma once
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#ifdef ENABLE_QT_BREAKPAD
|
||||
#include <qtsystemexceptionhandler.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
#define NOMINMAX
|
||||
#include "client/crash_report_database.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "client/settings.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
bool startCrashpad()
|
||||
{
|
||||
using namespace crashpad;
|
||||
|
||||
// Cache directory that will store crashpad information and minidumps
|
||||
base::FilePath database(L"crashpad_reports");
|
||||
base::FilePath handler(L"crashpad_handler.exe");
|
||||
|
||||
// URL used to submit minidumps to
|
||||
std::string url(CRASHPAD_BACKEND_URL);
|
||||
|
||||
// Optional annotations passed via --annotations to the handler
|
||||
std::map<std::string, std::string> annotations;
|
||||
annotations["qt-version"] = QT_VERSION_STR;
|
||||
|
||||
// Optional arguments to pass to the handler
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
|
||||
CrashpadClient *client = new CrashpadClient();
|
||||
bool success = client->StartHandler(handler,
|
||||
database,
|
||||
database,
|
||||
url,
|
||||
annotations,
|
||||
arguments,
|
||||
/* restartable */ true,
|
||||
/* asynchronous_start */ true);
|
||||
// TODO: research using this method, should avoid creating a separate CrashpadClient for the
|
||||
// puppet (needed only on windows according to docs).
|
||||
// client->SetHandlerIPCPipe(L"\\\\.\\pipe\\qml2puppet");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_BREAKPAD
|
||||
const QString libexecPath = QCoreApplication::applicationDirPath() + '/'
|
||||
+ RELATIVE_LIBEXEC_PATH;
|
||||
QtSystemExceptionHandler systemExceptionHandler(libexecPath);
|
||||
#endif //#ifdef ENABLE_QT_BREAKPAD
|
||||
#else //#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
bool startCrashpad()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif //#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
}
|
||||
@@ -1043,6 +1043,21 @@ QList<ServerNodeInstance> NodeInstanceServer::allGroupStateInstances() const
|
||||
return groups;
|
||||
}
|
||||
|
||||
QList<ServerNodeInstance> NodeInstanceServer::allView3DInstances() const
|
||||
{
|
||||
QList<ServerNodeInstance> view3Ds;
|
||||
std::copy_if(nodeInstances().cbegin(),
|
||||
nodeInstances().cend(),
|
||||
std::back_inserter(view3Ds),
|
||||
[](const ServerNodeInstance &instance) {
|
||||
return instance.isValid()
|
||||
&& ServerNodeInstance::isSubclassOf(instance.internalObject(),
|
||||
QByteArrayLiteral("QQuick3DViewport"));
|
||||
});
|
||||
|
||||
return view3Ds;
|
||||
}
|
||||
|
||||
void NodeInstanceServer::setStateInstance(const ServerNodeInstance &stateInstance)
|
||||
{
|
||||
m_activeStateInstance = stateInstance;
|
||||
|
||||
@@ -179,6 +179,7 @@ public:
|
||||
ServerNodeInstance rootNodeInstance() const;
|
||||
|
||||
QList<ServerNodeInstance> allGroupStateInstances() const;
|
||||
QList<ServerNodeInstance> allView3DInstances() const;
|
||||
|
||||
void notifyPropertyChange(qint32 instanceid, const PropertyName &propertyName);
|
||||
|
||||
|
||||
@@ -921,9 +921,9 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D([[maybe_unu
|
||||
if (toolStates.contains("syncBackgroundColor")) {
|
||||
bool sync = toolStates["syncBackgroundColor"].toBool();
|
||||
if (sync) {
|
||||
QColor color = helper->sceneEnvironmentColor(sceneId);
|
||||
QList<QColor> colors{helper->sceneEnvironmentColor(sceneId)};
|
||||
View3DActionCommand cmd(View3DActionType::SelectBackgroundColor,
|
||||
QVariant::fromValue(color));
|
||||
QVariant::fromValue(colors));
|
||||
view3DAction(cmd);
|
||||
}
|
||||
}
|
||||
@@ -2276,9 +2276,10 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyVa
|
||||
|
||||
if (toolStates.contains("syncBackgroundColor")) {
|
||||
bool sync = toolStates["syncBackgroundColor"].toBool();
|
||||
QList<QColor> colors{color};
|
||||
if (sync) {
|
||||
View3DActionCommand cmd(View3DActionType::SelectBackgroundColor,
|
||||
QVariant::fromValue(color));
|
||||
QVariant::fromValue(colors));
|
||||
view3DAction(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +53,28 @@ void Qt5PreviewNodeInstanceServer::collectItemChangesAndSendChangeCommands()
|
||||
QQuickDesignerSupport::polishItems(quickWindow());
|
||||
|
||||
QVector<ImageContainer> imageContainerVector;
|
||||
|
||||
// Base state needs to be rendered twice to properly render shared resources,
|
||||
// if there is more than one View3D and at least one of them is dirty.
|
||||
bool dirtyView3d = false;
|
||||
const QList<ServerNodeInstance> view3dInstances = allView3DInstances();
|
||||
for (const auto &instance : view3dInstances) {
|
||||
if (QQuickDesignerSupport::isDirty(instance.rootQuickItem(),
|
||||
QQuickDesignerSupport::ContentUpdateMask)) {
|
||||
dirtyView3d = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dirtyView3d)
|
||||
renderPreviewImage();
|
||||
imageContainerVector.append(ImageContainer(0, renderPreviewImage(), -1));
|
||||
|
||||
QList<ServerNodeInstance> stateInstances = rootNodeInstance().stateInstances();
|
||||
|
||||
const QList<ServerNodeInstance> groupInstances = allGroupStateInstances();
|
||||
|
||||
for (ServerNodeInstance instance : groupInstances) {
|
||||
for (const ServerNodeInstance &instance : groupInstances)
|
||||
stateInstances.append(instance.stateInstances());
|
||||
}
|
||||
|
||||
for (ServerNodeInstance instance : std::as_const(stateInstances)) {
|
||||
instance.activateState();
|
||||
|
||||
@@ -56,6 +56,7 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
|
||||
|
||||
if (quickWindow() && nodeInstanceClient()->bytesToWrite() < 10000) {
|
||||
bool windowDirty = false;
|
||||
bool hasView3D = false;
|
||||
for (QQuickItem *item : allItems()) {
|
||||
if (item) {
|
||||
if (Internal::QuickItemNodeInstance::unifiedRenderPath()) {
|
||||
@@ -65,8 +66,13 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
|
||||
}
|
||||
} else {
|
||||
if (hasInstanceForObject(item)) {
|
||||
if (QQuickDesignerSupport::isDirty(item, QQuickDesignerSupport::ContentUpdateMask))
|
||||
if (QQuickDesignerSupport::isDirty(item, QQuickDesignerSupport::ContentUpdateMask)) {
|
||||
if (!hasView3D && ServerNodeInstance::isSubclassOf(
|
||||
item, QByteArrayLiteral("QQuick3DViewport"))) {
|
||||
hasView3D = true;
|
||||
}
|
||||
m_dirtyInstanceSet.insert(instanceForObject(item));
|
||||
}
|
||||
if (QQuickItem *effectParent = parentEffectItem(item)) {
|
||||
if ((QQuickDesignerSupport::isDirty(
|
||||
item,
|
||||
@@ -100,9 +106,19 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
|
||||
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()}));
|
||||
} else {
|
||||
if (!m_dirtyInstanceSet.isEmpty()) {
|
||||
nodeInstanceClient()->pixmapChanged(
|
||||
createPixmapChangedCommand(QtHelpers::toList(m_dirtyInstanceSet)));
|
||||
m_dirtyInstanceSet.clear();
|
||||
auto renderList = QtHelpers::toList(m_dirtyInstanceSet);
|
||||
|
||||
// If there is a View3D to be rendered, add all other View3Ds to be rendered
|
||||
// as well, in case they share materials.
|
||||
if (hasView3D) {
|
||||
const QList<ServerNodeInstance> view3Ds = allView3DInstances();
|
||||
for (auto &view3D : view3Ds) {
|
||||
if (!m_dirtyInstanceSet.contains(view3D))
|
||||
renderList.append(view3D);
|
||||
}
|
||||
}
|
||||
|
||||
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand(renderList));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
src/tools/qml2puppet/qml2puppet/main.cpp
Normal file
31
src/tools/qml2puppet/qml2puppet/main.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "qmlpuppet.h"
|
||||
|
||||
#ifdef ENABLE_INTERNAL_QML_RUNTIME
|
||||
#include "runner/qmlruntime.h"
|
||||
#endif
|
||||
|
||||
QmlBase *getQmlRunner(int &argc, char **argv)
|
||||
{
|
||||
#ifdef ENABLE_INTERNAL_QML_RUNTIME
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--qml-runtime")){
|
||||
qInfo() << "Starting QML Runtime";
|
||||
return new QmlRuntime(argc, argv);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
qInfo() << "Starting QML Puppet";
|
||||
return new QmlPuppet(argc, argv);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QDSMeta::Logging::registerMessageHandler();
|
||||
QDSMeta::AppInfo::registerAppInfo("Qml2Puppet");
|
||||
|
||||
QmlBase *qmlRunner = getQmlRunner(argc, argv);
|
||||
return qmlRunner->run();
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "iconrenderer/iconrenderer.h"
|
||||
#include "import3d/import3d.h"
|
||||
|
||||
#include <qt5nodeinstanceclientproxy.h>
|
||||
#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER
|
||||
#include <sqlitelibraryinitializer.h>
|
||||
#endif
|
||||
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QDebug>
|
||||
#include <QApplication>
|
||||
#include <QStringList>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef ENABLE_QT_BREAKPAD
|
||||
#include <qtsystemexceptionhandler.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
#define NOMINMAX
|
||||
#include "client/crashpad_client.h"
|
||||
#include "client/crash_report_database.h"
|
||||
#include "client/settings.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
fprintf(stderr,
|
||||
"Debug: %s (%s:%u, %s)\n",
|
||||
localMsg.constData(),
|
||||
context.file,
|
||||
context.line,
|
||||
context.function);
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
fprintf(stderr,
|
||||
"Info: %s (%s:%u, %s)\n",
|
||||
localMsg.constData(),
|
||||
context.file,
|
||||
context.line,
|
||||
context.function);
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
fprintf(stderr,
|
||||
"Warning: %s (%s:%u, %s)\n",
|
||||
localMsg.constData(),
|
||||
context.file,
|
||||
context.line,
|
||||
context.function);
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
fprintf(stderr,
|
||||
"Critical: %s (%s:%u, %s)\n",
|
||||
localMsg.constData(),
|
||||
context.file,
|
||||
context.line,
|
||||
context.function);
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
fprintf(stderr,
|
||||
"Fatal: %s (%s:%u, %s)\n",
|
||||
localMsg.constData(),
|
||||
context.file,
|
||||
context.line,
|
||||
context.function);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
bool startCrashpad()
|
||||
{
|
||||
using namespace crashpad;
|
||||
|
||||
// Cache directory that will store crashpad information and minidumps
|
||||
base::FilePath database(L"crashpad_reports");
|
||||
base::FilePath handler(L"crashpad_handler.exe");
|
||||
|
||||
// URL used to submit minidumps to
|
||||
std::string url(CRASHPAD_BACKEND_URL);
|
||||
|
||||
// Optional annotations passed via --annotations to the handler
|
||||
std::map<std::string, std::string> annotations;
|
||||
annotations["qt-version"] = QT_VERSION_STR;
|
||||
|
||||
// Optional arguments to pass to the handler
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
|
||||
CrashpadClient *client = new CrashpadClient();
|
||||
bool success = client->StartHandler(
|
||||
handler,
|
||||
database,
|
||||
database,
|
||||
url,
|
||||
annotations,
|
||||
arguments,
|
||||
/* restartable */ true,
|
||||
/* asynchronous_start */ true
|
||||
);
|
||||
// TODO: research using this method, should avoid creating a separate CrashpadClient for the
|
||||
// puppet (needed only on windows according to docs).
|
||||
// client->SetHandlerIPCPipe(L"\\\\.\\pipe\\qml2puppet");
|
||||
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
||||
int internalMain(QGuiApplication *application)
|
||||
{
|
||||
QCoreApplication::setOrganizationName("QtProject");
|
||||
QCoreApplication::setOrganizationDomain("qt-project.org");
|
||||
QCoreApplication::setApplicationName("Qml2Puppet");
|
||||
QCoreApplication::setApplicationVersion("1.0.0");
|
||||
|
||||
if (application->arguments().count() < 2
|
||||
|| (application->arguments().at(1) == "--readcapturedstream" && application->arguments().count() < 3)
|
||||
|| (application->arguments().at(1) == "--rendericon" && application->arguments().count() < 5)
|
||||
|| (application->arguments().at(1) == "--import3dAsset" && application->arguments().count() < 6)) {
|
||||
qDebug() << "Usage:\n";
|
||||
qDebug() << "--test";
|
||||
qDebug() << "--version";
|
||||
qDebug() << "--readcapturedstream <stream file> [control stream file]";
|
||||
qDebug() << "--rendericon <icon size> <icon file name> <icon source qml>";
|
||||
qDebug() << "--import3dAsset <source asset file name> <output dir> <id number> <import options JSON>";
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (application->arguments().at(1) == "--readcapturedstream" && application->arguments().count() > 2) {
|
||||
QFileInfo inputStreamFileInfo(application->arguments().at(2));
|
||||
if (!inputStreamFileInfo.exists()) {
|
||||
qDebug() << "Input stream does not exist:" << inputStreamFileInfo.absoluteFilePath();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (application->arguments().count() > 3) {
|
||||
QFileInfo controlStreamFileInfo(application->arguments().at(3));
|
||||
if (!controlStreamFileInfo.exists()) {
|
||||
qDebug() << "Output stream does not exist:" << controlStreamFileInfo.absoluteFilePath();
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (application->arguments().count() == 2 && application->arguments().at(1) == "--test") {
|
||||
qDebug() << QCoreApplication::applicationVersion();
|
||||
QQmlEngine engine;
|
||||
|
||||
QQmlComponent component(&engine);
|
||||
component.setData("import QtQuick 2.0\nItem {\n}\n", QUrl::fromLocalFile("test.qml"));
|
||||
|
||||
QObject *object = component.create();
|
||||
|
||||
if (object) {
|
||||
qDebug() << "Basic QtQuick 2.0 working...";
|
||||
} else {
|
||||
qDebug() << "Basic QtQuick 2.0 not working...";
|
||||
qDebug() << component.errorString();
|
||||
}
|
||||
delete object;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (application->arguments().count() == 2 && application->arguments().at(1) == "--version") {
|
||||
std::cout << 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (application->arguments().at(1) != "--readcapturedstream" && application->arguments().count() < 4) {
|
||||
qDebug() << "Wrong argument count: " << application->arguments().count();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (application->arguments().at(1) == "--rendericon") {
|
||||
int size = application->arguments().at(2).toInt();
|
||||
QString iconFileName = application->arguments().at(3);
|
||||
QString iconSource = application->arguments().at(4);
|
||||
|
||||
IconRenderer *iconRenderer = new IconRenderer(size, iconFileName, iconSource);
|
||||
iconRenderer->setupRender();
|
||||
|
||||
return application->exec();
|
||||
}
|
||||
|
||||
if (application->arguments().at(1) == "--import3dAsset") {
|
||||
QString sourceAsset = application->arguments().at(2);
|
||||
QString outDir = application->arguments().at(3);
|
||||
QString options = application->arguments().at(4);
|
||||
|
||||
Import3D::import3D(sourceAsset, outDir, options);
|
||||
|
||||
return application->exec();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_BREAKPAD
|
||||
const QString libexecPath = QCoreApplication::applicationDirPath() + '/' + RELATIVE_LIBEXEC_PATH;
|
||||
QtSystemExceptionHandler systemExceptionHandler(libexecPath);
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
|
||||
/* startCrashpad(); */
|
||||
#endif
|
||||
|
||||
new QmlDesigner::Qt5NodeInstanceClientProxy(application);
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG)
|
||||
SetErrorMode(SEM_NOGPFAULTERRORBOX); //We do not want to see any message boxes
|
||||
#endif
|
||||
|
||||
if (application->arguments().at(1) == "--readcapturedstream")
|
||||
return 0;
|
||||
|
||||
return application->exec();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
qInstallMessageHandler(myMessageOutput);
|
||||
#endif
|
||||
// Since we always render text into an FBO, we need to globally disable
|
||||
// subpixel antialiasing and instead use gray.
|
||||
qputenv("QSG_DISTANCEFIELD_ANTIALIASING", "gray");
|
||||
#ifdef Q_OS_MACOS
|
||||
qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "true");
|
||||
#endif
|
||||
|
||||
//If a style different from Desktop is set we have to use QGuiApplication
|
||||
bool useGuiApplication = (!qEnvironmentVariableIsSet("QMLDESIGNER_FORCE_QAPPLICATION")
|
||||
|| qgetenv("QMLDESIGNER_FORCE_QAPPLICATION") != "true")
|
||||
&& qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")
|
||||
&& qgetenv("QT_QUICK_CONTROLS_STYLE") != "Desktop";
|
||||
|
||||
#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER
|
||||
Sqlite::LibraryInitializer::initialize();
|
||||
#endif
|
||||
|
||||
if (useGuiApplication) {
|
||||
QGuiApplication application(argc, argv);
|
||||
return internalMain(&application);
|
||||
} else {
|
||||
QApplication application(argc, argv);
|
||||
return internalMain(&application);
|
||||
}
|
||||
}
|
||||
111
src/tools/qml2puppet/qml2puppet/qmlbase.h
Normal file
111
src/tools/qml2puppet/qml2puppet/qmlbase.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH
|
||||
// Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDir>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFileOpenEvent>
|
||||
#include <QLibraryInfo>
|
||||
#include <QSurfaceFormat>
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "appmetadata.h"
|
||||
#include <iostream>
|
||||
|
||||
#include <QApplication>
|
||||
class QmlBase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct AppArgs
|
||||
{
|
||||
public:
|
||||
int argc;
|
||||
char **argv;
|
||||
};
|
||||
|
||||
QmlBase(int &argc, char **argv, QObject *parent = nullptr)
|
||||
: QObject{parent}
|
||||
, m_args({argc, argv})
|
||||
{
|
||||
m_argParser.setApplicationDescription("QML Runtime Provider for QDS");
|
||||
m_argParser.addOptions(
|
||||
{{"qml-puppet", "Run QML Puppet (default)"},
|
||||
{"qml-runtime", "Run QML Runtime"},
|
||||
{"appinfo", "Print build information"},
|
||||
{"test", "Run test mode"}
|
||||
});
|
||||
}
|
||||
|
||||
int run()
|
||||
{
|
||||
populateParser();
|
||||
initCoreApp();
|
||||
initParser();
|
||||
initQmlRunner();
|
||||
return m_coreApp->exec();
|
||||
}
|
||||
|
||||
QSharedPointer<QCoreApplication> coreApp() const { return m_coreApp; }
|
||||
|
||||
protected:
|
||||
virtual void initCoreApp() = 0;
|
||||
virtual void populateParser() = 0;
|
||||
virtual void initQmlRunner() = 0;
|
||||
|
||||
virtual int startTestMode()
|
||||
{
|
||||
qDebug() << "Test mode is not implemented for this type of runner";
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void createCoreApp()
|
||||
{
|
||||
m_coreApp.reset(new T(m_args.argc, m_args.argv));
|
||||
}
|
||||
|
||||
QSharedPointer<QCoreApplication> m_coreApp;
|
||||
QCommandLineParser m_argParser;
|
||||
QSharedPointer<QQmlApplicationEngine> m_qmlEngine;
|
||||
|
||||
AppArgs m_args;
|
||||
|
||||
private:
|
||||
void initParser()
|
||||
{
|
||||
QCommandLineOption optHelp = m_argParser.addHelpOption();
|
||||
QCommandLineOption optVers = m_argParser.addVersionOption();
|
||||
|
||||
if (!m_coreApp) {
|
||||
qCritical() << "Cannot initialize coreapp!";
|
||||
m_argParser.showHelp();
|
||||
}
|
||||
|
||||
if (!m_argParser.parse(m_coreApp->arguments())) {
|
||||
std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl
|
||||
<< std::endl;
|
||||
m_argParser.showHelp(1);
|
||||
} else if (m_argParser.isSet(optVers)) {
|
||||
m_argParser.showVersion();
|
||||
} else if (m_argParser.isSet(optHelp)) {
|
||||
m_argParser.showHelp(0);
|
||||
} else if (m_argParser.isSet("appinfo")) {
|
||||
QDSMeta::AppInfo::printAppInfo();
|
||||
} else if (m_argParser.isSet("test")) {
|
||||
exit(startTestMode());
|
||||
}
|
||||
}
|
||||
};
|
||||
130
src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
Normal file
130
src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "qmlpuppet.h"
|
||||
#include "configcrashpad.h"
|
||||
|
||||
#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER
|
||||
#include <sqlitelibraryinitializer.h>
|
||||
#endif
|
||||
|
||||
#include <qml2puppet/iconrenderer/iconrenderer.h>
|
||||
#include <qml2puppet/import3d/import3d.h>
|
||||
|
||||
#include <qt5nodeinstanceclientproxy.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG)
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
void QmlPuppet::initCoreApp()
|
||||
{
|
||||
// Since we always render text into an FBO, we need to globally disable
|
||||
// subpixel antialiasing and instead use gray.
|
||||
qputenv("QSG_DISTANCEFIELD_ANTIALIASING", "gray");
|
||||
#ifdef Q_OS_MACOS
|
||||
qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "true");
|
||||
#endif
|
||||
#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER
|
||||
Sqlite::LibraryInitializer::initialize();
|
||||
#endif
|
||||
|
||||
//If a style different from Desktop is set we have to use QGuiApplication
|
||||
bool useGuiApplication = (!qEnvironmentVariableIsSet("QMLDESIGNER_FORCE_QAPPLICATION")
|
||||
|| qgetenv("QMLDESIGNER_FORCE_QAPPLICATION") != "true")
|
||||
&& qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")
|
||||
&& qgetenv("QT_QUICK_CONTROLS_STYLE") != "Desktop";
|
||||
#ifndef QT_GUI_LIB
|
||||
createCoreApp<QCoreApplication>();
|
||||
#else
|
||||
#if defined QT_WIDGETS_LIB
|
||||
if (!useGuiApplication)
|
||||
createCoreApp<QApplication>();
|
||||
else
|
||||
#endif //QT_WIDGETS_LIB
|
||||
createCoreApp<QGuiApplication>();
|
||||
#endif //QT_GUI_LIB
|
||||
}
|
||||
|
||||
int QmlPuppet::startTestMode()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent component(&engine);
|
||||
component.setData("import QtQuick 2.0\nItem {\n}\n", QUrl::fromLocalFile("test.qml"));
|
||||
|
||||
if (!QSharedPointer<QObject>(component.create())) {
|
||||
qDebug() << "Basic QtQuick 2.0 not working...";
|
||||
qDebug() << component.errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
qDebug() << "Basic QtQuick 2.0 working...";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QmlPuppet::populateParser()
|
||||
{
|
||||
// we're not using the commandline parser but just populating the help text
|
||||
m_argParser.addOptions(
|
||||
{{"readcapturedstream", "Read captured stream.", "inputStream, [outputStream]"},
|
||||
{"rendericon", "Renders icon.", "size, fileName, sourceQml"},
|
||||
{"import3dAsset", "Import 3d asset.", "sourceAsset, outDir, importOptJson"}});
|
||||
}
|
||||
|
||||
void QmlPuppet::initQmlRunner()
|
||||
{
|
||||
if (m_coreApp->arguments().count() < 2
|
||||
|| (m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() < 3)
|
||||
|| (m_argParser.isSet("rendericon") && m_coreApp->arguments().count() < 5)
|
||||
|| (m_argParser.isSet("import3dAsset") && m_coreApp->arguments().count() < 6)
|
||||
|| (!m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() < 4)) {
|
||||
qDebug() << "Wrong argument count: " << m_coreApp->arguments().count();
|
||||
m_argParser.showHelp(1);
|
||||
}
|
||||
|
||||
if (m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() > 2) {
|
||||
QString fileName = m_argParser.value("readcapturedstream");
|
||||
if (!QFile::exists(fileName)) {
|
||||
qDebug() << "Input stream does not exist:" << fileName;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (m_coreApp->arguments().count() > 3) {
|
||||
fileName = m_coreApp->arguments().at(3);
|
||||
if (!QFile::exists(fileName)) {
|
||||
qDebug() << "Output stream does not exist:" << fileName;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_argParser.isSet("rendericon")) {
|
||||
int size = m_coreApp->arguments().at(2).toInt();
|
||||
QString iconFileName = m_coreApp->arguments().at(3);
|
||||
QString iconSource = m_coreApp->arguments().at(4);
|
||||
|
||||
m_iconRenderer.reset(new IconRenderer(size, iconFileName, iconSource));
|
||||
m_iconRenderer->setupRender();
|
||||
} else if (m_argParser.isSet("import3dAsset")) {
|
||||
QString sourceAsset = m_coreApp->arguments().at(2);
|
||||
QString outDir = m_coreApp->arguments().at(3);
|
||||
QString options = m_coreApp->arguments().at(4);
|
||||
|
||||
Import3D::import3D(sourceAsset, outDir, options);
|
||||
}
|
||||
|
||||
startCrashpad();
|
||||
|
||||
new QmlDesigner::Qt5NodeInstanceClientProxy(m_coreApp.get());
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG)
|
||||
SetErrorMode(SEM_NOGPFAULTERRORBOX); //We do not want to see any message boxes
|
||||
#endif
|
||||
|
||||
if (m_argParser.isSet("readcapturedstream"))
|
||||
exit(0);
|
||||
}
|
||||
21
src/tools/qml2puppet/qml2puppet/qmlpuppet.h
Normal file
21
src/tools/qml2puppet/qml2puppet/qmlpuppet.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../qmlbase.h"
|
||||
|
||||
class IconRenderer;
|
||||
class QmlPuppet : public QmlBase
|
||||
{
|
||||
using QmlBase::QmlBase;
|
||||
|
||||
private:
|
||||
void initCoreApp() override;
|
||||
void populateParser() override;
|
||||
int startTestMode() override;
|
||||
void initQmlRunner() override;
|
||||
|
||||
private:
|
||||
QSharedPointer<IconRenderer> m_iconRenderer;
|
||||
};
|
||||
97
src/tools/qml2puppet/qml2puppet/runner/loadwatcher.h
Normal file
97
src/tools/qml2puppet/qml2puppet/runner/loadwatcher.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qmlconfiguration.h"
|
||||
|
||||
#include "QtQml/qqmlcomponent.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
//// Listens to the appEngine signals to determine if all files failed to load
|
||||
class LoadWatcher : public QObject
|
||||
{
|
||||
// Q_OBJECT
|
||||
public:
|
||||
LoadWatcher(QQmlApplicationEngine *e, int expected, Config *conf)
|
||||
: QObject(e)
|
||||
, qae(e)
|
||||
, conf(conf)
|
||||
, expectedFileCount(expected)
|
||||
{
|
||||
connect(e, &QQmlApplicationEngine::objectCreated, this, &LoadWatcher::checkFinished);
|
||||
// QQmlApplicationEngine also connects quit() to QCoreApplication::quit
|
||||
// and exit() to QCoreApplication::exit but if called before exec()
|
||||
// then QCoreApplication::quit or QCoreApplication::exit does nothing
|
||||
connect(e, &QQmlEngine::quit, this, &LoadWatcher::quit);
|
||||
connect(e, &QQmlEngine::exit, this, &LoadWatcher::exit);
|
||||
}
|
||||
|
||||
int returnCode = 0;
|
||||
bool earlyExit = false;
|
||||
|
||||
public Q_SLOTS:
|
||||
void checkFinished(QObject *o, const QUrl &url)
|
||||
{
|
||||
Q_UNUSED(url);
|
||||
if (o) {
|
||||
checkForWindow(o);
|
||||
if (conf && qae) {
|
||||
for (PartialScene *ps : std::as_const(conf->completers)) {
|
||||
if (o->inherits(ps->itemType().toUtf8().constData()))
|
||||
contain(o, ps->container());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (haveWindow)
|
||||
return;
|
||||
|
||||
if (!--expectedFileCount) {
|
||||
printf("qml: Did not load any objects, exiting.\n");
|
||||
exit(2);
|
||||
QCoreApplication::exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
// Will be checked before calling exec()
|
||||
earlyExit = true;
|
||||
returnCode = 0;
|
||||
}
|
||||
void exit(int retCode)
|
||||
{
|
||||
earlyExit = true;
|
||||
returnCode = retCode;
|
||||
}
|
||||
|
||||
private:
|
||||
void contain(QObject *o, const QUrl &containPath)
|
||||
{
|
||||
QQmlComponent c(qae, containPath);
|
||||
QObject *o2 = c.create();
|
||||
if (!o2)
|
||||
return;
|
||||
o2->setParent(this);
|
||||
checkForWindow(o2);
|
||||
bool success = false;
|
||||
int idx;
|
||||
if ((idx = o2->metaObject()->indexOfProperty("containedObject")) != -1)
|
||||
success = o2->metaObject()->property(idx).write(o2, QVariant::fromValue<QObject *>(o));
|
||||
if (!success)
|
||||
o->setParent(o2); // Set QObject parent, and assume container will react as needed
|
||||
}
|
||||
void checkForWindow(QObject *o)
|
||||
{
|
||||
if (o->isWindowType() && o->inherits("QQuickWindow"))
|
||||
haveWindow = true;
|
||||
}
|
||||
|
||||
private:
|
||||
QQmlApplicationEngine *qae;
|
||||
Config *conf;
|
||||
|
||||
bool haveWindow = false;
|
||||
int expectedFileCount;
|
||||
};
|
||||
66
src/tools/qml2puppet/qml2puppet/runner/qmlconfiguration.h
Normal file
66
src/tools/qml2puppet/qml2puppet/runner/qmlconfiguration.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QUrl>
|
||||
#include <QtQml/qqml.h>
|
||||
|
||||
class PartialScene : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl container READ container WRITE setContainer NOTIFY containerChanged)
|
||||
Q_PROPERTY(QString itemType READ itemType WRITE setItemType NOTIFY itemTypeChanged)
|
||||
QML_ELEMENT
|
||||
QML_ADDED_IN_VERSION(1, 0)
|
||||
public:
|
||||
PartialScene(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
const QUrl container() const { return m_container; }
|
||||
const QString itemType() const { return m_itemType; }
|
||||
|
||||
void setContainer(const QUrl &a)
|
||||
{
|
||||
if (a == m_container)
|
||||
return;
|
||||
m_container = a;
|
||||
emit containerChanged();
|
||||
}
|
||||
void setItemType(const QString &a)
|
||||
{
|
||||
if (a == m_itemType)
|
||||
return;
|
||||
m_itemType = a;
|
||||
emit itemTypeChanged();
|
||||
}
|
||||
|
||||
signals:
|
||||
void containerChanged();
|
||||
void itemTypeChanged();
|
||||
|
||||
private:
|
||||
QUrl m_container;
|
||||
QString m_itemType;
|
||||
};
|
||||
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlListProperty<PartialScene> sceneCompleters READ sceneCompleters)
|
||||
Q_CLASSINFO("DefaultProperty", "sceneCompleters")
|
||||
QML_NAMED_ELEMENT(Configuration)
|
||||
QML_ADDED_IN_VERSION(1, 0)
|
||||
public:
|
||||
Config(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
QQmlListProperty<PartialScene> sceneCompleters()
|
||||
{
|
||||
return QQmlListProperty<PartialScene>(this, &completers);
|
||||
}
|
||||
|
||||
QList<PartialScene *> completers;
|
||||
};
|
||||
348
src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp
Normal file
348
src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
#include "loadwatcher.h"
|
||||
#include "qmlruntime.h"
|
||||
|
||||
#include <private/qqmlimport_p.h>
|
||||
#include <private/qtqmlglobal_p.h>
|
||||
#if QT_CONFIG(qml_animation)
|
||||
#include <private/qabstractanimation_p.h>
|
||||
#endif
|
||||
|
||||
#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
|
||||
#define QSL QStringLiteral
|
||||
|
||||
void QmlRuntime::populateParser()
|
||||
{
|
||||
m_argParser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
|
||||
m_argParser.setOptionsAfterPositionalArgumentsMode(
|
||||
QCommandLineParser::ParseAsPositionalArguments);
|
||||
|
||||
m_argParser.addOptions(
|
||||
{{QStringList() << QSL("a") << QSL("apptype"),
|
||||
QSL("Select which application class to use. Default is gui."),
|
||||
QSL("core|gui|widget")}, // just for translation
|
||||
|
||||
{QSL("I"), QSL("Prepend the given path to the import paths."), QSL("path")},
|
||||
|
||||
{QSL("f"), QSL("Load the given file as a QML file."), QSL("file")},
|
||||
|
||||
{QStringList() << QSL("c") << QSL("config"),
|
||||
QSL("Load the given built-in configuration or configuration file."),
|
||||
QSL("file")},
|
||||
|
||||
{QStringList() << QSL("list-conf"), QSL("List the built-in configurations.")},
|
||||
|
||||
{QSL("translation"), QSL("Load the given file as the translations file."), QSL("file")},
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
// OpenGL options
|
||||
{QSL("desktop"), QSL("Force use of desktop OpenGL (AA_UseDesktopOpenGL).")},
|
||||
|
||||
{QSL("gles"), QSL("Force use of GLES (AA_UseOpenGLES).")},
|
||||
|
||||
{QSL("software"), QSL("Force use of software rendering (AA_UseSoftwareOpenGL).")},
|
||||
|
||||
{QSL("core-profile"), QSL("Force use of OpenGL Core Profile.")},
|
||||
|
||||
{QSL("disable-context-sharing"),
|
||||
QSL("Disable the use of a shared GL context for QtQuick Windows")},
|
||||
#endif // QT_GUI_LIB
|
||||
|
||||
// Debugging and verbosity options
|
||||
{QSL("quiet"), QSL("Suppress all output.")},
|
||||
|
||||
{QSL("verbose"),
|
||||
QSL("Print information about what qml is doing, like specific file URLs being loaded.")},
|
||||
|
||||
{QSL("slow-animations"), QSL("Run all animations in slow motion.")},
|
||||
|
||||
{QSL("fixed-animations"), QSL("Run animations off animation tick rather than wall time.")},
|
||||
|
||||
{QStringList() << QSL("r") << QSL("rhi"),
|
||||
QSL("Set the backend for the Qt graphics abstraction (RHI). "
|
||||
"Backend is one of: default, vulkan, metal, d3d11, gl"),
|
||||
QSL("backend")},
|
||||
|
||||
{QSL("S"), QSL("Add selector to the list of QQmlFileSelectors."), QSL("selector")}});
|
||||
|
||||
// Positional arguments
|
||||
m_argParser.addPositionalArgument(
|
||||
"files",
|
||||
QSL("Any number of QML files can be loaded. They will share the same engine."),
|
||||
"[files...]");
|
||||
m_argParser.addPositionalArgument("args",
|
||||
QSL("Arguments after '--' are ignored, but passed through to "
|
||||
"the application.arguments variable in QML."),
|
||||
"[-- args...]");
|
||||
}
|
||||
|
||||
void QmlRuntime::initCoreApp()
|
||||
{
|
||||
bool glShareContexts = true;
|
||||
|
||||
// these attributes must be set before the QCoreApp is initialized
|
||||
for (int i = 0; i < m_args.argc; i++) {
|
||||
if (!strcmp(m_args.argv[i], "-desktop") || !strcmp(m_args.argv[i], "--desktop")) {
|
||||
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
||||
} else if (!strcmp(m_args.argv[i], "-gles") || !strcmp(m_args.argv[i], "--gles")) {
|
||||
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
|
||||
} else if (!strcmp(m_args.argv[i], "-software") || !strcmp(m_args.argv[i], "--software")) {
|
||||
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
|
||||
} else if (!strcmp(m_args.argv[i], "-disable-context-sharing")
|
||||
|| !strcmp(m_args.argv[i], "--disable-context-sharing")) {
|
||||
glShareContexts = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (glShareContexts)
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
|
||||
// since we handled all attributes above, now we can initialize the core app
|
||||
for (int i = 0; i < m_args.argc; i++) {
|
||||
if (!strcmp(m_args.argv[i], "--apptype") || !strcmp(m_args.argv[i], "-a")
|
||||
|| !strcmp(m_args.argv[i], "-apptype")) {
|
||||
if (i + 1 < m_args.argc) {
|
||||
++i;
|
||||
if (!strcmp(m_args.argv[i], "core")) {
|
||||
createCoreApp<QCoreApplication>();
|
||||
}
|
||||
else if (!strcmp(m_args.argv[i], "gui")) {
|
||||
createCoreApp<QGuiApplication>();
|
||||
}
|
||||
#ifdef QT_WIDGETS_LIB
|
||||
else if (!strcmp(m_args.argv[i], "widget")) {
|
||||
createCoreApp<QApplication>();
|
||||
static_cast<QApplication *>(m_coreApp.get())
|
||||
->setWindowIcon(QIcon(m_iconResourcePath));
|
||||
}
|
||||
#endif // QT_WIDGETS_LIB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QmlRuntime::initQmlRunner()
|
||||
{
|
||||
m_qmlEngine.reset(new QQmlApplicationEngine());
|
||||
|
||||
QStringList files;
|
||||
QString confFile;
|
||||
QString translationFile;
|
||||
|
||||
if (!m_argParser.parse(QCoreApplication::arguments())) {
|
||||
qWarning() << m_argParser.errorText();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// TODO: replace below logging modes with a proper logging category
|
||||
m_verboseMode = m_argParser.isSet("verbose");
|
||||
m_quietMode = (!m_verboseMode && m_argParser.isSet("quiet"));
|
||||
// FIXME: need to re-evaluate. we have our own message handler.
|
||||
// if (quietMode) {
|
||||
// qInstallMessageHandler(quietMessageHandler);
|
||||
// QLoggingCategory::setFilterRules(QStringLiteral("*=false"));
|
||||
// }
|
||||
|
||||
if (m_argParser.isSet("list-conf")) {
|
||||
listConfFiles();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(qml_animation)
|
||||
if (m_argParser.isSet("slow-animations"))
|
||||
QUnifiedTimer::instance()->setSlowModeEnabled(true);
|
||||
if (m_argParser.isSet("fixed-animations"))
|
||||
QUnifiedTimer::instance()->setConsistentTiming(true);
|
||||
#endif
|
||||
const auto valsImportPath = m_argParser.values("I");
|
||||
for (const QString &importPath : valsImportPath)
|
||||
m_qmlEngine->addImportPath(importPath);
|
||||
|
||||
QStringList customSelectors;
|
||||
|
||||
const auto valsSelectors = m_argParser.values("S");
|
||||
for (const QString &selector : valsSelectors)
|
||||
customSelectors.append(selector);
|
||||
|
||||
if (!customSelectors.isEmpty())
|
||||
m_qmlEngine->setExtraFileSelectors(customSelectors);
|
||||
|
||||
if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE")
|
||||
|| qEnvironmentVariableIsSet("QML_CORE_PROFILE") || m_argParser.isSet("core-profile")) {
|
||||
QSurfaceFormat surfaceFormat;
|
||||
surfaceFormat.setStencilBufferSize(8);
|
||||
surfaceFormat.setDepthBufferSize(24);
|
||||
surfaceFormat.setVersion(4, 1);
|
||||
surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
|
||||
QSurfaceFormat::setDefaultFormat(surfaceFormat);
|
||||
}
|
||||
|
||||
if (m_argParser.isSet("config"))
|
||||
confFile = m_argParser.value("config");
|
||||
if (m_argParser.isSet("translation"))
|
||||
translationFile = m_argParser.value("translation");
|
||||
if (m_argParser.isSet("rhi")) {
|
||||
const QString rhiBackend = m_argParser.value("rhi");
|
||||
if (rhiBackend == QLatin1String("default"))
|
||||
qunsetenv("QSG_RHI_BACKEND");
|
||||
else
|
||||
qputenv("QSG_RHI_BACKEND", rhiBackend.toLatin1());
|
||||
}
|
||||
|
||||
const auto valsPosArgs = m_argParser.positionalArguments();
|
||||
files << m_argParser.values("f");
|
||||
for (const QString &posArg : valsPosArgs) {
|
||||
if (posArg == QLatin1String("--"))
|
||||
break;
|
||||
else
|
||||
files << posArg;
|
||||
}
|
||||
|
||||
#if QT_CONFIG(translation)
|
||||
// Need to be installed before QQmlApplicationEngine's automatic translation loading
|
||||
// (qt_ translations are loaded there)
|
||||
if (!translationFile.isEmpty()) {
|
||||
QTranslator translator;
|
||||
|
||||
if (translator.load(translationFile)) {
|
||||
m_coreApp->installTranslator(&translator);
|
||||
if (m_verboseMode)
|
||||
qInfo() << "qml: Loaded translation file %s\n",
|
||||
qPrintable(QDir::toNativeSeparators(translationFile));
|
||||
} else {
|
||||
if (!m_quietMode)
|
||||
qInfo() << "qml: Could not load the translation file %s\n",
|
||||
qPrintable(QDir::toNativeSeparators(translationFile));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (!translationFile.isEmpty() && !quietMode)
|
||||
qInfo() << "qml: Translation file specified, but Qt built without translation support.\n");
|
||||
#endif
|
||||
|
||||
if (files.size() <= 0) {
|
||||
#if defined(Q_OS_DARWIN)
|
||||
if (qobject_cast<QGuiApplication *>(m_coreApp.data())) {
|
||||
m_exitTimerId = static_cast<QGuiApplication *>(m_coreApp.get())
|
||||
->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (!m_quietMode)
|
||||
qCritical() << "No files specified. Terminating.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
loadConf(confFile, !m_verboseMode);
|
||||
|
||||
// Load files
|
||||
QScopedPointer<LoadWatcher> lw(new LoadWatcher(m_qmlEngine.data(), files.size(), m_conf.data()));
|
||||
|
||||
for (const QString &path : std::as_const(files)) {
|
||||
QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile);
|
||||
if (m_verboseMode)
|
||||
qInfo() << "qml: loading %s\n", qPrintable(url.toString());
|
||||
m_qmlEngine->load(url);
|
||||
}
|
||||
|
||||
if (lw->earlyExit)
|
||||
exit(lw->returnCode);
|
||||
}
|
||||
|
||||
void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app on failure
|
||||
{
|
||||
const QString defaultFileName = QLatin1String("default.qml");
|
||||
QUrl settingsUrl;
|
||||
bool builtIn = false; //just for keeping track of the warning
|
||||
if (override.isEmpty()) {
|
||||
QFileInfo fi;
|
||||
fi.setFile(QStandardPaths::locate(QStandardPaths::AppDataLocation, defaultFileName));
|
||||
if (fi.exists()) {
|
||||
settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
|
||||
} else {
|
||||
// If different built-in configs are needed per-platform, just apply QFileSelector to the qrc conf.qml path
|
||||
fi.setFile(m_confResourcePath + defaultFileName);
|
||||
settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
|
||||
builtIn = true;
|
||||
}
|
||||
} else {
|
||||
QFileInfo fi;
|
||||
fi.setFile(m_confResourcePath + override + QLatin1String(".qml"));
|
||||
if (fi.exists()) {
|
||||
settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
|
||||
builtIn = true;
|
||||
} else {
|
||||
fi.setFile(QDir(QStandardPaths::locate(QStandardPaths::AppConfigLocation,
|
||||
override,
|
||||
QStandardPaths::LocateDirectory)),
|
||||
m_confResourcePath);
|
||||
if (fi.exists())
|
||||
settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
|
||||
else
|
||||
fi.setFile(override);
|
||||
if (!fi.exists()) {
|
||||
qCritical() << "qml: Couldn't find required configuration file: %s\n",
|
||||
qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath()));
|
||||
exit(1);
|
||||
}
|
||||
settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
qInfo() << "qml: %s\n", QLibraryInfo::build();
|
||||
if (builtIn) {
|
||||
qInfo() << "qml: Using built-in configuration: %s\n",
|
||||
qPrintable(override.isEmpty() ? defaultFileName : override);
|
||||
} else {
|
||||
qInfo() << "qml: Using configuration: %s\n",
|
||||
qPrintable(settingsUrl.isLocalFile()
|
||||
? QDir::toNativeSeparators(settingsUrl.toLocalFile())
|
||||
: settingsUrl.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: When we have better engine control, ban QtQuick* imports on this engine
|
||||
QQmlEngine e2;
|
||||
QQmlComponent c2(&e2, settingsUrl);
|
||||
m_conf.reset(qobject_cast<Config *>(c2.create()));
|
||||
|
||||
if (!m_conf) {
|
||||
qCritical() << "qml: Error loading configuration file: %s\n", qPrintable(c2.errorString());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlRuntime::listConfFiles()
|
||||
{
|
||||
const QDir confResourceDir(m_confResourcePath);
|
||||
qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:"));
|
||||
for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files))
|
||||
qInfo() << " %s\n", qPrintable(fi.baseName());
|
||||
qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:"));
|
||||
bool foundOther = false;
|
||||
const QStringList otherLocations = QStandardPaths::standardLocations(
|
||||
QStandardPaths::AppConfigLocation);
|
||||
for (const auto &confDirPath : otherLocations) {
|
||||
const QDir confDir(confDirPath);
|
||||
for (const QFileInfo &fi : confDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||
foundOther = true;
|
||||
if (m_verboseMode)
|
||||
qInfo() << " %s\n", qPrintable(fi.absoluteFilePath());
|
||||
else
|
||||
qInfo() << " %s\n", qPrintable(fi.baseName());
|
||||
}
|
||||
}
|
||||
if (!foundOther)
|
||||
qInfo() << " %s\n", qPrintable(QCoreApplication::translate("main", "none"));
|
||||
if (m_verboseMode) {
|
||||
qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:"));
|
||||
for (const auto &confDirPath : otherLocations)
|
||||
qInfo() << " %s\n", qPrintable(confDirPath);
|
||||
}
|
||||
}
|
||||
30
src/tools/qml2puppet/qml2puppet/runner/qmlruntime.h
Normal file
30
src/tools/qml2puppet/qml2puppet/runner/qmlruntime.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../qmlbase.h"
|
||||
#include "qmlconfiguration.h"
|
||||
|
||||
class QmlRuntime : public QmlBase
|
||||
{
|
||||
using QmlBase::QmlBase;
|
||||
|
||||
private:
|
||||
void initCoreApp() override;
|
||||
void populateParser() override;
|
||||
void initQmlRunner() override;
|
||||
|
||||
void listConfFiles();
|
||||
void loadConf(const QString &override, bool quiet);
|
||||
|
||||
const QString m_iconResourcePath = QStringLiteral(":/qt-project.org/QmlRuntime/resources/qml-64.png");
|
||||
const QString m_confResourcePath = QStringLiteral(":/runner/runnerconf/qmlruntime/");
|
||||
|
||||
|
||||
QSharedPointer<Config> m_conf;
|
||||
bool m_verboseMode = false;
|
||||
bool m_quietMode = false;
|
||||
int m_exitTimerId = -1;
|
||||
};
|
||||
|
||||
@@ -12,4 +12,9 @@
|
||||
<file>mockfiles/ToolBarButton.qml</file>
|
||||
<file>mockfiles/ToggleButton.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/runner">
|
||||
<file>runnerconf/qmlruntime/default.qml</file>
|
||||
<file>runnerconf/qmlruntime/content/resizeItemToWindow.qml</file>
|
||||
<file>runnerconf/qmlruntime/content/resizeWindowToItem.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2019 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import QtQuick.Window 2.0
|
||||
import QtQuick 2.0
|
||||
|
||||
Window {
|
||||
property Item containedObject: null
|
||||
property bool __resizeGuard: false
|
||||
onContainedObjectChanged: {
|
||||
if (containedObject == undefined || containedObject == null) {
|
||||
visible = false
|
||||
return
|
||||
}
|
||||
__resizeGuard = true
|
||||
width = containedObject.width
|
||||
height = containedObject.height
|
||||
containedObject.parent = contentItem
|
||||
visible = true
|
||||
__resizeGuard = false
|
||||
}
|
||||
onWidthChanged: if (!__resizeGuard && containedObject)
|
||||
containedObject.width = width
|
||||
onHeightChanged: if (!__resizeGuard && containedObject)
|
||||
containedObject.height = height
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (C) 2019 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import QtQuick.Window 2.0
|
||||
import QtQuick 2.0
|
||||
|
||||
Window {
|
||||
property Item containedObject: null
|
||||
onContainedObjectChanged: {
|
||||
if (containedObject == undefined || containedObject == null) {
|
||||
visible = false
|
||||
return
|
||||
}
|
||||
width = Qt.binding(function () {
|
||||
return containedObject.width
|
||||
})
|
||||
height = Qt.binding(function () {
|
||||
return containedObject.height
|
||||
})
|
||||
containedObject.parent = contentItem
|
||||
visible = true
|
||||
}
|
||||
}
|
||||
10
src/tools/qml2puppet/runnerconf/qmlruntime/default.qml
Normal file
10
src/tools/qml2puppet/runnerconf/qmlruntime/default.qml
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (C) 2019 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
import QmlRuntime.QmlConfiguration 1.0
|
||||
|
||||
Configuration {
|
||||
PartialScene {
|
||||
itemType: "QQuickItem"
|
||||
container: Qt.resolvedUrl("content/resizeItemToWindow.qml")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user