add propertyEditor library

This commit is contained in:
baiYue 2026-01-27 16:36:59 +08:00
parent cf2b5278f3
commit 5592c20747
89 changed files with 4854 additions and 5 deletions

View File

@ -1,9 +1,14 @@
cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0079 NEW)
if(POLICY CMP0079)
cmake_policy(GET CMP0079 policy_status)
message(STATUS "CMP0079 策略状态: ${policy_status}")
else()
message(STATUS "CMP0079 策略不可用")
endif()
project(DiagramDesigner LANGUAGES CXX VERSION 1.0)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
@ -14,7 +19,6 @@ find_package(Qt6 REQUIRED COMPONENTS SvgWidgets)
find_package(Qt6 COMPONENTS Network WebSockets REQUIRED)
find_package(PostgreSQL REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# ui .h
set(CMAKE_AUTOUIC_SEARCH_PATHS "ui")
@ -140,6 +144,7 @@ include_directories(common/include)
include_directories(${PostgreSQL_INCLUDE_DIRS})
target_include_directories(DiagramDesigner PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_include_directories(DiagramDesigner PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/PropertyEditor/source/include)")
target_link_libraries(DiagramDesigner PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
@ -162,9 +167,12 @@ set_target_properties(DiagramDesigner PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin"
)
target_link_libraries(DiagramDesigner PRIVATE diagramCavas diagramUtils diagramCommunication)
add_subdirectory(PropertyEditor)
add_subdirectory(diagramCavas)
add_subdirectory(diagramUtils)
add_subdirectory(diagramCommunication)
target_link_libraries(DiagramDesigner PRIVATE PropertyEditor)
target_link_libraries(DiagramDesigner PRIVATE diagramCavas diagramUtils diagramCommunication)
file(COPY setting.xml DESTINATION "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin")

View File

@ -0,0 +1,72 @@
cmake_minimum_required(VERSION 3.12)
project(PropertyEditor CXX)
find_package(Qt6 COMPONENTS Core Widgets Gui QuickWidgets QuickTemplates2 QuickControls2 REQUIRED)
qt6_add_resources(QRC_FILE resources.qrc)
file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/*.h ${CMAKE_CURRENT_SOURCE_DIR}/source/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/resources/*)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE})
#if(BUILD_SHARED_LIBS)
# add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE})
# target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_SHARED_LIBRARY)
# message(STATUS "[CMake] Building as SHARED library")
# message(STATUS "[CMake] Defined: PROPERTY_EDITOR_SHARED_LIBRARY")
#else()
# add_library(PropertyEditor STATIC ${PROJECT_SOURCE} ${QRC_FILE})
# target_compile_definitions(PropertyEditor PUBLIC PROPERTY_EDITOR_STATIC_LIBRARY)
# message(STATUS "[CMake] Building as STATIC library")
# message(STATUS "[CMake] Defined: PROPERTY_EDITOR_STATIC_LIBRARY")
#endif()
add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE})
target_compile_definitions(PropertyEditor
PUBLIC
DIAGRAM_DESIGNER_SHARED
PRIVATE
DIAGRAM_DESIGNER_EXPORTS
#QT_NO_KEYWORDS
)
set_property(TARGET PropertyEditor PROPERTY AUTOMOC ON)
set_property(TARGET PropertyEditor PROPERTY USE_FOLDERS ON)
set_property(TARGET PropertyEditor PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files")
target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_LIBRARY)
target_link_libraries(PropertyEditor PUBLIC
Qt::Gui
Qt::GuiPrivate
Qt::Widgets
Qt::WidgetsPrivate
Qt::QuickWidgets
Qt::QuickPrivate
Qt::QuickTemplates2
Qt::QuickTemplates2Private
Qt::QuickControls2
)
target_include_directories(PropertyEditor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/source/include)
file(GLOB_RECURSE PUBLIC_FILES LIST_DIRECTORIES TRUE ${CMAKE_CURRENT_SOURCE_DIR}/source/include/*)
foreach(PUBLIC_FILE ${PUBLIC_FILES})
if(IS_DIRECTORY ${PUBLIC_FILE})
target_include_directories(PropertyEditor PRIVATE ${PUBLIC_FILE})
endif()
endforeach()
set_target_properties(
PropertyEditor
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib"
)
if(PROJECT_IS_TOP_LEVEL)
add_subdirectory(example)
endif()

162
PropertyEditor/README.md Normal file
View File

@ -0,0 +1,162 @@
# QDetailsView
[中文用户?点我查看中文介绍](README_zh.md)
Inspired by the details view of Unreal Engine, QDetailsView leverages Qt's reflection system to easily build property editors for qobject.
Its core features are:
- Create type-based control editors that automatically organize the editor layout according to the reflection structure of the object.
- Utilize QML GPU rendering and control management based on the preview view.
## Usage
It is extremely easy to use—simply declare the meta-properties of a `QObject` using **`Q_PROPERTY(...)`**:
```c++
class QCustomObject : public QObject {
Q_OBJECT
Q_PROPERTY(int Int READ getInt WRITE setInt)
Q_PROPERTY(float Float READ getFloat WRITE setFloat)
...
};
```
```c++
QCustomObject obj;
QDetailsView view;
view.setObject(&obj);
view.show();
```
You will get the following result:
![image-20250826114654194](resources/image-20250826114654194.png)
## Customization
### About QPropertyHandle
`QPropertyHandle` serves as the unified entry point for QDetailsView to manipulate properties. It is typically constructed via the following interface:
```c++
static QPropertyHandle* QPropertyHandle::FindOrCreate(
QObject* inParent, // Parent object that manages the lifecycle of the PropertyHandle
QMetaType inType, // Meta-type of the property
QString inPropertyPath, // Path field of the property
Getter inGetter, // Getter function for the property
Setter inSetter // Setter function for the property
);
```
To ensure that changes to property values are detected by DetailsView, all value modifications must use the interface provided by PropertyHandle:
```c++
QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName");
if (handle) {
handle->setVar(QVariant::fromValue(newValue));
}
```
When creating a `QPropertyHandle`, you must specify a `parent`—the handle will be attached to the parent as a child object. Thus, its lifecycle is tied to the parent object. To clean it up, call:
```c++
static void QPropertyHandle::Cleanup(QObject* inParent);
```
### Custom Enum
For enum types, they must be defined within a class and declared using **`Q_ENUM(...)`**:
```c++
class QCustomObject : public QObject {
Q_OBJECT
public:
enum QCustomEnum {
One,
Two,
Three
};
Q_ENUM(QCustomEnum);
};
```
### Custom Type Editor
For custom types that do not inherit from `QObject`, you first need to declare the type using the macro **`Q_DECLARE_METATYPE(...)`** during definition.
For specific types that require only a single editor control, you can directly register the type editor using the following interface:
```c++
QQuickDetailsViewManager::Get()->registerTypeEditor(
metaType,
[](QPropertyHandle* handle, QQuickItem* parent) -> QQuickItem* {
// Implementation of the editor creation logic
}
);
```
The source code directory `QQuickDetailsViewBasicTypeEditor.cpp` contains many reference examples, such as the editor for `QDir`:
```c++
registerTypeEditor(
QMetaType::fromType<QDir>(),
[](QPropertyHandle* handle, QQuickItem* parent) -> QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
DirectorySelector {
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
}
);
```
For editor controls that span multiple rows or have complex behaviors, you can extend the property editor by deriving from `IPropertyTypeCustomization`. This class provides two virtual functions:
```c++
class IPropertyTypeCustomization : public QEnableSharedFromThis<IPropertyTypeCustomization>
{
public:
// Used to assemble the editor for the current property row
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
// Used to extend child items for the current property row
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
};
```
After implementing the derived class, register it using the following interface:
```c++
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
```
## TODO
- Undo/redo
- Container operations
- Extended meta-data support

159
PropertyEditor/README_zh.md Normal file
View File

@ -0,0 +1,159 @@
# QDetailsView
QDetailsView 受虚幻引擎的属性面板所启发借助Qt的反射系统可以轻易地搭建对象的属性编辑器。
其核心在于:
- 编写基于类型的控件编辑器,自动根据对象的反射结构来组织编辑器布局。
- 使用QML GPU渲染基于预览视图的控件管理。
## 使用
它的使用非常简单,只需要使用 **Q_PROPERTY(...) ** 声明 `QObject` 的元属性:
``` c++
class QCustomObject :public QObject {
Q_OBJECT
Q_PROPERTY(int Int READ getInt WRITE setInt)
Q_PROPERTY(float Float READ getFloat WRITE setFloat)
...
};
```
然后创建 `QDetailsView`并指定Object
``` c++
QCustomObject obj;
QDetailsView view;
view.setObject(&obj);
view.show();
```
你就能得到:
![image-20250826114654194](resources/image-20250826114654194.png)
## 定制化
### 关于 QPropertyHandle
`QPropertyHandle` 是QDetailsView操作属性的统一入口它通常通过如下接口构建
``` c++
static QPropertyHandle* QPropertyHandle::FindOrCreate(
QObject* inParent, // 父对象它会持有PropertyHandle周期
QMetaType inType, // 该属性的元类型
QString inPropertyPath, // 该属性的路径字段
Getter inGetter, // 该属性的获取器
Setter inSetter // 该属性的设置器
);
```
为了保证属性值的变动可以被DetailsView响应所有对值的修改请统一使用PropertyHandle的接口
``` C++
QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName");
if(handle){
handle->setVar(QVariant::fromValue(newValue));
}
```
`QPropertyHandle`创建时要求指定`parent`,它将会作为子对象挂载上去,因此它的生命周期将跟随父对象,如果要清理,请调用:
``` c++
static void QPropertyHandle::Cleanup(QObject* inParent);
```
### 自定义枚举
对于枚举类型,需要使用类内定义的方式,并通过**Q_ENUM(...)**声明:
``` c++
class QCustomObject: public QObject {
Q_OBJECT
public:
enum QCustomEnum {
One,
Two,
Three
};
Q_ENUM(QCustomEnum);
};
```
### 自定义类型编辑器
非`QObject`的自定义类型,首先需要在定义类型时使用宏**Q_DECLARE_METATYPE(...)**进行声明。
对于只有单个编辑器控件的特定类型,可以使用如下接口直接注册类型编辑器:
``` c++
QQuickDetailsViewManager::Get()->registerTypeEditor(
metaType,
[](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
}
)
```
在源代码的`QQuickDetailsViewBasicTypeEditor.cpp`目录下有许多参考示例比如QDir
``` c++
registerTypeEditor(
QMetaType::fromType<QDir>(),
[](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
DirectorySelector{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
}
);
```
对于具有多行或者行为较为复杂的编辑器控件,可以通过派生`IPropertyTypeCustomization`来扩展属性编辑器,它提供两个虚函数:
``` c++
class IPropertyTypeCustomization :public QEnableSharedFromThis<IPropertyTypeCustomization>
{
public:
// 用于装配当前属性行的编辑器
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
// 用于扩展当前属性行的子项
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
};
```
之后使用如下接口进行注册:
``` c++
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
```
## TODO
- 撤销重做
- 容器操作
- 扩展元信息

View File

@ -0,0 +1,29 @@
file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE})
add_executable(PropertyEditorExample
${PROJECT_SOURCE}
)
set_property(TARGET PropertyEditorExample PROPERTY AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS QuickControls2)
target_link_libraries(PropertyEditorExample PUBLIC PropertyEditor
Qt::Widgets
Qt::QuickWidgets
Qt::QuickPrivate
Qt::QuickTemplates2
Qt::QuickTemplates2Private
Qt::QuickControls2
)
set_target_properties(
PropertyEditorExample
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
)

View File

@ -0,0 +1,18 @@
#ifndef CommonInclude_h__
#define CommonInclude_h__
#include <QObject>
#include <QSharedPointer>
#include <QDebug>
#include <QMetaType>
#define Q_PROPERTY_VAR(Type,Name)\
Q_PROPERTY(Type Name READ get##Name WRITE set##Name) \
Type get##Name(){ return Name; } \
void set##Name(Type var){ \
Name = var; \
qDebug() <<"Set" <<this <<#Name <<": " <<var; \
} \
Type Name
#endif // CommonInclude_h__

View File

@ -0,0 +1,20 @@
#ifndef CustomGadget_h__
#define CustomGadget_h__
#include "CommonInclude.h"
class QCustomGadget {
Q_GADGET
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
public:
Q_PROPERTY_VAR(double, LimitedDouble) = 1;
Q_PROPERTY_VAR(QString, Desc) = "This is inline Gadget";
};
static QDebug operator<<(QDebug debug, const QCustomGadget& gadget) {
return debug << gadget.LimitedDouble << gadget.Desc;
}
Q_DECLARE_METATYPE(QSharedPointer<QCustomGadget>);
#endif // CustomGadget_h__

View File

@ -0,0 +1,48 @@
#ifndef CustomObject_h__
#define CustomObject_h__
#include <QObject>
#include <qvectornd.h>
#include <QFile>
#include <QDir>
#include <QColor>
#include <QMatrix4x4>
#include "CommonInclude.h"
#include "CustomGadget.h"
#include "CustomType.h"
class QCustomObject :public QObject {
Q_OBJECT
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
public:
enum QCustomEnum {
One,
Two,
Three
};
Q_ENUM(QCustomEnum);
Q_PROPERTY_VAR(int, Int) = 0;
Q_PROPERTY_VAR(float, Float) = 1.23f;
Q_PROPERTY_VAR(double, LimitedDouble) = 5;
Q_PROPERTY_VAR(QString, String);
Q_PROPERTY_VAR(QDir, Directory) = QDir(".");
Q_PROPERTY_VAR(QVector2D, Vec2) = QVector2D(1, 2);
Q_PROPERTY_VAR(QVector3D, Vec3) = QVector3D(1, 2, 3);
Q_PROPERTY_VAR(QVector4D, Vec4) = QVector4D(1, 2, 3, 4);
Q_PROPERTY_VAR(QMatrix4x4, Mat4);
Q_PROPERTY_VAR(QColor, Color);
Q_PROPERTY_VAR(QList<QColor>, ColorList) = { Qt::red,Qt::green,Qt::blue };
typedef QMap<QString, QColor> StringColorMap;
Q_PROPERTY_VAR(StringColorMap, ColorMap) = { {"Red",Qt::red},{"Green",Qt::green},{"Blue",Qt::blue} };
Q_PROPERTY_VAR(QCustomEnum, CustomEnum) = QCustomEnum::One;
Q_PROPERTY_VAR(QCustomType, CustomType);
Q_PROPERTY_VAR(QCustomGadget, CustomGadget);
Q_PROPERTY_VAR(QCustomGadget*, CustomGadgetPtr) = new QCustomGadget;
Q_PROPERTY_VAR(QSharedPointer<QCustomGadget>, CustomGadgetSharedPtr) = QSharedPointer<QCustomGadget>::create();
Q_PROPERTY_VAR(QCustomObject*, SubCustomObject) = nullptr;
};
#endif // CustomObject_h__

View File

@ -0,0 +1,18 @@
#ifndef CustomType_h__
#define CustomType_h__
#include <QVector>
#include <QMetaType>
struct QCustomType {
unsigned int ArraySize = 0;
QVector<int> Array;
};
static QDebug operator<<(QDebug debug, const QCustomType& it) {
return debug << it.Array;
}
Q_DECLARE_METATYPE(QCustomType)
#endif // CustomType_h__

View File

@ -0,0 +1,73 @@
#include "PropertyTypeCustomization_CustomType.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QPropertyHandle.h"
#include <QMetaType>
#include <QObject>
#include <QRandomGenerator>
#include "QQuickDetailsViewModel.h"
#include "CustomType.h"
#include "QQuickFunctionLibrary.h"
void PropertyTypeCustomization_CustomType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
{
auto editorSlot = inBuilder->makeNameValueSlot();
inPropertyHandle->setupNameEditor(editorSlot.first);
auto buttonItem = inBuilder->setupItem(editorSlot.second, R"(
import QtQuick;
import QtQuick.Controls;
Button{
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: 80
height: 20
text: "Sort"
}
)");
QQuickFunctionLibrary::connect(buttonItem, SIGNAL(clicked()), inPropertyHandle, [inPropertyHandle]() {
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
std::sort(customType.Array.begin(), customType.Array.end());
inPropertyHandle->setVar(QVariant::fromValue(customType));
if (auto arrayHandle = inPropertyHandle->findChild("Array")) {
}
});
}
void PropertyTypeCustomization_CustomType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
auto arrayHandle = inPropertyHandle->findOrCreateChild(
QMetaType::fromType<QVector<int>>(),
"Array",
[inPropertyHandle]() {
return QVariant::fromValue(inPropertyHandle->getVar().value<QCustomType>().Array);
},
[inPropertyHandle](QVariant var) {
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
customType.Array = var.value<QVector<int>>();
inPropertyHandle->setVar(QVariant::fromValue(customType));
}
);
auto arraySizeHandle = inPropertyHandle->findOrCreateChild(
QMetaType::fromType<unsigned int>(),
"ArraySize",
[inPropertyHandle]() {
return inPropertyHandle->getVar().value<QCustomType>().ArraySize;
},
[inPropertyHandle, arrayHandle](QVariant var) {
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
customType.ArraySize = var.toUInt();
customType.Array.resize(customType.ArraySize);
for (int i = 0; i < customType.ArraySize; ++i) {
customType.Array[i] = QRandomGenerator::global()->bounded(-100000, 100000);
}
inPropertyHandle->setVar(QVariant::fromValue(customType));
arrayHandle->invalidateStructure();
}
);
inBuilder->addProperty(arraySizeHandle);
inBuilder->addProperty(arrayHandle);
}

View File

@ -0,0 +1,12 @@
#ifndef PropertyTypeCustomization_CustomType_h__
#define PropertyTypeCustomization_CustomType_h__
#include "IPropertyTypeCustomization.h"
class PropertyTypeCustomization_CustomType : public IPropertyTypeCustomization {
protected:
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override;
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
};
#endif // PropertyTypeCustomization_CustomType_h__

View File

@ -0,0 +1,20 @@
#include <QApplication>
#include "QDetailsView.h"
#include "CustomObject.h"
#include "CustomType.h"
#include "PropertyTypeCustomization_CustomType.h"
#include "QQuickDetailsViewMananger.h"
int main(int argc, char** argv) {
QApplication app(argc, argv);
QCustomObject obj;
obj.setSubCustomObject(new QCustomObject);
obj.getSubCustomObject()->setSubCustomObject(new QCustomObject);
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
QDetailsView view;
view.setObject(&obj);
view.show();
return app.exec();
}

View File

@ -0,0 +1,27 @@
<RCC>
<qresource prefix="/">
<file>resources/Icon/arrow-left-l.png</file>
<file>resources/Icon/arrow-right-l.png</file>
<file>resources/Icon/close.png</file>
<file>resources/Icon/delete.png</file>
<file>resources/Icon/down.png</file>
<file>resources/Icon/expand.png</file>
<file>resources/Icon/plus.png</file>
<file>resources/Icon/reset.png</file>
<file>resources/Icon/unexpand.png</file>
<file>resources/Icon/up.png</file>
<file>resources/Qml/ValueEditor/ColorBox.qml</file>
<file>resources/Qml/ValueEditor/NumberBox.qml</file>
<file>resources/Qml/ValueEditor/TextComboBox.qml</file>
<file>resources/Qml/ValueEditor/Vec2Box.qml</file>
<file>resources/Qml/ValueEditor/Vec3Box.qml</file>
<file>resources/Qml/ValueEditor/Vec4Box.qml</file>
<file>resources/Qml/ColorPalette/ColorPalette.qml</file>
<file>resources/Qml/ColorPalette/ColorPalette_Dark.qml</file>
<file>resources/Qml/ColorPalette/ColorPalette_Light.qml</file>
<file>resources/Qml/ValueEditor/DirectorySelector.qml</file>
<file>resources/Qml/ValueEditor/FileSelector.qml</file>
<file>resources/Qml/ValueEditor/LineTextBox.qml</file>
<file>resources/Qml/ValueEditor/MultiLineTextBox.qml</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,9 @@
import QtQuick
pragma Singleton
QtObject {
id: colorPalette
property var theme : ColorPalette_Light
}

View File

@ -0,0 +1,27 @@
import QtQuick
pragma Singleton
QtObject {
property color labelPrimary : "#FFFFFF"
property color textPrimary: "#EEEEEE"
property color textBoxBackground: "#444444"
property color rowBackground: "#333333"
property color rowBackgroundHover: "#888888"
property color rowBorder: "#666666"
property color rowIndicator: "#CCCCCC"
property color rowSplitter: "#666666"
property color rowShadowStart: "#00000000"
property color rowShadowEnd: "#66000000"
property color boxHover: "#A478DB"
property color comboBoxBackground: "#444444"
property color comboBoxItemBackground: "#666666"
property color comboBoxItemBackgroundHover: "#888888"
property color buttonBackground: "#111111"
}

View File

@ -0,0 +1,27 @@
import QtQuick
pragma Singleton
QtObject {
property color labelPrimary : "#444444"
property color textPrimary: "#666666"
property color textBoxBackground: "#f8fef9"
property color rowBackground: "white"
property color rowBackgroundHover: "#F3F3F3"
property color rowBorder: "#EEEEEE"
property color rowIndicator: "#cef9ce"
property color rowSplitter: "#EEEEEE"
property color rowShadowStart: "#00000000"
property color rowShadowEnd: "#14000000"
property color boxHover: "#cef9ce"
property color comboBoxBackground: "#f8fef9"
property color comboBoxItemBackground: "#FFFFFF"
property color comboBoxItemBackgroundHover: "#cef9ce"
property color buttonBackground: "#cef9ce"
}

View File

@ -0,0 +1,40 @@
import QtQuick;
import QtQuick.Controls;
import QtQuick.Dialogs
Item{
id: control
property color value
implicitHeight: 25
signal asValueChanged(text:var)
function setValue(newValue:var){
if(newValue !== value){
value = newValue
asValueChanged(value)
}
}
Button{
anchors.margins: 2
anchors.fill: parent
palette {
button: value
}
background: Rectangle {
color: value
}
onClicked: {
colorDialog.open()
}
}
ColorDialog {
id: colorDialog
selectedColor: value
onAccepted: {
control.setValue(selectedColor)
}
}
}

View File

@ -0,0 +1,63 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtCore
import ColorPalette
Item{
id: control
property var value
implicitHeight: dirBox.implicitHeight
signal asValueChanged(text: var)
function setValue(newValue: var){
if(newValue !== value){
value = newValue
dirBox.value = value
asValueChanged(value)
}
}
LineTextBox {
id: dirBox
value: control.value
anchors.left: parent.left
anchors.right: button.left
anchors.verticalCenter: parent.verticalCenter
onValueChanged: {
control.setValue(value)
}
}
Button {
id: button
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 30
height: 25
text: "..."
palette.buttonText: ColorPalette.theme.textPrimary
background: Rectangle {
color: ColorPalette.theme.buttonBackground
}
onClicked: {
folderDialog.open()
}
}
FolderDialog {
id: folderDialog
title: "选择目录"
onAccepted: {
var filePath = currentFolder.toString();
if (filePath.startsWith("file:///")) {
filePath = filePath.substring(8);
}
control.setValue(filePath);
}
}
}

View File

@ -0,0 +1,39 @@
import QtQuick;
import QtQuick.Controls;
import QtQuick.Dialogs
Item{
id: control
property var value
implicitHeight: 25
signal asValueChanged(text:var)
function setValue(newValue:var){
if(newValue !== value){
value = newValue
asValueChanged(value)
}
}
Button{
anchors.margins: 2
anchors.fill: parent
palette {
button: value
}
background: Rectangle {
color: value
}
onClicked: {
colorDialog.open()
}
}
ColorDialog {
id: colorDialog
selectedColor: value
onAccepted: {
control.setValue(selectedColor)
}
}
}

View File

@ -0,0 +1,72 @@
import QtQuick;
import QtQuick.Controls;
import ColorPalette
Item{
id: control
property var value
implicitHeight: lineEditor.implicitHeight + 2
signal asValueChanged(text:var)
function setValue(newText:var){
if(newText !== value){
value = newText
asValueChanged(value)
}
}
Rectangle {
anchors.fill: parent
border.color: ColorPalette.theme.textBoxBackground
color: ColorPalette.theme.textBoxBackground
border.width: 1
clip: true
TextInput{
id: lineEditor
enabled: true
clip: true
padding : 3
anchors.fill: parent
anchors.leftMargin: 2
anchors.rightMargin: 2
text: control.value
color: ColorPalette.theme.textPrimary
wrapMode: TextInput.WordWrap
verticalAlignment: Text.AlignVCenter
onEditingFinished:{
setValue(lineEditor.text)
}
}
MouseArea{
id: hoverArea
hoverEnabled: true
propagateComposedEvents: true
anchors.fill: parent
onEntered:{
exitAnimation.stop()
enterAnimation.start()
hoverArea.cursorShape = Qt.IBeamCursor
}
onExited:{
enterAnimation.stop()
exitAnimation.start()
hoverArea.cursorShape = Qt.ArrowCursor
}
onPressed: (mouse)=> mouse.accepted = false
onReleased:(mouse)=> mouse.accepted = false
onClicked:(mouse)=> mouse.accepted = false
onDoubleClicked:(mouse)=> mouse.accepted = false
}
ColorAnimation on border.color{
id: enterAnimation
to: ColorPalette.theme.boxHover
duration: 100
running: false
}
ColorAnimation on border.color{
id: exitAnimation
to: ColorPalette.theme.textBoxBackground
duration: 100
running: false
}
}
}

View File

@ -0,0 +1,83 @@
import QtQuick;
import QtQuick.Controls;
import ColorPalette
Item{
id: control
property var value
implicitHeight: lineEditor.implicitHeight + 2
signal asValueChanged(text:var)
function setValue(newText:var){
if(newText !== value){
value = newText
asValueChanged(value)
}
}
Rectangle {
anchors.fill: parent
border.color: ColorPalette.theme.textBoxBackground
color: ColorPalette.theme.textBoxBackground
border.width: 1
clip: true
TextArea{
id: lineEditor
enabled: true
clip: true
padding: 3
anchors.fill: parent
anchors.leftMargin: 2
anchors.rightMargin: 2
text: control.value
color: ColorPalette.theme.textPrimary
wrapMode: TextInput.WordWrap
verticalAlignment: Text.AlignVCenter
onEditingFinished:{
setValue(lineEditor.text)
}
Keys.onPressed: {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (event.modifiers & Qt.ShiftModifier) {
// Shift+Enter
} else {
// Enter
event.accepted = true;
lineEditor.editingFinished();
}
}
}
}
MouseArea{
id: hoverArea
hoverEnabled: true
propagateComposedEvents: true
anchors.fill: parent
onEntered:{
exitAnimation.stop()
enterAnimation.start()
hoverArea.cursorShape = Qt.IBeamCursor
}
onExited:{
enterAnimation.stop()
exitAnimation.start()
hoverArea.cursorShape = Qt.ArrowCursor
}
onPressed: (mouse)=> mouse.accepted = false
onReleased:(mouse)=> mouse.accepted = false
onClicked:(mouse)=> mouse.accepted = false
onDoubleClicked:(mouse)=> mouse.accepted = false
}
ColorAnimation on border.color{
id: enterAnimation
to: ColorPalette.theme.boxHover
duration: 100
running: false
}
ColorAnimation on border.color{
id: exitAnimation
to: ColorPalette.theme.textBoxBackground
duration: 100
running: false
}
}
}

View File

@ -0,0 +1,159 @@
import QtQuick;
import QtQuick.Controls;
import ColorPalette
Item{
id: control
implicitHeight: 25
implicitWidth: 100
property bool isLimited: false
property bool isHovered: false
property var min:0
property var max:100
property var number: 0
property real step : 1
property int precision: 3
property bool isMousePressed: false
signal valueChanged(number:var)
function setNumber(value:var){
if(value !== number && !isNaN(value)){
number = value
if(min < max){
if(number>max){
number = max
}
if(number<min){
number = min
}
}
valueChanged(number)
}
}
Rectangle {
anchors.fill: parent
anchors.margins: 2
border.color: ColorPalette.theme.textBoxBackground
border.width: 1
color: ColorPalette.theme.textBoxBackground
clip: true
Rectangle{
visible: isHovered && isLimited
anchors.fill: parent
anchors.rightMargin: parent.width * (max - number) /(max - min)
color: ColorPalette.theme.boxHover
}
TextInput{
id: input
enabled: false
anchors.fill: parent
anchors.leftMargin: 5
anchors.rightMargin: 5
text: helper.numberToString(number,precision)
color: ColorPalette.theme.textPrimary
verticalAlignment: Text.AlignVCenter
validator: DoubleValidator{}
onEditingFinished:{
enabled = false
dragArea.visible = true
dragArea.cursorShape = Qt.SplitHCursor
var newVar = parseFloat(input.text)
if(isNaN(newVar)){
text = helper.numberToString(number,precision)
}
else{
control.setNumber(parseFloat(input.text))
}
}
}
MouseArea{
id: dragArea
property real lastPressX : -1
property real lastPressY : -1
anchors.fill: parent
hoverEnabled : true
cursorShape: Qt.SplitHCursor
onDoubleClicked: {
cursorShape = Qt.ArrowCursor
input.enabled = true
dragArea.visible = false
input.forceActiveFocus()
input.selectAll()
}
onClicked: {
input.forceActiveFocus()
}
onEntered:{
isHovered = true
exitAnimation.stop()
enterAnimation.start()
}
onExited:{
isHovered = false
if(!control.isMousePressed){
enterAnimation.stop()
exitAnimation.start()
}
}
onPressed:
(mouse)=>{
if(mouse.button === Qt.LeftButton){
control.isMousePressed = true
lastPressX = mouse.x
lastPressY = mouse.y
cursorShape = Qt.BlankCursor
}
}
onReleased:
(mouse)=>{
control.isMousePressed = false
lastPressX = -1
lastPressY = -1
cursorShape = Qt.SplitHCursor
if(!isHovered){
enterAnimation.stop()
exitAnimation.start()
}
}
onPositionChanged:
(mouse)=>{
if(!input.enabled && mouse.buttons&Qt.LeftButton){
if(!isLimited){
var offset = mouse.x - lastPressX
setNumber(number + offset * step)
var global = dragArea.mapToGlobal(lastPressX, lastPressY)
var local = dragArea.mapFromGlobal(global.x,global.y)
helper.setCursorPos(global.x,global.y)
}
else{
var xPercent = Math.max(0, Math.min(1, mouse.x / dragArea.width))
var range = max - min
var newValue = min + xPercent * range
control.setNumber(newValue)
const validMouseX = Math.max (0, Math.min (dragArea.width, mouse.x));
const validMouseY = Math.max (0, Math.min (dragArea.height, mouse.y));
if (mouse.x !== validMouseX || mouse.y !== validMouseY) {
const validGlobalPos = dragArea.mapToGlobal (validMouseX, validMouseY);
helper.setCursorPos (validGlobalPos.x, validGlobalPos.y);
}
}
}
}
}
ColorAnimation on border.color{
id: enterAnimation
to: ColorPalette.theme.boxHover
duration: 100
running: false
}
ColorAnimation on border.color{
id: exitAnimation
to: ColorPalette.theme.textBoxBackground
duration: 100
running: false
}
}
}

View File

@ -0,0 +1,103 @@
import QtQuick;
import QtQuick.Controls;
import Qt5Compat.GraphicalEffects
import ColorPalette
Item{
id: editor
property var value
property var model
implicitHeight: 25
signal asValueChanged(value:var)
function setValue(newValue:var){
if(newValue !== value){
value = newValue
asValueChanged(value)
}
}
ComboBox {
id: control
anchors.margins: 2
anchors.fill: parent
model: editor.model
onCurrentTextChanged: {
setValue(currentText)
}
delegate: ItemDelegate {
id: itemDelegate
width: control.width
height: 25
padding: 5
background: Rectangle {
color: itemDelegate.highlighted ? ColorPalette.theme.comboBoxItemBackgroundHover
: itemDelegate.hovered ? ColorPalette.theme.comboBoxItemBackgroundHover
: ColorPalette.theme.comboBoxItemBackground
Behavior on color {
ColorAnimation { duration: 100 }
}
}
contentItem: Text {
text: modelData
color: ColorPalette.theme.textPrimary
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter //
horizontalAlignment: Text.AlignLeft //
padding: 0
}
highlighted: control.highlightedIndex === index
required property int index
required property var modelData
}
indicator: Image {
id: indicator
x: control.width - width/2 - control.rightPadding
y: control.topPadding + (control.availableHeight - height) / 2
width: 13
height: 13
mipmap: true
source: "qrc:/resources/Icon/expand.png"
ColorOverlay {
anchors.fill: parent
source: parent
color: ColorPalette.theme.rowIndicator
opacity: 1.0
}
}
contentItem: Text {
leftPadding: 3
rightPadding: control.indicator.width + control.spacing
text: control.displayText
font: control.font
color: ColorPalette.theme.textPrimary
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
color: ColorPalette.theme.comboBoxBackground
}
popup: Popup {
y: control.height
width: control.width
implicitHeight: contentItem.implicitHeight + 5
padding : 2
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
color: ColorPalette.theme.comboBoxBackground
}
}
}
}

View File

@ -0,0 +1,44 @@
import QtQuick;
import QtQuick.Controls;
import QtQuick.Layouts;
Item{
id: control
property vector2d value
implicitHeight: 25
signal asValueChanged(value:var)
function setValue(newValue:var){
if(value !== newValue){
value = newValue
asValueChanged(value)
}
}
RowLayout{
anchors.fill: parent
NumberBox{
id: xBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.x
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector2d(number, control.value.y))
}
}
}
NumberBox{
id: yBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.y
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector2d(control.value.x, number))
}
}
}
Item {
Layout.fillWidth: true
}
}
}

View File

@ -0,0 +1,55 @@
import QtQuick;
import QtQuick.Controls;
import QtQuick.Layouts;
Item{
id: control
property vector3d value
implicitHeight: 25
signal asValueChanged(value:var)
function setValue(newValue:var){
if(value !== newValue){
value = newValue
asValueChanged(value)
}
}
RowLayout{
anchors.fill: parent
NumberBox{
id: xBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.x
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector3d(number, control.value.y, control.value.z))
}
}
}
NumberBox{
id: yBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.y
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector3d(control.value.x, number, control.value.z))
}
}
}
NumberBox{
id: zBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.z
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector3d(control.value.x, control.value.y, number))
}
}
}
Item {
Layout.fillWidth: true
}
}
}

View File

@ -0,0 +1,66 @@
import QtQuick;
import QtQuick.Controls;
import QtQuick.Layouts;
Item{
id: control
property vector4d value
implicitHeight: 25
signal asValueChanged(value:var)
function setValue(newValue:var){
if(value !== newValue){
value = newValue
asValueChanged(value)
}
}
RowLayout{
anchors.fill: parent
NumberBox{
id: xBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.x
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector4d(number, control.value.y, control.value.z, control.value.w))
}
}
}
NumberBox{
id: yBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.y
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector4d(control.value.x, number, control.value.z, control.value.w))
}
}
}
NumberBox{
id: zBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.z
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector4d(control.value.x, control.value.y, number, control.value.w))
}
}
}
NumberBox{
id: wBox
width: parent.width/4
Layout.alignment: Qt.AlignLeft
number: value.w
onNumberChanged: {
if (control.value) {
control.setValue(Qt.vector4d(control.value.x, control.value.y, control.value.z, number))
}
}
}
Item {
Layout.fillWidth: true
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -0,0 +1,18 @@
#ifndef IPropertyTypeCustomization_h__
#define IPropertyTypeCustomization_h__
#include <QSharedPointer>
#include "export.hpp"
class QPropertyHandle;
class QQuickDetailsViewRowBuilder;
class QQuickDetailsViewLayoutBuilder;
class DIAGRAM_DESIGNER_PUBLIC IPropertyTypeCustomization :public QEnableSharedFromThis<IPropertyTypeCustomization>
{
public:
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
};
#endif // IPropertyTypeCustomization_h__

View File

@ -0,0 +1,33 @@
#ifndef IPropertyHandleImpl_h__
#define IPropertyHandleImpl_h__
#include <QQmlContext>
#include <QQuickItem>
#include <QVariant>
#include <QObject>
#include "export.hpp"
class QPropertyHandle;
class IPropertyHandleImpl{
friend class QPropertyHandle;
public:
enum Type {
Null,
RawType,
Associative,
Sequential,
Enum,
Object,
};
protected:
IPropertyHandleImpl(QPropertyHandle* inHandle);
virtual QQuickItem* createNameEditor(QQuickItem* inParent);
virtual QQuickItem* createValueEditor(QQuickItem* inParent)= 0;
virtual Type type() { return Type::Null; };
protected:
QPropertyHandle* mHandle;
};
#endif // IPropertyHandleImpl_h__

View File

@ -0,0 +1,27 @@
#ifndef QAssociativePropertyHandle_h__
#define QAssociativePropertyHandle_h__
#include "QMetaContainer"
#include "IPropertyHandleImpl.h"
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Associative: public IPropertyHandleImpl {
public:
QPropertyHandleImpl_Associative(QPropertyHandle* inHandle);
const QMetaAssociation& metaAssociation() const;
void appendItem(QString inKey, QVariant inValue);
bool renameItem(QString inSrc, QString inDst);
void removeItem(QString inKey);
protected:
Type type() override { return Type::Associative; };
QQuickItem* createValueEditor(QQuickItem* inParent)override;
private:
QMetaAssociation mMetaAssociation;
};
#endif // QAssociativePropertyHandle_h__

View File

@ -0,0 +1,20 @@
#ifndef QPropertyHandleImpl_Enum_h__
#define QPropertyHandleImpl_Enum_h__
#include "IPropertyHandleImpl.h"
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Enum: public IPropertyHandleImpl {
public:
QPropertyHandleImpl_Enum(QPropertyHandle* inHandle);
protected:
QQuickItem* createValueEditor(QQuickItem* inParent) override;
Type type() override { return Type::Enum; };
private:
QHash<QString, int> mNameToValueMap;
QList<QString> mKeys;
};
#endif // QPropertyHandleImpl_Enum_h__

View File

@ -0,0 +1,27 @@
#ifndef QPropertyHandleImpl_Object_h__
#define QPropertyHandleImpl_Object_h__
#include "IPropertyHandleImpl.h"
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Object : public IPropertyHandleImpl {
public:
QPropertyHandleImpl_Object(QPropertyHandle* inHandle);
QObject* getObject();
void* getGadget();
bool isGadget() const;
QObject* getOwnerObject();
const QMetaObject* getMetaObject() const;
QQuickItem* createValueEditor(QQuickItem* inParent)override;
Type type() override { return Type::Object; };
void refreshObjectPtr();
QVariant& getObjectHolder();
private:
QVariant mObjectHolder;
void* mObjectPtr = nullptr;
QObject* mOwnerObject = nullptr;
const QMetaObject* mMetaObject = nullptr;
bool bIsSharedPointer = false;
bool bIsPointer = false;
};
#endif // QPropertyHandleImpl_Object_h__

View File

@ -0,0 +1,14 @@
#ifndef QPropertyHandleImpl_RawType_h__
#define QPropertyHandleImpl_RawType_h__
#include "IPropertyHandleImpl.h"
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_RawType : public IPropertyHandleImpl {
public:
QPropertyHandleImpl_RawType(QPropertyHandle* inHandle);
protected:
QQuickItem* createValueEditor(QQuickItem* inParent) override;
Type type() override { return Type::RawType; };
};
#endif // QPropertyHandleImpl_RawType_h__

View File

@ -0,0 +1,26 @@
#ifndef QSequentialPropertyHandle_h__
#define QSequentialPropertyHandle_h__
#include "QMetaContainer"
#include "IPropertyHandleImpl.h"
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Sequential: public IPropertyHandleImpl
{
public:
QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle);
const QMetaSequence& metaSequence() const;
int itemCount();
void appendItem(QVariant InVar);
void moveItem(int InSrcIndex, int InDstIndex);
void removeItem(int InIndex);
protected:
QQuickItem* createValueEditor(QQuickItem* inParent)override;
Type type() override { return Type::Sequential; };
private:
QMetaSequence mMetaSequence;
};
#endif // QSequentialPropertyHandle_h__

View File

@ -0,0 +1,20 @@
#ifndef QDetailsView_h__
#define QDetailsView_h__
#include <QWidget>
#include <QQuickWidget>
#include "QQuickDetailsView.h"
class DIAGRAM_DESIGNER_PUBLIC QDetailsView : public QWidget {
Q_OBJECT
public:
explicit QDetailsView(QWidget* parent = nullptr);
QQuickDetailsView* getQuickDetailsView() const;
void setObject(QObject* inObject);
QObject* getObject() const;
private:
QQuickWidget* mQuickWidget;
QQuickDetailsView* mQuickDetailsView;
};
#endif // QDetailsView_h__

View File

@ -0,0 +1,21 @@
#ifndef QDETAILS_VIEW_API_H
#define QDETAILS_VIEW_API_H
#include <QtCore/qglobal.h>
#ifdef _WIN32
# ifdef PROPERTY_EDITOR_STATIC_LIBRARY
# define QDETAILS_VIEW_API
# else
# ifdef QDETAILS_VIEW_SHARED_LIBRARY
# define QDETAILS_VIEW_API __declspec(dllexport)
# else
# define QDETAILS_VIEW_API __declspec(dllimport)
# endif
# endif
#else
# define QDETAILS_VIEW_API __attribute__((visibility("default")))
#endif
#endif // QDETAILS_VIEW_API_H

View File

@ -0,0 +1,108 @@
#ifndef QPropertyHandle_h__
#define QPropertyHandle_h__
#include <QObject>
#include <QQuickItem>
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
class IPropertyHandleImpl;
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandle : public QObject {
Q_OBJECT
Q_PROPERTY(QVariant Var READ getVar WRITE setVar NOTIFY asVarChanged)
public:
using Getter = std::function<QVariant()>;
using Setter = std::function<void(QVariant)>;
enum PropertyType {
Unknown,
RawType,
Enum,
Sequential,
Associative,
Object
};
static QPropertyHandle* Find(const QObject* inParent, const QString& inPropertyPath);
static QPropertyHandle* FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
static QPropertyHandle* FindOrCreate(QObject* inObject);
static QPropertyHandle* Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
static void Cleanup(QObject* inParent);
QMetaType getType();
PropertyType getPropertyType() const;
QString getName();
QString getPropertyPath();
QString createSubPath(const QString& inSubName);
void invalidateStructure();
Q_INVOKABLE QVariant getVar();
Q_INVOKABLE void setVar(QVariant var);
bool hasMetaData(const QString& inName) const;
QVariant getMetaData(const QString& inName) const;
const QVariantHash& getMetaDataMap() const;
QPropertyHandle* findChild(QString inPropertyName);
QPropertyHandle* findOrCreateChild(QMetaType inType, QString inPropertyName, QPropertyHandle::Getter inGetter, QPropertyHandle::Setter inSetter);
QQuickItem* setupNameEditor(QQuickItem* inParent);
QQuickItem* steupValueEditor(QQuickItem* inParent);
IPropertyHandleImpl::Type type();
QPropertyHandleImpl_Enum* asEnum();
QPropertyHandleImpl_Object* asObject();
QPropertyHandleImpl_Associative* asAssociative();
QPropertyHandleImpl_Sequential* asSequential();
static PropertyType parserType(QMetaType inType);
static QVariant createNewVariant(QMetaType inOutputType);
Q_SIGNALS:
void asVarChanged(QVariant);
void asStructureChanged();
void asRequestRollback(QVariant);
protected:
QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
void resloveMetaData();
private:
QMetaType mType;
PropertyType mPropertyType;
QString mPropertyPath;
Getter mGetter;
Setter mSetter;
QVariant mInitialValue;
QVariantHash mMetaData;
QSharedPointer<IPropertyHandleImpl> mImpl;
};
struct DIAGRAM_DESIGNER_PUBLIC ExternalRefCountWithMetaType : public QtSharedPointer::ExternalRefCountData {
typedef ExternalRefCountData Parent;
QMetaType mMetaType;
void* mData;
static void deleter(ExternalRefCountData* self) {
ExternalRefCountWithMetaType* that =
static_cast<ExternalRefCountWithMetaType*>(self);
that->mMetaType.destroy(that->mData);
Q_UNUSED(that); // MSVC warns if T has a trivial destructor
}
static inline ExternalRefCountData* create(QMetaType inMetaType, void* inPtr)
{
ExternalRefCountWithMetaType* d = static_cast<ExternalRefCountWithMetaType*>(::operator new(sizeof(ExternalRefCountWithMetaType)));
// initialize the d-pointer sub-object
// leave d->data uninitialized
new (d) Parent(ExternalRefCountWithMetaType::deleter); // can't throw
d->mData = inPtr;
d->mMetaType = inMetaType;
return d;
}
};
#endif // QPropertyHandle_h__

View File

@ -0,0 +1,28 @@
#ifndef QQuickDetailsView_h__
#define QQuickDetailsView_h__
#include "QQuickTreeViewEx.h"
class QQuickDetailsViewPrivate;
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsView: public QQuickTreeViewEx {
Q_OBJECT
QML_NAMED_ELEMENT(DetailsView)
Q_DISABLE_COPY(QQuickDetailsView)
Q_DECLARE_PRIVATE(QQuickDetailsView)
Q_PROPERTY(qreal SpliterPencent READ getSpliterPencent WRITE setSpliterPencent NOTIFY asSpliterPencentChanged FINAL)
Q_PROPERTY(QObject* Object READ getObject WRITE setObject NOTIFY asObjectChanged FINAL)
public:
QQuickDetailsView(QQuickItem* parent = nullptr);
qreal getSpliterPencent() const;
void setSpliterPencent(qreal val);
Q_INVOKABLE void setObject(QObject* inObject);
Q_INVOKABLE QObject* getObject() const;
Q_SIGNALS:
void asSpliterPencentChanged(qreal);
void asObjectChanged(QObject*);
protected:
void componentComplete() override;
};
#endif // QQuickDetailsView_h__

View File

@ -0,0 +1,36 @@
#ifndef QQuickDetailsViewLayoutBuilder_h__
#define QQuickDetailsViewLayoutBuilder_h__
#include "QQuickDetailsViewRow.h"
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewRowBuilder {
public:
QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem);
QPair<QQuickItem*, QQuickItem*> makeNameValueSlot();
IDetailsViewRow* row() const;
QQuickItem* rootItem() const;
void makePropertyRow(QPropertyHandle* inHandle);
QQuickItem* setupItem(QQuickItem* inParent, QString inQmlCode);
void setupLabel(QQuickItem* inParent, QString inText);
void setHeightProxy(QQuickItem* inProxyItem);
private:
IDetailsViewRow* mRow = nullptr;
QQuickItem* mRootItem = nullptr;
};
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewLayoutBuilder {
public:
QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRootRow);
IDetailsViewRow* row() const;
void addCustomRow(std::function<void(QQuickDetailsViewRowBuilder*)> inCustomRowCreator, QString inOverrideName = "");
void addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName = "");
void addObject(QObject* inObject);
private:
IDetailsViewRow* mRootRow = nullptr;
};
#endif // QQuickDetailsViewLayoutBuilder_h__

View File

@ -0,0 +1,58 @@
#ifndef QQuickDetailsViewManager_h__
#define QQuickDetailsViewManager_h__
#include <QObject>
#include <QHash>
#include <functional>
#include <QMetaType>
#include <QQmlEngine>
#include <QQuickItem>
#include "IPropertyTypeCustomization.h"
#include "export.hpp"
class QPropertyHandle;
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewManager : public QObject{
public:
using PropertyTypeCustomizationCreator = std::function<QSharedPointer<IPropertyTypeCustomization>()>;
using TypeEditorCreator = std::function<QQuickItem* (QPropertyHandle*, QQuickItem*)>;
static QQuickDetailsViewManager* Get();
void initialize();
bool isInitialized() const;
template<typename MetaType, typename IPropertyTypeCustomizationType>
void registerPropertyTypeCustomization() {
QMetaType metaType = QMetaType::fromType<MetaType>();
if (metaType.metaObject()) {
mClassCustomizationMap.insert(metaType.metaObject(), []() {
return QSharedPointer<IPropertyTypeCustomizationType>::create();
});
}
else {
mMetaTypeCustomizationMap.insert(metaType, []() {
return QSharedPointer<IPropertyTypeCustomizationType>::create();
});
}
}
void unregisterPropertyTypeCustomization(const QMetaType& inMetaType);
void registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator);
void unregisterTypeEditor(const QMetaType& inMetaType);
QQuickItem* createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent);
QSharedPointer<IPropertyTypeCustomization> getCustomPropertyType(QPropertyHandle* inHandle);
protected:
QQuickDetailsViewManager();
void RegisterBasicTypeEditor();
private:
bool mInitialized = false;
QHash<const QMetaObject*, PropertyTypeCustomizationCreator> mClassCustomizationMap;
QHash<QMetaType, PropertyTypeCustomizationCreator> mMetaTypeCustomizationMap;
QHash<QMetaType, TypeEditorCreator> mTypeEditorCreatorMap;
};
#endif // QQuickDetailsViewManager_h__

View File

@ -0,0 +1,44 @@
#ifndef QQuickDetailsViewModel_h__
#define QQuickDetailsViewModel_h__
#include <QObject>
#include <QMap>
#include <QAbstractItemModel>
#include <QMetaProperty>
#include <QQuickItem>
#include "export.hpp"
class IDetailsViewRow;
class QDetailsViewRow_Property;
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewModel : public QAbstractItemModel {
Q_OBJECT
enum Roles {
name = 0,
};
friend class IDetailsViewRow;
public:
QQuickDetailsViewModel(QObject* parent = 0);
~QQuickDetailsViewModel() {}
QVariant data(const QModelIndex& index, int role) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& index) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
void setObject(QObject* inObject);
QObject* getObject() const;
QModelIndex indexForRow(IDetailsViewRow* row) const;
void updateRowIndex(IDetailsViewRow* row);
private:
QSharedPointer<QDetailsViewRow_Property> mRoot;
QObject* mObject;
};
#endif // QQuickDetailsViewModel_h__

View File

@ -0,0 +1,66 @@
#ifndef QQuickDetailsViewRow_h__
#define QQuickDetailsViewRow_h__
#include <QObject>
#include <QMap>
#include <QAbstractItemModel>
#include <QMetaProperty>
#include <QQuickItem>
#include "QPropertyHandle.h"
#include "IPropertyTypeCustomization.h"
#include "QQuickDetailsViewModel.h"
#include "export.hpp"
class DIAGRAM_DESIGNER_PUBLIC IDetailsViewRow {
friend class QQuickDetailsViewModel;
public:
~IDetailsViewRow() {};
void setName(QString inName);
virtual QString name();
virtual void setupItem(QQuickItem* inParent){}
virtual void attachChildren() {}
virtual void addChild(QSharedPointer<IDetailsViewRow> inChild);
void clear();
QQuickDetailsViewModel* model();
QModelIndex modelIndex() const { return mModelIndex; }
void setModelIndex(const QModelIndex& index) { mModelIndex = index; }
int rowNumber() const {
if (!mParent) return -1;
return mParent->mChildren.indexOf(const_cast<IDetailsViewRow*>(this));
}
void invalidateChildren();
protected:
QString mName;
QQuickDetailsViewModel* mModel = nullptr;
QModelIndex mModelIndex;
IDetailsViewRow* mParent = nullptr;
QList<QSharedPointer<IDetailsViewRow>> mChildren;
};
class DIAGRAM_DESIGNER_PUBLIC QDetailsViewRow_Property : public IDetailsViewRow {
public:
QDetailsViewRow_Property(QPropertyHandle* inHandle);
~QDetailsViewRow_Property();
void setHandle(QPropertyHandle* inHandle);
void setupItem(QQuickItem* inParent) override;
void attachChildren() override;
protected:
QPropertyHandle* mHandle = nullptr;
QMetaObject::Connection mStructureChangedConnection;
QSharedPointer<IPropertyTypeCustomization> mPropertyTypeCustomization;
};
class DIAGRAM_DESIGNER_PUBLIC QDetailsViewRow_Custom : public IDetailsViewRow {
public:
QDetailsViewRow_Custom(std::function<void(QQuickDetailsViewRowBuilder*)> inRowCreator);
protected:
QString name() override { return "Custom"; }
void setupItem(QQuickItem* inParent) override;
private:
std::function<void(QQuickDetailsViewRowBuilder*)> mRowCreator;
};
#endif // QQuickDetailsViewRow_h__

View File

@ -0,0 +1,64 @@
#ifndef QQuickFunctionLibrary_h__
#define QQuickFunctionLibrary_h__
#include <QObject>
#include <QVariant>
#include <QQuickItem>
#include "export.hpp"
class QQuickLambdaHolder;
class QQuickLambdaHolder_OneParam;
class DIAGRAM_DESIGNER_PUBLIC QQuickFunctionLibrary : public QObject {
Q_OBJECT
public:
static QQuickFunctionLibrary* Get();
Q_INVOKABLE QString numberToString(QVariant var,int precision);
Q_INVOKABLE void setCursorPos(qreal x, qreal y);
Q_INVOKABLE void setOverrideCursorShape(Qt::CursorShape shape);
Q_INVOKABLE void restoreOverrideCursorShape();
Q_INVOKABLE void setCursorPosTest(QQuickItem* item, qreal x, qreal y);
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void()> callback);
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant)> callback);
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant, QVariant)> callback);
};
class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder : public QObject {
Q_OBJECT
std::function<void()> mCallback;
public:
QQuickLambdaHolder(std::function<void()> callback, QObject* parent)
: QObject(parent)
, mCallback(std::move(callback)) {}
Q_SLOT void call() { mCallback(); }
};
class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder_OneParam : public QObject {
Q_OBJECT
std::function<void(QVariant)> mCallback;
public:
QQuickLambdaHolder_OneParam(std::function<void(QVariant)> callback, QObject* parent)
: QObject(parent)
, mCallback(std::move(callback)) {
}
Q_SLOT void call(QVariant inParam0) { mCallback(inParam0); }
};
class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder_TwoParams : public QObject {
Q_OBJECT
std::function<void(QVariant, QVariant)> mCallback;
public:
QQuickLambdaHolder_TwoParams(std::function<void(QVariant, QVariant)> callback, QObject* parent)
: QObject(parent)
, mCallback(std::move(callback)) {
}
Q_SLOT void call(QVariant inParam0, QVariant inParam1) { mCallback(inParam0, inParam1); }
};
#endif // QQuickFunctionLibrary_h__

View File

@ -0,0 +1,53 @@
#ifndef QQuickTreeViewEx_h__
#define QQuickTreeViewEx_h__
#include "export.hpp"
#include "private/qquicktableview_p.h"
class QQuickTreeViewExPrivate;
class DIAGRAM_DESIGNER_PUBLIC QQuickTreeViewEx: public QQuickTableView {
Q_OBJECT
public:
QQuickTreeViewEx(QQuickItem* parent = nullptr);
~QQuickTreeViewEx() override;
QModelIndex rootIndex() const;
void setRootIndex(const QModelIndex& index);
void resetRootIndex();
Q_INVOKABLE int depth(int row) const;
Q_INVOKABLE bool isExpanded(int row) const;
Q_INVOKABLE void expand(int row);
Q_INVOKABLE void collapse(int row);
Q_INVOKABLE void toggleExpanded(int row);
Q_INVOKABLE void invalidateLayout();
Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1);
Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1);
Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex& index);
Q_INVOKABLE QModelIndex modelIndex(const QPoint& cell) const override;
Q_INVOKABLE QPoint cellAtIndex(const QModelIndex& index) const override;
#if QT_DEPRECATED_SINCE(6, 4)
QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
Q_REVISION(6, 4) Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
#endif
Q_SIGNALS:
void expanded(int row, int depth);
void collapsed(int row, bool recursively);
Q_REVISION(6, 6) void rootIndexChanged();
protected:
QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent);
void keyPressEvent(QKeyEvent* event) override;
private:
Q_DISABLE_COPY(QQuickTreeViewEx)
Q_DECLARE_PRIVATE(QQuickTreeViewEx)
};
QML_DECLARE_TYPE(QQuickTreeViewEx)
#endif // QQuickTreeViewEx_h__

View File

@ -0,0 +1,35 @@
#include "PropertyTypeCustomization_Associative.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QPropertyHandle.h"
#include "qsequentialiterable.h"
#include "qassociativeiterable.h"
void PropertyTypeCustomization_Associative::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
auto associativelHandle = inPropertyHandle->asAssociative();
QMetaAssociation metaAssociation = associativelHandle->metaAssociation();
QVariant varMap = inPropertyHandle->getVar();
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
for (auto iter = iterable.begin(); iter != iterable.end(); ++iter) {
QString key = iter.key().toString();
inBuilder->addProperty(inPropertyHandle->findOrCreateChild(
metaAssociation.mappedMetaType(),
key,
[inPropertyHandle, key]() {
QVariant varMap = inPropertyHandle->getVar();
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
return iterable.value(key);
},
[inPropertyHandle, key, metaAssociation](QVariant var) {
QVariant varMap = inPropertyHandle->getVar();
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
QtPrivate::QVariantTypeCoercer keyCoercer;
QtPrivate::QVariantTypeCoercer mappedCoercer;
void* containterPtr = const_cast<void*>(iterable.constIterable());
const void* dataPtr = mappedCoercer.coerce(var, metaAssociation.mappedMetaType());
metaAssociation.setMappedAtKey(containterPtr, keyCoercer.coerce(key, metaAssociation.keyMetaType()), dataPtr);
inPropertyHandle->setVar(varMap);
}
));
}
}

View File

@ -0,0 +1,11 @@
#ifndef PropertyTypeCustomization_Associative_h__
#define PropertyTypeCustomization_Associative_h__
#include "IPropertyTypeCustomization.h"
class PropertyTypeCustomization_Associative : public IPropertyTypeCustomization {
protected:
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
};
#endif // PropertyTypeCustomization_Associative_h__

View File

@ -0,0 +1,94 @@
#include "PropertyTypeCustomization_Matrix4x4.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QPropertyHandle.h"
#include <QMetaType>
#include <QObject>
#include <QMatrix4x4>
#include "QQuickFunctionLibrary.h"
void PropertyTypeCustomization_Matrix4x4::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
//inBuilder->addCustomRow([inPropertyHandle](QQuickDetailsViewRowBuilder* builder) {
// auto editorSlot = builder->makeNameValueSlot();
// builder->setupLabel(editorSlot.first, "0");
// auto valueItem = builder->setupItem(editorSlot.second, R"(
// import QtQuick;
// import QtQuick.Controls;
// import "qrc:/resources/Qml/ValueEditor"
// Vec4Box{
// anchors.verticalCenter: parent.verticalCenter
// width: parent.width
// }
// )");
// builder->setHeightProxy(valueItem);
// QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
// valueItem->setProperty("value", mat.row(0));
// QQuickFunctionLibrary::connect(valueItem, SIGNAL(asValueChanged(QVariant)), inPropertyHandle, [inPropertyHandle](QVariant var) {
// QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
// mat.setRow(0, var.value<QVector4D>());
// inPropertyHandle->setVar(mat);
// });
// QQuickFunctionLibrary::connect(inPropertyHandle, SIGNAL(asRequestRollback(QVariant)), valueItem, [inPropertyHandle, valueItem](QVariant var) {
// QMatrix4x4 mat = var.value<QMatrix4x4>();
// valueItem->setProperty("value", mat.row(0));
// });
//});
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
inPropertyHandle->parent(),
QMetaType::fromType<QVector4D>(),
inPropertyHandle->createSubPath("Row 0"),
[inPropertyHandle]() {
return inPropertyHandle->getVar().value<QMatrix4x4>().row(0);
},
[inPropertyHandle](QVariant var) {
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
mat.setRow(0, var.value<QVector4D>());
inPropertyHandle->setVar(mat);
}
));
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
inPropertyHandle->parent(),
QMetaType::fromType<QVector4D>(),
inPropertyHandle->createSubPath("Row 1"),
[inPropertyHandle]() {
return inPropertyHandle->getVar().value<QMatrix4x4>().row(1);
},
[inPropertyHandle](QVariant var) {
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
mat.setRow(1, var.value<QVector4D>());
inPropertyHandle->setVar(mat);
}
));
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
inPropertyHandle->parent(),
QMetaType::fromType<QVector4D>(),
inPropertyHandle->createSubPath("Row 2"),
[inPropertyHandle]() {
return inPropertyHandle->getVar().value<QMatrix4x4>().row(2);
},
[inPropertyHandle](QVariant var) {
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
mat.setRow(2, var.value<QVector4D>());
inPropertyHandle->setVar(mat);
}
));
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
inPropertyHandle->parent(),
QMetaType::fromType<QVector4D>(),
inPropertyHandle->createSubPath("Row 3"),
[inPropertyHandle]() {
return inPropertyHandle->getVar().value<QMatrix4x4>().row(3);
},
[inPropertyHandle](QVariant var) {
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
mat.setRow(3, var.value<QVector4D>());
inPropertyHandle->setVar(mat);
}
));
}

View File

@ -0,0 +1,11 @@
#ifndef PropertyTypeCustomization_Matrix4x4_h__
#define PropertyTypeCustomization_Matrix4x4_h__
#include "IPropertyTypeCustomization.h"
class PropertyTypeCustomization_Matrix4x4 : public IPropertyTypeCustomization {
protected:
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
};
#endif // PropertyTypeCustomization_Matrix4x4_h__

View File

@ -0,0 +1,52 @@
#include "PropertyTypeCustomization_ObjectDefault.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QPropertyHandle.h"
void PropertyTypeCustomization_ObjectDefault::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
auto objectHandle = inPropertyHandle->asObject();
const QMetaObject* metaObject = objectHandle->getMetaObject();
if (objectHandle->isGadget()) {
for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) {
QMetaProperty prop = metaObject->property(i);
QString propName = prop.name();
inBuilder->addProperty(
inPropertyHandle->findOrCreateChild(
prop.metaType(),
propName,
[this, prop, objectHandle]() {
return prop.readOnGadget(objectHandle->getGadget());
},
[this, prop, objectHandle, inPropertyHandle](QVariant var) {
prop.writeOnGadget(objectHandle->getGadget(), var);
inPropertyHandle->setVar(objectHandle->getObjectHolder());
objectHandle->refreshObjectPtr();
}
),
propName
);
}
}
else {
if (objectHandle->getObject() != nullptr) {
for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) {
QMetaProperty prop = metaObject->property(i);
QString propName = prop.name();
inBuilder->addProperty(
inPropertyHandle->findOrCreateChild(
prop.metaType(),
propName,
[this, prop, objectHandle]() {
return prop.read(objectHandle->getObject());
},
[this, prop, objectHandle, inPropertyHandle](QVariant var) {
prop.write(objectHandle->getObject(), var);
}
),
propName
);
}
}
}
}

View File

@ -0,0 +1,11 @@
#ifndef PropertyTypeCustomization_ObjectDefault_h__
#define PropertyTypeCustomization_ObjectDefault_h__
#include "IPropertyTypeCustomization.h"
class PropertyTypeCustomization_ObjectDefault : public IPropertyTypeCustomization {
protected:
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
};
#endif // PropertyTypeCustomization_ObjectDefault_h__

View File

@ -0,0 +1,40 @@
#include "PropertyTypeCustomization_Sequential.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QPropertyHandle.h"
#include "qsequentialiterable.h"
#include "qassociativeiterable.h"
void PropertyTypeCustomization_Sequential::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
auto sequentialHandle = inPropertyHandle->asSequential();
QVariant varList = inPropertyHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
for (int index = 0; index < iterable.size(); index++) {
QString name = QString::number(index);
inBuilder->addProperty(inPropertyHandle->findOrCreateChild(
sequentialHandle->metaSequence().valueMetaType(),
name,
[inPropertyHandle, index]() {
QVariant varList = inPropertyHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
if (index < 0 || index >= iterable.size()) {
return QVariant();
}
return iterable.at(index);
},
[inPropertyHandle, index](QVariant var) {
QVariant varList = inPropertyHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
if (index >= 0 && index < iterable.size()) {
const QMetaSequence metaSequence = iterable.metaContainer();
void* containterPtr = const_cast<void*>(iterable.constIterable());
QtPrivate::QVariantTypeCoercer coercer;
const void* dataPtr = coercer.coerce(var, iterable.valueMetaType());
metaSequence.setValueAtIndex(containterPtr, index, dataPtr);
inPropertyHandle->setVar(varList);
}
}
));
}
}

View File

@ -0,0 +1,11 @@
#ifndef PropertyTypeCustomization_Sequential_h__
#define PropertyTypeCustomization_Sequential_h__
#include "IPropertyTypeCustomization.h"
class PropertyTypeCustomization_Sequential : public IPropertyTypeCustomization {
protected:
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
};
#endif // PropertyTypeCustomization_Sequential_h__

View File

@ -0,0 +1,13 @@
#include "IPropertyTypeCustomization.h"
#include "QQuickDetailsViewLayoutBuilder.h"
void IPropertyTypeCustomization::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
{
inBuilder->makePropertyRow(inPropertyHandle);
}
void IPropertyTypeCustomization::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
}

View File

@ -0,0 +1,38 @@
#include "PropertyHandleImpl/IPropertyHandleImpl.h"
#include "QPropertyHandle.h"
IPropertyHandleImpl::IPropertyHandleImpl(QPropertyHandle* inHandle):
mHandle(inHandle)
{
}
QQuickItem* IPropertyHandleImpl::createNameEditor(QQuickItem* inParent)
{
QQmlEngine* engine = qmlEngine(inParent);
QQmlContext* context = qmlContext(inParent);
QQmlComponent nameComp(engine);
nameComp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import ColorPalette;
Item{
implicitHeight: 25
width: parent.width
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
clip: true
elide: Text.ElideRight
text: model.name
color: ColorPalette.theme.labelPrimary
}
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(inParent);
auto nameEditor = qobject_cast<QQuickItem*>(nameComp.createWithInitialProperties(initialProperties, context));
nameEditor->setParentItem(inParent);
return nameEditor;
}

View File

@ -0,0 +1,72 @@
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
#include <QAssociativeIterable>
#include "QBoxLayout"
#include "QPropertyHandle.h"
QPropertyHandleImpl_Associative::QPropertyHandleImpl_Associative(QPropertyHandle* inHandle)
:IPropertyHandleImpl(inHandle) {
QVariant varMap = mHandle->getVar();
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
mMetaAssociation = iterable.metaContainer();
}
const QMetaAssociation& QPropertyHandleImpl_Associative::metaAssociation() const
{
return mMetaAssociation;
}
QQuickItem* QPropertyHandleImpl_Associative::createValueEditor(QQuickItem* inParent)
{
return nullptr;
}
bool QPropertyHandleImpl_Associative::renameItem(QString inSrc, QString inDst) {
bool canRename = false;
QVariant varMap = mHandle->getVar();
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
if (iterable.containsKey(inSrc) && !iterable.containsKey(inDst)) {
canRename = true;
QVariant var = iterable.value(inSrc);
QtPrivate::QVariantTypeCoercer keyCoercer;
QtPrivate::QVariantTypeCoercer mappedCoercer;
void* containterPtr = const_cast<void*>(iterable.constIterable());
QMetaAssociation metaAssociation = iterable.metaContainer();
metaAssociation.removeKey(containterPtr, keyCoercer.coerce(inSrc, QMetaType::fromType<QString>()));
metaAssociation.setMappedAtKey(
containterPtr,
keyCoercer.coerce(inDst, QMetaType::fromType<QString>()),
mappedCoercer.coerce(var, var.metaType())
);
//mHandle->setVar(varMap, QString("Rename: %1 -> %2").arg(inSrc).arg(inDst));
//Q_EMIT mHandle->asRequestRebuildRow();
}
return canRename;
}
void QPropertyHandleImpl_Associative::appendItem(QString inKey, QVariant inValue) {
QVariant varList = mHandle->getVar();
QAssociativeIterable iterable = varList.value<QAssociativeIterable>();
void* containterPtr = const_cast<void*>(iterable.constIterable());
QtPrivate::QVariantTypeCoercer coercer;
QVariant key(inKey);
const void* keyDataPtr = coercer.coerce(key, key.metaType());
const void* valueDataPtr = coercer.coerce(inValue, inValue.metaType());
//metaAssociation.insertKey(containterPtr, keyDataPtr);
mMetaAssociation.setMappedAtKey(containterPtr, keyDataPtr, valueDataPtr);
//mHandle->setVar(varList, QString("%1 Insert: %2").arg(mHandle->getPath()).arg(inKey));
//Q_EMIT mHandle->asRequestRebuildRow();
}
void QPropertyHandleImpl_Associative::removeItem(QString inKey) {
QVariant varList = mHandle->getVar();
QAssociativeIterable iterable = varList.value<QAssociativeIterable>();
const QMetaAssociation metaAssociation = iterable.metaContainer();
void* containterPtr = const_cast<void*>(iterable.constIterable());
QtPrivate::QVariantTypeCoercer coercer;
QVariant key(inKey);
const void* keyDataPtr = coercer.coerce(key, key.metaType());
metaAssociation.removeKey(containterPtr, keyDataPtr);
//mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(inKey));
//Q_EMIT mHandle->asRequestRebuildRow();
}

View File

@ -0,0 +1,46 @@
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
#include <QComboBox>
#include <QMetaEnum>
#include <QWidget>
#include <QBoxLayout>
#include "QPropertyHandle.h"
QPropertyHandleImpl_Enum::QPropertyHandleImpl_Enum(QPropertyHandle* inHandle)
:IPropertyHandleImpl(inHandle) {
const QMetaObject* metaObj = mHandle->getType().metaObject();
if (metaObj){
const QMetaEnum& metaEnum = metaObj->enumerator(metaObj->indexOfEnumerator(QString(mHandle->getType().name()).split("::").last().toLocal8Bit()));
for (int i = 0; i < metaEnum.keyCount(); i++) {
mNameToValueMap[metaEnum.key(i)] = metaEnum.value(i);
mKeys << metaEnum.key(i);
}
}
}
QQuickItem* QPropertyHandleImpl_Enum::createValueEditor(QQuickItem* inParent)
{
QQmlEngine* engine = qmlEngine(inParent);
QQmlContext* context = qmlContext(inParent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
TextComboBox{
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(inParent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(inParent);
valueEditor->setProperty("value", mHandle->getVar());
valueEditor->setProperty("model", mKeys);
QObject::connect(valueEditor, SIGNAL(asValueChanged(QVariant)), mHandle, SLOT(setVar(QVariant)));
QObject::connect(mHandle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
}

View File

@ -0,0 +1,98 @@
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
#include <qsequentialiterable.h>
#include <QRegularExpression>
#include "QPropertyHandle.h"
#include <QMetaProperty>
QPropertyHandleImpl_Object::QPropertyHandleImpl_Object(QPropertyHandle* inHandle)
:IPropertyHandleImpl(inHandle) {
mObjectHolder = mHandle->getVar();
QMetaType metaType = mHandle->getType();
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
QRegularExpressionMatch match = reg.match(metaType.name());
QStringList matchTexts = match.capturedTexts();
if (!matchTexts.isEmpty()) {
QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
mMetaObject = innerMetaType.metaObject();
const void* ptr = *(const void**)mObjectHolder.data();
bIsSharedPointer = true;
bIsPointer = true;
if (ptr) {
mObjectHolder = QVariant(innerMetaType, mObjectHolder.data());
}
else {
mObjectHolder = QVariant();
}
}
else{
bIsPointer = metaType.flags().testFlag(QMetaType::IsPointer);
mMetaObject = metaType.metaObject();
}
mOwnerObject = mHandle->parent();
refreshObjectPtr();
}
QObject* QPropertyHandleImpl_Object::getObject()
{
if(mMetaObject->inherits(&QObject::staticMetaObject))
return (QObject*)mObjectPtr;
return nullptr;
}
void* QPropertyHandleImpl_Object::getGadget()
{
return mObjectPtr;
}
bool QPropertyHandleImpl_Object::isGadget() const
{
if (mMetaObject->inherits(&QObject::staticMetaObject))
return false;
return mObjectPtr != nullptr;
}
QObject* QPropertyHandleImpl_Object::getOwnerObject()
{
return mOwnerObject;
}
const QMetaObject* QPropertyHandleImpl_Object::getMetaObject() const
{
return mMetaObject;
}
void QPropertyHandleImpl_Object::refreshObjectPtr() {
mObjectHolder = mHandle->getVar();
if (mObjectHolder.isValid()) {
if (mMetaObject->inherits(&QObject::staticMetaObject)) {
QObject* objectPtr = mObjectHolder.value<QObject*>();
if (objectPtr) {
mMetaObject = objectPtr->metaObject();
}
mObjectPtr = objectPtr;
mOwnerObject = objectPtr;
if (mOwnerObject) {
//QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::moveToThread, mOwnerObject, mHandle->thread()));
//QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::installEventFilter, mOwnerObject, mHandle));
//mOwnerObject->installEventFilter(mHandle);
}
}
else {
void* ptr = mObjectHolder.data();
if (bIsPointer)
ptr = *(void**)mObjectHolder.data();
mObjectPtr = ptr;
}
}
}
QVariant& QPropertyHandleImpl_Object::getObjectHolder()
{
return mObjectHolder;
}
QQuickItem* QPropertyHandleImpl_Object::createValueEditor(QQuickItem* inParent)
{
return nullptr;
}

View File

@ -0,0 +1,14 @@
#include "PropertyHandleImpl/IPropertyHandleImpl.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h"
#include "QQuickDetailsViewMananger.h"
QPropertyHandleImpl_RawType::QPropertyHandleImpl_RawType(QPropertyHandle* inHandle)
: IPropertyHandleImpl(inHandle)
{
}
QQuickItem* QPropertyHandleImpl_RawType::createValueEditor(QQuickItem* inParent)
{
return QQuickDetailsViewManager::Get()->createValueEditor(mHandle, inParent);
}

View File

@ -0,0 +1,69 @@
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
#include <qsequentialiterable.h>
#include "QBoxLayout"
#include "QPropertyHandle.h"
QPropertyHandleImpl_Sequential::QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle)
:IPropertyHandleImpl(inHandle) {
QVariant varList = mHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
mMetaSequence = iterable.metaContainer();
}
const QMetaSequence& QPropertyHandleImpl_Sequential::metaSequence() const
{
return mMetaSequence;
}
QQuickItem* QPropertyHandleImpl_Sequential::createValueEditor(QQuickItem* inParent)
{
return nullptr;
}
int QPropertyHandleImpl_Sequential::itemCount() {
QVariant varList = mHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
return iterable.size();
}
void QPropertyHandleImpl_Sequential::appendItem( QVariant InVar) {
QVariant varList = mHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
const QMetaSequence metaSequence = iterable.metaContainer();
void* containterPtr = const_cast<void*>(iterable.constIterable());
QtPrivate::QVariantTypeCoercer coercer;
const void* dataPtr = coercer.coerce(InVar, InVar.metaType());
metaSequence.addValue(containterPtr, dataPtr);
//mHandle->setVar(varList, QString("%1 Append: %2").arg(mHandle->getPath()).arg(metaSequence.size(containterPtr) - 1));
//Q_EMIT mHandle->asRequestRebuildRow();
}
void QPropertyHandleImpl_Sequential::moveItem(int InSrcIndex, int InDstIndex) {
QVariant varList = mHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
const QMetaSequence metaSequence = iterable.metaContainer();
void* containterPtr = const_cast<void*>(iterable.constIterable());
QtPrivate::QVariantTypeCoercer coercer;
QVariant srcVar = iterable.at(InSrcIndex);
QVariant dstVar = iterable.at(InDstIndex);
metaSequence.setValueAtIndex(containterPtr, InDstIndex, coercer.coerce(srcVar, srcVar.metaType()));
metaSequence.setValueAtIndex(containterPtr, InSrcIndex, coercer.coerce(dstVar, dstVar.metaType()));
//mHandle->setVar(varList, QString("%1 Move: %2->%3").arg(mHandle->getPath()).arg(InSrcIndex).arg(InDstIndex));
//Q_EMIT mHandle->asRequestRebuildRow();
}
void QPropertyHandleImpl_Sequential::removeItem(int InIndex) {
QVariant varList = mHandle->getVar();
QSequentialIterable iterable = varList.value<QSequentialIterable>();
const QMetaSequence metaSequence = iterable.metaContainer();
void* containterPtr = const_cast<void*>(iterable.constIterable());
QtPrivate::QVariantTypeCoercer coercer;
for (int i = InIndex; i < iterable.size() - 1; i++) {
QVariant nextVar = iterable.at(i + 1);
metaSequence.setValueAtIndex(containterPtr, InIndex, coercer.coerce(nextVar, nextVar.metaType()));
}
metaSequence.removeValueAtEnd(containterPtr);
//mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(InIndex));
//Q_EMIT mHandle->asRequestRebuildRow();
}

View File

@ -0,0 +1,86 @@
#include "QDetailsView.h"
#include <QVBoxLayout>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQuickItem>
#include <QUrl>
#include "QQuickDetailsViewMananger.h"
QDetailsView::QDetailsView(QWidget* parent)
: QWidget(parent)
, mQuickWidget(nullptr)
, mQuickDetailsView(nullptr)
{
setMinimumSize(200, 200);
if (!QQuickDetailsViewManager::Get()->isInitialized()) {
QQuickDetailsViewManager::Get()->initialize();
}
mQuickWidget = new QQuickWidget(this);
mQuickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
const QString qmlContent = R"(
import QtQuick
import QtQuick.Controls
import QtQuick.DetailsView
DetailsView {
id: detailsView
anchors.fill: parent
boundsBehavior: Flickable.OvershootBounds
ScrollBar.vertical: ScrollBar {
parent: detailsView.parent
width : 10
anchors.top: detailsView.top
anchors.right: detailsView.right
anchors.bottom: detailsView.bottom
}
}
)";
QQmlComponent component(mQuickWidget->engine());
component.setData(qmlContent.toUtf8(), QUrl(""));
QObject* rootObject = component.create();
if (!component.errors().isEmpty()) {
qDebug() << component.errorString();
}
if (rootObject) {
mQuickWidget->setSource(QUrl());
mQuickWidget->engine()->setObjectOwnership(rootObject, QQmlEngine::CppOwnership);
QQuickItem* rootItem = qobject_cast<QQuickItem*>(rootObject);
if (rootItem) {
mQuickWidget->setContent(QUrl(), &component, rootItem);
mQuickDetailsView = qobject_cast<QQuickDetailsView*>(rootItem);
}
}
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(mQuickWidget);
setLayout(layout);
}
QQuickDetailsView* QDetailsView::getQuickDetailsView() const
{
return mQuickDetailsView;
}
void QDetailsView::setObject(QObject* inObject)
{
QQuickItem* rootObject = mQuickWidget->rootObject();
if (rootObject) {
rootObject->setProperty("Object", QVariant::fromValue(inObject));
}
}
QObject* QDetailsView::getObject() const
{
QQuickItem* rootObject = mQuickWidget->rootObject();
if (rootObject) {
return rootObject->property("Object").value<QObject*>();
}
return nullptr;
}

View File

@ -0,0 +1,322 @@
#include "QPropertyHandle.h"
#include <QRegularExpression>
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h"
QPropertyHandle::QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
: mType(inType)
, mPropertyPath(inPropertyPath)
, mGetter(inGetter)
, mSetter(inSetter)
{
setParent(inParent);
setObjectName(inPropertyPath.split(".").back());
resloveMetaData();
mInitialValue = inGetter();
mPropertyType = parserType(inType);
switch (mPropertyType)
{
default:
break;
case PropertyType::RawType:
mImpl.reset(new QPropertyHandleImpl_RawType(this));
break;
case PropertyType::Enum:
mImpl.reset(new QPropertyHandleImpl_Enum(this));
break;
case PropertyType::Sequential:
mImpl.reset(new QPropertyHandleImpl_Sequential(this));
break;
case PropertyType::Associative:
mImpl.reset(new QPropertyHandleImpl_Associative(this));
break;
case PropertyType::Object:
mImpl.reset(new QPropertyHandleImpl_Object(this));
break;
}
}
void QPropertyHandle::resloveMetaData()
{
auto metaObj = parent()->metaObject();
auto firstField = getPropertyPath().split(".").first();
for (int i = 0; i < metaObj->classInfoCount(); i++) {
auto metaClassInfo = metaObj->classInfo(i);
if (metaClassInfo.name() == firstField) {
QStringList fields = QString(metaClassInfo.value()).split(",", Qt::SplitBehaviorFlags::SkipEmptyParts);
for (auto field : fields) {
QStringList pair = field.split("=");
QString key, value;
if (pair.size() > 0) {
key = pair.first().trimmed();
}
if (pair.size() > 1) {
value = pair[1].trimmed();
}
mMetaData[key] = value;
}
return;
}
}
}
QPropertyHandle* QPropertyHandle::Find(const QObject* inParent, const QString& inPropertyPath)
{
for (QObject* child : inParent->children()) {
QPropertyHandle* handler = qobject_cast<QPropertyHandle*>(child);
if (handler && handler->getPropertyPath() == inPropertyPath) {
return handler;
}
}
return nullptr;
}
QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
{
QPropertyHandle* handle = Find(inParent, inPropertyPath);
if (handle)
return handle;
return new QPropertyHandle(
inParent,
inType,
inPropertyPath,
inGetter,
inSetter
);
}
QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inObject)
{
QPropertyHandle* handle = Find(inObject, "");
if (handle)
return handle;
return new QPropertyHandle(
inObject,
inObject->metaObject()->metaType(),
"",
[inObject]() {return QVariant::fromValue(inObject); },
[inObject](QVariant var) {}
);
}
QPropertyHandle* QPropertyHandle::Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
{
return new QPropertyHandle(inParent, inType, inPropertyPath, inGetter, inSetter);
}
void QPropertyHandle::Cleanup(QObject* inParent)
{
for (QObject* child : inParent->children()) {
QPropertyHandle* handler = qobject_cast<QPropertyHandle*>(child);
if (handler) {
handler->deleteLater();
}
}
}
QMetaType QPropertyHandle::getType()
{
return mType;
}
QPropertyHandle::PropertyType QPropertyHandle::getPropertyType() const
{
return mPropertyType;
}
QString QPropertyHandle::getName()
{
return objectName();
}
QString QPropertyHandle::getPropertyPath()
{
return mPropertyPath;
}
QString QPropertyHandle::createSubPath(const QString& inSubName)
{
return getPropertyPath() + "." + inSubName;
}
void QPropertyHandle::invalidateStructure()
{
Q_EMIT asStructureChanged();
}
QVariant QPropertyHandle::getVar()
{
return mGetter();
}
void QPropertyHandle::setVar(QVariant var)
{
QVariant lastVar = mGetter();
if (lastVar != var) {
mSetter(var);
Q_EMIT asVarChanged(var);
QVariant currVar = mGetter();
if (currVar != var) {
Q_EMIT asRequestRollback(currVar);
}
}
}
bool QPropertyHandle::hasMetaData(const QString& inName) const
{
return mMetaData.contains(inName);
}
QVariant QPropertyHandle::getMetaData(const QString& inName) const
{
return mMetaData.value(inName);
}
const QVariantHash& QPropertyHandle::getMetaDataMap() const
{
return mMetaData;
}
QPropertyHandle* QPropertyHandle::findChild(QString inPropertyName)
{
return Find(this->parent(), this->createSubPath(inPropertyName));
}
QPropertyHandle* QPropertyHandle::findOrCreateChild(QMetaType inType, QString inPropertyName, Getter inGetter, Setter inSetter)
{
QVariant var = inGetter();
QObject* object = var.value<QObject*>();
QPropertyHandle* childHandle = nullptr;
if (object) {
childHandle = QPropertyHandle::FindOrCreate(
object
);
childHandle->setObjectName(inPropertyName);
}
else {
childHandle = QPropertyHandle::FindOrCreate(
this->parent(),
inType,
this->createSubPath(inPropertyName),
inGetter,
inSetter
);
}
childHandle->disconnect(this);
connect(this, &QPropertyHandle::asVarChanged, childHandle, [childHandle](QVariant) {
QVariant var = childHandle->getVar();
Q_EMIT childHandle->asRequestRollback(var);
});
connect(this, &QPropertyHandle::asRequestRollback, childHandle, [childHandle](QVariant) {
QVariant var = childHandle->getVar();
Q_EMIT childHandle->asRequestRollback(var);
});
return childHandle;
}
QQuickItem* QPropertyHandle::setupNameEditor(QQuickItem* inParent)
{
return mImpl->createNameEditor(inParent);
}
QQuickItem* QPropertyHandle::steupValueEditor(QQuickItem* inParent)
{
return mImpl->createValueEditor(inParent);
}
IPropertyHandleImpl::Type QPropertyHandle::type()
{
return mImpl->type();
}
QPropertyHandleImpl_Enum* QPropertyHandle::asEnum()
{
return static_cast<QPropertyHandleImpl_Enum*>(mImpl.get());
}
QPropertyHandleImpl_Object* QPropertyHandle::asObject()
{
return static_cast<QPropertyHandleImpl_Object*>(mImpl.get());
}
QPropertyHandleImpl_Associative* QPropertyHandle::asAssociative()
{
return static_cast<QPropertyHandleImpl_Associative*>(mImpl.get());
}
QPropertyHandleImpl_Sequential* QPropertyHandle::asSequential()
{
return static_cast<QPropertyHandleImpl_Sequential*>(mImpl.get());
}
QPropertyHandle::PropertyType QPropertyHandle::parserType(QMetaType inType)
{
if (QMetaType::canConvert(inType, QMetaType::fromType<QVariantList>())
&& !QMetaType::canConvert(inType, QMetaType::fromType<QString>())
) {
return Sequential;
}
else if (QMetaType::canConvert(inType, QMetaType::fromType<QVariantMap>())) {
return Associative;
}
else if (inType.flags() & QMetaType::IsEnumeration) {
return Enum;
}
else {
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
QRegularExpressionMatch match = reg.match(inType.name(), 0, QRegularExpression::MatchType::PartialPreferCompleteMatch, QRegularExpression::AnchorAtOffsetMatchOption);
QStringList matchTexts = match.capturedTexts();
QMetaType innerMetaType;
if (!matchTexts.isEmpty()) {
QString metaTypeName = matchTexts.back();
innerMetaType = QMetaType::fromName(metaTypeName.toLocal8Bit());
}
if (innerMetaType.metaObject() || inType.metaObject()) {
return Object;
}
else {
return RawType;
}
}
}
QVariant QPropertyHandle::createNewVariant(QMetaType inOutputType)
{
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
QRegularExpressionMatch match = reg.match(inOutputType.name());
QStringList matchTexts = match.capturedTexts();
if (!matchTexts.isEmpty()) {
QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
if (innerMetaType.isValid()) {
void* ptr = innerMetaType.create();
QVariant sharedPtr(inOutputType);
memcpy(sharedPtr.data(), &ptr, sizeof(ptr));
QtSharedPointer::ExternalRefCountData* data = ExternalRefCountWithMetaType::create(innerMetaType, ptr);
memcpy((char*)sharedPtr.data() + sizeof(ptr), &data, sizeof(data));
return sharedPtr;
}
}
else if (inOutputType.flags().testFlag(QMetaType::IsPointer)) {
const QMetaObject* metaObject = inOutputType.metaObject();
if (metaObject && metaObject->inherits(&QObject::staticMetaObject)) {
QObject* obj = metaObject->newInstance();
if (obj)
return QVariant::fromValue(obj);
}
QMetaType innerMetaType = QMetaType::fromName(QString(inOutputType.name()).remove("*").toLocal8Bit());
if (innerMetaType.isValid()) {
void* ptr = innerMetaType.create();
QVariant var(inOutputType, ptr);
memcpy(var.data(), &ptr, sizeof(ptr));
return var;
}
else {
return QVariant();
}
}
return QVariant(inOutputType);
}

View File

@ -0,0 +1,86 @@
#include "QQuickDetailsView.h"
#include "private/qqmldata_p.h"
#include <QQmlEngine>
#include "qqmlcontext.h"
#include "QQuickDetailsViewPrivate.h"
#include "QQuickDetailsViewRow.h"
#include "QQuickFunctionLibrary.h"
void QQuickDetailsViewPrivate::initItemCallback(int serializedModelIndex, QObject* object)
{
QQuickTreeViewExPrivate::initItemCallback(serializedModelIndex, object);
auto item = qobject_cast<QQuickItem*>(object);
if (!item)
return;
const QModelIndex& index = m_treeModelToTableModel.mapToModel(serializedModelIndex);;
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
node->setupItem(item);
}
QQuickDetailsView::QQuickDetailsView(QQuickItem* parent /*= nullptr*/)
: QQuickTreeViewEx(*(new QQuickDetailsViewPrivate()),parent)
{
setModel(QVariant::fromValue(d_func()->mModel));
setReuseItems(false);
setEditTriggers(QQuickTableView::EditTrigger::DoubleTapped);
}
qreal QQuickDetailsView::getSpliterPencent() const
{
return d_func()->mSpliterPencent;
}
void QQuickDetailsView::setSpliterPencent(qreal val)
{
if(val != d_func()->mSpliterPencent){
d_func()->mSpliterPencent = val;
Q_EMIT asSpliterPencentChanged(val);
}
}
void QQuickDetailsView::setObject(QObject* inObject)
{
if (inObject != d_func()->mModel->getObject()) {
d_func()->mModel->setObject(inObject);
Q_EMIT asObjectChanged(inObject);
}
}
QObject* QQuickDetailsView::getObject() const
{
return d_func()->mModel->getObject();
}
void QQuickDetailsView::componentComplete()
{
QQmlEngine* engine = qmlEngine(this);
QQmlComponent* delegate = new QQmlComponent(engine, this);
engine->rootContext()->setContextProperty("helper", QQuickFunctionLibrary::Get());
delegate->setData(R"(
import QtQuick;
import QtQuick.Controls;
import QtQuick.Layouts;
import QtQuick.DetailsView;
Item {
id: detailsDelegate
readonly property real indent: 20
readonly property real padding: 5
required property DetailsView detailsView
required property int row
required property bool isTreeNode
required property bool expanded
required property int hasChildren
required property int depth
implicitWidth: detailsView.width
implicitHeight: heightProxy ? heightProxy.height + 5 : 30
TapHandler {
onTapped: detailsView.toggleExpanded(row)
}
onImplicitHeightChanged: {
detailsView.invalidateLayout();
}
})", QUrl("QQuickDetailsView.componentComplete"));;
setDelegate(delegate);
QQuickTreeViewEx::componentComplete();
}

View File

@ -0,0 +1,238 @@
#include "QQuickDetailsViewMananger.h"
#include "QPropertyHandle.h"
#include "QQuickFunctionLibrary.h"
#include <QRegularExpression>
#include <QDir>
#include <QMetaType>
#define REGINTER_NUMBER_EDITOR_CREATOR(TypeName, DefaultPrecision) \
registerTypeEditor(QMetaType::fromType<TypeName>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { \
QQmlEngine* engine = qmlEngine(parent); \
QQmlContext* context = qmlContext(parent); \
QQmlComponent nameComp(engine); \
const char* qmlData = \
"import QtQuick;\n" \
"import QtQuick.Controls;\n" \
"import \"qrc:/resources/Qml/ValueEditor\"\n" \
"NumberBox{\n" \
" anchors.verticalCenter: parent.verticalCenter\n" \
" width: parent.width\n" \
"}"; \
nameComp.setData(qmlData, QUrl()); \
QVariantMap initialProperties; \
initialProperties["parent"] = QVariant::fromValue(parent); \
auto valueEditor = qobject_cast<QQuickItem*>(nameComp.createWithInitialProperties(initialProperties, context)); \
if (!nameComp.errors().isEmpty()) { \
qDebug() << nameComp.errorString(); \
} \
if (valueEditor) { \
valueEditor->setParentItem(parent); \
TypeName min = handle->getMetaData("Min").toDouble(); \
TypeName max = handle->getMetaData("Max").toDouble(); \
double step = handle->getMetaData("Step").toDouble(); \
int precision = handle->getMetaData("Precision").toInt(); \
valueEditor->setProperty("step", step <= 0.0001 ? 1.0 / pow(10.0, DefaultPrecision) : step); \
valueEditor->setProperty("number", handle->getVar()); \
valueEditor->setProperty("precision", precision == 0 ? DefaultPrecision : precision); \
if (min >= max) { \
min = std::numeric_limits<TypeName>::min(); \
max = std::numeric_limits<TypeName>::max(); \
} else { \
valueEditor->setProperty("isLimited", true); \
} \
valueEditor->setProperty("min", QVariant::fromValue(min)); \
valueEditor->setProperty("max", QVariant::fromValue(max)); \
qDebug() << min << max; \
QObject::connect(valueEditor, SIGNAL(valueChanged(QVariant)), handle, SLOT(setVar(QVariant))); \
QObject::connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setNumber(QVariant))); \
} \
return valueEditor; \
});
void QQuickDetailsViewManager::RegisterBasicTypeEditor() {
REGINTER_NUMBER_EDITOR_CREATOR(int, 0);
REGINTER_NUMBER_EDITOR_CREATOR(unsigned int, 0);
REGINTER_NUMBER_EDITOR_CREATOR(size_t, 0);
REGINTER_NUMBER_EDITOR_CREATOR(float, 2);
REGINTER_NUMBER_EDITOR_CREATOR(double, 3);
registerTypeEditor(QMetaType::fromType<QString>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
MultiLineTextBox{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
registerTypeEditor(QMetaType::fromType<QVector4D>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
Vec4Box{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
registerTypeEditor(QMetaType::fromType<QVector3D>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
Vec3Box{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
registerTypeEditor(QMetaType::fromType<QVector2D>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
Vec2Box{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
registerTypeEditor(QMetaType::fromType<QColor>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
ColorBox{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
QMetaType::registerConverterFunction(
[](const void* src, void* target) -> bool {
const QDir& dir = *static_cast<const QDir*>(src);
QString& str = *static_cast<QString*>(target);
str = dir.absolutePath();
return true;
},
QMetaType::fromType<QDir>(),
QMetaType::fromType<QString>()
);
QMetaType::registerConverterFunction(
[](const void* src, void* target) -> bool {
const QString& str = *static_cast<const QString*>(src);
QDir& dir = *static_cast<QDir*>(target);
dir = QDir(str);
return true;
},
QMetaType::fromType<QString>(),
QMetaType::fromType<QDir>()
);
registerTypeEditor(QMetaType::fromType<QDir>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
QQmlEngine* engine = qmlEngine(parent);
QQmlContext* context = qmlContext(parent);
QQmlComponent comp(engine);
comp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import "qrc:/resources/Qml/ValueEditor"
DirectorySelector{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
}
)", QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(parent);
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
valueEditor->setParentItem(parent);
valueEditor->setProperty("value", handle->getVar());
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
}

View File

@ -0,0 +1,216 @@
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QQuickDetailsViewMananger.h"
QQuickDetailsViewRowBuilder::QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem)
: mRow(inRow)
, mRootItem(inRootItem)
{
setHeightProxy(nullptr);
}
QPair<QQuickItem*, QQuickItem*> QQuickDetailsViewRowBuilder::makeNameValueSlot()
{
QQmlEngine* engine = qmlEngine(mRootItem);
QQmlContext* context = qmlContext(mRootItem);
QQmlContext* newContext = new QQmlContext(context, mRootItem);
QQmlComponent rootComp(newContext->engine());
rootComp.setData(R"(
import QtQuick;
import QtQuick.Controls;
import Qt5Compat.GraphicalEffects
import ColorPalette
Rectangle{
id: topLevelRect
anchors.fill: parent
color: hoverHandler.hovered ? ColorPalette.theme.rowBackgroundHover : ColorPalette.theme.rowBackground
border.color: ColorPalette.theme.rowBorder
border.width: 0.5
Behavior on color {
ColorAnimation { duration: 100 }
}
HoverHandler { id: hoverHandler }
Image {
id: indicator
visible: detailsDelegate.isTreeNode && detailsDelegate.hasChildren
x: padding + (detailsDelegate.depth * detailsDelegate.indent)
anchors.verticalCenter: parent.verticalCenter
mipmap: true
source: detailsDelegate.expanded
? "qrc:/resources/Icon/expand.png"
: "qrc:/resources/Icon/unexpand.png"
width: 12
height: 12
ColorOverlay {
anchors.fill: parent
source: parent
color: ColorPalette.theme.rowIndicator
opacity: 1.0
}
}
Item{
id: nameEditorContent
height: parent.height
anchors.left: parent.left
anchors.leftMargin: padding + (detailsDelegate.isTreeNode ? (detailsDelegate.depth + 1) * detailsDelegate.indent : 0)
anchors.right: splitter.left
anchors.verticalCenter: parent.verticalCenter
}
Item{
id: valueEditorContent
implicitHeight: 25
anchors.left: splitter.left
anchors.leftMargin: 10
anchors.rightMargin: 10
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
Rectangle{
id: splitter
width: 3
height: parent.height
color : ColorPalette.theme.rowBackground
x: detailsView.SpliterPencent * detailsView.width - 1
Rectangle{
id: visibleSplitter
width: 1
height: parent.height
color: ColorPalette.theme.rowSplitter
anchors.horizontalCenter: parent.horizontalCenter
}
MouseArea {
id: dragArea
hoverEnabled: true
cursorShape: containsMouse ? Qt.SplitHCursor : Qt.ArrowCursor
anchors.fill: parent
drag.target: splitter
drag.axis: Drag.XAxis
drag.minimumX: 10 + 1
drag.maximumX: detailsView.width - 10 - 1
onPositionChanged: {
detailsView.SpliterPencent = (splitter.x + 1) / detailsView.width
}
}
}
Rectangle {
id: gradientBox
visible: detailsDelegate.depth > 0
width: 5
x: padding + (detailsDelegate.depth * detailsDelegate.indent) - 10
height: parent.height
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: ColorPalette.theme.rowShadowStart }
GradientStop { position: 1.0; color: ColorPalette.theme.rowShadowEnd }
}
}
}
)", QUrl());
QQuickItem* slotItem = qobject_cast<QQuickItem*>(rootComp.create(newContext));
if (!rootComp.errors().isEmpty()) {
qDebug() << rootComp.errorString();
}
slotItem->setParentItem(mRootItem);
return { slotItem->childItems()[1] ,slotItem->childItems()[2] };
}
QQuickItem* QQuickDetailsViewRowBuilder::rootItem() const
{
return mRootItem;
}
IDetailsViewRow* QQuickDetailsViewRowBuilder::row() const
{
return mRow;
}
QQuickItem* QQuickDetailsViewRowBuilder::setupItem(QQuickItem* inParent, QString inQmlCode)
{
QQmlEngine* engine = qmlEngine(inParent);
QQmlContext* context = qmlContext(inParent);
QQmlComponent comp(engine);
QQmlComponent nameComp(engine);
comp.setData(inQmlCode.toLocal8Bit(), QUrl());
QVariantMap initialProperties;
initialProperties["parent"] = QVariant::fromValue(inParent);
auto item = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
if (!comp.errors().isEmpty()) {
qDebug() << comp.errorString();
}
item->setParentItem(inParent);
return item;
}
void QQuickDetailsViewRowBuilder::setupLabel(QQuickItem* inParent, QString inText)
{
QQuickItem* lableItem = setupItem(inParent, R"(
import QtQuick;
import QtQuick.Controls;
import ColorPalette;
Item{
property string lableText
implicitHeight: 25
width: parent.width
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
clip: true
elide: Text.ElideRight
text: lableText
color: ColorPalette.theme.labelPrimary
}
}
)");
lableItem->setProperty("lableText", inText);
}
void QQuickDetailsViewRowBuilder::setHeightProxy(QQuickItem* inProxyItem)
{
QQmlEngine* engine = qmlEngine(mRootItem);
QQmlContext* context = qmlContext(mRootItem);
context->parentContext()->setContextProperty("heightProxy", inProxyItem);
}
void QQuickDetailsViewRowBuilder::makePropertyRow(QPropertyHandle* inHandle)
{
QQmlEngine* engine = qmlEngine(mRootItem);
QQmlContext* context = qmlContext(mRootItem);
QPair<QQuickItem*, QQuickItem*> slotItem = makeNameValueSlot();
QQuickItem* nameEditor = inHandle->setupNameEditor(slotItem.first);
QQuickItem* valueEditor = inHandle->steupValueEditor(slotItem.second);
context->parentContext()->setContextProperty("heightProxy", valueEditor ? valueEditor : nameEditor);
}
QQuickDetailsViewLayoutBuilder::QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRow)
: mRootRow(inRow)
{
}
IDetailsViewRow* QQuickDetailsViewLayoutBuilder::row() const
{
return mRootRow;
}
void QQuickDetailsViewLayoutBuilder::addCustomRow(std::function<void(QQuickDetailsViewRowBuilder*)> creator, QString inOverrideName)
{
QSharedPointer<IDetailsViewRow> child(new QDetailsViewRow_Custom(creator));
child->setName(inOverrideName);
mRootRow->addChild(child);
child->attachChildren();
}
void QQuickDetailsViewLayoutBuilder::addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName)
{
QSharedPointer<IDetailsViewRow> child(new QDetailsViewRow_Property(inPropertyHandle));
child->setName(inOverrideName.isEmpty() ? inPropertyHandle->getName() : inOverrideName);
mRootRow->addChild(child);
child->attachChildren();
}
void QQuickDetailsViewLayoutBuilder::addObject(QObject* inObject)
{
addProperty(QPropertyHandle::FindOrCreate(inObject));
}

View File

@ -0,0 +1,138 @@
#include "QQuickDetailsViewMananger.h"
#include "QPropertyHandle.h"
#include "QQuickFunctionLibrary.h"
#include <QRegularExpression>
#include "QQuickDetailsView.h"
#include "Customization/PropertyTypeCustomization_Sequential.h"
#include "Customization/PropertyTypeCustomization_Associative.h"
#include "Customization/PropertyTypeCustomization_ObjectDefault.h"
#include "Customization/PropertyTypeCustomization_Matrix4x4.h"
#include <QtQuickControls2/QQuickStyle>
#include <QMatrix4x4>
QQuickDetailsViewManager* QQuickDetailsViewManager::Get()
{
static QQuickDetailsViewManager ins;
return &ins;
}
void QQuickDetailsViewManager::initialize()
{
mInitialized = true;
#ifdef PROPERTY_EDITOR_STATIC_LIBRARY
Q_INIT_RESOURCE(resources);
#endif
QQuickStyle::setStyle("Basic");
qmlRegisterType<QQuickDetailsView>("QtQuick.DetailsView", 1, 0, "DetailsView");
qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette.qml"),
"ColorPalette",
1, 0,
"ColorPalette");
qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette_Light.qml"),
"ColorPalette",
1, 0,
"ColorPalette_Light");
qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette_Dark.qml"),
"ColorPalette",
1, 0,
"ColorPalette_Dark");
}
bool QQuickDetailsViewManager::isInitialized() const
{
return mInitialized;
}
void QQuickDetailsViewManager::unregisterPropertyTypeCustomization(const QMetaType& InMetaType)
{
if(InMetaType.metaObject())
mMetaTypeCustomizationMap.remove(InMetaType);
}
void QQuickDetailsViewManager::registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator)
{
mTypeEditorCreatorMap.insert(inMetaType, Creator);
}
void QQuickDetailsViewManager::unregisterTypeEditor(const QMetaType& inMetaType)
{
mTypeEditorCreatorMap.remove(inMetaType);
}
QQuickItem* QQuickDetailsViewManager::createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent)
{
if (mTypeEditorCreatorMap.contains(inHandle->getType())) {
QQuickItem* item = mTypeEditorCreatorMap[inHandle->getType()](inHandle, parent);
if (parent) {
item->setProperty("anchors.verticalCenter", QVariant::fromValue(parent->property("anchors.verticalCenter")));
//item->update();
}
return item;
}
return nullptr;
}
QSharedPointer<IPropertyTypeCustomization> QQuickDetailsViewManager::getCustomPropertyType(QPropertyHandle* inHandle)
{
if (inHandle->getPropertyType() == QPropertyHandle::Sequential) {
return QSharedPointer<PropertyTypeCustomization_Sequential>::create();
}
else if (inHandle->getPropertyType() == QPropertyHandle::Associative) {
return QSharedPointer<PropertyTypeCustomization_Associative>::create();
}
else if (inHandle->getPropertyType() == QPropertyHandle::Object) {
const QMetaObject* metaObject = inHandle->metaObject();
for (const auto& It : mClassCustomizationMap.asKeyValueRange()) {
if (It.first == metaObject) {
return It.second();
}
}
for (const auto& It : mClassCustomizationMap.asKeyValueRange()) {
if (metaObject->inherits(It.first)) {
return It.second();
}
}
return QSharedPointer<PropertyTypeCustomization_ObjectDefault>::create();
}
else if (inHandle->getPropertyType() == QPropertyHandle::RawType) {
const QMetaType& metaType = inHandle->getType();
for (const auto& It : mMetaTypeCustomizationMap.asKeyValueRange()) {
if (It.first == metaType) {
return It.second();
}
}
const QMetaObject* Child = nullptr;
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
QRegularExpressionMatch match = reg.match(metaType.name());
QStringList matchTexts = match.capturedTexts();
QMetaType innerMetaType;
if (!matchTexts.isEmpty()) {
innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
Child = innerMetaType.metaObject();
}
else {
Child = metaType.metaObject();
}
for (const auto& It : mMetaTypeCustomizationMap.asKeyValueRange()) {
const QMetaObject* Parent = It.first.metaObject();
if (Parent && Child && Child->inherits(Parent)) {
return It.second();
}
}
}
return nullptr;
}
QQuickDetailsViewManager::QQuickDetailsViewManager()
{
RegisterBasicTypeEditor();
registerPropertyTypeCustomization<QMatrix4x4, PropertyTypeCustomization_Matrix4x4>();
}

