Merge "Merge remote-tracking branch 'origin/4.11'"

This commit is contained in:
The Qt Project
2019-12-09 10:49:19 +00:00
145 changed files with 2761 additions and 634 deletions

1
.gitignore vendored
View File

@@ -55,6 +55,7 @@ CMakeLists.txt.user*
/src/plugins/**/*.json
/src/plugins/coreplugin/ide_version.h
/src/libs/qt-breakpad/bin
/.cmake/
app_version.h
phony.c

View File

@@ -491,16 +491,18 @@ function(add_qtc_library name)
install(TARGETS ${name}
EXPORT ${IDE_CASED_ID}
RUNTIME DESTINATION "${_DESTINATION}"
RUNTIME DESTINATION "${_DESTINATION}" OPTIONAL
LIBRARY
DESTINATION "${IDE_LIBRARY_PATH}"
${NAMELINK_OPTION}
OPTIONAL
OBJECTS
DESTINATION "${IDE_LIBRARY_PATH}"
COMPONENT Devel EXCLUDE_FROM_ALL
ARCHIVE
DESTINATION "${IDE_LIBRARY_PATH}"
COMPONENT Devel EXCLUDE_FROM_ALL
OPTIONAL
)
if (NAMELINK_OPTION)
@@ -509,6 +511,7 @@ function(add_qtc_library name)
DESTINATION "${IDE_LIBRARY_PATH}"
NAMELINK_ONLY
COMPONENT Devel EXCLUDE_FROM_ALL
OPTIONAL
)
endif()
@@ -703,11 +706,12 @@ function(add_qtc_plugin target_name)
if (NOT _arg_SKIP_INSTALL)
install(TARGETS ${target_name}
EXPORT ${IDE_CASED_ID}
RUNTIME DESTINATION "${plugin_dir}"
LIBRARY DESTINATION "${plugin_dir}"
RUNTIME DESTINATION "${plugin_dir}" OPTIONAL
LIBRARY DESTINATION "${plugin_dir}" OPTIONAL
ARCHIVE
DESTINATION "${plugin_dir}"
COMPONENT Devel EXCLUDE_FROM_ALL
OPTIONAL
)
endif()
endfunction()
@@ -857,7 +861,7 @@ function(add_qtc_executable name)
enable_pch(${name})
if (NOT _arg_SKIP_INSTALL)
install(TARGETS ${name} DESTINATION "${_DESTINATION}")
install(TARGETS ${name} DESTINATION "${_DESTINATION}" OPTIONAL)
endif()
endfunction()

View File

