Initial commit: BayTemplate Grid Framework DesignTime tool
- Qt-based application for designing electrical power grid structures - Features dockable UI with graphics canvas for placing grid elements - Includes QtADS (Advanced Docking System v4.3.1) and PropertyEditor - Supports Qt5/Qt6, multiple platforms (Windows, macOS, Linux, Android) - Architecture: DesignerView/Scene, SelectorManager, Command Pattern for undo/redo
|
|
@ -0,0 +1,17 @@
|
|||
kind: pipeline
|
||||
type: ssh
|
||||
name: default
|
||||
|
||||
server:
|
||||
host: 192.168.46.16
|
||||
user: jessequ
|
||||
password:
|
||||
from_secret: password
|
||||
|
||||
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.3/gcc_64 -DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=/home/jessequ/Qt/Platforms/package-manager/auto-setup.cmake
|
||||
- /home/jessequ/Qt/Tools/Ninja/ninja
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
build/
|
||||
.vscode/
|
||||
.qtcreator/
|
||||
|
||||
# ---> CMake
|
||||
CMakeLists.txt.user
|
||||
CMakeUserPresets.json
|
||||
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,144 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
BayTemplate is a **Grid Framework DesignTime** tool - a Qt-based application for designing electrical power grid structures. It features a dockable UI with a graphics canvas for placing and manipulating grid elements.
|
||||
|
||||
## Build Commands
|
||||
|
||||
### Build the Project
|
||||
|
||||
```bash
|
||||
# Create build/debug directory and configure
|
||||
mkdir -p build/debug && cd build/debug
|
||||
/Users/jesse/Qt/Tools/CMake/CMake.app/Contents/bin/cmake -S ../.. -B . -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_GENERATOR:STRING=Ninja -DCMAKE_MAKE_PROGRAM:FILEPATH=/Users/jesse/Qt/Tools/Ninja/ninja -DQT_QMAKE_EXECUTABLE:FILEPATH=/Users/jesse/Qt/6.9.3/macos/bin/qmake -DCMAKE_PREFIX_PATH:PATH=/Users/jesse/Qt/6.9.3/macos -DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=/Users/jesse/Platforms/package-manager/auto-setup.cmake -DCMAKE_CXX_COMPILER:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -DCMAKE_C_COMPILER:FILEPATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -DCMAKE_CXX_FLAGS_INIT:STRING=-DQT_QML_DEBUG -DPostgreSQL_INCLUDE_DIR:FILEPATH=/opt/homebrew/opt/postgresql@17/include/postgresql -DPostgreSQL_LIBRARY_DIR:FILEPATH=/opt/homebrew/opt/postgresql@17/lib/postgresql -DPostgreSQL_LIBRARY:FILEPATH=/opt/homebrew/opt/postgresql@17/lib/postgresql/libpq.dylib
|
||||
|
||||
# Build
|
||||
/Users/jesse/Qt/Tools/Ninja/ninja -C /Users/jesse/Workspaces/projects/qt-projects/qtclaw/BayTemplate/build/debug
|
||||
```
|
||||
|
||||
### Build Output Locations
|
||||
|
||||
Platform-specific output directories under `build/`:
|
||||
- `build/x64/bin/BayTemplate` - Executable (64-bit Intel/AMD)
|
||||
- `build/x86/bin/BayTemplate` - Executable (32-bit)
|
||||
- `build/arm64/bin/BayTemplate` - Executable (64-bit ARM)
|
||||
- `build/${pd_PlatformDir}/lib/` - Libraries
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
#### 1. Docking System (Qt Advanced Docking System 4.3.1)
|
||||
- **CDockManager** manages dockable panels
|
||||
- **Left Dock**: 图元面板 (GraphicElementsPanel) - library of draggable grid elements
|
||||
- **Center**: DrawingPanel - main canvas area with DesignerScene
|
||||
- **Right Dock**: 属性编辑器 (PropertyEditor) - QDetailsView for editing selected item properties
|
||||
|
||||
#### 2. Graphics View Framework
|
||||
- **DesignerView** (`QGraphicsView` subclass): Custom view with zoom (0.02-50x), pan (middle-button drag), checkerboard background
|
||||
- **DesignerScene** (`QGraphicsScene` subclass): Custom scene with grid overlay (20px spacing), routes mouse events through SelectorManager
|
||||
|
||||
#### 3. Selector System
|
||||
Selector hierarchy in `include/util/`:
|
||||
- **BaseSelector**: Abstract base for all selectors
|
||||
- **CreatingSelector**: Mode for creating new items from template
|
||||
- **MovingSelector**: Mode for moving items
|
||||
- **RotationSelector**: Mode for rotating items around origin
|
||||
- **ScalingSelector**: Mode for scaling items
|
||||
- **EditingSelector**: Mode for editing polygon vertex positions
|
||||
- **SelectorManager**: Singleton that manages the active working selector
|
||||
|
||||
Mouse events flow: `DesignerScene` → `SelectorManager::getWorkingSelector()` → specific selector implementation
|
||||
|
||||
#### 4. Graphics Items (`include/graphicsItem/`)
|
||||
- **GraphicsBaseItem**: Abstract base class for all grid elements
|
||||
- **GraphicsRectItem**: Rectangle element
|
||||
- **GraphicsPolygonItem**: Polygon element (editable vertices)
|
||||
- **GraphicsItemGroup**: Container for grouped items (supports flattening to prevent nesting)
|
||||
- **GraphicsBusSectionItem**: Bus section element
|
||||
- **ItemControlHandle**: Visual handles for manipulation (rotation, scaling, editing)
|
||||
|
||||
#### 5. Property Editor (Qt PropertyEditor)
|
||||
- **QDetailsView**: Qt Quick-based property editor in right dock
|
||||
- **QCustomType**: Custom property type for graphics item properties
|
||||
- **PropertyTypeCustomization_CustomType**: Custom property editor integration
|
||||
- Property editor observes `QGraphicsScene` and displays properties of selected items
|
||||
|
||||
#### 6. Command Pattern (Undo/Redo)
|
||||
- **QUndoStack** (`m_pUndoStack` in CMainWindow) manages undo/redo
|
||||
- **OperationCommand**: Base command class
|
||||
- **AddItemCommand**: Add item to scene
|
||||
- **DeleteItemCommand**: Remove item from scene
|
||||
- **CreateItemGoupCommand**: Group selected items (with flattening)
|
||||
- **DestroyItemGoupCommand**: Ungroup items
|
||||
|
||||
### Signal Flow
|
||||
|
||||
1. **User drags from 图元面板** → `GraphicElementsPanel` emits signal → `DesignerScene::signalAddItem()` → creates item with `AddItemCommand`
|
||||
|
||||
2. **User clicks on canvas** → `DesignerView` → `DesignerScene` → `SelectorManager::getWorkingSelector()` → selector processes event
|
||||
|
||||
3. **User selects items** → `DesignerScene::selectionChanged()` → `CMainWindow::onSignal_selectionChanged()` → updates property editor
|
||||
|
||||
4. **User modifies property in 属性编辑器** → `QDetailsView` updates item property → scene updates
|
||||
|
||||
### Key Files and Relationships
|
||||
|
||||
```
|
||||
source/main.cpp # Entry point - creates CMainWindow
|
||||
└── source/mainwindow.h/.cpp # Main window with CDockManager, initializes all components
|
||||
├── initializeDockUi() # Sets up docks (left, center, right)
|
||||
├── initializeAction() # Sets up QUndoStack and menu actions
|
||||
└── Event handlers # onSignal_addItem, onSignal_selectionChanged, etc.
|
||||
|
||||
source/drawingPanel.h/.cpp # Central dock widget containing DesignerView
|
||||
└── DesignerView + DesignerScene
|
||||
|
||||
source/designerView.h/.cpp # QGraphicsView subclass - zoom, pan, middle-button navigation
|
||||
|
||||
source/designerScene.h/.cpp # QGraphicsScene subclass - grid background, group operations
|
||||
└── Delegates to SelectorManager for mouse events
|
||||
|
||||
source/util/selectorManager.h/.cpp # Singleton managing active selector
|
||||
└── Selector::mousePressEvent/ReleaseEvent/MoveEvent()
|
||||
|
||||
source/graphicsItem/ # Graphics item implementations
|
||||
├── GraphicsBaseItem # Base class with common functionality
|
||||
├── GraphicsRectItem # Rectangle
|
||||
├── GraphicsPolygonItem # Editable polygon
|
||||
├── GraphicsItemGroup # Item grouping
|
||||
└── GraphicsBusSectionItem
|
||||
|
||||
source/propertyType/ # Custom property editor integration
|
||||
├── CustomType.h
|
||||
├── CustomGadget.h
|
||||
└── PropertyTypeCustomization_CustomType.cpp
|
||||
```
|
||||
|
||||
### Third-Party Dependencies
|
||||
|
||||
1. **QtADS** (`QtADS/` subdirectory, v4.3.1): Advanced docking system - must be present as subdirectory
|
||||
2. **PropertyEditor** (`PropertyEditor/` subdirectory): Qt Quick-based property editing system with QDetailsView
|
||||
|
||||
Both are added via `add_subdirectory()` in CMakeLists.txt and must exist in the repository root.
|
||||
|
||||
### Platform Support
|
||||
|
||||
- **Qt Version**: Qt5 or Qt6 (auto-detected, `find_package(QT NAMES Qt6 Qt5 ...)`)
|
||||
- **Architectures**: x86, x64, arm64, aarch64 (auto-detected in CMakeLists.txt)
|
||||
- **Platforms**: Windows, macOS, Linux, Android (Android uses shared library)
|
||||
|
||||
### Important Implementation Details
|
||||
|
||||
1. **CMAKE_AUTOUIC_SEARCH_PATHS**: Set to `"ui"` in CMakeLists.txt because .ui files are in separate directory from header files
|
||||
|
||||
2. **Group Flattening**: When creating a group that contains existing groups, the scene flattens nested groups first to prevent group nesting (see `DesignerScene::createGroup()`)
|
||||
|
||||
3. **Zoom Implementation**: Uses `QGraphicsView::zoom()` with smooth factor `qPow(1.0015, angleDelta.y())` per wheel event
|
||||
|
||||
4. **Wchar String Conversion**: Uses `QString::fromWCharArray(L"中文")` for Chinese UI strings
|
||||
|
||||
5. **Resource File**: `resource/BayTemplate.qrc` contains checkerboard background and icons, referenced in .ui files and CMakeLists.txt
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
cmake_minimum_required(VERSION 3.5...4.3)
|
||||
|
||||
project(BayTemplate LANGUAGES CXX VERSION 1.0)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(ADS_VERSION 4.3.1)
|
||||
add_subdirectory(QtADS)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets REQUIRED)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
#默认ui文件要和.h头文件在一个目录,若不在一个目录,需要指定其所在目录
|
||||
set(CMAKE_AUTOUIC_SEARCH_PATHS "ui")
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||
set(pd_PlatformDir "x86")
|
||||
else()
|
||||
if(DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(pd_PlatformDir "x64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64")
|
||||
set(pd_PlatformDir "arm64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
set(pd_PlatformDir "aarch64")
|
||||
else()
|
||||
set(pd_PlatformDir "x64")
|
||||
endif()
|
||||
else()
|
||||
set(pd_PlatformDir "x64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(PropertyEditor)
|
||||
|
||||
set(H_HEADER_FILES
|
||||
include/global.h
|
||||
include/mainwindow.h
|
||||
include/graphicElementsPanel.h
|
||||
include/drawingPanel.h
|
||||
include/designerScene.h
|
||||
include/designerView.h
|
||||
include/operationCommand.h
|
||||
|
||||
include/propertyType/CustomGadget.h
|
||||
include/propertyType/CustomType.h
|
||||
include/propertyType/PropertyTypeCustomization_CustomType.h
|
||||
|
||||
include/util/baseSelector.h
|
||||
include/util/creatingSelector.h
|
||||
include/util/movingSelector.h
|
||||
include/util/rotationSelector.h
|
||||
include/util/scalingSelector.h
|
||||
include/util/editingSelector.h
|
||||
include/util/selectorManager.h
|
||||
|
||||
include/graphicsItem/itemControlHandle.h
|
||||
include/graphicsItem/graphicsBaseItem.h
|
||||
include/graphicsItem/graphicsRectItem.h
|
||||
include/graphicsItem/graphicsPolygonItem.h
|
||||
include/graphicsItem/graphicsItemGroup.h
|
||||
include/graphicsItem/graphicsBusSectionItem.h
|
||||
)
|
||||
set(CPP_SOURCE_FILES
|
||||
source/main.cpp
|
||||
source/mainwindow.cpp
|
||||
source/graphicElementsPanel.cpp
|
||||
source/drawingPanel.cpp
|
||||
source/designerScene.cpp
|
||||
source/designerView.cpp
|
||||
source/operationCommand.cpp
|
||||
|
||||
source/propertyType/PropertyTypeCustomization_CustomType.cpp
|
||||
|
||||
source/util/baseSelector.cpp
|
||||
source/util/creatingSelector.cpp
|
||||
source/util/movingSelector.cpp
|
||||
source/util/rotationSelector.cpp
|
||||
source/util/scalingSelector.cpp
|
||||
source/util/editingSelector.cpp
|
||||
source/util/selectorManager.cpp
|
||||
|
||||
source/graphicsItem/itemControlHandle.cpp
|
||||
source/graphicsItem/graphicsBaseItem.cpp
|
||||
source/graphicsItem/graphicsRectItem.cpp
|
||||
source/graphicsItem/graphicsPolygonItem.cpp
|
||||
source/graphicsItem/graphicsItemGroup.cpp
|
||||
source/graphicsItem/graphicsBusSectionItem.cpp
|
||||
)
|
||||
set(UI_FILES
|
||||
ui/mainwindow.ui
|
||||
ui/graphicElementsPanel.ui
|
||||
ui/drawingPanel.ui
|
||||
)
|
||||
|
||||
# 包含源文件目录
|
||||
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
|
||||
|
||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_executable(BayTemplate
|
||||
MANUAL_FINALIZATION
|
||||
${H_HEADER_FILES}
|
||||
${CPP_SOURCE_FILES}
|
||||
${UI_FILES}
|
||||
resource/BayTemplate.qrc
|
||||
)
|
||||
else()
|
||||
if(ANDROID)
|
||||
add_library(BayTemplate SHARED
|
||||
${H_HEADER_FILES}
|
||||
${CPP_SOURCE_FILES}
|
||||
${UI_FILES}
|
||||
resource/BayTemplate.qrc
|
||||
)
|
||||
else()
|
||||
add_executable(BayTemplate WIN32
|
||||
${H_HEADER_FILES}
|
||||
${CPP_SOURCE_FILES}
|
||||
${UI_FILES}
|
||||
resource/BayTemplate.qrc
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(BayTemplate PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_include_directories(BayTemplate PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/PropertyEditor/source/include")
|
||||
target_link_libraries(BayTemplate PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
||||
target_link_libraries(BayTemplate PRIVATE PropertyEditor)
|
||||
target_link_libraries(BayTemplate PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
set_target_properties(BayTemplate PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
VERSION 1.0
|
||||
EXPORT_NAME "BayTemplate with Qt Advanced Docking System"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/bin"
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(PropertyEditor CXX)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library (ON) or static library (OFF)" OFF)
|
||||
|
||||
find_package(Qt6 COMPONENTS Core Widgets Gui QuickWidgets QuickTemplates2 QuickControls2 REQUIRED)
|
||||
|
||||
qt6_add_resources(QRC_FILE resources.qrc)
|
||||
|
||||
file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/*.h ${CMAKE_CURRENT_SOURCE_DIR}/source/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/resources/*)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE})
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
add_library(PropertyEditor SHARED ${PROJECT_SOURCE} ${QRC_FILE})
|
||||
target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_SHARED_LIBRARY)
|
||||
else()
|
||||
add_library(PropertyEditor STATIC ${PROJECT_SOURCE} ${QRC_FILE})
|
||||
target_compile_definitions(PropertyEditor PUBLIC PROPERTY_EDITOR_STATIC_LIBRARY)
|
||||
endif()
|
||||
|
||||
|
||||
set_property(TARGET PropertyEditor PROPERTY AUTOMOC ON)
|
||||
set_property(TARGET PropertyEditor PROPERTY USE_FOLDERS ON)
|
||||
set_property(TARGET PropertyEditor PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files")
|
||||
|
||||
target_compile_definitions(PropertyEditor PRIVATE PROPERTY_EDITOR_LIBRARY)
|
||||
|
||||
target_link_libraries(PropertyEditor PUBLIC
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Widgets
|
||||
Qt::WidgetsPrivate
|
||||
Qt::QuickWidgets
|
||||
Qt::QuickPrivate
|
||||
Qt::QuickTemplates2
|
||||
Qt::QuickTemplates2Private
|
||||
Qt::QuickControls2
|
||||
)
|
||||
|
||||
target_include_directories(PropertyEditor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/source/include)
|
||||
file(GLOB_RECURSE PUBLIC_FILES LIST_DIRECTORIES TRUE ${CMAKE_CURRENT_SOURCE_DIR}/source/include/*)
|
||||
foreach(PUBLIC_FILE ${PUBLIC_FILES})
|
||||
if(IS_DIRECTORY ${PUBLIC_FILE})
|
||||
target_include_directories(PropertyEditor PRIVATE ${PUBLIC_FILE})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set_target_properties(
|
||||
PropertyEditor
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/bin"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
)
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
# QDetailsView
|
||||
|
||||
[中文用户?点我查看中文介绍](README_zh.md)
|
||||
|
||||
Inspired by the details view of Unreal Engine, QDetailsView leverages Qt's reflection system to easily build property editors for qobject.
|
||||
|
||||
Its core features are:
|
||||
|
||||
- Create type-based control editors that automatically organize the editor layout according to the reflection structure of the object.
|
||||
- Utilize QML GPU rendering and control management based on the preview view.
|
||||
|
||||
## Usage
|
||||
|
||||
It is extremely easy to use—simply declare the meta-properties of a `QObject` using **`Q_PROPERTY(...)`**:
|
||||
|
||||
```c++
|
||||
class QCustomObject : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int Int READ getInt WRITE setInt)
|
||||
Q_PROPERTY(float Float READ getFloat WRITE setFloat)
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
```c++
|
||||
QCustomObject obj;
|
||||
QDetailsView view;
|
||||
view.setObject(&obj);
|
||||
view.show();
|
||||
```
|
||||
|
||||
You will get the following result:
|
||||
|
||||

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

|
||||
|
||||
## 定制化
|
||||
|
||||
### 关于 QPropertyHandle
|
||||
|
||||
`QPropertyHandle` 是QDetailsView操作属性的统一入口,它通常通过如下接口构建:
|
||||
|
||||
``` c++
|
||||
static QPropertyHandle* QPropertyHandle::FindOrCreate(
|
||||
QObject* inParent, // 父对象,它会持有PropertyHandle周期
|
||||
QMetaType inType, // 该属性的元类型
|
||||
QString inPropertyPath, // 该属性的路径字段
|
||||
Getter inGetter, // 该属性的获取器
|
||||
Setter inSetter // 该属性的设置器
|
||||
);
|
||||
```
|
||||
|
||||
为了保证属性值的变动可以被DetailsView响应,所有对值的修改请统一使用PropertyHandle的接口:
|
||||
|
||||
``` C++
|
||||
QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName");
|
||||
if(handle){
|
||||
handle->setVar(QVariant::fromValue(newValue));
|
||||
}
|
||||
```
|
||||
|
||||
`QPropertyHandle`创建时要求指定`parent`,它将会作为子对象挂载上去,因此它的生命周期将跟随父对象,如果要清理,请调用:
|
||||
|
||||
``` c++
|
||||
static void QPropertyHandle::Cleanup(QObject* inParent);
|
||||
```
|
||||
|
||||
### 自定义枚举
|
||||
|
||||
对于枚举类型,需要使用类内定义的方式,并通过**Q_ENUM(...)**声明:
|
||||
|
||||
``` c++
|
||||
class QCustomObject: public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum QCustomEnum {
|
||||
One,
|
||||
Two,
|
||||
Three
|
||||
};
|
||||
Q_ENUM(QCustomEnum);
|
||||
};
|
||||
```
|
||||
|
||||
### 自定义类型编辑器
|
||||
|
||||
非`QObject`的自定义类型,首先需要在定义类型时使用宏**Q_DECLARE_METATYPE(...)**进行声明。
|
||||
|
||||
对于只有单个编辑器控件的特定类型,可以使用如下接口直接注册类型编辑器:
|
||||
|
||||
``` c++
|
||||
QQuickDetailsViewManager::Get()->registerTypeEditor(
|
||||
metaType,
|
||||
[](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
在源代码的`QQuickDetailsViewBasicTypeEditor.cpp`目录下有许多参考示例,比如QDir:
|
||||
|
||||
``` c++
|
||||
registerTypeEditor(
|
||||
QMetaType::fromType<QDir>(),
|
||||
[](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
DirectorySelector{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
对于具有多行或者行为较为复杂的编辑器控件,可以通过派生`IPropertyTypeCustomization`来扩展属性编辑器,它提供两个虚函数:
|
||||
|
||||
``` c++
|
||||
class IPropertyTypeCustomization :public QEnableSharedFromThis<IPropertyTypeCustomization>
|
||||
{
|
||||
public:
|
||||
// 用于装配当前属性行的编辑器
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
|
||||
|
||||
// 用于扩展当前属性行的子项
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
|
||||
};
|
||||
```
|
||||
|
||||
之后使用如下接口进行注册:
|
||||
|
||||
``` c++
|
||||
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- 撤销重做
|
||||
- 容器操作
|
||||
- 扩展元信息
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
file(GLOB_RECURSE PROJECT_SOURCE FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${PROJECT_SOURCE})
|
||||
|
||||
add_executable(PropertyEditorExample
|
||||
${PROJECT_SOURCE}
|
||||
)
|
||||
|
||||
set_property(TARGET PropertyEditorExample PROPERTY AUTOMOC ON)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS QuickControls2)
|
||||
|
||||
target_link_libraries(PropertyEditorExample PUBLIC PropertyEditor
|
||||
Qt::Widgets
|
||||
Qt::QuickWidgets
|
||||
Qt::QuickPrivate
|
||||
Qt::QuickTemplates2
|
||||
Qt::QuickTemplates2Private
|
||||
Qt::QuickControls2
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
PropertyEditorExample
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/bin"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${pd_PlatformDir}/lib"
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef CommonInclude_h__
|
||||
#define CommonInclude_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
|
||||
#define Q_PROPERTY_VAR(Type,Name)\
|
||||
Q_PROPERTY(Type Name READ get##Name WRITE set##Name) \
|
||||
Type get##Name(){ return Name; } \
|
||||
void set##Name(Type var){ \
|
||||
Name = var; \
|
||||
qDebug() <<"Set" <<this <<#Name <<": " <<var; \
|
||||
} \
|
||||
Type Name
|
||||
|
||||
#endif // CommonInclude_h__
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef CustomGadget_h__
|
||||
#define CustomGadget_h__
|
||||
|
||||
#include "CommonInclude.h"
|
||||
|
||||
class QCustomGadget {
|
||||
Q_GADGET
|
||||
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
|
||||
public:
|
||||
Q_PROPERTY_VAR(double, LimitedDouble) = 1;
|
||||
Q_PROPERTY_VAR(QString, Desc) = "This is inline Gadget";
|
||||
};
|
||||
|
||||
static QDebug operator<<(QDebug debug, const QCustomGadget& gadget) {
|
||||
return debug << gadget.LimitedDouble << gadget.Desc;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedPointer<QCustomGadget>);
|
||||
|
||||
#endif // CustomGadget_h__
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef CustomObject_h__
|
||||
#define CustomObject_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <qvectornd.h>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QColor>
|
||||
#include <QMatrix4x4>
|
||||
#include "CommonInclude.h"
|
||||
#include "CustomGadget.h"
|
||||
#include "CustomType.h"
|
||||
|
||||
class QCustomObject :public QObject {
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("LimitedDouble", "Min=0,Max=10")
|
||||
public:
|
||||
enum QCustomEnum {
|
||||
One,
|
||||
Two,
|
||||
Three
|
||||
};
|
||||
Q_ENUM(QCustomEnum);
|
||||
|
||||
Q_PROPERTY_VAR(int, Int) = 0;
|
||||
Q_PROPERTY_VAR(float, Float) = 1.23f;
|
||||
Q_PROPERTY_VAR(double, LimitedDouble) = 5;
|
||||
Q_PROPERTY_VAR(QString, String);
|
||||
Q_PROPERTY_VAR(QDir, Directory) = QDir(".");
|
||||
Q_PROPERTY_VAR(QVector2D, Vec2) = QVector2D(1, 2);
|
||||
Q_PROPERTY_VAR(QVector3D, Vec3) = QVector3D(1, 2, 3);
|
||||
Q_PROPERTY_VAR(QVector4D, Vec4) = QVector4D(1, 2, 3, 4);
|
||||
Q_PROPERTY_VAR(QMatrix4x4, Mat4);
|
||||
Q_PROPERTY_VAR(QColor, Color);
|
||||
Q_PROPERTY_VAR(QList<QColor>, ColorList) = { Qt::red,Qt::green,Qt::blue };
|
||||
|
||||
typedef QMap<QString, QColor> StringColorMap;
|
||||
Q_PROPERTY_VAR(StringColorMap, ColorMap) = { {"Red",Qt::red},{"Green",Qt::green},{"Blue",Qt::blue} };
|
||||
|
||||
Q_PROPERTY_VAR(QCustomEnum, CustomEnum) = QCustomEnum::One;
|
||||
Q_PROPERTY_VAR(QCustomType, CustomType);
|
||||
Q_PROPERTY_VAR(QCustomGadget, CustomGadget);
|
||||
Q_PROPERTY_VAR(QCustomGadget*, CustomGadgetPtr) = new QCustomGadget;
|
||||
Q_PROPERTY_VAR(QSharedPointer<QCustomGadget>, CustomGadgetSharedPtr) = QSharedPointer<QCustomGadget>::create();
|
||||
Q_PROPERTY_VAR(QCustomObject*, SubCustomObject) = nullptr;
|
||||
};
|
||||
|
||||
#endif // CustomObject_h__
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef CustomType_h__
|
||||
#define CustomType_h__
|
||||
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
|
||||
struct QCustomType {
|
||||
unsigned int ArraySize = 0;
|
||||
QVector<int> Array;
|
||||
};
|
||||
|
||||
static QDebug operator<<(QDebug debug, const QCustomType& it) {
|
||||
return debug << it.Array;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QCustomType)
|
||||
|
||||
#endif // CustomType_h__
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#include "PropertyTypeCustomization_CustomType.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QRandomGenerator>
|
||||
#include "QQuickDetailsViewModel.h"
|
||||
#include "CustomType.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
|
||||
void PropertyTypeCustomization_CustomType::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
|
||||
{
|
||||
auto editorSlot = inBuilder->makeNameValueSlot();
|
||||
inPropertyHandle->setupNameEditor(editorSlot.first);
|
||||
auto buttonItem = inBuilder->setupItem(editorSlot.second, R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
Button{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 80
|
||||
height: 20
|
||||
text: "Sort"
|
||||
}
|
||||
)");
|
||||
|
||||
|
||||
QQuickFunctionLibrary::connect(buttonItem, SIGNAL(clicked()), inPropertyHandle, [inPropertyHandle]() {
|
||||
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
|
||||
std::sort(customType.Array.begin(), customType.Array.end());
|
||||
inPropertyHandle->setVar(QVariant::fromValue(customType));
|
||||
if (auto arrayHandle = inPropertyHandle->findChild("Array")) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PropertyTypeCustomization_CustomType::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto arrayHandle = inPropertyHandle->findOrCreateChild(
|
||||
QMetaType::fromType<QVector<int>>(),
|
||||
"Array",
|
||||
[inPropertyHandle]() {
|
||||
return QVariant::fromValue(inPropertyHandle->getVar().value<QCustomType>().Array);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
|
||||
customType.Array = var.value<QVector<int>>();
|
||||
inPropertyHandle->setVar(QVariant::fromValue(customType));
|
||||
}
|
||||
);
|
||||
|
||||
auto arraySizeHandle = inPropertyHandle->findOrCreateChild(
|
||||
QMetaType::fromType<unsigned int>(),
|
||||
"ArraySize",
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QCustomType>().ArraySize;
|
||||
},
|
||||
[inPropertyHandle, arrayHandle](QVariant var) {
|
||||
QCustomType customType = inPropertyHandle->getVar().value<QCustomType>();
|
||||
customType.ArraySize = var.toUInt();
|
||||
customType.Array.resize(customType.ArraySize);
|
||||
for (int i = 0; i < customType.ArraySize; ++i) {
|
||||
customType.Array[i] = QRandomGenerator::global()->bounded(-100000, 100000);
|
||||
}
|
||||
inPropertyHandle->setVar(QVariant::fromValue(customType));
|
||||
arrayHandle->invalidateStructure();
|
||||
}
|
||||
);
|
||||
|
||||
inBuilder->addProperty(arraySizeHandle);
|
||||
inBuilder->addProperty(arrayHandle);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef PropertyTypeCustomization_CustomType_h__
|
||||
#define PropertyTypeCustomization_CustomType_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_CustomType : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override;
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_CustomType_h__
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include <QApplication>
|
||||
#include "QDetailsView.h"
|
||||
#include "CustomObject.h"
|
||||
#include "CustomType.h"
|
||||
#include "PropertyTypeCustomization_CustomType.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QCustomObject obj;
|
||||
obj.setSubCustomObject(new QCustomObject);
|
||||
obj.getSubCustomObject()->setSubCustomObject(new QCustomObject);
|
||||
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
|
||||
QDetailsView view;
|
||||
view.setObject(&obj);
|
||||
view.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>resources/Icon/arrow-left-l.png</file>
|
||||
<file>resources/Icon/arrow-right-l.png</file>
|
||||
<file>resources/Icon/close.png</file>
|
||||
<file>resources/Icon/delete.png</file>
|
||||
<file>resources/Icon/down.png</file>
|
||||
<file>resources/Icon/expand.png</file>
|
||||
<file>resources/Icon/plus.png</file>
|
||||
<file>resources/Icon/reset.png</file>
|
||||
<file>resources/Icon/unexpand.png</file>
|
||||
<file>resources/Icon/up.png</file>
|
||||
<file>resources/Qml/ValueEditor/ColorBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/NumberBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/TextComboBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/Vec2Box.qml</file>
|
||||
<file>resources/Qml/ValueEditor/Vec3Box.qml</file>
|
||||
<file>resources/Qml/ValueEditor/Vec4Box.qml</file>
|
||||
<file>resources/Qml/ColorPalette/ColorPalette.qml</file>
|
||||
<file>resources/Qml/ColorPalette/ColorPalette_Dark.qml</file>
|
||||
<file>resources/Qml/ColorPalette/ColorPalette_Light.qml</file>
|
||||
<file>resources/Qml/ValueEditor/DirectorySelector.qml</file>
|
||||
<file>resources/Qml/ValueEditor/FileSelector.qml</file>
|
||||
<file>resources/Qml/ValueEditor/LineTextBox.qml</file>
|
||||
<file>resources/Qml/ValueEditor/MultiLineTextBox.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
import QtQuick
|
||||
|
||||
pragma Singleton
|
||||
|
||||
QtObject {
|
||||
id: colorPalette
|
||||
property var theme : ColorPalette_Light
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import QtQuick
|
||||
|
||||
pragma Singleton
|
||||
|
||||
QtObject {
|
||||
property color labelPrimary : "#FFFFFF"
|
||||
property color textPrimary: "#EEEEEE"
|
||||
|
||||
property color textBoxBackground: "#444444"
|
||||
|
||||
property color rowBackground: "#333333"
|
||||
property color rowBackgroundHover: "#888888"
|
||||
property color rowBorder: "#666666"
|
||||
property color rowIndicator: "#CCCCCC"
|
||||
property color rowSplitter: "#666666"
|
||||
property color rowShadowStart: "#00000000"
|
||||
property color rowShadowEnd: "#66000000"
|
||||
|
||||
property color boxHover: "#A478DB"
|
||||
|
||||
property color comboBoxBackground: "#444444"
|
||||
property color comboBoxItemBackground: "#666666"
|
||||
property color comboBoxItemBackgroundHover: "#888888"
|
||||
|
||||
property color buttonBackground: "#111111"
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import QtQuick
|
||||
|
||||
pragma Singleton
|
||||
|
||||
QtObject {
|
||||
property color labelPrimary : "#444444"
|
||||
property color textPrimary: "#666666"
|
||||
|
||||
property color textBoxBackground: "#f8fef9"
|
||||
|
||||
property color rowBackground: "white"
|
||||
property color rowBackgroundHover: "#F3F3F3"
|
||||
property color rowBorder: "#EEEEEE"
|
||||
property color rowIndicator: "#cef9ce"
|
||||
property color rowSplitter: "#EEEEEE"
|
||||
property color rowShadowStart: "#00000000"
|
||||
property color rowShadowEnd: "#14000000"
|
||||
|
||||
property color boxHover: "#cef9ce"
|
||||
|
||||
property color comboBoxBackground: "#f8fef9"
|
||||
property color comboBoxItemBackground: "#FFFFFF"
|
||||
property color comboBoxItemBackgroundHover: "#cef9ce"
|
||||
|
||||
property color buttonBackground: "#cef9ce"
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Dialogs
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property color value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(text:var)
|
||||
|
||||
function setValue(newValue:var){
|
||||
if(newValue !== value){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
Button{
|
||||
anchors.margins: 2
|
||||
anchors.fill: parent
|
||||
palette {
|
||||
button: value
|
||||
}
|
||||
background: Rectangle {
|
||||
color: value
|
||||
}
|
||||
onClicked: {
|
||||
colorDialog.open()
|
||||
}
|
||||
}
|
||||
ColorDialog {
|
||||
id: colorDialog
|
||||
selectedColor: value
|
||||
onAccepted: {
|
||||
control.setValue(selectedColor)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import QtCore
|
||||
import ColorPalette
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property var value
|
||||
implicitHeight: dirBox.implicitHeight
|
||||
signal asValueChanged(text: var)
|
||||
|
||||
function setValue(newValue: var){
|
||||
if(newValue !== value){
|
||||
value = newValue
|
||||
dirBox.value = value
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
|
||||
LineTextBox {
|
||||
id: dirBox
|
||||
value: control.value
|
||||
anchors.left: parent.left
|
||||
anchors.right: button.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onValueChanged: {
|
||||
control.setValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: button
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 30
|
||||
height: 25
|
||||
text: "..."
|
||||
palette.buttonText: ColorPalette.theme.textPrimary
|
||||
background: Rectangle {
|
||||
color: ColorPalette.theme.buttonBackground
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
folderDialog.open()
|
||||
}
|
||||
}
|
||||
FolderDialog {
|
||||
id: folderDialog
|
||||
title: "选择目录"
|
||||
onAccepted: {
|
||||
var filePath = currentFolder.toString();
|
||||
|
||||
if (filePath.startsWith("file:///")) {
|
||||
filePath = filePath.substring(8);
|
||||
}
|
||||
|
||||
control.setValue(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Dialogs
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property var value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(text:var)
|
||||
|
||||
function setValue(newValue:var){
|
||||
if(newValue !== value){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
Button{
|
||||
anchors.margins: 2
|
||||
anchors.fill: parent
|
||||
palette {
|
||||
button: value
|
||||
}
|
||||
background: Rectangle {
|
||||
color: value
|
||||
}
|
||||
onClicked: {
|
||||
colorDialog.open()
|
||||
}
|
||||
}
|
||||
ColorDialog {
|
||||
id: colorDialog
|
||||
selectedColor: value
|
||||
onAccepted: {
|
||||
control.setValue(selectedColor)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property var value
|
||||
implicitHeight: lineEditor.implicitHeight + 2
|
||||
signal asValueChanged(text:var)
|
||||
function setValue(newText:var){
|
||||
if(newText !== value){
|
||||
value = newText
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
border.color: ColorPalette.theme.textBoxBackground
|
||||
color: ColorPalette.theme.textBoxBackground
|
||||
border.width: 1
|
||||
clip: true
|
||||
TextInput{
|
||||
id: lineEditor
|
||||
enabled: true
|
||||
clip: true
|
||||
padding : 3
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 2
|
||||
anchors.rightMargin: 2
|
||||
text: control.value
|
||||
color: ColorPalette.theme.textPrimary
|
||||
wrapMode: TextInput.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
onEditingFinished:{
|
||||
setValue(lineEditor.text)
|
||||
}
|
||||
}
|
||||
MouseArea{
|
||||
id: hoverArea
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
anchors.fill: parent
|
||||
onEntered:{
|
||||
exitAnimation.stop()
|
||||
enterAnimation.start()
|
||||
hoverArea.cursorShape = Qt.IBeamCursor
|
||||
}
|
||||
onExited:{
|
||||
enterAnimation.stop()
|
||||
exitAnimation.start()
|
||||
hoverArea.cursorShape = Qt.ArrowCursor
|
||||
}
|
||||
onPressed: (mouse)=> mouse.accepted = false
|
||||
onReleased:(mouse)=> mouse.accepted = false
|
||||
onClicked:(mouse)=> mouse.accepted = false
|
||||
onDoubleClicked:(mouse)=> mouse.accepted = false
|
||||
}
|
||||
ColorAnimation on border.color{
|
||||
id: enterAnimation
|
||||
to: ColorPalette.theme.boxHover
|
||||
duration: 100
|
||||
running: false
|
||||
}
|
||||
ColorAnimation on border.color{
|
||||
id: exitAnimation
|
||||
to: ColorPalette.theme.textBoxBackground
|
||||
duration: 100
|
||||
running: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property var value
|
||||
implicitHeight: lineEditor.implicitHeight + 2
|
||||
signal asValueChanged(text:var)
|
||||
function setValue(newText:var){
|
||||
if(newText !== value){
|
||||
value = newText
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
border.color: ColorPalette.theme.textBoxBackground
|
||||
color: ColorPalette.theme.textBoxBackground
|
||||
border.width: 1
|
||||
clip: true
|
||||
TextArea{
|
||||
id: lineEditor
|
||||
enabled: true
|
||||
clip: true
|
||||
padding: 3
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 2
|
||||
anchors.rightMargin: 2
|
||||
text: control.value
|
||||
color: ColorPalette.theme.textPrimary
|
||||
wrapMode: TextInput.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
onEditingFinished:{
|
||||
setValue(lineEditor.text)
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
if (event.modifiers & Qt.ShiftModifier) {
|
||||
// Shift+Enter:保留换行,不处理事件
|
||||
} else {
|
||||
// 单独按Enter:触发编辑完成,不换行
|
||||
event.accepted = true;
|
||||
lineEditor.editingFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea{
|
||||
id: hoverArea
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
anchors.fill: parent
|
||||
onEntered:{
|
||||
exitAnimation.stop()
|
||||
enterAnimation.start()
|
||||
hoverArea.cursorShape = Qt.IBeamCursor
|
||||
}
|
||||
onExited:{
|
||||
enterAnimation.stop()
|
||||
exitAnimation.start()
|
||||
hoverArea.cursorShape = Qt.ArrowCursor
|
||||
}
|
||||
onPressed: (mouse)=> mouse.accepted = false
|
||||
onReleased:(mouse)=> mouse.accepted = false
|
||||
onClicked:(mouse)=> mouse.accepted = false
|
||||
onDoubleClicked:(mouse)=> mouse.accepted = false
|
||||
}
|
||||
ColorAnimation on border.color{
|
||||
id: enterAnimation
|
||||
to: ColorPalette.theme.boxHover
|
||||
duration: 100
|
||||
running: false
|
||||
}
|
||||
ColorAnimation on border.color{
|
||||
id: exitAnimation
|
||||
to: ColorPalette.theme.textBoxBackground
|
||||
duration: 100
|
||||
running: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette
|
||||
|
||||
Item{
|
||||
id: control
|
||||
implicitHeight: 25
|
||||
implicitWidth: 100
|
||||
property bool isLimited: false
|
||||
property bool isHovered: false
|
||||
property var min:0
|
||||
property var max:100
|
||||
property var number: 0
|
||||
property real step : 1
|
||||
property int precision: 3
|
||||
property bool isMousePressed: false
|
||||
|
||||
signal valueChanged(number:var)
|
||||
function setNumber(value:var){
|
||||
if(value !== number && !isNaN(value)){
|
||||
number = value
|
||||
if(min < max){
|
||||
if(number>max){
|
||||
number = max
|
||||
}
|
||||
if(number<min){
|
||||
number = min
|
||||
}
|
||||
}
|
||||
valueChanged(number)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
border.color: ColorPalette.theme.textBoxBackground
|
||||
border.width: 1
|
||||
color: ColorPalette.theme.textBoxBackground
|
||||
clip: true
|
||||
|
||||
Rectangle{
|
||||
visible: isHovered && isLimited
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: parent.width * (max - number) /(max - min)
|
||||
color: ColorPalette.theme.boxHover
|
||||
}
|
||||
TextInput{
|
||||
id: input
|
||||
enabled: false
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 5
|
||||
anchors.rightMargin: 5
|
||||
text: helper.numberToString(number,precision)
|
||||
color: ColorPalette.theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
validator: DoubleValidator{}
|
||||
onEditingFinished:{
|
||||
enabled = false
|
||||
dragArea.visible = true
|
||||
dragArea.cursorShape = Qt.SplitHCursor
|
||||
var newVar = parseFloat(input.text)
|
||||
if(isNaN(newVar)){
|
||||
text = helper.numberToString(number,precision)
|
||||
}
|
||||
else{
|
||||
control.setNumber(parseFloat(input.text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea{
|
||||
id: dragArea
|
||||
property real lastPressX : -1
|
||||
property real lastPressY : -1
|
||||
anchors.fill: parent
|
||||
hoverEnabled : true
|
||||
cursorShape: Qt.SplitHCursor
|
||||
onDoubleClicked: {
|
||||
cursorShape = Qt.ArrowCursor
|
||||
input.enabled = true
|
||||
dragArea.visible = false
|
||||
input.forceActiveFocus()
|
||||
input.selectAll()
|
||||
}
|
||||
onClicked: {
|
||||
input.forceActiveFocus()
|
||||
}
|
||||
onEntered:{
|
||||
isHovered = true
|
||||
exitAnimation.stop()
|
||||
enterAnimation.start()
|
||||
}
|
||||
onExited:{
|
||||
isHovered = false
|
||||
if(!control.isMousePressed){
|
||||
enterAnimation.stop()
|
||||
exitAnimation.start()
|
||||
}
|
||||
}
|
||||
onPressed:
|
||||
(mouse)=>{
|
||||
if(mouse.button === Qt.LeftButton){
|
||||
control.isMousePressed = true
|
||||
lastPressX = mouse.x
|
||||
lastPressY = mouse.y
|
||||
cursorShape = Qt.BlankCursor
|
||||
}
|
||||
}
|
||||
onReleased:
|
||||
(mouse)=>{
|
||||
control.isMousePressed = false
|
||||
lastPressX = -1
|
||||
lastPressY = -1
|
||||
cursorShape = Qt.SplitHCursor
|
||||
if(!isHovered){
|
||||
enterAnimation.stop()
|
||||
exitAnimation.start()
|
||||
}
|
||||
}
|
||||
onPositionChanged:
|
||||
(mouse)=>{
|
||||
if(!input.enabled && mouse.buttons&Qt.LeftButton){
|
||||
if(!isLimited){
|
||||
var offset = mouse.x - lastPressX
|
||||
setNumber(number + offset * step)
|
||||
var global = dragArea.mapToGlobal(lastPressX, lastPressY)
|
||||
var local = dragArea.mapFromGlobal(global.x,global.y)
|
||||
helper.setCursorPos(global.x,global.y)
|
||||
}
|
||||
else{
|
||||
var xPercent = Math.max(0, Math.min(1, mouse.x / dragArea.width))
|
||||
var range = max - min
|
||||
var newValue = min + xPercent * range
|
||||
control.setNumber(newValue)
|
||||
const validMouseX = Math.max (0, Math.min (dragArea.width, mouse.x));
|
||||
const validMouseY = Math.max (0, Math.min (dragArea.height, mouse.y));
|
||||
if (mouse.x !== validMouseX || mouse.y !== validMouseY) {
|
||||
const validGlobalPos = dragArea.mapToGlobal (validMouseX, validMouseY);
|
||||
helper.setCursorPos (validGlobalPos.x, validGlobalPos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ColorAnimation on border.color{
|
||||
id: enterAnimation
|
||||
to: ColorPalette.theme.boxHover
|
||||
duration: 100
|
||||
running: false
|
||||
}
|
||||
ColorAnimation on border.color{
|
||||
id: exitAnimation
|
||||
to: ColorPalette.theme.textBoxBackground
|
||||
duration: 100
|
||||
running: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import ColorPalette
|
||||
|
||||
Item{
|
||||
id: editor
|
||||
property var value
|
||||
property var model
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(newValue !== value){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
id: control
|
||||
anchors.margins: 2
|
||||
anchors.fill: parent
|
||||
model: editor.model
|
||||
onCurrentTextChanged: {
|
||||
setValue(currentText)
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: itemDelegate
|
||||
width: control.width
|
||||
height: 25
|
||||
padding: 5
|
||||
background: Rectangle {
|
||||
color: itemDelegate.highlighted ? ColorPalette.theme.comboBoxItemBackgroundHover
|
||||
: itemDelegate.hovered ? ColorPalette.theme.comboBoxItemBackgroundHover
|
||||
: ColorPalette.theme.comboBoxItemBackground
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 100 }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: modelData
|
||||
color: ColorPalette.theme.textPrimary
|
||||
font: control.font
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter // 文字垂直居中
|
||||
horizontalAlignment: Text.AlignLeft // 文字左对齐
|
||||
padding: 0
|
||||
}
|
||||
highlighted: control.highlightedIndex === index
|
||||
required property int index
|
||||
required property var modelData
|
||||
}
|
||||
|
||||
indicator: Image {
|
||||
id: indicator
|
||||
x: control.width - width/2 - control.rightPadding
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
width: 13
|
||||
height: 13
|
||||
mipmap: true
|
||||
source: "qrc:/resources/Icon/expand.png"
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: ColorPalette.theme.rowIndicator
|
||||
opacity: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 3
|
||||
rightPadding: control.indicator.width + control.spacing
|
||||
|
||||
text: control.displayText
|
||||
font: control.font
|
||||
color: ColorPalette.theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: ColorPalette.theme.comboBoxBackground
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: control.height
|
||||
width: control.width
|
||||
implicitHeight: contentItem.implicitHeight + 5
|
||||
padding : 2
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: control.popup.visible ? control.delegateModel : null
|
||||
currentIndex: control.highlightedIndex
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: ColorPalette.theme.comboBoxBackground
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property vector2d value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(value !== newValue){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
NumberBox{
|
||||
id: xBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.x
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector2d(number, control.value.y))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: yBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.y
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector2d(control.value.x, number))
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property vector3d value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(value !== newValue){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
NumberBox{
|
||||
id: xBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.x
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector3d(number, control.value.y, control.value.z))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: yBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.y
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector3d(control.value.x, number, control.value.z))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: zBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.z
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector3d(control.value.x, control.value.y, number))
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
|
||||
Item{
|
||||
id: control
|
||||
property vector4d value
|
||||
implicitHeight: 25
|
||||
signal asValueChanged(value:var)
|
||||
function setValue(newValue:var){
|
||||
if(value !== newValue){
|
||||
value = newValue
|
||||
asValueChanged(value)
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
NumberBox{
|
||||
id: xBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.x
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(number, control.value.y, control.value.z, control.value.w))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: yBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.y
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(control.value.x, number, control.value.z, control.value.w))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: zBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.z
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(control.value.x, control.value.y, number, control.value.w))
|
||||
}
|
||||
}
|
||||
}
|
||||
NumberBox{
|
||||
id: wBox
|
||||
width: parent.width/4
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
number: value.w
|
||||
onNumberChanged: {
|
||||
if (control.value) {
|
||||
control.setValue(Qt.vector4d(control.value.x, control.value.y, control.value.z, number))
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 122 KiB |
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef IPropertyTypeCustomization_h__
|
||||
#define IPropertyTypeCustomization_h__
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include "QDetailsViewAPI.h"
|
||||
|
||||
class QPropertyHandle;
|
||||
class QQuickDetailsViewRowBuilder;
|
||||
class QQuickDetailsViewLayoutBuilder;
|
||||
|
||||
class QDETAILS_VIEW_API IPropertyTypeCustomization :public QEnableSharedFromThis<IPropertyTypeCustomization>
|
||||
{
|
||||
public:
|
||||
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
|
||||
};
|
||||
|
||||
#endif // IPropertyTypeCustomization_h__
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef IPropertyHandleImpl_h__
|
||||
#define IPropertyHandleImpl_h__
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QQuickItem>
|
||||
#include <QVariant>
|
||||
#include <QObject>
|
||||
#include "QDetailsViewAPI.h"
|
||||
|
||||
class QPropertyHandle;
|
||||
|
||||
class IPropertyHandleImpl{
|
||||
friend class QPropertyHandle;
|
||||
public:
|
||||
enum Type {
|
||||
Null,
|
||||
RawType,
|
||||
Associative,
|
||||
Sequential,
|
||||
Enum,
|
||||
Object,
|
||||
};
|
||||
protected:
|
||||
IPropertyHandleImpl(QPropertyHandle* inHandle);
|
||||
virtual QQuickItem* createNameEditor(QQuickItem* inParent);
|
||||
virtual QQuickItem* createValueEditor(QQuickItem* inParent)= 0;
|
||||
virtual Type type() { return Type::Null; };
|
||||
|
||||
protected:
|
||||
QPropertyHandle* mHandle;
|
||||
};
|
||||
|
||||
#endif // IPropertyHandleImpl_h__
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef QAssociativePropertyHandle_h__
|
||||
#define QAssociativePropertyHandle_h__
|
||||
|
||||
#include "QMetaContainer"
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class QDETAILS_VIEW_API QPropertyHandleImpl_Associative: public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_Associative(QPropertyHandle* inHandle);
|
||||
|
||||
const QMetaAssociation& metaAssociation() const;
|
||||
|
||||
void appendItem(QString inKey, QVariant inValue);
|
||||
bool renameItem(QString inSrc, QString inDst);
|
||||
void removeItem(QString inKey);
|
||||
|
||||
protected:
|
||||
Type type() override { return Type::Associative; };
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent)override;
|
||||
|
||||
private:
|
||||
QMetaAssociation mMetaAssociation;
|
||||
};
|
||||
|
||||
|
||||
#endif // QAssociativePropertyHandle_h__
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef QPropertyHandleImpl_Enum_h__
|
||||
#define QPropertyHandleImpl_Enum_h__
|
||||
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class QDETAILS_VIEW_API QPropertyHandleImpl_Enum: public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_Enum(QPropertyHandle* inHandle);
|
||||
|
||||
protected:
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent) override;
|
||||
Type type() override { return Type::Enum; };
|
||||
|
||||
private:
|
||||
QHash<QString, int> mNameToValueMap;
|
||||
QList<QString> mKeys;
|
||||
};
|
||||
|
||||
|
||||
#endif // QPropertyHandleImpl_Enum_h__
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef QPropertyHandleImpl_Object_h__
|
||||
#define QPropertyHandleImpl_Object_h__
|
||||
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class QDETAILS_VIEW_API QPropertyHandleImpl_Object : public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_Object(QPropertyHandle* inHandle);
|
||||
QObject* getObject();
|
||||
void* getGadget();
|
||||
bool isGadget() const;
|
||||
QObject* getOwnerObject();
|
||||
const QMetaObject* getMetaObject() const;
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent)override;
|
||||
Type type() override { return Type::Object; };
|
||||
void refreshObjectPtr();
|
||||
QVariant& getObjectHolder();
|
||||
private:
|
||||
QVariant mObjectHolder;
|
||||
void* mObjectPtr = nullptr;
|
||||
QObject* mOwnerObject = nullptr;
|
||||
const QMetaObject* mMetaObject = nullptr;
|
||||
bool bIsSharedPointer = false;
|
||||
bool bIsPointer = false;
|
||||
};
|
||||
|
||||
#endif // QPropertyHandleImpl_Object_h__
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef QPropertyHandleImpl_RawType_h__
|
||||
#define QPropertyHandleImpl_RawType_h__
|
||||
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class QDETAILS_VIEW_API QPropertyHandleImpl_RawType : public IPropertyHandleImpl {
|
||||
public:
|
||||
QPropertyHandleImpl_RawType(QPropertyHandle* inHandle);
|
||||
protected:
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent) override;
|
||||
Type type() override { return Type::RawType; };
|
||||
};
|
||||
|
||||
#endif // QPropertyHandleImpl_RawType_h__
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef QSequentialPropertyHandle_h__
|
||||
#define QSequentialPropertyHandle_h__
|
||||
|
||||
#include "QMetaContainer"
|
||||
#include "IPropertyHandleImpl.h"
|
||||
|
||||
class QDETAILS_VIEW_API QPropertyHandleImpl_Sequential: public IPropertyHandleImpl
|
||||
{
|
||||
public:
|
||||
QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle);
|
||||
|
||||
const QMetaSequence& metaSequence() const;
|
||||
int itemCount();
|
||||
void appendItem(QVariant InVar);
|
||||
void moveItem(int InSrcIndex, int InDstIndex);
|
||||
void removeItem(int InIndex);
|
||||
|
||||
protected:
|
||||
QQuickItem* createValueEditor(QQuickItem* inParent)override;
|
||||
Type type() override { return Type::Sequential; };
|
||||
|
||||
private:
|
||||
QMetaSequence mMetaSequence;
|
||||
};
|
||||
|
||||
#endif // QSequentialPropertyHandle_h__
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef QDetailsView_h__
|
||||
#define QDetailsView_h__
|
||||
|
||||
#include <QWidget>
|
||||
#include <QQuickWidget>
|
||||
#include "QQuickDetailsView.h"
|
||||
|
||||
class QDETAILS_VIEW_API QDetailsView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QDetailsView(QWidget* parent = nullptr);
|
||||
QQuickDetailsView* getQuickDetailsView() const;
|
||||
void setObject(QObject* inObject);
|
||||
QObject* getObject() const;
|
||||
private:
|
||||
QQuickWidget* mQuickWidget;
|
||||
QQuickDetailsView* mQuickDetailsView;
|
||||
};
|
||||
|
||||
#endif // QDetailsView_h__
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef QDETAILS_VIEW_API_H
|
||||
#define QDETAILS_VIEW_API_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef 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 QDETAILS_VIEW_API QPropertyHandle : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QVariant Var READ getVar WRITE setVar NOTIFY asVarChanged)
|
||||
public:
|
||||
using Getter = std::function<QVariant()>;
|
||||
using Setter = std::function<void(QVariant)>;
|
||||
|
||||
enum PropertyType {
|
||||
Unknown,
|
||||
RawType,
|
||||
Enum,
|
||||
Sequential,
|
||||
Associative,
|
||||
Object
|
||||
};
|
||||
|
||||
static QPropertyHandle* Find(const QObject* inParent, const QString& inPropertyPath);
|
||||
static QPropertyHandle* FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
|
||||
static QPropertyHandle* FindOrCreate(QObject* inObject);
|
||||
static QPropertyHandle* Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
|
||||
static void Cleanup(QObject* inParent);
|
||||
|
||||
QMetaType getType();
|
||||
PropertyType getPropertyType() const;
|
||||
QString getName();
|
||||
QString getPropertyPath();
|
||||
QString createSubPath(const QString& inSubName);
|
||||
|
||||
void invalidateStructure();
|
||||
|
||||
Q_INVOKABLE QVariant getVar();
|
||||
Q_INVOKABLE void setVar(QVariant var);
|
||||
|
||||
bool hasMetaData(const QString& inName) const;
|
||||
QVariant getMetaData(const QString& inName) const;
|
||||
const QVariantHash& getMetaDataMap() const;
|
||||
|
||||
QPropertyHandle* findChild(QString inPropertyName);
|
||||
QPropertyHandle* findOrCreateChild(QMetaType inType, QString inPropertyName, QPropertyHandle::Getter inGetter, QPropertyHandle::Setter inSetter);
|
||||
|
||||
QQuickItem* setupNameEditor(QQuickItem* inParent);
|
||||
QQuickItem* steupValueEditor(QQuickItem* inParent);
|
||||
|
||||
IPropertyHandleImpl::Type type();
|
||||
QPropertyHandleImpl_Enum* asEnum();
|
||||
QPropertyHandleImpl_Object* asObject();
|
||||
QPropertyHandleImpl_Associative* asAssociative();
|
||||
QPropertyHandleImpl_Sequential* asSequential();
|
||||
|
||||
static PropertyType parserType(QMetaType inType);
|
||||
static QVariant createNewVariant(QMetaType inOutputType);
|
||||
Q_SIGNALS:
|
||||
void asVarChanged(QVariant);
|
||||
void asStructureChanged();
|
||||
void asRequestRollback(QVariant);
|
||||
protected:
|
||||
QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter);
|
||||
void resloveMetaData();
|
||||
private:
|
||||
QMetaType mType;
|
||||
PropertyType mPropertyType;
|
||||
QString mPropertyPath;
|
||||
Getter mGetter;
|
||||
Setter mSetter;
|
||||
QVariant mInitialValue;
|
||||
QVariantHash mMetaData;
|
||||
QSharedPointer<IPropertyHandleImpl> mImpl;
|
||||
};
|
||||
|
||||
struct QDETAILS_VIEW_API ExternalRefCountWithMetaType : public QtSharedPointer::ExternalRefCountData {
|
||||
typedef ExternalRefCountData Parent;
|
||||
QMetaType mMetaType;
|
||||
void* mData;
|
||||
|
||||
static void deleter(ExternalRefCountData* self) {
|
||||
ExternalRefCountWithMetaType* that =
|
||||
static_cast<ExternalRefCountWithMetaType*>(self);
|
||||
that->mMetaType.destroy(that->mData);
|
||||
Q_UNUSED(that); // MSVC warns if T has a trivial destructor
|
||||
}
|
||||
|
||||
static inline ExternalRefCountData* create(QMetaType inMetaType, void* inPtr)
|
||||
{
|
||||
ExternalRefCountWithMetaType* d = static_cast<ExternalRefCountWithMetaType*>(::operator new(sizeof(ExternalRefCountWithMetaType)));
|
||||
|
||||
// initialize the d-pointer sub-object
|
||||
// leave d->data uninitialized
|
||||
new (d) Parent(ExternalRefCountWithMetaType::deleter); // can't throw
|
||||
d->mData = inPtr;
|
||||
d->mMetaType = inMetaType;
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // QPropertyHandle_h__
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef QQuickDetailsView_h__
|
||||
#define QQuickDetailsView_h__
|
||||
|
||||
#include "QQuickTreeViewEx.h"
|
||||
|
||||
class QQuickDetailsViewPrivate;
|
||||
|
||||
class QDETAILS_VIEW_API QQuickDetailsView: public QQuickTreeViewEx {
|
||||
Q_OBJECT
|
||||
QML_NAMED_ELEMENT(DetailsView)
|
||||
Q_DISABLE_COPY(QQuickDetailsView)
|
||||
Q_DECLARE_PRIVATE(QQuickDetailsView)
|
||||
Q_PROPERTY(qreal SpliterPencent READ getSpliterPencent WRITE setSpliterPencent NOTIFY asSpliterPencentChanged FINAL)
|
||||
Q_PROPERTY(QObject* Object READ getObject WRITE setObject NOTIFY asObjectChanged FINAL)
|
||||
public:
|
||||
QQuickDetailsView(QQuickItem* parent = nullptr);
|
||||
qreal getSpliterPencent() const;
|
||||
void setSpliterPencent(qreal val);
|
||||
Q_INVOKABLE void setObject(QObject* inObject);
|
||||
Q_INVOKABLE QObject* getObject() const;
|
||||
Q_SIGNALS:
|
||||
void asSpliterPencentChanged(qreal);
|
||||
void asObjectChanged(QObject*);
|
||||
protected:
|
||||
void componentComplete() override;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsView_h__
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef QQuickDetailsViewLayoutBuilder_h__
|
||||
#define QQuickDetailsViewLayoutBuilder_h__
|
||||
|
||||
#include "QQuickDetailsViewRow.h"
|
||||
|
||||
class QDETAILS_VIEW_API QQuickDetailsViewRowBuilder {
|
||||
public:
|
||||
QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem);
|
||||
QPair<QQuickItem*, QQuickItem*> makeNameValueSlot();
|
||||
|
||||
IDetailsViewRow* row() const;
|
||||
QQuickItem* rootItem() const;
|
||||
|
||||
void makePropertyRow(QPropertyHandle* inHandle);
|
||||
QQuickItem* setupItem(QQuickItem* inParent, QString inQmlCode);
|
||||
void setupLabel(QQuickItem* inParent, QString inText);
|
||||
void setHeightProxy(QQuickItem* inProxyItem);
|
||||
private:
|
||||
IDetailsViewRow* mRow = nullptr;
|
||||
QQuickItem* mRootItem = nullptr;
|
||||
};
|
||||
|
||||
class QDETAILS_VIEW_API QQuickDetailsViewLayoutBuilder {
|
||||
public:
|
||||
QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRootRow);
|
||||
|
||||
IDetailsViewRow* row() const;
|
||||
|
||||
void addCustomRow(std::function<void(QQuickDetailsViewRowBuilder*)> inCustomRowCreator, QString inOverrideName = "");
|
||||
void addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName = "");
|
||||
void addObject(QObject* inObject);
|
||||
private:
|
||||
IDetailsViewRow* mRootRow = nullptr;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewLayoutBuilder_h__
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef QQuickDetailsViewManager_h__
|
||||
#define QQuickDetailsViewManager_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <functional>
|
||||
#include <QMetaType>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "QDetailsViewAPI.h"
|
||||
|
||||
class QPropertyHandle;
|
||||
|
||||
class QDETAILS_VIEW_API QQuickDetailsViewManager : public QObject{
|
||||
public:
|
||||
using PropertyTypeCustomizationCreator = std::function<QSharedPointer<IPropertyTypeCustomization>()>;
|
||||
using TypeEditorCreator = std::function<QQuickItem* (QPropertyHandle*, QQuickItem*)>;
|
||||
|
||||
static QQuickDetailsViewManager* Get();
|
||||
|
||||
void initialize();
|
||||
bool isInitialized() const;
|
||||
|
||||
template<typename MetaType, typename IPropertyTypeCustomizationType>
|
||||
void registerPropertyTypeCustomization() {
|
||||
QMetaType metaType = QMetaType::fromType<MetaType>();
|
||||
if (metaType.metaObject()) {
|
||||
mClassCustomizationMap.insert(metaType.metaObject(), []() {
|
||||
return QSharedPointer<IPropertyTypeCustomizationType>::create();
|
||||
});
|
||||
}
|
||||
else {
|
||||
mMetaTypeCustomizationMap.insert(metaType, []() {
|
||||
return QSharedPointer<IPropertyTypeCustomizationType>::create();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
void unregisterPropertyTypeCustomization(const QMetaType& inMetaType);
|
||||
|
||||
void registerTypeEditor(const QMetaType& inMetaType, TypeEditorCreator Creator);
|
||||
void unregisterTypeEditor(const QMetaType& inMetaType);
|
||||
|
||||
QQuickItem* createValueEditor(QPropertyHandle* inHandle, QQuickItem* parent);
|
||||
QSharedPointer<IPropertyTypeCustomization> getCustomPropertyType(QPropertyHandle* inHandle);
|
||||
protected:
|
||||
QQuickDetailsViewManager();
|
||||
void RegisterBasicTypeEditor();
|
||||
private:
|
||||
bool mInitialized = false;
|
||||
|
||||
QHash<const QMetaObject*, PropertyTypeCustomizationCreator> mClassCustomizationMap;
|
||||
QHash<QMetaType, PropertyTypeCustomizationCreator> mMetaTypeCustomizationMap;
|
||||
QHash<QMetaType, TypeEditorCreator> mTypeEditorCreatorMap;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewManager_h__
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef QQuickDetailsViewModel_h__
|
||||
#define QQuickDetailsViewModel_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QMetaProperty>
|
||||
#include <QQuickItem>
|
||||
#include "QDetailsViewAPI.h"
|
||||
|
||||
class IDetailsViewRow;
|
||||
class QDetailsViewRow_Property;
|
||||
|
||||
class QDETAILS_VIEW_API QQuickDetailsViewModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
enum Roles {
|
||||
name = 0,
|
||||
};
|
||||
friend class IDetailsViewRow;
|
||||
public:
|
||||
QQuickDetailsViewModel(QObject* parent = 0);
|
||||
~QQuickDetailsViewModel() {}
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex& index) const override;
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void setObject(QObject* inObject);
|
||||
QObject* getObject() const;
|
||||
|
||||
QModelIndex indexForRow(IDetailsViewRow* row) const;
|
||||
void updateRowIndex(IDetailsViewRow* row);
|
||||
|
||||
private:
|
||||
QSharedPointer<QDetailsViewRow_Property> mRoot;
|
||||
QObject* mObject;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewModel_h__
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef QQuickDetailsViewRow_h__
|
||||
#define QQuickDetailsViewRow_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QMetaProperty>
|
||||
#include <QQuickItem>
|
||||
#include "QPropertyHandle.h"
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "QQuickDetailsViewModel.h"
|
||||
#include "QDetailsViewAPI.h"
|
||||
|
||||
class QDETAILS_VIEW_API IDetailsViewRow {
|
||||
friend class QQuickDetailsViewModel;
|
||||
public:
|
||||
~IDetailsViewRow() {};
|
||||
|
||||
void setName(QString inName);
|
||||
virtual QString name();
|
||||
|
||||
virtual void setupItem(QQuickItem* inParent){}
|
||||
virtual void attachChildren() {}
|
||||
virtual void addChild(QSharedPointer<IDetailsViewRow> inChild);
|
||||
|
||||
void clear();
|
||||
QQuickDetailsViewModel* model();
|
||||
QModelIndex modelIndex() const { return mModelIndex; }
|
||||
void setModelIndex(const QModelIndex& index) { mModelIndex = index; }
|
||||
int rowNumber() const {
|
||||
if (!mParent) return -1;
|
||||
return mParent->mChildren.indexOf(const_cast<IDetailsViewRow*>(this));
|
||||
}
|
||||
void invalidateChildren();
|
||||
protected:
|
||||
QString mName;
|
||||
QQuickDetailsViewModel* mModel = nullptr;
|
||||
QModelIndex mModelIndex;
|
||||
IDetailsViewRow* mParent = nullptr;
|
||||
QList<QSharedPointer<IDetailsViewRow>> mChildren;
|
||||
};
|
||||
|
||||
class QDETAILS_VIEW_API QDetailsViewRow_Property : public IDetailsViewRow {
|
||||
public:
|
||||
QDetailsViewRow_Property(QPropertyHandle* inHandle);
|
||||
~QDetailsViewRow_Property();
|
||||
void setHandle(QPropertyHandle* inHandle);
|
||||
void setupItem(QQuickItem* inParent) override;
|
||||
void attachChildren() override;
|
||||
protected:
|
||||
QPropertyHandle* mHandle = nullptr;
|
||||
QMetaObject::Connection mStructureChangedConnection;
|
||||
QSharedPointer<IPropertyTypeCustomization> mPropertyTypeCustomization;
|
||||
};
|
||||
|
||||
class QDETAILS_VIEW_API QDetailsViewRow_Custom : public IDetailsViewRow {
|
||||
public:
|
||||
QDetailsViewRow_Custom(std::function<void(QQuickDetailsViewRowBuilder*)> inRowCreator);
|
||||
protected:
|
||||
QString name() override { return "Custom"; }
|
||||
void setupItem(QQuickItem* inParent) override;
|
||||
private:
|
||||
std::function<void(QQuickDetailsViewRowBuilder*)> mRowCreator;
|
||||
};
|
||||
|
||||
#endif // QQuickDetailsViewRow_h__
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef QQuickFunctionLibrary_h__
|
||||
#define QQuickFunctionLibrary_h__
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QQuickItem>
|
||||
#include "QDetailsViewAPI.h"
|
||||
|
||||
class QQuickLambdaHolder;
|
||||
class QQuickLambdaHolder_OneParam;
|
||||
|
||||
class QDETAILS_VIEW_API QQuickFunctionLibrary : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QQuickFunctionLibrary* Get();
|
||||
|
||||
Q_INVOKABLE QString numberToString(QVariant var,int precision);
|
||||
Q_INVOKABLE void setCursorPos(qreal x, qreal y);
|
||||
Q_INVOKABLE void setOverrideCursorShape(Qt::CursorShape shape);
|
||||
Q_INVOKABLE void restoreOverrideCursorShape();
|
||||
Q_INVOKABLE void setCursorPosTest(QQuickItem* item, qreal x, qreal y);
|
||||
|
||||
|
||||
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void()> callback);
|
||||
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant)> callback);
|
||||
static QMetaObject::Connection connect(QObject* sender, const char* signal, QObject* receiver, std::function<void(QVariant, QVariant)> callback);
|
||||
};
|
||||
|
||||
class QDETAILS_VIEW_API QQuickLambdaHolder : public QObject {
|
||||
Q_OBJECT
|
||||
std::function<void()> mCallback;
|
||||
public:
|
||||
QQuickLambdaHolder(std::function<void()> callback, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mCallback(std::move(callback)) {}
|
||||
|
||||
Q_SLOT void call() { mCallback(); }
|
||||
};
|
||||
|
||||
class QDETAILS_VIEW_API QQuickLambdaHolder_OneParam : public QObject {
|
||||
Q_OBJECT
|
||||
std::function<void(QVariant)> mCallback;
|
||||
public:
|
||||
QQuickLambdaHolder_OneParam(std::function<void(QVariant)> callback, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mCallback(std::move(callback)) {
|
||||
}
|
||||
|
||||
Q_SLOT void call(QVariant inParam0) { mCallback(inParam0); }
|
||||
};
|
||||
|
||||
class QDETAILS_VIEW_API QQuickLambdaHolder_TwoParams : public QObject {
|
||||
Q_OBJECT
|
||||
std::function<void(QVariant, QVariant)> mCallback;
|
||||
public:
|
||||
QQuickLambdaHolder_TwoParams(std::function<void(QVariant, QVariant)> callback, QObject* parent)
|
||||
: QObject(parent)
|
||||
, mCallback(std::move(callback)) {
|
||||
}
|
||||
|
||||
Q_SLOT void call(QVariant inParam0, QVariant inParam1) { mCallback(inParam0, inParam1); }
|
||||
};
|
||||
|
||||
#endif // QQuickFunctionLibrary_h__
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef QQuickTreeViewEx_h__
|
||||
#define QQuickTreeViewEx_h__
|
||||
|
||||
#include "QDetailsViewAPI.h"
|
||||
#include "private/qquicktableview_p.h"
|
||||
|
||||
class QQuickTreeViewExPrivate;
|
||||
|
||||
class QDETAILS_VIEW_API QQuickTreeViewEx: public QQuickTableView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QQuickTreeViewEx(QQuickItem* parent = nullptr);
|
||||
~QQuickTreeViewEx() override;
|
||||
|
||||
QModelIndex rootIndex() const;
|
||||
void setRootIndex(const QModelIndex& index);
|
||||
void resetRootIndex();
|
||||
|
||||
Q_INVOKABLE int depth(int row) const;
|
||||
|
||||
Q_INVOKABLE bool isExpanded(int row) const;
|
||||
Q_INVOKABLE void expand(int row);
|
||||
Q_INVOKABLE void collapse(int row);
|
||||
Q_INVOKABLE void toggleExpanded(int row);
|
||||
Q_INVOKABLE void invalidateLayout();
|
||||
|
||||
Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1);
|
||||
Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1);
|
||||
Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex& index);
|
||||
|
||||
Q_INVOKABLE QModelIndex modelIndex(const QPoint& cell) const override;
|
||||
Q_INVOKABLE QPoint cellAtIndex(const QModelIndex& index) const override;
|
||||
|
||||
#if QT_DEPRECATED_SINCE(6, 4)
|
||||
QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
|
||||
Q_REVISION(6, 4) Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void expanded(int row, int depth);
|
||||
void collapsed(int row, bool recursively);
|
||||
Q_REVISION(6, 6) void rootIndexChanged();
|
||||
protected:
|
||||
QQuickTreeViewEx(QQuickTreeViewExPrivate& dd, QQuickItem* parent);
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
private:
|
||||
Q_DISABLE_COPY(QQuickTreeViewEx)
|
||||
Q_DECLARE_PRIVATE(QQuickTreeViewEx)
|
||||
};
|
||||
|
||||
QML_DECLARE_TYPE(QQuickTreeViewEx)
|
||||
|
||||
#endif // QQuickTreeViewEx_h__
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#include "PropertyTypeCustomization_Associative.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "qsequentialiterable.h"
|
||||
#include "qassociativeiterable.h"
|
||||
|
||||
void PropertyTypeCustomization_Associative::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto associativelHandle = inPropertyHandle->asAssociative();
|
||||
QMetaAssociation metaAssociation = associativelHandle->metaAssociation();
|
||||
QVariant varMap = inPropertyHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
for (auto iter = iterable.begin(); iter != iterable.end(); ++iter) {
|
||||
QString key = iter.key().toString();
|
||||
inBuilder->addProperty(inPropertyHandle->findOrCreateChild(
|
||||
metaAssociation.mappedMetaType(),
|
||||
key,
|
||||
[inPropertyHandle, key]() {
|
||||
QVariant varMap = inPropertyHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
return iterable.value(key);
|
||||
},
|
||||
[inPropertyHandle, key, metaAssociation](QVariant var) {
|
||||
QVariant varMap = inPropertyHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
QtPrivate::QVariantTypeCoercer keyCoercer;
|
||||
QtPrivate::QVariantTypeCoercer mappedCoercer;
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
const void* dataPtr = mappedCoercer.coerce(var, metaAssociation.mappedMetaType());
|
||||
metaAssociation.setMappedAtKey(containterPtr, keyCoercer.coerce(key, metaAssociation.keyMetaType()), dataPtr);
|
||||
inPropertyHandle->setVar(varMap);
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_Associative_h__
|
||||
#define PropertyTypeCustomization_Associative_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_Associative : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_Associative_h__
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#include "PropertyTypeCustomization_Matrix4x4.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QMatrix4x4>
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
|
||||
void PropertyTypeCustomization_Matrix4x4::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
//inBuilder->addCustomRow([inPropertyHandle](QQuickDetailsViewRowBuilder* builder) {
|
||||
// auto editorSlot = builder->makeNameValueSlot();
|
||||
// builder->setupLabel(editorSlot.first, "0");
|
||||
// auto valueItem = builder->setupItem(editorSlot.second, R"(
|
||||
// import QtQuick;
|
||||
// import QtQuick.Controls;
|
||||
// import "qrc:/resources/Qml/ValueEditor"
|
||||
// Vec4Box{
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
// width: parent.width
|
||||
// }
|
||||
// )");
|
||||
// builder->setHeightProxy(valueItem);
|
||||
// QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
// valueItem->setProperty("value", mat.row(0));
|
||||
|
||||
// QQuickFunctionLibrary::connect(valueItem, SIGNAL(asValueChanged(QVariant)), inPropertyHandle, [inPropertyHandle](QVariant var) {
|
||||
// QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
// mat.setRow(0, var.value<QVector4D>());
|
||||
// inPropertyHandle->setVar(mat);
|
||||
// });
|
||||
// QQuickFunctionLibrary::connect(inPropertyHandle, SIGNAL(asRequestRollback(QVariant)), valueItem, [inPropertyHandle, valueItem](QVariant var) {
|
||||
// QMatrix4x4 mat = var.value<QMatrix4x4>();
|
||||
// valueItem->setProperty("value", mat.row(0));
|
||||
// });
|
||||
//});
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 0"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(0);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(0, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 1"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(1);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(1, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 2"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(2);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(2, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
|
||||
inBuilder->addProperty(QPropertyHandle::FindOrCreate(
|
||||
inPropertyHandle->parent(),
|
||||
QMetaType::fromType<QVector4D>(),
|
||||
inPropertyHandle->createSubPath("Row 3"),
|
||||
[inPropertyHandle]() {
|
||||
return inPropertyHandle->getVar().value<QMatrix4x4>().row(3);
|
||||
},
|
||||
[inPropertyHandle](QVariant var) {
|
||||
QMatrix4x4 mat = inPropertyHandle->getVar().value<QMatrix4x4>();
|
||||
mat.setRow(3, var.value<QVector4D>());
|
||||
inPropertyHandle->setVar(mat);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_Matrix4x4_h__
|
||||
#define PropertyTypeCustomization_Matrix4x4_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_Matrix4x4 : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_Matrix4x4_h__
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#include "PropertyTypeCustomization_ObjectDefault.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
void PropertyTypeCustomization_ObjectDefault::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto objectHandle = inPropertyHandle->asObject();
|
||||
const QMetaObject* metaObject = objectHandle->getMetaObject();
|
||||
if (objectHandle->isGadget()) {
|
||||
for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty prop = metaObject->property(i);
|
||||
QString propName = prop.name();
|
||||
inBuilder->addProperty(
|
||||
inPropertyHandle->findOrCreateChild(
|
||||
prop.metaType(),
|
||||
propName,
|
||||
[this, prop, objectHandle]() {
|
||||
return prop.readOnGadget(objectHandle->getGadget());
|
||||
},
|
||||
[this, prop, objectHandle, inPropertyHandle](QVariant var) {
|
||||
prop.writeOnGadget(objectHandle->getGadget(), var);
|
||||
inPropertyHandle->setVar(objectHandle->getObjectHolder());
|
||||
objectHandle->refreshObjectPtr();
|
||||
}
|
||||
),
|
||||
propName
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (objectHandle->getObject() != nullptr) {
|
||||
for (int i = objectHandle->isGadget() ? 0 : 1; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty prop = metaObject->property(i);
|
||||
QString propName = prop.name();
|
||||
inBuilder->addProperty(
|
||||
inPropertyHandle->findOrCreateChild(
|
||||
prop.metaType(),
|
||||
propName,
|
||||
[this, prop, objectHandle]() {
|
||||
return prop.read(objectHandle->getObject());
|
||||
},
|
||||
[this, prop, objectHandle, inPropertyHandle](QVariant var) {
|
||||
prop.write(objectHandle->getObject(), var);
|
||||
}
|
||||
),
|
||||
propName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_ObjectDefault_h__
|
||||
#define PropertyTypeCustomization_ObjectDefault_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_ObjectDefault : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_ObjectDefault_h__
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "PropertyTypeCustomization_Sequential.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "qsequentialiterable.h"
|
||||
#include "qassociativeiterable.h"
|
||||
|
||||
void PropertyTypeCustomization_Sequential::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
auto sequentialHandle = inPropertyHandle->asSequential();
|
||||
QVariant varList = inPropertyHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
for (int index = 0; index < iterable.size(); index++) {
|
||||
QString name = QString::number(index);
|
||||
inBuilder->addProperty(inPropertyHandle->findOrCreateChild(
|
||||
sequentialHandle->metaSequence().valueMetaType(),
|
||||
name,
|
||||
[inPropertyHandle, index]() {
|
||||
QVariant varList = inPropertyHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
if (index < 0 || index >= iterable.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
return iterable.at(index);
|
||||
},
|
||||
[inPropertyHandle, index](QVariant var) {
|
||||
QVariant varList = inPropertyHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
if (index >= 0 && index < iterable.size()) {
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
const void* dataPtr = coercer.coerce(var, iterable.valueMetaType());
|
||||
metaSequence.setValueAtIndex(containterPtr, index, dataPtr);
|
||||
inPropertyHandle->setVar(varList);
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PropertyTypeCustomization_Sequential_h__
|
||||
#define PropertyTypeCustomization_Sequential_h__
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
|
||||
class PropertyTypeCustomization_Sequential : public IPropertyTypeCustomization {
|
||||
protected:
|
||||
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
|
||||
};
|
||||
|
||||
#endif // PropertyTypeCustomization_Sequential_h__
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#include "IPropertyTypeCustomization.h"
|
||||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
|
||||
void IPropertyTypeCustomization::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
|
||||
{
|
||||
inBuilder->makePropertyRow(inPropertyHandle);
|
||||
}
|
||||
|
||||
void IPropertyTypeCustomization::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#include "PropertyHandleImpl/IPropertyHandleImpl.h"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
IPropertyHandleImpl::IPropertyHandleImpl(QPropertyHandle* inHandle):
|
||||
mHandle(inHandle)
|
||||
{
|
||||
}
|
||||
|
||||
QQuickItem* IPropertyHandleImpl::createNameEditor(QQuickItem* inParent)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(inParent);
|
||||
QQmlContext* context = qmlContext(inParent);
|
||||
QQmlComponent nameComp(engine);
|
||||
nameComp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette;
|
||||
Item{
|
||||
implicitHeight: 25
|
||||
width: parent.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
clip: true
|
||||
elide: Text.ElideRight
|
||||
text: model.name
|
||||
color: ColorPalette.theme.labelPrimary
|
||||
}
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(inParent);
|
||||
auto nameEditor = qobject_cast<QQuickItem*>(nameComp.createWithInitialProperties(initialProperties, context));
|
||||
nameEditor->setParentItem(inParent);
|
||||
return nameEditor;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
|
||||
#include <QAssociativeIterable>
|
||||
#include "QBoxLayout"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
QPropertyHandleImpl_Associative::QPropertyHandleImpl_Associative(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
QVariant varMap = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
mMetaAssociation = iterable.metaContainer();
|
||||
}
|
||||
|
||||
const QMetaAssociation& QPropertyHandleImpl_Associative::metaAssociation() const
|
||||
{
|
||||
return mMetaAssociation;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Associative::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool QPropertyHandleImpl_Associative::renameItem(QString inSrc, QString inDst) {
|
||||
bool canRename = false;
|
||||
QVariant varMap = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varMap.value<QAssociativeIterable>();
|
||||
if (iterable.containsKey(inSrc) && !iterable.containsKey(inDst)) {
|
||||
canRename = true;
|
||||
QVariant var = iterable.value(inSrc);
|
||||
QtPrivate::QVariantTypeCoercer keyCoercer;
|
||||
QtPrivate::QVariantTypeCoercer mappedCoercer;
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QMetaAssociation metaAssociation = iterable.metaContainer();
|
||||
metaAssociation.removeKey(containterPtr, keyCoercer.coerce(inSrc, QMetaType::fromType<QString>()));
|
||||
metaAssociation.setMappedAtKey(
|
||||
containterPtr,
|
||||
keyCoercer.coerce(inDst, QMetaType::fromType<QString>()),
|
||||
mappedCoercer.coerce(var, var.metaType())
|
||||
);
|
||||
//mHandle->setVar(varMap, QString("Rename: %1 -> %2").arg(inSrc).arg(inDst));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
return canRename;
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Associative::appendItem(QString inKey, QVariant inValue) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varList.value<QAssociativeIterable>();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
QVariant key(inKey);
|
||||
const void* keyDataPtr = coercer.coerce(key, key.metaType());
|
||||
const void* valueDataPtr = coercer.coerce(inValue, inValue.metaType());
|
||||
//metaAssociation.insertKey(containterPtr, keyDataPtr);
|
||||
mMetaAssociation.setMappedAtKey(containterPtr, keyDataPtr, valueDataPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Insert: %2").arg(mHandle->getPath()).arg(inKey));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Associative::removeItem(QString inKey) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QAssociativeIterable iterable = varList.value<QAssociativeIterable>();
|
||||
const QMetaAssociation metaAssociation = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
QVariant key(inKey);
|
||||
const void* keyDataPtr = coercer.coerce(key, key.metaType());
|
||||
metaAssociation.removeKey(containterPtr, keyDataPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(inKey));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
|
||||
#include <QComboBox>
|
||||
#include <QMetaEnum>
|
||||
#include <QWidget>
|
||||
#include <QBoxLayout>
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
|
||||
QPropertyHandleImpl_Enum::QPropertyHandleImpl_Enum(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
const QMetaObject* metaObj = mHandle->getType().metaObject();
|
||||
if (metaObj){
|
||||
const QMetaEnum& metaEnum = metaObj->enumerator(metaObj->indexOfEnumerator(QString(mHandle->getType().name()).split("::").last().toLocal8Bit()));
|
||||
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||
mNameToValueMap[metaEnum.key(i)] = metaEnum.value(i);
|
||||
mKeys << metaEnum.key(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Enum::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(inParent);
|
||||
QQmlContext* context = qmlContext(inParent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
TextComboBox{
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(inParent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(inParent);
|
||||
valueEditor->setProperty("value", mHandle->getVar());
|
||||
valueEditor->setProperty("model", mKeys);
|
||||
QObject::connect(valueEditor, SIGNAL(asValueChanged(QVariant)), mHandle, SLOT(setVar(QVariant)));
|
||||
QObject::connect(mHandle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
|
||||
#include <qsequentialiterable.h>
|
||||
#include <QRegularExpression>
|
||||
#include "QPropertyHandle.h"
|
||||
#include <QMetaProperty>
|
||||
|
||||
QPropertyHandleImpl_Object::QPropertyHandleImpl_Object(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
mObjectHolder = mHandle->getVar();
|
||||
QMetaType metaType = mHandle->getType();
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(metaType.name());
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
if (!matchTexts.isEmpty()) {
|
||||
QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
|
||||
mMetaObject = innerMetaType.metaObject();
|
||||
const void* ptr = *(const void**)mObjectHolder.data();
|
||||
bIsSharedPointer = true;
|
||||
bIsPointer = true;
|
||||
if (ptr) {
|
||||
mObjectHolder = QVariant(innerMetaType, mObjectHolder.data());
|
||||
}
|
||||
else {
|
||||
mObjectHolder = QVariant();
|
||||
}
|
||||
}
|
||||
else{
|
||||
bIsPointer = metaType.flags().testFlag(QMetaType::IsPointer);
|
||||
mMetaObject = metaType.metaObject();
|
||||
}
|
||||
mOwnerObject = mHandle->parent();
|
||||
refreshObjectPtr();
|
||||
}
|
||||
|
||||
QObject* QPropertyHandleImpl_Object::getObject()
|
||||
{
|
||||
if(mMetaObject->inherits(&QObject::staticMetaObject))
|
||||
return (QObject*)mObjectPtr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* QPropertyHandleImpl_Object::getGadget()
|
||||
{
|
||||
return mObjectPtr;
|
||||
}
|
||||
|
||||
bool QPropertyHandleImpl_Object::isGadget() const
|
||||
{
|
||||
if (mMetaObject->inherits(&QObject::staticMetaObject))
|
||||
return false;
|
||||
return mObjectPtr != nullptr;
|
||||
}
|
||||
|
||||
QObject* QPropertyHandleImpl_Object::getOwnerObject()
|
||||
{
|
||||
return mOwnerObject;
|
||||
}
|
||||
|
||||
const QMetaObject* QPropertyHandleImpl_Object::getMetaObject() const
|
||||
{
|
||||
return mMetaObject;
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Object::refreshObjectPtr() {
|
||||
mObjectHolder = mHandle->getVar();
|
||||
if (mObjectHolder.isValid()) {
|
||||
if (mMetaObject->inherits(&QObject::staticMetaObject)) {
|
||||
QObject* objectPtr = mObjectHolder.value<QObject*>();
|
||||
if (objectPtr) {
|
||||
mMetaObject = objectPtr->metaObject();
|
||||
}
|
||||
mObjectPtr = objectPtr;
|
||||
mOwnerObject = objectPtr;
|
||||
if (mOwnerObject) {
|
||||
//QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::moveToThread, mOwnerObject, mHandle->thread()));
|
||||
//QMetaObject::invokeMethod(mOwnerObject, std::bind(&QObject::installEventFilter, mOwnerObject, mHandle));
|
||||
//mOwnerObject->installEventFilter(mHandle);
|
||||
}
|
||||
}
|
||||
else {
|
||||
void* ptr = mObjectHolder.data();
|
||||
if (bIsPointer)
|
||||
ptr = *(void**)mObjectHolder.data();
|
||||
mObjectPtr = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant& QPropertyHandleImpl_Object::getObjectHolder()
|
||||
{
|
||||
return mObjectHolder;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Object::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#include "PropertyHandleImpl/IPropertyHandleImpl.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
QPropertyHandleImpl_RawType::QPropertyHandleImpl_RawType(QPropertyHandle* inHandle)
|
||||
: IPropertyHandleImpl(inHandle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_RawType::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return QQuickDetailsViewManager::Get()->createValueEditor(mHandle, inParent);
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
|
||||
#include <qsequentialiterable.h>
|
||||
#include "QBoxLayout"
|
||||
#include "QPropertyHandle.h"
|
||||
|
||||
|
||||
QPropertyHandleImpl_Sequential::QPropertyHandleImpl_Sequential(QPropertyHandle* inHandle)
|
||||
:IPropertyHandleImpl(inHandle) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
mMetaSequence = iterable.metaContainer();
|
||||
}
|
||||
|
||||
const QMetaSequence& QPropertyHandleImpl_Sequential::metaSequence() const
|
||||
{
|
||||
return mMetaSequence;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandleImpl_Sequential::createValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int QPropertyHandleImpl_Sequential::itemCount() {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
return iterable.size();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Sequential::appendItem( QVariant InVar) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
const void* dataPtr = coercer.coerce(InVar, InVar.metaType());
|
||||
metaSequence.addValue(containterPtr, dataPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Append: %2").arg(mHandle->getPath()).arg(metaSequence.size(containterPtr) - 1));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Sequential::moveItem(int InSrcIndex, int InDstIndex) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
QVariant srcVar = iterable.at(InSrcIndex);
|
||||
QVariant dstVar = iterable.at(InDstIndex);
|
||||
metaSequence.setValueAtIndex(containterPtr, InDstIndex, coercer.coerce(srcVar, srcVar.metaType()));
|
||||
metaSequence.setValueAtIndex(containterPtr, InSrcIndex, coercer.coerce(dstVar, dstVar.metaType()));
|
||||
//mHandle->setVar(varList, QString("%1 Move: %2->%3").arg(mHandle->getPath()).arg(InSrcIndex).arg(InDstIndex));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
||||
void QPropertyHandleImpl_Sequential::removeItem(int InIndex) {
|
||||
QVariant varList = mHandle->getVar();
|
||||
QSequentialIterable iterable = varList.value<QSequentialIterable>();
|
||||
const QMetaSequence metaSequence = iterable.metaContainer();
|
||||
void* containterPtr = const_cast<void*>(iterable.constIterable());
|
||||
QtPrivate::QVariantTypeCoercer coercer;
|
||||
for (int i = InIndex; i < iterable.size() - 1; i++) {
|
||||
QVariant nextVar = iterable.at(i + 1);
|
||||
metaSequence.setValueAtIndex(containterPtr, InIndex, coercer.coerce(nextVar, nextVar.metaType()));
|
||||
}
|
||||
metaSequence.removeValueAtEnd(containterPtr);
|
||||
//mHandle->setVar(varList, QString("%1 Remove: %2").arg(mHandle->getPath()).arg(InIndex));
|
||||
//Q_EMIT mHandle->asRequestRebuildRow();
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
#include "QDetailsView.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QUrl>
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
QDetailsView::QDetailsView(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, mQuickWidget(nullptr)
|
||||
, mQuickDetailsView(nullptr)
|
||||
{
|
||||
setMinimumSize(200, 200);
|
||||
|
||||
if (!QQuickDetailsViewManager::Get()->isInitialized()) {
|
||||
QQuickDetailsViewManager::Get()->initialize();
|
||||
}
|
||||
|
||||
mQuickWidget = new QQuickWidget(this);
|
||||
mQuickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
|
||||
const QString qmlContent = R"(
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.DetailsView
|
||||
|
||||
|
||||
DetailsView {
|
||||
id: detailsView
|
||||
anchors.fill: parent
|
||||
boundsBehavior: Flickable.OvershootBounds
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
parent: detailsView.parent
|
||||
width : 10
|
||||
anchors.top: detailsView.top
|
||||
anchors.right: detailsView.right
|
||||
anchors.bottom: detailsView.bottom
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
QQmlComponent component(mQuickWidget->engine());
|
||||
component.setData(qmlContent.toUtf8(), QUrl(""));
|
||||
QObject* rootObject = component.create();
|
||||
if (!component.errors().isEmpty()) {
|
||||
qDebug() << component.errorString();
|
||||
}
|
||||
if (rootObject) {
|
||||
mQuickWidget->setSource(QUrl());
|
||||
mQuickWidget->engine()->setObjectOwnership(rootObject, QQmlEngine::CppOwnership);
|
||||
|
||||
QQuickItem* rootItem = qobject_cast<QQuickItem*>(rootObject);
|
||||
if (rootItem) {
|
||||
mQuickWidget->setContent(QUrl(), &component, rootItem);
|
||||
mQuickDetailsView = qobject_cast<QQuickDetailsView*>(rootItem);
|
||||
}
|
||||
}
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(mQuickWidget);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
QQuickDetailsView* QDetailsView::getQuickDetailsView() const
|
||||
{
|
||||
return mQuickDetailsView;
|
||||
}
|
||||
|
||||
void QDetailsView::setObject(QObject* inObject)
|
||||
{
|
||||
QQuickItem* rootObject = mQuickWidget->rootObject();
|
||||
if (rootObject) {
|
||||
rootObject->setProperty("Object", QVariant::fromValue(inObject));
|
||||
}
|
||||
}
|
||||
|
||||
QObject* QDetailsView::getObject() const
|
||||
{
|
||||
QQuickItem* rootObject = mQuickWidget->rootObject();
|
||||
if (rootObject) {
|
||||
return rootObject->property("Object").value<QObject*>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
#include "QPropertyHandle.h"
|
||||
#include <QRegularExpression>
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Sequential.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Associative.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Enum.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_Object.h"
|
||||
#include "PropertyHandleImpl/QPropertyHandleImpl_RawType.h"
|
||||
|
||||
QPropertyHandle::QPropertyHandle(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
|
||||
: mType(inType)
|
||||
, mPropertyPath(inPropertyPath)
|
||||
, mGetter(inGetter)
|
||||
, mSetter(inSetter)
|
||||
{
|
||||
setParent(inParent);
|
||||
setObjectName(inPropertyPath.split(".").back());
|
||||
resloveMetaData();
|
||||
mInitialValue = inGetter();
|
||||
mPropertyType = parserType(inType);
|
||||
switch (mPropertyType)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case PropertyType::RawType:
|
||||
mImpl.reset(new QPropertyHandleImpl_RawType(this));
|
||||
break;
|
||||
case PropertyType::Enum:
|
||||
mImpl.reset(new QPropertyHandleImpl_Enum(this));
|
||||
break;
|
||||
case PropertyType::Sequential:
|
||||
mImpl.reset(new QPropertyHandleImpl_Sequential(this));
|
||||
break;
|
||||
case PropertyType::Associative:
|
||||
mImpl.reset(new QPropertyHandleImpl_Associative(this));
|
||||
break;
|
||||
case PropertyType::Object:
|
||||
mImpl.reset(new QPropertyHandleImpl_Object(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QPropertyHandle::resloveMetaData()
|
||||
{
|
||||
auto metaObj = parent()->metaObject();
|
||||
auto firstField = getPropertyPath().split(".").first();
|
||||
for (int i = 0; i < metaObj->classInfoCount(); i++) {
|
||||
auto metaClassInfo = metaObj->classInfo(i);
|
||||
if (metaClassInfo.name() == firstField) {
|
||||
QStringList fields = QString(metaClassInfo.value()).split(",", Qt::SplitBehaviorFlags::SkipEmptyParts);
|
||||
for (auto field : fields) {
|
||||
QStringList pair = field.split("=");
|
||||
QString key, value;
|
||||
if (pair.size() > 0) {
|
||||
key = pair.first().trimmed();
|
||||
}
|
||||
if (pair.size() > 1) {
|
||||
value = pair[1].trimmed();
|
||||
}
|
||||
mMetaData[key] = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::Find(const QObject* inParent, const QString& inPropertyPath)
|
||||
{
|
||||
for (QObject* child : inParent->children()) {
|
||||
QPropertyHandle* handler = qobject_cast<QPropertyHandle*>(child);
|
||||
if (handler && handler->getPropertyPath() == inPropertyPath) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
|
||||
{
|
||||
QPropertyHandle* handle = Find(inParent, inPropertyPath);
|
||||
if (handle)
|
||||
return handle;
|
||||
return new QPropertyHandle(
|
||||
inParent,
|
||||
inType,
|
||||
inPropertyPath,
|
||||
inGetter,
|
||||
inSetter
|
||||
);
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::FindOrCreate(QObject* inObject)
|
||||
{
|
||||
QPropertyHandle* handle = Find(inObject, "");
|
||||
if (handle)
|
||||
return handle;
|
||||
return new QPropertyHandle(
|
||||
inObject,
|
||||
inObject->metaObject()->metaType(),
|
||||
"",
|
||||
[inObject]() {return QVariant::fromValue(inObject); },
|
||||
[inObject](QVariant var) {}
|
||||
);
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::Create(QObject* inParent, QMetaType inType, QString inPropertyPath, Getter inGetter, Setter inSetter)
|
||||
{
|
||||
return new QPropertyHandle(inParent, inType, inPropertyPath, inGetter, inSetter);
|
||||
}
|
||||
|
||||
void QPropertyHandle::Cleanup(QObject* inParent)
|
||||
{
|
||||
for (QObject* child : inParent->children()) {
|
||||
QPropertyHandle* handler = qobject_cast<QPropertyHandle*>(child);
|
||||
if (handler) {
|
||||
handler->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMetaType QPropertyHandle::getType()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
QPropertyHandle::PropertyType QPropertyHandle::getPropertyType() const
|
||||
{
|
||||
return mPropertyType;
|
||||
}
|
||||
|
||||
QString QPropertyHandle::getName()
|
||||
{
|
||||
return objectName();
|
||||
}
|
||||
|
||||
QString QPropertyHandle::getPropertyPath()
|
||||
{
|
||||
return mPropertyPath;
|
||||
}
|
||||
|
||||
QString QPropertyHandle::createSubPath(const QString& inSubName)
|
||||
{
|
||||
return getPropertyPath() + "." + inSubName;
|
||||
}
|
||||
|
||||
void QPropertyHandle::invalidateStructure()
|
||||
{
|
||||
Q_EMIT asStructureChanged();
|
||||
}
|
||||
|
||||
QVariant QPropertyHandle::getVar()
|
||||
{
|
||||
return mGetter();
|
||||
}
|
||||
|
||||
void QPropertyHandle::setVar(QVariant var)
|
||||
{
|
||||
QVariant lastVar = mGetter();
|
||||
if (lastVar != var) {
|
||||
mSetter(var);
|
||||
Q_EMIT asVarChanged(var);
|
||||
QVariant currVar = mGetter();
|
||||
if (currVar != var) {
|
||||
Q_EMIT asRequestRollback(currVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QPropertyHandle::hasMetaData(const QString& inName) const
|
||||
{
|
||||
return mMetaData.contains(inName);
|
||||
}
|
||||
|
||||
QVariant QPropertyHandle::getMetaData(const QString& inName) const
|
||||
{
|
||||
return mMetaData.value(inName);
|
||||
}
|
||||
|
||||
const QVariantHash& QPropertyHandle::getMetaDataMap() const
|
||||
{
|
||||
return mMetaData;
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::findChild(QString inPropertyName)
|
||||
{
|
||||
return Find(this->parent(), this->createSubPath(inPropertyName));
|
||||
}
|
||||
|
||||
QPropertyHandle* QPropertyHandle::findOrCreateChild(QMetaType inType, QString inPropertyName, Getter inGetter, Setter inSetter)
|
||||
{
|
||||
QVariant var = inGetter();
|
||||
QObject* object = var.value<QObject*>();
|
||||
QPropertyHandle* childHandle = nullptr;
|
||||
if (object) {
|
||||
childHandle = QPropertyHandle::FindOrCreate(
|
||||
object
|
||||
);
|
||||
childHandle->setObjectName(inPropertyName);
|
||||
}
|
||||
else {
|
||||
childHandle = QPropertyHandle::FindOrCreate(
|
||||
this->parent(),
|
||||
inType,
|
||||
this->createSubPath(inPropertyName),
|
||||
inGetter,
|
||||
inSetter
|
||||
);
|
||||
}
|
||||
|
||||
childHandle->disconnect(this);
|
||||
connect(this, &QPropertyHandle::asVarChanged, childHandle, [childHandle](QVariant) {
|
||||
QVariant var = childHandle->getVar();
|
||||
Q_EMIT childHandle->asRequestRollback(var);
|
||||
});
|
||||
connect(this, &QPropertyHandle::asRequestRollback, childHandle, [childHandle](QVariant) {
|
||||
QVariant var = childHandle->getVar();
|
||||
Q_EMIT childHandle->asRequestRollback(var);
|
||||
});
|
||||
return childHandle;
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandle::setupNameEditor(QQuickItem* inParent)
|
||||
{
|
||||
return mImpl->createNameEditor(inParent);
|
||||
}
|
||||
|
||||
QQuickItem* QPropertyHandle::steupValueEditor(QQuickItem* inParent)
|
||||
{
|
||||
return mImpl->createValueEditor(inParent);
|
||||
}
|
||||
|
||||
IPropertyHandleImpl::Type QPropertyHandle::type()
|
||||
{
|
||||
return mImpl->type();
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Enum* QPropertyHandle::asEnum()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Enum*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Object* QPropertyHandle::asObject()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Object*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Associative* QPropertyHandle::asAssociative()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Associative*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandleImpl_Sequential* QPropertyHandle::asSequential()
|
||||
{
|
||||
return static_cast<QPropertyHandleImpl_Sequential*>(mImpl.get());
|
||||
}
|
||||
|
||||
QPropertyHandle::PropertyType QPropertyHandle::parserType(QMetaType inType)
|
||||
{
|
||||
if (QMetaType::canConvert(inType, QMetaType::fromType<QVariantList>())
|
||||
&& !QMetaType::canConvert(inType, QMetaType::fromType<QString>())
|
||||
) {
|
||||
return Sequential;
|
||||
}
|
||||
else if (QMetaType::canConvert(inType, QMetaType::fromType<QVariantMap>())) {
|
||||
return Associative;
|
||||
}
|
||||
else if (inType.flags() & QMetaType::IsEnumeration) {
|
||||
return Enum;
|
||||
}
|
||||
else {
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(inType.name(), 0, QRegularExpression::MatchType::PartialPreferCompleteMatch, QRegularExpression::AnchorAtOffsetMatchOption);
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
QMetaType innerMetaType;
|
||||
if (!matchTexts.isEmpty()) {
|
||||
QString metaTypeName = matchTexts.back();
|
||||
innerMetaType = QMetaType::fromName(metaTypeName.toLocal8Bit());
|
||||
}
|
||||
if (innerMetaType.metaObject() || inType.metaObject()) {
|
||||
return Object;
|
||||
}
|
||||
else {
|
||||
return RawType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QPropertyHandle::createNewVariant(QMetaType inOutputType)
|
||||
{
|
||||
QRegularExpression reg("QSharedPointer\\<(.+)\\>");
|
||||
QRegularExpressionMatch match = reg.match(inOutputType.name());
|
||||
QStringList matchTexts = match.capturedTexts();
|
||||
if (!matchTexts.isEmpty()) {
|
||||
QMetaType innerMetaType = QMetaType::fromName((matchTexts.back()).toLocal8Bit());
|
||||
if (innerMetaType.isValid()) {
|
||||
void* ptr = innerMetaType.create();
|
||||
QVariant sharedPtr(inOutputType);
|
||||
memcpy(sharedPtr.data(), &ptr, sizeof(ptr));
|
||||
QtSharedPointer::ExternalRefCountData* data = ExternalRefCountWithMetaType::create(innerMetaType, ptr);
|
||||
memcpy((char*)sharedPtr.data() + sizeof(ptr), &data, sizeof(data));
|
||||
return sharedPtr;
|
||||
}
|
||||
}
|
||||
else if (inOutputType.flags().testFlag(QMetaType::IsPointer)) {
|
||||
const QMetaObject* metaObject = inOutputType.metaObject();
|
||||
if (metaObject && metaObject->inherits(&QObject::staticMetaObject)) {
|
||||
QObject* obj = metaObject->newInstance();
|
||||
if (obj)
|
||||
return QVariant::fromValue(obj);
|
||||
}
|
||||
QMetaType innerMetaType = QMetaType::fromName(QString(inOutputType.name()).remove("*").toLocal8Bit());
|
||||
if (innerMetaType.isValid()) {
|
||||
void* ptr = innerMetaType.create();
|
||||
QVariant var(inOutputType, ptr);
|
||||
memcpy(var.data(), &ptr, sizeof(ptr));
|
||||
return var;
|
||||
}
|
||||
else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return QVariant(inOutputType);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
#include "QQuickDetailsView.h"
|
||||
#include "private/qqmldata_p.h"
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "qqmlcontext.h"
|
||||
#include "QQuickDetailsViewPrivate.h"
|
||||
#include "QQuickDetailsViewRow.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
|
||||
void QQuickDetailsViewPrivate::initItemCallback(int serializedModelIndex, QObject* object)
|
||||
{
|
||||
QQuickTreeViewExPrivate::initItemCallback(serializedModelIndex, object);
|
||||
auto item = qobject_cast<QQuickItem*>(object);
|
||||
if (!item)
|
||||
return;
|
||||
const QModelIndex& index = m_treeModelToTableModel.mapToModel(serializedModelIndex);;
|
||||
IDetailsViewRow* node = static_cast<IDetailsViewRow*>(index.internalPointer());
|
||||
node->setupItem(item);
|
||||
}
|
||||
|
||||
QQuickDetailsView::QQuickDetailsView(QQuickItem* parent /*= nullptr*/)
|
||||
: QQuickTreeViewEx(*(new QQuickDetailsViewPrivate()),parent)
|
||||
{
|
||||
setModel(QVariant::fromValue(d_func()->mModel));
|
||||
setReuseItems(false);
|
||||
setEditTriggers(QQuickTableView::EditTrigger::DoubleTapped);
|
||||
}
|
||||
|
||||
qreal QQuickDetailsView::getSpliterPencent() const
|
||||
{
|
||||
return d_func()->mSpliterPencent;
|
||||
}
|
||||
|
||||
void QQuickDetailsView::setSpliterPencent(qreal val)
|
||||
{
|
||||
if(val != d_func()->mSpliterPencent){
|
||||
d_func()->mSpliterPencent = val;
|
||||
Q_EMIT asSpliterPencentChanged(val);
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickDetailsView::setObject(QObject* inObject)
|
||||
{
|
||||
if (inObject != d_func()->mModel->getObject()) {
|
||||
d_func()->mModel->setObject(inObject);
|
||||
Q_EMIT asObjectChanged(inObject);
|
||||
}
|
||||
}
|
||||
|
||||
QObject* QQuickDetailsView::getObject() const
|
||||
{
|
||||
return d_func()->mModel->getObject();
|
||||
}
|
||||
|
||||
void QQuickDetailsView::componentComplete()
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(this);
|
||||
QQmlComponent* delegate = new QQmlComponent(engine, this);
|
||||
engine->rootContext()->setContextProperty("helper", QQuickFunctionLibrary::Get());
|
||||
delegate->setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import QtQuick.Layouts;
|
||||
import QtQuick.DetailsView;
|
||||
Item {
|
||||
id: detailsDelegate
|
||||
readonly property real indent: 20
|
||||
readonly property real padding: 5
|
||||
required property DetailsView detailsView
|
||||
required property int row
|
||||
required property bool isTreeNode
|
||||
required property bool expanded
|
||||
required property int hasChildren
|
||||
required property int depth
|
||||
implicitWidth: detailsView.width
|
||||
implicitHeight: heightProxy ? heightProxy.height + 5 : 30
|
||||
TapHandler {
|
||||
onTapped: detailsView.toggleExpanded(row)
|
||||
}
|
||||
onImplicitHeightChanged: {
|
||||
detailsView.invalidateLayout();
|
||||
}
|
||||
})", QUrl("QQuickDetailsView.componentComplete"));;
|
||||
setDelegate(delegate);
|
||||
QQuickTreeViewEx::componentComplete();
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
#include "QQuickDetailsViewMananger.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
#include <QRegularExpression>
|
||||
#include <QDir>
|
||||
#include <QMetaType>
|
||||
|
||||
#define REGINTER_NUMBER_EDITOR_CREATOR(TypeName, DefaultPrecision) \
|
||||
registerTypeEditor(QMetaType::fromType<TypeName>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { \
|
||||
QQmlEngine* engine = qmlEngine(parent); \
|
||||
QQmlContext* context = qmlContext(parent); \
|
||||
QQmlComponent nameComp(engine); \
|
||||
const char* qmlData = \
|
||||
"import QtQuick;\n" \
|
||||
"import QtQuick.Controls;\n" \
|
||||
"import \"qrc:/resources/Qml/ValueEditor\"\n" \
|
||||
"NumberBox{\n" \
|
||||
" anchors.verticalCenter: parent.verticalCenter\n" \
|
||||
" width: parent.width\n" \
|
||||
"}"; \
|
||||
nameComp.setData(qmlData, QUrl()); \
|
||||
QVariantMap initialProperties; \
|
||||
initialProperties["parent"] = QVariant::fromValue(parent); \
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(nameComp.createWithInitialProperties(initialProperties, context)); \
|
||||
if (!nameComp.errors().isEmpty()) { \
|
||||
qDebug() << nameComp.errorString(); \
|
||||
} \
|
||||
if (valueEditor) { \
|
||||
valueEditor->setParentItem(parent); \
|
||||
TypeName min = handle->getMetaData("Min").toDouble(); \
|
||||
TypeName max = handle->getMetaData("Max").toDouble(); \
|
||||
double step = handle->getMetaData("Step").toDouble(); \
|
||||
int precision = handle->getMetaData("Precision").toInt(); \
|
||||
valueEditor->setProperty("step", step <= 0.0001 ? 1.0 / pow(10.0, DefaultPrecision) : step); \
|
||||
valueEditor->setProperty("number", handle->getVar()); \
|
||||
valueEditor->setProperty("precision", precision == 0 ? DefaultPrecision : precision); \
|
||||
if (min >= max) { \
|
||||
min = std::numeric_limits<TypeName>::min(); \
|
||||
max = std::numeric_limits<TypeName>::max(); \
|
||||
} else { \
|
||||
valueEditor->setProperty("isLimited", true); \
|
||||
} \
|
||||
valueEditor->setProperty("min", QVariant::fromValue(min)); \
|
||||
valueEditor->setProperty("max", QVariant::fromValue(max)); \
|
||||
qDebug() << min << max; \
|
||||
QObject::connect(valueEditor, SIGNAL(valueChanged(QVariant)), handle, SLOT(setVar(QVariant))); \
|
||||
QObject::connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setNumber(QVariant))); \
|
||||
} \
|
||||
return valueEditor; \
|
||||
});
|
||||
|
||||
|
||||
void QQuickDetailsViewManager::RegisterBasicTypeEditor() {
|
||||
REGINTER_NUMBER_EDITOR_CREATOR(int, 0);
|
||||
REGINTER_NUMBER_EDITOR_CREATOR(unsigned int, 0);
|
||||
REGINTER_NUMBER_EDITOR_CREATOR(size_t, 0);
|
||||
REGINTER_NUMBER_EDITOR_CREATOR(float, 2);
|
||||
REGINTER_NUMBER_EDITOR_CREATOR(double, 3);
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QString>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
MultiLineTextBox{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QVector4D>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
Vec4Box{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QVector3D>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
Vec3Box{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QVector2D>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
Vec2Box{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QColor>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
ColorBox{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
|
||||
QMetaType::registerConverterFunction(
|
||||
[](const void* src, void* target) -> bool {
|
||||
const QDir& dir = *static_cast<const QDir*>(src);
|
||||
QString& str = *static_cast<QString*>(target);
|
||||
str = dir.absolutePath();
|
||||
return true;
|
||||
},
|
||||
QMetaType::fromType<QDir>(),
|
||||
QMetaType::fromType<QString>()
|
||||
);
|
||||
|
||||
QMetaType::registerConverterFunction(
|
||||
[](const void* src, void* target) -> bool {
|
||||
const QString& str = *static_cast<const QString*>(src);
|
||||
QDir& dir = *static_cast<QDir*>(target);
|
||||
dir = QDir(str);
|
||||
return true;
|
||||
},
|
||||
QMetaType::fromType<QString>(),
|
||||
QMetaType::fromType<QDir>()
|
||||
);
|
||||
|
||||
registerTypeEditor(QMetaType::fromType<QDir>(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
|
||||
QQmlEngine* engine = qmlEngine(parent);
|
||||
QQmlContext* context = qmlContext(parent);
|
||||
QQmlComponent comp(engine);
|
||||
comp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import "qrc:/resources/Qml/ValueEditor"
|
||||
DirectorySelector{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
}
|
||||
)", QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(parent);
|
||||
auto valueEditor = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
valueEditor->setParentItem(parent);
|
||||
valueEditor->setProperty("value", handle->getVar());
|
||||
connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
|
||||
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
|
||||
return valueEditor;
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
#include "QQuickDetailsViewLayoutBuilder.h"
|
||||
#include "QQuickDetailsViewMananger.h"
|
||||
|
||||
QQuickDetailsViewRowBuilder::QQuickDetailsViewRowBuilder(IDetailsViewRow* inRow, QQuickItem* inRootItem)
|
||||
: mRow(inRow)
|
||||
, mRootItem(inRootItem)
|
||||
{
|
||||
setHeightProxy(nullptr);
|
||||
}
|
||||
|
||||
QPair<QQuickItem*, QQuickItem*> QQuickDetailsViewRowBuilder::makeNameValueSlot()
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(mRootItem);
|
||||
QQmlContext* context = qmlContext(mRootItem);
|
||||
QQmlContext* newContext = new QQmlContext(context, mRootItem);
|
||||
QQmlComponent rootComp(newContext->engine());
|
||||
rootComp.setData(R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import ColorPalette
|
||||
Rectangle{
|
||||
id: topLevelRect
|
||||
anchors.fill: parent
|
||||
color: hoverHandler.hovered ? ColorPalette.theme.rowBackgroundHover : ColorPalette.theme.rowBackground
|
||||
border.color: ColorPalette.theme.rowBorder
|
||||
border.width: 0.5
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 100 }
|
||||
}
|
||||
HoverHandler { id: hoverHandler }
|
||||
Image {
|
||||
id: indicator
|
||||
visible: detailsDelegate.isTreeNode && detailsDelegate.hasChildren
|
||||
x: padding + (detailsDelegate.depth * detailsDelegate.indent)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
mipmap: true
|
||||
source: detailsDelegate.expanded
|
||||
? "qrc:/resources/Icon/expand.png"
|
||||
: "qrc:/resources/Icon/unexpand.png"
|
||||
|
||||
width: 12
|
||||
height: 12
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: ColorPalette.theme.rowIndicator
|
||||
opacity: 1.0
|
||||
}
|
||||
}
|
||||
Item{
|
||||
id: nameEditorContent
|
||||
height: parent.height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: padding + (detailsDelegate.isTreeNode ? (detailsDelegate.depth + 1) * detailsDelegate.indent : 0)
|
||||
anchors.right: splitter.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Item{
|
||||
id: valueEditorContent
|
||||
implicitHeight: 25
|
||||
anchors.left: splitter.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Rectangle{
|
||||
id: splitter
|
||||
width: 3
|
||||
height: parent.height
|
||||
color : ColorPalette.theme.rowBackground
|
||||
x: detailsView.SpliterPencent * detailsView.width - 1
|
||||
|
||||
Rectangle{
|
||||
id: visibleSplitter
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: ColorPalette.theme.rowSplitter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
hoverEnabled: true
|
||||
cursorShape: containsMouse ? Qt.SplitHCursor : Qt.ArrowCursor
|
||||
anchors.fill: parent
|
||||
drag.target: splitter
|
||||
drag.axis: Drag.XAxis
|
||||
drag.minimumX: 10 + 1
|
||||
drag.maximumX: detailsView.width - 10 - 1
|
||||
onPositionChanged: {
|
||||
detailsView.SpliterPencent = (splitter.x + 1) / detailsView.width
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: gradientBox
|
||||
visible: detailsDelegate.depth > 0
|
||||
width: 5
|
||||
x: padding + (detailsDelegate.depth * detailsDelegate.indent) - 10
|
||||
height: parent.height
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
GradientStop { position: 0.0; color: ColorPalette.theme.rowShadowStart }
|
||||
GradientStop { position: 1.0; color: ColorPalette.theme.rowShadowEnd }
|
||||
}
|
||||
}
|
||||
}
|
||||
)", QUrl());
|
||||
QQuickItem* slotItem = qobject_cast<QQuickItem*>(rootComp.create(newContext));
|
||||
if (!rootComp.errors().isEmpty()) {
|
||||
qDebug() << rootComp.errorString();
|
||||
}
|
||||
slotItem->setParentItem(mRootItem);
|
||||
return { slotItem->childItems()[1] ,slotItem->childItems()[2] };
|
||||
}
|
||||
|
||||
QQuickItem* QQuickDetailsViewRowBuilder::rootItem() const
|
||||
{
|
||||
return mRootItem;
|
||||
}
|
||||
|
||||
IDetailsViewRow* QQuickDetailsViewRowBuilder::row() const
|
||||
{
|
||||
return mRow;
|
||||
}
|
||||
|
||||
QQuickItem* QQuickDetailsViewRowBuilder::setupItem(QQuickItem* inParent, QString inQmlCode)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(inParent);
|
||||
QQmlContext* context = qmlContext(inParent);
|
||||
QQmlComponent comp(engine);
|
||||
QQmlComponent nameComp(engine);
|
||||
comp.setData(inQmlCode.toLocal8Bit(), QUrl());
|
||||
QVariantMap initialProperties;
|
||||
initialProperties["parent"] = QVariant::fromValue(inParent);
|
||||
auto item = qobject_cast<QQuickItem*>(comp.createWithInitialProperties(initialProperties, context));
|
||||
if (!comp.errors().isEmpty()) {
|
||||
qDebug() << comp.errorString();
|
||||
}
|
||||
item->setParentItem(inParent);
|
||||
return item;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewRowBuilder::setupLabel(QQuickItem* inParent, QString inText)
|
||||
{
|
||||
QQuickItem* lableItem = setupItem(inParent, R"(
|
||||
import QtQuick;
|
||||
import QtQuick.Controls;
|
||||
import ColorPalette;
|
||||
Item{
|
||||
property string lableText
|
||||
implicitHeight: 25
|
||||
width: parent.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
clip: true
|
||||
elide: Text.ElideRight
|
||||
text: lableText
|
||||
color: ColorPalette.theme.labelPrimary
|
||||
}
|
||||
}
|
||||
)");
|
||||
lableItem->setProperty("lableText", inText);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewRowBuilder::setHeightProxy(QQuickItem* inProxyItem)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(mRootItem);
|
||||
QQmlContext* context = qmlContext(mRootItem);
|
||||
context->parentContext()->setContextProperty("heightProxy", inProxyItem);
|
||||
}
|
||||
|
||||
void QQuickDetailsViewRowBuilder::makePropertyRow(QPropertyHandle* inHandle)
|
||||
{
|
||||
QQmlEngine* engine = qmlEngine(mRootItem);
|
||||
QQmlContext* context = qmlContext(mRootItem);
|
||||
QPair<QQuickItem*, QQuickItem*> slotItem = makeNameValueSlot();
|
||||
QQuickItem* nameEditor = inHandle->setupNameEditor(slotItem.first);
|
||||
QQuickItem* valueEditor = inHandle->steupValueEditor(slotItem.second);
|
||||
context->parentContext()->setContextProperty("heightProxy", valueEditor ? valueEditor : nameEditor);
|
||||
}
|
||||
|
||||
QQuickDetailsViewLayoutBuilder::QQuickDetailsViewLayoutBuilder(IDetailsViewRow* inRow)
|
||||
: mRootRow(inRow)
|
||||
{
|
||||
}
|
||||
|
||||
IDetailsViewRow* QQuickDetailsViewLayoutBuilder::row() const
|
||||
{
|
||||
return mRootRow;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewLayoutBuilder::addCustomRow(std::function<void(QQuickDetailsViewRowBuilder*)> creator, QString inOverrideName)
|
||||
{
|
||||
QSharedPointer<IDetailsViewRow> child(new QDetailsViewRow_Custom(creator));
|
||||
child->setName(inOverrideName);
|
||||
mRootRow->addChild(child);
|
||||
child->attachChildren();
|
||||
}
|
||||
|
||||
void QQuickDetailsViewLayoutBuilder::addProperty(QPropertyHandle* inPropertyHandle, QString inOverrideName)
|
||||
{
|
||||
QSharedPointer<IDetailsViewRow> child(new QDetailsViewRow_Property(inPropertyHandle));
|
||||
child->setName(inOverrideName.isEmpty() ? inPropertyHandle->getName() : inOverrideName);
|
||||
mRootRow->addChild(child);
|
||||
child->attachChildren();
|
||||
}
|
||||
|
||||
void QQuickDetailsViewLayoutBuilder::addObject(QObject* inObject)
|
||||
{
|
||||
addProperty(QPropertyHandle::FindOrCreate(inObject));
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
#include "QQuickDetailsViewMananger.h"
|
||||
#include "QPropertyHandle.h"
|
||||
#include "QQuickFunctionLibrary.h"
|
||||
#include <QRegularExpression>
|
||||
#include "QQuickDetailsView.h"
|
||||
#include "Customization/PropertyTypeCustomization_Sequential.h"
|
||||
#include "Customization/PropertyTypeCustomization_Associative.h"
|
||||
#include "Customization/PropertyTypeCustomization_ObjectDefault.h"
|
||||
#include "Customization/PropertyTypeCustomization_Matrix4x4.h"
|
||||
#include <QtQuickControls2/QQuickStyle>
|
||||
#include <QMatrix4x4>
|
||||
|
||||
QQuickDetailsViewManager* QQuickDetailsViewManager::Get()
|
||||
{
|
||||
static QQuickDetailsViewManager ins;
|
||||
return &ins;
|
||||
}
|
||||
|
||||
void QQuickDetailsViewManager::initialize()
|
||||
{
|
||||
mInitialized = true;
|
||||
|
||||
#ifdef 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,151 @@
|
|||
version: '{build}'
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
global:
|
||||
# Appveyor doesn't have Qt 12 yet
|
||||
LatestQtVersion: 5.13
|
||||
matrix:
|
||||
# 32 bit builds
|
||||
# MSVC 2015 builds
|
||||
# Dynamic Library builds
|
||||
# LTS version of Qt, dll, 32bit, MSVC 2015, qmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: x86
|
||||
use_mingw: "false"
|
||||
use_static: "false"
|
||||
use_cmake: "false"
|
||||
# LTS version of Qt, dll, 32bit, MSVC 2015, cmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: x86
|
||||
use_mingw: "false"
|
||||
use_static: "false"
|
||||
use_cmake: "true"
|
||||
# end Dynamic Library builds
|
||||
# Static Library builds
|
||||
# LTS version of Qt, static, 32bit, MSVC 2015, qmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: x86
|
||||
use_mingw: "false"
|
||||
use_static: "true"
|
||||
use_cmake: "false"
|
||||
# LTS version of Qt, static, 32bit, MSVC 2015, cmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: x86
|
||||
use_mingw: "false"
|
||||
use_static: "true"
|
||||
use_cmake: "true"
|
||||
# end Static Library builds
|
||||
# end MSVC 2015 builds
|
||||
# MinGW builds
|
||||
# Dynamic Library builds
|
||||
# LTS version of Qt, dll, 32bit, MinGW, qmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\mingw73_32
|
||||
COMPILER: C:\Qt\Tools\mingw730_32
|
||||
targetPlatform: x86
|
||||
use_mingw: "true"
|
||||
use_static: "false"
|
||||
use_cmake: "false"
|
||||
# LTS version of Qt, dll, 32bit, MinGW, cmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\mingw73_32
|
||||
COMPILER: C:\Qt\Tools\mingw730_32
|
||||
targetPlatform: x86
|
||||
use_mingw: "true"
|
||||
use_static: "false"
|
||||
use_cmake: "true"
|
||||
# end Dynamic Library builds
|
||||
# Static Library builds
|
||||
# LTS version of Qt, static, 32bit, MinGW, qmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\mingw73_32
|
||||
COMPILER: C:\Qt\Tools\mingw730_32
|
||||
targetPlatform: x86
|
||||
use_mingw: "true"
|
||||
use_static: "true"
|
||||
use_cmake: "false"
|
||||
# LTS version of Qt, static, 32bit, MinGW, cmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\mingw73_32
|
||||
COMPILER: C:\Qt\Tools\mingw730_32
|
||||
targetPlatform: x86
|
||||
use_mingw: "true"
|
||||
use_static: "true"
|
||||
use_cmake: "true"
|
||||
# end Static Library builds
|
||||
# end MinGW builds
|
||||
# end 32 bit builds
|
||||
# 64 bit builds
|
||||
# MSVC 2015 builds
|
||||
# Dynamic Library builds
|
||||
# LTS version of Qt, dll, 64bit, MSVC 2015, qmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017_64
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: amd64
|
||||
use_mingw: "false"
|
||||
use_static: "false"
|
||||
use_cmake: "false"
|
||||
# LTS version of Qt, dll, 64bit, MSVC 2015, cmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017_64
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: amd64
|
||||
use_mingw: "false"
|
||||
use_static: "false"
|
||||
use_cmake: "true"
|
||||
# end Dynamic Library builds
|
||||
# Static Library builds
|
||||
# LTS version of Qt, static, 64bit, MSVC 2015, qmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017_64
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: amd64
|
||||
use_mingw: "false"
|
||||
use_static: "true"
|
||||
use_cmake: "false"
|
||||
# LTS version of Qt, static, 64bit, MSVC 2015, cmake
|
||||
- QT5: C:\Qt\%LatestQtVersion%\msvc2017_64
|
||||
COMPILER: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build
|
||||
targetPlatform: amd64
|
||||
use_mingw: "false"
|
||||
use_static: "true"
|
||||
use_cmake: "true"
|
||||
# end Static Library builds
|
||||
# end MSVC 2015 builds
|
||||
# end 64 bit builds
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
before_build:
|
||||
- set originalWD=%CD%
|
||||
- call "%QT5%\bin\qtenv2.bat"
|
||||
- cd /D %originalWD%
|
||||
- if %use_mingw%==false call "%COMPILER%\vcvarsall.bat" %targetPlatform%
|
||||
- if %use_static%==true (set USESTATIC=ON) else (set USESTATIC=OFF)
|
||||
- if %use_mingw%==true (set CMAKEGENERATOR="MinGW Makefiles") else (set CMAKEGENERATOR="NMake Makefiles")
|
||||
- if %use_mingw%==true (set MAKEENGINE=mingw32-make) else (set MAKEENGINE=nmake)
|
||||
- if %use_mingw%==true set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
||||
|
||||
build_script:
|
||||
- if %use_cmake%==true mkdir build
|
||||
- if %use_cmake%==true cd build
|
||||
- if %use_cmake%==true cmake --version
|
||||
- if %use_cmake%==true cmake -G %CMAKEGENERATOR% -DCMAKE_BUILD_TYPE=DEBUG -DBUILD_EXAMPLES=ON -DCMAKE_DEBUG_POSTFIX=d -DBUILD_STATIC=%USESTATIC% -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- if %use_cmake%==true cmake --build .
|
||||
- if %use_cmake%==true cmake --build . --target install
|
||||
- if %use_cmake%==true cmake -G %CMAKEGENERATOR% -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_EXAMPLES=ON -DBUILD_STATIC=%USESTATIC% -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- if %use_cmake%==true cmake --build .
|
||||
- if %use_cmake%==true cmake --build . --target install
|
||||
- if %use_cmake%==false if %use_static%==true qmake "CONFIG+=adsBuildStatic"
|
||||
- if %use_cmake%==false if %use_static%==false qmake
|
||||
- if %use_cmake%==false %MAKEENGINE% debug
|
||||
- if %use_cmake%==false %MAKEENGINE% install
|
||||
- if %use_cmake%==false %MAKEENGINE% release
|
||||
- if %use_cmake%==false %MAKEENGINE% install
|
||||
|
||||
after_build:
|
||||
- if %use_mingw%==true set PATH=C:\Program Files\Git\usr\bin;%PATH%
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: Inline
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
BeforeLambdaBody: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakStringLiterals: true
|
||||
|
||||
ColumnLimit: 82
|
||||
CommentPragmas: '^(!.*|@c)'
|
||||
|
||||
CompactNamespaces: false
|
||||
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
|
||||
FixNamespaceComments: true
|
||||
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<windows(\.h)?>' # windows headers
|
||||
Priority: -1
|
||||
- Regex: '^<labbcan[[:alnum:]._/]+' # labbcan headers
|
||||
Priority: 1
|
||||
- Regex: '^<diag[[:alnum:]._/]+' # usl headers
|
||||
Priority: 1
|
||||
- Regex: '^<usl[[:alnum:]._/]+' # usl headers
|
||||
Priority: 1
|
||||
- Regex: '^<qt5compat[[:alnum:]._/]+' # qtlabb headers
|
||||
Priority: 2
|
||||
- Regex: '^<qtcoreaddons[[:alnum:]._/]+' # qtlabb headers
|
||||
Priority: 2
|
||||
- Regex: '^<qtlabb[[:alnum:]._/]+' # qtlabb headers
|
||||
Priority: 2
|
||||
- Regex: '<Q[[:alnum:].]+>' # Qt headers
|
||||
Priority: 3
|
||||
- Regex: '^<sila_cpp[[:alnum:]._/]+' # sila_cpp headers
|
||||
Priority: 4
|
||||
- Regex: '<[[:alnum:]._/]+\.h>' # other headers
|
||||
Priority: 5
|
||||
- Regex: '<[[:alnum:]._/]+>' # system headers
|
||||
Priority: 6
|
||||
- Regex: '.*'
|
||||
Priority: 7
|
||||
|
||||
IncludeIsMainRegex: '(_p)?$'
|
||||
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
|
||||
NamespaceIndentation: None
|
||||
|
||||
PenaltyBreakAssignment: 20
|
||||
PenaltyBreakBeforeFirstCallParameter: 15
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 5
|
||||
# PenaltyBreakOpenParenthesis: 30
|
||||
PenaltyBreakString: 150
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 150
|
||||
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
|
||||
SortIncludes: CaseSensitive
|
||||
SortUsingDeclarations: true
|
||||
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
Standard: c++17
|
||||
|
||||
StatementMacros: [ 'Q_UNUSED', 'PIMPL_D', 'PIMPL_Q', 'OD_ENTRY', 'OD_ENTRY_PROCIMG' ]
|
||||
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
|
|
@ -0,0 +1 @@
|
|||
PyQtAds/_version.py export-subst
|
||||
|
|
@ -0,0 +1,389 @@
|
|||
*.pro.user*
|
||||
/build
|
||||
*.o
|
||||
*.dylib
|
||||
*.app
|
||||
qrc_*
|
||||
moc_*
|
||||
ui_*
|
||||
Makefile
|
||||
*.dll
|
||||
*.a
|
||||
build-*
|
||||
|
||||
# IDEs
|
||||
.idea
|
||||
|
||||
# Python
|
||||
.eggs
|
||||
*.pyc
|
||||
*.pyd
|
||||
__pycache__
|
||||
PyQtAds/rc.py
|
||||
/.cproject
|
||||
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*[.json, .xml, .info]
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
/ build
|
||||
/Settings.ini
|
||||
.vscode/settings.json
|
||||
/.settings
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>QtAdvancedDockingSystem</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
language: cpp
|
||||
# gcc is clang on mac
|
||||
compiler: gcc
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- name: Ubuntu qmake Qt5.5.1
|
||||
os: linux
|
||||
dist: trusty
|
||||
group: stable
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt551-trusty'
|
||||
update: true
|
||||
packages:
|
||||
- qt55base
|
||||
- qt55tools
|
||||
- gcc-9
|
||||
- g++-9
|
||||
script:
|
||||
- PATH="/opt/qt55/bin:$PATH"
|
||||
- CXX="g++-9"
|
||||
- CC="gcc-9"
|
||||
- qt55-env.sh
|
||||
- qmake
|
||||
- make
|
||||
- make install
|
||||
- name: Ubuntu qmake dll
|
||||
os: linux
|
||||
dist: bionic
|
||||
group: stable
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic'
|
||||
update: true
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
- libgl-dev
|
||||
- libgl1-mesa-dev
|
||||
- mesa-common-dev
|
||||
script:
|
||||
- PATH="/opt/qt514/bin:$PATH"
|
||||
- CXX="g++-9"
|
||||
- CC="gcc-9"
|
||||
- qt514-env.sh
|
||||
- qmake
|
||||
- make
|
||||
- make install
|
||||
- name: Ubuntu qmake static
|
||||
os: linux
|
||||
dist: bionic
|
||||
group: stable
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic'
|
||||
update: true
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
- libgl-dev
|
||||
- libgl1-mesa-dev
|
||||
- mesa-common-dev
|
||||
script:
|
||||
- PATH="/opt/qt514/bin:$PATH"
|
||||
- CXX="g++-9"
|
||||
- CC="gcc-9"
|
||||
- qt514-env.sh
|
||||
- qmake "CONFIG+=adsBuildStatic"
|
||||
- make
|
||||
- make install
|
||||
- name: Ubuntu CMake dll
|
||||
os: linux
|
||||
dist: bionic
|
||||
group: stable
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic'
|
||||
update: true
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
- libgl-dev
|
||||
- libgl1-mesa-dev
|
||||
- mesa-common-dev
|
||||
script:
|
||||
- PATH="/opt/qt514/bin:$PATH"
|
||||
- CXX="g++-9"
|
||||
- CC="gcc-9"
|
||||
- qt514-env.sh
|
||||
- mkdir ./build
|
||||
- cd ./build
|
||||
- cmake --version
|
||||
- cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_STATIC=OFF -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=DEBUG -DBUILD_STATIC=OFF -DBUILD_EXAMPLES=ON -DCMAKE_DEBUG_POSTFIX=d -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- name: Ubuntu CMake Static
|
||||
os: linux
|
||||
dist: bionic
|
||||
group: stable
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic'
|
||||
update: true
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
- libgl-dev
|
||||
- libgl1-mesa-dev
|
||||
- mesa-common-dev
|
||||
script:
|
||||
- PATH="/opt/qt514/bin:$PATH"
|
||||
- CXX="g++-9"
|
||||
- CC="gcc-9"
|
||||
- qt514-env.sh
|
||||
- mkdir ./build
|
||||
- cd ./build
|
||||
- cmake --version
|
||||
- cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_STATIC=ON -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=DEBUG -DBUILD_STATIC=ON -DBUILD_EXAMPLES=ON -DCMAKE_DEBUG_POSTFIX=d -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- name: macOS CMake dll
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
compiler: clang
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- qt
|
||||
update: true
|
||||
script:
|
||||
- PATH="/usr/local/opt/qt5/bin:$PATH"
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake --version
|
||||
- cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=DEBUG -DCMAKE_DEBUG_POSTFIX=_debug -DBUILD_EXAMPLES=ON -DBUILD_STATIC=OFF -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_EXAMPLES=ON -DBUILD_STATIC=OFF -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- name: macOS CMake static
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
compiler: clang
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- qt
|
||||
update: true
|
||||
script:
|
||||
- PATH="/usr/local/opt/qt5/bin:$PATH"
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake --version
|
||||
- cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=DEBUG -DCMAKE_DEBUG_POSTFIX=_debug -DBUILD_EXAMPLES=ON -DBUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_EXAMPLES=ON -DBUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX="./installed" ../
|
||||
- cmake --build .
|
||||
- cmake --build . --target install
|
||||
- name: macOS qmake dll
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
compiler: clang
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- qt
|
||||
update: true
|
||||
script:
|
||||
- PATH="/usr/local/opt/qt5/bin:$PATH"
|
||||
- qmake
|
||||
- make
|
||||
- make install
|
||||
- name: macOS qmake static
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
compiler: clang
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- qt
|
||||
update: true
|
||||
script:
|
||||
- PATH="/usr/local/opt/qt5/bin:$PATH"
|
||||
- qmake "CONFIG+=adsBuildStatic"
|
||||
- make
|
||||
- make install
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
cmake_minimum_required(VERSION 3.5...4.3)
|
||||
|
||||
if (POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif (POLICY CMP0091)
|
||||
|
||||
# By default, the version information is extracted from the git index. However,
|
||||
# we can override this behavior by explicitly setting ADS_VERSION and
|
||||
# skipping the git checks. This is useful for cases where this project is being
|
||||
# used independently of its original git repo (e.g. vendored in another project)
|
||||
if(NOT ADS_VERSION)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
||||
include(GetGitRevisionDescription)
|
||||
git_describe(GitTagVersion --tags)
|
||||
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GitTagVersion}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${GitTagVersion}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${GitTagVersion}")
|
||||
set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||
else()
|
||||
string(REGEX MATCHALL "[\.]" VERSION_DOT_MATCHES ${ADS_VERSION})
|
||||
list(LENGTH VERSION_DOT_MATCHES VERSION_DOT_COUNT)
|
||||
if(VERSION_DOT_COUNT EQUAL 2)
|
||||
set(VERSION_SHORT ${ADS_VERSION})
|
||||
else()
|
||||
message(FATAL_ERROR "ADS_VERSION must be in major.minor.patch format, e.g. 3.8.1. Got ${ADS_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
project(QtADS LANGUAGES CXX VERSION ${VERSION_SHORT})
|
||||
|
||||
option(BUILD_STATIC "Build the static library" OFF)
|
||||
option(BUILD_EXAMPLES "Build the examples" ON)
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||
set(ads_PlatformDir "x86")
|
||||
else()
|
||||
if(DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(ads_PlatformDir "x64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64")
|
||||
set(ads_PlatformDir "arm64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
set(ads_PlatformDir "aarch64")
|
||||
else()
|
||||
set(ads_PlatformDir "x64")
|
||||
endif()
|
||||
else()
|
||||
set(ads_PlatformDir "x64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
add_subdirectory(demo)
|
||||
endif()
|
||||
|
||||
|
|
@ -0,0 +1,504 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random
|
||||
Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include versioneer.py
|
||||
include PyQtAds/_version.py
|
||||
|
|
@ -0,0 +1,717 @@
|
|||

|
||||
|
||||

|
||||
|
||||
------------------
|
||||
|
||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest)
|
||||
[](gnu-lgpl-v2.1.md)
|
||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
||||
[](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/graphs/contributors)
|
||||
|
||||
Qt Advanced Docking System lets you create customizable layouts using a full
|
||||
featured window docking system similar to what is found in many popular
|
||||
integrated development environments (IDEs) such as Visual Studio.
|
||||
|
||||
- [What's new...](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest)
|
||||
- [Documentation](doc/user-guide.md)
|
||||
- Original Repository: https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System
|
||||
|
||||
[](https://www.youtube.com/watch?v=7pdNfafg3Qc)
|
||||
|
||||
## New and Noteworthy
|
||||
|
||||
Release [4.1](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) significantly improves the Auto-Hide functionality and also brings improvements
|
||||
for Drag and Drop of dock widgets into dock area tabs. These are the highlights of the new version:
|
||||
|
||||
#### Drag & Drop to Auto-Hide
|
||||
|
||||
Now you can easily drag any dock widget or any floating widget to the
|
||||
borders of a window to pin it as a auto-hide tab in one of the 4 sidebars.
|
||||
If you drag a dock widget close the one of the four window borders, special
|
||||
drop overlays will be shown to indicate the drop area for auto-hide widgets:
|
||||
|
||||

|
||||
|
||||
Of course, this also works with dock areas:
|
||||
|
||||

|
||||
|
||||
If you drag a dock widget or dock area into a sidebar, then you even have
|
||||
control over where tabs are inserted. Simply drag your mouse over a specific
|
||||
auto-hide tab, and your dragged dock widget will be inserted before this tab.
|
||||
Drag to the sidebar area behind the last tab, and the dragged widget will be
|
||||
appended as last tab. In the following screen capture, the **Image Viewer 1** will
|
||||
be inserted before the **Table 0** Auto-Hide tab and the **Image Viewer 2**
|
||||
is appende behind the last tab:
|
||||
|
||||

|
||||
|
||||
#### Auto-Hide Tab Insertion Order
|
||||
|
||||
It is also possible to drag Auto-Hide tabs to a new auto-hide position.
|
||||
That means, you can drag them to a different border or sidebar:
|
||||
|
||||

|
||||
|
||||
#### Auto-Hide Tab Sorting
|
||||
|
||||
You can drag Auto-Hide tabs to a new position in the current sidebar
|
||||
to sort them:
|
||||
|
||||

|
||||
|
||||
#### Auto-Hide Drag to Float / Dock
|
||||
|
||||
But that is not all. You can also simply move Auto-Hide tabs to another
|
||||
floating widget or dock them via drag and drop:
|
||||
|
||||

|
||||
|
||||
#### Auto-Hide Context Menu
|
||||
|
||||
All Auto-Hide tabs now have a context menu, that provides all the functionality
|
||||
that you know from Dock widget tabs. With the **Pin To...** item from the
|
||||
context menu it is very easy to move an Auto-Hide tab to a different Auto-Hide
|
||||
sidebar:
|
||||
|
||||

|
||||
|
||||
#### Dock Area Tab Insert Order
|
||||
|
||||
And last but not least the new version also improves the docking of widgets
|
||||
into the tabs of a Dock area. Just as with Auto-Hide tabs, you can now determine the position at which a tab is inserted by moving the mouse over an already existing tab (insertion before the tab) or behind the last tab
|
||||
(appending):
|
||||
|
||||

|
||||
|
||||
The [release 4.0](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest)
|
||||
adds the following features:
|
||||
|
||||
- Auto-Hide functionality ([read more...](#auto-hide-functionality))
|
||||
|
||||

|
||||
|
||||
- improved demo application with new image viewer dock widgets
|
||||
|
||||

|
||||
|
||||
- Visual Studio like CSS theme in demo application
|
||||
|
||||
The [release 3.8](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/3.8.3)
|
||||
adds the following features:
|
||||
|
||||
- option to close tabs with the middle mouse button
|
||||
- `DeleteContentOnClose` flag for dynamic deletion and creation of dock widget
|
||||
content
|
||||
- improved focus highlighting functionality
|
||||
|
||||
The [release 3.7](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.7.2)
|
||||
adds the following features:
|
||||
|
||||
- support for **Qt6.**
|
||||
- support for [empty dock area](doc/user-guide.md#empty-dock-area)
|
||||
|
||||
The [release 3.6](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.6.3)
|
||||
adds some nice new features:
|
||||
|
||||
- support for [central widget](doc/user-guide.md#central-widget) concept
|
||||
|
||||

|
||||
|
||||
- support for [native floating widgets](doc/user-guide.md#floatingcontainerforcenativetitlebar-linux-only) on Linux
|
||||
|
||||

|
||||
|
||||
Both features are contributions from ADS users. Read the [documentation](doc/user-guide.md)
|
||||
to learn more about both new features.
|
||||
|
||||
The [release 3.5](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.5.0)
|
||||
adds the new [focus highlighting](doc/user-guide.md#focushighlighting) feature.
|
||||
This optional feature enables highlighting of the focused dock widget like you
|
||||
know it from Visual Studio.
|
||||
|
||||

|
||||
|
||||
[learn more...](doc/user-guide.md#focushighlighting)
|
||||
|
||||
## Features
|
||||
|
||||
### Overview
|
||||
|
||||
- [New and Noteworthy](#new-and-noteworthy)
|
||||
- [Drag \& Drop to Auto-Hide](#drag--drop-to-auto-hide)
|
||||
- [Auto-Hide Tab Insertion Order](#auto-hide-tab-insertion-order)
|
||||
- [Auto-Hide Tab Sorting](#auto-hide-tab-sorting)
|
||||
- [Auto-Hide Drag to Float / Dock](#auto-hide-drag-to-float--dock)
|
||||
- [Auto-Hide Context Menu](#auto-hide-context-menu)
|
||||
- [Dock Area Tab Insert Order](#dock-area-tab-insert-order)
|
||||
- [Features](#features)
|
||||
- [Overview](#overview)
|
||||
- [Docking everywhere - no central widget](#docking-everywhere---no-central-widget)
|
||||
- [Docking inside floating windows](#docking-inside-floating-windows)
|
||||
- [Grouped dragging](#grouped-dragging)
|
||||
- [Perspectives for fast switching of the complete main window layout](#perspectives-for-fast-switching-of-the-complete-main-window-layout)
|
||||
- [Opaque and non-opaque splitter resizing](#opaque-and-non-opaque-splitter-resizing)
|
||||
- [Cancelable docking process](#cancelable-docking-process)
|
||||
- [Tab-menu for easy handling of many tabbed dock widgets](#tab-menu-for-easy-handling-of-many-tabbed-dock-widgets)
|
||||
- [Many different ways to detach dock widgets](#many-different-ways-to-detach-dock-widgets)
|
||||
- [Supports deletion of dynamically created dock widgets](#supports-deletion-of-dynamically-created-dock-widgets)
|
||||
- [Auto-Hide Functionality](#auto-hide-functionality)
|
||||
- [Python Bindings](#python-bindings)
|
||||
- [PySide6](#pyside6)
|
||||
- [PyQt5](#pyqt5)
|
||||
- [Tested Compatible Environments](#tested-compatible-environments)
|
||||
- [Supported Qt Versions](#supported-qt-versions)
|
||||
- [Windows](#windows)
|
||||
- [macOS](#macos)
|
||||
- [Linux](#linux)
|
||||
- [Build](#build)
|
||||
- [Qt5 on Ubuntu 18.04 or 20.04](#qt5-on-ubuntu-1804-or-2004)
|
||||
- [Qt5 on Ubuntu 22.04](#qt5-on-ubuntu-2204)
|
||||
- [Qt6 on Ubuntu 22.04](#qt6-on-ubuntu-2204)
|
||||
- [Getting started / Example](#getting-started--example)
|
||||
- [License information](#license-information)
|
||||
- [Donation](#donation)
|
||||
- [Showcase](#showcase)
|
||||
- [Qt Creator IDE](#qt-creator-ide)
|
||||
- [Qt Design Studio](#qt-design-studio)
|
||||
- [CETONI Elements](#cetoni-elements)
|
||||
- [ezEditor](#ezeditor)
|
||||
- [D-Tect X](#d-tect-x)
|
||||
- [HiveWE](#hivewe)
|
||||
- [Ramses Composer](#ramses-composer)
|
||||
- [Plot Juggler](#plot-juggler)
|
||||
- [Notepad Next](#notepad-next)
|
||||
- [MetGem](#metgem)
|
||||
- [PRE Workbench](#pre-workbench)
|
||||
- [RDE – Robox Development Environment](#rde--robox-development-environment)
|
||||
- [ResInsight](#resinsight)
|
||||
- [ADTF 3](#adtf-3)
|
||||
- [DREAM.3D NX](#dream3d-nx)
|
||||
- [LabPlot](#labplot)
|
||||
- [Alternative Docking System Implementations](#alternative-docking-system-implementations)
|
||||
- [KDDockWidgets](#kddockwidgets)
|
||||
- [QtitanDocking](#qtitandocking)
|
||||
- [DockingPanes](#dockingpanes)
|
||||
|
||||
### Docking everywhere - no central widget
|
||||
|
||||
There is no central widget like in the Qt docking system. You can dock on every
|
||||
border of the main window or you can dock into each dock area - so you are
|
||||
free to dock almost everywhere.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Docking inside floating windows
|
||||
|
||||
There is no difference between the main window and a floating window. Docking
|
||||
into floating windows is supported.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Grouped dragging
|
||||
|
||||
When dragging the titlebar of a dock, all the tabs that are tabbed with it are
|
||||
going to be dragged. So you can move complete groups of tabbed widgets into
|
||||
a floating widget or from one dock area to another one.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Perspectives for fast switching of the complete main window layout
|
||||
|
||||
A perspective defines the set and layout of dock windows in the main
|
||||
window. You can save the current layout of the dockmanager into a named
|
||||
perspective to make your own custom perspective. Later you can simply
|
||||
select a perspective from the perspective list to quickly switch the complete
|
||||
main window layout.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Opaque and non-opaque splitter resizing
|
||||
|
||||
The advanced docking system uses standard QSplitters as resize separators and thus supports opaque and non-opaque resizing functionality of QSplitter. In some rare cases, for very complex widgets or on slow machines resizing via separator on the fly may cause flicking and glaring of rendered content inside a widget. The global dock manager flag `OpaqueSplitterResize` configures the resizing behaviour of the splitters. If this flag is set, then widgets are resized dynamically (opaquely) while interactively moving the splitters.
|
||||
|
||||

|
||||
|
||||
If this flag is cleared, the widget resizing is deferred until the mouse button is released - this is some kind of lazy resizing separator.
|
||||
|
||||

|
||||
|
||||
### Cancelable docking process
|
||||
|
||||
In contrast to the standard Qt docking system, docking with the ADS works more like a drag & drop operation. That means, the dragged dock widget or dock area is not undocked immediately. Instead, a drag preview widget is created and dragged around to indicate the future position of the dock widget or dock area. The actual dock operation is only executed when the mouse button is released. That makes it possible, to cancel an active drag operation with the escape key.
|
||||
|
||||
The drag preview widget can be configured by a number of global dock manager flags:
|
||||
- `DragPreviewIsDynamic`: if this flag is enabled, the preview will be adjusted dynamically to the drop area
|
||||
- `DragPreviewShowsContentPixmap`: the created drag preview window shows a static copy of the content of the dock widget / dock are that is dragged
|
||||
- `DragPreviewHasWindowFrame`: this flag configures if the drag preview is frameless like a QRubberBand or looks like a real window
|
||||
|
||||
### Tab-menu for easy handling of many tabbed dock widgets
|
||||
|
||||
Tabs are a good way to quickly switch between dockwidgets in a dockarea. However, if the number of dockwidgets in a dockarea is too large, this may affect the usability of the tab bar. To keep track in this situation, you can use the tab menu. The menu allows you to quickly select the dockwidget you want to activate from a drop down menu.
|
||||
|
||||

|
||||
|
||||
### Many different ways to detach dock widgets
|
||||
|
||||
You can detach dock widgets and also dock areas in the following ways:
|
||||
|
||||
- by dragging the dock widget tab or the dock area title bar
|
||||
- by double clicking the tab or title bar
|
||||
- by using the detach menu entry from the tab and title bar drop down menu
|
||||
|
||||
### Supports deletion of dynamically created dock widgets
|
||||
|
||||
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the toggleView() action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
||||
|
||||
### Auto-Hide Functionality
|
||||
|
||||
The 4.0 release of ADS added the new **Auto-Hide** feature. Thanks to the
|
||||
[initial contribution](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/pull/452) by [Ahmad Syarifuddin](https://github.com/SyarifFakhri) it was
|
||||
possible to close this long standing [feature request](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/147). The "Auto Hide" feature
|
||||
allows to display more information using less screen space by hiding or showing
|
||||
windows pinned to one of the four dock container borders.
|
||||
|
||||

|
||||
|
||||
The Advanced Docking
|
||||
System supports "Auto-Hide" functionality for **all** dock containers - that means,
|
||||
for the main window and for each floating widget. Here is short list of all
|
||||
auto hide features:
|
||||
|
||||
- supported for the main window and all floating dock containers
|
||||
- supports showing and hiding via mouse click or mouse hover
|
||||
- respects opaque / non opaque splitter resizing flag
|
||||
- context menu for pinning a dock widget or a complete dock area to a certain border
|
||||
- configuration option to configure if the pin button should pin the current
|
||||
dock widget tab or a complete dock area
|
||||
- click the pin button holding the Ctrl key to pin a complete dock area
|
||||
- fully CSS styleable
|
||||
- backward compatible state file format - is is possible to load older dock manager
|
||||
state files without auto hide support and older versions can load the new state
|
||||
files with Auto-Hide state information
|
||||
|
||||
More about the auto hide configuration options in the [online documentation...](doc/user-guide.md#auto-hide-configuration-flags)
|
||||
|
||||
## Python Bindings
|
||||
|
||||

|
||||
|
||||
Thanks to the contribution of several users, the Advanced Docking System comes
|
||||
with a complete Python integration. Python bindings are available for **PyQt5** and
|
||||
**PySide6**.
|
||||
|
||||
### PySide6
|
||||
|
||||
A PySide6 ADS package is available via PyPi and can be installed on Windows,
|
||||
macOS, and Linux with:
|
||||
|
||||
```bash
|
||||
pip install PySide6-QtAds
|
||||
```
|
||||
|
||||
Sample code is available [here](https://github.com/mborgerson/Qt-Advanced-Docking-System/tree/pyside6/examples). To run the samples, you'll also need to install latest qtpy
|
||||
from source (pip install https://github.com/spyder-ide/qtpy/archive/refs/heads/master.zip).
|
||||
The PySide6 bindings were contributed by:
|
||||
|
||||
- [mborgerson](https://github.com/mborgerson)
|
||||
|
||||
Please file PySide6-QtAds-specific issues on its [pyside6_qtads](https://github.com/mborgerson/pyside6_qtads) fork for tracking. For more information about the PySide6 bindings read [this](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/298) issue.
|
||||
|
||||
### PyQt5
|
||||
|
||||
A package is available via [conda-forge](https://github.com/conda-forge/pyqtads-feedstock).
|
||||
The python integration has been contributed to this project by the following people:
|
||||
|
||||
- [n-elie](https://github.com/n-elie)
|
||||
- [Hugo Slepicka](https://github.com/hhslepicka)
|
||||
- [K Lauer](https://github.com/klauer)
|
||||
|
||||
A Python integration is also available via PyPi. You can install the
|
||||
[PyQtAds](https://pypi.org/project/PyQtAds/) package via pip. This feature has been
|
||||
contributed to this project by:
|
||||
|
||||
- [Mira Weller](https://github.com/luelista)
|
||||
|
||||
## Tested Compatible Environments
|
||||
|
||||
### Supported Qt Versions
|
||||
|
||||
The library supports **Qt5** and **Qt6**.
|
||||
|
||||
### Windows
|
||||
|
||||
Windows 10 [](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
||||
|
||||
The library was developed on and for Windows. It is used in a commercial Windows application and is therefore constantly tested.
|
||||
|
||||
### macOS
|
||||
|
||||
macOS [](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
||||
|
||||
The application can be compiled for macOS. A user reported, that the library works on macOS. If have not tested it.
|
||||
|
||||

|
||||
|
||||
### Linux
|
||||
|
||||
[](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
||||
|
||||
Unfortunately, there is no such thing as a Linux operating system. Linux is a heterogeneous environment with a variety of different distributions. So it is not possible to support "Linux" like it is possible for Windows. It is only possible to support and test a small subset of Linux distributions. The library can be compiled for and has been developed and tested with some Linux distributions. Depending on the used window manager or compositor, dock widgets
|
||||
with native title bars are supported or not. If native title bars are not supported,
|
||||
the library switches to `QWidget` based title bars.
|
||||
|
||||
- **Kubuntu 18.04 and 19.10** - uses KWin - no native title bars
|
||||
- **Ubuntu 18.04, 19.10 and 20.04** - native title bars are supported
|
||||
- **Ubuntu 22.04** - uses Wayland -> no native title bars
|
||||
|
||||
There are some requirements for the Linux distribution that have to be met:
|
||||
|
||||
- an X server that supports ARGB visuals and a compositing window manager. This is required to display the translucent dock overlays ([https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows](https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows)). If your Linux distribution does not support this, or if you disable this feature, you will very likely see issue [#95](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/95).
|
||||
- Wayland is not properly supported by Qt yet. If you use Wayland, then you should set the session type to x11: `XDG_SESSION_TYPE=x11 ./AdvancedDockingSystemDemo`. You will find more details about this in issue [#288](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/288).
|
||||
|
||||
Screenshot Kubuntu:
|
||||

|
||||
|
||||
Screenshot Ubuntu:
|
||||

|
||||
|
||||
## Build
|
||||
|
||||
The Linux build requires private header files. Make sure that they are installed.
|
||||
The library uses SVG icons, so ensure that Qt SVG support is installed. The demo
|
||||
application creates a `QQuickWidget` for testing, so ensure that the required
|
||||
libraries are installed.
|
||||
|
||||
### Qt5 on Ubuntu 18.04 or 20.04
|
||||
|
||||
```bash
|
||||
sudo apt install qt5-default qtbase5-private-dev
|
||||
```
|
||||
|
||||
### Qt5 on Ubuntu 22.04
|
||||
|
||||
```bash
|
||||
sudo apt install qtbase5-dev qtbase5-private-dev qtbase5-dev-tools libqt5svg5 libqt5qml5 qtdeclarative5-dev
|
||||
```
|
||||
|
||||
### Qt6 on Ubuntu 22.04
|
||||
|
||||
```bash
|
||||
sudo apt install qt6-default qt6-base-dev qt6-base-private-dev qt6-tools-dev libqt6svg6 qt6-qtdeclarative
|
||||
```
|
||||
|
||||
Open the `ads.pro` file with QtCreator and start the build, that's it.
|
||||
You can run the demo project and test it yourself.
|
||||
|
||||
## Getting started / Example
|
||||
|
||||
The following example shows the minimum code required to use the advanced Qt docking system.
|
||||
|
||||
*MainWindow.h*
|
||||
|
||||
```cpp
|
||||
#include <QMainWindow>
|
||||
#include "DockManager.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
|
||||
// The main container for docking
|
||||
ads::CDockManager* m_DockManager;
|
||||
};
|
||||
```
|
||||
|
||||
*MainWindow.cpp*
|
||||
|
||||
```cpp
|
||||
#include "MainWindow.h"
|
||||
#include "ui_MainWindow.h"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Create the dock manager after the ui is setup. Because the
|
||||
// parent parameter is a QMainWindow the dock manager registers
|
||||
// itself as the central widget as such the ui must be set up first.
|
||||
m_DockManager = new ads::CDockManager(this);
|
||||
|
||||
// Create example content label - this can be any application specific
|
||||
// widget
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||
|
||||
// Create a dock widget with the title Label 1 and set the created label
|
||||
// as the dock widget content
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget("Label 1");
|
||||
DockWidget->setWidget(l);
|
||||
|
||||
// Add the toggleViewAction of the dock widget to the menu to give
|
||||
// the user the possibility to show the dock widget if it has been closed
|
||||
ui->menuView->addAction(DockWidget->toggleViewAction());
|
||||
|
||||
// Add the dock widget to the top dock widget area
|
||||
m_DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
```
|
||||
|
||||
## License information
|
||||
|
||||
[](gnu-lgpl-v2.1.md)
|
||||
This project uses the [LGPLv2.1 license](gnu-lgpl-v2.1.md)
|
||||
|
||||
## Donation
|
||||
|
||||
If this project help you reduce time to develop or if you just like it, you can give me a cup of coffee :coffee::wink:.
|
||||
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=85R64TMMSY9T6">
|
||||
<img src="doc/donate.png" alt="Donate with PayPal" width="160"/>
|
||||
</a>
|
||||
|
||||
## Showcase
|
||||
|
||||
### [Qt Creator IDE](https://www.qt.io/development-tools)
|
||||
|
||||
From version 4.12 on, Qt Creator uses the Advanced Docking Framework for its
|
||||
Qt Quick Designer. This improves the usability when using multiple screens.
|
||||
|
||||

|
||||
|
||||
### [Qt Design Studio](https://www.qt.io/ui-design-tools)
|
||||
|
||||
Taken from the [Qt Blog](https://www.qt.io/blog/qt-design-studio-1.5-beta-released):
|
||||
|
||||
> The most obvious change in [Qt Design Studio 1.5](https://www.qt.io/blog/qt-design-studio-1.5-beta-released) is the integration of dock widgets using the Qt Advanced Docking System. This allows the user to fully customize the workspace and also to undock any view into its own top level window. This especially improves the usability when using multiple screens.
|
||||
|
||||
[](https://youtu.be/za9KBWcFXEw?t=84)
|
||||
|
||||
### [CETONI Elements](https://cetoni.com/cetoni-elements/)
|
||||
|
||||
The CETONI Elements software from [CETONI](https://www.cetoni.com) is a comprehensive,
|
||||
plugin-based and modular laboratory automation software for controlling CETONI devices using a joint graphical user interface. The software features a powerful script system to automate processes. The software uses the advanced docking system to give the user the freedom to arrange all the views and windows that are provided by the various plugins.
|
||||
|
||||
[learn more...](https://cetoni.com/cetoni-elements/)
|
||||
|
||||
[](https://www.youtube.com/watch?v=7pdNfafg3Qc)
|
||||
|
||||
### [ezEditor](https://github.com/ezEngine/ezEngine)
|
||||
|
||||
The ezEditor is a full blown graphical editor used for editing scenes and
|
||||
importing and authoring assets for the [ezEngine](https://github.com/ezEngine/ezEngine) -
|
||||
an open source C++ game engine in active development.
|
||||
|
||||

|
||||
|
||||
### [D-Tect X](https://www.duerr-ndt.com/products/ndt-software/d-tect-xray-inspection-software.html)
|
||||
|
||||
D-Tect X is a X-ray inspection software for industrial radiography. It is a state-of-the-art 64-bit application which supports GPU (Graphics Processing Unit) acceleration and takes full advantage of computers with multiple CPU cores. A large set of tools assist the user in image analysis and evaluation. Thanks to the Qt Advanced Docking System the flexible and intuitive user interface can be completely customized to each user’s preference.
|
||||
|
||||
[learn more...](https://www.duerr-ndt.com/products/ndt-software/d-tect-xray-inspection-software.html)
|
||||
|
||||
[](https://youtu.be/mOor7GmmIJo?t=13)
|
||||
|
||||
### [HiveWE](https://github.com/stijnherfst/HiveWE)
|
||||
|
||||
HiveWE is a Warcraft III world editor. It focusses on speed and ease of use,
|
||||
especially for large maps where the regular World Editor is often too slow and clunky.
|
||||
It has a JASS editor with syntax highlighting, tabs, code completion and more.
|
||||
The JASS editor uses the Qt Advanced Docking System for the management and layout
|
||||
of the open editor windows.
|
||||
|
||||
[learn more...](https://github.com/stijnherfst/HiveWE)
|
||||
|
||||

|
||||
|
||||
### [Ramses Composer](https://github.com/GENIVI/ramses-composer)
|
||||
|
||||
Ramses Composer is the authoring tool for the open source [RAMSES](https://github.com/GENIVI/ramses)
|
||||
rendering ecosystem.
|
||||
|
||||
Ramses is a low-level rendering engine which is optimized for embedded hardware
|
||||
mobile devices, automotive ECUs, IoT electronics. Ramses was initially developed
|
||||
at the BMW Group and open-sourced in 2018 as part of a collaboration initiative
|
||||
with the Genivi Alliance. It is an important part of the BMW infotainment cluster
|
||||
and digital portfolio.
|
||||
|
||||
[learn more...](https://github.com/GENIVI/ramses-composer)
|
||||
|
||||

|
||||
|
||||
### [Plot Juggler](https://github.com/facontidavide/PlotJuggler)
|
||||
|
||||
PlotJuggler is a fast, powerful and intuitive tool to visualize time series.
|
||||
It makes it easy to visualize data but also to analyze it. You can manipulate
|
||||
your time series using a simple and extendable Transform Editor. Some of the
|
||||
highlights are:
|
||||
|
||||
- Simple Drag & Drop user interface.
|
||||
- Load data from file.
|
||||
- Connect to live streaming of data.
|
||||
- Save the visualization layout and configurations to re-use them later.
|
||||
- Fast OpenGL visualization.
|
||||
- Can handle thousands of timeseries and millions of data points.
|
||||
- Transform your data using a simple editor: derivative, moving average, integral, etc…
|
||||
- PlotJuggler can be easily extended using plugins.
|
||||
|
||||
[read more...](https://github.com/facontidavide/PlotJuggler)
|
||||
|
||||
[](https://vimeo.com/480588113#t=46s)
|
||||
|
||||
### [Notepad Next](https://github.com/dail8859/NotepadNext)
|
||||
|
||||
Notepad Next is a cross-platform reimplementation of Notepad++ that uses the
|
||||
Advanced Docking System to arrange the open source files on the screen.
|
||||
|
||||
[read more...](https://github.com/dail8859/NotepadNext)
|
||||
|
||||

|
||||
|
||||
### [MetGem](https://metgem.github.io/)
|
||||
|
||||
MetGem is an open-source software for tandem mass-spectrometry data visualization.
|
||||
It's key features are standalone molecular networking and t-SNE based projections.
|
||||
MetGem uses the Qt-Advanced-Docking-System to manage docks and to create independent
|
||||
molecular network views.
|
||||
|
||||
[read more...](https://metgem.github.io/)
|
||||
|
||||

|
||||
|
||||
### [PRE Workbench](https://luelista.github.io/pre_workbench/)
|
||||
|
||||
Protocol Reverse Engineering Workbench is a software to support researchers in reverse engineering protocols and documenting the results. It supports various sources to import protocol traffic from, helps the discovery process by displaying different views and heuristic-based highlighting on data, and aids in documenting and sharing findings.
|
||||
|
||||
PRE Workbench is a Python software and uses the ADS PyQt integration.
|
||||
|
||||
[read more...](https://luelista.github.io/pre_workbench/)
|
||||
|
||||
[](https://youtu.be/U3op5UreV1Q)
|
||||
|
||||
### [RDE – Robox Development Environment](https://www.robox.it/en/product/rde-robox-development-environment/)
|
||||
|
||||
This software is a development environment for PAC (Programmable Automation Controllers)
|
||||
from ROBOX. It offers a lot of tools to write, compile and debug machine control
|
||||
and application software. The Advanced Docking System helps to organize all the tools and
|
||||
windows (Project window, Shell window, Monitor windows, Oscilloscope window...)
|
||||
on the screen to provide a easy to use, highly configurable and visual pleasing
|
||||
development experience.
|
||||
|
||||
[read more...](https://www.robox.it/en/product/rde-robox-development-environment/)
|
||||
|
||||

|
||||
|
||||
### [ResInsight](https://www.ceetronsolutions.com/projects/resinsight)
|
||||
|
||||
ResInsight as a software from Ceetron Solutions for visualization of oil and
|
||||
gas reservoir simulation data. It allows reservoir models, simulation results,
|
||||
and measurements to be visualized with very high performance. Optimized use of
|
||||
graphics technology and simultaneous processing on multiple CPU cores have been
|
||||
vital to enhance the performance and capacity of ResInsight for large data sets.
|
||||
The Advanced Docking System has empowered Ceetron to build a much more intuitive
|
||||
user interface for its ResInsight users.
|
||||
|
||||
[read more...](https://resinsight.org/)
|
||||
|
||||
[](https://www.youtube.com/watch?v=HzLaQ1p6AUc)
|
||||
|
||||
### [ADTF 3](https://www.digitalwerk.net/adtf/)
|
||||
|
||||
The Automotive Data and Time-Triggered Framework was designed as a Rapid Prototyping Toolset, Simulation Framework and Test- and Measurement Tool. It is meant for:
|
||||
|
||||
- Developing and testing ADAS and HAD components
|
||||
- Recording of vehicle data for visualisation
|
||||
- Simulation of complex scenarios in SIL/HIL test environments
|
||||
|
||||
The software features time-based processing of multiple data streams and graphical editing of dynamic filter graphs. It also includes an SDK for custom plug-ins and reusable components, as well as components for data visualization in both 2D and 3D. This is was the
|
||||
[manual](https://support.digitalwerk.net/adtf/v3/adtf_html/page_adtf_xsystem_plugin.html)
|
||||
says about the switch to Qt Advanced Docking:
|
||||
|
||||
> After several minor improvements the Qt5 ADTF XSystem uses the Advanced Docking System for Qt since ADTF 3.10.0 for more convenience and usability regarding layouting, docking and embedding several widgets.
|
||||
|
||||
[read more...](https://support.digitalwerk.net/adtf/v3/adtf_html/index.html)
|
||||
|
||||

|
||||
|
||||
### [DREAM.3D NX](https://github.com/BlueQuartzSoftware/DREAM3D)
|
||||
|
||||
DREAM.3D *(Digital Representation Environment for Analysis of Materials in 3D)* is an open source, cross-platform and modular, software suite that allows users to prepare, reconstruct, quantify, instantiate, and mesh, multidimensional, multimodal microstructural data, as well as many other applications.
|
||||
|
||||
[BlueQuartz Software](http://www.bluequartz.net/) is currently completely rewriting the DREAM.3D application. For the upcoming version **[DREAM3D NX](http://www.dream3d.io/)** they improved the UI by using the Advanced Docking System. An [early version](http://www.dream3d.io/) of **DREAM3D NX** with ADS is already available to any user who would like to take the brand new version out for a spin.
|
||||
|
||||

|
||||
|
||||
[read more...](http://dream3d.bluequartz.net/)
|
||||
|
||||
### [LabPlot](https://labplot.kde.org/)
|
||||
|
||||
KDE LabPlot is the ultimate free, open source and cross-platform tool for scientists, engineers, and students who need to analyze and visualize data. With its intuitive interface and powerful features, you can create stunning plots and diagrams with ease. Whether you're working with CSV, FITS, or HDF5 data, KDE LabPlot makes it simple to import and analyze your data.
|
||||
|
||||
The LabPlot project recently switched to the Qt Advanced Docking System for their user interface. This switch represents a significant improvement to the LabPlot software, allowing users to create and manage complex data visualization layouts with ease.
|
||||
|
||||

|
||||
|
||||
[read more...](https://labplot.kde.org/)
|
||||
|
||||
## Alternative Docking System Implementations
|
||||
|
||||
If this Qt Advanced Docking System does not fit to your needs you may consider some of the alternative docking system solutions for Qt.
|
||||
|
||||
### KDDockWidgets
|
||||
|
||||
This is an advanced docking framework for Qt from [KDAB](https://www.kdab.com/). The interesting thing is, that they separated GUI code from logic, so they can easily provide a QtQuick backend in the future.
|
||||
|
||||
- [Blog post about KDDockWidgets](https://www.kdab.com/kddockwidgets/)
|
||||
- [GitHub project](https://github.com/KDAB/KDDockWidgets)
|
||||
|
||||
**License:** dual-licensed, available under both commercial and GPL license.
|
||||
|
||||
### QtitanDocking
|
||||
|
||||
This is a commercial component from [Developer Machines](https://www.devmachines.com/) for Qt Framework that allows to create a Microsoft like dockable user interface. They also offer a lot of other interesting and useful components for Qt. The library is available
|
||||
|
||||
- [Product page](https://www.devmachines.com/qtitandocking-overview.html)
|
||||
|
||||
**License:** Commercial license
|
||||
|
||||
### DockingPanes
|
||||
|
||||
DockingPanes is a library for Qt Widgets that implements docking windows that have the look and feel of Visual Studio. It provides a simple API which allows an application to make use of docking windows with a few calls.
|
||||
|
||||
- [GitHub project](https://github.com/KestrelRadarSensors/dockingpanes)
|
||||
|
||||
**License:** GPL
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
CONFIG(debug, debug|release){
|
||||
win32-g++ {
|
||||
versionAtLeast(QT_VERSION, 5.15.0) {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
}
|
||||
else:msvc {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
|
||||
unix:!macx {
|
||||
LIBS += -lxcb
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
src \
|
||||
demo \
|
||||
examples
|
||||
|
||||
demo.depends = src
|
||||
examples.depends = src
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(Qt5Core ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_dependency(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_dependency(Qt5Widgets ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/adsTargets.cmake")
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# git_local_changes(<var>)
|
||||
#
|
||||
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
|
||||
# Uses the return code of "git diff-index --quiet HEAD --".
|
||||
# Does not regard untracked files.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
||||
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
||||
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
||||
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
endwhile()
|
||||
# check if this is a submodule
|
||||
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||
endif()
|
||||
if(NOT IS_DIRECTORY "${GIT_DIR}")
|
||||
file(READ ${GIT_DIR} worktree)
|
||||
string(REGEX REPLACE "gitdir: (.*)worktrees(.*)\n$" "\\1" GIT_DIR ${worktree})
|
||||
endif()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake"
|
||||
@ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
git_describe(out --exact-match ${ARGN})
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_local_changes _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(res EQUAL 0)
|
||||
set(${_var} "CLEAN" PARENT_SCOPE)
|
||||
else()
|
||||
set(${_var} "DIRTY" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
cmake_minimum_required(VERSION 3.5...4.3)
|
||||
project(ads_demo VERSION ${VERSION_SHORT})
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets Quick QuickWidgets REQUIRED)
|
||||
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS AxContainer REQUIRED)
|
||||
endif()
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(ads_demo_SRCS
|
||||
main.cpp
|
||||
MainWindow.cpp
|
||||
mainwindow.ui
|
||||
StatusDialog.cpp
|
||||
StatusDialog.ui
|
||||
ImageViewer.cpp
|
||||
RenderWidget.cpp
|
||||
demo.qrc
|
||||
)
|
||||
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
|
||||
target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src")
|
||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::Quick
|
||||
Qt${QT_VERSION_MAJOR}::QuickWidgets)
|
||||
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
|
||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::AxContainer)
|
||||
endif()
|
||||
target_link_libraries(AdvancedDockingSystemDemo PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
||||
set_target_properties(AdvancedDockingSystemDemo PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
VERSION ${VERSION_SHORT}
|
||||
EXPORT_NAME "Qt Advanced Docking System Demo"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin/ads"
|
||||
)
|
||||
#if(BUILD_STATIC)
|
||||
# target_compile_definitions(AdvancedDockingSystemDemo PRIVATE ADS_STATIC)
|
||||
#endif()
|
||||
|
||||