View File

@ -0,0 +1,137 @@
#include "QQuickDetailsViewModel.h"
#include "QQuickDetailsViewRow.h"
#include "QQuickDetailsViewLayoutBuilder.h"
QQuickDetailsViewModel::QQuickDetailsViewModel(QObject* parent)
: QAbstractItemModel(parent)
, mRoot(new QDetailsViewRow_Property(nullptr))
{
mRoot->mModel = this;
}
QVariant QQuickDetailsViewModel::data(const QModelIndex& index, int role) const {
if (!index.isValid())
return QVariant();
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
return node->name();
}
Qt::ItemFlags QQuickDetailsViewModel::flags(const QModelIndex& index) const {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QModelIndex QQuickDetailsViewModel::index(int row, int column, const QModelIndex& parent) const {
if (column < 0 || column >= columnCount(parent)) {
return QModelIndex();
}
IDetailsViewRow* parentNode = nullptr;
if (!parent.isValid()) {
parentNode = mRoot.get();
}
else {
parentNode = static_cast<IDetailsViewRow*>(parent.internalPointer());
if (!parentNode) {
return QModelIndex();
}
}
if (row < 0 || row >= parentNode->mChildren.size()) {
return QModelIndex();
}
IDetailsViewRow* childNode = parentNode->mChildren[row].get();
QModelIndex childIndex = createIndex(row, column, childNode);
childNode->setModelIndex(childIndex);
return childIndex;
}
QModelIndex QQuickDetailsViewModel::parent(const QModelIndex& index) const {
if (!index.isValid())
return QModelIndex();
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
IDetailsViewRow* parentNode = node->mParent;
if (!parentNode || parentNode == mRoot.get()) {
return QModelIndex();
}
IDetailsViewRow* grandparent = parentNode->mParent;
int parentRow = grandparent ? grandparent->mChildren.indexOf(parentNode) : -1;
if (parentRow == -1) {
return QModelIndex();
}
QModelIndex parentIndex = createIndex(parentRow, 0, parentNode);
parentNode->setModelIndex(parentIndex);
return parentIndex;
}
int QQuickDetailsViewModel::rowCount(const QModelIndex& parent) const {
if (!parent.isValid())
return mRoot->mChildren.size();
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(parent.internalPointer());
return node->mChildren.count();
}
void QQuickDetailsViewModel::setObject(QObject* inObject)
{
mObject = inObject;
mRoot->setHandle(QPropertyHandle::FindOrCreate(mObject));
mRoot->invalidateChildren();
}
QObject* QQuickDetailsViewModel::getObject() const
{
return mObject;
}
QModelIndex QQuickDetailsViewModel::indexForRow(IDetailsViewRow* row) const
{
if (!row || row == mRoot.get()) {
return QModelIndex();
}
IDetailsViewRow* parentRow = row->mParent;
if (!parentRow) {
return QModelIndex();
}
int rowNum = -1;
for (int i = 0; i < parentRow->mChildren.size(); ++i) {
if (parentRow->mChildren[i].data() == row) {
rowNum = i;
break;
}
}
if (rowNum == -1) {
return QModelIndex();
}
QModelIndex parentIndex = indexForRow(parentRow);
return createIndex(rowNum, 0, row);
}
void QQuickDetailsViewModel::updateRowIndex(IDetailsViewRow* row)
{
if (!row) return;
QModelIndex oldIndex = row->modelIndex();
QModelIndex newIndex = indexForRow(row);
if (oldIndex != newIndex) {
row->setModelIndex(newIndex);
dataChanged(newIndex, newIndex);
}
}
int QQuickDetailsViewModel::columnCount(const QModelIndex& parent) const {
return 1;
}
QHash<int, QByteArray> QQuickDetailsViewModel::roleNames() const {
return {
{ Roles::name,"name" },
};
}

View File

@ -0,0 +1,41 @@
#ifndef QQuickDetailsViewPrivate_h__
#define QQuickDetailsViewPrivate_h__
#include "private/qquicktreeview_p_p.h"
#include "QQuickTreeViewExPrivate.h"
#include "QQuickDetailsView.h"
#include "QQuickDetailsViewModel.h"
class QQuickDetailsViewPrivate : public QQuickTreeViewExPrivate
{
Q_DECLARE_PUBLIC(QQuickDetailsView)
public:
QQuickDetailsViewPrivate();
void initItemCallback(int serializedModelIndex, QObject* object) override;
void updateRequiredProperties(int serializedModelIndex, QObject* object, bool init) override;
private:
qreal mSpliterPencent = 0.3;
QList<QObject*> mObjects;
QQuickDetailsViewModel* mModel;
};
QQuickDetailsViewPrivate::QQuickDetailsViewPrivate()
:mModel(new QQuickDetailsViewModel)
{
}
void QQuickDetailsViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject* object, bool init)
{
Q_Q(QQuickDetailsView);
const QPoint cell = cellAtModelIndex(serializedModelIndex);
const int row = cell.y();
const int column = cell.x();
setRequiredProperty("row", QVariant::fromValue(serializedModelIndex), serializedModelIndex, object, init);
setRequiredProperty("detailsView", QVariant::fromValue(q), serializedModelIndex, object, init);
setRequiredProperty("isTreeNode", column == 0, serializedModelIndex, object, init);
setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init);
setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init);
setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init);
}
#endif // QQuickDetailsViewPrivate_h__