@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
@@ -61,10 +61,6 @@
switch to the nfs-kernel-server, or create a symlink so that the
settings are stored locally.
\li Qt 4.7.4 is known to contain a bug exposed by g++ 4.6 which triggers
a crash in \QC. For more information, see
\l{https://bugreports.qt.io/browse/QTBUG-21265}{QTBUG-21265}
\li The Okteta KDE custom widget plugin might be installed as part of
some Linux distributions. It can cause Qt Designer to crash. For
more information, see:

View File

@@ -111,7 +111,6 @@
\li \l {Editing PathView Properties}
\li \l {Browsing ISO 7000 Icons}
\li \l {Qt Quick UI Forms}
\li \l {Exporting Designs from Graphics Software}
\li \l {Using QML Modules with Plugins}
\li \l {Converting UI Projects to Applications}
\endlist

View File

@@ -157,12 +157,6 @@
QML language and you can edit them in the Design mode.
\if defined(qtcreator)
\li \l {Exporting Designs from Graphics Software}
You can export designs from graphics software, such as Adobe
Photoshop and GIMP, to QML files. You can then edit QML files in
\QC.
\li \l {Using QML Modules with Plugins}
QML modules may use plugins to expose components defined in C++ to

View File

@@ -1,171 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
**
****************************************************************************/
// **********************************************************************
// NOTE: the sections are not ordered by their logical order to avoid
// reshuffling the file each time the index order changes (i.e., often).
// Run the fixnavi.pl script to adjust the links to the index order.
// **********************************************************************
/*!
\contentspage index.html
\previouspage creator-quick-ui-forms.html
\page quick-export-to-qml.html
\nextpage creator-qml-modules-with-plugins.html
\title Exporting Designs from Graphics Software
You can export designs from graphics software, such as Adobe Photoshop and
GIMP, to QML files.
\section1 Exporting from Adobe Photoshop to QML
You can use a QML Asset Exporter to export designs from Adobe Photoshop
to .ui.qml files. The QML Asset Exporter provides templates that are
based on the \l{http://www.pngexpress.com}{PNG EXPRESS} plugin for
Photoshop. They enable you to export PSD files to QML format, so that each
PSD file is converted into an .ui.qml file.
The filename of the generated document is based on the name of the PSD file.
Tagged image and text objects are exported with absolute positioning. Text
objects are exported with font and alignment information.
For more information, see the
\l {https://github.com/Pelagicore/QmlAssetExporter}{QML Asset Exporter}
documentation.
\section1 Exporting from GIMP to QML
\image qml-export-gimp.png
Each scene is converted into a single QML file with an
\l [QML]{Image} or a \l [QML]{Text} item for each layer and saved on the
development PC. Each layer is exported as an item.
You can open the QML file in \QC for editing. By default, the export scripts
generate Qt Quick 1 files. To edit the files in the Design mode, change the import
statements in the export scripts to import Qt Quick 2. Or, you can change
the import statement in each file after generating the files.
\section2 Conversion Rules
The following rules apply to the conversions:
\list
\li Layer names are used as item names. Spaces and hash marks (#) are
replaced with underscore characters to create valid ids for the
items.
\li Layer styles, such as drop shadows, are converted to images.
\li Offset, size, ordering and opacity are preserved.
\li Text layers are converted to \l [QML]{Text} items, unless you
specify that they be converted to \l [QML]{Image} items.
\li Hidden layers can be exported, and their visibility is set to
hidden.
\li PNG images are copied to the images subirectory.
\endlist
\section2 Preparing Files for Conversion
To create QML files that are easy to use, prepare the
GIMP designs for exporting, as follows:
\list
\li To minimize the number of items, minimize the number of layers,
because each layer or is
exported as a \l [QML]{Text} or \l [QML]{Image} item.
\li To determine that some layers are not exported, hide them, and
deselect the \uicontrol {Export hidden} check box during exporting.
\li To make it easier to find the layers after
exporting them, use descriptive names for them.
\li To make sure that image dimensions are preserved during export,
create at least one fully filled layer (which can be hidden), such
as a background layer. If the export script does not find a fully
filled layer, it resizes all images to the size of the canvas.
\li To prevent errors during export, make sure that the layers are not
locked. Locked layers cannot be exported.
\li To avoid unexpected results, do not use Blending Mode effects. They
are not exported.
\endlist
\section2 Running the Export Script
The script has been tested to work on GIMP 2. You can download GIMP 2 from
\l{http://www.gimp.org/downloads/}{GIMP Downloads}.
\list 1
\li Clone the repository that contains the export script,
\e qmlexporter.py, from
\l{https://codereview.qt-project.org/#/admin/projects/qt-labs/gimp-qmlexporter}
{Qt Code Review}.
\note Read the INSTALL.txt in the repository for latest information
about the script.
\li Copy the export script to the plug-ins directory in the GIMP
installation directory.
\li Check the properties of the file to make sure that it is executable.
On Linux, run the following command: \c {chmod u+rx}
\li To generate QML files that you can edit in the Design mode, edit the
import statement in \e qmlexporter.py. For example:
\code
f.write('import QtQuick 2.5\n')
\endcode
\li Restart GIMP to have the export command added to the \uicontrol File menu.
\li Choose \uicontrol {File > Export to QML} to export the design to a QML
file.
\li In the \uicontrol {Export Layers to a QML Document} dialog, enter a name
and location for the QML file, and click \uicontrol Export.
\endlist
The QML file is saved to the location that you specified. In \QC, choose
\uicontrol {File > Open File or Project} to open the QML file.
\note Existing files are replaced without warning.
*/

View File

@@ -31,7 +31,7 @@
/*!
\contentspage index.html
\previouspage quick-export-to-qml.html
\previouspage creator-quick-ui-forms.html
\page creator-qml-modules-with-plugins.html
\nextpage quick-converting-ui-projects.html

View File

@@ -25,7 +25,7 @@
/*!
\contentspage index.html
\previouspage qmldesigner-pathview-editor.html
\previouspage quick-states.html
\page qmldesigner-connections.html
\nextpage quick-signals.html

View File

@@ -26,7 +26,7 @@
/*!
\contentspage {Qt Creator}
\if defined(qtdesignstudio)
\previouspage creator-quick-ui-forms.html
\previouspage studio-3d-camera.html
\else
\previouspage creator-building-running.html
\endif

View File

@@ -26,15 +26,13 @@
/*!
\contentspage index.html
\page qmldesigner-pathview-editor.html
\if defined(qtdesignstudio)
\previouspage studio-fonts.html
\nextpage creator-quick-ui-forms.html
\else
\previouspage quick-states.html
\if defined(qtdesignstudio)
\nextpage studio-3d.html
\else
\nextpage qtquick-iso-icon-browser.html
\endif
\title Editing PathView Properties
A \l{PathView}{Path View} lays out data provided by data models on a

View File

@@ -27,7 +27,11 @@
\contentspage {Qt Creator Manual}
\page qtquick-properties.html
\previouspage qtquick-navigator.html
\if defined(qtdesignstudio)
\nextpage studio-fonts.html
\else
\nextpage studio-timeline.html
\endif
\title Specifying Item Properties

View File

@@ -28,11 +28,10 @@
\page quick-states.html
\if defined(qtdesignstudio)
\previouspage quick-property-bindings.html
\nextpage studio-fonts.html
\else
\previouspage quick-connections-backend.html
\nextpage qmldesigner-pathview-editor.html
\endif
\nextpage qmldesigner-pathview-editor.html
\title Adding States

View File

@@ -25,7 +25,11 @@
/*!
\contentspage {Qt Design Studio Manual}
\if defined(qtdesignstudio)
\previouspage studio-adding-dynamics.html
\else
\previouspage qtquick-properties.html
\endif
\page studio-timeline.html
\nextpage qmldesigner-connections.html

View File

@@ -38,7 +38,7 @@
\nextpage creator-live-preview.html
\else
\previouspage qtquick-iso-icon-browser.html
\nextpage quick-export-to-qml.html
\nextpage creator-qml-modules-with-plugins.html
\endif
\title Qt Quick UI Forms

View File

@@ -144,7 +144,7 @@ def kindName(d, value):
e = value.integer()
if e:
kindType = d.lookupType("CPlusPlus::Kind")
return kindType.typeData().enumDisplay(e, value.address())[11:]
return kindType.typeData().enumDisplay(e, value.address(), '%d')[11:]
else:
return ''

View File

@@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "change3dviewcommand.h"
#include <QDebug>
namespace QmlDesigner {
Change3DViewCommand::Change3DViewCommand() = default;
Change3DViewCommand::Change3DViewCommand(const QVector<InformationContainer> &informationVector)
: m_informationVector(informationVector)
{
}
QVector<InformationContainer> Change3DViewCommand::informationVector() const
{
return m_informationVector;
}
QDataStream &operator<<(QDataStream &out, const Change3DViewCommand &command)
{
out << command.informationVector();
return out;
}
QDataStream &operator>>(QDataStream &in, Change3DViewCommand &command)
{
in >> command.m_informationVector;
return in;
}
QDebug operator <<(QDebug debug, const Change3DViewCommand &command)
{
return debug.nospace() << "Change3DViewCommand(auxiliaryChanges: " << command.m_informationVector << ")";
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,57 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QMetaType>
#include <QVector>
#include "informationcontainer.h"
namespace QmlDesigner {
class Change3DViewCommand
{
friend QDataStream &operator>>(QDataStream &in, Change3DViewCommand &command);
friend QDebug operator <<(QDebug debug, const Change3DViewCommand &command);
public:
Change3DViewCommand();
explicit Change3DViewCommand(const QVector<InformationContainer> &auxiliaryChangeVector);
QVector<InformationContainer> informationVector() const;
private:
QVector<InformationContainer> m_informationVector;
};
QDataStream &operator<<(QDataStream &out, const Change3DViewCommand &command);
QDataStream &operator>>(QDataStream &in, Change3DViewCommand &command);
QDebug operator <<(QDebug debug, const Change3DViewCommand &command);
} // namespace QmlDesigner
Q_DECLARE_METATYPE(QmlDesigner::Change3DViewCommand)

View File

@@ -28,6 +28,7 @@ HEADERS += $$PWD/removesharedmemorycommand.h
HEADERS += $$PWD/puppetalivecommand.h
HEADERS += $$PWD/changeselectioncommand.h
HEADERS += $$PWD/drop3dlibraryitemcommand.h
HEADERS += $$PWD/change3dviewcommand.h
SOURCES += $$PWD/synchronizecommand.cpp
SOURCES += $$PWD/debugoutputcommand.cpp
@@ -57,3 +58,4 @@ SOURCES += $$PWD/removesharedmemorycommand.cpp
SOURCES += $$PWD/puppetalivecommand.cpp
SOURCES += $$PWD/changeselectioncommand.cpp
SOURCES += $$PWD/drop3dlibraryitemcommand.cpp
SOURCES += $$PWD/change3dviewcommand.cpp

View File

@@ -41,6 +41,7 @@
#include "instancecontainer.h"
#include "createinstancescommand.h"
#include "createscenecommand.h"
#include "change3dviewcommand.h"
#include "changevaluescommand.h"
#include "changebindingscommand.h"
#include "changeauxiliarycommand.h"
@@ -360,6 +361,11 @@ void NodeInstanceClientProxy::createScene(const CreateSceneCommand &command)
nodeInstanceServer()->createScene(command);
}
void NodeInstanceClientProxy::change3DView(const Change3DViewCommand &command)
{
nodeInstanceServer()->change3DView(command);
}
void NodeInstanceClientProxy::clearScene(const ClearSceneCommand &command)
{
nodeInstanceServer()->clearScene(command);
@@ -447,6 +453,7 @@ void NodeInstanceClientProxy::changeSelection(const ChangeSelectionCommand &comm
void NodeInstanceClientProxy::dispatchCommand(const QVariant &command)
{
static const int createInstancesCommandType = QMetaType::type("CreateInstancesCommand");
static const int change3DViewCommandType = QMetaType::type("Change3DViewCommand");
static const int changeFileUrlCommandType = QMetaType::type("ChangeFileUrlCommand");
static const int createSceneCommandType = QMetaType::type("CreateSceneCommand");
static const int clearSceneCommandType = QMetaType::type("ClearSceneCommand");
@@ -470,6 +477,8 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command)
if (commandType == createInstancesCommandType)
createInstances(command.value<CreateInstancesCommand>());
else if (commandType == change3DViewCommandType)
change3DView(command.value<Change3DViewCommand>());
else if (commandType == changeFileUrlCommandType)
changeFileUrl(command.value<ChangeFileUrlCommand>());
else if (commandType == createSceneCommandType)

View File

@@ -45,6 +45,7 @@ class CreateSceneCommand;
class CreateInstancesCommand;
class ClearSceneCommand;
class ReparentInstancesCommand;
class Change3DViewCommand;
class ChangeFileUrlCommand;
class ChangeValuesCommand;
class ChangeAuxiliaryCommand;
@@ -95,6 +96,7 @@ protected:
void changeFileUrl(const ChangeFileUrlCommand &command);
void createScene(const CreateSceneCommand &command);
void clearScene(const ClearSceneCommand &command);
void change3DView(const Change3DViewCommand &command);
void removeInstances(const RemoveInstancesCommand &command);
void removeProperties(const RemovePropertiesCommand &command);
void changePropertyBindings(const ChangeBindingsCommand &command);

View File

@@ -52,7 +52,11 @@ enum InformationName
HasBindingForProperty,
ContentTransform,
ContentItemTransform,
ContentItemBoundingRect
ContentItemBoundingRect,
MoveView,
ShowView,
ResizeView,
HideView
};
}

View File

@@ -32,6 +32,7 @@
#include "instancecontainer.h"
#include "createinstancescommand.h"
#include "createscenecommand.h"
#include "change3dviewcommand.h"
#include "changevaluescommand.h"
#include "changebindingscommand.h"
#include "changeauxiliarycommand.h"
@@ -90,6 +91,9 @@ void NodeInstanceServerInterface::registerCommands()
qRegisterMetaType<CreateSceneCommand>("CreateSceneCommand");
qRegisterMetaTypeStreamOperators<CreateSceneCommand>("CreateSceneCommand");
qRegisterMetaType<Change3DViewCommand>("Change3DViewCommand");
qRegisterMetaTypeStreamOperators<Change3DViewCommand>("Change3DViewCommand");
qRegisterMetaType<ChangeBindingsCommand>("ChangeBindingsCommand");
qRegisterMetaTypeStreamOperators<ChangeBindingsCommand>("ChangeBindingsCommand");

View File

@@ -33,6 +33,7 @@ class PropertyAbstractContainer;
class PropertyBindingContainer;
class PropertyValueContainer;
class Change3DViewCommand;
class ChangeFileUrlCommand;
class ChangeValuesCommand;
class ChangeBindingsCommand;
@@ -66,6 +67,7 @@ public:
virtual void changeFileUrl(const ChangeFileUrlCommand &command) = 0;
virtual void createScene(const CreateSceneCommand &command) = 0;
virtual void clearScene(const ClearSceneCommand &command) = 0;
virtual void change3DView(const Change3DViewCommand &command) = 0;
virtual void removeInstances(const RemoveInstancesCommand &command) = 0;
virtual void removeProperties(const RemovePropertiesCommand &command) = 0;
virtual void changePropertyBindings(const ChangeBindingsCommand &command) = 0;

View File

@@ -33,15 +33,18 @@ Node {
property View3D view3D
property Node target: parent
property bool autoScale: true
property Camera camera: view3D.camera
// Read-only
property real relativeScale: 1
onSceneTransformChanged: updateScale()
onAutoScaleChanged: updateScale()
// Trigger delayed update on camera change to ensure camera values are correct
onCameraChanged: _generalHelper.requestOverlayUpdate();
Connections {
target: view3D.camera
target: camera
onSceneTransformChanged: updateScale()
}
@@ -66,16 +69,22 @@ Node {
// "anchor" distance. Map the two positions back to the target node, and measure the
// distance between them now, in the 3D scene. The difference of the two distances,
// view and scene, will tell us what the distance independent scale should be.
var posInView1 = view3D.mapFrom3DScene(scenePosition);
// Need to recreate vector as we need to adjust it and we can't do that on reference of
// scenePosition, which is read-only property
var scenePos = Qt.vector3d(scenePosition.x, scenePosition.y, scenePosition.z);
if (orientation === Node.RightHanded)
scenePos.z = -scenePos.z;
var posInView1 = view3D.mapFrom3DScene(scenePos);
var posInView2 = Qt.vector3d(posInView1.x + 100, posInView1.y, posInView1.z);
var rayPos1 = view3D.mapTo3DScene(Qt.vector3d(posInView2.x, posInView2.y, 0));
var rayPos2 = view3D.mapTo3DScene(Qt.vector3d(posInView2.x, posInView2.y, 10));
var planeNormal = view3D.camera.forward;
var rayHitPos = helper.rayIntersectsPlane(rayPos1, rayPos2, scenePosition,
planeNormal);
relativeScale = scenePosition.minus(rayHitPos).length() / 100;
var planeNormal = camera.forward;
var rayHitPos = helper.rayIntersectsPlane(rayPos1, rayPos2, scenePos, planeNormal);
relativeScale = scenePos.minus(rayHitPos).length() / 100;
}
}

View File

@@ -120,8 +120,8 @@ View3D {
onPressed: {
pick(mouse);
if (pickObj) {
axisHelperView.editCameraCtrl.fitObject(axisHelperView.selectedNode,
pickObj.cameraRotation);
axisHelperView.editCameraCtrl.focusObject(axisHelperView.selectedNode,
pickObj.cameraRotation, false);
} else {
mouse.accepted = false;
}

View File

@@ -41,8 +41,7 @@ IconGizmo {
materials: [
DefaultMaterial {
id: defaultMaterial
emissiveColor: cameraGizmo.targetNode === cameraGizmo.selectedNode ? "#FF0000"
: "#555555"
emissiveColor: cameraGizmo.selected ? "#FF0000" : "#555555"
lighting: DefaultMaterial.NoLighting
cullingMode: Material.DisableCulling
}

View File

@@ -29,7 +29,6 @@ import MouseArea3D 1.0
Model {
id: rootModel
rotationOrder: Node.XYZr
property View3D view3D
property alias color: material.emissiveColor
@@ -59,6 +58,8 @@ Model {
var maskedPosition = Qt.vector3d(scenePos.x, 0, 0);
_pointerPosPressed = mouseArea.mapPositionToScene(maskedPosition);
if (targetNode.orientation === Node.RightHanded)
_pointerPosPressed.z = -_pointerPosPressed.z;
var sp = targetNode.scenePosition;
_targetStartPos = Qt.vector3d(sp.x, sp.y, sp.z);
pressed(mouseArea);
@@ -68,9 +69,9 @@ Model {
{
var maskedPosition = Qt.vector3d(scenePos.x, 0, 0);
var scenePointerPos = mouseArea.mapPositionToScene(maskedPosition);
return Qt.vector3d(scenePointerPos.x - _pointerPosPressed.x,
scenePointerPos.y - _pointerPosPressed.y,
scenePointerPos.z - _pointerPosPressed.z);
if (targetNode.orientation === Node.RightHanded)
scenePointerPos.z = -scenePointerPos.z;
return scenePointerPos.minus(_pointerPosPressed);
}
function handleDragged(mouseArea, scenePos)

View File

@@ -45,11 +45,11 @@ Item {
property real _defaultCameraLookAtDistance: 0
property Camera _prevCamera: null
function fitObject(targetObject, rotation)
function focusObject(targetObject, rotation, updateZoom)
{
camera.rotation = rotation;
var newLookAtAndZoom = _generalHelper.fitObjectToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d);
var newLookAtAndZoom = _generalHelper.focusObjectToCamera(
camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, updateZoom);
_lookAtPoint = newLookAtAndZoom.toVector3d();
_zoomFactor = newLookAtAndZoom.w;
}

View File

@@ -24,7 +24,7 @@
****************************************************************************/
import QtQuick 2.12
import QtQuick.Window 2.0
import QtQuick.Window 2.12
import QtQuick3D 1.0
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
@@ -33,36 +33,95 @@ Window {
id: viewWindow
width: 1024
height: 768
visible: true
visible: false
title: "3D"
flags: Qt.WindowStaysOnTopHint | Qt.Window | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
flags: Qt.Widget | Qt.SplashScreen
onActiveChanged: {
if (viewWindow.active)
cameraControl.forceActiveFocus()
}
property alias scene: editView.importScene
property alias showEditLight: btnEditViewLight.toggled
property alias usePerspective: btnPerspective.toggled
property Node selectedNode: null
property Node selectedNode: null // This is non-null only in single selection case
property var selectedNodes: [] // All selected nodes
property var lightGizmos: []
property var cameraGizmos: []
property var selectionBoxes: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
signal objectClicked(var object)
signal selectionChanged(var selectedNodes)
signal commitObjectProperty(var object, var propName)
signal changeObjectProperty(var object, var propName)
function selectObject(object) {
selectedNode = object;
function ensureSelectionBoxes(count) {
var needMore = count - selectionBoxes.length
if (needMore > 0) {
var component = Qt.createComponent("SelectionBox.qml");
if (component.status === Component.Ready) {
for (var i = 0; i < needMore; ++i) {
var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry");
var box = component.createObject(mainSceneHelpers, {"view3D": editView,
"geometryName": geometryName});
selectionBoxes[selectionBoxes.length] = box;
}
}
}
}
function handleObjectClicked(object) {
function selectObjects(objects) {
// Create selection boxes as necessary. One more box than is actually needed is created, so
// that we always have a previously created box to use for new selection.
// This fixes an occasional visual glitch when creating a new box.
ensureSelectionBoxes(objects.length + 1)
var i;
for (i = 0; i < objects.length; ++i)
selectionBoxes[i].targetNode = objects[i];
for (i = objects.length; i < selectionBoxes.length; ++i)
selectionBoxes[i].targetNode = null;
selectedNodes = objects;
if (objects.length === 0 || objects.length > 1)
selectedNode = null;
else
selectedNode = objects[0];
}
function handleObjectClicked(object, multi) {
var theObject = object;
if (btnSelectGroup.selected) {
while (theObject && theObject.parent !== scene)
theObject = theObject.parent;
}
selectObject(theObject);
objectClicked(theObject);
// Object selection logic:
// Regular click: Clear any multiselection, single-selects the clicked object
// Ctrl-click: No objects selected: Act as single select
// One or more objects selected: Multiselect
// Null object always clears entire selection
var newSelection = [];
if (object !== null) {
if (multi && selectedNodes.length > 0) {
var deselect = false;
for (var i = 0; i < selectedNodes.length; ++i) {
// Multiselecting already selected object clears that object from selection
if (selectedNodes[i] !== object)
newSelection[newSelection.length] = selectedNodes[i];
else
deselect = true;
}
if (!deselect)
newSelection[newSelection.length] = object;
} else {
newSelection[0] = theObject;
}
}
selectObjects(newSelection);
selectionChanged(newSelection);
}
function addLightGizmo(obj)
@@ -71,10 +130,10 @@ Window {
if (component.status === Component.Ready) {
var gizmo = component.createObject(overlayScene,
{"view3D": overlayView, "targetNode": obj,
"selectedNode": selectedNode});
"selectedNodes": selectedNodes});
lightGizmos[lightGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNode = Qt.binding(function() {return selectedNode;});
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
}
}
@@ -86,17 +145,20 @@ Window {
var gizmo = component.createObject(
overlayScene,
{"view3D": overlayView, "targetNode": obj, "geometryName": geometryName,
"viewPortRect": viewPortRect, "selectedNode": selectedNode});
"viewPortRect": viewPortRect, "selectedNodes": selectedNodes});
cameraGizmos[cameraGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;});
gizmo.selectedNode = Qt.binding(function() {return selectedNode;});
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
}
}
// Work-around the fact that the projection matrix for the camera is not calculated until
// the first frame is rendered, so any initial calls to mapFrom3DScene() will fail.
Component.onCompleted: _generalHelper.requestOverlayUpdate();
Component.onCompleted: {
selectObjects([]);
_generalHelper.requestOverlayUpdate();
}
onWidthChanged: _generalHelper.requestOverlayUpdate();
onHeightChanged: _generalHelper.requestOverlayUpdate();
@@ -126,8 +188,6 @@ Window {
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
highlightOnHover: true
targetNode: viewWindow.selectedNode
position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition
: Qt.vector3d(0, 0, 0)
globalOrientation: btnLocalGlobal.toggled
visible: selectedNode && btnMove.selected
view3D: overlayView
@@ -141,8 +201,6 @@ Window {
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
highlightOnHover: true
targetNode: viewWindow.selectedNode
position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition
: Qt.vector3d(0, 0, 0)
globalOrientation: false
visible: selectedNode && btnScale.selected
view3D: overlayView
@@ -156,8 +214,6 @@ Window {
scale: autoScale.getScale(Qt.vector3d(7, 7, 7))
highlightOnHover: true
targetNode: viewWindow.selectedNode
position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition
: Qt.vector3d(0, 0, 0)
globalOrientation: btnLocalGlobal.toggled
visible: selectedNode && btnRotate.selected
view3D: overlayView
@@ -170,6 +226,7 @@ Window {
id: autoScale
view3D: overlayView
position: moveGizmo.scenePosition
orientation: moveGizmo.orientation
}
}
@@ -182,11 +239,15 @@ Window {
GradientStop { position: 0.0; color: "#999999" }
}
TapHandler { // check tapping/clicking an object in the scene
onTapped: {
var pickResult = editView.pick(eventPoint.scenePosition.x,
eventPoint.scenePosition.y);
handleObjectClicked(pickResult.objectHit);
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onClicked: {
var pickResult = editView.pick(mouse.x, mouse.y);
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
mouse.modifiers & Qt.ControlModifier);
if (!pickResult.objectHit)
mouse.accepted = false;
}
}
@@ -208,12 +269,6 @@ Window {
step: 50
}
SelectionBox {
id: selectionBox
view3D: editView
targetNode: viewWindow.selectedNode
}
PointLight {
id: editLight
visible: showEditLight
@@ -382,8 +437,9 @@ Window {
onSelectedChanged: {
if (selected) {
var targetNode = viewWindow.selectedNode ? selectionBox.model : null;
cameraControl.fitObject(targetNode, editView.camera.rotation);
var targetNode = viewWindow.selectedNodes.length > 0
? selectionBoxes[0].model : null;
cameraControl.focusObject(targetNode, editView.camera.rotation, true);
}
}
}
@@ -396,7 +452,7 @@ Window {
width: 100
height: width
editCameraCtrl: cameraControl
selectedNode : viewWindow.selectedNode ? selectionBox.model : null
selectedNode : viewWindow.selectedNodes.length ? selectionBoxes[0].model : null
}
Rectangle { // top controls bar

View File

@@ -33,13 +33,20 @@ Node {
property View3D view3D
property bool highlightOnHover: true
property Node targetNode: null
property Node selectedNode: null
property var selectedNodes: null
readonly property bool selected: {
for (var i = 0; i < selectedNodes.length; ++i) {
if (selectedNodes[i] === targetNode)
return true;
}
return false;
}
property alias iconSource: iconImage.source
property alias overlayColor: colorOverlay.color
signal positionCommit()
signal clicked(Node node)
signal clicked(Node node, bool multi)
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
rotation: targetNode ? targetNode.sceneRotation : Qt.vector3d(0, 0, 0)
@@ -60,21 +67,26 @@ Node {
y: -height / 2
color: "transparent"
border.color: "#7777ff"
border.width: iconGizmo.selectedNode !== iconGizmo.targetNode
border.width: !iconGizmo.selected
&& iconGizmo.highlightOnHover && iconMouseArea.containsMouse ? 2 : 0
radius: 5
opacity: iconGizmo.selectedNode === iconGizmo.targetNode ? 0.2 : 1
opacity: iconGizmo.selected ? 0.2 : 1
Image {
id: iconImage
fillMode: Image.Pad
MouseArea {
id: iconMouseArea
anchors.fill: parent
onClicked: iconGizmo.clicked(iconGizmo.targetNode)
hoverEnabled: iconGizmo.highlightOnHover
&& iconGizmo.selectedNode !== iconGizmo.targetNode
acceptedButtons: iconGizmo.selectedNode !== iconGizmo.targetNode
? Qt.LeftButton : Qt.NoButton
onPressed: {
if (iconGizmo.selected && !(mouse.modifiers & Qt.ControlModifier)) {
mouse.accepted = false;
}
}
onClicked: iconGizmo.clicked(iconGizmo.targetNode,
mouse.modifiers & Qt.ControlModifier)
hoverEnabled: iconGizmo.highlightOnHover && !iconGizmo.selected
acceptedButtons: Qt.LeftButton
}
}
ColorOverlay {

View File

@@ -37,12 +37,17 @@ Node {
readonly property bool dragging: arrowX.dragging || arrowY.dragging || arrowZ.dragging
|| planeX.dragging || planeY.dragging || planeZ.dragging
|| centerBall.dragging
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
orientation: targetNode ? targetNode.orientation : Node.LeftHanded
signal positionCommit()
signal positionMove()
Node {
rotation: globalOrientation || !targetNode ? Qt.vector3d(0, 0, 0) : targetNode.sceneRotation
rotation: globalOrientation || !moveGizmo.targetNode ? Qt.vector3d(0, 0, 0)
: moveGizmo.targetNode.sceneRotation
rotationOrder: moveGizmo.targetNode ? moveGizmo.targetNode.rotationOrder : Node.YXZ
orientation: moveGizmo.orientation
Arrow {
id: arrowX

View File

@@ -55,9 +55,13 @@ Item {
function updateOverlay()
{
var scenePos = targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0);
// Need separate variable as scenePos is reference to read-only property
var scenePosZ = scenePos.z
if (targetNode && targetNode.orientation === Node.RightHanded)
scenePosZ = -scenePosZ;
var scenePosWithOffset = Qt.vector3d(scenePos.x + offset.x,
scenePos.y + offset.y,
scenePos.z + offset.z);
scenePosZ + offset.z);
var viewPos = targetView ? targetView.mapFrom3DScene(scenePosWithOffset)
: Qt.vector3d(0, 0, 0);
root.x = viewPos.x;

View File

@@ -63,6 +63,8 @@ Model {
return;
_pointerPosPressed = mouseArea.mapPositionToScene(scenePos);
if (targetNode.orientation === Node.RightHanded)
_pointerPosPressed.z = -_pointerPosPressed.z;
var sp = targetNode.scenePosition;
_targetStartPos = Qt.vector3d(sp.x, sp.y, sp.z);
pressed(mouseArea);
@@ -71,9 +73,9 @@ Model {
function calcRelativeDistance(mouseArea, scenePos)
{
var scenePointerPos = mouseArea.mapPositionToScene(scenePos);
return Qt.vector3d(scenePointerPos.x - _pointerPosPressed.x,
scenePointerPos.y - _pointerPosPressed.y,
scenePointerPos.z - _pointerPosPressed.z);
if (targetNode.orientation === Node.RightHanded)
scenePointerPos.z = -scenePointerPos.z;
return scenePointerPos.minus(_pointerPosPressed);
}
function handleDragged(mouseArea, scenePos)

View File

@@ -39,6 +39,9 @@ Node {
property real currentAngle
property point currentMousePos
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
orientation: targetNode ? targetNode.orientation : Node.LeftHanded
signal rotateCommit()
signal rotateChange()
@@ -69,7 +72,11 @@ Node {
}
Node {
rotation: globalOrientation || !targetNode ? Qt.vector3d(0, 0, 0) : targetNode.sceneRotation
id: rotNode
rotation: globalOrientation || !rotateGizmo.targetNode ? Qt.vector3d(0, 0, 0)
: rotateGizmo.targetNode.sceneRotation
rotationOrder: rotateGizmo.targetNode ? rotateGizmo.targetNode.rotationOrder : Node.YXZ
orientation: rotateGizmo.orientation
RotateRing {
id: rotRingX
@@ -166,7 +173,14 @@ Node {
if (!rotateGizmo.targetNode)
return;
_targetPosOnScreen = view3D.mapFrom3DScene(rotateGizmo.targetNode.scenePosition);
// Need to recreate vector as we need to adjust it and we can't do that on reference of
// scenePosition, which is read-only property
var scenePos = Qt.vector3d(rotateGizmo.targetNode.scenePosition.x,
rotateGizmo.targetNode.scenePosition.y,
rotateGizmo.targetNode.scenePosition.z);
if (rotateGizmo.targetNode && rotateGizmo.targetNode.orientation === Node.RightHanded)
scenePos.z = -scenePos.z
_targetPosOnScreen = view3D.mapFrom3DScene(scenePos);
_targetPosOnScreen.z = 0;
_pointerPosPressed = Qt.vector3d(screenPos.x, screenPos.y, 0);

View File

@@ -77,7 +77,15 @@ Model {
if (!targetNode)
return;
_targetPosOnScreen = view3D.mapFrom3DScene(targetNode.scenePosition);
// Need to recreate vector as we need to adjust it and we can't do that on reference of
// scenePosition, which is read-only property
var scenePos = Qt.vector3d(targetNode.scenePosition.x,
targetNode.scenePosition.y,
targetNode.scenePosition.z);
if (targetNode && targetNode.orientation === Node.RightHanded)
scenePos.z = -scenePos.z
_targetPosOnScreen = view3D.mapFrom3DScene(scenePos);
_targetPosOnScreen.z = 0;
_pointerPosPressed = Qt.vector3d(screenPos.x, screenPos.y, 0);
_trackBall = angle < 0.1;

View File

@@ -38,11 +38,16 @@ Node {
|| planeX.dragging || planeY.dragging || planeZ.dragging
|| centerMouseArea.dragging
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
orientation: targetNode ? targetNode.orientation : Node.LeftHanded
signal scaleCommit()
signal scaleChange()
Node {
rotation: globalOrientation || !targetNode ? Qt.vector3d(0, 0, 0) : targetNode.sceneRotation
rotationOrder: scaleGizmo.targetNode ? scaleGizmo.targetNode.rotationOrder : Node.YXZ
orientation: scaleGizmo.orientation
ScaleRod {
id: scaleRodX

View File

@@ -33,6 +33,7 @@ Node {
property View3D view3D
property Node targetNode: null
property alias model: selectionBoxModel
property alias geometryName: selectionBoxGeometry.name
SelectionBoxGeometry {
id: selectionBoxGeometry

View File

@@ -139,8 +139,9 @@ float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float de
}
// Return value contains new lookAt point (xyz) and zoom factor (w)
QVector4D GeneralHelper::fitObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort)
QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom)
{
if (!camera)
return QVector4D(0.f, 0.f, 0.f, 1.f);
@@ -148,7 +149,8 @@ QVector4D GeneralHelper::fitObjectToCamera(QQuick3DCamera *camera, float default
QVector3D lookAt = targetObject ? targetObject->scenePosition() : QVector3D();
// Get object bounds
qreal maxExtent = 200.;
const qreal defaultExtent = 200.;
qreal maxExtent = defaultExtent;
if (auto modelNode = qobject_cast<QQuick3DModel *>(targetObject)) {
auto targetPriv = QQuick3DObjectPrivate::get(targetObject);
if (auto renderModel = static_cast<QSSGRenderModel *>(targetPriv->spatialNode)) {
@@ -172,6 +174,9 @@ QVector4D GeneralHelper::fitObjectToCamera(QQuick3DCamera *camera, float default
maxExtent = qSqrt(qreal(e.x() * e.x() + e.y() * e.y() + e.z() * e.z()));
maxExtent *= maxScale;
if (maxExtent < 0.0001)
maxExtent = defaultExtent;
// Adjust lookAt to look directly at the center of the object bounds
lookAt = renderModel->globalTransform.map(center);
lookAt.setZ(-lookAt.z()); // Render node transforms have inverted z
@@ -189,11 +194,10 @@ QVector4D GeneralHelper::fitObjectToCamera(QQuick3DCamera *camera, float default
camera->setPosition(lookAt + newLookVector);
// Emprically determined algorithm for nice zoom
float newZoomFactor = qBound(.0001f, float(maxExtent / 700.), 10000.f);
float newZoomFactor = updateZoom ? qBound(.0001f, float(maxExtent / 700.), 10000.f) : oldZoom;
float cameraZoomFactor = zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false);
return QVector4D(lookAt,
zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false));
return QVector4D(lookAt, cameraZoomFactor);
}
void GeneralHelper::delayedPropertySet(QObject *obj, int delay, const QString &property,
@@ -204,6 +208,20 @@ void GeneralHelper::delayedPropertySet(QObject *obj, int delay, const QString &p
});
}
QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *pickNode)
{
if (pickNode) {
// Check if the picked node actually specifies another node as the pick target
QVariant componentVar = pickNode->property("_pickTarget");
if (componentVar.isValid()) {
auto componentNode = componentVar.value<QQuick3DNode *>();
if (componentNode)
return componentNode;
}
}
return pickNode;
}
}
}

View File

@@ -62,10 +62,13 @@ public:
Q_INVOKABLE float zoomCamera(QQuick3DCamera *camera, float distance,
float defaultLookAtDistance, const QVector3D &lookAt,
float zoomFactor, bool relative);
Q_INVOKABLE QVector4D fitObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort);
Q_INVOKABLE QVector4D focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance,
QQuick3DNode *targetObject, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom = true);
Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property,
const QVariant& value);
Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode);
signals:
void overlayUpdateNeeded();

View File

@@ -322,11 +322,11 @@ QVector3D MouseArea3D::getNewScale(QQuick3DNode *node, const QVector3D &startSca
scaleVec *= magnitude;
// Zero axes on scale vector indicate directions we don't want scaling to affect
if (qFuzzyIsNull(scaleVec.x()))
if (scaleDirVector.x() < 0.0001f)
scaleVec.setX(1.f);
if (qFuzzyIsNull(scaleVec.y()))
if (scaleDirVector.y() < 0.0001f)
scaleVec.setY(1.f);
if (qFuzzyIsNull(scaleVec.z()))
if (scaleDirVector.z() < 0.0001f)
scaleVec.setZ(1.f);
scaleVec *= startScale;
@@ -344,11 +344,17 @@ qreal QmlDesigner::Internal::MouseArea3D::getNewRotationAngle(
QQuick3DNode *node, const QVector3D &pressPos, const QVector3D &currentPos,
const QVector3D &nodePos, qreal prevAngle, bool trackBall)
{
const QVector3D cameraToNodeDir = getCameraToNodeDir(node);
// Get camera to node direction in node orientation
QVector3D cameraToNodeDir = getCameraToNodeDir(node);
if (trackBall) {
// Only the distance in plane direction is relevant in trackball drag
QVector3D dragDir = QVector3D::crossProduct(getNormal(), cameraToNodeDir).normalized();
QVector3D screenDragDir = m_view3D->mapFrom3DScene(node->scenePosition() + dragDir);
QVector3D scenePos = node->scenePosition();
if (node->orientation() == QQuick3DNode::RightHanded) {
scenePos.setZ(-scenePos.z());
dragDir = -dragDir;
}
QVector3D screenDragDir = m_view3D->mapFrom3DScene(scenePos + dragDir);
screenDragDir.setZ(0);
dragDir = (screenDragDir - nodePos).normalized();
const QVector3D pressToCurrent = (currentPos - pressPos);
@@ -361,7 +367,9 @@ qreal QmlDesigner::Internal::MouseArea3D::getNewRotationAngle(
qreal angle = qAcos(qreal(QVector3D::dotProduct(nodeToPress, nodeToCurrent)));
// Determine drag direction left/right
const QVector3D dragNormal = QVector3D::crossProduct(nodeToPress, nodeToCurrent).normalized();
QVector3D dragNormal = QVector3D::crossProduct(nodeToPress, nodeToCurrent).normalized();
if (node->orientation() == QQuick3DNode::RightHanded)
dragNormal = -dragNormal;
angle *= QVector3D::dotProduct(QVector3D(0.f, 0.f, 1.f), dragNormal) < 0 ? -1.0 : 1.0;
// Determine drag ring orientation relative to camera
@@ -392,7 +400,10 @@ void QmlDesigner::Internal::MouseArea3D::applyRotationAngleToNode(
{
if (!qFuzzyIsNull(angle)) {
node->setRotation(startRotation);
node->rotate(qRadiansToDegrees(angle), getNormal(), QQuick3DNode::SceneSpace);
QVector3D normal = getNormal();
if (orientation() != node->orientation())
normal.setZ(-normal.z());
node->rotate(qRadiansToDegrees(angle), normal, QQuick3DNode::SceneSpace);
}
}
@@ -407,6 +418,10 @@ void MouseArea3D::applyFreeRotation(QQuick3DNode *node, const QVector3D &startRo
const float *dataPtr(sceneTransform().data());
QVector3D xAxis = QVector3D(-dataPtr[0], -dataPtr[1], -dataPtr[2]).normalized();
QVector3D yAxis = QVector3D(-dataPtr[4], -dataPtr[5], -dataPtr[6]).normalized();
if (node->orientation() == QQuick3DNode::RightHanded) {
xAxis = QVector3D(-xAxis.x(), -xAxis.y(), xAxis.z());
yAxis = QVector3D(-yAxis.x(), -yAxis.y(), yAxis.z());
}
QVector3D finalAxis = (dragVector.x() * yAxis + dragVector.y() * xAxis);
@@ -596,11 +611,14 @@ QVector3D MouseArea3D::getCameraToNodeDir(QQuick3DNode *node) const
{
QVector3D dir;
if (qobject_cast<QQuick3DOrthographicCamera *>(m_view3D->camera())) {
dir = m_view3D->camera()->cameraNode()->getDirection();
// Camera direction has x and y flipped
dir = QVector3D(-dir.x(), -dir.y(), dir.z());
dir = -m_view3D->camera()->cameraNode()->getDirection();
dir.setZ(-dir.z());
} else {
dir = (node->scenePosition() - m_view3D->camera()->scenePosition()).normalized();
QVector3D camPos = m_view3D->camera()->scenePosition();
QVector3D nodePos = node->scenePosition();
if (node->orientation() == QQuick3DNode::RightHanded)
nodePos.setZ(-nodePos.z());
dir = (node->scenePosition() - camPos).normalized();
}
return dir;
}

View File

@@ -41,6 +41,11 @@
namespace QmlDesigner {
namespace Internal {
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
static const QVector3D maxVec = QVector3D(floatMax, floatMax, floatMax);
static const QVector3D minVec = QVector3D(floatMin, floatMin, floatMin);
SelectionBoxGeometry::SelectionBoxGeometry()
: QQuick3DGeometry()
{
@@ -133,11 +138,8 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
QByteArray vertexData;
QByteArray indexData;
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
QVector3D minBounds = QVector3D(floatMax, floatMax, floatMax);
QVector3D maxBounds = QVector3D(floatMin, floatMin, floatMin);
QVector3D minBounds = maxVec;
QVector3D maxBounds = minVec;
if (m_targetNode) {
auto rootPriv = QQuick3DObjectPrivate::get(m_rootNode);
@@ -154,10 +156,20 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty);
rootRN->calculateGlobalVariables();
}
getBounds(m_targetNode, vertexData, indexData, minBounds, maxBounds, QMatrix4x4());
getBounds(m_targetNode, vertexData, indexData, minBounds, maxBounds);
appendVertexData(QMatrix4x4(), vertexData, indexData, minBounds, maxBounds);
// Track changes in ancestors, as they can move node without affecting node properties
auto parentNode = m_targetNode->parentNode();
while (parentNode) {
trackNodeChanges(parentNode);
parentNode = parentNode->parentNode();
}
} else {
// Fill some dummy data so geometry won't get rejected
appendVertexData(vertexData, indexData, minBounds, maxBounds);
minBounds = {};
maxBounds = {};
appendVertexData(QMatrix4x4(), vertexData, indexData, minBounds, maxBounds);
}
geometry->addAttribute(QSSGRenderGeometry::Attribute::PositionSemantic, 0,
@@ -182,62 +194,73 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
return node;
}
void SelectionBoxGeometry::getBounds(QQuick3DNode *node, QByteArray &vertexData,
QByteArray &indexData, QVector3D &minBounds,
QVector3D &maxBounds, const QMatrix4x4 &transform)
void SelectionBoxGeometry::getBounds(
QQuick3DNode *node, QByteArray &vertexData, QByteArray &indexData,
QVector3D &minBounds, QVector3D &maxBounds)
{
QMatrix4x4 fullTransform;
QMatrix4x4 localTransform;
auto nodePriv = QQuick3DObjectPrivate::get(node);
auto renderNode = static_cast<QSSGRenderNode *>(nodePriv->spatialNode);
// All transforms are relative to targetNode transform, so its local transform is ignored
if (node != m_targetNode) {
if (renderNode) {
if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty))
renderNode->calculateLocalTransform();
fullTransform = transform * renderNode->localTransform;
localTransform = renderNode->localTransform;
}
m_connections << QObject::connect(node, &QQuick3DNode::scaleChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::rotationChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::positionChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::pivotChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::orientationChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::rotationOrderChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
trackNodeChanges(node);
}
QVector3D localMinBounds = maxVec;
QVector3D localMaxBounds = minVec;
// Find bounds for children
QVector<QVector3D> minBoundsVec;
QVector<QVector3D> maxBoundsVec;
// Check for children
const auto children = node->childItems();
for (const auto child : children) {
if (auto childNode = qobject_cast<QQuick3DNode *>(child)) {
QVector3D newMinBounds = minBounds;
QVector3D newMaxBounds = maxBounds;
getBounds(childNode, vertexData, indexData, newMinBounds, newMaxBounds, fullTransform);
getBounds(childNode, vertexData, indexData, newMinBounds, newMaxBounds);
minBoundsVec << newMinBounds;
maxBoundsVec << newMaxBounds;
}
}
auto combineMinBounds = [](QVector3D &target, const QVector3D &source) {
target.setX(qMin(source.x(), target.x()));
target.setY(qMin(source.y(), target.y()));
target.setZ(qMin(source.z(), target.z()));
};
auto combineMaxBounds = [](QVector3D &target, const QVector3D &source) {
target.setX(qMax(source.x(), target.x()));
target.setY(qMax(source.y(), target.y()));
target.setZ(qMax(source.z(), target.z()));
};
auto transformCorner = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget,
const QVector3D &corner) {
QVector3D mappedCorner = m.map(corner);
combineMinBounds(minTarget, mappedCorner);
combineMaxBounds(maxTarget, mappedCorner);
};
auto transformCorners = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget,
const QVector3D &minCorner, const QVector3D &maxCorner) {
transformCorner(m, minTarget, maxTarget, minCorner);
transformCorner(m, minTarget, maxTarget, maxCorner);
transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), minCorner.y(), maxCorner.z()));
transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), minCorner.z()));
transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), minCorner.z()));
transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), maxCorner.z()));
transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), maxCorner.y(), minCorner.z()));
transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), maxCorner.z()));
};
// Combine all child bounds
for (const auto &newBounds : qAsConst(minBoundsVec)) {
minBounds.setX(qMin(newBounds.x(), minBounds.x()));
minBounds.setY(qMin(newBounds.y(), minBounds.y()));
minBounds.setZ(qMin(newBounds.z(), minBounds.z()));
}
for (const auto &newBounds : qAsConst(maxBoundsVec)) {
maxBounds.setX(qMax(newBounds.x(), maxBounds.x()));
maxBounds.setY(qMax(newBounds.y(), maxBounds.y()));
maxBounds.setZ(qMax(newBounds.z(), maxBounds.z()));
}
for (const auto &newBounds : qAsConst(minBoundsVec))
combineMinBounds(localMinBounds, newBounds);
for (const auto &newBounds : qAsConst(maxBoundsVec))
combineMaxBounds(localMaxBounds, newBounds);
if (auto modelNode = qobject_cast<QQuick3DModel *>(node)) {
if (auto renderModel = static_cast<QSSGRenderModel *>(renderNode)) {
@@ -253,47 +276,38 @@ void SelectionBoxGeometry::getBounds(QQuick3DNode *node, QByteArray &vertexData,
QVector3D localMin = center - extents;
QVector3D localMax = center + extents;
// Transform all corners of the local bounding box to find final extent in
// in parent space
auto checkCorner = [&minBounds, &maxBounds, &fullTransform]
(const QVector3D &corner) {
QVector3D mappedCorner = fullTransform.map(corner);
minBounds.setX(qMin(mappedCorner.x(), minBounds.x()));
minBounds.setY(qMin(mappedCorner.y(), minBounds.y()));
minBounds.setZ(qMin(mappedCorner.z(), minBounds.z()));
maxBounds.setX(qMax(mappedCorner.x(), maxBounds.x()));
maxBounds.setY(qMax(mappedCorner.y(), maxBounds.y()));
maxBounds.setZ(qMax(mappedCorner.z(), maxBounds.z()));
};
checkCorner(localMin);
checkCorner(localMax);
checkCorner(QVector3D(localMin.x(), localMin.y(), localMax.z()));
checkCorner(QVector3D(localMin.x(), localMax.y(), localMin.z()));
checkCorner(QVector3D(localMax.x(), localMin.y(), localMin.z()));
checkCorner(QVector3D(localMin.x(), localMax.y(), localMax.z()));
checkCorner(QVector3D(localMax.x(), localMax.y(), localMin.z()));
checkCorner(QVector3D(localMax.x(), localMin.y(), localMax.z()));
combineMinBounds(localMinBounds, localMin);
combineMaxBounds(localMaxBounds, localMax);
}
}
}
} else {
combineMinBounds(localMinBounds, {});
combineMaxBounds(localMaxBounds, {});
}
// Target node and immediate children get selection boxes
if (transform.isIdentity()) {
// Adjust bounds to reduce targetNode pixels obscuring the selection box
QVector3D extents = (maxBounds - minBounds) / 1000.f;
QVector3D minAdjBounds = minBounds - extents;
QVector3D maxAdjBounds = maxBounds + extents;
appendVertexData(vertexData, indexData, minAdjBounds, maxAdjBounds);
if (localMaxBounds == minVec) {
localMinBounds = {};
localMaxBounds = {};
}
// Transform local space bounding box to parent space
transformCorners(localTransform, minBounds, maxBounds, localMinBounds, localMaxBounds);
// Immediate child boxes
if (node->parentNode() == m_targetNode)
appendVertexData(localTransform, vertexData, indexData, localMinBounds, localMaxBounds);
}
void SelectionBoxGeometry::appendVertexData(QByteArray &vertexData, QByteArray &indexData,
const QVector3D &minBounds, const QVector3D &maxBounds)
void SelectionBoxGeometry::appendVertexData(const QMatrix4x4 &m, QByteArray &vertexData,
QByteArray &indexData, const QVector3D &minBounds,
const QVector3D &maxBounds)
{
// Adjust bounds to reduce targetNode pixels obscuring the selection box
QVector3D extents = (maxBounds - minBounds) / 1000.f;
QVector3D minAdjBounds = minBounds - extents;
QVector3D maxAdjBounds = maxBounds + extents;
int initialVertexSize = vertexData.size();
int initialIndexSize = indexData.size();
const int vertexSize = int(sizeof(float)) * 8 * 3; // 8 vertices, 3 floats/vert
@@ -305,14 +319,20 @@ void SelectionBoxGeometry::appendVertexData(QByteArray &vertexData, QByteArray &
auto dataPtr = reinterpret_cast<float *>(vertexData.data() + initialVertexSize);
auto indexPtr = reinterpret_cast<quint16 *>(indexData.data() + initialIndexSize);
*dataPtr++ = maxBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = maxBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = maxBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = minBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z();
*dataPtr++ = maxBounds.x(); *dataPtr++ = minBounds.y(); *dataPtr++ = minBounds.z();
QVector3D corners[8];
corners[0] = QVector3D(maxAdjBounds.x(), maxAdjBounds.y(), maxAdjBounds.z());
corners[1] = QVector3D(minAdjBounds.x(), maxAdjBounds.y(), maxAdjBounds.z());
corners[2] = QVector3D(minAdjBounds.x(), minAdjBounds.y(), maxAdjBounds.z());
corners[3] = QVector3D(maxAdjBounds.x(), minAdjBounds.y(), maxAdjBounds.z());
corners[4] = QVector3D(maxAdjBounds.x(), maxAdjBounds.y(), minAdjBounds.z());
corners[5] = QVector3D(minAdjBounds.x(), maxAdjBounds.y(), minAdjBounds.z());
corners[6] = QVector3D(minAdjBounds.x(), minAdjBounds.y(), minAdjBounds.z());
corners[7] = QVector3D(maxAdjBounds.x(), minAdjBounds.y(), minAdjBounds.z());
for (int i = 0; i < 8; ++i) {
corners[i] = m.map(corners[i]);
*dataPtr++ = corners[i].x(); *dataPtr++ = corners[i].y(); *dataPtr++ = corners[i].z();
}
*indexPtr++ = 0 + indexAdd; *indexPtr++ = 1 + indexAdd;
*indexPtr++ = 1 + indexAdd; *indexPtr++ = 2 + indexAdd;
@@ -330,6 +350,22 @@ void SelectionBoxGeometry::appendVertexData(QByteArray &vertexData, QByteArray &
*indexPtr++ = 7 + indexAdd; *indexPtr++ = 4 + indexAdd;
}
void SelectionBoxGeometry::trackNodeChanges(QQuick3DNode *node)
{
m_connections << QObject::connect(node, &QQuick3DNode::sceneScaleChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::sceneRotationChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::scenePositionChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::pivotChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::orientationChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
m_connections << QObject::connect(node, &QQuick3DNode::rotationOrderChanged,
this, &SelectionBoxGeometry::update, Qt::QueuedConnection);
}
}
}

View File

@@ -69,10 +69,11 @@ protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
private:
void getBounds(QQuick3DNode *node, QByteArray &vertexData, QByteArray &indexData,
QVector3D &minBounds, QVector3D &maxBounds, const QMatrix4x4 &transform);
void appendVertexData(QByteArray &vertexData, QByteArray &indexData,
void getBounds(QQuick3DNode *node, QByteArray &vertexData,
QByteArray &indexData, QVector3D &minBounds, QVector3D &maxBounds);
void appendVertexData(const QMatrix4x4 &m, QByteArray &vertexData, QByteArray &indexData,
const QVector3D &minBounds, const QVector3D &maxBounds);
void trackNodeChanges(QQuick3DNode *node);
QQuick3DNode *m_targetNode = nullptr;
QQuick3DViewport *m_view3D = nullptr;

View File

@@ -332,6 +332,10 @@ void NodeInstanceServer::clearScene(const ClearSceneCommand &/*command*/)
m_fileUrl.clear();
}
void NodeInstanceServer::change3DView(const Change3DViewCommand &/*command*/)
{
}
void NodeInstanceServer::changeSelection(const ChangeSelectionCommand & /*command*/)
{
}

View File

@@ -101,6 +101,7 @@ public:
void changeIds(const ChangeIdsCommand &command) override;
void createScene(const CreateSceneCommand &command) override;
void clearScene(const ClearSceneCommand &command) override;
void change3DView(const Change3DViewCommand &command) override;
void removeInstances(const RemoveInstancesCommand &command) override;
void removeProperties(const RemovePropertiesCommand &command) override;
void reparentInstances(const ReparentInstancesCommand &command) override;

View File

@@ -40,6 +40,7 @@
#include "changefileurlcommand.h"
#include "clearscenecommand.h"
#include "reparentinstancescommand.h"
#include "change3dviewcommand.h"
#include "changevaluescommand.h"
#include "changebindingscommand.h"
#include "changeidscommand.h"
@@ -57,7 +58,6 @@
#include "createscenecommand.h"
#include "tokencommand.h"
#include "removesharedmemorycommand.h"
#include "changeselectioncommand.h"
#include "objectnodeinstance.h"
#include <drop3dlibraryitemcommand.h>
@@ -124,13 +124,18 @@ QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
}
window->installEventFilter(this);
QObject::connect(window, SIGNAL(objectClicked(QVariant)), this, SLOT(objectClicked(QVariant)));
QObject::connect(window, SIGNAL(selectionChanged(QVariant)),
this, SLOT(handleSelectionChanged(QVariant)));
QObject::connect(window, SIGNAL(commitObjectProperty(QVariant, QVariant)),
this, SLOT(handleObjectPropertyCommit(QVariant, QVariant)));
QObject::connect(window, SIGNAL(changeObjectProperty(QVariant, QVariant)),
this, SLOT(handleObjectPropertyChange(QVariant, QVariant)));
QObject::connect(window, SIGNAL(activeChanged()),
this, SLOT(handleActiveChanged()));
QObject::connect(&m_propertyChangeTimer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout);
QObject::connect(&m_selectionChangeTimer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout);
//For macOS we have to use the 4.1 core profile
QSurfaceFormat surfaceFormat = window->requestedFormat();
@@ -145,14 +150,21 @@ QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
return window;
}
// an object is clicked in the 3D edit view. Null object indicates selection clearing.
void Qt5InformationNodeInstanceServer::objectClicked(const QVariant &object)
// The selection has changed in the 3D edit view. Empty list indicates selection is cleared.
void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &objs)
{
auto obj = object.value<QObject *>();
ServerNodeInstance instance;
if (obj)
instance = instanceForObject(obj);
selectInstance(instance);
QList<ServerNodeInstance> instanceList;
const QVariantList varObjs = objs.value<QVariantList>();
for (const auto &object : varObjs) {
auto obj = object.value<QObject *>();
if (obj) {
ServerNodeInstance instance = instanceForObject(obj);
instanceList << instance;
}
}
selectInstances(instanceList);
// Hold selection changes reflected back from designer for a bit
m_selectionChangeTimer.start(500);
}
QVector<Qt5InformationNodeInstanceServer::InstancePropertyValueTriple>
@@ -219,6 +231,65 @@ void Qt5InformationNodeInstanceServer::modifyVariantValue(
}
}
void Qt5InformationNodeInstanceServer::showEditView(const QPoint &pos, const QSize &size)
{
m_blockViewActivate = false;
auto window = qobject_cast<QWindow *>(m_editView3D);
if (window) {
activateEditView();
window->setPosition(pos);
window->resize(size);
}
}
void Qt5InformationNodeInstanceServer::hideEditView()
{
m_blockViewActivate = true;
auto window = qobject_cast<QWindow *>(m_editView3D);
if (window)
window->hide();
}
void Qt5InformationNodeInstanceServer::activateEditView()
{
auto window = qobject_cast<QWindow *>(m_editView3D);
if (window) {
Qt::WindowFlags flags = window->flags();
#ifdef Q_OS_MACOS
window->setFlags(Qt::Popup);
window->show();
window->setFlags(flags);
#else
window->raise();
window->setFlags(flags | Qt::WindowStaysOnTopHint);
window->show();
window->requestActivate();
window->raise();
window->setFlags(flags);
#endif
}
}
void Qt5InformationNodeInstanceServer::moveEditView(const QPoint &pos)
{
auto window = qobject_cast<QWindow*>(m_editView3D);
if (window) {
activateEditView();
window->setPosition(pos);
}
}
void Qt5InformationNodeInstanceServer::resizeEditView(const QSize &size)
{
auto window = qobject_cast<QWindow *>(m_editView3D);
if (window) {
activateEditView();
window->resize(size);
}
}
void Qt5InformationNodeInstanceServer::handleObjectPropertyCommit(const QVariant &object,
const QVariant &propName)
{
@@ -253,10 +324,19 @@ void Qt5InformationNodeInstanceServer::updateViewPortRect()
viewPortProperty.write(viewPortrect);
}
void Qt5InformationNodeInstanceServer::handleActiveChanged()
{
if (m_blockViewActivate)
return;
activateEditView();
}
Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
Qt5NodeInstanceServer(nodeInstanceClient)
{
m_propertyChangeTimer.setInterval(100);
m_selectionChangeTimer.setSingleShot(true);
}
void Qt5InformationNodeInstanceServer::sendTokenBack()
@@ -315,9 +395,9 @@ bool Qt5InformationNodeInstanceServer::isDirtyRecursiveForParentInstances(QQuick
}
/* This method allows changing the selection from the puppet */
void Qt5InformationNodeInstanceServer::selectInstance(const ServerNodeInstance &instance)
void Qt5InformationNodeInstanceServer::selectInstances(const QList<ServerNodeInstance> &instanceList)
{
nodeInstanceClient()->selectionChanged(createChangeSelectionCommand({instance}));
nodeInstanceClient()->selectionChanged(createChangeSelectionCommand(instanceList));
}
/* This method allows changing property values from the puppet
@@ -335,6 +415,11 @@ void Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout()
ValuesModifiedCommand::TransactionOption::None);
}
void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout()
{
changeSelection(m_pendingSelectionChangeCommand);
}
QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport(
const QList<ServerNodeInstance> &instanceList) const
{
@@ -389,26 +474,25 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
{
ServerNodeInstance root = rootNodeInstance();
QObject *node = nullptr;
bool showCustomLight = false;
if (root.isSubclassOf("QQuick3DNode")) {
node = root.internalObject();
m_rootNode = root.internalObject();
showCustomLight = true; // Pure node scene we should add a custom light
} else {
node = findRootNodeOf3DViewport(instanceList);
m_rootNode = findRootNodeOf3DViewport(instanceList);
}
if (node) { // If we found a scene we create the edit view
if (m_rootNode) { // If we found a scene we create the edit view
m_editView3D = createEditView3D(engine());
if (!m_editView3D)
return;
QQmlProperty sceneProperty(m_editView3D, "scene", context());
node->setParent(m_editView3D);
sceneProperty.write(objectToVariant(node));
QQmlProperty parentProperty(node, "parent", context());
m_rootNode->setParent(m_editView3D);
sceneProperty.write(objectToVariant(m_rootNode));
QQmlProperty parentProperty(m_rootNode, "parent", context());
parentProperty.write(objectToVariant(m_editView3D));
QQmlProperty showLightProperty(m_editView3D, "showLight", context());
showLightProperty.write(showCustomLight);
@@ -610,18 +694,41 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
if (!m_editView3D)
return;
if (m_selectionChangeTimer.isActive()) {
// If selection was recently changed by puppet, hold updating the selection for a bit to
// avoid selection flicker, especially in multiselect cases.
m_pendingSelectionChangeCommand = command;
// Add additional time in case more commands are still coming through
m_selectionChangeTimer.start(500);
return;
}
const QVector<qint32> instanceIds = command.instanceIds();
QVariantList selectedObjs;
for (qint32 id : instanceIds) {
if (hasInstanceForId(id)) {
ServerNodeInstance instance = instanceForId(id);
QObject *object = nullptr;
if (instance.isSubclassOf("QQuick3DNode"))
object = instance.internalObject();
QMetaObject::invokeMethod(m_editView3D, "selectObject", Q_ARG(QVariant,
objectToVariant(object)));
return; // TODO: support multi-selection
if (object && object != m_rootNode)
selectedObjs << objectToVariant(object);
}
}
// Ensure the UI has enough selection box items. If it doesn't yet have them, which can be the
// case when the first selection processed is a multiselection, we wait a bit as
// using the new boxes immediately leads to visual glitches.
int boxCount = m_editView3D->property("selectionBoxes").value<QVariantList>().size();
if (boxCount < selectedObjs.size()) {
QMetaObject::invokeMethod(m_editView3D, "ensureSelectionBoxes",
Q_ARG(QVariant, QVariant::fromValue(selectedObjs.size())));
m_pendingSelectionChangeCommand = command;
m_selectionChangeTimer.start(100);
} else {
QMetaObject::invokeMethod(m_editView3D, "selectObjects",
Q_ARG(QVariant, QVariant::fromValue(selectedObjs)));
}
}
void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command)
@@ -641,4 +748,18 @@ void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCo
startRenderTimer();
}
void Qt5InformationNodeInstanceServer::change3DView(const Change3DViewCommand &command)
{
for (const InformationContainer &container : command.informationVector()) {
if (container.name() == InformationName::ShowView)
showEditView(container.information().toPoint(), container.secondInformation().toSize());
else if (container.name() == InformationName::HideView)
hideEditView();
else if (container.name() == InformationName::MoveView)
moveEditView(container.information().toPoint());
else if (container.name() == InformationName::ResizeView)
resizeEditView(container.secondInformation().toSize());
}
}
} // namespace QmlDesigner

View File

@@ -28,6 +28,7 @@
#include "qt5nodeinstanceserver.h"
#include "tokencommand.h"
#include "valueschangedcommand.h"
#include "changeselectioncommand.h"
#include <QTimer>
#include <QVariant>
@@ -42,6 +43,7 @@ public:
void reparentInstances(const ReparentInstancesCommand &command) override;
void clearScene(const ClearSceneCommand &command) override;
void change3DView(const Change3DViewCommand &command) override;
void createScene(const CreateSceneCommand &command) override;
void completeComponent(const CompleteComponentCommand &command) override;
void token(const TokenCommand &command) override;
@@ -50,10 +52,11 @@ public:
void changePropertyValues(const ChangeValuesCommand &command) override;
private slots:
void objectClicked(const QVariant &object);
void handleSelectionChanged(const QVariant &objs);
void handleObjectPropertyCommit(const QVariant &object, const QVariant &propName);
void handleObjectPropertyChange(const QVariant &object, const QVariant &propName);
void updateViewPortRect();
void handleActiveChanged();
protected:
void collectItemChangesAndSendChangeCommands() override;
@@ -62,11 +65,12 @@ protected:
void sendTokenBack();
bool isDirtyRecursiveForNonInstanceItems(QQuickItem *item) const;
bool isDirtyRecursiveForParentInstances(QQuickItem *item) const;
void selectInstance(const ServerNodeInstance &instance);
void selectInstances(const QList<ServerNodeInstance> &instanceList);
void modifyProperties(const QVector<InstancePropertyValueTriple> &properties);
private:
void handleObjectPropertyChangeTimeout();
void handleSelectionChangeTimeout();
QObject *createEditView3D(QQmlEngine *engine);
void setup3DEditView(const QList<ServerNodeInstance> &instanceList);
QObject *findRootNodeOf3DViewport(const QList<ServerNodeInstance> &instanceList) const;
@@ -80,14 +84,24 @@ private:
const PropertyName &propertyName,
ValuesModifiedCommand::TransactionOption option);
void showEditView(const QPoint &pos, const QSize &size);
void hideEditView();
void activateEditView();
void moveEditView(const QPoint &pos);
void resizeEditView(const QSize &size);
QObject *m_editView3D = nullptr;
QSet<ServerNodeInstance> m_parentChangedSet;
QList<ServerNodeInstance> m_completedComponentList;
QList<TokenCommand> m_tokenList;
QTimer m_propertyChangeTimer;
QTimer m_selectionChangeTimer;
QVariant m_changedNode;
PropertyName m_changedProperty;
ServerNodeInstance m_viewPortInstance;
bool m_blockViewActivate = false;
QObject *m_rootNode = nullptr;
ChangeSelectionCommand m_pendingSelectionChangeCommand;
};
} // namespace QmlDesigner

View File

@@ -37,6 +37,7 @@
#ifdef QUICK3D_MODULE
#include <private/qquick3dnode_p.h>
#include <private/qquick3dmodel_p.h>
#include <private/qquick3dnode_p_p.h>
#endif
@@ -73,7 +74,7 @@ QQuick3DNode *Quick3DNodeInstance::quick3DNode() const
#endif
}
void Quick3DNodeInstance::setPickable(bool enable, bool checkParent, bool applyToChildren)
void Quick3DNodeInstance::setPickable(bool enable, bool checkParent, bool applyToChildInstances)
{
#ifdef QUICK3D_MODULE
auto node = quick3DNode();
@@ -90,22 +91,42 @@ void Quick3DNodeInstance::setPickable(bool enable, bool checkParent, bool applyT
}
if (!parentHidden) {
if (applyToChildren) {
auto getQuick3DInstance = [this](QQuick3DObject *obj) -> Quick3DNodeInstance * {
if (nodeInstanceServer()->hasInstanceForObject(obj)) {
ServerNodeInstance instance = nodeInstanceServer()->instanceForObject(obj);
if (instance.isValid() && qobject_cast<QQuick3DNode *>(instance.internalObject()))
return static_cast<Quick3DNodeInstance *>(instance.internalInstance().data());
}
return nullptr;
};
const auto childItems = node->childItems();
for (auto childItem : childItems) {
if (auto quick3dInstance = getQuick3DInstance(childItem)) {
auto getQuick3DInstance = [this](QQuick3DObject *obj) -> Quick3DNodeInstance * {
if (nodeInstanceServer()->hasInstanceForObject(obj)) {
ServerNodeInstance instance = nodeInstanceServer()->instanceForObject(obj);
if (instance.isValid() && qobject_cast<QQuick3DNode *>(instance.internalObject()))
return static_cast<Quick3DNodeInstance *>(instance.internalInstance().data());
}
return nullptr;
};
const auto childItems = node->childItems();
for (auto childItem : childItems) {
if (auto quick3dInstance = getQuick3DInstance(childItem)) {
if (applyToChildInstances) {
// Don't override explicit block in children
if (!QQuick3DNodePrivate::get(quick3dInstance->quick3DNode())->m_isHiddenInEditor)
quick3dInstance->setPickable(enable, false, true);
}
} else {
// Children of components do not have instances, but will still need to be
// pickable. These need to be set even if applyToChildInstances is false.
std::function<void(QQuick3DNode *)> checkChildren;
checkChildren = [&](QQuick3DNode *checkNode) {
const auto childItems = checkNode->childItems();
for (auto child : childItems) {
if (auto childNode = qobject_cast<QQuick3DNode *>(child))
checkChildren(childNode);
}
if (auto checkModel = qobject_cast<QQuick3DModel *>(checkNode)) {
QVariant value;
if (enable)
value = QVariant::fromValue(node);
// Specify the actual pick target with dynamic property
checkModel->setProperty("_pickTarget", value);
checkModel->setPickable(enable);
}
};
checkChildren(node);
}
}
if (nodeType == QQuick3DObject::Model)
@@ -115,7 +136,7 @@ void Quick3DNodeInstance::setPickable(bool enable, bool checkParent, bool applyT
#else
Q_UNUSED(enable)
Q_UNUSED(checkParent)
Q_UNUSED(applyToChildren)
Q_UNUSED(applyToChildInstances)
#endif
}

View File

@@ -53,7 +53,7 @@ protected:
private:
Qt5NodeInstanceServer *qt5NodeInstanceServer() const;
QQuick3DNode *quick3DNode() const;
void setPickable(bool enable, bool checkParent, bool applyToChildren);
void setPickable(bool enable, bool checkParent, bool applyToChildInstances);
};
} // namespace Internal

View File

@@ -615,6 +615,8 @@ Rectangle {
anchors.right: parent.right
AdvancedSection {
}
LayerSection {
}
}
}
}

View File

@@ -0,0 +1,319 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.0
import HelperWidgets 2.0
import QtQuick.Layouts 1.0
Section {
anchors.left: parent.left
anchors.right: parent.right
caption: qsTr("Layer")
SectionLayout {
columns: 2
Label {
text: qsTr("Effect")
tooltip: qsTr("Sets the effect that is applied to this layer.")
}
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "QtQuick.Item"
validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.layer_effect
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Enabled")
tooltip: qsTr("Sets whether the item is layered or not.")
}
SecondColumnLayout {
CheckBox {
text: backendValues.layer_enabled.valueToString
backendValue: backendValues.layer_enabled
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Format")
tooltip: qsTr("Defines the internal OpenGL format of the texture.")
}
SecondColumnLayout {
ComboBox {
scope: "ShaderEffectSource"
model: ["Alpha", "RGB", "RGBA"]
backendValue: backendValues.layer_format
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Mipmap")
tooltip: qsTr("Enables the generation of mipmaps for the texture.")
}
SecondColumnLayout {
CheckBox {
text: backendValues.layer_mipmap.valueToString
backendValue: backendValues.layer_mipmap
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Sampler name")
tooltip: qsTr("Sets the name of the effect's source texture property.")
}
SecondColumnLayout {
LineEdit {
backendValue: backendValues.layer_samplerName
text: backendValues.layer_samplerName.valueToString
Layout.fillWidth: true
showTranslateCheckBox: false
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Samples")
tooltip: qsTr("Allows requesting multisampled rendering in the layer.")
}
SecondColumnLayout {
ComboBox {
id: samplesComboBox
model: [2, 4, 8, 16]
backendValue: backendValues.layer_samples
manualMapping: true
Layout.fillWidth: true
onValueFromBackendChanged: {
if (!samplesComboBox.__isCompleted)
return
samplesComboBox.syncIndexToBackendValue()
}
onCompressedActivated: {
if (!samplesComboBox.__isCompleted)
return
if (samplesComboBox.block)
return
backendValues.layer_samples.value = samplesComboBox.model[samplesComboBox.currentIndex]
}
Component.onCompleted: samplesComboBox.syncIndexToBackendValue()
function syncIndexToBackendValue() {
samplesComboBox.block = true
samplesComboBox.currentIndex = samplesComboBox.model.indexOf(backendValues.layer_samples.value)
samplesComboBox.block = false
}
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Smooth")
tooltip: qsTr("Sets whether the layer is smoothly transformed.")
}
SecondColumnLayout {
CheckBox {
text: backendValues.layer_smooth.valueToString
backendValue: backendValues.layer_smooth
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
/*
Label {
text: qsTr("Source rectangle")
tooltip: qsTr("TODO.")
}
SecondColumnLayout {
Label {
text: "X"
width: 12
}
SpinBox {
backendValue: backendValues.layer_sourceRect_x
maximumValue: 0xffff
minimumValue: -0xffff
decimals: 0
realDragRange: 5000
}
Item {
width: 4
height: 4
}
Label {
text: "Y"
width: 12
}
SpinBox {
backendValue: backendValues.layer_sourceRect_y
maximumValue: 0xffff
minimumValue: -0xffff
decimals: 0
realDragRange: 5000
}
ExpandingSpacer {
}
}
Item {
width: 4
height: 4
}
SecondColumnLayout {
Layout.fillWidth: true
Label {
text: "W"
width: 12
}
SpinBox {
backendValue: backendValues.layer_sourceRect_width
maximumValue: 0xffff
minimumValue: 0
decimals: 0
realDragRange: 5000
}
Item {
width: 4
height: 4
}
Label {
text: "H"
width: 12
}
SpinBox {
backendValue: backendValues.layer_sourceRect_height
maximumValue: 0xffff
minimumValue: 0
decimals: 0
realDragRange: 5000
}
ExpandingSpacer {
}
}
*/
Label {
text: qsTr("Texture mirroring")
tooltip: qsTr("Defines how the generated OpenGL texture should be mirrored.")
}
SecondColumnLayout {
ComboBox {
scope: "ShaderEffectSource"
model: ["NoMirroring", "MirrorHorizontally", "MirrorVertically"]
backendValue: backendValues.layer_textureMirroring
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Texture size")
tooltip: qsTr("Sets the requested pixel size of the layers texture.")
}
SecondColumnLayout {
Label {
text: "W"
width: 12
}
SpinBox {
backendValue: backendValues.layer_textureSize_width
minimumValue: 0
maximumValue: 2000
decimals: 0
}
Item {
width: 4
height: 4
}
Label {
text: "H"
width: 12
}
SpinBox {
backendValue: backendValues.layer_textureSize_height
minimumValue: 0
maximumValue: 2000
decimals: 0
}
ExpandingSpacer {
}
}
Label {
text: qsTr("Wrap mode")
tooltip: qsTr("Defines the OpenGL wrap modes associated with the texture.")
}
SecondColumnLayout {
ComboBox {
scope: "ShaderEffectSource"
model: ["ClampToEdge", "RepeatHorizontally", "RepeatVertically", "Repeat"]
backendValue: backendValues.layer_wrapMode
Layout.fillWidth: true
}
ExpandingSpacer {
}
}
}
}

