exchange commit
|
|
@ -0,0 +1,20 @@
|
|||
kind: pipeline
|
||||
type: ssh
|
||||
name: default
|
||||
|
||||
server:
|
||||
host: 192.168.46.100:2223
|
||||
user: jessequ
|
||||
password:
|
||||
from_secret: password
|
||||
|
||||
workspace:
|
||||
path: /home/tmp
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
commands:
|
||||
- mkdir -p build/debug
|
||||
- cd build/debug
|
||||
- /home/jessequ/Qt/Tools/CMake/bin/cmake -S ../.. -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_GENERATOR:STRING=Ninja -DCMAKE_MAKE_PROGRAM:FILEPATH=/home/jessequ/Qt/Tools/Ninja/ninja -DCMAKE_PREFIX_PATH:PATH=/home/jessequ/Qt/6.7.2/gcc_64 -DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=/home/jessequ/Qt/Platforms/package-manager/auto-setup.cmake -DPostgreSQL_INCLUDE_DIR:FILEPATH=/usr/pgsql-17/include -DPostgreSQL_LIBRARY_DIR:FILEPATH=/usr/pgsql-17/lib -DPostgreSQL_LIBRARY:FILEPATH=/usr/pgsql-17/lib/libpq.so
|
||||
- /home/jessequ/Qt/Tools/Ninja/ninja
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
build/
|
||||
.vscode/
|
||||
.qtcreator/
|
||||
|
||||
# ---> CMake
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
# ---> C++
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# ---> C
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_policy(SET CMP0079 NEW)
|
||||
if(POLICY CMP0079)
|
||||
cmake_policy(GET CMP0079 policy_status)
|
||||
message(STATUS "CMP0079 策略状态: ${policy_status}")
|
||||
else()
|
||||
message(STATUS "CMP0079 策略不可用")
|
||||
endif()
|
||||
|
||||
project(DiagramDesigner LANGUAGES CXX VERSION 1.0)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets Sql Xml REQUIRED Charts)
|
||||
find_package(Qt6 REQUIRED COMPONENTS SvgWidgets)
|
||||
find_package(Qt6 COMPONENTS Network WebSockets REQUIRED)
|
||||
find_package(PostgreSQL REQUIRED)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
# 默认 ui 文件要和 .h 头文件在一个目录,若不在一个目录,需要指定其所在目录
|
||||
set(CMAKE_AUTOUIC_SEARCH_PATHS "ui")
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||
set(dd_PlatformDir "x86")
|
||||
else()
|
||||
if(DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(dd_PlatformDir "x64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
set(dd_PlatformDir "aarch64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64")
|
||||
set(dd_PlatformDir "arm64")
|
||||
else()
|
||||
set(dd_PlatformDir "x64")
|
||||
endif()
|
||||
else()
|
||||
set(dd_PlatformDir "x64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(H_HEADER_FILES
|
||||
include/mainwindow.h
|
||||
include/graphicElementsPanel.h
|
||||
include/electricElementsPanel.h
|
||||
include/electricElementsBox.h
|
||||
include/electricElementsListwidget.h
|
||||
include/operationCommand.h
|
||||
include/toolPage.h
|
||||
include/toolBox.h
|
||||
include/projectTableDelegate.h
|
||||
include/selectorDialog.h
|
||||
include/topologyView.h
|
||||
include/diagramView.h
|
||||
include/topologyTree.h
|
||||
include/monitorItemsDlg.h
|
||||
include/monitorPagesDlg.h
|
||||
include/baseDockWidget.h
|
||||
include/enhancedToolBar.h
|
||||
include/toolBarConfig.h
|
||||
include/configToolBar.h
|
||||
|
||||
common/include/tools.h
|
||||
common/include/httpInterface.h
|
||||
common/include/baseProperty.h
|
||||
common/include/compiler.hpp
|
||||
common/include/export.hpp
|
||||
common/include/operatingSystem.hpp
|
||||
common/include/structDataSource.h
|
||||
common/include/extraPropertyManager.h
|
||||
|
||||
common/core_model/types.h
|
||||
common/core_model/topology.h
|
||||
common/core_model/diagram.h
|
||||
common/backend/project_model.h
|
||||
common/backend/meta_model.h
|
||||
)
|
||||
set(CPP_SOURCE_FILES
|
||||
source/main.cpp
|
||||
source/mainwindow.cpp
|
||||
source/graphicElementsPanel.cpp
|
||||
source/electricElementsPanel.cpp
|
||||
source/electricElementsBox.cpp
|
||||
source/electricElementsListwidget.cpp
|
||||
source/operationCommand.cpp
|
||||
source/toolPage.cpp
|
||||
source/toolBox.cpp
|
||||
source/projectTableDelegate.cpp
|
||||
source/selectorDialog.cpp
|
||||
source/topologyView.cpp
|
||||
source/diagramView.cpp
|
||||
source/topologyTree.cpp
|
||||
source/monitorItemsDlg.cpp
|
||||
source/monitorPagesDlg.cpp
|
||||
source/baseDockWidget.cpp
|
||||
source/enhancedToolBar.cpp
|
||||
source/toolBarConfig.cpp
|
||||
source/configToolBar.cpp
|
||||
|
||||
common/source/httpInterface.cpp
|
||||
common/source/tools.cpp
|
||||
common/source/baseProperty.cpp
|
||||
common/source/structDataSource.cpp
|
||||
common/source/extraPropertyManager.cpp
|
||||
|
||||
common/core_model/types.cpp
|
||||
)
|
||||
set(UI_FILES
|
||||
ui/mainwindow.ui
|
||||
ui/graphicElementsPanel.ui
|
||||
ui/topologyView.ui
|
||||
ui/diagramView.ui
|
||||
ui/monitorItemsDlg.ui
|
||||
ui/monitorPagesDlg.ui
|
||||
)
|
||||
|
||||
# 包含源文件目录
|
||||
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
|
||||
|
||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_executable(DiagramDesigner
|
||||
MANUAL_FINALIZATION
|
||||
${H_HEADER_FILES}
|
||||
${CPP_SOURCE_FILES}
|
||||
${UI_FILES}
|
||||
resource/DiagramDesigner.qrc
|
||||
)
|
||||
else()
|
||||
if(ANDROID)
|
||||
add_library(DiagramDesigner SHARED
|
||||
${H_HEADER_FILES}
|
||||
${CPP_SOURCE_FILES}
|
||||
${UI_FILES}
|
||||
resource/DiagramDesigner.qrc
|
||||
)
|
||||
else()
|
||||
add_executable(DiagramDesigner WIN32
|
||||
${H_HEADER_FILES}
|
||||
${CPP_SOURCE_FILES}
|
||||
${UI_FILES}
|
||||
resource/DiagramDesigner.qrc
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
include_directories(common/include)
|
||||
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
||||
|
||||
target_include_directories(DiagramDesigner PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_include_directories(DiagramDesigner PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/common")
|
||||
target_include_directories(DiagramDesigner PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/PropertyEditor/source/include)")
|
||||
target_link_libraries(DiagramDesigner PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
target_link_libraries(DiagramDesigner PRIVATE Qt6::SvgWidgets)
|
||||
target_link_libraries(DiagramDesigner PRIVATE Qt6::Xml)
|
||||
target_link_libraries(DiagramDesigner PRIVATE Qt6::Network)
|
||||
target_link_libraries(DiagramDesigner PRIVATE Qt6::Sql ${PostgreSQL_LIBRARIES})
|
||||
message(STATUS "POSTGRESQL_LIBRARIES: ${PostgreSQL_LIBRARIES}")
|
||||
set_target_properties(DiagramDesigner PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
VERSION 1.0
|
||||
EXPORT_NAME "DiagramDesigner with Qt Advanced Docking System"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin"
|
||||
)
|
||||
|
||||
add_subdirectory(PropertyEditor)
|
||||
add_subdirectory(diagramCavas)
|
||||
add_subdirectory(diagramUtils)
|
||||
add_subdirectory(diagramCommunication)
|
||||
|
||||
target_link_libraries(DiagramDesigner PRIVATE PropertyEditor)
|
||||
target_link_libraries(DiagramDesigner PRIVATE diagramCavas diagramUtils diagramCommunication)
|
||||
|
||||
file(COPY setting.xml DESTINATION "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin")
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(PropertyEditor CXX)
|
||||
|
||||
find_package(Qt6 COMPONENTS Core Widgets Gui QuickWidgets QuickTemplates2 QuickControls2 REQUIRED)
|
||||
|
||||
qt6_add_resources(QRC_FILE resources.qrc)
|
||||
|
||||
file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/*.h ${CMAKE_CURRENT_SOURCE_DIR}/source/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/resources/*)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE})
|
||||
|
||||
#if(BUILD_SHARED_LIBS)
|
||||
# add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE})
|
||||
# target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_SHARED_LIBRARY)
|
||||
# message(STATUS "[CMake] Building as SHARED library")
|
||||
# message(STATUS "[CMake] Defined: PROPERTY_EDITOR_SHARED_LIBRARY")
|
||||
#else()
|
||||
# add_library(PropertyEditor STATIC ${PROJECT_SOURCE} ${QRC_FILE})
|
||||
# target_compile_definitions(PropertyEditor PUBLIC PROPERTY_EDITOR_STATIC_LIBRARY)
|
||||
# message(STATUS "[CMake] Building as STATIC library")
|
||||
# message(STATUS "[CMake] Defined: PROPERTY_EDITOR_STATIC_LIBRARY")
|
||||
#endif()
|
||||
|
||||
add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE})
|
||||
|
||||
target_compile_definitions(PropertyEditor
|
||||
PUBLIC
|
||||
DIAGRAM_DESIGNER_SHARED
|
||||
PRIVATE
|
||||
DIAGRAM_DESIGNER_EXPORTS
|
||||
#QT_NO_KEYWORDS
|
||||
)
|
||||
|
||||
|
||||
set_property(TARGET PropertyEditor PROPERTY AUTOMOC ON)
|
||||
set_property(TARGET PropertyEditor PROPERTY USE_FOLDERS ON)
|
||||
set_property(TARGET PropertyEditor PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files")
|
||||
|
||||
target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_LIBRARY)
|
||||
|
||||
target_link_libraries(PropertyEditor PUBLIC
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Widgets
|
||||
Qt::WidgetsPrivate
|
||||
Qt::QuickWidgets
|
||||
Qt::QuickPrivate
|
||||
Qt::QuickTemplates2
|
||||
Qt::QuickTemplates2Private
|
||||
Qt::QuickControls2
|
||||
)
|
||||
|
||||
target_include_directories(PropertyEditor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/source/include)
|
||||
file(GLOB_RECURSE PUBLIC_FILES LIST_DIRECTORIES TRUE ${CMAKE_CURRENT_SOURCE_DIR}/source/include/*)
|
||||
foreach(PUBLIC_FILE ${PUBLIC_FILES})
|
||||
if(IS_DIRECTORY ${PUBLIC_FILE})
|
||||
target_include_directories(PropertyEditor PRIVATE ${PUBLIC_FILE})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set_target_properties(
|
||||
PropertyEditor
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/bin"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${dd_PlatformDir}/lib"
|
||||
)
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
# QDetailsView
|
||||
|
||||
[中文用户?点我查看中文介绍](README_zh.md)
|
||||
|
||||
Inspired by the details view of Unreal Engine, QDetailsView leverages Qt's reflection system to easily build property editors for qobject.
|
||||
|
||||
Its core features are:
|
||||
|
||||
- Create type-based control editors that automatically organize the editor layout according to the reflection structure of the object.
|
||||
- Utilize QML GPU rendering and control management based on the preview view.
|
||||
|
||||
## Usage
|
||||
|
||||
It is extremely easy to use—simply declare the meta-properties of a `QObject` using **`Q_PROPERTY(...)`**:
|
||||
|
||||
```c++
|
||||
class QCustomObject : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int Int READ getInt WRITE setInt)
|
||||
Q_PROPERTY(float Float READ getFloat WRITE setFloat)
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
```c++
|
||||
QCustomObject obj;
|
||||
QDetailsView view;
|
||||
view.setObject(&obj);
|
||||
view.show();
|
||||
```
|
||||
|
||||
You will get the following result:
|
||||
|
||||

|
||||
|
||||
## Customization
|
||||
|
||||
### About QPropertyHandle
|
||||
|
||||
`QPropertyHandle` serves as the unified entry point for QDetailsView to manipulate properties. It is typically constructed via the following interface:
|
||||
|
||||
```c++
|
||||
static QPropertyHandle* QPropertyHandle::FindOrCreate(
|
||||
QObject* inParent, // Parent object that manages the lifecycle of the PropertyHandle
|
||||
QMetaType inType, // Meta-type of the property
|
||||
QString inPropertyPath, // Path field of the property
|
||||
Getter inGetter, // Getter function for the property
|
||||
Setter inSetter // Setter function for the property
|
||||
);
|
||||
```
|
||||
|
||||
To ensure that changes to property values are detected by DetailsView, all value modifications must use the interface provided by PropertyHandle:
|
||||
|
||||
```c++
|
||||
QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName");
|
||||
if (handle) {
|
||||
handle->setVar(QVariant::fromValue(newValue));
|
||||
}
|
||||
```
|
||||
|
||||
When creating a `QPropertyHandle`, you must specify a `parent`—the handle will be attached to the parent as a child object. Thus, its lifecycle is tied to the parent object. To clean it up, call:
|
||||
|
||||
```c++
|
||||
static void QPropertyHandle::Cleanup(QObject* inParent);
|
||||
```
|
||||
|
||||
### Custom Enum
|
||||
|
||||
For enum types, they must be defined within a class and declared using **`Q_ENUM(...)`**:
|
||||
|
||||
```c++
|
||||
class QCustomObject : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum QCustomEnum {
|
||||
One,
|
||||
Two,
|
||||
Three
|
||||
};
|
||||
Q_ENUM(QCustomEnum);
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Type Editor
|
||||
|
||||
For custom types that do not inherit from `QObject`, you first need to declare the type using the macro **`Q_DECLARE_METATYPE(...)`** during definition.
|
||||
|
||||
For specific types that require only a single editor control, you can directly register the type editor using the following interface:
|
||||
|
||||
```c++
|
||||
QQuickDetailsViewManager::Get()->registerTypeEditor(
|
||||
metaType,
|
||||
[](QPropertyHandle* handle, QQuickItem* parent) -> QQuickItem* {
|
||||
// Implementation of the editor creation logic
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
The source code directory `QQuickDetailsViewBasicTypeEditor.cpp` contains many reference examples, such as the editor for `QDir`:
|
||||
|
||||
```c++
|
||||
registerTypeEditor(
|
||||
QMetaType::fromType<QDir>(),
|
||||
[](QPropertyHandle* handle, QQuickItem* parent) -> QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
DirectorySelector {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
|
||||
return valueEditor;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
For editor controls that span multiple rows or have complex behaviors, you can extend the property editor by deriving from `IPropertyTypeCustomization`. This class provides two virtual functions:
|
||||
|
||||
```c++
|
||||
class IPropertyTypeCustomization : public QEnableSharedFromThis<IPropertyTypeCustomization>
|
||||
{
|
||||
public:
|
||||
// Used to assemble the editor for the current property row
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
|
||||
|
||||
// Used to extend child items for the current property row
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
|
||||
};
|
||||
```
|
||||
|
||||
After implementing the derived class, register it using the following interface:
|
||||
|
||||
```c++
|
||||
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- Undo/redo
|
||||
- Container operations
|
||||
- Extended meta-data support
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
# QDetailsView
|
||||
|
||||
QDetailsView 受虚幻引擎的属性面板所启发,借助Qt的反射系统,可以轻易地搭建对象的属性编辑器。
|
||||
|
||||
其核心在于:
|
||||
|
||||
- 编写基于类型的控件编辑器,自动根据对象的反射结构来组织编辑器布局。
|
||||
- 使用QML GPU渲染,基于预览视图的控件管理。
|
||||
|
||||
## 使用
|
||||
|
||||
它的使用非常简单,只需要使用 **Q_PROPERTY(...) ** 声明 `QObject` 的元属性:
|
||||
|
||||
``` c++
|
||||
class QCustomObject :public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int Int READ getInt WRITE setInt)
|
||||
Q_PROPERTY(float Float READ getFloat WRITE setFloat)
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
然后创建 `QDetailsView`,并指定Object:
|
||||
|
||||
``` c++
|
||||
QCustomObject obj;
|
||||
QDetailsView view;
|
||||
view.setObject(&obj);
|
||||
view.show();
|
||||
```
|
||||
|
||||
你就能得到:
|
||||
|
||||

|
||||
|
||||
## 定制化
|
||||
|
||||
### 关于 QPropertyHandle
|
||||
|
||||
`QPropertyHandle` 是QDetailsView操作属性的统一入口,它通常通过如下接口构建:
|
||||
|
||||
``` c++
|
||||
static QPropertyHandle* QPropertyHandle::FindOrCreate(
|
||||
QObject* inParent, // 父对象,它会持有PropertyHandle周期
|
||||
QMetaType inType, // 该属性的元类型
|
||||
QString inPropertyPath, // 该属性的路径字段
|
||||
Getter inGetter, // 该属性的获取器
|
||||
Setter inSetter // 该属性的设置器
|
||||
);
|
||||
```
|
||||
|
||||
为了保证属性值的变动可以被DetailsView响应,所有对值的修改请统一使用PropertyHandle的接口:
|
||||
|
||||
``` C++
|
||||
QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName");
|
||||
if(handle){
|
||||
handle->setVar(QVariant::fromValue(newValue));
|
||||
}
|
||||
```
|
||||
|
||||
`QPropertyHandle`创建时要求指定`parent`,它将会作为子对象挂载上去,因此它的生命周期将跟随父对象,如果要清理,请调用:
|
||||
|
||||
``` c++
|
||||
static void QPropertyHandle::Cleanup(QObject* inParent);
|
||||
```
|
||||
|
||||
### 自定义枚举
|
||||
|
||||
对于枚举类型,需要使用类内定义的方式,并通过**Q_ENUM(...)**声明:
|
||||
|
||||
``` c++
|
||||
class QCustomObject: public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum QCustomEnum {
|
||||
One,
|
||||
Two,
|
||||
Three
|
||||
};
|
||||
Q_ENUM(QCustomEnum);
|
||||
};
|
||||
```
|
||||
|
||||
### 自定义类型编辑器
|
||||
|
||||
非`QObject`的自定义类型,首先需要在定义类型时使用宏**Q_DECLARE_METATYPE(...)**进行声明。
|
||||
|
||||
对于只有单个编辑器控件的特定类型,可以使用如下接口直接注册类型编辑器:
|
||||
|
||||
``` c++
|
||||
QQuickDetailsViewManager::Get()->registerTypeEditor(
|
||||
metaType,
|
||||
[](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
在源代码的`QQuickDetailsViewBasicTypeEditor.cpp`目录下有许多参考示例,比如QDir:
|
||||
|
||||
``` c++
|
||||
registerTypeEditor(
|
||||
QMetaType::fromType<QDir>(),
|
||||
[](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
DirectorySelector{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
对于具有多行或者行为较为复杂的编辑器控件,可以通过派生`IPropertyTypeCustomization`来扩展属性编辑器,它提供两个虚函数:
|
||||
|
||||
``` c++
|
||||
class IPropertyTypeCustomization :public QEnableSharedFromThis<IPropertyTypeCustomization>
|
||||
{
|
||||
public:
|
||||
// 用于装配当前属性行的编辑器
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
|
||||
|
||||
// 用于扩展当前属性行的子项
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
|
||||
};
|
||||
```
|
||||
|
||||
之后使用如下接口进行注册:
|
||||
|
||||
``` c++
|
||||
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- 撤销重做
|
||||
- 容器操作
|
||||
- 扩展元信息
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE})
|
||||
|
||||
add_executable(PropertyEditorExample
|
||||
${PROJECT_SOURCE}
|
||||
)
|
||||
|
||||
set_property(TARGET PropertyEditorExample PROPERTY AUTOMOC ON)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS QuickControls2)
|
||||
|
||||
target_link_libraries(PropertyEditorExample PUBLIC PropertyEditor
|
||||
Qt::Widgets
|
||||
Qt::QuickWidgets
|
||||
Qt::QuickPrivate
|
||||
Qt::QuickTemplates2
|
||||
Qt::QuickTemplates2Private
|
||||
Qt::QuickControls2
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
PropertyEditorExample
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/bin"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef CommonInclude_h__
|
||||
#define CommonInclude_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
|
||||
#define Q_PROPERTY_VAR(Type,Name)\
|
||||
Q_PROPERTY(Type Name READ get##Name WRITE set##Name) \
|
||||
Type get##Name(){ return Name; } \
|
||||
void set##Name(Type var){ \
|
||||
Name = var; \
|
||||
qDebug() <<"Set" <<this <<#Name <<": " <<var; \
|
||||
} \
|
||||
Type Name
|
||||
|
||||
#endif // CommonInclude_h__
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef CustomGadget_h__
|
||||
#define CustomGadget_h__
|
||||
|
||||
#include "CommonInclude.h"
|
||||
|
||||
class QCustomGadget {
|
||||
Q_GADGET
|
||||
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
|
||||
public:
|
||||
Q_PROPERTY_VAR(double, LimitedDouble) = 1;
|
||||
Q_PROPERTY_VAR(QString, Desc) = "This is inline Gadget";
|
||||
};
|
||||
|
||||
static QDebug operator<<(QDebug debug, const QCustomGadget& gadget) {
|
||||
return debug << gadget.LimitedDouble << gadget.Desc;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedPointer<QCustomGadget>);
|
||||
|
||||
#endif // CustomGadget_h__
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef CustomObject_h__
|
||||
#define CustomObject_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <qvectornd.h>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QColor>
|
||||
#include <QMatrix4x4>
|
||||
#include "CommonInclude.h"
|
||||
#include "CustomGadget.h"
|
||||
#include "CustomType.h"
|
||||
|
||||
class QCustomObject :public QObject {
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
|
||||
public:
|
||||
enum QCustomEnum {
|
||||
One,
|
||||
Two,
|
||||
Three
|
||||
};
|
||||
Q_ENUM(QCustomEnum);
|
||||
|
||||
Q_PROPERTY_VAR(int, Int) = 0;
|
||||
Q_PROPERTY_VAR(float, Float) = 1.23f;
|
||||
Q_PROPERTY_VAR(double, LimitedDouble) = 5;
|
||||
Q_PROPERTY_VAR(QString, String);
|
||||
Q_PROPERTY_VAR(QDir, Directory) = QDir(".");
|
||||
Q_PROPERTY_VAR(QVector2D, Vec2) = QVector2D(1, 2);
|
||||
Q_PROPERTY_VAR(QVector3D, Vec3) = QVector3D(1, 2, 3);
|
||||
Q_PROPERTY_VAR(QVector4D, Vec4) = QVector4D(1, 2, 3, 4);
|
||||
Q_PROPERTY_VAR(QMatrix4x4, Mat4);
|
||||
Q_PROPERTY_VAR(QColor, Color);
|
||||
Q_PROPERTY_VAR(QList<QColor>, ColorList) = { Qt::red,Qt::green,Qt::blue };
|
||||
|
||||
typedef QMap<QString, QColor> StringColorMap;
|
||||
Q_PROPERTY_VAR(StringColorMap, ColorMap) = { {"Red",Qt::red},{"Green",Qt::green},{"Blue",Qt::blue} };
|
||||
|
||||
Q_PROPERTY_VAR(QCustomEnum, CustomEnum) = QCustomEnum::One;
|
||||
Q_PROPERTY_VAR(QCustomType, CustomType);
|
||||
Q_PROPERTY_VAR(QCustomGadget, CustomGadget);
|
||||
Q_PROPERTY_VAR(QCustomGadget*, CustomGadgetPtr) = new QCustomGadget;
|
||||
Q_PROPERTY_VAR(QSharedPointer<QCustomGadget>, CustomGadgetSharedPtr) = QSharedPointer<QCustomGadget>::create();
|
||||
Q_PROPERTY_VAR(QCustomObject*, SubCustomObject) = nullptr;
|
||||
};
|
||||
|
||||
#endif // CustomObject_h__
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef CustomType_h__
|
||||
#define CustomType_h__
|
||||
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
|
||||
struct QCustomType {
|
||||
unsigned int ArraySize = 0;
|
||||
QVector<int> Array;
|
||||
};
|
||||
|
||||
static QDebug operator<<(QDebug debug, const QCustomType& it) {
|
||||
return debug << it.Array;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QCustomType)
|
||||
|
||||
#endif // CustomType_h__
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#include "PropertyTypeCustomization_CustomType.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QRandomGenerator>
|
||||
#include "QQuickDetailsViewModel.h"
|
||||
#include "CustomType.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
|
||||
void PropertyTypeCustomization_CustomType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
|
||||
{
|
||||
auto editorSlot = inBuilder->makeNameValueSlot();
|
||||
inPropertyHandle->setupNameEditor(editorSlot.first);
|
||||
auto buttonItem = inBuilder->setupItem(editorSlot.second, R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
Button{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 80
|
||||
height: 20
|
||||
text: "Sort"
|
||||
}
|
||||
)");
|
||||
|
||||
|
||||
QQuickFunctionLibrary::connect(buttonItem, SIGNAL(clicked()), inPropertyHandle, [inPropertyHandle]() {
|
||||
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
|
||||
std::sort(customType.Array.begin(), customType.Array.end());
|
||||
inPropertyHandle->setVar(QVariant::fromValue(customType));
|
||||
if (auto arrayHandle = inPropertyHandle->findChild("Array")) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PropertyTypeCustomization_CustomType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto arrayHandle = inPropertyHandle->findOrCreateChild(
|
||||
QMetaType::fromType<QVector<int>>(),
|
||||
"Array",
|
||||
[inPropertyHandle]() {
|
||||
return QVariant::fromValue(inPropertyHandle->getVar().value<QCustomType>().Array);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
|
||||
customType.Array = var.value<QVector<int>>();
|
||||
inPropertyHandle->setVar(QVariant::fromValue(customType));
|
||||
}
|
||||
);
|
||||
|
||||
auto arraySizeHandle = inPropertyHandle->findOrCreateChild(
|
||||
QMetaType::fromType<unsigned int>(),
|
||||
"ArraySize",
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QCustomType>().ArraySize;
|
||||
},
|
||||
[inPropertyHandle, arrayHandle](QVariant var) {
|
||||
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
|
||||
customType.ArraySize = var.toUInt();
|
||||
customType.Array.resize(customType.ArraySize);
|
||||
for (int i = 0; i < customType.ArraySize; ++i) {
|
||||
customType.Array[i] = QRandomGenerator::global()->bounded(-100000, 100000);
|
||||
}
|
||||
inPropertyHandle->setVar(QVariant::fromValue(customType));
|
||||
arrayHandle->invalidateStructure();
|
||||
}
|
||||
);
|
||||
|
||||
inBuilder->addProperty(arraySizeHandle);
|
||||
inBuilder->addProperty(arrayHandle);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef PropertyTypeCustomization_CustomType_h__
|
||||
#define PropertyTypeCustomization_CustomType_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_CustomType : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override;
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_CustomType_h__
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include <QApplication>
|
||||
#include "QDetailsView.h"
|
||||
#include "CustomObject.h"
|
||||
#include "CustomType.h"
|
||||
#include "PropertyTypeCustomization_CustomType.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QCustomObject obj;
|
||||
obj.setSubCustomObject(new QCustomObject);
|
||||
obj.getSubCustomObject()->setSubCustomObject(new QCustomObject);
|
||||
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
|
||||
QDetailsView view;
|
||||
view.setObject(&obj);
|
||||
view.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<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>
|
||||
<file>resources/Qml/ValueEditor/PointFBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/RectFBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/SizeFBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/BoolBox.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,184 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Shapes
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
// 保持与框架一致的接口
|
||||
property var value: false
|
||||
property int decimals: 0
|
||||
property string text: "Enabled"
|
||||
property color textColor: "black"
|
||||
property color checkedColor: "#2196F3"
|
||||
property color uncheckedColor: "#cccccc"
|
||||
property color checkedBoxColor: "white"
|
||||
property color uncheckedBoxColor: "white"
|
||||
property int boxSize: 16
|
||||
property int spacing: 8
|
||||
property int textSize: 12
|
||||
property alias font: label.font
|
||||
|
||||
// 与框架一致的信号
|
||||
signal asValueChanged(value: var)
|
||||
signal boolValueChanged(bool checked) // 辅助信号,避免与内置信号冲突
|
||||
|
||||
implicitHeight: Math.max(boxSize, label.implicitHeight)
|
||||
implicitWidth: boxSize + spacing + label.implicitWidth
|
||||
|
||||
function toggle() {
|
||||
var newValue = !getBoolValue()
|
||||
setValue(newValue)
|
||||
}
|
||||
|
||||
function setValue(newValue) {
|
||||
var boolValue = getBoolValue()
|
||||
var newBoolValue = toBool(newValue)
|
||||
|
||||
if (boolValue !== newBoolValue) {
|
||||
value = newBoolValue
|
||||
boolValueChanged(newBoolValue)
|
||||
asValueChanged(newBoolValue)
|
||||
}
|
||||
}
|
||||
|
||||
function getBoolValue() {
|
||||
return toBool(value)
|
||||
}
|
||||
|
||||
function toBool(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value
|
||||
} else if (typeof value === 'number') {
|
||||
return value !== 0
|
||||
} else if (typeof value === 'string') {
|
||||
var str = value.toString().toLowerCase()
|
||||
return str === 'true' || str === '1' || str === 'on'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 整个区域的点击事件
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: function(mouse) {
|
||||
control.toggle()
|
||||
mouse.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: control.spacing
|
||||
|
||||
// 复选框
|
||||
Rectangle {
|
||||
id: checkBox
|
||||
width: control.boxSize
|
||||
height: control.boxSize
|
||||
radius: 3
|
||||
border.color: getBoolValue() ? control.checkedColor : control.uncheckedColor
|
||||
border.width: 1
|
||||
color: getBoolValue() ? control.checkedColor : control.uncheckedBoxColor
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
// 勾选标记
|
||||
Shape {
|
||||
anchors.centerIn: parent
|
||||
width: 10
|
||||
height: 8
|
||||
visible: getBoolValue()
|
||||
|
||||
ShapePath {
|
||||
strokeColor: "transparent"
|
||||
fillColor: control.checkedBoxColor
|
||||
strokeWidth: 0
|
||||
|
||||
PathMove { x: 0; y: 4 }
|
||||
PathLine { x: 4; y: 8 }
|
||||
PathLine { x: 10; y: 0 }
|
||||
PathLine { x: 9; y: 0 }
|
||||
PathLine { x: 4; y: 7 }
|
||||
PathLine { x: 1; y: 4 }
|
||||
}
|
||||
}
|
||||
|
||||
// 悬停效果
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: "transparent"
|
||||
border.color: "#888888"
|
||||
border.width: boxMouseArea.containsMouse ? 1 : 0
|
||||
opacity: boxMouseArea.containsMouse ? 0.3 : 0
|
||||
}
|
||||
|
||||
// 复选框自身的点击事件
|
||||
MouseArea {
|
||||
id: boxMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: function(mouse) {
|
||||
control.toggle()
|
||||
mouse.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 标签文本
|
||||
Label {
|
||||
id: label
|
||||
text: control.text
|
||||
color: control.textColor
|
||||
font.pixelSize: control.textSize
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
// 鼠标悬停在文本上时的点击事件
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: function(mouse) {
|
||||
control.toggle()
|
||||
mouse.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘支持
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Space || event.key === Qt.Key_Return) {
|
||||
control.toggle()
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
// 聚焦支持
|
||||
activeFocusOnTab: true
|
||||
|
||||
// 聚焦时的边框
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: control.activeFocus ? "#2196F3" : "transparent"
|
||||
border.width: 2
|
||||
radius: 2
|
||||
visible: control.activeFocus
|
||||
}
|
||||
|
||||
// 当value从外部改变时,更新显示
|
||||
onValueChanged: {
|
||||
var boolValue = getBoolValue()
|
||||
if (checkBox.border.color !== (boolValue ? control.checkedColor : control.uncheckedColor)) {
|
||||
checkBox.border.color = boolValue ? control.checkedColor : control.uncheckedColor
|
||||
checkBox.color = boolValue ? control.checkedColor : control.uncheckedBoxColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
|
||||
Item {
|
||||
id: control
|
||||
property color value
|
||||
implicitHeight: 25
|
||||
implicitWidth: 150
|
||||
signal asValueChanged(text: var)
|
||||
|
||||
// 计算颜色代码(hex格式)
|
||||
function getColorCode(color) {
|
||||
if (color.a === 1) {
|
||||
// 不透明颜色
|
||||
return color.toString().toUpperCase()
|
||||
} else {
|
||||
// 透明颜色
|
||||
return Qt.rgba(color.r, color.g, color.b, color.a).toString()
|
||||
}
|
||||
}
|
||||
|
||||
function setValue(newValue: var) {
|
||||
if (newValue !== value) {
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
|
||||
// 左边颜色显示矩形
|
||||
Rectangle {
|
||||
id: colorRect
|
||||
width: 25
|
||||
height: parent.height
|
||||
radius: 2
|
||||
border.width: 1
|
||||
border.color: Qt.darker(colorRect.color, 1.5)
|
||||
color: value
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
colorDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 右边颜色代码显示(不可编辑)
|
||||
Rectangle {
|
||||
width: parent.width - colorRect.width - parent.spacing
|
||||
height: parent.height
|
||||
radius: 2
|
||||
border.width: 1
|
||||
border.color: "#cccccc"
|
||||
|
||||
Text {
|
||||
id: colorText
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: getColorCode(value)
|
||||
font.pixelSize: 12
|
||||
elide: Text.ElideRight
|
||||
|
||||
// 颜色值改变时更新文本
|
||||
Connections {
|
||||
target: control
|
||||
function onValueChanged() {
|
||||
colorText.text = getColorCode(control.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击整个右侧区域也可以打开颜色选择
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
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,102 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import QtCore
|
||||
import ColorPalette
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
// 属性接口
|
||||
property var value
|
||||
property string fileFilter: "svg(*.svg)" // 文件过滤器
|
||||
property bool selectMultiple: false // 是否多选
|
||||
|
||||
implicitHeight: fileBox.implicitHeight
|
||||
signal asValueChanged(text: var)
|
||||
|
||||
function setValue(newValue: var) {
|
||||
if (newValue !== value) {
|
||||
value = newValue
|
||||
fileBox.value = value
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
LineTextBox {
|
||||
id: fileBox
|
||||
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: {
|
||||
fileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
modality: Qt.WindowModal // 改为窗口模态
|
||||
options: FolderDialog.DontUseNativeDialog // 尝试禁用原生对话框
|
||||
title: control.selectMultiple ? "选择多个文件" : "选择文件"
|
||||
fileMode: control.selectMultiple ? FileDialog.OpenFiles : FileDialog.OpenFile
|
||||
|
||||
// 设置文件过滤器
|
||||
nameFilters: {
|
||||
if (control.fileFilter) {
|
||||
return control.fileFilter.split(";;")
|
||||
}
|
||||
return ["所有文件 (*.*)"]
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (control.selectMultiple) {
|
||||
// 多选模式
|
||||
var filePaths = []
|
||||
|
||||
for (var i = 0; i < selectedFiles.length; i++) {
|
||||
var filePath = selectedFiles[i].toString()
|
||||
filePath = removeFileProtocol(filePath)
|
||||
filePaths.push(filePath)
|
||||
}
|
||||
|
||||
// 多个文件用分号分隔
|
||||
control.setValue(filePaths.join(";"))
|
||||
} else {
|
||||
// 单选模式
|
||||
var filePath = selectedFile.toString()
|
||||
filePath = removeFileProtocol(filePath)
|
||||
control.setValue(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:移除文件协议前缀
|
||||
function removeFileProtocol(path) {
|
||||
if (path.startsWith("file:///")) {
|
||||
return path.substring(8)
|
||||
} else if (path.startsWith("file://")) {
|
||||
return path.substring(7)
|
||||
}
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
|
@ -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,184 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
property var value: Qt.point(0, 0)
|
||||
property int decimals: 2
|
||||
implicitHeight: 25
|
||||
implicitWidth: 120 // 设置默认宽度
|
||||
|
||||
signal asValueChanged(value: var)
|
||||
|
||||
function setValue(newValue: var) {
|
||||
if (!control.value ||
|
||||
Math.abs(control.value.x - newValue.x) > 0.000001 ||
|
||||
Math.abs(control.value.y - newValue.y) > 0.000001) {
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 4 // 减小间距
|
||||
|
||||
Label {
|
||||
text: "X:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 10
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: xBox
|
||||
Layout.preferredWidth: 60 // 减小宽度
|
||||
Layout.preferredHeight: 22
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: control.value ? control.value.x.toFixed(control.decimals) : "0.00"
|
||||
font.pixelSize: 10
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
selectByMouse: true
|
||||
|
||||
// 双击全选的处理
|
||||
MouseArea {
|
||||
id: xMouseArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onDoubleClicked: function(mouse) {
|
||||
parent.selectAll()
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = true
|
||||
}
|
||||
|
||||
onClicked: function(mouse) {
|
||||
// 单击时设置焦点但不全选
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
// 如果不是双击触发的焦点变化,就全选文本
|
||||
if (!xMouseArea.containsPress) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validator: DoubleValidator {
|
||||
bottom: -999999
|
||||
top: 999999
|
||||
decimals: 2
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: xBox.enabled ? "white" : "#f0f0f0"
|
||||
border.color: xBox.activeFocus ? "#2196F3" : "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
var xValue = parseFloat(text)
|
||||
if (!isNaN(xValue) && control.value) {
|
||||
control.setValue(Qt.point(xValue, control.value.y))
|
||||
} else {
|
||||
xBox.text = control.value ? control.value.x.toFixed(control.decimals) : "0.00"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Y:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 10
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: yBox
|
||||
Layout.preferredWidth: 60
|
||||
Layout.preferredHeight: 22
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: control.value ? control.value.y.toFixed(control.decimals) : "0.00"
|
||||
font.pixelSize: 10
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
selectByMouse: true
|
||||
|
||||
// 双击全选的处理
|
||||
MouseArea {
|
||||
id: yMouseArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onDoubleClicked: function(mouse) {
|
||||
parent.selectAll()
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = true
|
||||
}
|
||||
|
||||
onClicked: function(mouse) {
|
||||
// 单击时设置焦点但不全选
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
// 如果不是双击触发的焦点变化,就全选文本
|
||||
if (!yMouseArea.containsPress) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validator: DoubleValidator {
|
||||
bottom: -999999
|
||||
top: 999999
|
||||
decimals: 2
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: yBox.enabled ? "white" : "#f0f0f0"
|
||||
border.color: yBox.activeFocus ? "#2196F3" : "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
var yValue = parseFloat(text)
|
||||
if (!isNaN(yValue) && control.value) {
|
||||
control.setValue(Qt.point(control.value.x, yValue))
|
||||
} else {
|
||||
yBox.text = control.value ? control.value.y.toFixed(control.decimals) : "0.00"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
// 当value从外部改变时,更新输入框显示
|
||||
onValueChanged: {
|
||||
if (value) {
|
||||
if (!xBox.activeFocus) {
|
||||
xBox.text = value.x.toFixed(decimals)
|
||||
}
|
||||
if (!yBox.activeFocus) {
|
||||
yBox.text = value.y.toFixed(decimals)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
property var value: Qt.rect(0, 0, 0, 0)
|
||||
property int decimals: 1
|
||||
implicitHeight: 25
|
||||
implicitWidth: 240
|
||||
|
||||
signal asValueChanged(value: var)
|
||||
|
||||
function setValue(newValue) {
|
||||
if (!control.value ||
|
||||
Math.abs(control.value.x - newValue.x) > 0.000001 ||
|
||||
Math.abs(control.value.y - newValue.y) > 0.000001 ||
|
||||
Math.abs(control.value.width - newValue.width) > 0.000001 ||
|
||||
Math.abs(control.value.height - newValue.height) > 0.000001) {
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: [
|
||||
{ label: "X", prop: "x", validator: [-999999, 999999] },
|
||||
{ label: "Y", prop: "y", validator: [-999999, 999999] },
|
||||
{ label: "W", prop: "width", validator: [0, 999999] },
|
||||
{ label: "H", prop: "height", validator: [0, 999999] }
|
||||
]
|
||||
|
||||
RowLayout {
|
||||
id: inputRow
|
||||
spacing: 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
property string propertyName: modelData.prop
|
||||
property var textField: inputField
|
||||
|
||||
Label {
|
||||
text: modelData.label + ":"
|
||||
font.pixelSize: 10
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: inputField
|
||||
Layout.preferredWidth: 60
|
||||
Layout.preferredHeight: 22
|
||||
text: control.value ? control.value[modelData.prop].toFixed(control.decimals) : "0.0"
|
||||
font.pixelSize: 9
|
||||
padding: 2
|
||||
selectByMouse: true
|
||||
|
||||
// 双击全选的处理
|
||||
MouseArea {
|
||||
id: fieldMouseArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onDoubleClicked: function(mouse) {
|
||||
parent.selectAll()
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = true
|
||||
}
|
||||
|
||||
onClicked: function(mouse) {
|
||||
// 单击时设置焦点但不全选
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
// 如果不是双击触发的焦点变化,就全选文本
|
||||
if (!fieldMouseArea.containsPress) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validator: DoubleValidator {
|
||||
bottom: modelData.validator[0]
|
||||
top: modelData.validator[1]
|
||||
decimals: 2
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
border.color: parent.activeFocus ? "#0066cc" : "#999999"
|
||||
border.width: 1
|
||||
radius: 1
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
var val = parseFloat(text)
|
||||
if (!isNaN(val) && control.value) {
|
||||
var newRect = Qt.rect(
|
||||
modelData.prop === "x" ? val : control.value.x,
|
||||
modelData.prop === "y" ? val : control.value.y,
|
||||
modelData.prop === "width" ? val : control.value.width,
|
||||
modelData.prop === "height" ? val : control.value.height
|
||||
)
|
||||
control.setValue(newRect)
|
||||
} else {
|
||||
text = control.value ? control.value[modelData.prop].toFixed(control.decimals) : "0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
|
||||
onValueChanged: {
|
||||
if (!value) return
|
||||
|
||||
// 通过 Repeater 的 itemAt 方法安全访问
|
||||
var props = ["x", "y", "width", "height"]
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var item = repeater.itemAt(i)
|
||||
if (item && item.textField && !item.textField.activeFocus) {
|
||||
item.textField.text = value[props[i]].toFixed(decimals)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
// 属性定义
|
||||
property var value: Qt.size(0, 0) // QSizeF
|
||||
property int decimals: 1 // 默认小数位为1
|
||||
property int precision: 2 // 精度,允许的小数位数
|
||||
|
||||
// 尺寸相关属性
|
||||
property real minWidth: 0
|
||||
property real maxWidth: 999999
|
||||
property real minHeight: 0
|
||||
property real maxHeight: 999999
|
||||
|
||||
// 步进增量
|
||||
property real widthStep: 1.0
|
||||
property real heightStep: 1.0
|
||||
|
||||
// 是否显示步进按钮
|
||||
property bool showSpinButtons: true
|
||||
|
||||
implicitHeight: 25
|
||||
implicitWidth: showSpinButtons ? 140 : 120
|
||||
|
||||
// 信号 - 重命名避免与内置信号冲突
|
||||
signal asValueChanged(value: var)
|
||||
signal sizeWidthChanged(width: real) // 改为 sizeWidthChanged
|
||||
signal sizeHeightChanged(height: real) // 改为 sizeHeightChanged
|
||||
|
||||
// 获取宽高
|
||||
function getWidth() {
|
||||
return value ? value.width : 0
|
||||
}
|
||||
|
||||
function getHeight() {
|
||||
return value ? value.height : 0
|
||||
}
|
||||
|
||||
// 设置值
|
||||
function setValue(newValue: var) {
|
||||
if (!control.value ||
|
||||
Math.abs(control.value.width - newValue.width) > 0.000001 ||
|
||||
Math.abs(control.value.height - newValue.height) > 0.000001) {
|
||||
|
||||
// 应用边界限制
|
||||
var limitedWidth = Math.max(minWidth, Math.min(maxWidth, newValue.width))
|
||||
var limitedHeight = Math.max(minHeight, Math.min(maxHeight, newValue.height))
|
||||
|
||||
// 保留精度
|
||||
limitedWidth = parseFloat(limitedWidth.toFixed(precision))
|
||||
limitedHeight = parseFloat(limitedHeight.toFixed(precision))
|
||||
|
||||
value = Qt.size(limitedWidth, limitedHeight)
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置宽度
|
||||
function setWidth(width: real) {
|
||||
if (control.value && Math.abs(control.value.width - width) > 0.000001) {
|
||||
var limitedWidth = Math.max(minWidth, Math.min(maxWidth, width))
|
||||
limitedWidth = parseFloat(limitedWidth.toFixed(precision))
|
||||
control.setValue(Qt.size(limitedWidth, control.value.height))
|
||||
}
|
||||
}
|
||||
|
||||
// 设置高度
|
||||
function setHeight(height: real) {
|
||||
if (control.value && Math.abs(control.value.height - height) > 0.000001) {
|
||||
var limitedHeight = Math.max(minHeight, Math.min(maxHeight, height))
|
||||
limitedHeight = parseFloat(limitedHeight.toFixed(precision))
|
||||
control.setValue(Qt.size(control.value.width, limitedHeight))
|
||||
}
|
||||
}
|
||||
|
||||
// 步进增加/减少
|
||||
function stepWidth(positive: bool) {
|
||||
if (!control.value) return
|
||||
var step = positive ? widthStep : -widthStep
|
||||
setWidth(getWidth() + step)
|
||||
}
|
||||
|
||||
function stepHeight(positive: bool) {
|
||||
if (!control.value) return
|
||||
var step = positive ? heightStep : -heightStep
|
||||
setHeight(getHeight() + step)
|
||||
}
|
||||
|
||||
// UI布局
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
|
||||
// 宽度部分
|
||||
Label {
|
||||
text: "W:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 10
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: widthBox
|
||||
Layout.preferredWidth: 60
|
||||
Layout.preferredHeight: 22
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
selectByMouse: true // 允许鼠标选择文本
|
||||
|
||||
text: control.value ? control.value.width.toFixed(control.decimals) : "0.0"
|
||||
font.pixelSize: 10
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
|
||||
// 双击全选的处理
|
||||
MouseArea {
|
||||
id: widthMouseArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onDoubleClicked: function(mouse) {
|
||||
parent.selectAll()
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = true
|
||||
}
|
||||
|
||||
onClicked: function(mouse) {
|
||||
// 单击时设置焦点但不全选
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
// 如果不是双击触发的焦点变化,就全选文本
|
||||
if (!widthMouseArea.containsPress) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证器
|
||||
validator: DoubleValidator {
|
||||
bottom: control.minWidth
|
||||
top: control.maxWidth
|
||||
decimals: control.precision
|
||||
notation: DoubleValidator.StandardNotation
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: widthBox.enabled ? "white" : "#f0f0f0"
|
||||
border.color: widthBox.activeFocus ? "#2196F3" : "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
var num = parseFloat(text)
|
||||
if (!isNaN(num) && control.value) {
|
||||
control.setWidth(num)
|
||||
} else {
|
||||
// 恢复原值
|
||||
widthBox.text = control.value ?
|
||||
control.value.width.toFixed(control.decimals) :
|
||||
"0.0"
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘上下键调整
|
||||
Keys.onUpPressed: function(event) {
|
||||
control.stepWidth(true)
|
||||
event.accepted = true
|
||||
}
|
||||
|
||||
Keys.onDownPressed: function(event) {
|
||||
control.stepWidth(false)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
// 宽度步进按钮
|
||||
Row {
|
||||
visible: control.showSpinButtons
|
||||
spacing: 0
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Button {
|
||||
width: 12
|
||||
height: 11
|
||||
text: "▲"
|
||||
font.pixelSize: 6
|
||||
padding: 0
|
||||
onClicked: control.stepWidth(true)
|
||||
|
||||
background: Rectangle {
|
||||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||||
border.color: "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 12
|
||||
height: 11
|
||||
text: "▼"
|
||||
font.pixelSize: 6
|
||||
padding: 0
|
||||
onClicked: control.stepWidth(false)
|
||||
|
||||
background: Rectangle {
|
||||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||||
border.color: "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 高度部分
|
||||
Label {
|
||||
text: "H:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 10
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: heightBox
|
||||
Layout.preferredWidth: 60
|
||||
Layout.preferredHeight: 22
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
selectByMouse: true // 允许鼠标选择文本
|
||||
|
||||
text: control.value ? control.value.height.toFixed(control.decimals) : "0.0"
|
||||
font.pixelSize: 10
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
|
||||
// 双击全选的处理
|
||||
MouseArea {
|
||||
id: heightMouseArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onDoubleClicked: function(mouse) {
|
||||
parent.selectAll()
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = true
|
||||
}
|
||||
|
||||
onClicked: function(mouse) {
|
||||
// 单击时设置焦点但不全选
|
||||
parent.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
// 如果不是双击触发的焦点变化,就全选文本
|
||||
if (!heightMouseArea.containsPress) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证器
|
||||
validator: DoubleValidator {
|
||||
bottom: control.minHeight
|
||||
top: control.maxHeight
|
||||
decimals: control.precision
|
||||
notation: DoubleValidator.StandardNotation
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: heightBox.enabled ? "white" : "#f0f0f0"
|
||||
border.color: heightBox.activeFocus ? "#2196F3" : "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
var num = parseFloat(text)
|
||||
if (!isNaN(num) && control.value) {
|
||||
control.setHeight(num)
|
||||
} else {
|
||||
// 恢复原值
|
||||
heightBox.text = control.value ?
|
||||
control.value.height.toFixed(control.decimals) :
|
||||
"0.0"
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘上下键调整
|
||||
Keys.onUpPressed: function(event) {
|
||||
control.stepHeight(true)
|
||||
event.accepted = true
|
||||
}
|
||||
|
||||
Keys.onDownPressed: function(event) {
|
||||
control.stepHeight(false)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
// 高度步进按钮
|
||||
Row {
|
||||
visible: control.showSpinButtons
|
||||
spacing: 0
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Button {
|
||||
width: 12
|
||||
height: 11
|
||||
text: "▲"
|
||||
font.pixelSize: 6
|
||||
padding: 0
|
||||
onClicked: control.stepHeight(true)
|
||||
|
||||
background: Rectangle {
|
||||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||||
border.color: "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 12
|
||||
height: 11
|
||||
text: "▼"
|
||||
font.pixelSize: 6
|
||||
padding: 0
|
||||
onClicked: control.stepHeight(false)
|
||||
|
||||
background: Rectangle {
|
||||
color: parent.hovered ? "#e0e0e0" : "transparent"
|
||||
border.color: "#cccccc"
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
// 当value从外部改变时,更新输入框显示
|
||||
onValueChanged: {
|
||||
if (value) {
|
||||
if (!widthBox.activeFocus) {
|
||||
widthBox.text = value.width.toFixed(decimals)
|
||||
}
|
||||
if (!heightBox.activeFocus) {
|
||||
heightBox.text = value.height.toFixed(decimals)
|
||||
}
|
||||
|
||||
// 发出单独的宽高变化信号
|
||||
sizeWidthChanged(value.width)
|
||||
sizeHeightChanged(value.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import ColorPalette
|
||||
|
||||
Item{
|
||||
id: editor
|
||||
property var value
|
||||
property var model
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(newValue !== value){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
id: control
|
||||
anchors.margins: 2
|
||||
anchors.fill: parent
|
||||
model: editor.model
|
||||
onCurrentTextChanged: {
|
||||
setValue(currentText)
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: itemDelegate
|
||||
width: control.width
|
||||
height: 25
|
||||
padding: 5
|
||||
background: Rectangle {
|
||||
color: itemDelegate.highlighted ? ColorPalette.theme.comboBoxItemBackgroundHover
|
||||
: itemDelegate.hovered ? ColorPalette.theme.comboBoxItemBackgroundHover
|
||||
: ColorPalette.theme.comboBoxItemBackground
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 100 }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: modelData
|
||||
color: ColorPalette.theme.textPrimary
|
||||
font: control.font
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter // 文字垂直居中
|
||||
horizontalAlignment: Text.AlignLeft // 文字左对齐
|
||||
padding: 0
|
||||
}
|
||||
highlighted: control.highlightedIndex === index
|
||||
required property int index
|
||||
required property var modelData
|
||||
}
|
||||
|
||||
indicator: Image {
|
||||
id: indicator
|
||||
x: control.width - width/2 - control.rightPadding
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
width: 13
|
||||
height: 13
|
||||
mipmap: true
|
||||
source: "qrc:/resources/Icon/expand.png"
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: ColorPalette.theme.rowIndicator
|
||||
opacity: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 3
|
||||
rightPadding: control.indicator.width + control.spacing
|
||||
|
||||
text: control.displayText
|
||||
font: control.font
|
||||
color: ColorPalette.theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: ColorPalette.theme.comboBoxBackground
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: control.height
|
||||
width: control.width
|
||||
implicitHeight: contentItem.implicitHeight + 5
|
||||
padding : 2
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: control.popup.visible ? control.delegateModel : null
|
||||
currentIndex: control.highlightedIndex
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: ColorPalette.theme.comboBoxBackground
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property vector2d value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(value !== newValue){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
NumberBox{
|
||||
id: xBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.x
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector2d(number, control.value.y))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: yBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.y
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector2d(control.value.x, number))
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property vector3d value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(value !== newValue){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
NumberBox{
|
||||
id: xBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.x
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector3d(number, control.value.y, control.value.z))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: yBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.y
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector3d(control.value.x, number, control.value.z))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: zBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.z
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector3d(control.value.x, control.value.y, number))
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property vector4d value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(value !== newValue){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
NumberBox{
|
||||
id: xBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.x
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(number, control.value.y, control.value.z, control.value.w))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: yBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.y
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(control.value.x, number, control.value.z, control.value.w))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: zBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.z
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(control.value.x, control.value.y, number, control.value.w))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: wBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.w
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(control.value.x, control.value.y, control.value.z, number))
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 122 KiB |
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef IPropertyTypeCustomization_h__
|
||||
#define IPropertyTypeCustomization_h__
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include "export.hpp"
|
||||
|
||||
class QPropertyHandle;
|
||||
class QQuickDetailsViewRowBuilder;
|
||||
class QQuickDetailsViewLayoutBuilder;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC IPropertyTypeCustomization :public QEnableSharedFromThis<IPropertyTypeCustomization>
|
||||
{
|
||||
public:
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
|
||||
};
|
||||
|
||||
#endif // IPropertyTypeCustomization_h__
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef IPropertyHandleImpl_h__
|
||||
#define IPropertyHandleImpl_h__
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QQuickItem>
|
||||
#include <QVariant>
|
||||
#include <QObject>
|
||||
#include "export.hpp"
|
||||
|
||||
class QPropertyHandle;
|
||||
|
||||
class IPropertyHandleImpl{
|
||||
friend class QPropertyHandle;
|
||||
public:
|
||||
enum Type {
|
||||
Null,
|
||||
RawType,
|
||||
Associative,
|
||||
Sequential,
|
||||
Enum,
|
||||
Object,
|
||||
};
|
||||
protected:
|
||||
IPropertyHandleImpl(QPropertyHandle* inHandle);
|
||||
virtual QQuickItem* createNameEditor(QQuickItem* inParent);
|
||||
virtual QQuickItem* createValueEditor(QQuickItem* inParent)= 0;
|
||||
virtual Type type() { return Type::Null; };
|
||||
|
||||
protected:
|
||||
QPropertyHandle* mHandle;
|
||||
};
|
||||
|
||||
#endif // IPropertyHandleImpl_h__
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef QAssociativePropertyHandle_h__
|
||||
#define QAssociativePropertyHandle_h__
|
||||
|
||||
#include "QMetaContainer"
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Associative: public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_Associative(QPropertyHandle* inHandle);
|
||||
|
||||
const QMetaAssociation& metaAssociation() const;
|
||||
|
||||
void appendItem(QString inKey, QVariant inValue);
|
||||
bool renameItem(QString inSrc, QString inDst);
|
||||
void removeItem(QString inKey);
|
||||
|
||||
protected:
|
||||
Type type() override { return Type::Associative; };
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent)override;
|
||||
|
||||
private:
|
||||
QMetaAssociation mMetaAssociation;
|
||||
};
|
||||
|
||||
|
||||
#endif // QAssociativePropertyHandle_h__
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef QPropertyHandleImpl_Enum_h__
|
||||
#define QPropertyHandleImpl_Enum_h__
|
||||
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Enum: public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_Enum(QPropertyHandle* inHandle);
|
||||
|
||||
protected:
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent) override;
|
||||
Type type() override { return Type::Enum; };
|
||||
|
||||
private:
|
||||
QHash<QString, int> mNameToValueMap;
|
||||
QList<QString> mKeys;
|
||||
};
|
||||
|
||||
|
||||
#endif // QPropertyHandleImpl_Enum_h__
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef QPropertyHandleImpl_Object_h__
|
||||
#define QPropertyHandleImpl_Object_h__
|
||||
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Object : public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_Object(QPropertyHandle* inHandle);
|
||||
QObject* getObject();
|
||||
void* getGadget();
|
||||
bool isGadget() const;
|
||||
QObject* getOwnerObject();
|
||||
const QMetaObject* getMetaObject() const;
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent)override;
|
||||
Type type() override { return Type::Object; };
|
||||
void refreshObjectPtr();
|
||||
QVariant& getObjectHolder();
|
||||
private:
|
||||
QVariant mObjectHolder;
|
||||
void* mObjectPtr = nullptr;
|
||||
QObject* mOwnerObject = nullptr;
|
||||
const QMetaObject* mMetaObject = nullptr;
|
||||
bool bIsSharedPointer = false;
|
||||
bool bIsPointer = false;
|
||||
};
|
||||
|
||||
#endif // QPropertyHandleImpl_Object_h__
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef QPropertyHandleImpl_RawType_h__
|
||||
#define QPropertyHandleImpl_RawType_h__
|
||||
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_RawType : public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_RawType(QPropertyHandle* inHandle);
|
||||
protected:
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent) override;
|
||||
Type type() override { return Type::RawType; };
|
||||
};
|
||||
|
||||
#endif // QPropertyHandleImpl_RawType_h__
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef QSequentialPropertyHandle_h__
|
||||
#define QSequentialPropertyHandle_h__
|
||||
|
||||
#include "QMetaContainer"
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandleImpl_Sequential: public IPropertyHandleImpl
|
||||
{
|
||||
public:
|
||||
QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle);
|
||||
|
||||
const QMetaSequence& metaSequence() const;
|
||||
int itemCount();
|
||||
void appendItem(QVariant InVar);
|
||||
void moveItem(int InSrcIndex, int InDstIndex);
|
||||
void removeItem(int InIndex);
|
||||
|
||||
protected:
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent)override;
|
||||
Type type() override { return Type::Sequential; };
|
||||
|
||||
private:
|
||||
QMetaSequence mMetaSequence;
|
||||
};
|
||||
|
||||
#endif // QSequentialPropertyHandle_h__
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef QDetailsView_h__
|
||||
#define QDetailsView_h__
|
||||
|
||||
#include <QWidget>
|
||||
#include <QQuickWidget>
|
||||
#include "QQuickDetailsView.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QDetailsView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QDetailsView(QWidget* parent = nullptr);
|
||||
QQuickDetailsView* getQuickDetailsView() const;
|
||||
void setObject(QObject* inObject);
|
||||
QObject* getObject() const;
|
||||
private:
|
||||
QQuickWidget* mQuickWidget;
|
||||
QQuickDetailsView* mQuickDetailsView;
|
||||
};
|
||||
|
||||
#endif // QDetailsView_h__
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef QDETAILS_VIEW_API_H
|
||||
#define QDETAILS_VIEW_API_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef PROPERTY_EDITOR_STATIC_LIBRARY
|
||||
# define QDETAILS_VIEW_API
|
||||
# else
|
||||
# ifdef QDETAILS_VIEW_SHARED_LIBRARY
|
||||
# define QDETAILS_VIEW_API __declspec(dllexport)
|
||||
# else
|
||||
# define QDETAILS_VIEW_API __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# define QDETAILS_VIEW_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
|
||||
#endif // QDETAILS_VIEW_API_H
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
#ifndef QPropertyHandle_h__
|
||||
#define QPropertyHandle_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QQuickItem>
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
|
||||
|
||||
class IPropertyHandleImpl;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QPropertyHandle : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QVariant Var READ getVar WRITE setVar NOTIFY asVarChanged)
|
||||
public:
|
||||
using Getter = std::function<QVariant()>;
|
||||
using Setter = std::function<void(QVariant)>;
|
||||
|
||||
enum PropertyType {
|
||||
Unknown,
|
||||
RawType,
|
||||
Enum,
|
||||
Sequential,
|
||||
Associative,
|
||||
Object
|
||||
};
|
||||
|
||||
static QPropertyHandle* Find(const QObject* inParent, const QString& inPropertyPath);
|
||||
static QPropertyHandle* FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
|
||||
static QPropertyHandle* FindOrCreate(QObject* inObject);
|
||||
static QPropertyHandle* Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
|
||||
static void Cleanup(QObject* inParent);
|
||||
|
||||
QMetaType getType();
|
||||
PropertyType getPropertyType() const;
|
||||
QString getName();
|
||||
QString getPropertyPath();
|
||||
QString createSubPath(const QString& inSubName);
|
||||
|
||||
void invalidateStructure();
|
||||
|
||||
Q_INVOKABLE QVariant getVar();
|
||||
Q_INVOKABLE void setVar(QVariant var);
|
||||
|
||||
bool hasMetaData(const QString& inName) const;
|
||||
QVariant getMetaData(const QString& inName) const;
|
||||
const QVariantHash& getMetaDataMap() const;
|
||||
|
||||
QPropertyHandle* findChild(QString inPropertyName);
|
||||
QPropertyHandle* findOrCreateChild(QMetaType inType, QString inPropertyName, QPropertyHandle::Getter inGetter, QPropertyHandle::Setter inSetter);
|
||||
|
||||
QQuickItem* setupNameEditor(QQuickItem* inParent);
|
||||
QQuickItem* steupValueEditor(QQuickItem* inParent);
|
||||
|
||||
IPropertyHandleImpl::Type type();
|
||||
QPropertyHandleImpl_Enum* asEnum();
|
||||
QPropertyHandleImpl_Object* asObject();
|
||||
QPropertyHandleImpl_Associative* asAssociative();
|
||||
QPropertyHandleImpl_Sequential* asSequential();
|
||||
|
||||
static PropertyType parserType(QMetaType inType);
|
||||
static QVariant createNewVariant(QMetaType inOutputType);
|
||||
Q_SIGNALS:
|
||||
void asVarChanged(QVariant);
|
||||
void asStructureChanged();
|
||||
void asRequestRollback(QVariant);
|
||||
protected:
|
||||
QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
|
||||
void resloveMetaData();
|
||||
private:
|
||||
QMetaType mType;
|
||||
PropertyType mPropertyType;
|
||||
QString mPropertyPath;
|
||||
Getter mGetter;
|
||||
Setter mSetter;
|
||||
QVariant mInitialValue;
|
||||
QVariantHash mMetaData;
|
||||
QSharedPointer<IPropertyHandleImpl> mImpl;
|
||||
};
|
||||
|
||||
struct DIAGRAM_DESIGNER_PUBLIC ExternalRefCountWithMetaType : public QtSharedPointer::ExternalRefCountData {
|
||||
typedef ExternalRefCountData Parent;
|
||||
QMetaType mMetaType;
|
||||
void* mData;
|
||||
|
||||
static void deleter(ExternalRefCountData* self) {
|
||||
ExternalRefCountWithMetaType* that =
|
||||
static_cast<ExternalRefCountWithMetaType*>(self);
|
||||
that->mMetaType.destroy(that->mData);
|
||||
Q_UNUSED(that); // MSVC warns if T has a trivial destructor
|
||||
}
|
||||
|
||||
static inline ExternalRefCountData* create(QMetaType inMetaType, void* inPtr)
|
||||
{
|
||||
ExternalRefCountWithMetaType* d = static_cast<ExternalRefCountWithMetaType*>(::operator new(sizeof(ExternalRefCountWithMetaType)));
|
||||
|
||||
// initialize the d-pointer sub-object
|
||||
// leave d->data uninitialized
|
||||
new (d) Parent(ExternalRefCountWithMetaType::deleter); // can't throw
|
||||
d->mData = inPtr;
|
||||
d->mMetaType = inMetaType;
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // QPropertyHandle_h__
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef QQuickDetailsView_h__
|
||||
#define QQuickDetailsView_h__
|
||||
|
||||
#include "QQuickTreeViewEx.h"
|
||||
|
||||
class QQuickDetailsViewPrivate;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsView: public QQuickTreeViewEx {
|
||||
Q_OBJECT
|
||||
QML_NAMED_ELEMENT(DetailsView)
|
||||
Q_DISABLE_COPY(QQuickDetailsView)
|
||||
Q_DECLARE_PRIVATE(QQuickDetailsView)
|
||||
Q_PROPERTY(qreal SpliterPencent READ getSpliterPencent WRITE setSpliterPencent NOTIFY asSpliterPencentChanged FINAL)
|
||||
Q_PROPERTY(QObject* Object READ getObject WRITE setObject NOTIFY asObjectChanged FINAL)
|
||||
public:
|
||||
QQuickDetailsView(QQuickItem* parent = nullptr);
|
||||
qreal getSpliterPencent() const;
|
||||
void setSpliterPencent(qreal val);
|
||||
Q_INVOKABLE void setObject(QObject* inObject);
|
||||
Q_INVOKABLE QObject* getObject() const;
|
||||
Q_SIGNALS:
|
||||
void asSpliterPencentChanged(qreal);
|
||||
void asObjectChanged(QObject*);
|
||||
protected:
|
||||
void componentComplete() override;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsView_h__
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef QQuickDetailsViewLayoutBuilder_h__
|
||||
#define QQuickDetailsViewLayoutBuilder_h__
|
||||
|
||||
#include "QQuickDetailsViewRow.h"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewRowBuilder {
|
||||
public:
|
||||
QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem);
|
||||
QPair<QQuickItem*, QQuickItem*> makeNameValueSlot();
|
||||
|
||||
IDetailsViewRow* row() const;
|
||||
QQuickItem* rootItem() const;
|
||||
|
||||
void makePropertyRow(QPropertyHandle* inHandle);
|
||||
QQuickItem* setupItem(QQuickItem* inParent, QString inQmlCode);
|
||||
void setupLabel(QQuickItem* inParent, QString inText);
|
||||
void setHeightProxy(QQuickItem* inProxyItem);
|
||||
private:
|
||||
IDetailsViewRow* mRow = nullptr;
|
||||
QQuickItem* mRootItem = nullptr;
|
||||
};
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewLayoutBuilder {
|
||||
public:
|
||||
QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRootRow);
|
||||
|
||||
IDetailsViewRow* row() const;
|
||||
|
||||
void addCustomRow(std::function<void(QQuickDetailsViewRowBuilder*)> inCustomRowCreator, QString inOverrideName = "");
|
||||
void addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName = "");
|
||||
void addObject(QObject* inObject);
|
||||
private:
|
||||
IDetailsViewRow* mRootRow = nullptr;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewLayoutBuilder_h__
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef QQuickDetailsViewManager_h__
|
||||
#define QQuickDetailsViewManager_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <functional>
|
||||
#include <QMetaType>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "export.hpp"
|
||||
|
||||
class QPropertyHandle;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewManager : public QObject{
|
||||
public:
|
||||
using PropertyTypeCustomizationCreator = std::function<QSharedPointer<IPropertyTypeCustomization>()>;
|
||||
using TypeEditorCreator = std::function<QQuickItem* (QPropertyHandle*, QQuickItem*)>;
|
||||
|
||||
static QQuickDetailsViewManager* Get();
|
||||
|
||||
void initialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
template<typename MetaType, typename IPropertyTypeCustomizationType>
|
||||
void registerPropertyTypeCustomization() {
|
||||
QMetaType metaType = QMetaType::fromType<MetaType>();
|
||||
if (metaType.metaObject()) {
|
||||
mClassCustomizationMap.insert(metaType.metaObject(), []() {
|
||||
return QSharedPointer<IPropertyTypeCustomizationType>::create();
|
||||
});
|
||||
}
|
||||
else {
|
||||
mMetaTypeCustomizationMap.insert(metaType, []() {
|
||||
return QSharedPointer<IPropertyTypeCustomizationType>::create();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
void unregisterPropertyTypeCustomization(const QMetaType& inMetaType);
|
||||
|
||||
void registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator);
|
||||
void unregisterTypeEditor(const QMetaType& inMetaType);
|
||||
|
||||
QQuickItem* createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent);
|
||||
QSharedPointer<IPropertyTypeCustomization> getCustomPropertyType(QPropertyHandle* inHandle);
|
||||
protected:
|
||||
QQuickDetailsViewManager();
|
||||
void RegisterBasicTypeEditor();
|
||||
private:
|
||||
bool mInitialized = false;
|
||||
|
||||
QHash<const QMetaObject*, PropertyTypeCustomizationCreator> mClassCustomizationMap;
|
||||
QHash<QMetaType, PropertyTypeCustomizationCreator> mMetaTypeCustomizationMap;
|
||||
QHash<QMetaType, TypeEditorCreator> mTypeEditorCreatorMap;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewManager_h__
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef QQuickDetailsViewModel_h__
|
||||
#define QQuickDetailsViewModel_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QMetaProperty>
|
||||
#include <QQuickItem>
|
||||
#include "export.hpp"
|
||||
|
||||
class IDetailsViewRow;
|
||||
class QDetailsViewRow_Property;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickDetailsViewModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
enum Roles {
|
||||
name = 0,
|
||||
};
|
||||
friend class IDetailsViewRow;
|
||||
public:
|
||||
QQuickDetailsViewModel(QObject* parent = 0);
|
||||
~QQuickDetailsViewModel() {}
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex& index) const override;
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void setObject(QObject* inObject);
|
||||
QObject* getObject() const;
|
||||
|
||||
QModelIndex indexForRow(IDetailsViewRow* row) const;
|
||||
void updateRowIndex(IDetailsViewRow* row);
|
||||
|
||||
private:
|
||||
QSharedPointer<QDetailsViewRow_Property> mRoot;
|
||||
QObject* mObject;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewModel_h__
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef QQuickDetailsViewRow_h__
|
||||
#define QQuickDetailsViewRow_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QMetaProperty>
|
||||
#include <QQuickItem>
|
||||
#include "QPropertyHandle.h"
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "QQuickDetailsViewModel.h"
|
||||
#include "export.hpp"
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC IDetailsViewRow {
|
||||
friend class QQuickDetailsViewModel;
|
||||
public:
|
||||
~IDetailsViewRow() {};
|
||||
|
||||
void setName(QString inName);
|
||||
virtual QString name();
|
||||
|
||||
virtual void setupItem(QQuickItem* inParent){}
|
||||
virtual void attachChildren() {}
|
||||
virtual void addChild(QSharedPointer<IDetailsViewRow> inChild);
|
||||
|
||||
void clear();
|
||||
QQuickDetailsViewModel* model();
|
||||
QModelIndex modelIndex() const { return mModelIndex; }
|
||||
void setModelIndex(const QModelIndex& index) { mModelIndex = index; }
|
||||
int rowNumber() const {
|
||||
if (!mParent) return -1;
|
||||
return mParent->mChildren.indexOf(const_cast<IDetailsViewRow*>(this));
|
||||
}
|
||||
void invalidateChildren();
|
||||
protected:
|
||||
QString mName;
|
||||
QQuickDetailsViewModel* mModel = nullptr;
|
||||
QModelIndex mModelIndex;
|
||||
IDetailsViewRow* mParent = nullptr;
|
||||
QList<QSharedPointer<IDetailsViewRow>> mChildren;
|
||||
};
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QDetailsViewRow_Property : public IDetailsViewRow {
|
||||
public:
|
||||
QDetailsViewRow_Property(QPropertyHandle* inHandle);
|
||||
~QDetailsViewRow_Property();
|
||||
void setHandle(QPropertyHandle* inHandle);
|
||||
void setupItem(QQuickItem* inParent) override;
|
||||
void attachChildren() override;
|
||||
protected:
|
||||
QPropertyHandle* mHandle = nullptr;
|
||||
QMetaObject::Connection mStructureChangedConnection;
|
||||
QSharedPointer<IPropertyTypeCustomization> mPropertyTypeCustomization;
|
||||
};
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QDetailsViewRow_Custom : public IDetailsViewRow {
|
||||
public:
|
||||
QDetailsViewRow_Custom(std::function<void(QQuickDetailsViewRowBuilder*)> inRowCreator);
|
||||
protected:
|
||||
QString name() override { return "Custom"; }
|
||||
void setupItem(QQuickItem* inParent) override;
|
||||
private:
|
||||
std::function<void(QQuickDetailsViewRowBuilder*)> mRowCreator;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewRow_h__
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef QQuickFunctionLibrary_h__
|
||||
#define QQuickFunctionLibrary_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QQuickItem>
|
||||
#include "export.hpp"
|
||||
|
||||
class QQuickLambdaHolder;
|
||||
class QQuickLambdaHolder_OneParam;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickFunctionLibrary : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QQuickFunctionLibrary* Get();
|
||||
|
||||
Q_INVOKABLE QString numberToString(QVariant var,int precision);
|
||||
Q_INVOKABLE void setCursorPos(qreal x, qreal y);
|
||||
Q_INVOKABLE void setOverrideCursorShape(Qt::CursorShape shape);
|
||||
Q_INVOKABLE void restoreOverrideCursorShape();
|
||||
Q_INVOKABLE void setCursorPosTest(QQuickItem* item, qreal x, qreal y);
|
||||
|
||||
|
||||
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void()> callback);
|
||||
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant)> callback);
|
||||
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant, QVariant)> callback);
|
||||
};
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder : public QObject {
|
||||
Q_OBJECT
|
||||
std::function<void()> mCallback;
|
||||
public:
|
||||
QQuickLambdaHolder(std::function<void()> callback, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mCallback(std::move(callback)) {}
|
||||
|
||||
Q_SLOT void call() { mCallback(); }
|
||||
};
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder_OneParam : public QObject {
|
||||
Q_OBJECT
|
||||
std::function<void(QVariant)> mCallback;
|
||||
public:
|
||||
QQuickLambdaHolder_OneParam(std::function<void(QVariant)> callback, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mCallback(std::move(callback)) {
|
||||
}
|
||||
|
||||
Q_SLOT void call(QVariant inParam0) { mCallback(inParam0); }
|
||||
};
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickLambdaHolder_TwoParams : public QObject {
|
||||
Q_OBJECT
|
||||
std::function<void(QVariant, QVariant)> mCallback;
|
||||
public:
|
||||
QQuickLambdaHolder_TwoParams(std::function<void(QVariant, QVariant)> callback, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mCallback(std::move(callback)) {
|
||||
}
|
||||
|
||||
Q_SLOT void call(QVariant inParam0, QVariant inParam1) { mCallback(inParam0, inParam1); }
|
||||
};
|
||||
|
||||
#endif // QQuickFunctionLibrary_h__
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef QQuickTreeViewEx_h__
|
||||
#define QQuickTreeViewEx_h__
|
||||
|
||||
#include "export.hpp"
|
||||
#include "private/qquicktableview_p.h"
|
||||
|
||||
class QQuickTreeViewExPrivate;
|
||||
|
||||
class DIAGRAM_DESIGNER_PUBLIC QQuickTreeViewEx: public QQuickTableView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QQuickTreeViewEx(QQuickItem* parent = nullptr);
|
||||
~QQuickTreeViewEx() override;
|
||||
|
||||
QModelIndex rootIndex() const;
|
||||
void setRootIndex(const QModelIndex& index);
|
||||
void resetRootIndex();
|
||||
|
||||
Q_INVOKABLE int depth(int row) const;
|
||||
|
||||
Q_INVOKABLE bool isExpanded(int row) const;
|
||||
Q_INVOKABLE void expand(int row);
|
||||
Q_INVOKABLE void collapse(int row);
|
||||
Q_INVOKABLE void toggleExpanded(int row);
|
||||
Q_INVOKABLE void invalidateLayout();
|
||||
|
||||
Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1);
|
||||
Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1);
|
||||
Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex& index);
|
||||
|
||||
Q_INVOKABLE QModelIndex modelIndex(const QPoint& cell) const override;
|
||||
Q_INVOKABLE QPoint cellAtIndex(const QModelIndex& index) const override;
|
||||
|
||||
#if QT_DEPRECATED_SINCE(6, 4)
|
||||
QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
|
||||
Q_REVISION(6, 4) Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void expanded(int row, int depth);
|
||||
void collapsed(int row, bool recursively);
|
||||
Q_REVISION(6, 6) void rootIndexChanged();
|
||||
protected:
|
||||
QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent);
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
private:
|
||||
Q_DISABLE_COPY(QQuickTreeViewEx)
|
||||
Q_DECLARE_PRIVATE(QQuickTreeViewEx)
|
||||
};
|
||||
|
||||
QML_DECLARE_TYPE(QQuickTreeViewEx)
|
||||
|
||||
#endif // QQuickTreeViewEx_h__
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#include "PropertyTypeCustomization_Associative.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "qsequentialiterable.h"
|
||||
#include "qassociativeiterable.h"
|
||||
|
||||
void PropertyTypeCustomization_Associative::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto associativelHandle = inPropertyHandle->asAssociative();
|
||||
QMetaAssociation metaAssociation = associativelHandle->metaAssociation();
|
||||
QVariant varMap = inPropertyHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
for (auto iter = iterable.begin(); iter != iterable.end(); ++iter) {
|
||||
QString key = iter.key().toString();
|
||||
inBuilder->addProperty(inPropertyHandle->findOrCreateChild(
|
||||
metaAssociation.mappedMetaType(),
|
||||
key,
|
||||
[inPropertyHandle, key]() {
|
||||
QVariant varMap = inPropertyHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
return iterable.value(key);
|
||||
},
|
||||
[inPropertyHandle, key, metaAssociation](QVariant var) {
|
||||
QVariant varMap = inPropertyHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
QtPrivate::QVariantTypeCoercer keyCoercer;
|
||||
QtPrivate::QVariantTypeCoercer mappedCoercer;
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
const void* dataPtr = mappedCoercer.coerce(var, metaAssociation.mappedMetaType());
|
||||
metaAssociation.setMappedAtKey(containterPtr, keyCoercer.coerce(key, metaAssociation.keyMetaType()), dataPtr);
|
||||
inPropertyHandle->setVar(varMap);
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_Associative_h__
|
||||
#define PropertyTypeCustomization_Associative_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_Associative : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_Associative_h__
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#include "PropertyTypeCustomization_Matrix4x4.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QMatrix4x4>
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
|
||||
void PropertyTypeCustomization_Matrix4x4::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
//inBuilder->addCustomRow([inPropertyHandle](QQuickDetailsViewRowBuilder* builder) {
|
||||
// auto editorSlot = builder->makeNameValueSlot();
|
||||
// builder->setupLabel(editorSlot.first, "0");
|
||||
// auto valueItem = builder->setupItem(editorSlot.second, R"(
|
||||
// import QtQuick;
|
||||
// import QtQuick.Controls;
|
||||
// import "qrc:/resources/Qml/ValueEditor"
|
||||
// Vec4Box{
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
// width: parent.width
|
||||
// }
|
||||
// )");
|
||||
// builder->setHeightProxy(valueItem);
|
||||
// QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
// valueItem->setProperty("value", mat.row(0));
|
||||
|
||||
// QQuickFunctionLibrary::connect(valueItem, SIGNAL(asValueChanged(QVariant)), inPropertyHandle, [inPropertyHandle](QVariant var) {
|
||||
// QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
// mat.setRow(0, var.value<QVector4D>());
|
||||
// inPropertyHandle->setVar(mat);
|
||||
// });
|
||||
// QQuickFunctionLibrary::connect(inPropertyHandle, SIGNAL(asRequestRollback(QVariant)), valueItem, [inPropertyHandle, valueItem](QVariant var) {
|
||||
// QMatrix4x4 mat = var.value<QMatrix4x4>();
|
||||
// valueItem->setProperty("value", mat.row(0));
|
||||
// });
|
||||
//});
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 0"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(0);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(0, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 1"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(1);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(1, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 2"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(2);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(2, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 3"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(3);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(3, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_Matrix4x4_h__
|
||||
#define PropertyTypeCustomization_Matrix4x4_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_Matrix4x4 : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_Matrix4x4_h__
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#include "PropertyTypeCustomization_ObjectDefault.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
void PropertyTypeCustomization_ObjectDefault::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto objectHandle = inPropertyHandle->asObject();
|
||||
const QMetaObject* metaObject = objectHandle->getMetaObject();
|
||||
if (objectHandle->isGadget()) {
|
||||
for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty prop = metaObject->property(i);
|
||||
QString propName = prop.name();
|
||||
inBuilder->addProperty(
|
||||
inPropertyHandle->findOrCreateChild(
|
||||
prop.metaType(),
|
||||
propName,
|
||||
[this, prop, objectHandle]() {
|
||||
return prop.readOnGadget(objectHandle->getGadget());
|
||||
},
|
||||
[this, prop, objectHandle, inPropertyHandle](QVariant var) {
|
||||
prop.writeOnGadget(objectHandle->getGadget(), var);
|
||||
inPropertyHandle->setVar(objectHandle->getObjectHolder());
|
||||
objectHandle->refreshObjectPtr();
|
||||
}
|
||||
),
|
||||
propName
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (objectHandle->getObject() != nullptr) {
|
||||
for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty prop = metaObject->property(i);
|
||||
QString propName = prop.name();
|
||||
inBuilder->addProperty(
|
||||
inPropertyHandle->findOrCreateChild(
|
||||
prop.metaType(),
|
||||
propName,
|
||||
[this, prop, objectHandle]() {
|
||||
return prop.read(objectHandle->getObject());
|
||||
},
|
||||
[this, prop, objectHandle, inPropertyHandle](QVariant var) {
|
||||
prop.write(objectHandle->getObject(), var);
|
||||
}
|
||||
),
|
||||
propName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_ObjectDefault_h__
|
||||
#define PropertyTypeCustomization_ObjectDefault_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_ObjectDefault : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_ObjectDefault_h__
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "PropertyTypeCustomization_Sequential.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "qsequentialiterable.h"
|
||||
#include "qassociativeiterable.h"
|
||||
|
||||
void PropertyTypeCustomization_Sequential::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto sequentialHandle = inPropertyHandle->asSequential();
|
||||
QVariant varList = inPropertyHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
for (int index = 0; index < iterable.size(); index++) {
|
||||
QString name = QString::number(index);
|
||||
inBuilder->addProperty(inPropertyHandle->findOrCreateChild(
|
||||
sequentialHandle->metaSequence().valueMetaType(),
|
||||
name,
|
||||
[inPropertyHandle, index]() {
|
||||
QVariant varList = inPropertyHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
if (index < 0 || index >= iterable.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
return iterable.at(index);
|
||||
},
|
||||
[inPropertyHandle, index](QVariant var) {
|
||||
QVariant varList = inPropertyHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
if (index >= 0 && index < iterable.size()) {
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
const void* dataPtr = coercer.coerce(var, iterable.valueMetaType());
|
||||
metaSequence.setValueAtIndex(containterPtr, index, dataPtr);
|
||||
inPropertyHandle->setVar(varList);
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_Sequential_h__
|
||||
#define PropertyTypeCustomization_Sequential_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_Sequential : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_Sequential_h__
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#include "IPropertyTypeCustomization.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
|
||||
void IPropertyTypeCustomization::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
|
||||
{
|
||||
inBuilder->makePropertyRow(inPropertyHandle);
|
||||
}
|
||||
|
||||
void IPropertyTypeCustomization::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#include "PropertyHandleImpl/IPropertyHandleImpl.h"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
IPropertyHandleImpl::IPropertyHandleImpl(QPropertyHandle* inHandle):
|
||||
mHandle(inHandle)
|
||||
{
|
||||
}
|
||||
|
||||
QQuickItem* IPropertyHandleImpl::createNameEditor(QQuickItem* inParent)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(inParent);
|
||||
QQmlContext* context = qmlContext(inParent);
|
||||
QQmlComponent nameComp(engine);
|
||||
nameComp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette;
|
||||
Item{
|
||||
implicitHeight: 25
|
||||
width: parent.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
clip: true
|
||||
elide: Text.ElideRight
|
||||
text: model.name
|
||||
color: ColorPalette.theme.labelPrimary
|
||||
}
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(inParent);
|
||||
auto nameEditor = qobject_cast<QQuickItem*>(nameComp.createWithInitialProperties(initialProperties, context));
|
||||
nameEditor->setParentItem(inParent);
|
||||
return nameEditor;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
|
||||
#include <QAssociativeIterable>
|
||||
#include "QBoxLayout"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
QPropertyHandleImpl_Associative::QPropertyHandleImpl_Associative(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
QVariant varMap = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
mMetaAssociation = iterable.metaContainer();
|
||||
}
|
||||
|
||||
const QMetaAssociation& QPropertyHandleImpl_Associative::metaAssociation() const
|
||||
{
|
||||
return mMetaAssociation;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Associative::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool QPropertyHandleImpl_Associative::renameItem(QString inSrc, QString inDst) {
|
||||
bool canRename = false;
|
||||
QVariant varMap = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
if (iterable.containsKey(inSrc) && !iterable.containsKey(inDst)) {
|
||||
canRename = true;
|
||||
QVariant var = iterable.value(inSrc);
|
||||
QtPrivate::QVariantTypeCoercer keyCoercer;
|
||||
QtPrivate::QVariantTypeCoercer mappedCoercer;
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QMetaAssociation metaAssociation = iterable.metaContainer();
|
||||
metaAssociation.removeKey(containterPtr, keyCoercer.coerce(inSrc, QMetaType::fromType<QString>()));
|
||||
metaAssociation.setMappedAtKey(
|
||||
containterPtr,
|
||||
keyCoercer.coerce(inDst, QMetaType::fromType<QString>()),
|
||||
mappedCoercer.coerce(var, var.metaType())
|
||||
);
|
||||
//mHandle->setVar(varMap, QString("Rename: %1 -> %2").arg(inSrc).arg(inDst));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
return canRename;
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Associative::appendItem(QString inKey, QVariant inValue) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varList.value<QAssociativeIterable>();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
QVariant key(inKey);
|
||||
const void* keyDataPtr = coercer.coerce(key, key.metaType());
|
||||
const void* valueDataPtr = coercer.coerce(inValue, inValue.metaType());
|
||||
//metaAssociation.insertKey(containterPtr, keyDataPtr);
|
||||
mMetaAssociation.setMappedAtKey(containterPtr, keyDataPtr, valueDataPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Insert: %2").arg(mHandle->getPath()).arg(inKey));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Associative::removeItem(QString inKey) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varList.value<QAssociativeIterable>();
|
||||
const QMetaAssociation metaAssociation = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
QVariant key(inKey);
|
||||
const void* keyDataPtr = coercer.coerce(key, key.metaType());
|
||||
metaAssociation.removeKey(containterPtr, keyDataPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(inKey));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
|
||||
#include <QComboBox>
|
||||
#include <QMetaEnum>
|
||||
#include <QWidget>
|
||||
#include <QBoxLayout>
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
|
||||
QPropertyHandleImpl_Enum::QPropertyHandleImpl_Enum(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
const QMetaObject* metaObj = mHandle->getType().metaObject();
|
||||
if (metaObj){
|
||||
const QMetaEnum& metaEnum = metaObj->enumerator(metaObj->indexOfEnumerator(QString(mHandle->getType().name()).split("::").last().toLocal8Bit()));
|
||||
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||
mNameToValueMap[metaEnum.key(i)] = metaEnum.value(i);
|
||||
mKeys << metaEnum.key(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Enum::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(inParent);
|
||||
QQmlContext* context = qmlContext(inParent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
TextComboBox{
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(inParent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(inParent);
|
||||
valueEditor->setProperty("value", mHandle->getVar());
|
||||
valueEditor->setProperty("model", mKeys);
|
||||
QObject::connect(valueEditor, SIGNAL(asValueChanged(QVariant)), mHandle, SLOT(setVar(QVariant)));
|
||||
QObject::connect(mHandle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
|
||||
#include <qsequentialiterable.h>
|
||||
#include <QRegularExpression>
|
||||
#include "QPropertyHandle.h"
|
||||
#include <QMetaProperty>
|
||||
|
||||
QPropertyHandleImpl_Object::QPropertyHandleImpl_Object(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
mObjectHolder = mHandle->getVar();
|
||||
QMetaType metaType = mHandle->getType();
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(metaType.name());
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
if (!matchTexts.isEmpty()) {
|
||||
QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
|
||||
mMetaObject = innerMetaType.metaObject();
|
||||
const void* ptr = *(const void**)mObjectHolder.data();
|
||||
bIsSharedPointer = true;
|
||||
bIsPointer = true;
|
||||
if (ptr) {
|
||||
mObjectHolder = QVariant(innerMetaType, mObjectHolder.data());
|
||||
}
|
||||
else {
|
||||
mObjectHolder = QVariant();
|
||||
}
|
||||
}
|
||||
else{
|
||||
bIsPointer = metaType.flags().testFlag(QMetaType::IsPointer);
|
||||
mMetaObject = metaType.metaObject();
|
||||
}
|
||||
mOwnerObject = mHandle->parent();
|
||||
refreshObjectPtr();
|
||||
}
|
||||
|
||||
QObject* QPropertyHandleImpl_Object::getObject()
|
||||
{
|
||||
if(mMetaObject->inherits(&QObject::staticMetaObject))
|
||||
return (QObject*)mObjectPtr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* QPropertyHandleImpl_Object::getGadget()
|
||||
{
|
||||
return mObjectPtr;
|
||||
}
|
||||
|
||||
bool QPropertyHandleImpl_Object::isGadget() const
|
||||
{
|
||||
if (mMetaObject->inherits(&QObject::staticMetaObject))
|
||||
return false;
|
||||
return mObjectPtr != nullptr;
|
||||
}
|
||||
|
||||
QObject* QPropertyHandleImpl_Object::getOwnerObject()
|
||||
{
|
||||
return mOwnerObject;
|
||||
}
|
||||
|
||||
const QMetaObject* QPropertyHandleImpl_Object::getMetaObject() const
|
||||
{
|
||||
return mMetaObject;
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Object::refreshObjectPtr() {
|
||||
mObjectHolder = mHandle->getVar();
|
||||
if (mObjectHolder.isValid()) {
|
||||
if (mMetaObject->inherits(&QObject::staticMetaObject)) {
|
||||
QObject* objectPtr = mObjectHolder.value<QObject*>();
|
||||
if (objectPtr) {
|
||||
mMetaObject = objectPtr->metaObject();
|
||||
}
|
||||
mObjectPtr = objectPtr;
|
||||
mOwnerObject = objectPtr;
|
||||
if (mOwnerObject) {
|
||||
//QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::moveToThread, mOwnerObject, mHandle->thread()));
|
||||
//QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::installEventFilter, mOwnerObject, mHandle));
|
||||
//mOwnerObject->installEventFilter(mHandle);
|
||||
}
|
||||
}
|
||||
else {
|
||||
void* ptr = mObjectHolder.data();
|
||||
if (bIsPointer)
|
||||
ptr = *(void**)mObjectHolder.data();
|
||||
mObjectPtr = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant& QPropertyHandleImpl_Object::getObjectHolder()
|
||||
{
|
||||
return mObjectHolder;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Object::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#include "PropertyHandleImpl/IPropertyHandleImpl.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
QPropertyHandleImpl_RawType::QPropertyHandleImpl_RawType(QPropertyHandle* inHandle)
|
||||
: IPropertyHandleImpl(inHandle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_RawType::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return QQuickDetailsViewManager::Get()->createValueEditor(mHandle, inParent);
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
|
||||
#include <qsequentialiterable.h>
|
||||
#include "QBoxLayout"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
|
||||
QPropertyHandleImpl_Sequential::QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
mMetaSequence = iterable.metaContainer();
|
||||
}
|
||||
|
||||
const QMetaSequence& QPropertyHandleImpl_Sequential::metaSequence() const
|
||||
{
|
||||
return mMetaSequence;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Sequential::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int QPropertyHandleImpl_Sequential::itemCount() {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
return iterable.size();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Sequential::appendItem( QVariant InVar) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
const void* dataPtr = coercer.coerce(InVar, InVar.metaType());
|
||||
metaSequence.addValue(containterPtr, dataPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Append: %2").arg(mHandle->getPath()).arg(metaSequence.size(containterPtr) - 1));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Sequential::moveItem(int InSrcIndex, int InDstIndex) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
QVariant srcVar = iterable.at(InSrcIndex);
|
||||
QVariant dstVar = iterable.at(InDstIndex);
|
||||
metaSequence.setValueAtIndex(containterPtr, InDstIndex, coercer.coerce(srcVar, srcVar.metaType()));
|
||||
metaSequence.setValueAtIndex(containterPtr, InSrcIndex, coercer.coerce(dstVar, dstVar.metaType()));
|
||||
//mHandle->setVar(varList, QString("%1 Move: %2->%3").arg(mHandle->getPath()).arg(InSrcIndex).arg(InDstIndex));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Sequential::removeItem(int InIndex) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
for (int i = InIndex; i < iterable.size() - 1; i++) {
|
||||
QVariant nextVar = iterable.at(i + 1);
|
||||
metaSequence.setValueAtIndex(containterPtr, InIndex, coercer.coerce(nextVar, nextVar.metaType()));
|
||||
}
|
||||
metaSequence.removeValueAtEnd(containterPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(InIndex));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
#include "QDetailsView.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QUrl>
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
QDetailsView::QDetailsView(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, mQuickWidget(nullptr)
|
||||
, mQuickDetailsView(nullptr)
|
||||
{
|
||||
setMinimumSize(200, 200);
|
||||
|
||||
if (!QQuickDetailsViewManager::Get()->isInitialized()) {
|
||||
QQuickDetailsViewManager::Get()->initialize();
|
||||
}
|
||||
|
||||
mQuickWidget = new QQuickWidget(this);
|
||||
mQuickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
|
||||
const QString qmlContent = R"(
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.DetailsView
|
||||
|
||||
|
||||
DetailsView {
|
||||
id: detailsView
|
||||
anchors.fill: parent
|
||||
boundsBehavior: Flickable.OvershootBounds
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
parent: detailsView.parent
|
||||
width : 10
|
||||
anchors.top: detailsView.top
|
||||
anchors.right: detailsView.right
|
||||
anchors.bottom: detailsView.bottom
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
QQmlComponent component(mQuickWidget->engine());
|
||||
component.setData(qmlContent.toUtf8(), QUrl(""));
|
||||
QObject* rootObject = component.create();
|
||||
if (!component.errors().isEmpty()) {
|
||||
qDebug() << component.errorString();
|
||||
}
|
||||
if (rootObject) {
|
||||
mQuickWidget->setSource(QUrl());
|
||||
mQuickWidget->engine()->setObjectOwnership(rootObject, QQmlEngine::CppOwnership);
|
||||
|
||||
QQuickItem* rootItem = qobject_cast<QQuickItem*>(rootObject);
|
||||
if (rootItem) {
|
||||
mQuickWidget->setContent(QUrl(), &component, rootItem);
|
||||
mQuickDetailsView = qobject_cast<QQuickDetailsView*>(rootItem);
|
||||
}
|
||||
}
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(mQuickWidget);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
QQuickDetailsView* QDetailsView::getQuickDetailsView() const
|
||||
{
|
||||
return mQuickDetailsView;
|
||||
}
|
||||
|
||||
void QDetailsView::setObject(QObject* inObject)
|
||||
{
|
||||
QQuickItem* rootObject = mQuickWidget->rootObject();
|
||||
if (rootObject) {
|
||||
rootObject->setProperty("Object", QVariant::fromValue(inObject));
|
||||
}
|
||||
}
|
||||
|
||||
QObject* QDetailsView::getObject() const
|
||||
{
|
||||
QQuickItem* rootObject = mQuickWidget->rootObject();
|
||||
if (rootObject) {
|
||||
return rootObject->property("Object").value<QObject*>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
#include "QPropertyHandle.h"
|
||||
#include <QRegularExpression>
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h"
|
||||
|
||||
QPropertyHandle::QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
|
||||
: mType(inType)
|
||||
, mPropertyPath(inPropertyPath)
|
||||
, mGetter(inGetter)
|
||||
, mSetter(inSetter)
|
||||
{
|
||||
setParent(inParent);
|
||||
setObjectName(inPropertyPath.split(".").back());
|
||||
resloveMetaData();
|
||||
mInitialValue = inGetter();
|
||||
mPropertyType = parserType(inType);
|
||||
switch (mPropertyType)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case PropertyType::RawType:
|
||||
mImpl.reset(new QPropertyHandleImpl_RawType(this));
|
||||
break;
|
||||
case PropertyType::Enum:
|
||||
mImpl.reset(new QPropertyHandleImpl_Enum(this));
|
||||
break;
|
||||
case PropertyType::Sequential:
|
||||
mImpl.reset(new QPropertyHandleImpl_Sequential(this));
|
||||
break;
|
||||
case PropertyType::Associative:
|
||||
mImpl.reset(new QPropertyHandleImpl_Associative(this));
|
||||
break;
|
||||
case PropertyType::Object:
|
||||
mImpl.reset(new QPropertyHandleImpl_Object(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QPropertyHandle::resloveMetaData()
|
||||
{
|
||||
auto metaObj = parent()->metaObject();
|
||||
auto firstField = getPropertyPath().split(".").first();
|
||||
for (int i = 0; i < metaObj->classInfoCount(); i++) {
|
||||
auto metaClassInfo = metaObj->classInfo(i);
|
||||
if (metaClassInfo.name() == firstField) {
|
||||
QStringList fields = QString(metaClassInfo.value()).split(",", Qt::SplitBehaviorFlags::SkipEmptyParts);
|
||||
for (auto field : fields) {
|
||||
QStringList pair = field.split("=");
|
||||
QString key, value;
|
||||
if (pair.size() > 0) {
|
||||
key = pair.first().trimmed();
|
||||
}
|
||||
if (pair.size() > 1) {
|
||||
value = pair[1].trimmed();
|
||||
}
|
||||
mMetaData[key] = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::Find(const QObject* inParent, const QString& inPropertyPath)
|
||||
{
|
||||
for (QObject* child : inParent->children()) {
|
||||
QPropertyHandle* handler = qobject_cast<QPropertyHandle*>(child);
|
||||
if (handler && handler->getPropertyPath() == inPropertyPath) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
|
||||
{
|
||||
QPropertyHandle* handle = Find(inParent, inPropertyPath);
|
||||
if (handle)
|
||||
return handle;
|
||||
return new QPropertyHandle(
|
||||
inParent,
|
||||
inType,
|
||||
inPropertyPath,
|
||||
inGetter,
|
||||
inSetter
|
||||
);
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inObject)
|
||||
{
|
||||
QPropertyHandle* handle = Find(inObject, "");
|
||||
if (handle)
|
||||
return handle;
|
||||
return new QPropertyHandle(
|
||||
inObject,
|
||||
inObject->metaObject()->metaType(),
|
||||
"",
|
||||
[inObject]() {return QVariant::fromValue(inObject); },
|
||||
[inObject](QVariant var) {}
|
||||
);
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
|
||||
{
|
||||
return new QPropertyHandle(inParent, inType, inPropertyPath, inGetter, inSetter);
|
||||
}
|
||||
|
||||
void QPropertyHandle::Cleanup(QObject* inParent)
|
||||
{
|
||||
for (QObject* child : inParent->children()) {
|
||||
QPropertyHandle* handler = qobject_cast<QPropertyHandle*>(child);
|
||||
if (handler) {
|
||||
handler->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMetaType QPropertyHandle::getType()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
QPropertyHandle::PropertyType QPropertyHandle::getPropertyType() const
|
||||
{
|
||||
return mPropertyType;
|
||||
}
|
||||
|
||||
QString QPropertyHandle::getName()
|
||||
{
|
||||
return objectName();
|
||||
}
|
||||
|
||||
QString QPropertyHandle::getPropertyPath()
|
||||
{
|
||||
return mPropertyPath;
|
||||
}
|
||||
|
||||
QString QPropertyHandle::createSubPath(const QString& inSubName)
|
||||
{
|
||||
return getPropertyPath() + "." + inSubName;
|
||||
}
|
||||
|
||||
void QPropertyHandle::invalidateStructure()
|
||||
{
|
||||
Q_EMIT asStructureChanged();
|
||||
}
|
||||
|
||||
QVariant QPropertyHandle::getVar()
|
||||
{
|
||||
return mGetter();
|
||||
}
|
||||
|
||||
void QPropertyHandle::setVar(QVariant var)
|
||||
{
|
||||
QVariant lastVar = mGetter();
|
||||
if (lastVar != var) {
|
||||
mSetter(var);
|
||||
Q_EMIT asVarChanged(var);
|
||||
QVariant currVar = mGetter();
|
||||
if (currVar != var) {
|
||||
Q_EMIT asRequestRollback(currVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QPropertyHandle::hasMetaData(const QString& inName) const
|
||||
{
|
||||
return mMetaData.contains(inName);
|
||||
}
|
||||
|
||||
QVariant QPropertyHandle::getMetaData(const QString& inName) const
|
||||
{
|
||||
return mMetaData.value(inName);
|
||||
}
|
||||
|
||||
const QVariantHash& QPropertyHandle::getMetaDataMap() const
|
||||
{
|
||||
return mMetaData;
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::findChild(QString inPropertyName)
|
||||
{
|
||||
return Find(this->parent(), this->createSubPath(inPropertyName));
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::findOrCreateChild(QMetaType inType, QString inPropertyName, Getter inGetter, Setter inSetter)
|
||||
{
|
||||
QVariant var = inGetter();
|
||||
QObject* object = var.value<QObject*>();
|
||||
QPropertyHandle* childHandle = nullptr;
|
||||
if (object) {
|
||||
childHandle = QPropertyHandle::FindOrCreate(
|
||||
object
|
||||
);
|
||||
childHandle->setObjectName(inPropertyName);
|
||||
}
|
||||
else {
|
||||
childHandle = QPropertyHandle::FindOrCreate(
|
||||
this->parent(),
|
||||
inType,
|
||||
this->createSubPath(inPropertyName),
|
||||
inGetter,
|
||||
inSetter
|
||||
);
|
||||
}
|
||||
|
||||
childHandle->disconnect(this);
|
||||
connect(this, &QPropertyHandle::asVarChanged, childHandle, [childHandle](QVariant) {
|
||||
QVariant var = childHandle->getVar();
|
||||
Q_EMIT childHandle->asRequestRollback(var);
|
||||
});
|
||||
connect(this, &QPropertyHandle::asRequestRollback, childHandle, [childHandle](QVariant) {
|
||||
QVariant var = childHandle->getVar();
|
||||
Q_EMIT childHandle->asRequestRollback(var);
|
||||
});
|
||||
return childHandle;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandle::setupNameEditor(QQuickItem* inParent)
|
||||
{
|
||||
return mImpl->createNameEditor(inParent);
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandle::steupValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return mImpl->createValueEditor(inParent);
|
||||
}
|
||||
|
||||
IPropertyHandleImpl::Type QPropertyHandle::type()
|
||||
{
|
||||
return mImpl->type();
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Enum* QPropertyHandle::asEnum()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Enum*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Object* QPropertyHandle::asObject()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Object*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Associative* QPropertyHandle::asAssociative()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Associative*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Sequential* QPropertyHandle::asSequential()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Sequential*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandle::PropertyType QPropertyHandle::parserType(QMetaType inType)
|
||||
{
|
||||
if (QMetaType::canConvert(inType, QMetaType::fromType<QVariantList>())
|
||||
&& !QMetaType::canConvert(inType, QMetaType::fromType<QString>())
|
||||
) {
|
||||
return Sequential;
|
||||
}
|
||||
else if (QMetaType::canConvert(inType, QMetaType::fromType<QVariantMap>())) {
|
||||
return Associative;
|
||||
}
|
||||
else if (inType.flags() & QMetaType::IsEnumeration) {
|
||||
return Enum;
|
||||
}
|
||||
else {
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(inType.name(), 0, QRegularExpression::MatchType::PartialPreferCompleteMatch, QRegularExpression::AnchorAtOffsetMatchOption);
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
QMetaType innerMetaType;
|
||||
if (!matchTexts.isEmpty()) {
|
||||
QString metaTypeName = matchTexts.back();
|
||||
innerMetaType = QMetaType::fromName(metaTypeName.toLocal8Bit());
|
||||
}
|
||||
if (innerMetaType.metaObject() || inType.metaObject()) {
|
||||
return Object;
|
||||
}
|
||||
else {
|
||||
return RawType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QPropertyHandle::createNewVariant(QMetaType inOutputType)
|
||||
{
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(inOutputType.name());
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
if (!matchTexts.isEmpty()) {
|
||||
QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
|
||||
if (innerMetaType.isValid()) {
|
||||
void* ptr = innerMetaType.create();
|
||||
QVariant sharedPtr(inOutputType);
|
||||
memcpy(sharedPtr.data(), &ptr, sizeof(ptr));
|
||||
QtSharedPointer::ExternalRefCountData* data = ExternalRefCountWithMetaType::create(innerMetaType, ptr);
|
||||
memcpy((char*)sharedPtr.data() + sizeof(ptr), &data, sizeof(data));
|
||||
return sharedPtr;
|
||||
}
|
||||
}
|
||||
else if (inOutputType.flags().testFlag(QMetaType::IsPointer)) {
|
||||
const QMetaObject* metaObject = inOutputType.metaObject();
|
||||
if (metaObject && metaObject->inherits(&QObject::staticMetaObject)) {
|
||||
QObject* obj = metaObject->newInstance();
|
||||
if (obj)
|
||||
return QVariant::fromValue(obj);
|
||||
}
|
||||
QMetaType innerMetaType = QMetaType::fromName(QString(inOutputType.name()).remove("*").toLocal8Bit());
|
||||
if (innerMetaType.isValid()) {
|
||||
void* ptr = innerMetaType.create();
|
||||
QVariant var(inOutputType, ptr);
|
||||
memcpy(var.data(), &ptr, sizeof(ptr));
|
||||
return var;
|
||||
}
|
||||
else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return QVariant(inOutputType);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
#include "QQuickDetailsView.h"
|
||||
#include "private/qqmldata_p.h"
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "qqmlcontext.h"
|
||||
#include "QQuickDetailsViewPrivate.h"
|
||||
#include "QQuickDetailsViewRow.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
|
||||
void QQuickDetailsViewPrivate::initItemCallback(int serializedModelIndex, QObject* object)
|
||||
{
|
||||
QQuickTreeViewExPrivate::initItemCallback(serializedModelIndex, object);
|
||||
auto item = qobject_cast<QQuickItem*>(object);
|
||||
if (!item)
|
||||
return;
|
||||
const QModelIndex& index = m_treeModelToTableModel.mapToModel(serializedModelIndex);;
|
||||
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
|
||||
node->setupItem(item);
|
||||
}
|
||||
|
||||
QQuickDetailsView::QQuickDetailsView(QQuickItem* parent /*= nullptr*/)
|
||||
: QQuickTreeViewEx(*(new QQuickDetailsViewPrivate()),parent)
|
||||
{
|
||||
setModel(QVariant::fromValue(d_func()->mModel));
|
||||
setReuseItems(false);
|
||||
setEditTriggers(QQuickTableView::EditTrigger::DoubleTapped);
|
||||
}
|
||||
|
||||
qreal QQuickDetailsView::getSpliterPencent() const
|
||||
{
|
||||
return d_func()->mSpliterPencent;
|
||||
}
|
||||
|
||||
void QQuickDetailsView::setSpliterPencent(qreal val)
|
||||
{
|
||||
if(val != d_func()->mSpliterPencent){
|
||||
d_func()->mSpliterPencent = val;
|
||||
Q_EMIT asSpliterPencentChanged(val);
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickDetailsView::setObject(QObject* inObject)
|
||||
{
|
||||
if (inObject != d_func()->mModel->getObject()) {
|
||||
d_func()->mModel->setObject(inObject);
|
||||
Q_EMIT asObjectChanged(inObject);
|
||||
}
|
||||
}
|
||||
|
||||
QObject* QQuickDetailsView::getObject() const
|
||||
{
|
||||
return d_func()->mModel->getObject();
|
||||
}
|
||||
|
||||
void QQuickDetailsView::componentComplete()
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(this);
|
||||
QQmlComponent* delegate = new QQmlComponent(engine, this);
|
||||
engine->rootContext()->setContextProperty("helper", QQuickFunctionLibrary::Get());
|
||||
delegate->setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
import QtQuick.DetailsView;
|
||||
Item {
|
||||
id: detailsDelegate
|
||||
readonly property real indent: 20
|
||||
readonly property real padding: 5
|
||||
required property DetailsView detailsView
|
||||
required property int row
|
||||
required property bool isTreeNode
|
||||
required property bool expanded
|
||||
required property int hasChildren
|
||||
required property int depth
|
||||
implicitWidth: detailsView.width
|
||||
implicitHeight: heightProxy ? heightProxy.height + 5 : 30
|
||||
TapHandler {
|
||||
onTapped: detailsView.toggleExpanded(row)
|
||||
}
|
||||
onImplicitHeightChanged: {
|
||||
detailsView.invalidateLayout();
|
||||
}
|
||||
})", QUrl("QQuickDetailsView.componentComplete"));;
|
||||
setDelegate(delegate);
|
||||
QQuickTreeViewEx::componentComplete();
|
||||
}
|
||||
|
|
@ -0,0 +1,468 @@
|
|||
#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<bool>(), [](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"
|
||||
BoolBox{
|
||||
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<QPoint>(), [](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"
|
||||
PointFBox{
|
||||
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<QPointF>(), [](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"
|
||||
PointFBox{
|
||||
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<QRect>(), [](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"
|
||||
RectFBox{
|
||||
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<QRectF>(), [](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"
|
||||
RectFBox{
|
||||
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<QSize>(), [](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"
|
||||
SizeFBox{
|
||||
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<QSizeF>(), [](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"
|
||||
SizeFBox{
|
||||
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;
|
||||
});
|
||||
|
||||
QMetaType::registerConverterFunction(
|
||||
[](const void* src, void* target) -> bool {
|
||||
const QFileInfo& file = *static_cast<const QFileInfo*>(src);
|
||||
QString& str = *static_cast<QString*>(target);
|
||||
str = file.fileName();
|
||||
return true;
|
||||
},
|
||||
QMetaType::fromType<QFileInfo>(),
|
||||
QMetaType::fromType<QString>()
|
||||
);
|
||||
|
||||
QMetaType::registerConverterFunction(
|
||||
[](const void* src, void* target) -> bool {
|
||||
const QString& str = *static_cast<const QString*>(src);
|
||||
QFileInfo& file = *static_cast<QFileInfo*>(target);
|
||||
file = QFileInfo(str);
|
||||
return true;
|
||||
},
|
||||
QMetaType::fromType<QString>(),
|
||||
QMetaType::fromType<QFileInfo>()
|
||||
);
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QFileInfo>(), [](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"
|
||||
FileSelector{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
QQuickDetailsViewRowBuilder::QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem)
|
||||
: mRow(inRow)
|
||||
, mRootItem(inRootItem)
|
||||
{
|
||||
setHeightProxy(nullptr);
|
||||
}
|
||||
|
||||
QPair<QQuickItem*, QQuickItem*> QQuickDetailsViewRowBuilder::makeNameValueSlot()
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(mRootItem);
|
||||
QQmlContext* context = qmlContext(mRootItem);
|
||||
QQmlContext* newContext = new QQmlContext(context, mRootItem);
|
||||
QQmlComponent rootComp(newContext->engine());
|
||||
rootComp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import ColorPalette
|
||||
Rectangle{
|
||||
id: topLevelRect
|
||||
anchors.fill: parent
|
||||
color: hoverHandler.hovered ? ColorPalette.theme.rowBackgroundHover : ColorPalette.theme.rowBackground
|
||||
border.color: ColorPalette.theme.rowBorder
|
||||
border.width: 0.5
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 100 }
|
||||
}
|
||||
HoverHandler { id: hoverHandler }
|
||||
Image {
|
||||
id: indicator
|
||||
visible: detailsDelegate.isTreeNode && detailsDelegate.hasChildren
|
||||
x: padding + (detailsDelegate.depth * detailsDelegate.indent)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
mipmap: true
|
||||
source: detailsDelegate.expanded
|
||||
? "qrc:/resources/Icon/expand.png"
|
||||
: "qrc:/resources/Icon/unexpand.png"
|
||||
|
||||
width: 12
|
||||
height: 12
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: ColorPalette.theme.rowIndicator
|
||||
opacity: 1.0
|
||||
}
|
||||
}
|
||||
Item{
|
||||
id: nameEditorContent
|
||||
height: parent.height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: padding + (detailsDelegate.isTreeNode ? (detailsDelegate.depth + 1) * detailsDelegate.indent : 0)
|
||||
anchors.right: splitter.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Item{
|
||||
id: valueEditorContent
|
||||
implicitHeight: 25
|
||||
anchors.left: splitter.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Rectangle{
|
||||
id: splitter
|
||||
width: 3
|
||||
height: parent.height
|
||||
color : ColorPalette.theme.rowBackground
|
||||
x: detailsView.SpliterPencent * detailsView.width - 1
|
||||
|
||||
Rectangle{
|
||||
id: visibleSplitter
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: ColorPalette.theme.rowSplitter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
hoverEnabled: true
|
||||
cursorShape: containsMouse ? Qt.SplitHCursor : Qt.ArrowCursor
|
||||
anchors.fill: parent
|
||||
drag.target: splitter
|
||||
drag.axis: Drag.XAxis
|
||||
drag.minimumX: 10 + 1
|
||||
drag.maximumX: detailsView.width - 10 - 1
|
||||
onPositionChanged: {
|
||||
detailsView.SpliterPencent = (splitter.x + 1) / detailsView.width
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: gradientBox
|
||||
visible: detailsDelegate.depth > 0
|
||||
width: 5
|
||||
x: padding + (detailsDelegate.depth * detailsDelegate.indent) - 10
|
||||
height: parent.height
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
GradientStop { position: 0.0; color: ColorPalette.theme.rowShadowStart }
|
||||
GradientStop { position: 1.0; color: ColorPalette.theme.rowShadowEnd }
|
||||
}
|
||||
}
|
||||
}
|
||||
)", QUrl());
|
||||
QQuickItem* slotItem = qobject_cast<QQuickItem*>(rootComp.create(newContext));
|
||||
if (!rootComp.errors().isEmpty()) {
|
||||
qDebug() << rootComp.errorString();
|
||||
}
|
||||
slotItem->setParentItem(mRootItem);
|
||||
return { slotItem->childItems()[1] ,slotItem->childItems()[2] };
|
||||
}
|
||||
|
||||
QQuickItem* QQuickDetailsViewRowBuilder::rootItem() const
|
||||
{
|
||||
return mRootItem;
|
||||
}
|
||||
|
||||
IDetailsViewRow* QQuickDetailsViewRowBuilder::row() const
|
||||
{
|
||||
return mRow;
|
||||
}
|
||||
|
||||
QQuickItem* QQuickDetailsViewRowBuilder::setupItem(QQuickItem* inParent, QString inQmlCode)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(inParent);
|
||||
QQmlContext* context = qmlContext(inParent);
|
||||
QQmlComponent comp(engine);
|
||||
QQmlComponent nameComp(engine);
|
||||
comp.setData(inQmlCode.toLocal8Bit(), QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(inParent);
|
||||
auto item = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
item->setParentItem(inParent);
|
||||
return item;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewRowBuilder::setupLabel(QQuickItem* inParent, QString inText)
|
||||
{
|
||||
QQuickItem* lableItem = setupItem(inParent, R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette;
|
||||
Item{
|
||||
property string lableText
|
||||
implicitHeight: 25
|
||||
width: parent.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
clip: true
|
||||
elide: Text.ElideRight
|
||||
text: lableText
|
||||
color: ColorPalette.theme.labelPrimary
|
||||
}
|
||||
}
|
||||
)");
|
||||
lableItem->setProperty("lableText", inText);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewRowBuilder::setHeightProxy(QQuickItem* inProxyItem)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(mRootItem);
|
||||
QQmlContext* context = qmlContext(mRootItem);
|
||||
context->parentContext()->setContextProperty("heightProxy", inProxyItem);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewRowBuilder::makePropertyRow(QPropertyHandle* inHandle)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(mRootItem);
|
||||
QQmlContext* context = qmlContext(mRootItem);
|
||||
QPair<QQuickItem*, QQuickItem*> slotItem = makeNameValueSlot();
|
||||
QQuickItem* nameEditor = inHandle->setupNameEditor(slotItem.first);
|
||||
QQuickItem* valueEditor = inHandle->steupValueEditor(slotItem.second);
|
||||
context->parentContext()->setContextProperty("heightProxy", valueEditor ? valueEditor : nameEditor);
|
||||
}
|
||||
|
||||
QQuickDetailsViewLayoutBuilder::QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRow)
|
||||
: mRootRow(inRow)
|
||||
{
|
||||
}
|
||||
|
||||
IDetailsViewRow* QQuickDetailsViewLayoutBuilder::row() const
|
||||
{
|
||||
return mRootRow;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewLayoutBuilder::addCustomRow(std::function<void(QQuickDetailsViewRowBuilder*)> creator, QString inOverrideName)
|
||||
{
|
||||
QSharedPointer<IDetailsViewRow> child(new QDetailsViewRow_Custom(creator));
|
||||
child->setName(inOverrideName);
|
||||
mRootRow->addChild(child);
|
||||
child->attachChildren();
|
||||
}
|
||||
|
||||
void QQuickDetailsViewLayoutBuilder::addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName)
|
||||
{
|
||||
QSharedPointer<IDetailsViewRow> child(new QDetailsViewRow_Property(inPropertyHandle));
|
||||
child->setName(inOverrideName.isEmpty() ? inPropertyHandle->getName() : inOverrideName);
|
||||
mRootRow->addChild(child);
|
||||
child->attachChildren();
|
||||
}
|
||||
|
||||
void QQuickDetailsViewLayoutBuilder::addObject(QObject* inObject)
|
||||
{
|
||||
addProperty(QPropertyHandle::FindOrCreate(inObject));
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
#include "QQuickDetailsViewMananger.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
#include <QRegularExpression>
|
||||
#include "QQuickDetailsView.h"
|
||||
#include "Customization/PropertyTypeCustomization_Sequential.h"
|
||||
#include "Customization/PropertyTypeCustomization_Associative.h"
|
||||
#include "Customization/PropertyTypeCustomization_ObjectDefault.h"
|
||||
#include "Customization/PropertyTypeCustomization_Matrix4x4.h"
|
||||
#include <QtQuickControls2/QQuickStyle>
|
||||
#include <QMatrix4x4>
|
||||
|
||||
QQuickDetailsViewManager* QQuickDetailsViewManager::Get()
|
||||
{
|
||||
static QQuickDetailsViewManager ins;
|
||||
return &ins;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewManager::initialize()
|
||||
{
|
||||
mInitialized = true;
|
||||
|
||||
#ifdef PROPERTY_EDITOR_STATIC_LIBRARY
|
||||
Q_INIT_RESOURCE(resources);
|
||||
#endif
|
||||
|
||||
QQuickStyle::setStyle("Basic");
|
||||
|
||||
qmlRegisterType<QQuickDetailsView>("QtQuick.DetailsView", 1, 0, "DetailsView");
|
||||
|
||||
qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette.qml"),
|
||||
"ColorPalette",
|
||||
1, 0,
|
||||
"ColorPalette");
|
||||
|
||||
qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette_Light.qml"),
|
||||
"ColorPalette",
|
||||
1, 0,
|
||||
"ColorPalette_Light");
|
||||
|
||||
qmlRegisterSingletonType(QUrl("qrc:/resources/Qml/ColorPalette/ColorPalette_Dark.qml"),
|
||||
"ColorPalette",
|
||||
1, 0,
|
||||
"ColorPalette_Dark");
|
||||
}
|
||||
|
||||
bool QQuickDetailsViewManager::isInitialized() const
|
||||
{
|
||||
return mInitialized;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewManager::unregisterPropertyTypeCustomization(const QMetaType& InMetaType)
|
||||
{
|
||||
if(InMetaType.metaObject())
|
||||
mMetaTypeCustomizationMap.remove(InMetaType);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewManager::registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator)
|
||||
{
|
||||
mTypeEditorCreatorMap.insert(inMetaType, Creator);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewManager::unregisterTypeEditor(const QMetaType& inMetaType)
|
||||
{
|
||||
mTypeEditorCreatorMap.remove(inMetaType);
|
||||
}
|
||||
|
||||
QQuickItem* QQuickDetailsViewManager::createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent)
|
||||
{
|
||||
if (mTypeEditorCreatorMap.contains(inHandle->getType())) {
|
||||
QQuickItem* item = mTypeEditorCreatorMap[inHandle->getType()](inHandle, parent);
|
||||
if (parent) {
|
||||
item->setProperty("anchors.verticalCenter", QVariant::fromValue(parent->property("anchors.verticalCenter")));
|
||||
//item->update();
|
||||
}
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<IPropertyTypeCustomization> QQuickDetailsViewManager::getCustomPropertyType(QPropertyHandle* inHandle)
|
||||
{
|
||||
if (inHandle->getPropertyType() == QPropertyHandle::Sequential) {
|
||||
return QSharedPointer<PropertyTypeCustomization_Sequential>::create();
|
||||
}
|
||||
else if (inHandle->getPropertyType() == QPropertyHandle::Associative) {
|
||||
return QSharedPointer<PropertyTypeCustomization_Associative>::create();
|
||||
}
|
||||
else if (inHandle->getPropertyType() == QPropertyHandle::Object) {
|
||||
const QMetaObject* metaObject = inHandle->metaObject();
|
||||
for (const auto& It : mClassCustomizationMap.asKeyValueRange()) {
|
||||
if (It.first == metaObject) {
|
||||
return It.second();
|
||||
}
|
||||
}
|
||||
for (const auto& It : mClassCustomizationMap.asKeyValueRange()) {
|
||||
if (metaObject->inherits(It.first)) {
|
||||
return It.second();
|
||||
}
|
||||
}
|
||||
return QSharedPointer<PropertyTypeCustomization_ObjectDefault>::create();
|
||||
}
|
||||
else if (inHandle->getPropertyType() == QPropertyHandle::RawType) {
|
||||
const QMetaType& metaType = inHandle->getType();
|
||||
for (const auto& It : mMetaTypeCustomizationMap.asKeyValueRange()) {
|
||||
if (It.first == metaType) {
|
||||
return It.second();
|
||||
}
|
||||
}
|
||||
const QMetaObject* Child = nullptr;
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(metaType.name());
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
QMetaType innerMetaType;
|
||||
if (!matchTexts.isEmpty()) {
|
||||
innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
|
||||
Child = innerMetaType.metaObject();
|
||||
}
|
||||
else {
|
||||
Child = metaType.metaObject();
|
||||
}
|
||||
for (const auto& It : mMetaTypeCustomizationMap.asKeyValueRange()) {
|
||||
const QMetaObject* Parent = It.first.metaObject();
|
||||
if (Parent && Child && Child->inherits(Parent)) {
|
||||
return It.second();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QQuickDetailsViewManager::QQuickDetailsViewManager()
|
||||
{
|
||||
RegisterBasicTypeEditor();
|
||||
|
||||
registerPropertyTypeCustomization<QMatrix4x4, PropertyTypeCustomization_Matrix4x4>();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
#include "QQuickDetailsViewModel.h"
|
||||
#include "QQuickDetailsViewRow.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
|
||||
QQuickDetailsViewModel::QQuickDetailsViewModel(QObject* parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, mRoot(new QDetailsViewRow_Property(nullptr))
|
||||
{
|
||||
mRoot->mModel = this;
|
||||
}
|
||||
|
||||
QVariant QQuickDetailsViewModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
|
||||
return node->name();
|
||||
}
|
||||
|
||||
Qt::ItemFlags QQuickDetailsViewModel::flags(const QModelIndex& index) const {
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
QModelIndex QQuickDetailsViewModel::index(int row, int column, const QModelIndex& parent) const {
|
||||
if (column < 0 || column >= columnCount(parent)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
IDetailsViewRow* parentNode = nullptr;
|
||||
if (!parent.isValid()) {
|
||||
parentNode = mRoot.get();
|
||||
}
|
||||
else {
|
||||
parentNode = static_cast<IDetailsViewRow*>(parent.internalPointer());
|
||||
if (!parentNode) {
|
||||
return QModelIndex();
|
||||
}
|
||||
}
|
||||
|
||||
if (row < 0 || row >= parentNode->mChildren.size()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
IDetailsViewRow* childNode = parentNode->mChildren[row].get();
|
||||
QModelIndex childIndex = createIndex(row, column, childNode);
|
||||
childNode->setModelIndex(childIndex);
|
||||
return childIndex;
|
||||
}
|
||||
|
||||
QModelIndex QQuickDetailsViewModel::parent(const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
|
||||
IDetailsViewRow* parentNode = node->mParent;
|
||||
|
||||
if (!parentNode || parentNode == mRoot.get()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
IDetailsViewRow* grandparent = parentNode->mParent;
|
||||
int parentRow = grandparent ? grandparent->mChildren.indexOf(parentNode) : -1;
|
||||
|
||||
if (parentRow == -1) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex parentIndex = createIndex(parentRow, 0, parentNode);
|
||||
parentNode->setModelIndex(parentIndex);
|
||||
return parentIndex;
|
||||
}
|
||||
|
||||
int QQuickDetailsViewModel::rowCount(const QModelIndex& parent) const {
|
||||
if (!parent.isValid())
|
||||
return mRoot->mChildren.size();
|
||||
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(parent.internalPointer());
|
||||
return node->mChildren.count();
|
||||
}
|
||||
|
||||
void QQuickDetailsViewModel::setObject(QObject* inObject)
|
||||
{
|
||||
mObject = inObject;
|
||||
mRoot->setHandle(QPropertyHandle::FindOrCreate(mObject));
|
||||
mRoot->invalidateChildren();
|
||||
}
|
||||
|
||||
QObject* QQuickDetailsViewModel::getObject() const
|
||||
{
|
||||
return mObject;
|
||||
}
|
||||
|
||||
QModelIndex QQuickDetailsViewModel::indexForRow(IDetailsViewRow* row) const
|
||||
{
|
||||
if (!row || row == mRoot.get()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
IDetailsViewRow* parentRow = row->mParent;
|
||||
if (!parentRow) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int rowNum = -1;
|
||||
for (int i = 0; i < parentRow->mChildren.size(); ++i) {
|
||||
if (parentRow->mChildren[i].data() == row) {
|
||||
rowNum = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rowNum == -1) {
|
||||
return QModelIndex();
|
||||
}
|
||||
QModelIndex parentIndex = indexForRow(parentRow);
|
||||
return createIndex(rowNum, 0, row);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewModel::updateRowIndex(IDetailsViewRow* row)
|
||||
{
|
||||
if (!row) return;
|
||||
|
||||
QModelIndex oldIndex = row->modelIndex();
|
||||
QModelIndex newIndex = indexForRow(row);
|
||||
|
||||
if (oldIndex != newIndex) {
|
||||
row->setModelIndex(newIndex);
|
||||
dataChanged(newIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
int QQuickDetailsViewModel::columnCount(const QModelIndex& parent) const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> QQuickDetailsViewModel::roleNames() const {
|
||||
return {
|
||||
{ Roles::name,"name" },
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef QQuickDetailsViewPrivate_h__
|
||||
#define QQuickDetailsViewPrivate_h__
|
||||
|
||||
#include "private/qquicktreeview_p_p.h"
|
||||
#include "QQuickTreeViewExPrivate.h"
|
||||
#include "QQuickDetailsView.h"
|
||||
#include "QQuickDetailsViewModel.h"
|
||||
|
||||
class QQuickDetailsViewPrivate : public QQuickTreeViewExPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QQuickDetailsView)
|
||||
public:
|
||||
QQuickDetailsViewPrivate();
|
||||
void initItemCallback(int serializedModelIndex, QObject* object) override;
|
||||
void updateRequiredProperties(int serializedModelIndex, QObject* object, bool init) override;
|
||||
private:
|
||||
qreal mSpliterPencent = 0.3;
|
||||
QList<QObject*> mObjects;
|
||||
QQuickDetailsViewModel* mModel;
|
||||
};
|
||||
|
||||
QQuickDetailsViewPrivate::QQuickDetailsViewPrivate()
|
||||
:mModel(new QQuickDetailsViewModel)
|
||||
{
|
||||
}
|
||||
|
||||
void QQuickDetailsViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject* object, bool init)
|
||||
{
|
||||
Q_Q(QQuickDetailsView);
|
||||
const QPoint cell = cellAtModelIndex(serializedModelIndex);
|
||||
const int row = cell.y();
|
||||
const int column = cell.x();
|
||||
setRequiredProperty("row", QVariant::fromValue(serializedModelIndex), serializedModelIndex, object, init);
|
||||
setRequiredProperty("detailsView", QVariant::fromValue(q), serializedModelIndex, object, init);
|
||||
setRequiredProperty("isTreeNode", column == 0, serializedModelIndex, object, init);
|
||||
setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init);
|
||||
setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init);
|
||||
setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init);
|
||||
}
|
||||
|
||||
#endif // QQuickDetailsViewPrivate_h__
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
#include "QQuickDetailsViewRow.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
#include "QQuickDetailsViewModel.h"
|
||||
|
||||
void IDetailsViewRow::setName(QString inName)
|
||||
{
|
||||
mName = inName;
|
||||
}
|
||||
|
||||
QString IDetailsViewRow::name()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
void IDetailsViewRow::addChild(QSharedPointer<IDetailsViewRow> inChild)
|
||||
{
|
||||
mChildren << inChild;
|
||||
inChild->mModel = mModel;
|
||||
inChild->mParent = this;
|
||||
}
|
||||
|
||||
void IDetailsViewRow::clear()
|
||||
{
|
||||
mChildren.clear();
|
||||
}
|
||||
|
||||
QQuickDetailsViewModel* IDetailsViewRow::model()
|
||||
{
|
||||
return mModel;
|
||||
}
|
||||
|
||||
void IDetailsViewRow::invalidateChildren()
|
||||
{
|
||||
if (!mModel) {
|
||||
mChildren.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex parentIndex = mModel->indexForRow(this);
|
||||
if (!parentIndex.isValid()) {
|
||||
parentIndex = QModelIndex();
|
||||
}
|
||||
|
||||
const int oldChildCount = mChildren.size();
|
||||
if (oldChildCount > 0) {
|
||||
mModel->beginRemoveRows(parentIndex, 0, oldChildCount - 1);
|
||||
mChildren.clear();
|
||||
mModel->endRemoveRows();
|
||||
}
|
||||
|
||||
attachChildren();
|
||||
|
||||
const int newChildCount = mChildren.size();
|
||||
if (newChildCount > 0) {
|
||||
mModel->beginInsertRows(parentIndex, 0, newChildCount - 1);
|
||||
for (auto& child : mChildren) {
|
||||
child->mParent = this;
|
||||
child->mModel = mModel;
|
||||
}
|
||||
mModel->endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
QDetailsViewRow_Property::QDetailsViewRow_Property(QPropertyHandle* inHandle)
|
||||
{
|
||||
setHandle(inHandle);
|
||||
}
|
||||
|
||||
QDetailsViewRow_Property::~QDetailsViewRow_Property()
|
||||
{
|
||||
if (mStructureChangedConnection) {
|
||||
QObject::disconnect(mStructureChangedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QDetailsViewRow_Property::setupItem(QQuickItem* inParent)
|
||||
{
|
||||
QQuickDetailsViewRowBuilder builder(this, inParent);
|
||||
if (mPropertyTypeCustomization) {
|
||||
mPropertyTypeCustomization->customizeHeaderRow(mHandle, &builder);
|
||||
return;
|
||||
}
|
||||
builder.makePropertyRow(mHandle);
|
||||
}
|
||||
|
||||
void QDetailsViewRow_Property::attachChildren()
|
||||
{
|
||||
if (mPropertyTypeCustomization){
|
||||
QQuickDetailsViewLayoutBuilder builder(this);
|
||||
mPropertyTypeCustomization->customizeChildren(mHandle, &builder);
|
||||
}
|
||||
}
|
||||
|
||||
void QDetailsViewRow_Property::setHandle(QPropertyHandle* inHandle)
|
||||
{
|
||||
mHandle = inHandle;
|
||||
if (!inHandle)
|
||||
return;
|
||||
mPropertyTypeCustomization = QQuickDetailsViewManager::Get()->getCustomPropertyType(inHandle);
|
||||
mStructureChangedConnection = QObject::connect(inHandle, &QPropertyHandle::asStructureChanged, [this]() {
|
||||
invalidateChildren();
|
||||
});
|
||||
}
|
||||
|
||||
QDetailsViewRow_Custom::QDetailsViewRow_Custom(std::function<void(QQuickDetailsViewRowBuilder*)> inRowCreator)
|
||||
: mRowCreator(inRowCreator)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QDetailsViewRow_Custom::setupItem(QQuickItem* inParent)
|
||||
{
|
||||
if (mRowCreator) {
|
||||
QQuickDetailsViewRowBuilder builder(this, inParent);
|
||||
mRowCreator(&builder);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#include "QQuickFunctionLibrary.h"
|
||||
#include <QCursor>
|
||||
#include <QApplication>
|
||||
|
||||
QQuickFunctionLibrary* QQuickFunctionLibrary::Get()
|
||||
{
|
||||
static QQuickFunctionLibrary Ins;
|
||||
return &Ins;
|
||||
}
|
||||
|
||||
QString QQuickFunctionLibrary::numberToString(QVariant var, int precision)
|
||||
{
|
||||
double value = var.toDouble();
|
||||
return QString::number(value, 'f', precision);
|
||||
}
|
||||
|
||||
void QQuickFunctionLibrary::setCursorPos(qreal x, qreal y)
|
||||
{
|
||||
QCursor::setPos(x, y);
|
||||
}
|
||||
|
||||
void QQuickFunctionLibrary::setOverrideCursorShape(Qt::CursorShape shape)
|
||||
{
|
||||
qApp->setOverrideCursor(shape);
|
||||
}
|
||||
|
||||
void QQuickFunctionLibrary::restoreOverrideCursorShape()
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void QQuickFunctionLibrary::setCursorPosTest(QQuickItem* item, qreal x, qreal y)
|
||||
{
|
||||
QPointF global = item->mapToGlobal(x, y);
|
||||
QCursor::setPos(global.x(), global.y());
|
||||
qDebug() << x << y << global << QCursor::pos();
|
||||
}
|
||||
|
||||
QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function<void()> callback)
|
||||
{
|
||||
if (!sender)
|
||||
return {};
|
||||
return QObject::connect(sender, signal, new QQuickLambdaHolder(callback, receiver ? receiver : sender), SLOT(call()));
|
||||
}
|
||||
|
||||
QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant)> callback)
|
||||
{
|
||||
if (!sender)
|
||||
return {};
|
||||
return QObject::connect(sender, signal, new QQuickLambdaHolder_OneParam(callback, receiver ? receiver : sender), SLOT(call(QVariant)));
|
||||
}
|
||||
|
||||
QMetaObject::Connection QQuickFunctionLibrary::connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant, QVariant)> callback)
|
||||
{
|
||||
if (!sender)
|
||||
return {};
|
||||
return QObject::connect(sender, signal, new QQuickLambdaHolder_TwoParams(callback, receiver ? receiver : sender), SLOT(call(QVariant, QVariant)));
|
||||
}
|
||||
|
|
@ -0,0 +1,480 @@
|
|||
#include "QQuickTreeViewEx.h"
|
||||
#include "QQuickTreeViewExPrivate.h"
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtQml/qqmlcontext.h>
|
||||
#include <QtQuick/private/qquicktaphandler_p.h>
|
||||
#include <QtQmlModels/private/qqmltreemodeltotablemodel_p_p.h>
|
||||
|
||||
// Hard-code the tree column to be 0 for now
|
||||
static const int kTreeColumn = 0;
|
||||
|
||||
|
||||
QQuickTreeViewExPrivate::QQuickTreeViewExPrivate()
|
||||
: QQuickTableViewPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
QQuickTreeViewExPrivate::~QQuickTreeViewExPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
QVariant QQuickTreeViewExPrivate::modelImpl() const
|
||||
{
|
||||
return m_assignedModel;
|
||||
}
|
||||
|
||||
void QQuickTreeViewExPrivate::setModelImpl(const QVariant& newModel)
|
||||
{
|
||||
Q_Q(QQuickTreeViewEx);
|
||||
|
||||
if (newModel == m_assignedModel)
|
||||
return;
|
||||
|
||||
m_assignedModel = newModel;
|
||||
QVariant effectiveModel = m_assignedModel;
|
||||
if (effectiveModel.userType() == qMetaTypeId<QJSValue>())
|
||||
effectiveModel = effectiveModel.value<QJSValue>().toVariant();
|
||||
|
||||
if (effectiveModel.isNull())
|
||||
m_treeModelToTableModel.setModel(nullptr);
|
||||
else if (const auto qaim = qvariant_cast<QAbstractItemModel*>(effectiveModel))
|
||||
m_treeModelToTableModel.setModel(qaim);
|
||||
else
|
||||
qmlWarning(q) << "TreeView only accepts a model of type QAbstractItemModel";
|
||||
|
||||
|
||||
scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
|
||||
emit q->modelChanged();
|
||||
}
|
||||
|
||||
void QQuickTreeViewExPrivate::initItemCallback(int serializedModelIndex, QObject* object)
|
||||
{
|
||||
Q_Q(QQuickTreeViewEx);
|
||||
updateRequiredProperties(serializedModelIndex, object, true);
|
||||
QQuickTableViewPrivate::initItemCallback(serializedModelIndex, object);
|
||||
}
|
||||
|
||||
void QQuickTreeViewExPrivate::itemReusedCallback(int serializedModelIndex, QObject* object)
|
||||
{
|
||||
updateRequiredProperties(serializedModelIndex, object, false);
|
||||
QQuickTableViewPrivate::itemReusedCallback(serializedModelIndex, object);
|
||||
}
|
||||
|
||||
void QQuickTreeViewExPrivate::dataChangedCallback(
|
||||
const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
|
||||
{
|
||||
Q_Q(QQuickTreeViewEx);
|
||||
Q_UNUSED(roles);
|
||||
|
||||
for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
|
||||
for (int column = topLeft.column(); column <= bottomRight.column(); ++column) {
|
||||
const QPoint cell(column, row);
|
||||
auto item = q->itemAtCell(cell);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
const int serializedModelIndex = modelIndexAtCell(QPoint(column, row));
|
||||
updateRequiredProperties(serializedModelIndex, item, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickTreeViewExPrivate::updateRequiredProperties(int serializedModelIndex, QObject* object, bool init)
|
||||
{
|
||||
Q_Q(QQuickTreeViewEx);
|
||||
const QPoint cell = cellAtModelIndex(serializedModelIndex);
|
||||
const int row = cell.y();
|
||||
const int column = cell.x();
|
||||
|
||||
setRequiredProperty("treeView", QVariant::fromValue(q), serializedModelIndex, object, init);
|
||||
setRequiredProperty("isTreeNode", column == kTreeColumn, serializedModelIndex, object, init);
|
||||
setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init);
|
||||
setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init);
|
||||
setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init);
|
||||
}
|
||||
|
||||
void QQuickTreeViewExPrivate::updateSelection(const QRect& oldSelection, const QRect& newSelection)
|
||||
{
|
||||
Q_Q(QQuickTreeViewEx);
|
||||
|
||||
const QRect oldRect = oldSelection.normalized();
|
||||
const QRect newRect = newSelection.normalized();
|
||||
|
||||
if (oldSelection == newSelection)
|
||||
return;
|
||||
|
||||
// Select the rows inside newRect that doesn't overlap with oldRect
|
||||
for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) {
|
||||
if (oldRect.y() != -1 && oldRect.y() <= row && row <= oldRect.y() + oldRect.height())
|
||||
continue;
|
||||
const QModelIndex startIndex = q->index(row, newRect.x());
|
||||
const QModelIndex endIndex = q->index(row, newRect.x() + newRect.width());
|
||||
selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
|
||||
}
|
||||
|
||||
if (oldRect.x() != -1) {
|
||||
// Since oldRect is valid, this update is a continuation of an already existing selection!
|
||||
|
||||
// Select the columns inside newRect that don't overlap with oldRect
|
||||
for (int column = newRect.x(); column <= newRect.x() + newRect.width(); ++column) {
|
||||
if (oldRect.x() <= column && column <= oldRect.x() + oldRect.width())
|
||||
continue;
|
||||
for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
|
||||
selectionModel->select(q->index(row, column), QItemSelectionModel::Select);
|
||||
}
|
||||
|
||||
// Unselect the rows inside oldRect that don't overlap with newRect
|
||||
for (int row = oldRect.y(); row <= oldRect.y() + oldRect.height(); ++row) {
|
||||
if (newRect.y() <= row && row <= newRect.y() + newRect.height())
|
||||
continue;
|
||||
const QModelIndex startIndex = q->index(row, oldRect.x());
|
||||
const QModelIndex endIndex = q->index(row, oldRect.x() + oldRect.width());
|
||||
selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
|
||||
}
|
||||
|
||||
// Unselect the columns inside oldRect that don't overlap with newRect
|
||||
for (int column = oldRect.x(); column <= oldRect.x() + oldRect.width(); ++column) {
|
||||
if (newRect.x() <= column && column <= newRect.x() + newRect.width())
|
||||
continue;
|
||||
// Since we're not allowed to call select/unselect on the selectionModel with
|
||||
// indices from different parents, and since indicies from different parents are
|
||||
// expected when working with trees, we need to unselect the indices in the column
|
||||
// one by one, rather than the whole column in one go. This, however, can cause a
|
||||
// lot of selection fragments in the selectionModel, which eventually can hurt
|
||||
// performance. But large selections containing a lot of columns is not normally
|
||||
// the case for a treeview, so accept this potential corner case for now.
|
||||
for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
|
||||
selectionModel->select(q->index(row, column), QItemSelectionModel::Deselect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQuickTreeViewEx::QQuickTreeViewEx(QQuickItem* parent)
|
||||
: QQuickTableView(*(new QQuickTreeViewExPrivate), parent)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
|
||||
setSelectionBehavior(SelectRows);
|
||||
setEditTriggers(EditKeyPressed);
|
||||
|
||||
// Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and
|
||||
// never the actual tree model that is assigned to us by the application.
|
||||
const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
|
||||
d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
|
||||
QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
|
||||
d, &QQuickTreeViewExPrivate::dataChangedCallback);
|
||||
QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
|
||||
this, &QQuickTreeViewEx::rootIndexChanged);
|
||||
|
||||
auto tapHandler = new QQuickTapHandler(this);
|
||||
tapHandler->setAcceptedModifiers(Qt::NoModifier);
|
||||
connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler] {
|
||||
if (!pointerNavigationEnabled())
|
||||
return;
|
||||
if (editTriggers() & DoubleTapped)
|
||||
return;
|
||||
|
||||
const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
|
||||
toggleExpanded(row);
|
||||
});
|
||||
}
|
||||
|
||||
QQuickTreeViewEx::QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent)
|
||||
: QQuickTableView(dd, parent)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
|
||||
setSelectionBehavior(SelectRows);
|
||||
setEditTriggers(EditKeyPressed);
|
||||
|
||||
// Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and
|
||||
// never the actual tree model that is assigned to us by the application.
|
||||
const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
|
||||
d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
|
||||
QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
|
||||
d, &QQuickTreeViewExPrivate::dataChangedCallback);
|
||||
QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
|
||||
this, &QQuickTreeViewEx::rootIndexChanged);
|
||||
|
||||
auto tapHandler = new QQuickTapHandler(this);
|
||||
tapHandler->setAcceptedModifiers(Qt::NoModifier);
|
||||
connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler] {
|
||||
if (!pointerNavigationEnabled())
|
||||
return;
|
||||
if (editTriggers() & DoubleTapped)
|
||||
return;
|
||||
|
||||
const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
|
||||
toggleExpanded(row);
|
||||
});
|
||||
d_func()->init();
|
||||
}
|
||||
|
||||
QQuickTreeViewEx::~QQuickTreeViewEx()
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex QQuickTreeViewEx::rootIndex() const
|
||||
{
|
||||
return d_func()->m_treeModelToTableModel.rootIndex();
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::setRootIndex(const QModelIndex& index)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
d->m_treeModelToTableModel.setRootIndex(index);
|
||||
positionViewAtCell({ 0, 0 }, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::resetRootIndex()
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
d->m_treeModelToTableModel.resetRootIndex();
|
||||
positionViewAtCell({ 0, 0 }, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
|
||||
}
|
||||
|
||||
int QQuickTreeViewEx::depth(int row) const
|
||||
{
|
||||
Q_D(const QQuickTreeViewEx);
|
||||
if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
|
||||
return -1;
|
||||
|
||||
return d->m_treeModelToTableModel.depthAtRow(row);
|
||||
}
|
||||
|
||||
bool QQuickTreeViewEx::isExpanded(int row) const
|
||||
{
|
||||
Q_D(const QQuickTreeViewEx);
|
||||
if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
|
||||
return false;
|
||||
|
||||
return d->m_treeModelToTableModel.isExpanded(row);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::expand(int row)
|
||||
{
|
||||
if (row >= 0)
|
||||
expandRecursively(row, 1);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::expandRecursively(int row, int depth)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
if (row >= d->m_treeModelToTableModel.rowCount())
|
||||
return;
|
||||
if (row < 0 && row != -1)
|
||||
return;
|
||||
if (depth == 0 || depth < -1)
|
||||
return;
|
||||
|
||||
auto expandRowRecursively = [this, d, depth](int startRow) {
|
||||
d->m_treeModelToTableModel.expandRecursively(startRow, depth);
|
||||
// Update the expanded state of the startRow. The descendant rows that gets
|
||||
// expanded will get the correct state set from initItem/itemReused instead.
|
||||
for (int c = leftColumn(); c <= rightColumn(); ++c) {
|
||||
const QPoint treeNodeCell(c, startRow);
|
||||
if (const auto item = itemAtCell(treeNodeCell))
|
||||
d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
|
||||
}
|
||||
};
|
||||
|
||||
if (row >= 0) {
|
||||
// Expand only one row recursively
|
||||
const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
|
||||
if (isExpanded && depth == 1)
|
||||
return;
|
||||
expandRowRecursively(row);
|
||||
}
|
||||
else {
|
||||
// Expand all root nodes recursively
|
||||
const auto model = d->m_treeModelToTableModel.model();
|
||||
for (int r = 0; r < model->rowCount(); ++r) {
|
||||
const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
|
||||
if (rootRow != -1)
|
||||
expandRowRecursively(rootRow);
|
||||
}
|
||||
}
|
||||
|
||||
emit expanded(row, depth);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::expandToIndex(const QModelIndex& index)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
|
||||
if (!index.isValid()) {
|
||||
qmlWarning(this) << "index is not valid: " << index;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index.model() != d->m_treeModelToTableModel.model()) {
|
||||
qmlWarning(this) << "index doesn't belong to correct model: " << index;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rowAtIndex(index) != -1) {
|
||||
// index is already visible
|
||||
return;
|
||||
}
|
||||
|
||||
int depth = 1;
|
||||
QModelIndex parent = index.parent();
|
||||
int row = rowAtIndex(parent);
|
||||
|
||||
while (parent.isValid()) {
|
||||
if (row != -1) {
|
||||
// The node is already visible, since it maps to a row in the table!
|
||||
d->m_treeModelToTableModel.expandRow(row);
|
||||
|
||||
// Update the state of the already existing delegate item
|
||||
for (int c = leftColumn(); c <= rightColumn(); ++c) {
|
||||
const QPoint treeNodeCell(c, row);
|
||||
if (const auto item = itemAtCell(treeNodeCell))
|
||||
d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
|
||||
}
|
||||
|
||||
// When we hit a node that is visible, we know that all other nodes
|
||||
// up to the parent have to be visible as well, so we can stop.
|
||||
break;
|
||||
}
|
||||
else {
|
||||
d->m_treeModelToTableModel.expand(parent);
|
||||
parent = parent.parent();
|
||||
row = rowAtIndex(parent);
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
|
||||
emit expanded(row, depth);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::collapse(int row)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
|
||||
return;
|
||||
|
||||
if (!d->m_treeModelToTableModel.isExpanded(row))
|
||||
return;
|
||||
|
||||
d_func()->m_treeModelToTableModel.collapseRow(row);
|
||||
|
||||
for (int c = leftColumn(); c <= rightColumn(); ++c) {
|
||||
const QPoint treeNodeCell(c, row);
|
||||
if (const auto item = itemAtCell(treeNodeCell))
|
||||
d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
|
||||
}
|
||||
|
||||
emit collapsed(row, false);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::collapseRecursively(int row)
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
if (row >= d->m_treeModelToTableModel.rowCount())
|
||||
return;
|
||||
if (row < 0 && row != -1)
|
||||
return;
|
||||
|
||||
auto collapseRowRecursive = [this, d](int startRow) {
|
||||
// Always collapse descendants recursively,
|
||||
// even if the top row itself is already collapsed.
|
||||
d->m_treeModelToTableModel.collapseRecursively(startRow);
|
||||
// Update the expanded state of the (still visible) startRow
|
||||
for (int c = leftColumn(); c <= rightColumn(); ++c) {
|
||||
const QPoint treeNodeCell(c, startRow);
|
||||
if (const auto item = itemAtCell(treeNodeCell))
|
||||
d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
|
||||
}
|
||||
};
|
||||
|
||||
if (row >= 0) {
|
||||
collapseRowRecursive(row);
|
||||
}
|
||||
else {
|
||||
// Collapse all root nodes recursively
|
||||
const auto model = d->m_treeModelToTableModel.model();
|
||||
for (int r = 0; r < model->rowCount(); ++r) {
|
||||
const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
|
||||
if (rootRow != -1)
|
||||
collapseRowRecursive(rootRow);
|
||||
}
|
||||
}
|
||||
|
||||
emit collapsed(row, true);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::toggleExpanded(int row)
|
||||
{
|
||||
if (isExpanded(row))
|
||||
collapse(row);
|
||||
else
|
||||
expand(row);
|
||||
}
|
||||
|
||||
void QQuickTreeViewEx::invalidateLayout()
|
||||
{
|
||||
Q_D(QQuickTreeViewEx);
|
||||
d->forceLayout(false);
|
||||
}
|
||||
|
||||
QModelIndex QQuickTreeViewEx::modelIndex(const QPoint& cell) const
|
||||
{
|
||||
Q_D(const QQuickTreeViewEx);
|
||||
const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
|
||||
return d->m_treeModelToTableModel.mapToModel(tableIndex);
|
||||
}
|
||||
|
||||
QPoint QQuickTreeViewEx::cellAtIndex(const QModelIndex& index) const
|
||||
{
|
||||
const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
|
||||
return QPoint(tableIndex.column(), tableIndex.row());
|
||||
}
|
||||
|
||||
#if QT_DEPRECATED_SINCE(6, 4)
|
||||
QModelIndex QQuickTreeViewEx::modelIndex(int row, int column) const
|
||||
{
|
||||
static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
|
||||
if (compat6_4) {
|
||||
// XXX Qt 7: Remove this compatibility path here and in QQuickTableView.
|
||||
// In Qt 6.4.0 and 6.4.1, a source incompatible change led to row and column
|
||||
// being documented to be specified in the opposite order.
|
||||
// QT_QUICK_TABLEVIEW_COMPAT_VERSION can therefore be set to force tableview
|
||||
// to continue accepting calls to modelIndex(column, row).
|
||||
return modelIndex({ row, column });
|
||||
}
|
||||
else {
|
||||
qmlWarning(this) << "modelIndex(row, column) is deprecated. "
|
||||
"Use index(row, column) instead. For more information, see "
|
||||
"https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
|
||||
return modelIndex({ column, row });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void QQuickTreeViewEx::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
if (!keyNavigationEnabled())
|
||||
return;
|
||||
if (!selectionModel())
|
||||
return;
|
||||
|
||||
const int row = cellAtIndex(selectionModel()->currentIndex()).y();
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Left:
|
||||
collapse(row);
|
||||
event->accept();
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
expand(row);
|
||||
event->accept();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!event->isAccepted())
|
||||
QQuickTableView::keyPressEvent(event);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef QQuickTreeViewExPrivate_h__
|
||||
#define QQuickTreeViewExPrivate_h__
|
||||
|
||||
#include "private/qquicktreeview_p_p.h"
|
||||
#include "QQuickTreeViewEx.h"
|
||||
|
||||
class QQuickTreeViewExPrivate : public QQuickTableViewPrivate
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PUBLIC(QQuickTreeViewEx)
|
||||
|
||||
QQuickTreeViewExPrivate();
|
||||
~QQuickTreeViewExPrivate() override;
|
||||
|
||||
static inline QQuickTreeViewExPrivate* get(QQuickTreeViewEx* q) { return q->d_func(); }
|
||||
|
||||
QVariant modelImpl() const override;
|
||||
void setModelImpl(const QVariant& newModel) override;
|
||||
|
||||
void initItemCallback(int serializedModelIndex, QObject* object) override;
|
||||
void itemReusedCallback(int serializedModelIndex, QObject* object) override;
|
||||
void dataChangedCallback(const QModelIndex& topLeft,
|
||||
const QModelIndex& bottomRight,
|
||||
const QVector<int>& roles);
|
||||
|
||||
virtual void updateRequiredProperties(int serializedModelIndex, QObject* object, bool init);
|
||||
void updateSelection(const QRect& oldSelection, const QRect& newSelection) override;
|
||||
public:
|
||||
QQmlTreeModelToTableModel m_treeModelToTableModel;
|
||||
QVariant m_assignedModel;
|
||||
};
|
||||
|
||||
|
||||
#endif // QQuickTreeViewExPrivate_h__
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[](http://192.168.46.100:4080/CL-Softwares/DiagramDesigner)
|
||||
|
||||
# DiagramDesigner
|
||||
|
||||
桌面端人机界面(Desktop one-line HMI DesignTime)
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef META_MODEL_H
|
||||
#define META_MODEL_H
|
||||
/*********元模型********/
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
|
||||
struct attributeGroup {
|
||||
int id = 0;
|
||||
QString groupType;
|
||||
QString groupName;
|
||||
int isPublic = -1;
|
||||
QString remark;
|
||||
};
|
||||
|
||||
struct dataType //数据类型(元模)
|
||||
{
|
||||
int id = 0;
|
||||
QString dataType;
|
||||
QString databaseType;
|
||||
};
|
||||
|
||||
struct modelType {
|
||||
int id = 0;
|
||||
QString modelType;
|
||||
QString modelName;
|
||||
int graphicElement; // 图形元素类型
|
||||
QByteArray icon; //图片
|
||||
QString remark;
|
||||
};
|
||||
|
||||
struct modelGroup
|
||||
{
|
||||
int id = 0;
|
||||
qint64 modelTypeId = 0;
|
||||
qint64 attributeGroupId = 0;
|
||||
};
|
||||
|
||||
struct attribute {
|
||||
int id = 0;
|
||||
QString attribute; // 属性名
|
||||
QString attributeName; // 别名
|
||||
qint64 dataTypeId = 0; //数据类型id
|
||||
int lengthPrecision = 0; //长度限制(varchar)
|
||||
int scale = 0; //小数点位数
|
||||
int isNotNull = 0; //是否非空
|
||||
QString defaultValue; //默认值
|
||||
QString valueRange; //数值范围
|
||||
int isVisible = 1;
|
||||
};
|
||||
|
||||
struct modelAttribute //模型属性表(所有模型属性的索引)
|
||||
{
|
||||
int id = 0;
|
||||
qint64 modelTypeId = 0;
|
||||
qint64 attributeGroupId = 0;
|
||||
qint64 attributeId = 0;
|
||||
};
|
||||
|
||||
struct modelAttributePublic //公共属性表
|
||||
{
|
||||
int id = 0;
|
||||
qint64 attributeGroupId = 0;
|
||||
qint64 attributeId = 0;
|
||||
};
|
||||
|
||||
struct modelConnectivity { //模型连接性表(元模是否可以连接)
|
||||
int id = 0;
|
||||
QString fromModel; //属性名
|
||||
QString toModel;
|
||||
int connectivity = 0; //是否可连
|
||||
};
|
||||
#endif
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
#ifndef PROJECT_MODEL_H
|
||||
#define PROJECT_MODEL_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
#include <QStandardItemModel>
|
||||
#include "tools.h"
|
||||
|
||||
struct ProjectManagerStruct {
|
||||
int id = 0;
|
||||
QString name; // 工程模表名
|
||||
QString tag; // 工程模名称
|
||||
QString metaModel; // 元模名
|
||||
QString groupName; // 属性组名
|
||||
int linkType; // 图元链接类型
|
||||
QJsonObject checkState; // 属性选择状态
|
||||
};
|
||||
|
||||
struct ProjectModelSettingStruct //工程模设定类,如图标
|
||||
{
|
||||
QString modelName; //工程模名
|
||||
QMap<QString,QByteArray> mapSvg; //存放选择的svg图片
|
||||
QMap<QString,QByteArray> mapUsedSvg; //存放使用的svg
|
||||
};
|
||||
|
||||
struct PropertyState {
|
||||
bool checkState = false;
|
||||
QString dataType;
|
||||
bool editable = true;
|
||||
};
|
||||
|
||||
struct PropertyPage {
|
||||
QMap<QString, PropertyState> checkState;
|
||||
bool isPublic = false;
|
||||
};
|
||||
|
||||
struct FormerName //曾用名,记录修改前名称
|
||||
{
|
||||
QString sName;
|
||||
bool bChanged = false; //是否改变过
|
||||
};
|
||||
|
||||
typedef QMap<QString, PropertyPage> MapProperty;
|
||||
|
||||
struct PropertyModel {
|
||||
MapProperty mapProperty;
|
||||
int nType = 0; //工程模类型,选择图标后确定
|
||||
QStandardItemModel* pBase = nullptr; //基础属性
|
||||
QStandardItemModel* pSelect = nullptr; //已选择属性
|
||||
FormerName formerMeta; //曾用元模名
|
||||
FormerName formerProject; //曾用工程模名
|
||||
QMap<QString,ProjectManagerStruct> dataInfo; //存放数据库内容
|
||||
ProjectModelSettingStruct modelSetting;
|
||||
PropertyModel deepCopy() //深拷贝
|
||||
{
|
||||
PropertyModel copy;
|
||||
copy.mapProperty = mapProperty;
|
||||
copy.nType = nType;
|
||||
copy.pBase = deepCloneModel(pBase);
|
||||
copy.pSelect = deepCloneModel(pSelect);
|
||||
copy.formerMeta = formerMeta;
|
||||
copy.formerProject = formerProject;
|
||||
copy.dataInfo = dataInfo;
|
||||
copy.modelSetting = modelSetting;
|
||||
return copy;
|
||||
}
|
||||
void release()
|
||||
{
|
||||
delete pBase;
|
||||
delete pSelect;
|
||||
pBase = nullptr;
|
||||
pSelect = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
typedef QMap<QString,PropertyModel> MapProject; //str为工程名,PropertyModel为工程属性
|
||||
typedef QMap<QString,MapProject> MapMeta; //str为元模名,PropertyModel为工程模集
|
||||
|
||||
struct PropertyStateInfo {
|
||||
QString name; // 属性名
|
||||
QString tagName; // 别名
|
||||
QString type; // 属性类型
|
||||
QVariant defaultValue; // 默认值
|
||||
int lengthPrecision = 999; // 长度限制
|
||||
int isVisible = 1; // 是否可见(0不可见,1可见,2特殊项)
|
||||
bool lock = false; // 值手动改变时置true,下次保存时恢复
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(PropertyStateInfo)
|
||||
|
||||
// 属性值信息映射
|
||||
typedef QMap<QString, PropertyStateInfo> PropertyValueInfo;
|
||||
|
||||
// 属性组状态信息
|
||||
struct GroupStateInfo {
|
||||
QString groupName; // 组名
|
||||
QString tableName; // 表名
|
||||
bool isPublic = false; // 是否公共
|
||||
PropertyValueInfo info; // 属性信息
|
||||
};
|
||||
|
||||
// 属性组实时数据
|
||||
struct GroupStateValue {
|
||||
QString groupName; // 组名
|
||||
QString tableName; // 表名
|
||||
QMap<QUuid, PropertyValueInfo> mapInfo; // 实时信息映射
|
||||
};
|
||||
|
||||
struct ModelStateInfo //模型结构信息
|
||||
{
|
||||
QString modelName;
|
||||
int modelType;
|
||||
QWidget* _PropertyDlg = NULL; //属性设置界面,每个模型维护一种界面
|
||||
QMap<QString,GroupStateInfo> groupInfo; //属性组信息
|
||||
void release()
|
||||
{
|
||||
if(_PropertyDlg){
|
||||
delete _PropertyDlg;
|
||||
_PropertyDlg = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ModelDataInfo //模型数据信息
|
||||
{
|
||||
QString modelName;
|
||||
int modelType;
|
||||
QMap<QString,GroupStateValue> groupInfo; //属性组实时信息
|
||||
};
|
||||
|
||||
struct PropertyGroupState {
|
||||
QString groupName; // 属性组名称
|
||||
QString tableName; // 属性组表名
|
||||
QJsonObject propertyState; // 属性状态信息
|
||||
};
|
||||
|
||||
struct MeasureAttributeType //量测可用属性类型(从基模获取的占位符变量)
|
||||
{
|
||||
QString tag;
|
||||
QString name;
|
||||
};
|
||||
|
||||
// 属性内容信息
|
||||
struct PropertyContentInfo {
|
||||
QString proTag; // 属性标签
|
||||
QString proName; // 属性名称
|
||||
QString proType; // 属性类型
|
||||
QWidget* proEditor = nullptr; // 编辑窗口对象
|
||||
|
||||
bool isValid() const {
|
||||
return !proTag.isEmpty() && !proName.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
struct PtExtraInfo
|
||||
{
|
||||
int index = 0;
|
||||
QString scope; //变比标签
|
||||
QString accuracy; //精度等级标签
|
||||
QString volume; //二次负载容量标签
|
||||
QString star; //线圈接法
|
||||
double ratio; //变比
|
||||
int polarity = 1; //极性
|
||||
};
|
||||
|
||||
struct CtExtraInfo
|
||||
{
|
||||
int index = 0;
|
||||
QString scope; //变比标签
|
||||
QString accuracy; //精度等级标签
|
||||
QString volume; //二次负载容量标签
|
||||
double ratio; //变比
|
||||
int polarity = 1; //极性
|
||||
};
|
||||
|
||||
struct MeasurementInfo //量测
|
||||
{
|
||||
QString tag; //量测tag
|
||||
QString name; //量测名称
|
||||
int type; //量测类型 0:遥测 1:遥信 2:遥控
|
||||
int size; //量测size
|
||||
QUuid bayUuid; //所属间隔
|
||||
QUuid componentUuid; //所属设备
|
||||
|
||||
//通讯
|
||||
int nSource; //数据来源 1:cl3611 2:104
|
||||
QString sStation; //子站名称
|
||||
QString sDevice; //设备名称
|
||||
QString sChannel; //通道名(cl3611) 遥测:TMx(1-8); P; Q; S; PF; F; deltaF; UAB; UBC; UCA; 遥信: TSxx(01-10); 遥控: TCx;
|
||||
int nPacket; //包号(104)
|
||||
int nOffset; //偏移量(104)
|
||||
//事件
|
||||
bool bEnable = false; //"enable"开启标志
|
||||
QMap<QString,double> mapTE; //遥测"cause" key:upup,up,down,downdown可选,val:0-100
|
||||
QString sEdge; //遥信"cause:edge" raising, falling 字符串单选
|
||||
QString sCommand; //"action:command" info, warning, error, critical, exception 字符串单选
|
||||
QStringList lstParameter; //"action:parameters" 字符串数组
|
||||
|
||||
QString sWindType; //绕组类型 ctpt
|
||||
int nRatio; //变比
|
||||
int nPolarity; //极性
|
||||
int nIndex; //对应绕组序号
|
||||
|
||||
QString sSymmetry; //对称量测的name
|
||||
};
|
||||
|
||||
// 定义比较键的结构
|
||||
struct MeasurementKey {
|
||||
int nSource;
|
||||
QString sStation;
|
||||
QString sDevice;
|
||||
QString sChannel;
|
||||
int nPacket;
|
||||
int nOffset;
|
||||
|
||||
MeasurementKey(const MeasurementInfo& info)
|
||||
: nSource(info.nSource)
|
||||
, sStation(info.sStation)
|
||||
, sDevice(info.sDevice)
|
||||
, sChannel(info.sChannel)
|
||||
, nPacket(info.nPacket)
|
||||
, nOffset(info.nOffset) {}
|
||||
|
||||
// 用于QMap排序
|
||||
bool operator<(const MeasurementKey& other) const {
|
||||
if (nSource != other.nSource) return nSource < other.nSource;
|
||||
if (sStation != other.sStation) return sStation < other.sStation;
|
||||
if (sDevice != other.sDevice) return sDevice < other.sDevice;
|
||||
if (sChannel != other.sChannel) return sChannel < other.sChannel;
|
||||
if (nPacket != other.nPacket) return nPacket < other.nPacket;
|
||||
return nOffset < other.nOffset;
|
||||
}
|
||||
};
|
||||
|
||||
struct ComponentInfo //工程模图元数据
|
||||
{
|
||||
QUuid uuid;
|
||||
QString modelName;
|
||||
QString nspath;
|
||||
QString tag;
|
||||
QString name;
|
||||
QString description;
|
||||
QString grid;
|
||||
QString zone;
|
||||
QString station;
|
||||
int type = 0;
|
||||
bool inService = true;
|
||||
int state = 0;
|
||||
int status = 0;
|
||||
QJsonObject connected_bus;
|
||||
QJsonObject label;
|
||||
QJsonObject context;
|
||||
int op = 0;
|
||||
};
|
||||
|
||||
struct ComponentTypeInfo //元件类型
|
||||
{
|
||||
int id = 0;
|
||||
QString type;
|
||||
QString name;
|
||||
QJsonObject config;
|
||||
};
|
||||
|
||||
struct PageInfo
|
||||
{
|
||||
int id = -1;
|
||||
QString tag;
|
||||
QString name;
|
||||
QJsonObject label;
|
||||
QJsonObject context;
|
||||
QString description;
|
||||
int op;
|
||||
};
|
||||
|
||||
// 页面中保存的项信息
|
||||
struct ItemPageInfo {
|
||||
QPointF pos;
|
||||
double dWidth = 0.0;
|
||||
double dHeight = 0.0;
|
||||
double dRotate = 0.0;
|
||||
};
|
||||
#endif
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
/*********常量定义********/
|
||||
namespace Constants {
|
||||
const double SCENE_WIDTH = 800;
|
||||
const double SCENE_HEIGHT = 600;
|
||||
const int EDITOR_ITEM_WIDTH = 150;
|
||||
const int EDITOR_ITEM_HEIGHT = 80;
|
||||
const int EDITOR_BUS_HEIGHT = 10;
|
||||
const int V_DIAGRAM_SPACING = 80;
|
||||
const int H_DIAGRAM_SPACING = 80;
|
||||
const int TRANSFORMER_LEVEL = 999; // 层级结构中变压器所处层级
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef DATA_TRANSMISSION_H
|
||||
#define DATA_TRANSMISSION_H
|
||||
/*********数据传输********/
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/*******************传递的数据************************/
|
||||
struct HttpRecommandInfo{ //推荐数据服务
|
||||
QString sInput; //输入的前缀
|
||||
int nOffset = 0; //正确位置计数
|
||||
QStringList lstRecommand; //推荐列表
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
#ifndef DIAGRAM_H
|
||||
#define DIAGRAM_H
|
||||
/*********组态图********/
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
#include <QJsonObject>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
|
||||
// 组态图信息
|
||||
struct DiagramInfo {
|
||||
QVariant id; //临时id使用uuid,load数据使用数据库自增id
|
||||
QString sName;
|
||||
QString sTag;
|
||||
QVariant parentId;
|
||||
QString sBasePageName; //父组态图名称
|
||||
};
|
||||
|
||||
struct DiagramEditorWizardBusInfo //组态编辑母线信息
|
||||
{
|
||||
int nIndex = 0;
|
||||
double dVoltage = 0; //电压等级
|
||||
int nLineType = 1; //1单母线,2双母线
|
||||
int nNum1 = 0; //1母
|
||||
int nNum2 = 0; //2母
|
||||
int connectType = 0; //接线方式,1为分段连接
|
||||
int nState = 0; //母线状态位 0已保存1新建2修改
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorWizardBusInfo &data) {
|
||||
out << data.nIndex << data.dVoltage << data.nLineType << data.nNum1 << data.nNum2 << data.connectType << data.nState;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorWizardBusInfo &data) {
|
||||
in >> data.nIndex >> data.dVoltage >> data.nLineType >> data.nNum1 >> data.nNum2 >> data.connectType >> data.nState;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
enum class TransformerType //变压器类型
|
||||
{
|
||||
twoWinding = 0, //两绕组
|
||||
threeWinding //三绕组
|
||||
};
|
||||
|
||||
enum class BayType //间隔类型
|
||||
{
|
||||
busSectionBay = 0, //分段
|
||||
busCouplerBay, //母联
|
||||
ptBay, //pt
|
||||
incomingBay, //进线
|
||||
outcomingBay, //出线
|
||||
compensationBay, //无功补偿
|
||||
bypassBay, //旁路
|
||||
mainTransformerBay //主变
|
||||
};
|
||||
|
||||
enum class EditorItemType //组态编辑中的图形项类型
|
||||
{
|
||||
bus = 1, //母线
|
||||
bay, //间隔
|
||||
trans, //变压器
|
||||
line, //连接线
|
||||
None //空白占位图
|
||||
};
|
||||
|
||||
struct EditorTransConnection //组态变压器连接信息
|
||||
{
|
||||
QString sName;
|
||||
int nPara = 0; //012,高中低侧
|
||||
};
|
||||
|
||||
struct DiagramEditorWizardTransformerInfo //组态变压器信息
|
||||
{
|
||||
QString sName;
|
||||
TransformerType nType;
|
||||
double dVoltageLevel;
|
||||
QList<EditorTransConnection> lstBindObj; //连接的对象
|
||||
};
|
||||
|
||||
struct DiagramEditorWizardBayInfo //组态间隔信息
|
||||
{
|
||||
QString sName;
|
||||
BayType nType;
|
||||
double dVoltageLevel;
|
||||
QList<QString> lstBindObj; //连接的对象
|
||||
};
|
||||
|
||||
struct DiagramEditorComponentInfo //组态设备信息
|
||||
{
|
||||
int nCategory = 0; //分类 0电气设备1连接关系
|
||||
QString sName;
|
||||
int nType = 0; //类型 1母线2异步电动机3断路器4电缆5电流互感器6电压互感器7隔离开关8接地开关9快速接地开关10双掷接地隔离开关11带电指示器12避雷器13电缆出线套筒14电缆端
|
||||
QString sBindObj; //所关联的实体名 (母线block,间隔block,变压器高中低端子)
|
||||
int nBindType = 0; //关联实体的类型 1母线2间隔3变压器
|
||||
int nBindPara = 0; //关联额外参数,关联变压器时为(0高1中2低)
|
||||
QString sBindParent; //关联父item名,关联变压器时为变压器名
|
||||
QStringList sUsedRoute; //使用设备的线路名
|
||||
int nUsedDirection = 0; //被占用的方向 8421 上下左右
|
||||
QPoint deltaPos = QPoint(0,0); //相对坐标(相对间隔)
|
||||
QUuid uid;
|
||||
int nFlag = 0; //标志0未使用1新建2修改
|
||||
int nRotate = 0; //旋转角
|
||||
|
||||
bool operator ==(const DiagramEditorComponentInfo& obj) const {
|
||||
if(nCategory == obj.nCategory && sName == obj.sName && nType == obj.nType && sBindObj == obj.sBindObj && nBindType == obj.nBindType &&
|
||||
sUsedRoute == obj.sUsedRoute && nUsedDirection == obj.nUsedDirection && deltaPos == obj.deltaPos && uid == obj.uid && nFlag == obj.nFlag && nRotate == obj.nRotate)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorComponentInfo &data) {
|
||||
out << data.nCategory << data.sName << data.nType << data.sBindObj << data.nBindType << data.nBindPara << data.sBindParent << data.sUsedRoute << data.nUsedDirection << data.deltaPos << data.uid << data.nFlag << data.nRotate;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorComponentInfo &data) {
|
||||
in >> data.nCategory >> data.sName >> data.nType >> data.sBindObj >> data.nBindType >> data.nBindPara >> data.sBindParent >> data.sUsedRoute >> data.nUsedDirection >> data.deltaPos >> data.uid >> data.nFlag >> data.nRotate;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
inline uint qHash(const DiagramEditorComponentInfo &key, uint seed = 0) {
|
||||
return qHash(key.uid, seed);
|
||||
}
|
||||
|
||||
struct DiagramEditorRouteInfo //间隔中单条线路信息
|
||||
{
|
||||
QString sRouteName;
|
||||
bool bMainRoute = false; //主线路(包含设备最多的线路,决定整体布局)
|
||||
QList<DiagramEditorComponentInfo> lstCompo;
|
||||
QList<DiagramEditorComponentInfo> lstOrder; //线路顺序容器(计算用)
|
||||
QList<DiagramEditorComponentInfo> lstReverse; //逆序容器(计算用)
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorRouteInfo &data) {
|
||||
out << data.sRouteName << data.bMainRoute << data.lstCompo << data.lstOrder << data.lstReverse;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorRouteInfo &data) {
|
||||
in >> data.sRouteName >> data.bMainRoute >> data.lstCompo >> data.lstOrder >> data.lstReverse;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorBayInfo //组态编辑间隔信息
|
||||
{
|
||||
QString name; //间隔名
|
||||
int nLayout; //布局 0纵向1横向
|
||||
QList<QUuid> lstFrom; //起始
|
||||
QList<QUuid> lstTo; //结束
|
||||
QMap<QString,DiagramEditorRouteInfo> mapRoute; //线路信息
|
||||
QMap<QString,DiagramEditorComponentInfo> mapComponent; //设备信息
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBayInfo &data) {
|
||||
out << data.name << data.nLayout << data.lstFrom << data.lstTo << data.mapRoute << data.mapComponent;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorBayInfo &data) {
|
||||
in >> data.name >> data.nLayout >> data.lstFrom >> data.lstTo >> data.mapRoute >> data.mapComponent;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorTransNeutralInfo //组态编辑变压器中性点信息
|
||||
{
|
||||
QString name; //中性点名
|
||||
int nType = 0; //中性点类型 0高1中2低
|
||||
QPointF delPoint; //相对变压器偏移量
|
||||
QMap<QString,DiagramEditorRouteInfo> mapRoute; //中性点对应的线路结构
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorTransNeutralInfo &data) {
|
||||
out << data.name << data.nType << data.delPoint << data.mapRoute;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorTransNeutralInfo &data) {
|
||||
in >> data.name >> data.nType >> data.delPoint >> data.mapRoute;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorTransInfo //组态编辑变压器信息
|
||||
{
|
||||
QString name; //变压器名
|
||||
QMap<int,DiagramEditorTransNeutralInfo> mapNeutral; //中性点结构
|
||||
QMap<QString,DiagramEditorComponentInfo> mapComponent; //设备信息
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorTransInfo &data) {
|
||||
out << data.name << data.mapNeutral << data.mapComponent;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorTransInfo &data) {
|
||||
in >> data.name >> data.mapNeutral >> data.mapComponent;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
enum class DiagramEditorStructType
|
||||
{
|
||||
block = 0, //模块(母线段、间隔、变压器)
|
||||
blockContainer, //模块容器(包含若干模块)
|
||||
rowData //行数据(包含若干容器)
|
||||
};
|
||||
|
||||
struct DiagramEditorConnectType //组态编辑连接信息
|
||||
{
|
||||
QString sName;
|
||||
int nType = 0; //1母线,2间隔,3变压器
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorConnectType &data) {
|
||||
out << data.sName << data.nType;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorConnectType &data) {
|
||||
in >> data.sName >> data.nType;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorBriefConnect //组态编辑时连接信息
|
||||
{
|
||||
QUuid uid;
|
||||
DiagramEditorConnectType con1;
|
||||
DiagramEditorConnectType con2;
|
||||
int nPara = 0; //万用参数(变压器中表示连接位置,0高压侧,1中压侧,2低压侧)
|
||||
|
||||
bool operator==(const DiagramEditorBriefConnect& obj)
|
||||
{
|
||||
if(this == &obj)
|
||||
return false;
|
||||
if((con1.sName == obj.con1.sName && con2.sName == obj.con2.sName)||(con1.sName == obj.con2.sName && con2.sName == obj.con1.sName))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBriefConnect &data) {
|
||||
out << data.uid << data.con1 << data.con2 << data.nPara;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorBriefConnect &data) {
|
||||
in >> data.uid >> data.con1 >> data.con2 >> data.nPara;
|
||||
return in;
|
||||
}
|
||||
|
||||
DiagramEditorConnectType getOpposite(const QString& s){ //获取另一端名称
|
||||
if(con1.sName == s)
|
||||
return con2;
|
||||
else if(con2.sName == s)
|
||||
return con1;
|
||||
return DiagramEditorConnectType();
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorBlockInfo //组态编辑block信息
|
||||
{
|
||||
QString sName;
|
||||
QString sContainerId; //所属的容器id
|
||||
int nType; //1母线,2间隔,3变压器
|
||||
int nContainerLevel; //所处容器的层级 0,1,2,3
|
||||
QUuid uid;
|
||||
QList<QUuid> _lstCon; //连接信息
|
||||
QList<QPair<int,QUuid>> _lstSub; //子对象列表(非拓扑计算使用) 如母线子对象为间隔,间隔子对象为item,电动机子对象为item <类型,uid> 类型:0设备1间隔
|
||||
QRectF recSize; //当前大小(根据内容确定)
|
||||
QPointF sceneDelta; //block中心相对位移(计算布局位置
|
||||
bool bEditState; //详细编辑状态
|
||||
|
||||
//bus
|
||||
float fVoltage; //母线电压
|
||||
int nBusType; //0无前缀,1:Ⅰ母,2Ⅱ母
|
||||
int nIndex; //第几段,1A,2B,3C,4D,5E,6F,7G,8H
|
||||
//bay
|
||||
BayType nBayType;
|
||||
DiagramEditorBayInfo bayInfo; //间隔信息
|
||||
//transformer
|
||||
TransformerType nTransType;
|
||||
DiagramEditorTransInfo transInfo;
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorBlockInfo &data) {
|
||||
out << data.sName << data.sContainerId << data.nType << data.nContainerLevel << data.uid << data._lstCon << data.recSize << data.sceneDelta
|
||||
<< data.bEditState << data.fVoltage << data.nBusType << data.nIndex << data.nBayType << data.bayInfo << data.nTransType << data.transInfo;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorBlockInfo &data) {
|
||||
in >> data.sName >> data.sContainerId >> data.nType >> data.nContainerLevel >> data.uid >> data._lstCon >> data.recSize >> data.sceneDelta
|
||||
>> data.bEditState >> data.fVoltage >> data.nBusType >> data.nIndex >> data.nBayType >> data.bayInfo >> data.nTransType >> data.transInfo;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorContainerInfo //组态编辑容器信息
|
||||
{
|
||||
QString sId;
|
||||
double dMidUpY; //1母上边界
|
||||
double dMidDownY; //2母下边界
|
||||
double dStartX; //起始x
|
||||
double dStartY; //起始y
|
||||
double dWidth; //宽度
|
||||
double dHeight;
|
||||
double dMaxUpH; //上方最大高度(1母线到上边界)
|
||||
double dMaxDownH; //下方最大高度(2母线到下边界)
|
||||
QMap<int,QList<DiagramEditorBlockInfo>> mapBlockInfo;
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorContainerInfo &data) {
|
||||
out << data.sId << data.dMidUpY << data.dMidDownY << data.dStartX << data.dStartY << data.dWidth << data.dHeight << data.dMaxUpH << data.dMaxDownH << data.mapBlockInfo;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorContainerInfo &data) {
|
||||
in >> data.sId >> data.dMidUpY >> data.dMidDownY >> data.dStartX >> data.dStartY >> data.dWidth >> data.dHeight >> data.dMaxUpH >> data.dMaxDownH >> data.mapBlockInfo;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramEditorProjectInfo //editor工程信息
|
||||
{
|
||||
QString sName;
|
||||
QUuid uid;
|
||||
QMap<int,DiagramEditorWizardBusInfo> mapBus;
|
||||
QMap<int,QList<DiagramEditorContainerInfo>> mapSturctContainer;
|
||||
QMap<QUuid,DiagramEditorBriefConnect> mapConnect;
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const DiagramEditorProjectInfo &data) {
|
||||
out << data.sName << data.uid << data.mapBus << data.mapSturctContainer << data.mapConnect;
|
||||
return out;
|
||||
}
|
||||
friend QDataStream &operator>>(QDataStream &in, DiagramEditorProjectInfo &data) {
|
||||
in >> data.sName >> data.uid >> data.mapBus >> data.mapSturctContainer >> data.mapConnect;
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
struct DiagramContent {
|
||||
QString diagramId; // 对应的PowerEntity ID
|
||||
|
||||
// 元素位置信息 <元素ID, 位置>
|
||||
QHash<QString, QPointF> elementPositions;
|
||||
QHash<QString, QList<QPointF>> connectionPaths; // 自定义路径点
|
||||
};
|
||||
|
||||
struct BaseComponentInfo //基模图元数据
|
||||
{
|
||||
int id = 0;
|
||||
QUuid uuid;
|
||||
QString name;
|
||||
QString tag;
|
||||
int typeId = 0;
|
||||
QJsonObject context;
|
||||
QString metaMmodel;
|
||||
QString projectModel;
|
||||
};
|
||||
|
||||
struct EditorProjectInfo //editor工程数据
|
||||
{
|
||||
int id = 0;
|
||||
QUuid uuid;
|
||||
QString name;
|
||||
QString tag;
|
||||
};
|
||||
|
||||
struct EditorBaseSettingInfo //editor基础设置
|
||||
{
|
||||
int id = 0;
|
||||
QUuid uuid;
|
||||
QString projectName; //所属工程名
|
||||
QString autor; //作者
|
||||
QByteArray context; //内容
|
||||
QUuid generateUid; //生成的图uuid
|
||||
QString ts; //时间戳
|
||||
};
|
||||
|
||||
|
||||
#endif // DIAGRAM_H
|
||||
|
|
@ -0,0 +1,413 @@
|
|||
#ifndef TOPOLOGY_H
|
||||
#define TOPOLOGY_H
|
||||
/*********拓扑相关结构********/
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QJsonArray>
|
||||
|
||||
// 数据状态
|
||||
enum class DataState {
|
||||
Unchanged = 0,
|
||||
Changed,
|
||||
PrepareDelete
|
||||
};
|
||||
|
||||
// 端口位置
|
||||
enum PortPos {
|
||||
P_top = 0,
|
||||
P_down,
|
||||
P_left,
|
||||
P_right
|
||||
};
|
||||
|
||||
enum HandleType
|
||||
{
|
||||
T_none = 0,
|
||||
T_resize, //调整大小
|
||||
T_rotate, //旋转
|
||||
T_editShape, //编辑形状
|
||||
T_text, //文本
|
||||
T_lineIn, //入线口
|
||||
T_lineOut, //出线口
|
||||
T_lineInOut, //双端线
|
||||
T_newTral //中性点
|
||||
};
|
||||
|
||||
struct Connection
|
||||
{
|
||||
QUuid nSrcNodeId;
|
||||
QUuid nSrcPortId;
|
||||
HandleType srcType;
|
||||
PortPos srcPos;
|
||||
QUuid nDestNodeId;
|
||||
QUuid nDestPortId;
|
||||
HandleType destType;
|
||||
PortPos destPos;
|
||||
|
||||
Connection()
|
||||
{
|
||||
srcType = T_none;
|
||||
srcPos = P_top;
|
||||
destType = T_none;
|
||||
destPos = P_top;
|
||||
nSrcNodeId = QUuid();
|
||||
nSrcPortId = QUuid();
|
||||
nDestNodeId = QUuid();
|
||||
nDestPortId = QUuid();
|
||||
}
|
||||
|
||||
Connection(const Connection& obj)
|
||||
{
|
||||
nSrcNodeId = obj.nSrcNodeId;
|
||||
nSrcPortId = obj.nSrcPortId;
|
||||
srcType = obj.srcType;
|
||||
srcPos = obj.srcPos;
|
||||
nDestNodeId = obj.nDestNodeId;
|
||||
nDestPortId = obj.nDestPortId;
|
||||
destType = obj.destType;
|
||||
destPos = obj.destPos;
|
||||
}
|
||||
|
||||
Connection(QUuid nSNI,QUuid nSPI,HandleType sT,PortPos sPOS,QUuid nDNI,QUuid nDPI,HandleType dT,PortPos dPOS)
|
||||
{
|
||||
nSrcNodeId = nSNI;
|
||||
nSrcPortId = nSPI;
|
||||
srcType = sT;
|
||||
srcPos = sPOS;
|
||||
nDestNodeId = nDNI;
|
||||
nDestPortId = nDPI;
|
||||
destType = dT;
|
||||
destPos = dPOS;
|
||||
}
|
||||
bool operator==(const Connection& obj)
|
||||
{
|
||||
return ((nSrcNodeId == obj.nSrcNodeId)&&(nSrcPortId == obj.nSrcPortId)&&(srcType == obj.srcType)&&(nDestNodeId == obj.nDestNodeId)&&(nDestPortId == obj.nDestPortId)&&(destType == obj.destType));
|
||||
}
|
||||
|
||||
Connection& operator=(const Connection& obj)
|
||||
{
|
||||
if(this == &obj)
|
||||
return *this;
|
||||
nSrcNodeId = obj.nSrcNodeId;
|
||||
nSrcPortId = obj.nSrcPortId;
|
||||
srcType = obj.srcType;
|
||||
srcPos = obj.srcPos;
|
||||
nDestNodeId = obj.nDestNodeId;
|
||||
nDestPortId = obj.nDestPortId;
|
||||
destType = obj.destType;
|
||||
destPos = obj.destPos;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// 拓扑连接信息
|
||||
struct TopologyInfo
|
||||
{
|
||||
int id = -1;
|
||||
QUuid uuid_from;
|
||||
QUuid uuid_to;
|
||||
QJsonObject context;
|
||||
int flag;
|
||||
QString description;
|
||||
int op;
|
||||
};
|
||||
|
||||
// 电网信息
|
||||
struct GridInfo
|
||||
{
|
||||
int id = -1;
|
||||
QString tagname;
|
||||
QString name;
|
||||
QString description;
|
||||
};
|
||||
|
||||
// 区域信息
|
||||
struct ZoneInfo
|
||||
{
|
||||
int id = -1;
|
||||
int grid_id = -1;
|
||||
QString tagname;
|
||||
QString name;
|
||||
QString description;
|
||||
};
|
||||
|
||||
// 电站信息
|
||||
struct StationInfo
|
||||
{
|
||||
int id = -1;
|
||||
int zone_id = -1;
|
||||
QString tagname;
|
||||
QString name;
|
||||
QString description;
|
||||
bool is_local = true;
|
||||
};
|
||||
|
||||
// 间隔信息
|
||||
struct BayInfo
|
||||
{
|
||||
QUuid uuid;
|
||||
QString name;
|
||||
QString tag;
|
||||
QString type;
|
||||
double unom;
|
||||
double fla;
|
||||
double capacity;
|
||||
QString description;
|
||||
bool inService;
|
||||
int nState;
|
||||
QString grid;
|
||||
QString zone;
|
||||
QString station;
|
||||
QJsonObject business;
|
||||
QJsonObject fromUuid;
|
||||
QJsonObject toUuid;
|
||||
QJsonObject protect;
|
||||
QJsonObject faultRec;
|
||||
QJsonObject status;
|
||||
QJsonObject dynSense;
|
||||
QJsonObject instruct;
|
||||
QJsonObject etc;
|
||||
QJsonObject context;
|
||||
QList<QUuid> components;
|
||||
};
|
||||
|
||||
// 层级关系结构项
|
||||
struct HierarchyStructItem {
|
||||
QString sVoltageLevel;
|
||||
int nCategory; //类型 0设备1间隔
|
||||
int nEquipType; //设备类别
|
||||
QString sName; //名称
|
||||
QUuid uid; //id
|
||||
QString grid; //电网
|
||||
QString zone; //区域
|
||||
QString station; //电站
|
||||
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["sVoltageLevel"] = sVoltageLevel;
|
||||
obj["nCategory"] = nCategory;
|
||||
obj["nEquipType"] = nEquipType;
|
||||
obj["sName"] = sName;
|
||||
obj["uid"] = uid.toString();
|
||||
obj["grid"] = grid;
|
||||
obj["zone"] = zone;
|
||||
obj["station"] = station;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 从JSON对象解析
|
||||
void fromJson(const QJsonObject& json) {
|
||||
sVoltageLevel = json["sVoltageLevel"].toString();
|
||||
nCategory = json["nCategory"].toInt();
|
||||
nEquipType = json["nEquipType"].toInt();
|
||||
sName = json["sName"].toString();
|
||||
uid = QUuid::fromString(json["uid"].toString());
|
||||
grid = json["grid"].toString();
|
||||
zone = json["zone"].toString();
|
||||
station = json["station"].toString();
|
||||
}
|
||||
|
||||
// 检查有效性
|
||||
bool isValid() const {
|
||||
return !uid.isNull() && !sName.isEmpty();
|
||||
}
|
||||
|
||||
// 重载相等运算符
|
||||
bool operator==(const HierarchyStructItem& other) const {
|
||||
return uid == other.uid &&
|
||||
sName == other.sName &&
|
||||
nCategory == other.nCategory;
|
||||
}
|
||||
};
|
||||
|
||||
// 层级关系项
|
||||
struct HierarchyItem {
|
||||
HierarchyStructItem parent;
|
||||
HierarchyStructItem item;
|
||||
QList<HierarchyStructItem> subList;
|
||||
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["parent"] = parent.toJson();
|
||||
obj["item"] = item.toJson();
|
||||
|
||||
// 序列化子列表
|
||||
QJsonArray subArray;
|
||||
for (const auto& subItem : subList) {
|
||||
subArray.append(subItem.toJson());
|
||||
}
|
||||
obj["subList"] = subArray;
|
||||
obj["subCount"] = subList.size();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 从JSON对象解析
|
||||
void fromJson(const QJsonObject& json) {
|
||||
parent.fromJson(json["parent"].toObject());
|
||||
item.fromJson(json["item"].toObject());
|
||||
|
||||
// 解析子列表
|
||||
subList.clear();
|
||||
QJsonArray subArray = json["subList"].toArray();
|
||||
for (const QJsonValue& subValue : subArray) {
|
||||
HierarchyStructItem sub;
|
||||
sub.fromJson(subValue.toObject());
|
||||
subList.append(sub);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查有效性
|
||||
bool isValid() const {
|
||||
return parent.isValid() && item.isValid();
|
||||
}
|
||||
|
||||
// 添加子项
|
||||
void addSubItem(const HierarchyStructItem& subItem) {
|
||||
subList.append(subItem);
|
||||
}
|
||||
|
||||
// 移除子项
|
||||
bool removeSubItem(const QUuid& subUid) {
|
||||
for (int i = 0; i < subList.size(); ++i) {
|
||||
if (subList[i].uid == subUid) {
|
||||
subList.removeAt(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查找子项
|
||||
HierarchyStructItem* findSubItem(const QUuid& subUid) {
|
||||
for (auto& sub : subList) {
|
||||
if (sub.uid == subUid) {
|
||||
return ⊂
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//属性其他参数与层级关系
|
||||
struct ExtraProperty
|
||||
{
|
||||
int id = 0;
|
||||
QString code; // 唯一编码(拼接字符串)
|
||||
QString tag; // 属性tag
|
||||
QString name; // 显示名称
|
||||
QString connect_para; // 连接参数
|
||||
|
||||
// 层级名称
|
||||
QString grid_name;
|
||||
QString zone_name;
|
||||
QString station_name;
|
||||
QString currentLevel;
|
||||
QString bay_name;
|
||||
QString component_name;
|
||||
QString group_name;
|
||||
QString type_name;
|
||||
|
||||
//层级索引
|
||||
QString grid_tag;
|
||||
QString zone_tag;
|
||||
QString station_tag;
|
||||
QString page_tag;
|
||||
QString bay_tag;
|
||||
QUuid component_uuid;
|
||||
QString component_tag;
|
||||
QString group_tag;
|
||||
QString type_tag;
|
||||
|
||||
// 数据源配置
|
||||
QString sourceType; // "property", "measurement"
|
||||
QVariantMap sourceConfig;
|
||||
|
||||
bool bDataChanged = false; //数据改变标志(临时)
|
||||
// 获取完整路径
|
||||
QString getFullName() const {
|
||||
QStringList parts = {grid_name, zone_name, station_name, currentLevel, bay_name, component_name, group_name, name};
|
||||
parts.removeAll("");
|
||||
return parts.join(".");
|
||||
}
|
||||
|
||||
//获取完整索引
|
||||
QString getFullTag() const {
|
||||
QStringList parts = {grid_tag, zone_tag, station_tag, page_tag, currentLevel, bay_tag, component_uuid.toString(), group_tag, tag};
|
||||
parts.removeAll("");
|
||||
return parts.join(".");
|
||||
}
|
||||
|
||||
// 检查是否匹配过滤条件
|
||||
bool matches(const QVariantMap& filter,const QString& type) const {
|
||||
for (auto it = filter.begin(); it != filter.end(); ++it) {
|
||||
QString field = it.key();
|
||||
QString filterValue = it.value().toString();
|
||||
QString fieldValue;
|
||||
if(type == "name"){
|
||||
fieldValue = getFieldName(field);
|
||||
}
|
||||
else if(type == "tag"){
|
||||
fieldValue = getFieldTag(field);
|
||||
}
|
||||
|
||||
if (!filterValue.isEmpty() && fieldValue != filterValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString getFieldName(const QString& field) const {
|
||||
if (field == "grid") return grid_name;
|
||||
if (field == "zone") return zone_name;
|
||||
if (field == "station") return station_name;
|
||||
if (field == "currentLevel") return currentLevel;
|
||||
if (field == "bay") return bay_name;
|
||||
if (field == "component") return component_name;
|
||||
if (field == "group") return group_name;
|
||||
if (field == "property") return name;
|
||||
return "";
|
||||
}
|
||||
|
||||
QString getFieldTag(const QString& field) const {
|
||||
if (field == "grid") return grid_tag;
|
||||
if (field == "zone") return zone_tag;
|
||||
if (field == "station") return station_tag;
|
||||
if (field == "page") return page_tag;
|
||||
if (field == "currentLevel") return currentLevel;
|
||||
if (field == "bay") return bay_tag;
|
||||
if (field == "component") return component_name;
|
||||
if (field == "group") return component_uuid.toString();
|
||||
if (field == "tag") return name;
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
struct ExtraPropertyLevelInfo { //层级关系item信息
|
||||
QString displayText; // 显示文本
|
||||
QString nameValue; // 名称值
|
||||
QString tagValue; // 标签值
|
||||
bool isRequired; // 是否必填
|
||||
QString placeholder; // 缺省占位符
|
||||
};
|
||||
|
||||
// 基础实体类型
|
||||
enum class EntityType {
|
||||
Grid,
|
||||
Zone,
|
||||
Station,
|
||||
ConfigurationDiagram,
|
||||
Component
|
||||
};
|
||||
|
||||
struct EntityInfo {
|
||||
QString sName;
|
||||
QString sUuid;
|
||||
QString sParentId;
|
||||
EntityType eType;
|
||||
};
|
||||
#endif // TOPOLOGY_H
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#include "types.h"
|
||||
|
||||
const QMap<AbstractItemType,GraphicsItemType> linkType = {
|
||||
{AIT_motor,GIT_itemRect},{AIT_bus,GIT_bus},
|
||||
};
|
||||
//类型转换
|
||||
const QMap<int,GraphicsItemType> typeToProGraphic = {
|
||||
{0,GIT_node},{1,GIT_bus},{3,GIT_itemRect},{4,GIT_ctGroup},{5,GIT_ptGroup},{6,GIT_ES},{7,GIT_FES},{8,GIT_link},{9,GIT_DS},{10,GIT_DTEDS},{11,GIT_PI},{12,GIT_LA},{13,GIT_cableTer},{14,GIT_cableEnd},{15,GIT_2wTransformer},{16,GIT_3wTransformer}
|
||||
};
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
/*********枚举类型********/
|
||||
#include <QGraphicsItem>
|
||||
#include <QObject>
|
||||
#include <QMetaType>
|
||||
|
||||
// 图形项类型
|
||||
enum GraphicsItemType
|
||||
{
|
||||
GIT_base = QGraphicsItem::UserType + 1,
|
||||
GIT_line = QGraphicsItem::UserType + 2,
|
||||
GIT_rect = QGraphicsItem::UserType + 3,
|
||||
GIT_roundRect = QGraphicsItem::UserType + 4,
|
||||
GIT_ellipse = QGraphicsItem::UserType + 5,
|
||||
GIT_polygon = QGraphicsItem::UserType + 6,
|
||||
//======================================
|
||||
GIT_bus = QGraphicsItem::UserType + 50,
|
||||
GIT_itemRect = QGraphicsItem::UserType + 51,
|
||||
GIT_itemTri = QGraphicsItem::UserType + 52,
|
||||
GIT_link= QGraphicsItem::UserType + 53,
|
||||
GIT_ctItem= QGraphicsItem::UserType + 54,
|
||||
GIT_ctGroup= QGraphicsItem::UserType + 55,
|
||||
GIT_ptItem= QGraphicsItem::UserType + 56,
|
||||
GIT_ptGroup= QGraphicsItem::UserType + 57,
|
||||
GIT_ES= QGraphicsItem::UserType + 58,
|
||||
GIT_DS= QGraphicsItem::UserType + 59,
|
||||
GIT_FES= QGraphicsItem::UserType + 60,
|
||||
GIT_DTEDS= QGraphicsItem::UserType + 61,
|
||||
GIT_PI= QGraphicsItem::UserType + 62,
|
||||
GIT_LA= QGraphicsItem::UserType + 63,
|
||||
GIT_cableTer= QGraphicsItem::UserType + 64,
|
||||
GIT_cableEnd= QGraphicsItem::UserType + 65,
|
||||
GIT_2wTransformer= QGraphicsItem::UserType + 66,
|
||||
GIT_3wTransformer= QGraphicsItem::UserType + 67,
|
||||
GIT_node= QGraphicsItem::UserType + 79,
|
||||
GIT_bay= QGraphicsItem::UserType + 80, //间隔
|
||||
//======================================
|
||||
GIT_baseNode = QGraphicsItem::UserType + 199,
|
||||
GIT_baseBus = QGraphicsItem::UserType + 200,
|
||||
GIT_baseLine = QGraphicsItem::UserType + 201,
|
||||
GIT_baseBreaker = QGraphicsItem::UserType + 202,
|
||||
GIT_baseCT = QGraphicsItem::UserType + 203,
|
||||
GIT_basePT = QGraphicsItem::UserType + 204,
|
||||
GIT_baseDS = QGraphicsItem::UserType + 205, //隔离开关
|
||||
GIT_baseES = QGraphicsItem::UserType + 206, //接地开关
|
||||
GIT_baseFES = QGraphicsItem::UserType + 207, //快速接地
|
||||
GIT_baseDTEDS = QGraphicsItem::UserType + 208, //双掷接地隔离开关
|
||||
GIT_basePI = QGraphicsItem::UserType + 209, //带电指示器
|
||||
GIT_baseLightningArrester = QGraphicsItem::UserType + 210, //避雷器
|
||||
GIT_baseCableTer = QGraphicsItem::UserType + 211, //电缆出线套筒
|
||||
GIT_baseCableEnd = QGraphicsItem::UserType + 212,
|
||||
GIT_base2wTransformer = QGraphicsItem::UserType + 213, //两绕阻变压器
|
||||
GIT_base3wTransformer = QGraphicsItem::UserType + 214, //三绕组变压器
|
||||
};
|
||||
|
||||
enum AttributeField //元模属性字段对照
|
||||
{
|
||||
Id = Qt::UserRole + 1,
|
||||
Attribute = Qt::UserRole + 2,
|
||||
AttributeName = Qt::UserRole + 3,
|
||||
DataType = Qt::UserRole + 4,
|
||||
LengthPrecision = Qt::UserRole + 5,
|
||||
Scale = Qt::UserRole + 6,
|
||||
IsNotNull = Qt::UserRole + 7,
|
||||
DefaultValue = Qt::UserRole + 8,
|
||||
ValueRange = Qt::UserRole + 9,
|
||||
IsVisible = Qt::UserRole + 10
|
||||
};
|
||||
|
||||
// 抽象项类型
|
||||
enum AbstractItemType {
|
||||
AIT_motor = 1,
|
||||
AIT_bus
|
||||
};
|
||||
|
||||
// 模型功能类型
|
||||
enum class ModelFunctionType {
|
||||
ProjectModel = 0,
|
||||
BaseModel,
|
||||
EditorModel,
|
||||
BlockEditorModel,
|
||||
RuntimeModel
|
||||
};
|
||||
|
||||
// 组态图模式
|
||||
enum DiagramMode {
|
||||
DM_edit = 0,
|
||||
DM_run,
|
||||
DM_academic,
|
||||
DM_baseModel
|
||||
};
|
||||
|
||||
//端口类型
|
||||
enum PortState
|
||||
{
|
||||
P_const = 0, //固定端口
|
||||
p_movable, //移动端口
|
||||
};
|
||||
|
||||
// 项目状态
|
||||
enum ProjectState {
|
||||
Error = -1, // 错误
|
||||
NotExist = 0, // 不存在
|
||||
Exist, // 已存在
|
||||
Changed // 已改变
|
||||
};
|
||||
|
||||
// 表单项状态
|
||||
enum TableItemState {
|
||||
TS_Create = 1, // 创建
|
||||
TS_Select = 2, // 选择
|
||||
TS_Edit = 4 // 编辑
|
||||
};
|
||||
|
||||
// 警告提示信息
|
||||
enum class AlertInfo {
|
||||
Success = 1, // 成功
|
||||
Fail = 2, // 失败
|
||||
Exist = 4 // 已存在
|
||||
};
|
||||
|
||||
// 选择对话框类型
|
||||
enum SelectorDialogType {
|
||||
ST_MetaModel = 0, // 元模对话框
|
||||
ST_ComponentType // 元件选择
|
||||
};
|
||||
|
||||
// 表格代理内容
|
||||
enum TableDelegateContent {
|
||||
TD_ProjectModel = 0, // 工程模
|
||||
TD_MetaModel, // 基模
|
||||
TD_ComponentType // 元件类型
|
||||
};
|
||||
|
||||
// 变种图标
|
||||
enum VariantIcon {
|
||||
VI_Thumbnail = 0, // 缩略图
|
||||
VI_Normal_1, // 常规1
|
||||
VI_Normal_2, // 常规2
|
||||
VI_Normal_3, // 常规3
|
||||
VI_Normal_4, // 常规4
|
||||
VI_Normal_5 // 常规5
|
||||
};
|
||||
|
||||
// 组件类型枚举
|
||||
enum class ComponentCategory {
|
||||
ElectricalEquipment = 0, // 电气设备
|
||||
ConnectionRelation = 1 // 连接关系
|
||||
};
|
||||
|
||||
enum class ComponentType {
|
||||
Bus = 1, // 母线
|
||||
AsynchronousMotor = 2, // 异步电动机
|
||||
CircuitBreaker = 3, // 断路器
|
||||
Cable = 4, // 电缆
|
||||
CurrentTransformer = 5, // 电流互感器
|
||||
VoltageTransformer = 6, // 电压互感器
|
||||
Disconnector = 7, // 隔离开关
|
||||
EarthingSwitch = 8, // 接地开关
|
||||
FastEarthingSwitch = 9, // 快速接地开关
|
||||
DoubleThrowEarthingSwitch = 10, // 双掷接地隔离开关
|
||||
VoltageIndicator = 11, // 带电指示器
|
||||
LightningArrester = 12, // 避雷器
|
||||
CableTerminal = 13, // 电缆出线套筒
|
||||
CableEnd = 14 // 电缆端
|
||||
};
|
||||
|
||||
extern const QMap<AbstractItemType,GraphicsItemType> linkType;
|
||||
//类型转换
|
||||
extern const QMap<int,GraphicsItemType> typeToProGraphic;
|
||||
#endif
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef GRAPHICS_ITEMS_H
|
||||
#define GRAPHICS_ITEMS_H
|
||||
/*********图元********/
|
||||
#include <QGraphicsItem>
|
||||
|
||||
// 句柄标签
|
||||
enum HandleTag {
|
||||
H_none = 0,
|
||||
H_leftTop,
|
||||
H_top,
|
||||
H_rightTop,
|
||||
H_right,
|
||||
H_rightBottom,
|
||||
H_bottom,
|
||||
H_leftBottom,
|
||||
H_left,
|
||||
H_rotate_leftTop,
|
||||
H_rotate_rightTop,
|
||||
H_rotate_rightBottom,
|
||||
H_rotate_leftBottom,
|
||||
H_edit,
|
||||
H_textCaption = 40, // 标题文本
|
||||
H_textCurrent, // 电流
|
||||
H_textVoltage, // 电压
|
||||
H_connect = 50 // 连接操作点
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
#ifndef MONITOR_ITEM_H
|
||||
#define MONITOR_ITEM_H
|
||||
/*********人机界面********/
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QJsonObject>
|
||||
|
||||
enum class MonitorItemState { //监控item的状态
|
||||
Normal = 0, //正常
|
||||
Closing, //合闸
|
||||
Opening, //分闸
|
||||
AccidentTrip, //事故跳闸
|
||||
StatusFault, //状态故障
|
||||
Energized, //带电
|
||||
DeEnergized, //失电
|
||||
Grounding, //接地
|
||||
Running, //运行
|
||||
ShutDown, //停运
|
||||
Alarm, //告警
|
||||
LineBreak, //断线
|
||||
MeasureMentOutLimit //量测越限
|
||||
};
|
||||
|
||||
struct MonitorItemAttributeInfo //单个监控item属性
|
||||
{
|
||||
QString sGroup; //所属组别
|
||||
QString sTag; //索引名
|
||||
QString sName; //显示名
|
||||
int nConnectType = 0; //关联数据类别 0字段 1量测
|
||||
QString sConnectPara; //查询参数(参数服务使用)
|
||||
int nShowType = 0; //显示类别 0字符 1图表
|
||||
bool bShowDiagram = false; //显示到组态中
|
||||
int nGraphType = 0; //图表类型 0折线1柱状
|
||||
QString sTimeRange; //时间范围(分)
|
||||
QMap<quint64,double> mapValue; //属性值
|
||||
bool bSelected = false;
|
||||
|
||||
// 转换为JSON对象
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["sGroup"] = sGroup;
|
||||
obj["sTag"] = sTag;
|
||||
obj["sName"] = sName;
|
||||
obj["nConnectType"] = nConnectType;
|
||||
obj["sConnectPara"] = sConnectPara;
|
||||
obj["nShowType"] = nShowType;
|
||||
obj["bShowDiagram"] = bShowDiagram;
|
||||
obj["nGraphType"] = nGraphType;
|
||||
obj["sTimeRange"] = sTimeRange;
|
||||
obj["sValue"] = mapToJson(mapValue);
|
||||
obj["bSelected"] = bSelected;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 从JSON对象解析
|
||||
void fromJson(const QJsonObject& json) {
|
||||
sGroup = json["sGroup"].toString();
|
||||
sTag = json["sTag"].toString();
|
||||
sName = json["sName"].toString();
|
||||
nConnectType = json["nConnectType"].toInt();
|
||||
sConnectPara = json["sConnectPara"].toString();
|
||||
nShowType = json["nShowType"].toInt();
|
||||
bShowDiagram = json["bShowDiagram"].toBool();
|
||||
nGraphType = json["nGraphType"].toInt();
|
||||
sTimeRange = json["sTimeRange"].toString();
|
||||
mapValue = jsonToMap(json["sValue"].toObject());
|
||||
bSelected = json["bSelected"].toBool();
|
||||
}
|
||||
|
||||
// 检查有效性
|
||||
bool isValid() const {
|
||||
return !sTag.isEmpty() && !sName.isEmpty();
|
||||
}
|
||||
|
||||
// 重载相等运算符
|
||||
bool operator==(const MonitorItemAttributeInfo& other) const {
|
||||
return sTag == other.sTag &&
|
||||
sName == other.sName &&
|
||||
sGroup == other.sGroup;
|
||||
}
|
||||
|
||||
QJsonObject mapToJson(const QMap<quint64, double>& map) const{
|
||||
QJsonObject jsonObj;
|
||||
|
||||
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
|
||||
// 将quint64的key转换为QString
|
||||
QString key = QString::number(it.key());
|
||||
jsonObj[key] = it.value();
|
||||
}
|
||||
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
// 从JSON转换回来
|
||||
QMap<quint64, double> jsonToMap(const QJsonObject& jsonObj) const{
|
||||
QMap<quint64, double> map;
|
||||
|
||||
for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it) {
|
||||
bool ok;
|
||||
quint64 key = it.key().toULongLong(&ok);
|
||||
if (ok) {
|
||||
double value = it.value().toDouble();
|
||||
map[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
};
|
||||
|
||||
struct MonitorItemTypeStruct //监控设备类型
|
||||
{
|
||||
QString sTag;
|
||||
QString sName;
|
||||
|
||||
bool isValid() const {
|
||||
return !sTag.isEmpty() && !sName.isEmpty();
|
||||
}
|
||||
|
||||
bool operator==(const MonitorItemTypeStruct& other) const {
|
||||
return sTag == other.sTag && sName == other.sName;
|
||||
}
|
||||
|
||||
bool operator<(const MonitorItemTypeStruct& other) const {
|
||||
return sTag < other.sTag || (sTag == other.sTag && sName < other.sName);
|
||||
}
|
||||
|
||||
// 转换为JSON对象 - 成员函数
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["sTag"] = sTag;
|
||||
obj["sName"] = sName;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 从JSON对象解析 - 成员函数
|
||||
void fromJson(const QJsonObject& json) {
|
||||
sTag = json["sTag"].toString();
|
||||
sName = json["sName"].toString();
|
||||
}
|
||||
};
|
||||
|
||||
struct MonitorItemStateStruct //监控设备状态
|
||||
{
|
||||
QString sState;
|
||||
MonitorItemState eState;
|
||||
|
||||
bool isValid() const {
|
||||
return !sState.isEmpty();
|
||||
}
|
||||
|
||||
bool operator==(const MonitorItemStateStruct& other) const {
|
||||
return sState == other.sState && eState == other.eState;
|
||||
}
|
||||
|
||||
bool operator<(const MonitorItemStateStruct& other) const {
|
||||
return sState < other.sState || (sState == other.sState && eState < other.eState);
|
||||
}
|
||||
|
||||
// 转换为JSON对象 - 成员函数
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["sState"] = sState;
|
||||
obj["eState"] = static_cast<int>(eState);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 从JSON对象解析 - 成员函数
|
||||
void fromJson(const QJsonObject& json) {
|
||||
sState = json["sState"].toString();
|
||||
eState = static_cast<MonitorItemState>(json["eState"].toInt());
|
||||
}
|
||||
};
|
||||
|
||||
struct ModelTypeInfo{ //类型临时信息
|
||||
int nType;
|
||||
QString sType;
|
||||
QString sName;
|
||||
QString sMeta; //该类型元模名
|
||||
QString sModel; //该类型工程模名
|
||||
};
|
||||
|
||||
struct MonitorPageInfo //运行时page
|
||||
{
|
||||
int id = -1;
|
||||
QUuid uid;
|
||||
QString tag;
|
||||
QString name;
|
||||
QString parent;
|
||||
QJsonObject context;
|
||||
QString ts;
|
||||
};
|
||||
|
||||
struct HMIPageInfo //人机界面组态page
|
||||
{
|
||||
int id = -1;
|
||||
QUuid uid;
|
||||
QString tag;
|
||||
QString name;
|
||||
QJsonObject context;
|
||||
QString ts;
|
||||
};
|
||||
|
||||
struct HMIImageInfo //人机界面中的图片资源
|
||||
{
|
||||
int id = -1;
|
||||
int baseType;
|
||||
QString imageName;
|
||||
QByteArray hash256;
|
||||
QByteArray svgData;
|
||||
};
|
||||
|
||||
struct HMIImageRef //人机界面中的图片引用
|
||||
{
|
||||
int id = -1;
|
||||
QUuid hmiId;
|
||||
QString model;
|
||||
QByteArray hash256;
|
||||
int slot;
|
||||
|
||||
bool operator==(const HMIImageRef& other) const
|
||||
{
|
||||
return hmiId == other.hmiId &&
|
||||
model == other.model &&
|
||||
hash256 == other.hash256 &&
|
||||
slot == other.slot;
|
||||
}
|
||||
};
|
||||
|
||||
struct MonitorItemDisplayInfo //监控模式显示信息
|
||||
{
|
||||
int nItemType; //设备类型
|
||||
QString sStateName; //状态名
|
||||
bool bEnable = false;
|
||||
QString sColor;
|
||||
QByteArray bytPicture; //存储二进制数据
|
||||
int nWidth;
|
||||
int nHeight;
|
||||
QString sMeta; //元模型名
|
||||
QString sModel; //工程模名
|
||||
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["nItemType"] = nItemType;
|
||||
obj["sStateName"] = sStateName;
|
||||
obj["bEnable"] = bEnable;
|
||||
obj["sColor"] = sColor;
|
||||
obj["bytPicture"] = QString(bytPicture.toBase64());
|
||||
obj["nWidth"] = nWidth;
|
||||
obj["nHeight"] = nHeight;
|
||||
obj["sMeta"] = sMeta;
|
||||
obj["sModel"] = sModel;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 从JSON对象解析 - 成员函数
|
||||
void fromJson(const QJsonObject& json) {
|
||||
nItemType = json["nItemType"].toInt();
|
||||
sStateName = json["sStateName"].toString();
|
||||
bEnable = json["bEnable"].toBool();
|
||||
sColor = json["sColor"].toString();
|
||||
|
||||
QString pictureBase64 = json["bytPicture"].toString();
|
||||
bytPicture = QByteArray::fromBase64(pictureBase64.toUtf8());
|
||||
|
||||
nWidth = json["nWidth"].toInt();
|
||||
nHeight = json["nHeight"].toInt();
|
||||
sMeta = json["sMeta"].toString();
|
||||
sModel = json["sModel"].toString();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
#ifndef BASEPROPERTY_H
|
||||
#define BASEPROPERTY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QJsonObject>
|
||||
//#include "global.h"
|
||||
#include "common/backend/project_model.h"
|
||||
#include "common/core_model/topology.h"
|
||||
|
||||
class AbstractProperty:public QObject //抽象属性类
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AbstractProperty(QObject* parent);
|
||||
virtual ~AbstractProperty();
|
||||
|
||||
virtual void setUuid(QUuid id) {uUid = id;}
|
||||
virtual QUuid uuid() const {return uUid;}
|
||||
virtual void setTag(QString s){sTag = s;}
|
||||
virtual QString tag() const {return sTag;}
|
||||
virtual void setName(QString s){sName = s;}
|
||||
virtual QString name() const {return sName;}
|
||||
virtual void setContext(QJsonObject j){jContext = j;}
|
||||
virtual QJsonObject context() const {return jContext;}
|
||||
virtual void setSubList(QList<QPair<int,QUuid>> lst) {subList = lst;}
|
||||
virtual QList<QPair<int,QUuid>>& getSubList() {return subList;}
|
||||
virtual void setVoltageLevel(double d){dVoltageLevel = d;}
|
||||
virtual double getVoltageLevel() const {return dVoltageLevel;}
|
||||
|
||||
virtual QJsonArray saveSubToJsonArr();
|
||||
protected:
|
||||
QUuid uUid;
|
||||
QString sTag;
|
||||
QString sName;
|
||||
QJsonObject jContext; //存放port信息
|
||||
QList<QPair<int,QUuid>> subList; //可能存在的子对象(不用来拓朴计算) <类型,uid> //类型0:设备 1:间隔
|
||||
double dVoltageLevel; //所属电压等级
|
||||
};
|
||||
|
||||
class BayProperty:public AbstractProperty //间隔属性(待扩展)
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BayProperty(QObject* parent = nullptr)
|
||||
:AbstractProperty(parent){
|
||||
dVoltage = 0.0;
|
||||
dFla = 0.0; //电流
|
||||
dCapacity = 0.0; //容量
|
||||
sType = "normal";
|
||||
}
|
||||
virtual ~BayProperty(){};
|
||||
public:
|
||||
virtual void setType(QString s) {sType = s;}
|
||||
virtual QString getType(){return sType;}
|
||||
virtual void setLstComponent(QList<QUuid> lst) {lstComponent = lst;}
|
||||
virtual QList<QUuid>& getLstComponent() {return lstComponent;}
|
||||
virtual void setVoltage(double d) {dVoltage = d;}
|
||||
virtual double getVoltage() {return dVoltage;}
|
||||
virtual void setFla(double d) {dFla = d;}
|
||||
virtual double getFla() {return dFla;}
|
||||
virtual void setCapacity(double d) {dCapacity = d;}
|
||||
virtual double getCapacity() {return dCapacity;}
|
||||
virtual void setInService(bool b) {bInService = b;}
|
||||
virtual bool getInService() {return bInService;}
|
||||
virtual void setLstFrom(QList<QUuid> lst) {lstFrom = lst;}
|
||||
virtual QList<QUuid>& getLstFrom() {return lstFrom;}
|
||||
virtual void setLstTo(QList<QUuid> lst) {lstTo = lst;}
|
||||
virtual QList<QUuid>& getLstTo() {return lstTo;}
|
||||
virtual void setLstProtect(QList<QUuid> lst) {lstProtect = lst;}
|
||||
virtual QList<QUuid>& getLstProtect() {return lstProtect;}
|
||||
virtual void setLstFaultRecord(QList<QUuid> lst) {lstFaultRecord = lst;}
|
||||
virtual QList<QUuid>& getLstFaultRecord() {return lstFaultRecord;}
|
||||
virtual void setLstDynSense(QList<QUuid> lst) {lstDynSense = lst;}
|
||||
virtual QList<QUuid>& getLstDynSense() {return lstDynSense;}
|
||||
virtual void setLstStatus(QList<QUuid> lst) {lstStatus = lst;}
|
||||
virtual QList<QUuid>& getLstStatus() {return lstStatus;}
|
||||
virtual void setLstInstruct(QList<QUuid> lst) {lstInstruct = lst;}
|
||||
virtual QList<QUuid>& getLstInstruct() {return lstInstruct;}
|
||||
virtual void setLstEtc(QList<QUuid> lst) {lstEtc = lst;}
|
||||
virtual QList<QUuid>& getLstEtc() {return lstEtc;}
|
||||
void setMeasurement(QMap<QString,MeasurementInfo> map) {mMeasurement = map;}
|
||||
auto getMeasurement() {return mMeasurement;}
|
||||
protected:
|
||||
QString sType;
|
||||
QList<QUuid> lstComponent; //包含的设备
|
||||
double dVoltage; //电压
|
||||
double dFla; //电流
|
||||
double dCapacity; //容量
|
||||
bool bInService; //服役状态
|
||||
QList<QUuid> lstFrom; //联结 从
|
||||
QList<QUuid> lstTo; //联结到
|
||||
|
||||
QList<QUuid> lstProtect; //综合保护
|
||||
QList<QUuid> lstFaultRecord; //故障录播
|
||||
QList<QUuid> lstDynSense; //动态感知
|
||||
QList<QUuid> lstStatus; //状态检测
|
||||
QList<QUuid> lstInstruct; //监控
|
||||
QList<QUuid> lstEtc; //其他设备
|
||||
QMap<QString,MeasurementInfo> mMeasurement; //量测 <name,info>
|
||||
};
|
||||
|
||||
class ModelProperty:public AbstractProperty //模型基类
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ModelProperty(QObject* parent);
|
||||
virtual ~ModelProperty();
|
||||
virtual void setType(int n) {nType = n;} //设置实际类型
|
||||
virtual int type() const {return nType;}
|
||||
virtual void setGraphicsType(int n) {nGraphicsType = n;} //设置显示类型
|
||||
virtual int graphicsType() const {return nGraphicsType;}
|
||||
virtual void setModelName(QString sName) {sModelName = sName;}
|
||||
virtual QString modelName() const {return sModelName;}
|
||||
virtual void setMetaModelName(QString sName) {sMetaName = sName;}
|
||||
virtual QString metaModelName() const {return sMetaName;}
|
||||
virtual void notifyUpdate(){emit updateData();}
|
||||
virtual void setBay(QString s){sBay = s;}
|
||||
virtual QString getBay(){return sBay;}
|
||||
|
||||
void setPrepareDelete(bool b) {_prepareDelete = b;}
|
||||
bool prepareDelete() const {return _prepareDelete;}
|
||||
void setDataChanged(bool b) {_dataChanged = b;} //数据变换标签
|
||||
bool dataChanged() const {return _dataChanged;}
|
||||
void setConnection(Connection con){m_connectState = con;} //保留,用以获取当前图中的连接
|
||||
Connection getConnection() const {return m_connectState;}
|
||||
signals:
|
||||
void updateData(); //通知数据拥有者更新
|
||||
protected:
|
||||
Connection m_connectState;
|
||||
int nType; //设备类型
|
||||
int nGraphicsType;
|
||||
QString sModelName; //模型名
|
||||
QString sMetaName; //元模型名
|
||||
QString sBay; //所属间隔
|
||||
|
||||
bool _dataChanged; //数据状态,为真则写入库
|
||||
bool _prepareDelete; //状态,为真准备删除
|
||||
};
|
||||
|
||||
class DiagramEditorItemProperty:public ModelProperty //基模编辑中预览元件的属性
|
||||
{
|
||||
public:
|
||||
DiagramEditorItemProperty(QObject* parent);
|
||||
virtual ~DiagramEditorItemProperty();
|
||||
void setBlock(const QString& s) {sBlock = s;}
|
||||
QString getBlock(){return sBlock;}
|
||||
private:
|
||||
QString sBlock; //所属的block(跨间隔连线等可能无此值)
|
||||
};
|
||||
|
||||
class BaseProperty;
|
||||
class BaseModelProperty:public ModelProperty //图像基模属性
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BaseModelProperty(QObject* parent);
|
||||
virtual ~BaseModelProperty();
|
||||
|
||||
virtual void addProData(QString sPage,BaseProperty* pData) {_generatedData.insert(sPage,pData);}
|
||||
virtual PropertyModel& getModelProperty() {return pm;}
|
||||
virtual void setModelProperty(PropertyModel pro) {pm = pro;}
|
||||
private:
|
||||
QMap<QString,BaseProperty*> _generatedData; //该数据生成过的工程模数据
|
||||
PropertyModel pm; //工程模的选择状态
|
||||
};
|
||||
|
||||
class BaseProperty:public ModelProperty //图像工程模模属性类,存放电路元件属性
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BaseProperty(QObject* parent);
|
||||
virtual ~BaseProperty();
|
||||
|
||||
void setPath(QString s){sPath = s;}
|
||||
QString path() const {return sPath;}
|
||||
void setDescription(QString s) {sDescription = s;}
|
||||
QString description() const {return sDescription;}
|
||||
void setInService(bool b) {bInService = b;}
|
||||
bool inService() {return bInService;}
|
||||
void setState(int n) {nState = n;}
|
||||
int state() const {return nState;}
|
||||
void setStatus(int n) {nStatus = n;}
|
||||
int status() const {return nStatus;}
|
||||
void setConnectedBus(QJsonObject j) {jConnectedBus = j;}
|
||||
QJsonObject connectedBus() const {return jConnectedBus;}
|
||||
void setLabel(QJsonObject j){jLabel = j;}
|
||||
QJsonObject label() const {return jLabel;}
|
||||
void setGrid(const QString& s) {sGrid = s;}
|
||||
QString grid() const {return sGrid;}
|
||||
void setZone(const QString& s) {sZone = s;}
|
||||
QString zone() const {return sZone;}
|
||||
void setStation(const QString& s) {sStation = s;}
|
||||
QString station() const {return sStation;}
|
||||
void setSourceItemId(const QString& s) {sSourceItemId = s;}
|
||||
QString getSourceItemId() {return sSourceItemId;}
|
||||
void setMeasurement(QMap<QString,MeasurementInfo> map) {mMeasurement = map;}
|
||||
auto getMeasurement() {return mMeasurement;}
|
||||
protected:
|
||||
QString sPath;
|
||||
QString sDescription;
|
||||
QString sGrid;
|
||||
QString sZone;
|
||||
QString sStation;
|
||||
bool bInService;
|
||||
int nState;
|
||||
int nStatus;
|
||||
QJsonObject jConnectedBus;
|
||||
QJsonObject jLabel;
|
||||
QString sSourceItemId; //被哪个对象生成
|
||||
QMap<QString,MeasurementInfo> mMeasurement; //量测
|
||||
};
|
||||
|
||||
typedef QMap<QString,QVariant> VariableMap; //属性名,值
|
||||
|
||||
class VariableProperty:public BaseProperty //收到的变量数据
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VariableProperty(QObject* parent = nullptr);
|
||||
~VariableProperty();
|
||||
|
||||
ModelDataInfo& getPropertyValue() const;
|
||||
};
|
||||
#endif // DATABASE_H
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "MinGW"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_MINGW
|
||||
#elif defined(__clang__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "Clang"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_CLANG
|
||||
#elif defined(_MSC_VER)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "Microsoft Visual C++"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_MICROSOFT
|
||||
#elif defined(__GNUC__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "GNU"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_GNU
|
||||
#define DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_MAJOR __GNUC__
|
||||
#define DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_MINOR __GNUC_MINOR__
|
||||
#define DIAGRAM_DESIGNER_COMPILER_GNU_VERSION_PATCH __GNUC_PATCHLEVEL__
|
||||
#elif defined(__BORLANDC__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "Borland C++ Builder"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_BORLAND
|
||||
#elif defined(__CODEGEARC__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "CodeGear C++ Builder"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_CODEGEAR
|
||||
#elif defined(__INTEL_COMPILER) || defined(__ICL)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "Intel C++"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_INTEL
|
||||
#elif defined(__xlC__) || defined(__IBMCPP__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "IBM XL C++"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_IBM
|
||||
#elif defined(__HP_aCC)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "HP aC++"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_HP
|
||||
#elif defined(__WATCOMC__)
|
||||
#define DIAGRAM_DESIGNER_COMPILER "Watcom C++"
|
||||
#define DIAGRAM_DESIGNER_COMPILER_WATCOM
|
||||
#endif
|
||||
|
||||
#ifndef DIAGRAM_DESIGNER_COMPILER
|
||||
#error "Current compiler is not supported."
|
||||
#endif
|
||||