diff --git a/CMakeLists.txt b/CMakeLists.txt index a54b9f1..a8d0d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,3 +14,4 @@ add_subdirectory(ExampleTransformation) add_subdirectory(ExampleLoader) add_subdirectory(ExampleWriter) add_subdirectory(ExampleData) +add_subdirectory(ExampleActions) diff --git a/ExampleActions/CMakeLists.txt b/ExampleActions/CMakeLists.txt new file mode 100644 index 0000000..47816b8 --- /dev/null +++ b/ExampleActions/CMakeLists.txt @@ -0,0 +1,139 @@ +cmake_minimum_required(VERSION 3.17) + +option(MV_UNITY_BUILD "Combine target source files into batches for faster compilation" OFF) + +# ----------------------------------------------------------------------------- +# ExampleView Plugin +# ----------------------------------------------------------------------------- +PROJECT("ExampleActionsPlugin") + +# ----------------------------------------------------------------------------- +# CMake Options +# ----------------------------------------------------------------------------- +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) + +if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DWIN32 /EHsc /MP /permissive- /Zc:__cplusplus") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:LIBCMT") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") +endif(MSVC) + +# ----------------------------------------------------------------------------- +# Set install directory +# ----------------------------------------------------------------------------- +# Check if the directory to the ManiVault installation has been provided +if(NOT DEFINED MV_INSTALL_DIR) + set(MV_INSTALL_DIR "" CACHE PATH "Directory where ManiVault is installed") + message(FATAL_ERROR "Please set MV_INSTALL_DIR to the directory where ManiVault is installed") +endif() +file(TO_CMAKE_PATH ${MV_INSTALL_DIR} MV_INSTALL_DIR) + +# ----------------------------------------------------------------------------- +# Dependencies +# ----------------------------------------------------------------------------- +find_package(Qt6 COMPONENTS Widgets WebEngineWidgets REQUIRED) + +# ----------------------------------------------------------------------------- +# Source files +# ----------------------------------------------------------------------------- +# Define the plugin sources +set(PLUGIN_SOURCES + src/ExampleActionsPlugin.h + src/ExampleActionsPlugin.cpp + src/ExampleActionsPlugin.json +) + +set(MODEL + src/AbstractExampleActionsModel.h + src/AbstractExampleActionsModel.cpp + src/ExampleActionsTreeModel.h + src/ExampleActionsTreeModel.cpp + src/ExampleActionsFilterModel.h + src/ExampleActionsFilterModel.cpp +) + +set(ACTIONS + src/ExampleProxyAction.h + src/ExampleProxyAction.cpp +) + +set(PLUGIN_MOC_HEADERS + src/ExampleActionsPlugin.h + src/ExampleActionsTreeModel.h +) + +source_group(Plugin FILES ${PLUGIN_SOURCES}) +source_group(Model FILES ${MODEL}) +source_group(Actions FILES ${ACTIONS}) + +# ----------------------------------------------------------------------------- +# CMake Target +# ----------------------------------------------------------------------------- +# Create dynamic library for the plugin +add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES} ${MODEL} ${ACTIONS}) + +# ----------------------------------------------------------------------------- +# Target include directories +# ----------------------------------------------------------------------------- +# Include ManiVault headers, including system data plugins +target_include_directories(${PROJECT_NAME} PRIVATE "${MV_INSTALL_DIR}/$/include/") + +# ----------------------------------------------------------------------------- +# Target properties +# ----------------------------------------------------------------------------- +# Request C++17 +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + +# Enable unity build +if(MV_UNITY_BUILD) + set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD ON) +endif() + +# ----------------------------------------------------------------------------- +# Target library linking +# ----------------------------------------------------------------------------- +# Link to Qt libraries +target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::WebEngineWidgets) + +# Link to ManiVault and data plugins +# The link path in this repo assumes that the ManiVault core was built locally +# in contrast to having been installed with an installer. In the latter case you'll have +# to adapt the MV_LINK_PATH and PLUGIN_LINK_PATH to your install folder +set(MV_LINK_PATH "${MV_INSTALL_DIR}/$/lib") +set(PLUGIN_LINK_PATH "${MV_INSTALL_DIR}/$/$,lib,Plugins>") +set(MV_LINK_SUFFIX $,${CMAKE_LINK_LIBRARY_SUFFIX},${CMAKE_SHARED_LIBRARY_SUFFIX}>) + +set(MV_LINK_LIBRARY "${MV_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}MV_Public${MV_LINK_SUFFIX}") +set(POINTDATA_LINK_LIBRARY "${PLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}PointData${MV_LINK_SUFFIX}") + +target_link_libraries(${PROJECT_NAME} PRIVATE "${MV_LINK_LIBRARY}") +target_link_libraries(${PROJECT_NAME} PRIVATE "${POINTDATA_LINK_LIBRARY}") + +# ----------------------------------------------------------------------------- +# Target installation +# ----------------------------------------------------------------------------- +# Install the shared plugin libary to the "Plugins" folder in the ManiVault install directory +install(TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION Plugins COMPONENT PLUGINS # Windows .dll + LIBRARY DESTINATION Plugins COMPONENT PLUGINS # Linux/Mac .so +) + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" + --install ${CMAKE_CURRENT_BINARY_DIR} + --config $ + --prefix ${MV_INSTALL_DIR}/$ +) + +# ----------------------------------------------------------------------------- +# Miscellaneous +# ----------------------------------------------------------------------------- +# Automatically set the debug environment (command + working directory) for MSVC +if(MSVC) + set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $,${MV_INSTALL_DIR}/debug,${MV_INSTALL_DIR}/release>) + set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_COMMAND $,${MV_INSTALL_DIR}/debug/ManiVault\ Studio.exe,${MV_INSTALL_DIR}/release/ManiVault\ Studio.exe>) +endif() diff --git a/ExampleActions/src/.editorconfig b/ExampleActions/src/.editorconfig new file mode 100644 index 0000000..7eddc72 --- /dev/null +++ b/ExampleActions/src/.editorconfig @@ -0,0 +1,10 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +root = true + +# All files +[*] +indent_style = space + +# Cpp files +[*.{h,cpp}] +indent_size = 4 diff --git a/ExampleActions/src/AbstractExampleActionsModel.cpp b/ExampleActions/src/AbstractExampleActionsModel.cpp new file mode 100644 index 0000000..8f86e3f --- /dev/null +++ b/ExampleActions/src/AbstractExampleActionsModel.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "AbstractExampleActionsModel.h" + +#include + +#include + +using namespace mv; +using namespace mv::util; +using namespace mv::gui; + +#ifdef _DEBUG + #define ABSTRACT_EXAMPLE_ACTIONS_MODEL_VERBOSE +#endif + +AbstractExampleActionsModel::ExampleActionItem::ExampleActionItem(const QString& title, mv::gui::WidgetAction* action) : + QStandardItem(title), + QObject(), + _action(action) +{ + setEditable(false); + setDropEnabled(false); + + Q_ASSERT(_action != nullptr); + + if (!_action) + return; +} + +mv::gui::WidgetAction* AbstractExampleActionsModel::ExampleActionItem::getAction() const +{ + return _action; +} diff --git a/ExampleActions/src/AbstractExampleActionsModel.h b/ExampleActions/src/AbstractExampleActionsModel.h new file mode 100644 index 0000000..028ae7a --- /dev/null +++ b/ExampleActions/src/AbstractExampleActionsModel.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include + +#include + +namespace mv::gui { + class WidgetAction; +} + +/** + * Abstract example actions model class + * + * @author Thomas Kroes + */ +class AbstractExampleActionsModel : public mv::StandardItemModel +{ +protected: + + /** Base standard model item class for example widget action */ + class ExampleActionItem : public QStandardItem, public QObject { + public: + + /** + * Construct with pointer to \p action + * @param title Item title + * @param action Pointer to action to display item for + */ + ExampleActionItem(const QString& title, mv::gui::WidgetAction* action); + + /** + * Get action + * return Pointer to action to display item for + */ + mv::gui::WidgetAction* getAction() const; + + private: + mv::gui::WidgetAction* _action; /** Pointer to action to display item for */ + }; + +public: + + /** No need for custom constructor */ + using mv::StandardItemModel::StandardItemModel; + + friend class ExampleActionStyledItemDelegate; + friend class ExampleActionsPlugin; +}; diff --git a/ExampleActions/src/ExampleActionsFilterModel.cpp b/ExampleActions/src/ExampleActionsFilterModel.cpp new file mode 100644 index 0000000..9f458f0 --- /dev/null +++ b/ExampleActions/src/ExampleActionsFilterModel.cpp @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "ExampleActionsFilterModel.h" + +#include + +#ifdef _DEBUG + #define EXAMPLE_ACTIONS_FILTER_MODEL_VERBOSE +#endif + +ExampleActionsFilterModel::ExampleActionsFilterModel(QObject* parent /*= nullptr*/) : + SortFilterProxyModel(parent) +{ + setRecursiveFilteringEnabled(true); +} diff --git a/ExampleActions/src/ExampleActionsFilterModel.h b/ExampleActions/src/ExampleActionsFilterModel.h new file mode 100644 index 0000000..198f30a --- /dev/null +++ b/ExampleActions/src/ExampleActionsFilterModel.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include + +/** + * Example actions filter model class + * + * Sorting and filtering model for example actions + * + * @author Thomas Kroes + */ +class ExampleActionsFilterModel : public mv::SortFilterProxyModel +{ +public: + + /** + * Construct the filter model with \p parent + * @param parent Pointer to parent object + */ + ExampleActionsFilterModel(QObject* parent = nullptr); +}; diff --git a/ExampleActions/src/ExampleActionsPlugin.cpp b/ExampleActions/src/ExampleActionsPlugin.cpp new file mode 100644 index 0000000..3ee34e8 --- /dev/null +++ b/ExampleActions/src/ExampleActionsPlugin.cpp @@ -0,0 +1,104 @@ +#include "ExampleActionsPlugin.h" +#include "ExampleProxyAction.h" + +#include + +#include +#include +#include + +Q_PLUGIN_METADATA(IID "studio.manivault.ExampleActionsPlugin") + +using namespace mv; + +ExampleActionsPlugin::ExampleActionsPlugin(const PluginFactory* factory) : + ViewPlugin(factory), + _exampleActionsTreeModel(this), + _exampleActionsFilterModel(this), + _examplesTreeAction(this, "Examples tree"), + _examplesGroupsAction(this, "Examples groups") +{ + _examplesTreeAction.setIconByName("play"); + + _examplesTreeAction.setWidgetConfigurationFunction([this](WidgetAction* action, QWidget* widget) -> void { + auto hierarchyWidget = widget->findChild("HierarchyWidget"); + + Q_ASSERT(hierarchyWidget); + + if (!hierarchyWidget) + return; + + auto& treeView = hierarchyWidget->getTreeView(); + + //treeView.setRootIsDecorated(false); + treeView.setTextElideMode(Qt::ElideMiddle); + + treeView.setColumnHidden(static_cast(ExampleActionsTreeModel::Column::ClassName), true); + + auto treeViewHeader = treeView.header(); + + treeViewHeader->setSectionResizeMode(static_cast(ExampleActionsTreeModel::Column::Name), QHeaderView::Stretch); + treeViewHeader->setSectionResizeMode(static_cast(ExampleActionsTreeModel::Column::Description), QHeaderView::Stretch); + + auto selectionModel = hierarchyWidget->getTreeView().selectionModel(); + + connect(selectionModel, &QItemSelectionModel::selectionChanged, hierarchyWidget, [this, selectionModel](const QItemSelection& selected, const QItemSelection& deselected) -> void { + _examplesGroupsAction.resetGroupActions(); + + QSet selectedModelIndexes; + + for (const auto& selectedIndex : selectionModel->selectedRows(static_cast(ExampleActionsTreeModel::Column::ClassName))) { + const auto nameIndex = selectedIndex.siblingAtColumn(static_cast(ExampleActionsTreeModel::Column::Name)); + + if (nameIndex.parent().isValid()) { + selectedModelIndexes << selectedIndex; + } + else { + for (int rowIndex = 0; rowIndex < _exampleActionsFilterModel.rowCount(nameIndex); rowIndex++) + selectedModelIndexes << _exampleActionsFilterModel.index(rowIndex, static_cast(ExampleActionsTreeModel::Column::ClassName), nameIndex); + } + } + + for (const auto& selectedModelIndex : selectedModelIndexes) { + auto actionItem = _exampleActionsTreeModel.itemFromIndex(_exampleActionsFilterModel.mapToSource(selectedModelIndex)); + auto action = dynamic_cast(actionItem)->getAction(); + auto name = QString("%1/%2").arg(selectedModelIndex.parent().data().toString(), selectedModelIndex.data().toString()); + auto verticalGroupAction = new VerticalGroupAction(this, name, true); + + verticalGroupAction->setDefaultWidgetFlag(VerticalGroupAction::WidgetFlag::NoMargins); + verticalGroupAction->setShowLabels(false); + verticalGroupAction->addAction(new ExampleProxyAction(verticalGroupAction, "ExampleProxy", action)); + + _examplesGroupsAction.addGroupAction(verticalGroupAction); + } + }); + }); +} + +void ExampleActionsPlugin::init() +{ + auto layout = new QVBoxLayout(); + + layout->setContentsMargins(0, 0, 0, 0); + + _examplesTreeAction.initialize(&_exampleActionsTreeModel, &_exampleActionsFilterModel, "Example action"); + + auto splitter = new QSplitter(); + + splitter->setOrientation(Qt::Orientation::Horizontal); + + splitter->addWidget(_examplesTreeAction.createWidget(&getWidget())); + splitter->addWidget(_examplesGroupsAction.createWidget(&getWidget())); + + splitter->setStretchFactor(0, 1); + splitter->setStretchFactor(1, 3); + + layout->addWidget(splitter); + + getWidget().setLayout(layout); +} + +ViewPlugin* ExampleActionsPluginFactory::produce() +{ + return new ExampleActionsPlugin(this); +} diff --git a/ExampleActions/src/ExampleActionsPlugin.h b/ExampleActions/src/ExampleActionsPlugin.h new file mode 100644 index 0000000..7f826c1 --- /dev/null +++ b/ExampleActions/src/ExampleActionsPlugin.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include +#include + +#include "ExampleActionsTreeModel.h" +#include "ExampleActionsFilterModel.h" + +#include +#include + +/** All plugin related classes are in the ManiVault plugin namespace */ +using namespace mv::plugin; + +/** Drop widget used in this plugin is located in the ManiVault gui namespace */ +using namespace mv::gui; + +/** Dataset reference used in this plugin is located in the ManiVault util namespace */ +using namespace mv::util; + +/** + * Example actions view plugin class + * + * This view plugin class provides skeleton code that shows how to use GUI building + * blocks (aka actions) to build interfaces. + * + * To see the plugin in action, please follow the steps below: + * + * 1. Go to the visualization menu in ManiVault + * 2. Choose the Example actions menu item, the view will be added to the layout + * + * @author T. Kroes + */ +class ExampleActionsPlugin : public ViewPlugin +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to plugin \p factory + * @param factory Pointer to the plugin factory + */ + ExampleActionsPlugin(const PluginFactory* factory); + + /** Destructor */ + ~ExampleActionsPlugin() override = default; + + /** This function is called by the core after the view plugin has been created */ + void init() override; + +protected: + ExampleActionsTreeModel _exampleActionsTreeModel; /** Standard item model which stores example actions grouped by category */ + ExampleActionsFilterModel _exampleActionsFilterModel; /** Sort/filter proxy model for ExampleActionsPlugin#_exampleActionsTreeModel */ + mv::gui::TreeAction _examplesTreeAction; /** For displaying the ExampleActionsPlugin#_exampleActionsFilterModel */ + mv::gui::GroupsAction _examplesGroupsAction; /** For displaying the ExampleActionsPlugin#_exampleActionsListModel */ + + friend class ExampleActionStyledItemDelegate; +}; + +/** + * Example actions plugin factory class + * + * Note: Factory does not need to be altered (merely responsible for generating new plugins when requested) + */ +class ExampleActionsPluginFactory : public ViewPluginFactory +{ + Q_INTERFACES(mv::plugin::ViewPluginFactory mv::plugin::PluginFactory) + Q_OBJECT + Q_PLUGIN_METADATA(IID "studio.manivault.ExampleActionsPlugin" + FILE "ExampleActionsPlugin.json") + +public: + + /** Default constructor */ + ExampleActionsPluginFactory() {} + + /** Destructor */ + ~ExampleActionsPluginFactory() override {} + + /** Creates an instance of the example view plugin */ + ViewPlugin* produce() override; +}; diff --git a/ExampleActions/src/ExampleActionsPlugin.json b/ExampleActions/src/ExampleActionsPlugin.json new file mode 100644 index 0000000..dffc5ec --- /dev/null +++ b/ExampleActions/src/ExampleActionsPlugin.json @@ -0,0 +1,5 @@ +{ + "name" : "Example Actions", + "version" : "1.1", + "dependencies" : ["Points"] +} diff --git a/ExampleActions/src/ExampleActionsTreeModel.cpp b/ExampleActions/src/ExampleActionsTreeModel.cpp new file mode 100644 index 0000000..adcde3b --- /dev/null +++ b/ExampleActions/src/ExampleActionsTreeModel.cpp @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "ExampleActionsTreeModel.h" + +#include + +#include + +using namespace mv; +using namespace mv::util; +using namespace mv::gui; + +#ifdef _DEBUG + #define EXAMPLE_ACTIONS_TREE_MODEL_VERBOSE +#endif + +ExampleActionsTreeModel::ExampleActionsTreeModel(QObject* parent /*= nullptr*/) : + AbstractExampleActionsModel(parent) +{ + setColumnCount(static_cast(Column::Count)); + + appendRow(CategoryRow("Color", { + { "Color action", "For picking colors", "mv::gui::ColorAction" }, + { "1D Color map action", "For configuring one-dimensional color maps", "mv::gui::ColorMap1DAction" }, + { "2D Color map action", "For configuring two-dimensional color maps", "mv::gui::ColorMap2DAction" } + })); + + appendRow(CategoryRow("File", { + { "File picker", "For picking file location", "mv::gui::FilePickerAction" }, + { "Directory picker", "For picking directories", "mv::gui::DirectoryPickerAction" } + })); + + appendRow(CategoryRow("Grouping", { + { "Horizontal group", "For laying out actions horizontally", "mv::gui::HorizontalGroupAction" }, + { "Vertical group", "For laying out actions vertically", "mv::gui::VerticalGroupAction" }, + { "Groups", "Vertical groups accordion action", "mv::gui::GroupsAction" }, + { "Horizontal toolbar", "Horizontal group action that changes the state of action based on the available width ", "mv::gui::HorizontalToolbarAction" }, + { "Stretch", "Adds stretch to a group action", "mv::gui::StretchAction" } + })); + + appendRow(CategoryRow("Miscellaneous", { + { "Dataset picker", "For picking datasets", "mv::gui::DatasetPickerAction" } + })); + + appendRow(CategoryRow("Numerical", { + { "Integral", "For configuring an integral value", "mv::gui::IntegralAction" }, + { "IntegralRange", "For configuring an integral range", "mv::gui::IntegralRangeAction" }, + { "Decimal", "For configuring a decimal value", "mv::gui::DecimalAction" }, + { "DecimalRange", "For configuring a decimal range", "mv::gui::DecimalRangeAction" }, + })); +} + +QVariant ExampleActionsTreeModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const +{ + switch (static_cast(section)) + { + case Column::Name: + return "Name"; + + case Column::Description: + return "Description"; + + case Column::ClassName: + return "Class name"; + + default: + break; + } + + return {}; +} + +ExampleActionsTreeModel::CategoryRow::CategoryRow(const QString& category, const QList& actions) : QList() +{ + auto categoryItem = new QStandardItem(category); + + for (const auto& action : actions) { + QList items; + + try { + for (const auto& string : action) { + + const auto metaType = action[2]; + const auto metaTypeId = QMetaType::type(metaType.toLatin1()); + const auto metaObject = QMetaType::metaObjectForType(metaTypeId); + + if (!metaObject) + throw std::runtime_error(QString("Meta object type '%1' is not known. Did you forget to register the action correctly with Qt meta object system? See ToggleAction.h for an example.").arg(metaType).toLatin1()); + + auto metaObjectInstance = metaObject->newInstance(Q_ARG(QObject*, nullptr), Q_ARG(QString, QString("Example%1").arg(metaType))); + auto exampleAction = dynamic_cast(metaObjectInstance); + + if (!exampleAction) + throw std::runtime_error(QString("Unable to create a new instance of type '%1'").arg(metaType).toLatin1()); + + items << new ExampleActionItem(string, exampleAction); + } + } + catch (std::exception& e) + { + exceptionMessageBox("Unable to load example action:", e); + } + catch (...) + { + exceptionMessageBox("Unable to load example action:"); + } + + categoryItem->appendRow(items); + } + + append(categoryItem); +} diff --git a/ExampleActions/src/ExampleActionsTreeModel.h b/ExampleActions/src/ExampleActionsTreeModel.h new file mode 100644 index 0000000..caae898 --- /dev/null +++ b/ExampleActions/src/ExampleActionsTreeModel.h @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "AbstractExampleActionsModel.h" + +#include +#include + +/** + * Example actions tree model class + * + * Example actions grouped by category + * + * @author Thomas Kroes + */ +class ExampleActionsTreeModel : public AbstractExampleActionsModel +{ +public: + + /** Model columns */ + enum class Column { + Name, /** Item name */ + Description, /** Action/category description */ + ClassName, /** Action class name */ + + Count + }; + +protected: + + /** Convenience class for category row and child action items */ + class CategoryRow final : public QList + { + public: + + /** + * Construct with \p name, \p description and \p className + * @param category Action category + * @param actions Child actions + */ + CategoryRow(const QString& category, const QList& actions); + }; + +public: + + /** + * Construct with pointer to \p parent object + * @param parent Pointer to parent object + */ + ExampleActionsTreeModel(QObject* parent = nullptr); + + /** + * Get header data for \p section, \p orientation and display \p role + * @param section Section + * @param orientation Orientation + * @param role Data role + * @return Header + */ + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + friend class ExampleActionStyledItemDelegate; +}; diff --git a/ExampleActions/src/ExampleProxyAction.cpp b/ExampleActions/src/ExampleProxyAction.cpp new file mode 100644 index 0000000..93f07b7 --- /dev/null +++ b/ExampleActions/src/ExampleProxyAction.cpp @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "ExampleProxyAction.h" + +#include +#include + +using namespace mv::gui; + +ExampleProxyAction::ExampleProxyAction(QObject* parent, const QString& title, WidgetAction* action /*= nullptr*/) : + WidgetAction(parent, title), + _action(action) +{ +} + +QWidget* ExampleProxyAction::getWidget(QWidget* parent, const std::int32_t& widgetFlags) +{ + auto widget = new WidgetActionWidget(parent, this); + auto layout = new QHBoxLayout(); + + //layout->setContentsMargins(0, 0, 0, 0); + + if (_action) { + auto exampleActionWidget = _action->createExampleWidget(widget); + + if (exampleActionWidget) + layout->addWidget(exampleActionWidget); + else + layout->addWidget(new QLabel("Coming soon...")); + } + + widget->setLayout(layout); + + return widget; +} diff --git a/ExampleActions/src/ExampleProxyAction.h b/ExampleActions/src/ExampleProxyAction.h new file mode 100644 index 0000000..51bd4d1 --- /dev/null +++ b/ExampleActions/src/ExampleProxyAction.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include + +/** + * Example proxy action class + * + * Uses target action createExampleWidget(...) in stead of createWidget() + * + * @author Thomas Kroes + */ +class ExampleProxyAction : public mv::gui::WidgetAction +{ + Q_OBJECT + +protected: + + /** + * Get widget representation of the proxy action + * @param parent Pointer to parent widget + * @param widgetFlags Widget flags for the configuration of the widget (type) + */ + QWidget* getWidget(QWidget* parent, const std::int32_t& widgetFlags) override; + +public: + + /** + * Constructor + * @param parent Pointer to parent object + * @param title Title of the action + * @param action Pointer to action + */ + Q_INVOKABLE ExampleProxyAction(QObject* parent, const QString& title, mv::gui::WidgetAction* action = nullptr); + +private: + mv::gui::WidgetAction* _action; /** Pointer to target action */ + + friend class AbstractActionsManager; +}; + +Q_DECLARE_METATYPE(ExampleProxyAction) + +inline const auto exampleProxyActionMetaTypeId = qRegisterMetaType("ExampleProxyAction");