added property editor first version
|
|
@ -34,6 +34,8 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(PropertyEditor)
|
||||||
|
|
||||||
set(H_HEADER_FILES
|
set(H_HEADER_FILES
|
||||||
include/global.h
|
include/global.h
|
||||||
include/mainwindow.h
|
include/mainwindow.h
|
||||||
|
|
@ -120,7 +122,9 @@ endif()
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
|
||||||
target_include_directories(GridFrame PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
target_include_directories(GridFrame PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
target_include_directories(GridFrame PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/PropertyEditor/source/include)`")
|
||||||
target_link_libraries(GridFrame PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
target_link_libraries(GridFrame PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
||||||
|
target_link_libraries(GridFrame PRIVATE PropertyEditor)
|
||||||
target_link_libraries(GridFrame PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
target_link_libraries(GridFrame PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
project(PropertyEditor CXX)
|
||||||
|
|
||||||
|
option(BUILD_SHARED_LIBS "Build shared library (ON) or static library (OFF)" OFF)
|
||||||
|
|
||||||
|
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)
|
||||||
|
else()
|
||||||
|
add_library(PropertyEditor STATIC ${PROJECT_SOURCE} ${QRC_FILE})
|
||||||
|
target_compile_definitions(PropertyEditor PUBLIC PROPERTY_EDITOR_STATIC_LIBRARY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
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}/${pd_PlatformDir}/bin"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(example)
|
||||||
|
|
@ -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 "QDetailsViewAPI.h"
|
||||||
|
|
||||||
|
class QPropertyHandle;
|
||||||
|
class QQuickDetailsViewRowBuilder;
|
||||||
|
class QQuickDetailsViewLayoutBuilder;
|
||||||
|
|
||||||
|
class QDETAILS_VIEW_API 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 "QDetailsViewAPI.h"
|
||||||
|
|
||||||
|
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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 "QDetailsViewAPI.h"
|
||||||
|
|
||||||
|
class QPropertyHandle;
|
||||||
|
|
||||||
|
class QDETAILS_VIEW_API 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 "QDetailsViewAPI.h"
|
||||||
|
|
||||||
|
class IDetailsViewRow;
|
||||||
|
class QDetailsViewRow_Property;
|
||||||
|
|
||||||
|
class QDETAILS_VIEW_API 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 "QDetailsViewAPI.h"
|
||||||
|
|
||||||
|
class QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 "QDetailsViewAPI.h"
|
||||||
|
|
||||||
|
class QQuickLambdaHolder;
|
||||||
|
class QQuickLambdaHolder_OneParam;
|
||||||
|
|
||||||
|
class QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 QDETAILS_VIEW_API 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 "QDetailsViewAPI.h"
|
||||||
|
#include "private/qquicktableview_p.h"
|
||||||
|
|
||||||
|
class QQuickTreeViewExPrivate;
|
||||||
|
|
||||||
|
class QDETAILS_VIEW_API 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 QDETAILS_VIEW_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__
|
||||||
|
|
@ -45,17 +45,17 @@ private slots:
|
||||||
void onSignal_deleteItem();
|
void onSignal_deleteItem();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QAction* SavePerspectiveAction = nullptr;
|
QAction* m_pSavePerspectiveAction = nullptr;
|
||||||
QWidgetAction* PerspectiveListAction = nullptr;
|
QWidgetAction* m_pPerspectiveListAction = nullptr;
|
||||||
QComboBox* PerspectiveComboBox = nullptr;
|
QComboBox* m_pPerspectiveComboBox = nullptr;
|
||||||
|
|
||||||
QUndoStack* m_pUndoStack;
|
QUndoStack* m_pUndoStack;
|
||||||
|
|
||||||
Ui::CMainWindow *ui;
|
Ui::CMainWindow *ui;
|
||||||
|
|
||||||
ads::CDockManager* DockManager;
|
ads::CDockManager* m_pDockManager;
|
||||||
ads::CDockAreaWidget* StatusDockArea;
|
ads::CDockAreaWidget* m_pStatusDockArea;
|
||||||
ads::CDockWidget* TimelineDockWidget;
|
ads::CDockWidget* m_pTimelineDockWidget;
|
||||||
|
|
||||||
DrawingPanel* m_pDrawingPanel;
|
DrawingPanel* m_pDrawingPanel;
|
||||||
GraphicElementsPanel* m_pGraphicElementsPanel;
|
GraphicElementsPanel* m_pGraphicElementsPanel;
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ void CMainWindow::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
// Delete dock manager here to delete all floating widgets. This ensures
|
// Delete dock manager here to delete all floating widgets. This ensures
|
||||||
// that all top level windows of the dock manager are properly closed
|
// that all top level windows of the dock manager are properly closed
|
||||||
DockManager->deleteLater();
|
m_pDockManager->deleteLater();
|
||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,37 +62,37 @@ void CMainWindow::initializeDockUi()
|
||||||
CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true);
|
CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true);
|
||||||
CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false);
|
CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false);
|
||||||
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
||||||
DockManager = new CDockManager(this);
|
m_pDockManager = new CDockManager(this);
|
||||||
|
|
||||||
// Set central widget
|
// Set central widget
|
||||||
m_pDrawingPanel = new DrawingPanel();
|
m_pDrawingPanel = new DrawingPanel();
|
||||||
DesignerScene* designerScene = m_pDrawingPanel->getDesignerScene();
|
DesignerScene* designerScene = m_pDrawingPanel->getDesignerScene();
|
||||||
connect(designerScene, SIGNAL(signalAddItem(QGraphicsItem*)), this, SLOT(onSignal_addItem(QGraphicsItem*)));
|
connect(designerScene, SIGNAL(signalAddItem(QGraphicsItem*)), this, SLOT(onSignal_addItem(QGraphicsItem*)));
|
||||||
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
CDockWidget* centralDockWidget = new CDockWidget("CentralWidget");
|
||||||
CentralDockWidget->setWidget(m_pDrawingPanel);
|
centralDockWidget->setWidget(m_pDrawingPanel);
|
||||||
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
auto* centralDockArea = m_pDockManager->setCentralWidget(centralDockWidget);
|
||||||
CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas);
|
centralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas);
|
||||||
|
|
||||||
// create other dock widgets
|
// create other dock widgets
|
||||||
m_pGraphicElementsPanel = new GraphicElementsPanel();
|
m_pGraphicElementsPanel = new GraphicElementsPanel();
|
||||||
CDockWidget* GrapicElementsDockWidget = new CDockWidget(QString::fromWCharArray(L"图元面板"));
|
CDockWidget* grapicElementsDockWidget = new CDockWidget(QString::fromWCharArray(L"图元面板"));
|
||||||
GrapicElementsDockWidget->setWidget(m_pGraphicElementsPanel);
|
grapicElementsDockWidget->setWidget(m_pGraphicElementsPanel);
|
||||||
GrapicElementsDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
grapicElementsDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
GrapicElementsDockWidget->resize(400, 150);
|
grapicElementsDockWidget->resize(400, 150);
|
||||||
GrapicElementsDockWidget->setMinimumSize(200,150);
|
grapicElementsDockWidget->setMinimumSize(200,150);
|
||||||
DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, GrapicElementsDockWidget);
|
m_pDockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, grapicElementsDockWidget);
|
||||||
ui->menuView->addAction(GrapicElementsDockWidget->toggleViewAction());
|
ui->menuView->addAction(grapicElementsDockWidget->toggleViewAction());
|
||||||
|
|
||||||
QTableWidget* propertiesTable = new QTableWidget();
|
QTableWidget* propertiesTable = new QTableWidget();
|
||||||
propertiesTable->setColumnCount(3);
|
propertiesTable->setColumnCount(3);
|
||||||
propertiesTable->setRowCount(10);
|
propertiesTable->setRowCount(10);
|
||||||
CDockWidget* PropertiesDockWidget = new CDockWidget(QString::fromWCharArray(L"属性编辑器"));
|
CDockWidget* propertiesDockWidget = new CDockWidget(QString::fromWCharArray(L"属性编辑器"));
|
||||||
PropertiesDockWidget->setWidget(propertiesTable);
|
propertiesDockWidget->setWidget(propertiesTable);
|
||||||
PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
propertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
PropertiesDockWidget->resize(250, 150);
|
propertiesDockWidget->resize(250, 150);
|
||||||
PropertiesDockWidget->setMinimumSize(200,150);
|
propertiesDockWidget->setMinimumSize(200,150);
|
||||||
DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea);
|
m_pDockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, propertiesDockWidget, centralDockArea);
|
||||||
ui->menuView->addAction(PropertiesDockWidget->toggleViewAction());
|
ui->menuView->addAction(propertiesDockWidget->toggleViewAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMainWindow::initializeAction()
|
void CMainWindow::initializeAction()
|
||||||
|
|
|
||||||