View File

@ -0,0 +1,118 @@
#include "QQuickDetailsViewRow.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QQuickDetailsViewMananger.h"
#include "QQuickDetailsViewModel.h"
void IDetailsViewRow::setName(QString inName)
{
mName = inName;
}
QString IDetailsViewRow::name()
{
return mName;
}
void IDetailsViewRow::addChild(QSharedPointer<IDetailsViewRow> inChild)
{
mChildren << inChild;
inChild->mModel = mModel;
inChild->mParent = this;
}
void IDetailsViewRow::clear()
{
mChildren.clear();
}
QQuickDetailsViewModel* IDetailsViewRow::model()
{
return mModel;
}
void IDetailsViewRow::invalidateChildren()
{
if (!mModel) {
mChildren.clear();
return;
}
QModelIndex parentIndex = mModel->indexForRow(this);
if (!parentIndex.isValid()) {
parentIndex = QModelIndex();
}
const int oldChildCount = mChildren.size();
if (oldChildCount > 0) {
mModel->beginRemoveRows(parentIndex, 0, oldChildCount - 1);
mChildren.clear();
mModel->endRemoveRows();
}
attachChildren();
const int newChildCount = mChildren.size();
if (newChildCount > 0) {
mModel->beginInsertRows(parentIndex, 0, newChildCount - 1);
for (auto& child : mChildren) {
child->mParent = this;
child->mModel = mModel;
}
mModel->endInsertRows();
}
}
QDetailsViewRow_Property::QDetailsViewRow_Property(QPropertyHandle* inHandle)
{
setHandle(inHandle);
}
QDetailsViewRow_Property::~QDetailsViewRow_Property()
{
if (mStructureChangedConnection) {
QObject::disconnect(mStructureChangedConnection);
}
}
void QDetailsViewRow_Property::setupItem(QQuickItem* inParent)
{
QQuickDetailsViewRowBuilder builder(this, inParent);
if (mPropertyTypeCustomization) {
mPropertyTypeCustomization->customizeHeaderRow(mHandle, &builder);
return;
}
builder.makePropertyRow(mHandle);
}
void QDetailsViewRow_Property::attachChildren()
{
if (mPropertyTypeCustomization){
QQuickDetailsViewLayoutBuilder builder(this);
mPropertyTypeCustomization->customizeChildren(mHandle, &builder);
}
}
void QDetailsViewRow_Property::setHandle(QPropertyHandle* inHandle)
{
mHandle = inHandle;
if (!inHandle)
return;
mPropertyTypeCustomization = QQuickDetailsViewManager::Get()->getCustomPropertyType(inHandle);
mStructureChangedConnection = QObject::connect(inHandle, &QPropertyHandle::asStructureChanged, [this]() {
invalidateChildren();
});
}
QDetailsViewRow_Custom::QDetailsViewRow_Custom(std::function<void(QQuickDetailsViewRowBuilder*)> inRowCreator)
: mRowCreator(inRowCreator)
{
}
void QDetailsViewRow_Custom::setupItem(QQuickItem* inParent)
{
if (mRowCreator) {
QQuickDetailsViewRowBuilder builder(this, inParent);
mRowCreator(&builder);
}
}