View File

@@ -0,0 +1,213 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.12
import QtQuick.Layouts 1.12
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
Rectangle {
id: editableListView
property variant backendValue
ExtendedFunctionLogic {
id: extFuncLogic
backendValue: editableListView.backendValue
}
property var model
onModelChanged: myRepeater.updateModel()
signal add(string value)
signal remove(int idx)
signal replace(int idx, string value)
property alias actionIndicator: actionIndicator
property alias actionIndicatorVisible: actionIndicator.visible
property real __actionIndicatorWidth: StudioTheme.Values.squareComponentWidth
property real __actionIndicatorHeight: StudioTheme.Values.height
color: "transparent"
border.color: StudioTheme.Values.themeControlOutline
border.width: StudioTheme.Values.border
property int numVisibleItems: myRepeater.count
Layout.preferredWidth: StudioTheme.Values.height * 10
Layout.preferredHeight: myColumn.height
Component {
id: myDelegate
ListViewComboBox {
id: itemFilterComboBox
property int myIndex: index
property bool empty: itemFilterComboBox.initialModelData === ""
validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ }
actionIndicatorVisible: false
typeFilter: "QtQuick3D.Material"
editText: modelData
initialModelData: modelData
width: editableListView.width
onFocusChanged: {
if (itemFilterComboBox.focus) {
myColumn.currentIndex = index
}
if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
myRepeater.dirty = false
editableListView.add(itemFilterComboBox.editText)
}
}
onCompressedActivated: {
if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
myRepeater.dirty = false
editableListView.add(itemFilterComboBox.editText)
} else {
editableListView.replace(myIndex, itemFilterComboBox.editText)
}
}
}
}
Rectangle {
id: highlightRect
color: "transparent"
border.width: StudioTheme.Values.border
border.color: StudioTheme.Values.themeInteraction
x: myColumn.currentItem ? myColumn.currentItem.x : 0
y: myColumn.currentItem ? myColumn.currentItem.y : 0
z: 10
width: myColumn.currentItem ? myColumn.currentItem.width : 0
height: myColumn.currentItem ? myColumn.currentItem.height : 0
}
Column {
id: myColumn
property int currentIndex: -1
property Item currentItem
spacing: -1
onCurrentIndexChanged: myColumn.currentItem = myRepeater.itemAt(myColumn.currentIndex)
Repeater {
id: myRepeater
property bool dirty: false
property var localModel: []
delegate: myDelegate
onItemAdded: function(index, item) {
if (index === myColumn.currentIndex)
myColumn.currentItem = item
}
function updateModel() {
var lastIndex = myColumn.currentIndex
myColumn.currentIndex = -1
myRepeater.localModel = []
editableListView.model.forEach(function(item) {
myRepeater.localModel.push(item)
});
// if list view is still dirty, then last state had an unfinished/empty ComboBox
if (myRepeater.dirty)
myRepeater.localModel.push("")
myRepeater.model = myRepeater.localModel // trigger on change handler
if (lastIndex < 0 && myRepeater.localModel.length > 0)
myColumn.currentIndex = 0
else if (myRepeater.localModel.length > lastIndex)
myColumn.currentIndex = lastIndex
else
myColumn.currentIndex = myRepeater.localModel.length - 1
}
}
Row {
id: row
spacing: -StudioTheme.Values.border
StudioControls.ActionIndicator {
id: actionIndicator
width: actionIndicator.visible ? __actionIndicatorWidth : 0
height: actionIndicator.visible ? __actionIndicatorHeight : 0
showBackground: true
icon.color: extFuncLogic.color
icon.text: extFuncLogic.glyph
onClicked: extFuncLogic.show()
}
StudioControls.AbstractButton {
buttonIcon: "+"
iconFont: StudioTheme.Constants.font
enabled: !myRepeater.dirty
onClicked: {
var idx = myRepeater.localModel.push("") - 1
myRepeater.model = myRepeater.localModel // trigger on change handler
myRepeater.dirty = true
myColumn.currentIndex = idx
myColumn.currentItem.forceActiveFocus()
}
}
StudioControls.AbstractButton {
buttonIcon: "-"
iconFont: StudioTheme.Constants.font
enabled: myRepeater.model.length
onClicked: {
var lastItem = myColumn.currentIndex === myRepeater.localModel.length - 1
if (myColumn.currentItem.initialModelData === "") {
myRepeater.localModel.pop()
myRepeater.dirty = false
myRepeater.model = myRepeater.localModel // trigger on change handler
} else {
editableListView.remove(myColumn.currentIndex)
}
if (!lastItem)
myColumn.currentIndex = myColumn.currentIndex - 1
}
}
Rectangle {
color: StudioTheme.Values.themeControlBackground
border.width: StudioTheme.Values.border
border.color: StudioTheme.Values.themeControlOutline
height: StudioTheme.Values.height
width: editableListView.width - (StudioTheme.Values.height - StudioTheme.Values.border) * (actionIndicatorVisible ? 3 : 2)
}
}
}
}

