add propertyEditor library
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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:
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
|
@ -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();
|
||||
```
|
||||
|
||||
你就能得到:
|
||||
|
||||

|
||||
|
||||
## 定制化
|
||||
|
||||
### 关于 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
|
||||
|
||||
- 撤销重做
|
||||
- 容器操作
|
||||
- 扩展元信息
|
||||
|
||||
|
||||
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
import QtQuick
|
||||
|
||||
pragma Singleton
|
||||
|
||||
QtObject {
|
||||
id: colorPalette
|
||||
property var theme : ColorPalette_Light
|
||||
}
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 122 KiB |
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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);
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -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__
|
||||
|
|
@ -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);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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>();
|
||||
}
|
||||
|
||||
|
|
@ -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" },
|
||||
};
|
||||
}
|
||||
|
|
@ -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__
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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__
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||