View File

@ -0,0 +1,58 @@
#include "QQuickFunctionLibrary.h"
#include <QCursor>
#include <QApplication>
QQuickFunctionLibrary* QQuickFunctionLibrary::Get()
{
static QQuickFunctionLibrary Ins;
return &Ins;
}
QString QQuickFunctionLibrary::numberToString(QVariant var, int precision)
{
double value = var.toDouble();
return QString::number(value, 'f', precision);
}
void QQuickFunctionLibrary::setCursorPos(qreal x, qreal y)
{
QCursor::setPos(x, y);
}
void QQuickFunctionLibrary::setOverrideCursorShape(Qt::CursorShape shape)
{
qApp->setOverrideCursor(shape);
}
void QQuickFunctionLibrary::restoreOverrideCursorShape()
{
qApp->restoreOverrideCursor();
}
void QQuickFunctionLibrary::setCursorPosTest(QQuickItem* item, qreal x, qreal y)
{
QPointF global = item->mapToGlobal(x, y);
QCursor::setPos(global.x(), global.y());
qDebug() << x << y << global << QCursor::pos();
}
QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function<void()> callback)
{
if (!sender)
return {};
return QObject::connect(sender, signal, new QQuickLambdaHolder(callback, receiver ? receiver : sender), SLOT(call()));
}
QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant)> callback)
{
if (!sender)
return {};
return QObject::connect(sender, signal, new QQuickLambdaHolder_OneParam(callback, receiver ? receiver : sender), SLOT(call(QVariant)));
}
QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant, QVariant)> callback)
{
if (!sender)
return {};
return QObject::connect(sender, signal, new QQuickLambdaHolder_TwoParams(callback, receiver ? receiver : sender), SLOT(call(QVariant, QVariant)));
}