View File

@@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.12
import HelperWidgets 2.0 as HelperWidgets
HelperWidgets.ComboBox {
id: comboBox
property alias typeFilter: itemFilterModel.typeFilter
manualMapping: true
editable: true
model: comboBox.addDefaultItem(itemFilterModel.itemModel)
HelperWidgets.ItemFilterModel {
id: itemFilterModel
modelNodeBackendProperty: modelNodeBackend
}
property string defaultItem: qsTr("[None]")
property string textValue: comboBox.backendValue.expression
property bool block: false
property bool dirty: true
onTextValueChanged: {
if (comboBox.block)
return
comboBox.setCurrentText(comboBox.textValue)
}
onModelChanged: comboBox.setCurrentText(comboBox.textValue)
onCompressedActivated: comboBox.handleActivate(index)
Component.onCompleted: comboBox.setCurrentText(comboBox.textValue)
onEditTextChanged: comboBox.dirty = true
onFocusChanged: {
if (comboBox.dirty)
comboBox.handleActivate(comboBox.currentIndex)
}
function handleActivate(index)
{
if (!comboBox.__isCompleted || comboBox.backendValue === undefined)
return
var cText = (index === -1) ? comboBox.editText : comboBox.textAt(index)
comboBox.block = true
comboBox.setCurrentText(cText)
comboBox.block = false
}
function setCurrentText(text)
{
if (!comboBox.__isCompleted || comboBox.backendValue === undefined)
return
comboBox.currentIndex = comboBox.find(text)
if (text === "") {
comboBox.currentIndex = 0
comboBox.editText = comboBox.defaultItem
} else {
if (comboBox.currentIndex === -1)
comboBox.editText = text
else if (comboBox.currentIndex === 0)
comboBox.editText = comboBox.defaultItem
}
if (comboBox.currentIndex === 0) {
comboBox.backendValue.resetValue()
} else {
if (comboBox.backendValue.expression !== comboBox.editText)
comboBox.backendValue.expression = comboBox.editText
}
comboBox.dirty = false
}
function addDefaultItem(arr)
{
var copy = arr.slice()
copy.unshift(comboBox.defaultItem)
return copy
}
}

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.12
import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls
StudioControls.ComboBox {
id: comboBox
property alias typeFilter: itemFilterModel.typeFilter
property var initialModelData
property bool __isCompleted: false
editable: true
model: itemFilterModel.itemModel
HelperWidgets.ItemFilterModel {
id: itemFilterModel
modelNodeBackendProperty: modelNodeBackend
}
Component.onCompleted: {
comboBox.__isCompleted = true
// Workaround for proper initialization. Use the initial modelData value and search for it
// in the model. If nothing was found, set the editText to the initial modelData.
comboBox.currentIndex = comboBox.find(comboBox.initialModelData)
if (comboBox.currentIndex === -1)
comboBox.editText = comboBox.initialModelData
}
}

View File

@@ -17,6 +17,7 @@ ComboBox 2.0 ComboBox.qml
CustomCheckBoxStyle 2.0 CustomCheckBoxStyle.qml
CustomComboBoxStyle 2.0 CustomComboBoxStyle.qml
CustomSpinBoxStyle 2.0 CustomSpinBoxStyle.qml
EditableListView 2.0 EditableListView.qml
ExpandingSpacer 2.0 ExpandingSpacer.qml
ExtendedFunctionButton 2.0 ExtendedFunctionButton.qml
ExtendedFunctionLogic 2.0 ExtendedFunctionLogic.qml
@@ -30,6 +31,8 @@ GradientPresetTabContent 2.0 GradientPresetTabContent.qml
GroupBox 2.0 GroupBox.qml
HueSlider 2.0 HueSlider.qml
IconLabel 2.0 IconLabel.qml
ItemFilterComboBox 2.0 ItemFilterComboBox.qml
ListViewComboBox 2.0 ListViewComboBox.qml
Label 2.0 Label.qml
LineEdit 2.0 LineEdit.qml
OriginControl 2.0 OriginControl.qml

View File

@@ -20,7 +20,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
enable_testing()
add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix})
add_test(%{TestCaseName} COMMAND %{TestCaseName})
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
@if "%{RequireGUI}" == "true"
target_link_libraries(%{TestCaseName} PRIVATE Qt5::Gui Qt5::Test)
@@ -46,7 +46,7 @@ enable_testing()
add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
add_executable(%{TestCaseName} %{MainCppName})
add_test(%{TestCaseName} COMMAND %{TestCaseName})
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
target_link_libraries(%{TestCaseName} PRIVATE Qt5::QuickTest)
@@ -91,7 +91,7 @@ include_directories(${GTestIncludes})
add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileWithHeaderSuffix}
${GTestFiles})
add_test(%{TestCaseName} COMMAND %{TestCaseName})
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
target_link_libraries(%{TestCaseName} PRIVATE Threads::Threads)
@endif
@@ -103,7 +103,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
enable_testing()
add_executable(%{TestCaseName} %{MainCppName})
add_test(%{TestCaseName} COMMAND %{TestCaseName})
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
if (DEFINED ENV{BOOST_INCLUDE_DIR})
set(BOOST_INCLUDE_DIR $ENV{BOOST_INCLUDE_DIR})