View File

@ -0,0 +1,480 @@
#include "QQuickTreeViewEx.h"
#include "QQuickTreeViewExPrivate.h"
#include <QtCore/qobject.h>
#include <QtQml/qqmlcontext.h>
#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQmlModels/private/qqmltreemodeltotablemodel_p_p.h>
// Hard-code the tree column to be 0 for now
static const int kTreeColumn = 0;
QQuickTreeViewExPrivate::QQuickTreeViewExPrivate()
: QQuickTableViewPrivate()
{
}
QQuickTreeViewExPrivate::~QQuickTreeViewExPrivate()
{
}
QVariant QQuickTreeViewExPrivate::modelImpl() const
{
return m_assignedModel;
}
void QQuickTreeViewExPrivate::setModelImpl(const QVariant& newModel)
{
Q_Q(QQuickTreeViewEx);
if (newModel == m_assignedModel)
return;
m_assignedModel = newModel;
QVariant effectiveModel = m_assignedModel;
if (effectiveModel.userType() == qMetaTypeId<QJSValue>())
effectiveModel = effectiveModel.value<QJSValue>().toVariant();
if (effectiveModel.isNull())
m_treeModelToTableModel.setModel(nullptr);
else if (const auto qaim = qvariant_cast<QAbstractItemModel*>(effectiveModel))
m_treeModelToTableModel.setModel(qaim);
else
qmlWarning(q) << "TreeView only accepts a model of type QAbstractItemModel";
scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
emit q->modelChanged();
}
void QQuickTreeViewExPrivate::initItemCallback(int serializedModelIndex, QObject* object)
{
Q_Q(QQuickTreeViewEx);
updateRequiredProperties(serializedModelIndex, object, true);
QQuickTableViewPrivate::initItemCallback(serializedModelIndex, object);
}
void QQuickTreeViewExPrivate::itemReusedCallback(int serializedModelIndex, QObject* object)
{
updateRequiredProperties(serializedModelIndex, object, false);
QQuickTableViewPrivate::itemReusedCallback(serializedModelIndex, object);
}
void QQuickTreeViewExPrivate::dataChangedCallback(
const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
{
Q_Q(QQuickTreeViewEx);
Q_UNUSED(roles);
for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
for (int column = topLeft.column(); column <= bottomRight.column(); ++column) {
const QPoint cell(column, row);
auto item = q->itemAtCell(cell);
if (!item)
continue;
const int serializedModelIndex = modelIndexAtCell(QPoint(column, row));
updateRequiredProperties(serializedModelIndex, item, false);
}
}
}
void QQuickTreeViewExPrivate::updateRequiredProperties(int serializedModelIndex, QObject* object, bool init)
{
Q_Q(QQuickTreeViewEx);
const QPoint cell = cellAtModelIndex(serializedModelIndex);
const int row = cell.y();
const int column = cell.x();
setRequiredProperty("treeView", QVariant::fromValue(q), serializedModelIndex, object, init);
setRequiredProperty("isTreeNode", column == kTreeColumn, serializedModelIndex, object, init);
setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init);
setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init);
setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init);
}
void QQuickTreeViewExPrivate::updateSelection(const QRect& oldSelection, const QRect& newSelection)
{
Q_Q(QQuickTreeViewEx);
const QRect oldRect = oldSelection.normalized();
const QRect newRect = newSelection.normalized();
if (oldSelection == newSelection)
return;
// Select the rows inside newRect that doesn't overlap with oldRect
for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) {
if (oldRect.y() != -1 && oldRect.y() <= row && row <= oldRect.y() + oldRect.height())
continue;
const QModelIndex startIndex = q->index(row, newRect.x());
const QModelIndex endIndex = q->index(row, newRect.x() + newRect.width());
selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
}
if (oldRect.x() != -1) {
// Since oldRect is valid, this update is a continuation of an already existing selection!
// Select the columns inside newRect that don't overlap with oldRect
for (int column = newRect.x(); column <= newRect.x() + newRect.width(); ++column) {
if (oldRect.x() <= column && column <= oldRect.x() + oldRect.width())
continue;
for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
selectionModel->select(q->index(row, column), QItemSelectionModel::Select);
}
// Unselect the rows inside oldRect that don't overlap with newRect
for (int row = oldRect.y(); row <= oldRect.y() + oldRect.height(); ++row) {
if (newRect.y() <= row && row <= newRect.y() + newRect.height())
continue;
const QModelIndex startIndex = q->index(row, oldRect.x());
const QModelIndex endIndex = q->index(row, oldRect.x() + oldRect.width());
selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
}
// Unselect the columns inside oldRect that don't overlap with newRect
for (int column = oldRect.x(); column <= oldRect.x() + oldRect.width(); ++column) {
if (newRect.x() <= column && column <= newRect.x() + newRect.width())
continue;
// Since we're not allowed to call select/unselect on the selectionModel with
// indices from different parents, and since indicies from different parents are
// expected when working with trees, we need to unselect the indices in the column
// one by one, rather than the whole column in one go. This, however, can cause a
// lot of selection fragments in the selectionModel, which eventually can hurt
// performance. But large selections containing a lot of columns is not normally
// the case for a treeview, so accept this potential corner case for now.
for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
selectionModel->select(q->index(row, column), QItemSelectionModel::Deselect);
}
}
}
QQuickTreeViewEx::QQuickTreeViewEx(QQuickItem* parent)
: QQuickTableView(*(new QQuickTreeViewExPrivate), parent)
{
Q_D(QQuickTreeViewEx);
setSelectionBehavior(SelectRows);
setEditTriggers(EditKeyPressed);
// Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and
// never the actual tree model that is assigned to us by the application.
const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
d, &QQuickTreeViewExPrivate::dataChangedCallback);
QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
this, &QQuickTreeViewEx::rootIndexChanged);
auto tapHandler = new QQuickTapHandler(this);
tapHandler->setAcceptedModifiers(Qt::NoModifier);
connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler] {
if (!pointerNavigationEnabled())
return;
if (editTriggers() & DoubleTapped)
return;
const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
toggleExpanded(row);
});
}
QQuickTreeViewEx::QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent)
: QQuickTableView(dd, parent)
{
Q_D(QQuickTreeViewEx);
setSelectionBehavior(SelectRows);
setEditTriggers(EditKeyPressed);
// Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and
// never the actual tree model that is assigned to us by the application.
const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
d, &QQuickTreeViewExPrivate::dataChangedCallback);
QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
this, &QQuickTreeViewEx::rootIndexChanged);
auto tapHandler = new QQuickTapHandler(this);
tapHandler->setAcceptedModifiers(Qt::NoModifier);
connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler] {
if (!pointerNavigationEnabled())
return;
if (editTriggers() & DoubleTapped)
return;
const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
toggleExpanded(row);
});
d_func()->init();
}
QQuickTreeViewEx::~QQuickTreeViewEx()
{
}
QModelIndex QQuickTreeViewEx::rootIndex() const
{
return d_func()->m_treeModelToTableModel.rootIndex();
}
void QQuickTreeViewEx::setRootIndex(const QModelIndex& index)
{
Q_D(QQuickTreeViewEx);
d->m_treeModelToTableModel.setRootIndex(index);
positionViewAtCell({ 0, 0 }, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
}
void QQuickTreeViewEx::resetRootIndex()
{
Q_D(QQuickTreeViewEx);
d->m_treeModelToTableModel.resetRootIndex();
positionViewAtCell({ 0, 0 }, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
}
int QQuickTreeViewEx::depth(int row) const
{
Q_D(const QQuickTreeViewEx);
if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
return -1;
return d->m_treeModelToTableModel.depthAtRow(row);
}
bool QQuickTreeViewEx::isExpanded(int row) const
{
Q_D(const QQuickTreeViewEx);
if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
return false;
return d->m_treeModelToTableModel.isExpanded(row);
}
void QQuickTreeViewEx::expand(int row)
{
if (row >= 0)
expandRecursively(row, 1);
}
void QQuickTreeViewEx::expandRecursively(int row, int depth)
{
Q_D(QQuickTreeViewEx);
if (row >= d->m_treeModelToTableModel.rowCount())
return;
if (row < 0 && row != -1)
return;
if (depth == 0 || depth < -1)
return;
auto expandRowRecursively = [this, d, depth](int startRow) {
d->m_treeModelToTableModel.expandRecursively(startRow, depth);
// Update the expanded state of the startRow. The descendant rows that gets
// expanded will get the correct state set from initItem/itemReused instead.
for (int c = leftColumn(); c <= rightColumn(); ++c) {
const QPoint treeNodeCell(c, startRow);
if (const auto item = itemAtCell(treeNodeCell))
d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
}
};
if (row >= 0) {
// Expand only one row recursively
const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
if (isExpanded && depth == 1)
return;
expandRowRecursively(row);
}
else {
// Expand all root nodes recursively
const auto model = d->m_treeModelToTableModel.model();
for (int r = 0; r < model->rowCount(); ++r) {
const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
if (rootRow != -1)
expandRowRecursively(rootRow);
}
}
emit expanded(row, depth);
}
void QQuickTreeViewEx::expandToIndex(const QModelIndex& index)
{
Q_D(QQuickTreeViewEx);
if (!index.isValid()) {
qmlWarning(this) << "index is not valid: " << index;
return;
}
if (index.model() != d->m_treeModelToTableModel.model()) {
qmlWarning(this) << "index doesn't belong to correct model: " << index;
return;
}
if (rowAtIndex(index) != -1) {
// index is already visible
return;
}
int depth = 1;
QModelIndex parent = index.parent();
int row = rowAtIndex(parent);
while (parent.isValid()) {
if (row != -1) {
// The node is already visible, since it maps to a row in the table!
d->m_treeModelToTableModel.expandRow(row);
// Update the state of the already existing delegate item
for (int c = leftColumn(); c <= rightColumn(); ++c) {
const QPoint treeNodeCell(c, row);
if (const auto item = itemAtCell(treeNodeCell))
d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
}
// When we hit a node that is visible, we know that all other nodes
// up to the parent have to be visible as well, so we can stop.
break;
}
else {
d->m_treeModelToTableModel.expand(parent);
parent = parent.parent();
row = rowAtIndex(parent);
depth++;
}
}
emit expanded(row, depth);
}
void QQuickTreeViewEx::collapse(int row)
{
Q_D(QQuickTreeViewEx);
if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
return;
if (!d->m_treeModelToTableModel.isExpanded(row))
return;
d_func()->m_treeModelToTableModel.collapseRow(row);
for (int c = leftColumn(); c <= rightColumn(); ++c) {
const QPoint treeNodeCell(c, row);
if (const auto item = itemAtCell(treeNodeCell))
d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
}
emit collapsed(row, false);
}
void QQuickTreeViewEx::collapseRecursively(int row)
{
Q_D(QQuickTreeViewEx);
if (row >= d->m_treeModelToTableModel.rowCount())
return;
if (row < 0 && row != -1)
return;
auto collapseRowRecursive = [this, d](int startRow) {
// Always collapse descendants recursively,
// even if the top row itself is already collapsed.
d->m_treeModelToTableModel.collapseRecursively(startRow);
// Update the expanded state of the (still visible) startRow
for (int c = leftColumn(); c <= rightColumn(); ++c) {
const QPoint treeNodeCell(c, startRow);
if (const auto item = itemAtCell(treeNodeCell))
d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
}
};
if (row >= 0) {
collapseRowRecursive(row);
}
else {
// Collapse all root nodes recursively
const auto model = d->m_treeModelToTableModel.model();
for (int r = 0; r < model->rowCount(); ++r) {
const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
if (rootRow != -1)
collapseRowRecursive(rootRow);
}
}
emit collapsed(row, true);
}
void QQuickTreeViewEx::toggleExpanded(int row)
{
if (isExpanded(row))
collapse(row);
else
expand(row);
}
void QQuickTreeViewEx::invalidateLayout()
{
Q_D(QQuickTreeViewEx);
d->forceLayout(false);
}
QModelIndex QQuickTreeViewEx::modelIndex(const QPoint& cell) const
{
Q_D(const QQuickTreeViewEx);
const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
return d->m_treeModelToTableModel.mapToModel(tableIndex);
}
QPoint QQuickTreeViewEx::cellAtIndex(const QModelIndex& index) const
{
const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
return QPoint(tableIndex.column(), tableIndex.row());
}
#if QT_DEPRECATED_SINCE(6, 4)
QModelIndex QQuickTreeViewEx::modelIndex(int row, int column) const
{
static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
if (compat6_4) {
// XXX Qt 7: Remove this compatibility path here and in QQuickTableView.
// In Qt 6.4.0 and 6.4.1, a source incompatible change led to row and column
// being documented to be specified in the opposite order.
// QT_QUICK_TABLEVIEW_COMPAT_VERSION can therefore be set to force tableview
// to continue accepting calls to modelIndex(column, row).
return modelIndex({ row, column });
}
else {
qmlWarning(this) << "modelIndex(row, column) is deprecated. "
"Use index(row, column) instead. For more information, see "
"https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
return modelIndex({ column, row });
}
}
#endif
void QQuickTreeViewEx::keyPressEvent(QKeyEvent* event)
{
event->ignore();
if (!keyNavigationEnabled())
return;
if (!selectionModel())
return;
const int row = cellAtIndex(selectionModel()->currentIndex()).y();
switch (event->key()) {
case Qt::Key_Left:
collapse(row);
event->accept();
break;
case Qt::Key_Right:
expand(row);
event->accept();
break;
default:
break;
}
if (!event->isAccepted())
QQuickTableView::keyPressEvent(event);
}

View File

@ -0,0 +1,34 @@
#ifndef QQuickTreeViewExPrivate_h__
#define QQuickTreeViewExPrivate_h__
#include "private/qquicktreeview_p_p.h"
#include "QQuickTreeViewEx.h"
class QQuickTreeViewExPrivate : public QQuickTableViewPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickTreeViewEx)
QQuickTreeViewExPrivate();
~QQuickTreeViewExPrivate() override;
static inline QQuickTreeViewExPrivate* get(QQuickTreeViewEx* q) { return q->d_func(); }
QVariant modelImpl() const override;
void setModelImpl(const QVariant& newModel) override;
void initItemCallback(int serializedModelIndex, QObject* object) override;
void itemReusedCallback(int serializedModelIndex, QObject* object) override;
void dataChangedCallback(const QModelIndex& topLeft,
const QModelIndex& bottomRight,
const QVector<int>& roles);
virtual void updateRequiredProperties(int serializedModelIndex, QObject* object, bool init);
void updateSelection(const QRect& oldSelection, const QRect& newSelection) override;
public:
QQmlTreeModelToTableModel m_treeModelToTableModel;
QVariant m_assignedModel;
};
#endif // QQuickTreeViewExPrivate_h__