View File

@@ -1674,7 +1674,8 @@ bool Check::visit(CallExpression *ast)
if (!whiteListedFunction && !isMathFunction && !isDateFunction && !isDirectInConnectionsScope)
addMessage(ErrFunctionsNotSupportedInQmlUi, location);
static const QStringList globalFunctions = {"String", "Boolean", "Date", "Number", "Object", "Array", "QT_TR_NOOP", "QT_TRANSLATE_NOOP", "QT_TRID_NOOP"};
static const QStringList globalFunctions = {"String", "Boolean", "Date", "Number", "Object", "Array", "Symbol", "Object", "Function", "RegExp",
"QT_TR_NOOP", "QT_TRANSLATE_NOOP", "QT_TRID_NOOP"};
if (!name.isEmpty() && name.at(0).isUpper() && !globalFunctions.contains(name)) {
addMessage(WarnExpectedNewWithUppercaseFunction, location);

View File

@@ -145,7 +145,7 @@ void AndroidDebugSupport::start()
setSymbolFile(runControl()->buildDirectory().pathAppended("app_process"));
setSkipExecutableValidation(true);
setUseExtendedRemote(true);
QString devicePreferredAbi = AndroidManager::devicePreferredAbi(target);
QString devicePreferredAbi = AndroidManager::apkDevicePreferredAbi(target);
setAbi(AndroidManager::androidAbi2Abi(devicePreferredAbi));
QUrl gdbServer;
gdbServer.setHost(QHostAddress(QHostAddress::LocalHost).toString());

View File

@@ -509,7 +509,7 @@ void AndroidDeployQtStep::gatherFilesToPull()
QString linkerName("linker");
QString libDirName("lib");
auto preferreABI = AndroidManager::devicePreferredAbi(target());
auto preferreABI = AndroidManager::apkDevicePreferredAbi(target());
if (preferreABI == "arm64-v8a" || preferreABI == "x86_64") {
m_filesToPull["/system/bin/app_process64"] = buildDir + "app_process";
libDirName = "lib64";

View File

@@ -56,12 +56,14 @@ AndroidDevice::AndroidDevice()
setOsType(Utils::OsTypeOtherUnix);
setDeviceState(DeviceReadyToUse);
QString activityPath;
const AndroidConfig &config = AndroidConfigurations::currentConfig();
AndroidManager::apkInfo(config.qtLiveApkPath(), nullptr, nullptr, &activityPath);
qCDebug(androidDeviceLog) << "Using Qt live apk from: " << config.qtLiveApkPath()
<< "Activity Path:" << activityPath;
setQmlsceneCommand(activityPath);
if (config.qtLiveApkPath().exists()) {
QString activityPath;
AndroidManager::apkInfo(config.qtLiveApkPath(), nullptr, nullptr, &activityPath);
qCDebug(androidDeviceLog) << "Using Qt live apk from: " << config.qtLiveApkPath()
<< "Activity Path:" << activityPath;
setQmlsceneCommand(activityPath);
}
}
IDevice::DeviceInfo AndroidDevice::deviceInformation() const

View File

@@ -423,9 +423,8 @@ void AndroidManager::setDeviceSerialNumber(ProjectExplorer::Target *target, cons
target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber);
}
QString AndroidManager::devicePreferredAbi(Target *target)
static QString preferredAbi(const QStringList &appAbis, Target *target)
{
auto appAbis = applicationAbis(target);
const auto deviceAbis = target->namedSettings(AndroidDeviceAbis).toStringList();
for (const auto &abi : deviceAbis) {
if (appAbis.contains(abi))
@@ -434,6 +433,16 @@ QString AndroidManager::devicePreferredAbi(Target *target)
return {};
}
QString AndroidManager::apkDevicePreferredAbi(Target *target)
{
auto libsPath = dirPath(target).pathAppended("libs");
QStringList apkAbis;
for (const auto &abi : QDir{libsPath.toString()}.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
if (QDir{libsPath.pathAppended(abi).toString()}.entryList(QStringList("*.so"), QDir::Files | QDir::NoDotAndDotDot).length())
apkAbis << abi;
return preferredAbi(apkAbis, target);
}
void AndroidManager::setDeviceAbis(ProjectExplorer::Target *target, const QStringList &deviceAbis)
{
target->setNamedSettings(AndroidDeviceAbis, deviceAbis);

View File

@@ -84,7 +84,7 @@ public:
static QString deviceSerialNumber(ProjectExplorer::Target *target);
static void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber);
static QString devicePreferredAbi(ProjectExplorer::Target *target);
static QString apkDevicePreferredAbi(ProjectExplorer::Target *target);
static void setDeviceAbis(ProjectExplorer::Target *target, const QStringList &deviceAbis);
static int deviceApiLevel(ProjectExplorer::Target *target);

View File

@@ -224,7 +224,7 @@ void AndroidRunner::remoteErrorOutput(const QString &output)
}
void AndroidRunner::handleRemoteProcessStarted(Utils::Port gdbServerPort,
const QUrl &qmlServer, int pid)
const QUrl &qmlServer, qint64 pid)
{
m_pid = ProcessHandle(pid);
m_gdbServerPort = gdbServerPort;

View File

@@ -72,7 +72,7 @@ private:
void remoteOutput(const QString &output);
void remoteErrorOutput(const QString &output);
void gotRemoteOutput(const QString &output);
void handleRemoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, int pid);
void handleRemoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, qint64 pid);
void handleRemoteProcessFinished(const QString &errString = QString());
void checkAVD();
void launchAVD();

View File

@@ -225,7 +225,9 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
<< "Extra Start Args:" << m_amStartExtraArgs
<< "Before Start ADB cmds:" << m_beforeStartAdbCommands
<< "After finish ADB cmds:" << m_afterFinishAdbCommands;
m_gdbserverPath = AndroidConfigurations::instance()->currentConfig().gdbServer(AndroidManager::devicePreferredAbi(target)).toString();
QString preferredAbi = AndroidManager::apkDevicePreferredAbi(target);
if (!preferredAbi.isEmpty())
m_gdbserverPath = AndroidConfigurations::instance()->currentConfig().gdbServer(preferredAbi).toString();
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit());
m_useAppParamsForQmlDebugger = version->qtVersion() >= QtSupport::QtVersionNumber(5, 12);
}

View File

@@ -64,7 +64,7 @@ public:
void handleJdbSettled();
signals:
void remoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, int pid);
void remoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, qint64 pid);
void remoteProcessFinished(const QString &errString = QString());
void remoteOutput(const QString &output);

View File

@@ -294,7 +294,7 @@ private:
const std::map<SdkManagerOutputParser::MarkerTag, const char *> markerTags {
{SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"},
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"},
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Updates:"},
{SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"},
{SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"},
{SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"},
{SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"},

View File

@@ -122,10 +122,10 @@ void CMakeBuildConfiguration::initialize()
auto androidAbis = bs->data(Android::Constants::AndroidABIs).toStringList();
QString preferredAbi;
if (androidAbis.contains("arm64-v8a")) {
preferredAbi = "arm64-v8a";
} else if (androidAbis.isEmpty() || androidAbis.contains("armeabi-v7a")) {
if (androidAbis.contains("armeabi-v7a")) {
preferredAbi = "armeabi-v7a";
} else if (androidAbis.isEmpty() || androidAbis.contains("arm64-v8a")) {
preferredAbi = "arm64-v8a";
} else {
preferredAbi = androidAbis.first();
}

View File

@@ -76,13 +76,12 @@ static std::vector<std::unique_ptr<CMakeTool>> autoDetectCMakeTools()
path = Utils::filteredUnique(path);
if (HostOsInfo::isWindowsHost()) {
const QString progFiles = QLatin1String(qgetenv("ProgramFiles"));
path.append(Utils::FilePath::fromString(progFiles + "/CMake"));
path.append(Utils::FilePath::fromString(progFiles + "/CMake/bin"));
const QString progFilesX86 = QLatin1String(qgetenv("ProgramFiles(x86)"));
if (!progFilesX86.isEmpty()) {
path.append(Utils::FilePath::fromString(progFilesX86 + "/CMake"));
path.append(Utils::FilePath::fromString(progFilesX86 + "/CMake/bin"));
for (auto envVar : {"ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"}) {
if (qEnvironmentVariableIsSet(envVar)) {
const QString progFiles = qEnvironmentVariable(envVar);
path.append(Utils::FilePath::fromString(progFiles + "/CMake"));
path.append(Utils::FilePath::fromString(progFiles + "/CMake/bin"));
}
}
}

View File

@@ -203,7 +203,7 @@ void filteredFlags(const QString &fileName,
if (CppTools::ProjectFile::isHeader(CppTools::ProjectFile::classify(fileName)))
fileKind = cpp ? CppTools::ProjectFile::CXXHeader : CppTools::ProjectFile::CHeader;
else
fileKind = cpp ? CppTools::ProjectFile::CXXSource : CppTools::ProjectFile::CXXHeader;
fileKind = cpp ? CppTools::ProjectFile::CXXSource : CppTools::ProjectFile::CSource;
}
// Skip all remaining Windows flags except feature flags.

View File

@@ -62,6 +62,7 @@ public:
QString settingsKey;
bool enforceNewline = false;
bool prependCarriageReturn = false;
bool scrollToBottom = true;
bool linksActive = true;
bool zoomEnabled = false;
@@ -390,7 +391,16 @@ int OutputWindow::maxCharCount() const
void OutputWindow::appendMessage(const QString &output, OutputFormat format)
{
QString out = SynchronousProcess::normalizeNewlines(output);
QString out = output;
if (d->prependCarriageReturn) {
d->prependCarriageReturn = false;
out.prepend('\r');
}
out = SynchronousProcess::normalizeNewlines(out);
if (out.endsWith('\r')) {
d->prependCarriageReturn = true;
out.chop(1);
}
if (out.size() > d->maxCharCount) {
// Current line alone exceeds limit, we need to cut it.
@@ -435,20 +445,22 @@ void OutputWindow::appendMessage(const QString &output, OutputFormat format)
} else {
newline = out.indexOf(QLatin1Char('\n'));
moveCursor(QTextCursor::End);
if (newline != -1 && d->formatter)
d->formatter->appendMessage(out.left(newline), format);// doesn't enforce new paragraph like appendPlainText
if (newline != -1) {
if (d->formatter)
d->formatter->appendMessage(out.left(newline), format);// doesn't enforce new paragraph like appendPlainText
out = out.mid(newline);
}
}
QString s = out.mid(newline+1);
if (s.isEmpty()) {
if (out.isEmpty()) {
d->enforceNewline = true;
} else {
if (s.endsWith(QLatin1Char('\n'))) {
if (out.endsWith(QLatin1Char('\n'))) {
d->enforceNewline = true;
s.chop(1);
out.chop(1);
}
if (d->formatter)
d->formatter->appendMessage(s, format);
d->formatter->appendMessage(out, format);
}
} else {
if (d->formatter)
@@ -502,6 +514,7 @@ bool OutputWindow::isScrollbarAtBottom() const
void OutputWindow::clear()
{
d->enforceNewline = false;
d->prependCarriageReturn = false;
QPlainTextEdit::clear();
if (d->formatter)
d->formatter->clear();

View File

@@ -2114,7 +2114,10 @@ GlobalBreakpointItem::GlobalBreakpointItem()
}
GlobalBreakpointItem::~GlobalBreakpointItem()
{}
{
delete m_marker;
m_marker = nullptr;
}
QVariant GlobalBreakpointItem::data(int column, int role) const
{
@@ -2414,8 +2417,6 @@ BreakpointManager::BreakpointManager()
this, &BreakpointManager::loadSessionData);
connect(SessionManager::instance(), &SessionManager::aboutToSaveSession,
this, &BreakpointManager::saveSessionData);
connect(SessionManager::instance(), &SessionManager::aboutToUnloadSession,
this, &BreakpointManager::aboutToUnloadSession);
}
QAbstractItemModel *BreakpointManager::model()
@@ -2797,12 +2798,6 @@ void BreakpointManager::saveSessionData()
SessionManager::setValue("Breakpoints", list);
}
void BreakpointManager::aboutToUnloadSession()
{
saveSessionData();
clear();
}
void BreakpointManager::loadSessionData()
{
clear();

View File

@@ -312,7 +312,6 @@ private:
void loadSessionData();
void saveSessionData();
void aboutToUnloadSession();
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
void gotoLocation(const GlobalBreakpoint &gbp) const;

View File

@@ -262,8 +262,7 @@ void QmlInspectorAgent::onResult(quint32 queryId, const QVariant &value,
if (index < 0) {
if (QTC_GUARD(m_qmlEngine))
m_qmlEngine->expressionEvaluated(queryId, value);
} else {
Q_ASSERT(index < m_engines.length());
} else if (QTC_GUARD(index < m_engines.length())) {
const int engineId = m_engines.at(index).debugId();
m_rootContexts.insert(engineId, qvariant_cast<ContextReference>(value));
if (m_rootContexts.size() == m_engines.size()) {
@@ -373,7 +372,7 @@ void QmlInspectorAgent::reloadEngines()
void QmlInspectorAgent::queryEngineContext()
{
qCDebug(qmlInspectorLog) << __FUNCTION__;
qCDebug(qmlInspectorLog) << __FUNCTION__ << "pending queries:" << m_rootContextQueryIds;
if (!isConnected() || !boolSetting(ShowQmlObjectTree))
return;
@@ -381,6 +380,7 @@ void QmlInspectorAgent::queryEngineContext()
log(LogSend, "LIST_OBJECTS");
m_rootContexts.clear();
m_rootContextQueryIds.clear();
for (const auto &engine : qAsConst(m_engines))
m_rootContextQueryIds.append(m_engineClient->queryRootContexts(engine));
}

View File

@@ -261,6 +261,19 @@ void McuTarget::setColorDepth(int colorDepth)
m_colorDepth = colorDepth;
}
static QString findInProgramFiles(const QString &folder)
{
for (auto envVar : {"ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"}) {
if (!qEnvironmentVariableIsSet(envVar))
continue;
const Utils::FilePath dir =
Utils::FilePath::fromUserInput(qEnvironmentVariable(envVar) + "/" + folder);
if (dir.exists())
return dir.toString();
}
return {};
}
static McuPackage *createQtForMCUsPackage()
{
auto result = new McuPackage(
@@ -280,8 +293,7 @@ static McuPackage *createArmGccPackage()
if (qEnvironmentVariableIsSet(envVar))
defaultPath = qEnvironmentVariable(envVar);
if (defaultPath.isEmpty() && Utils::HostOsInfo::isWindowsHost()) {
const QDir installDir(
qEnvironmentVariable("ProgramFiles(x86)") + "/GNU Tools ARM Embedded/");
const QDir installDir(findInProgramFiles("/GNU Tools ARM Embedded/"));
if (installDir.exists()) {
// If GNU Tools installation dir has only one sub dir,
// select the sub dir, otherwise the installation dir.
@@ -320,11 +332,14 @@ static McuPackage *createStm32CubeFwF7SdkPackage()
static McuPackage *createStm32CubeProgrammerPackage()
{
const QString defaultPath =
Utils::HostOsInfo::isWindowsHost() ?
QDir::fromNativeSeparators(qEnvironmentVariable("ProgramFiles"))
+ "/STMicroelectronics/STM32Cube/STM32CubeProgrammer/"
: QDir::homePath();
QString defaultPath = QDir::homePath();
if (Utils::HostOsInfo::isWindowsHost()) {
const QString programPath =
findInProgramFiles("/STMicroelectronics/STM32Cube/STM32CubeProgrammer/");
if (!programPath.isEmpty())
defaultPath = programPath;
}
auto result = new McuPackage(
McuPackage::tr("STM32CubeProgrammer"),
defaultPath,
@@ -351,11 +366,12 @@ static McuPackage *createEvkbImxrt1050SdkPackage()
static McuPackage *createSeggerJLinkPackage()
{
const QString defaultPath =
Utils::HostOsInfo::isWindowsHost() ?
QDir::fromNativeSeparators(qEnvironmentVariable("ProgramFiles(x86)"))
+ "/SEGGER/JLink"
: QString("%{Env:SEGGER_JLINK_SOFTWARE_AND_DOCUMENTATION_PATH}");
QString defaultPath = QString("%{Env:SEGGER_JLINK_SOFTWARE_AND_DOCUMENTATION_PATH}");
if (Utils::HostOsInfo::isWindowsHost()) {
const QString programPath = findInProgramFiles("/SEGGER/JLink");
if (!programPath.isEmpty())
defaultPath = programPath;
}
auto result = new McuPackage(
McuPackage::tr("SEGGER JLink"),
defaultPath,
@@ -471,6 +487,13 @@ static bool mcuTargetIsDesktop(const McuTarget* mcuTarget)
return mcuTarget->qulPlatform() == "Qt";
}
static Utils::FilePath jomExecutablePath()
{
return Utils::HostOsInfo::isWindowsHost() ?
Utils::FilePath::fromUserInput(Core::ICore::libexecPath() + "/jom.exe")
: Utils::FilePath();
}
static void setKitProperties(const QString &kitName, ProjectExplorer::Kit *k,
const McuTarget* mcuTarget)
{
@@ -484,9 +507,13 @@ static void setKitProperties(const QString &kitName, ProjectExplorer::Kit *k,
if (mcuTargetIsDesktop(mcuTarget)) {
k->setDeviceTypeForIcon(Constants::DEVICE_TYPE);
} else {
k->setIrrelevantAspects({SysRootKitAspect::id(),
"QtSupport.QtInformation" // QtKitAspect::id()
});
QSet<Core::Id> irrelevant = {
SysRootKitAspect::id(),
"QtSupport.QtInformation" // QtKitAspect::id()
};
if (jomExecutablePath().exists()) // TODO: add id() getter to CMakeGeneratorKitAspect
irrelevant.insert("CMake.GeneratorKitInformation");
k->setIrrelevantAspects(irrelevant);
}
}
@@ -548,10 +575,9 @@ static void setKitEnvironment(ProjectExplorer::Kit *k, const McuTarget* mcuTarge
QDir::toNativeSeparators(package->path())});
}
pathAdditions.append("${Path}");
if (Utils::HostOsInfo::isWindowsHost())
pathAdditions.append(QDir::toNativeSeparators(Core::ICore::libexecPath())); // for jom
pathAdditions.append(QDir::toNativeSeparators(Core::ICore::libexecPath() + "/clang/bin"));
changes.append({"Path", pathAdditions.join(Utils::HostOsInfo::pathListSeparator())});
const QString path = QLatin1String(Utils::HostOsInfo().isWindowsHost() ? "Path" : "PATH");
changes.append({path, pathAdditions.join(Utils::HostOsInfo::pathListSeparator())});
EnvironmentKitAspect::setEnvironmentChanges(k, changes);
}
@@ -574,9 +600,12 @@ static void setKitCMakeOptions(ProjectExplorer::Kit *k, const McuTarget* mcuTarg
if (mcuTarget->colorDepth() >= 0)
config.append(CMakeConfigItem("QUL_COLOR_DEPTH",
QString::number(mcuTarget->colorDepth()).toLatin1()));
CMakeConfigurationKitAspect::setConfiguration(k, config);
if (Utils::HostOsInfo::isWindowsHost())
const Utils::FilePath jom = jomExecutablePath();
if (jom.exists()) {
config.append(CMakeConfigItem("CMAKE_MAKE_PROGRAM", jom.toString().toLatin1()));
CMakeGeneratorKitAspect::setGenerator(k, "NMake Makefiles JOM");
}
CMakeConfigurationKitAspect::setConfiguration(k, config);
}
QString McuSupportOptions::kitName(const McuTarget *mcuTarget) const

View File

@@ -27,6 +27,8 @@
#include "mcusupportoptionspage.h"
#include "mcusupportoptions.h"
#include <cmakeprojectmanager/cmakeprojectconstants.h>
#include <cmakeprojectmanager/cmaketoolmanager.h>
#include <coreplugin/icore.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -36,6 +38,7 @@
#include <QComboBox>
#include <QDir>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
@@ -44,6 +47,11 @@
namespace McuSupport {
namespace Internal {
static bool cMakeAvailable()
{
return !CMakeProjectManager::CMakeToolManager::cmakeTools().isEmpty();
}
class McuSupportOptionsWidget : public QWidget
{
public:
@@ -53,12 +61,16 @@ public:
void showMcuTargetPackages();
McuTarget *currentMcuTarget() const;
protected:
void showEvent(QShowEvent *event) override;
private:
QString m_armGccPath;
const McuSupportOptions *m_options;
QMap <McuPackage*, QWidget*> m_packageWidgets;
QMap <McuTarget*, QWidget*> m_mcuTargetPacketWidgets;
QFormLayout *m_packagesLayout = nullptr;
QLabel *m_statusIcon = nullptr;
QLabel *m_statusLabel = nullptr;
QComboBox *m_mcuTargetComboBox = nullptr;
};
@@ -87,17 +99,29 @@ McuSupportOptionsWidget::McuSupportOptionsWidget(const McuSupportOptions *option
m_packagesLayout = new QFormLayout;
m_packagesGroupBox->setLayout(m_packagesLayout);
m_statusIcon = new QLabel;
m_statusIcon->setAlignment(Qt::AlignBottom);
m_statusLabel = new QLabel;
mainLayout->addWidget(m_statusLabel, 2);
m_statusLabel->setWordWrap(true);
m_statusLabel->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
m_statusLabel->setOpenExternalLinks(false);
auto statusWidget = new QWidget;
auto statusLayout = new QHBoxLayout(statusWidget);
statusLayout->setMargin(0);
statusLayout->addWidget(m_statusIcon, 0);
statusLayout->addWidget(m_statusLabel, 2);
mainLayout->addWidget(statusWidget, 2);
connect(options, &McuSupportOptions::changed, this, &McuSupportOptionsWidget::updateStatus);
connect(m_mcuTargetComboBox, &QComboBox::currentTextChanged,
this, &McuSupportOptionsWidget::showMcuTargetPackages);
connect(m_statusLabel, &QLabel::linkActivated, this, []{
Core::ICore::showOptionsDialog(
CMakeProjectManager::Constants::CMAKE_SETTINGSPAGE_ID,
Core::ICore::mainWindow());
});
showMcuTargetPackages();
updateStatus();
}
void McuSupportOptionsWidget::updateStatus()
@@ -106,12 +130,22 @@ void McuSupportOptionsWidget::updateStatus()
if (!mcuTarget)
return;
m_statusLabel->setText(mcuTarget->isValid()
static const QPixmap okIcon = Utils::Icons::OK.pixmap();
static const QPixmap notOkIcon = Utils::Icons::BROKEN.pixmap();
m_statusIcon->setPixmap(cMakeAvailable() && mcuTarget->isValid() ? okIcon : notOkIcon);
QStringList errorStrings;
if (!mcuTarget->isValid())
errorStrings << "Provide the package paths in order to create a kit for your target.";
if (!cMakeAvailable())
errorStrings << "No CMake tool was detected. Add a CMake tool in the "
"<a href=\"cmake\">CMake options</a> and press Apply.";
m_statusLabel->setText(errorStrings.isEmpty()
? QString::fromLatin1("A kit <b>%1</b> for the selected target can be generated. "
"Press Apply to generate it.").arg(m_options->kitName(
mcuTarget))
: QString::fromLatin1("Provide the package paths in order to create a kit for "
"your target."));
: errorStrings.join("<br/>"));
}
void McuSupportOptionsWidget::showMcuTargetPackages()
@@ -143,6 +177,12 @@ McuTarget *McuSupportOptionsWidget::currentMcuTarget() const
return m_options->mcuTargets.isEmpty() ? nullptr : m_options->mcuTargets.at(mcuTargetIndex);
}
void McuSupportOptionsWidget::showEvent(QShowEvent *event)
{
Q_UNUSED(event)
updateStatus();
}
McuSupportOptionsPage::McuSupportOptionsPage(QObject* parent)
: Core::IOptionsPage(parent)
{
@@ -168,8 +208,8 @@ void McuSupportOptionsPage::apply()
QTC_ASSERT(m_options->armGccPackage, return);
QTC_ASSERT(m_options->qtForMCUsSdkPackage, return);
if (!widget()->isVisible())
return; // Only create/overwrite kits when this option page is shown
if (!widget()->isVisible() || !cMakeAvailable())
return;
const McuTarget *mcuTarget = m_widget->currentMcuTarget();
if (!mcuTarget)

View File

@@ -42,12 +42,16 @@ using namespace Utils;
namespace McuSupport {
namespace Internal {
static CommandLine flashAndRunCommand(Target *target)
static FilePath cmakeFilePath(const Target *target)
{
const QString projectName = target->project()->displayName();
const CMakeProjectManager::CMakeTool *tool =
CMakeProjectManager::CMakeKitAspect::cmakeTool(target->kit());
return tool->filePath();
}
static QStringList flashAndRunArgs(const Target *target)
{
const QString projectName = target->project()->displayName();
// TODO: Hack! Implement flash target name handling, properly
const QString targetName =
@@ -55,23 +59,19 @@ static CommandLine flashAndRunCommand(Target *target)
? QString("flash_%1").arg(projectName)
: QString("flash_%1_and_bootloader").arg(projectName);
return CommandLine(tool->filePath(), {
"--build",
".",
"--target",
targetName
});
return {"--build", ".", "--target", targetName};
}
FlashAndRunConfiguration::FlashAndRunConfiguration(Target *target, Core::Id id)
: RunConfiguration(target, id)
{
auto effectiveFlashAndRunCall = addAspect<BaseStringAspect>();
effectiveFlashAndRunCall->setLabelText(tr("Effective flash and run call:"));
effectiveFlashAndRunCall->setDisplayStyle(BaseStringAspect::TextEditDisplay);
auto flashAndRunParameters = addAspect<BaseStringAspect>();
flashAndRunParameters->setLabelText("Flash and run CMake parameters:");
flashAndRunParameters->setDisplayStyle(BaseStringAspect::TextEditDisplay);
flashAndRunParameters->setSettingsKey("FlashAndRunConfiguration.Parameters");
setUpdater([target, effectiveFlashAndRunCall] {
effectiveFlashAndRunCall->setValue(flashAndRunCommand(target).toUserOutput());
setUpdater([target, flashAndRunParameters] {
flashAndRunParameters->setValue(flashAndRunArgs(target).join(' '));
});
update();
@@ -86,8 +86,11 @@ public:
: SimpleTargetRunner(runControl)
{
setStarter([this, runControl] {
ProjectExplorer::Target *target = runControl->target();
const CommandLine cmd = flashAndRunCommand(target);
const Target *target = runControl->target();
const CommandLine cmd(
cmakeFilePath(target),
runControl->runConfiguration()->aspect<BaseStringAspect>()->value(),
CommandLine::Raw);
Runnable r;
r.workingDirectory =
target->activeBuildConfiguration()->buildDirectory().toUserOutput();

View File

@@ -1178,7 +1178,7 @@ static QString wrappedMakeCommand(const QString &command)
return command;
QTextStream stream(&wrapper);
stream << "chcp 65001\n";
stream << command << " %*";
stream << "\"" << QDir::toNativeSeparators(command) << "\" %*";
return wrapperPath;
}

View File

@@ -25,6 +25,11 @@
\" <comment>Python source file without console</comment>\",
\" <glob pattern=\'*.pyw\'/>\",
\" </mime-type>\",
\" <mime-type type=\'text/x-python-interface\'>\",
\" <sub-class-of type=\'text/x-python\'/>\",
\" <comment>Python module interface file</comment>\",
\" <glob pattern=\'*.pyi\'/>\",
\" </mime-type>\",
\" <mime-type type=\'text/x-python-project\'>\",
\" <sub-class-of type=\'text/x-python\'/>\",
\" <comment>Qt Creator Python project file</comment>\",

View File

@@ -193,6 +193,21 @@ PythonProject::PythonProject(const FilePath &fileName)
setBuildSystemCreator([](Target *t) { return new PythonBuildSystem(t); });
}
static FileType getFileType(const FilePath &f)
{
if (f.endsWith(".py"))
return FileType::Source;
if (f.endsWith(".pyproject") || f.endsWith(".pyqtc"))
return FileType::Project;
if (f.endsWith(".qrc"))
return FileType::Resource;
if (f.endsWith(".ui"))
return FileType::Form;
if (f.endsWith(".qml") || f.endsWith(".js"))
return FileType::QML;
return Node::fileTypeForFileName(f);
}
void PythonBuildSystem::triggerParsing()
{
ParseGuard guard = guardParsingRun();
@@ -205,13 +220,7 @@ void PythonBuildSystem::triggerParsing()
for (const QString &f : qAsConst(m_files)) {
const QString displayName = baseDir.relativeFilePath(f);
const FilePath filePath = FilePath::fromString(f);
FileType fileType;
if (f.endsWith(".py"))
fileType = FileType::Source;
else if (f.endsWith(".pyproject") || f.endsWith(".pyqtc"))
fileType = FileType::Project;
else
fileType = Node::fileTypeForFileName(filePath);
const FileType fileType = getFileType(filePath);
newRoot->addNestedNode(std::make_unique<PythonFileNode>(filePath, displayName, fileType));
if (fileType == FileType::Source) {

View File

@@ -32,6 +32,9 @@
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <resourceeditor/resourcenode.h>
#include <utils/qtcassert.h>
@@ -409,11 +412,16 @@ bool QmakeProFileNode::setData(Core::Id role, const QVariant &value) const
QmakeProFile *pro = proFile();
if (!pro)
return false;
const QString arch = pro->singleVariableValue(Variable::AndroidArch);
const QString scope = "contains(ANDROID_TARGET_ARCH," + arch + ')';
auto flags = QmakeProjectManager::Internal::ProWriter::ReplaceValues
| QmakeProjectManager::Internal::ProWriter::MultiLine;
QString scope;
int flags = QmakeProjectManager::Internal::ProWriter::ReplaceValues;
if (Target *target = m_buildSystem->target()) {
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit());
if (version && version->qtVersion() < QtSupport::QtVersionNumber(5, 14, 0)) {
const QString arch = pro->singleVariableValue(Variable::AndroidArch);
scope = "contains(ANDROID_TARGET_ARCH," + arch + ')';
flags |= QmakeProjectManager::Internal::ProWriter::MultiLine;
}
}
if (role == Android::Constants::AndroidExtraLibs)
return pro->setProVariable("ANDROID_EXTRA_LIBS", value.toStringList(), scope, flags);

View File

@@ -722,8 +722,8 @@ void QMakeStepConfigWidget::updateSummaryLabel()
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
item->setCheckState(Qt::Unchecked);
isAndroid = isAndroid && abi.osFlavor() == Abi::OSFlavor::AndroidLinuxFlavor;
if (isAndroid && (item->text() == "arm64-v8a" ||
(m_preferredAbiIndex == -1 && item->text() == "armeabi-v7a"))) {
if (isAndroid && (item->text() == "armeabi-v7a" ||
(m_preferredAbiIndex == -1 && item->text() == "arm64-v8a"))) {
m_preferredAbiIndex = abisListWidget->count() - 1;
}
}

View File

@@ -140,6 +140,7 @@ extend_qtc_plugin(QmlDesigner
valueschangedcommand.cpp valueschangedcommand.h
changeselectioncommand.cpp changeselectioncommand.h
drop3dlibraryitemcommand.cpp drop3dlibraryitemcommand.h
change3dviewcommand.cpp change3dviewcommand.h
)
extend_qtc_plugin(QmlDesigner
@@ -234,6 +235,7 @@ extend_qtc_plugin(QmlDesigner
snappinglinecreator.cpp snappinglinecreator.h
toolbox.cpp toolbox.h
option3daction.cpp option3daction.h
editview3dproxydialog.cpp editview3dproxydialog.h
)
extend_qtc_plugin(QmlDesigner
@@ -524,6 +526,7 @@ extend_qtc_plugin(QmlDesigner
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/bindingeditor
SOURCES bindingeditor.cpp bindingeditor.h
actioneditor.cpp actioneditor.h
bindingeditordialog.cpp bindingeditordialog.h
bindingeditorwidget.cpp bindingeditorwidget.h
)
@@ -624,6 +627,7 @@ extend_qtc_plugin(QmlDesigner
detail/keyframeitem.cpp detail/keyframeitem.h
detail/playhead.cpp detail/playhead.h
detail/selectableitem.cpp detail/selectableitem.h
detail/selectionmodel.cpp detail/selectionmodel.h
detail/selector.cpp detail/selector.h
detail/shortcut.cpp detail/shortcut.h
detail/treeitemdelegate.cpp detail/treeitemdelegate.h

View File

@@ -0,0 +1,131 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "actioneditor.h"
#include <qmldesignerplugin.h>
#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <metainfo.h>
#include <qmlmodelnodeproxy.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <propertyeditorvalue.h>
namespace QmlDesigner {
static ActionEditor *s_lastActionEditor = nullptr;
ActionEditor::ActionEditor(QObject *)
: m_index(QModelIndex())
{
}
ActionEditor::~ActionEditor()
{
hideWidget();
}
void ActionEditor::registerDeclarativeType()
{
qmlRegisterType<ActionEditor>("HelperWidgets", 2, 0, "ActionEditor");
}
void ActionEditor::showWidget(int x, int y)
{
if (s_lastActionEditor)
s_lastActionEditor->hideWidget();
s_lastActionEditor = this;
m_dialog = new BindingEditorDialog(Core::ICore::dialogParent(),
BindingEditorDialog::DialogType::ActionDialog);
QObject::connect(m_dialog, &BindingEditorDialog::accepted,
this, &ActionEditor::accepted);
QObject::connect(m_dialog, &BindingEditorDialog::rejected,
this, &ActionEditor::rejected);
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
m_dialog->showWidget(x, y);
m_dialog->activateWindow();
}
void ActionEditor::hideWidget()
{
if (s_lastActionEditor == this)
s_lastActionEditor = nullptr;
if (m_dialog)
{
m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override
m_dialog->close();
}
}
QString ActionEditor::bindingValue() const
{
if (!m_dialog)
return {};
return m_dialog->editorValue();
}
void ActionEditor::setBindingValue(const QString &text)
{
if (m_dialog)
m_dialog->setEditorValue(text);
}
bool ActionEditor::hasModelIndex() const
{
return m_index.isValid();
}
void ActionEditor::resetModelIndex()
{
m_index = QModelIndex();
}
QModelIndex ActionEditor::modelIndex() const
{
return m_index;
}
void ActionEditor::setModelIndex(const QModelIndex &index)
{
m_index = index;
}
void ActionEditor::updateWindowName()
{
if (!m_dialog.isNull())
{
m_dialog->setWindowTitle(tr("Action Editor"));
m_dialog->raise();
}
}
} // QmlDesigner namespace

View File

@@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#ifndef ACTIONEDITOR_H
#define ACTIONEDITOR_H
#include <bindingeditor/bindingeditordialog.h>
#include <qmldesignercorelib_global.h>
#include <modelnode.h>
#include <QtQml>
#include <QObject>
#include <QPointer>
namespace QmlDesigner {
class ActionEditor : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ bindingValue WRITE setBindingValue)
public:
ActionEditor(QObject *parent = nullptr);
~ActionEditor();
static void registerDeclarativeType();
Q_INVOKABLE void showWidget(int x, int y);
Q_INVOKABLE void hideWidget();
QString bindingValue() const;
void setBindingValue(const QString &text);
bool hasModelIndex() const;
void resetModelIndex();
QModelIndex modelIndex() const;
void setModelIndex(const QModelIndex &index);
Q_INVOKABLE void updateWindowName();
signals:
void accepted();
void rejected();
private:
QVariant backendValue() const;
QVariant modelNodeBackend() const;
QVariant stateModelNode() const;
private:
QPointer<BindingEditorDialog> m_dialog;
QModelIndex m_index;
};
}
QML_DECLARE_TYPE(QmlDesigner::ActionEditor)
#endif //ACTIONEDITOR_H

View File

@@ -1,7 +1,9 @@
HEADERS += $$PWD/bindingeditor.h
HEADERS += $$PWD/actioneditor.h
HEADERS += $$PWD/bindingeditordialog.h
HEADERS += $$PWD/bindingeditorwidget.h
SOURCES += $$PWD/bindingeditor.cpp
SOURCES += $$PWD/actioneditor.cpp
SOURCES += $$PWD/bindingeditordialog.cpp
SOURCES += $$PWD/bindingeditorwidget.cpp

View File

@@ -41,10 +41,12 @@
namespace QmlDesigner {
BindingEditorDialog::BindingEditorDialog(QWidget *parent)
BindingEditorDialog::BindingEditorDialog(QWidget *parent, DialogType type)
: QDialog(parent)
, m_dialogType(type)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowFlag(Qt::Tool, true);
setWindowTitle(defaultTitle());
setModal(false);
@@ -58,12 +60,14 @@ BindingEditorDialog::BindingEditorDialog(QWidget *parent)
QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked,
this, &BindingEditorDialog::accepted);
QObject::connect(m_comboBoxItem, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &BindingEditorDialog::itemIDChanged);
QObject::connect(m_comboBoxProperty, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &BindingEditorDialog::propertyIDChanged);
QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged,
this, &BindingEditorDialog::textChanged);
if (m_dialogType == DialogType::BindingDialog) {
QObject::connect(m_comboBoxItem, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &BindingEditorDialog::itemIDChanged);
QObject::connect(m_comboBoxProperty, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &BindingEditorDialog::propertyIDChanged);
QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged,
this, &BindingEditorDialog::textChanged);
}
}
BindingEditorDialog::~BindingEditorDialog()
@@ -185,10 +189,12 @@ void BindingEditorDialog::setupJSEditor()
void BindingEditorDialog::setupUIComponents()
{
m_verticalLayout = new QVBoxLayout(this);
m_comboBoxLayout = new QHBoxLayout;
m_comboBoxItem = new QComboBox(this);
m_comboBoxProperty = new QComboBox(this);
if (m_dialogType == DialogType::BindingDialog) {
m_comboBoxLayout = new QHBoxLayout;
m_comboBoxItem = new QComboBox(this);
m_comboBoxProperty = new QComboBox(this);
}
m_editorWidget->setParent(this);
m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
@@ -199,11 +205,11 @@ void BindingEditorDialog::setupUIComponents()
m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
m_comboBoxLayout->addWidget(m_comboBoxItem);
m_comboBoxLayout->addWidget(m_comboBoxProperty);
m_verticalLayout->addLayout(m_comboBoxLayout);
if (m_dialogType == DialogType::BindingDialog) {
m_comboBoxLayout->addWidget(m_comboBoxItem);
m_comboBoxLayout->addWidget(m_comboBoxProperty);
m_verticalLayout->addLayout(m_comboBoxLayout);
}
m_verticalLayout->addWidget(m_editorWidget);
m_verticalLayout->addWidget(m_buttonBox);

View File

@@ -57,8 +57,14 @@ public:
QStringList properties;
};
enum DialogType {
Unknown = 0,
BindingDialog = 1,
ActionDialog = 2
};
public:
BindingEditorDialog(QWidget *parent = nullptr);
BindingEditorDialog(QWidget *parent = nullptr, DialogType type = DialogType::BindingDialog);
~BindingEditorDialog() override;
void showWidget(int x, int y);
@@ -84,6 +90,7 @@ public slots:
void textChanged();
private:
DialogType m_dialogType = DialogType::BindingDialog;
TextEditor::BaseTextEditor *m_editor = nullptr;
BindingEditorWidget *m_editorWidget = nullptr;
QVBoxLayout *m_verticalLayout = nullptr;

View File

@@ -47,6 +47,7 @@ QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &pr
foreach (QmlDesigner::PropertyName propertyName, propertyNameList) {
stringList << QString::fromUtf8(propertyName);
}
stringList.removeDuplicates();
return stringList;
}
@@ -127,7 +128,7 @@ void ConnectionModel::addSignalHandler(const SignalHandlerProperty &signalHandle
ModelNode connectionsModelNode = signalHandlerProperty.parentModelNode();
if (connectionsModelNode.bindingProperty("target").isValid()) {
idLabel =connectionsModelNode.bindingProperty("target").expression();
idLabel = connectionsModelNode.bindingProperty("target").expression();
}
targetItem = new QStandardItem(idLabel);
@@ -281,6 +282,12 @@ void ConnectionModel::variantPropertyChanged(const VariantProperty &variantPrope
resetModel();
}
void ConnectionModel::abstractPropertyChanged(const AbstractProperty &abstractProperty)
{
if (isConnection(abstractProperty.parentModelNode()))
resetModel();
}
void ConnectionModel::deleteConnectionByRow(int currentRow)
{
signalHandlerPropertyForRow(currentRow).parentModelNode().destroy();

View File

@@ -29,6 +29,7 @@
namespace QmlDesigner {
class AbstractProperty;
class ModelNode;
class BindingProperty;
class SignalHandlerProperty;
@@ -59,6 +60,7 @@ public:
void bindingPropertyChanged(const BindingProperty &bindingProperty);
void variantPropertyChanged(const VariantProperty &variantProperty);
void abstractPropertyChanged(const AbstractProperty &abstractProperty);
void deleteConnectionByRow(int currentRow);

View File

@@ -34,6 +34,7 @@
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <variantproperty.h>
#include <signalhandlerproperty.h>
namespace QmlDesigner {
@@ -139,6 +140,13 @@ void ConnectionView::bindingPropertiesChanged(const QList<BindingProperty> &prop
}
}
void ConnectionView::signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &propertyList,
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
for (const SignalHandlerProperty &signalHandlerProperty : propertyList)
connectionModel()->abstractPropertyChanged(signalHandlerProperty);
}
void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList,
const QList<ModelNode> & /*lastSelectedNodeList*/)
{

View File

@@ -65,6 +65,7 @@ public:
void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) override;
void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) override;
void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) override;
void signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty>& propertyList, PropertyChangeFlags propertyChange) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;

View File

@@ -33,6 +33,7 @@
#include "connectionmodel.h"
#include "dynamicpropertiesmodel.h"
#include "theme.h"
#include "signalhandlerproperty.h"
#include <designersettings.h>
#include <qmldesignerplugin.h>
@@ -43,6 +44,9 @@
#include <QToolButton>
#include <QStyleFactory>
#include <QMenu>
#include <bindingeditor/actioneditor.h>
namespace QmlDesigner {
@@ -52,6 +56,27 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
QFrame(parent),
ui(new Ui::ConnectionViewWidget)
{
m_actionEditor = new QmlDesigner::ActionEditor(this);
QObject::connect(m_actionEditor, &QmlDesigner::ActionEditor::accepted,
[&]() {
if (m_actionEditor->hasModelIndex()) {
ConnectionModel *connectionModel = qobject_cast<ConnectionModel *>(ui->connectionView->model());
if (connectionModel->rowCount() > m_actionEditor->modelIndex().row())
{
SignalHandlerProperty signalHandler =
connectionModel->signalHandlerPropertyForRow(m_actionEditor->modelIndex().row());
signalHandler.setSource(m_actionEditor->bindingValue());
}
m_actionEditor->resetModelIndex();
}
m_actionEditor->hideWidget();
});
QObject::connect(m_actionEditor, &QmlDesigner::ActionEditor::rejected,
[&]() {
m_actionEditor->resetModelIndex();
m_actionEditor->hideWidget();
});
setWindowTitle(tr("Connections", "Title of connection view"));
ui->setupUi(this);
@@ -96,6 +121,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
ConnectionViewWidget::~ConnectionViewWidget()
{
delete m_actionEditor;
delete ui;
}
@@ -116,9 +142,37 @@ void ConnectionViewWidget::setConnectionModel(ConnectionModel *model)
ui->connectionView->horizontalHeader()->setDefaultSectionSize(160);
ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->connectionView->setItemDelegate(new ConnectionDelegate);
connect(ui->connectionView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ConnectionViewWidget::connectionTableViewSelectionChanged);
}
void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
{
if (currentTab() != ConnectionTab || ui->connectionView == nullptr)
return;
//adjusting qpoint to the qtableview entrances:
QPoint posInTable(ui->connectionView->mapFromGlobal(mapToGlobal(event->pos())));
posInTable = QPoint(posInTable.x(), posInTable.y() - ui->connectionView->horizontalHeader()->height());
//making sure that we have source column in our hands:
QModelIndex index = ui->connectionView->indexAt(posInTable).siblingAtColumn(ConnectionModel::SourceRow);
if (!index.isValid())
return;
QMenu menu(this);
menu.addAction(tr("Open Action Editor"), [&]() {
if (index.isValid()) {
m_actionEditor->showWidget(mapToGlobal(event->pos()).x(), mapToGlobal(event->pos()).y());
m_actionEditor->setBindingValue(index.data().toString());
m_actionEditor->setModelIndex(index);
m_actionEditor->updateWindowName();
}
});
menu.exec(event->globalPos());
}
void ConnectionViewWidget::setDynamicPropertiesModel(DynamicPropertiesModel *model)
@@ -155,7 +209,6 @@ QList<QToolButton *> ConnectionViewWidget::createToolBarWidgets()
buttons << new QToolButton();
buttons.constLast()->setIcon(Utils::Icons::MINUS.icon());
buttons.constLast()->setToolTip(tr("Remove selected binding or connection."));
buttons.constLast()->setShortcut(QKeySequence(Qt::Key_Delete));
connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::removeButtonClicked);
connect(this, &ConnectionViewWidget::setEnabledRemoveButton, buttons.constLast(), &QWidget::setEnabled);

View File

@@ -38,6 +38,8 @@ namespace QmlDesigner {
namespace Ui { class ConnectionViewWidget; }
class ActionEditor;
namespace Internal {
class BindingModel;
@@ -88,6 +90,9 @@ signals:
void setEnabledAddButton(bool enabled);
void setEnabledRemoveButton(bool enabled);
protected:
void contextMenuEvent(QContextMenuEvent *event) override;
private:
void handleTabChanged(int i);
void removeButtonClicked();
@@ -95,6 +100,7 @@ private:
private:
Ui::ConnectionViewWidget *ui;
QmlDesigner::ActionEditor *m_actionEditor;
};
} // namespace Internal

View File

@@ -52,7 +52,7 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
box->addWidget(splitter);
setLayout(box);
connect(m_tree, &TreeView::curvesSelected, m_view, &GraphicsView::reset);
connect(m_tree->selectionModel(), &SelectionModel::curvesSelected, m_view, &GraphicsView::reset);
}
void CurveEditor::zoomX(double zoom)
@@ -67,7 +67,7 @@ void CurveEditor::zoomY(double zoom)
void CurveEditor::clearCanvas()
{
m_view->reset(m_tree->selection());
m_view->reset({});
}
QToolBar *CurveEditor::createToolBar()

View File

@@ -14,6 +14,7 @@ HEADERS += \
$$PWD/detail/keyframeitem.h \
$$PWD/detail/playhead.h \
$$PWD/detail/selectableitem.h \
$$PWD/detail/selectionmodel.h \
$$PWD/detail/selector.h \
$$PWD/detail/shortcut.h \
$$PWD/detail/treeitemdelegate.h \
@@ -36,6 +37,7 @@ SOURCES += \
$$PWD/detail/keyframeitem.cpp \
$$PWD/detail/playhead.cpp \
$$PWD/detail/selectableitem.cpp \
$$PWD/detail/selectionmodel.cpp \
$$PWD/detail/selector.cpp \
$$PWD/detail/shortcut.cpp \
$$PWD/detail/treeitemdelegate.cpp \

View File

@@ -26,6 +26,7 @@
#include "curveeditormodel.h"
#include "treeitem.h"
#include "detail/graphicsview.h"
#include "detail/selectionmodel.h"
namespace DesignTools {
@@ -53,7 +54,9 @@ void CurveEditorModel::setCurve(unsigned int id, const AnimationCurve &curve)
void CurveEditorModel::reset(const std::vector<TreeItem *> &items)
{
std::vector<TreeItem::Path> sel = selection();
std::vector<TreeItem::Path> sel;
if (SelectionModel *sm = selectionModel())
sel = sm->selectedPaths();
beginResetModel();
@@ -67,7 +70,8 @@ void CurveEditorModel::reset(const std::vector<TreeItem *> &items)
endResetModel();
select(sel);
if (SelectionModel *sm = selectionModel())
sm->select(sel);
}
} // End namespace DesignTools.

View File

@@ -81,6 +81,8 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
, m_playheadColor(new ColorControl(style.playhead.color))
{
setWindowFlag(Qt::Tool, true);
m_canvasMargin->setValue(style.canvasMargin);
m_zoomInWidth->setValue(style.zoomInWidth);
m_zoomInHeight->setValue(style.zoomInHeight);

View File

@@ -339,6 +339,8 @@ void CurveItem::setInterpolation(Keyframe::Interpolation interpolation)
void CurveItem::connect(GraphicsScene *scene)
{
QObject::connect(this, &CurveItem::curveChanged, scene, &GraphicsScene::curveChanged);
for (auto *frame : m_keyframes) {
QObject::connect(frame, &KeyframeItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
QObject::connect(frame, &KeyframeItem::handleMoved, scene, &GraphicsScene::handleMoved);

View File

@@ -68,11 +68,8 @@ void GraphicsScene::addCurveItem(CurveItem *item)
{
m_dirty = true;
item->setDirty(false);
connect(item, &CurveItem::curveChanged, this, &GraphicsScene::curveChanged);
addItem(item);
item->connect(this);
addItem(item);
}
void GraphicsScene::setComponentTransform(const QTransform &transform)

View File

@@ -554,7 +554,7 @@ double GraphicsView::timeLabelInterval(QPainter *painter, double maxTime)
double tickDistance = mapTimeToX(deltaTime);
while (true) {
if (tickDistance == 0 && deltaTime > maxTime)
if (tickDistance == 0 && deltaTime >= maxTime)
return maxTime;
if (tickDistance > minTextSpacing)

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