View File

@ -125,6 +125,10 @@ set(DIAGRAMCAVAS_HEADER_FILES
include/util/subMovingSelector.h
include/instance/dataAccessor.h
include/instance/extraPropertyManager.h
include/propertyType/CustomGadget.h
include/propertyType/CustomType.h
include/propertyType/PropertyTypeCustomization_CustomType.h
../common/include/httpInterface.h
../common/include/tools.h
../common/include/global.h
@ -258,6 +262,8 @@ set(DIAGRAMCAVAS_SOURCE_FILES
source/util/subMovingSelector.cpp
source/instance/dataAccessor.cpp
source/instance/extraPropertyManager.cpp
source/propertyType/PropertyTypeCustomization_CustomType.cpp
../common/source/httpInterface.cpp
../common/source/baseProperty.cpp
../common/source/tools.cpp
@ -324,11 +330,11 @@ target_link_libraries(diagramCavas PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES})
option(BUILD_SHARED_LIBS "Build as shared library" ON)
target_include_directories(diagramCavas PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(diagramCavas PRIVATE diagramUtils)
target_link_libraries(diagramCavas PRIVATE diagramCommunication)
target_link_libraries(diagramCavas PUBLIC PropertyEditor)
target_compile_definitions(diagramCavas
PUBLIC

View File

@ -0,0 +1,20 @@
#ifndef CustomGadget_h__
#define CustomGadget_h__
#include "CommonInclude.h"
class QCustomGadget {
Q_GADGET
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
public:
Q_PROPERTY_VAR(double, LimitedDouble) = 1;
Q_PROPERTY_VAR(QString, Desc) = "This is inline Gadget";
};
static QDebug operator<<(QDebug debug, const QCustomGadget& gadget) {
return debug << gadget.LimitedDouble << gadget.Desc;
}
Q_DECLARE_METATYPE(QSharedPointer<QCustomGadget>);
#endif // CustomGadget_h__

View File

@ -0,0 +1,18 @@
#ifndef CustomType_h__
#define CustomType_h__
#include <QVector>
#include <QMetaType>
struct QCustomType {
unsigned int ArraySize = 0;
QVector<int> Array;
};
static QDebug operator<<(QDebug debug, const QCustomType& it) {
return debug << it.Array;
}
Q_DECLARE_METATYPE(QCustomType)
#endif // CustomType_h__

View File

@ -0,0 +1,12 @@
#ifndef PropertyTypeCustomization_CustomType_h__
#define PropertyTypeCustomization_CustomType_h__
#include "IPropertyTypeCustomization.h"
class PropertyTypeCustomization_CustomType : public IPropertyTypeCustomization {
protected:
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override;
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
};
#endif // PropertyTypeCustomization_CustomType_h__

View File

@ -0,0 +1,73 @@
#include "propertyType/PropertyTypeCustomization_CustomType.h"
#include "QQuickDetailsViewLayoutBuilder.h"
#include "QPropertyHandle.h"
#include <QMetaType>
#include <QObject>
#include <QRandomGenerator>
#include "QQuickDetailsViewModel.h"
#include "propertyType/CustomType.h"
#include "QQuickFunctionLibrary.h"
void PropertyTypeCustomization_CustomType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
{
auto editorSlot = inBuilder->makeNameValueSlot();
inPropertyHandle->setupNameEditor(editorSlot.first);
auto buttonItem = inBuilder->setupItem(editorSlot.second, R"(
import QtQuick;
import QtQuick.Controls;
Button{
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: 80
height: 20
text: "Sort"
}
)");
QQuickFunctionLibrary::connect(buttonItem, SIGNAL(clicked()), inPropertyHandle, [inPropertyHandle]() {
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
std::sort(customType.Array.begin(), customType.Array.end());
inPropertyHandle->setVar(QVariant::fromValue(customType));
if (auto arrayHandle = inPropertyHandle->findChild("Array")) {
}
});
}
void PropertyTypeCustomization_CustomType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
{
auto arrayHandle = inPropertyHandle->findOrCreateChild(
QMetaType::fromType<QVector<int>>(),
"Array",
[inPropertyHandle]() {
return QVariant::fromValue(inPropertyHandle->getVar().value<QCustomType>().Array);
},
[inPropertyHandle](QVariant var) {
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
customType.Array = var.value<QVector<int>>();
inPropertyHandle->setVar(QVariant::fromValue(customType));
}
);
auto arraySizeHandle = inPropertyHandle->findOrCreateChild(
QMetaType::fromType<unsigned int>(),
"ArraySize",
[inPropertyHandle]() {
return inPropertyHandle->getVar().value<QCustomType>().ArraySize;
},
[inPropertyHandle, arrayHandle](QVariant var) {
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
customType.ArraySize = var.toUInt();
customType.Array.resize(customType.ArraySize);
for (int i = 0; i < customType.ArraySize; ++i) {
customType.Array[i] = QRandomGenerator::global()->bounded(-100000, 100000);
}
inPropertyHandle->setVar(QVariant::fromValue(customType));
arrayHandle->invalidateStructure();
}
);
inBuilder->addProperty(arraySizeHandle);
inBuilder->addProperty(arrayHandle);
}

18
include/CommonInclude.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef CommonInclude_h__
#define CommonInclude_h__
#include <QObject>
#include <QSharedPointer>
#include <QDebug>
#include <QMetaType>
#define Q_PROPERTY_VAR(Type,Name)\
Q_PROPERTY(Type Name READ get##Name WRITE set##Name) \
Type get##Name(){ return Name; } \
void set##Name(Type var){ \
Name = var; \
qDebug() << "Set" <<#Name <<": " <<var; \
} \
Type Name
#endif // CommonInclude_h__

View File

@ -24,6 +24,7 @@ class DiagramView;
class CreateEditor;
class MonitorItemsDlg;
class MonitorPagesDlg;
class QDetailsView;
class CMainWindow : public QMainWindow
{
@ -85,6 +86,7 @@ private:
MonitorPagesDlg* m_pMonitorPagesDlg;
QDockWidget* m_pMonitorItemsDock;
QDockWidget* m_pMonitorPagesDock;
QDetailsView* m_pPropertiesEditorView;
QAction* _pActMonitor;
};
#endif // MAINWINDOW_H

View File

@ -27,6 +27,7 @@
#include "projectManager.h"
#include "monitorItemsDlg.h"
#include "monitorPagesDlg.h"
#include "QDetailsView.h"
#include "baseDockWidget.h"
CMainWindow::CMainWindow(QWidget *parent)
@ -43,6 +44,7 @@ CMainWindow::CMainWindow(QWidget *parent)
m_pMonitorPagesDlg = nullptr;
m_pMonitorItemsDock = nullptr;
m_pMonitorPagesDock = nullptr;
m_pPropertiesEditorView = nullptr;
_pActMonitor = nullptr;
initializeDockUi();
@ -55,6 +57,11 @@ CMainWindow::~CMainWindow()
delete ui;
if(m_pElectricElementsBox)
delete m_pElectricElementsBox;
if(m_pPropertiesEditorView){
auto pView = m_pPropertiesEditorView->getQuickDetailsView();
delete pView;
delete m_pPropertiesEditorView;
}
}
@ -126,6 +133,14 @@ void CMainWindow::initializeDockUi()
m_pProjectModelDlg = new projectModelDlg(this);
connect(&ProjectModelManager::instance(),&ProjectModelManager::modelChange,m_pElectricElementsBox,&ElectricElementsBox::onSignal_modelChanged);
m_pPropertiesEditorView = new QDetailsView();
QDockWidget* PropertyEditorDock = new QDockWidget(QString::fromWCharArray(L"属性面板"),this);
PropertyEditorDock->setWidget(m_pPropertiesEditorView);
PropertyEditorDock->setMinimumSize(200,150);
m_pPropertiesEditorView->setObject(m_pDiagramCavas);
PropertyEditorDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea);
this->addDockWidget(Qt::RightDockWidgetArea,PropertyEditorDock);
}
void CMainWindow::initializeAction()