diff --git a/Serial-Studio.pro b/Serial-Studio.pro index 7cfcd3a0..53bd4a25 100644 --- a/Serial-Studio.pro +++ b/Serial-Studio.pro @@ -156,10 +156,12 @@ HEADERS += \ src/Widgets/Gyroscope.h \ src/Widgets/Plot.h \ src/Widgets/Terminal.h \ - src/Widgets/Thermometer.h + src/Widgets/Thermometer.h \ + src/Widgets/WidgetLoader.h SOURCES += \ src/UI/Dashboard.cpp \ + src/Widgets/WidgetLoader.cpp \ src/main.cpp \ src/CSV/Export.cpp \ src/CSV/Player.cpp \ diff --git a/assets/qml/Widgets/Window.qml b/assets/qml/Widgets/Window.qml index 778bbd73..1049fc52 100644 --- a/assets/qml/Widgets/Window.qml +++ b/assets/qml/Widgets/Window.qml @@ -145,9 +145,9 @@ Page { Layout.alignment: Qt.AlignVCenter Layout.maximumHeight: parent.height Layout.minimumHeight: parent.height + icon.source: "qrc:/icons/widget.svg" Layout.minimumWidth: root.headerHeight Layout.maximumWidth: root.headerHeight - icon.source: "qrc:/icons/equalizer.svg" icon.width: root.headerHeight * 24 / 32 icon.height: root.headerHeight * 24 / 32 } diff --git a/assets/qml/Windows/Dashboard.qml b/assets/qml/Windows/Dashboard.qml index 1e8d0292..2b984a0d 100644 --- a/assets/qml/Windows/Dashboard.qml +++ b/assets/qml/Windows/Dashboard.qml @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Alex Spataru + * Copyright (c) 2021 Alex Spataru * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,7 +38,6 @@ Item { // Main layout // ColumnLayout { - x: 2 * app.spacing anchors.fill: parent spacing: app.spacing * 2 anchors.margins: app.spacing * 1.5 @@ -75,6 +74,15 @@ Item { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: 240 + + WidgetGrid { + id: widgetGrid + anchors.fill: parent + } + + Widgets.Shadow { + source: widgetGrid + } } } diff --git a/assets/qml/Windows/DashboardTitle.qml b/assets/qml/Windows/DashboardTitle.qml index a4744570..98722861 100644 --- a/assets/qml/Windows/DashboardTitle.qml +++ b/assets/qml/Windows/DashboardTitle.qml @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Alex Spataru + * Copyright (c) 2021 Alex Spataru * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/assets/qml/Windows/ViewOptions.qml b/assets/qml/Windows/ViewOptions.qml index 5b5b94bd..7de10827 100644 --- a/assets/qml/Windows/ViewOptions.qml +++ b/assets/qml/Windows/ViewOptions.qml @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Alex Spataru + * Copyright (c) 2021 Alex Spataru * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/assets/qml/Windows/WidgetGrid.qml b/assets/qml/Windows/WidgetGrid.qml new file mode 100644 index 00000000..fcbb4614 --- /dev/null +++ b/assets/qml/Windows/WidgetGrid.qml @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 + +import SerialStudio 1.0 +import "../Widgets" as Widgets + +Widgets.Window { + id: root + + // + // Window properties + // + gradient: true + title: qsTr("Data") + headerDoubleClickEnabled: false + icon.source: "qrc:/icons/dataset.svg" + backgroundColor: Cpp_ThemeManager.embeddedWindowBackground + + // Hacks for calculating cell width + readonly property int columns: Math.floor(grid.width / cWidth) + readonly property int cWidth: Math.min(Math.max(356, grid.width / Math.min(3, grid.count)), 480) + + // + // Put all items inside a grid view (the column layout in combination with the "clip" property + // of the grid is used to inhibit the generated widgets from escaping the window) + // + ColumnLayout { + spacing: 0 + + anchors { + fill: parent + margins: 0 + leftMargin: app.spacing + rightMargin: app.spacing + } + + GridView { + id: grid + clip: true + contentWidth: -1 + Layout.fillWidth: true + Layout.fillHeight: true + + // Calculate cell size so that cells fill the grid space and heigth < width + cellHeight: cellWidth * (2/3) + cellWidth: cWidth + (grid.width - cWidth * columns) / columns + + // Model + delegate + model: Cpp_UI_Dashboard.totalWidgetCount + delegate: Item { + width: grid.cellWidth + height: grid.cellHeight + + visible: opacity > 0 + opacity: loader.widgetVisible ? 1 : 0 + Behavior on opacity {NumberAnimation{}} + + Widgets.Window { + id: window + title: loader.widgetTitle + icon.source: loader.widgetIcon + onHeaderDoubleClicked: loader.displayWindow() + borderColor: Cpp_ThemeManager.datasetWindowBorder + + anchors { + fill: parent + margins: app.spacing + bottomMargin: app.spacing + topMargin: 2 * app.spacing + } + + WidgetLoader { + id: loader + widgetIndex: index + anchors.fill: parent + anchors.margins: app.spacing + } + } + + Widgets.Shadow { + source: window + } + } + } + + Item { + Layout.fillWidth: true + height: root.borderWidth + } + } +} diff --git a/assets/qml/rcc_qml.qrc b/assets/qml/rcc_qml.qrc index bb21274e..5f189d74 100644 --- a/assets/qml/rcc_qml.qrc +++ b/assets/qml/rcc_qml.qrc @@ -28,5 +28,6 @@ JSONDropArea.qml Windows/ViewOptions.qml Windows/DashboardTitle.qml + Windows/WidgetGrid.qml diff --git a/src/Misc/ModuleManager.cpp b/src/Misc/ModuleManager.cpp index ef4a1f89..248684a6 100644 --- a/src/Misc/ModuleManager.cpp +++ b/src/Misc/ModuleManager.cpp @@ -50,15 +50,8 @@ #include #include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include #include @@ -126,6 +119,7 @@ void ModuleManager::registerQmlTypes() qmlRegisterType("SerialStudio", 1, 0, "Group"); qmlRegisterType("SerialStudio", 1, 0, "Dataset"); qmlRegisterType("SerialStudio", 1, 0, "Terminal"); + qmlRegisterType("SerialStudio", 1, 0, "WidgetLoader"); } /** diff --git a/src/UI/Dashboard.cpp b/src/UI/Dashboard.cpp index a321e61d..b9b6445f 100644 --- a/src/UI/Dashboard.cpp +++ b/src/UI/Dashboard.cpp @@ -90,6 +90,12 @@ bool Dashboard::frameValid() const // Widget count functions //-------------------------------------------------------------------------------------------------- +int Dashboard::totalWidgetCount() +{ + return mapCount() + barCount() + plotCount() + gaugeCount() + groupCount() + + compassCount() + gyroscopeCount() + thermometerCount() + accelerometerCount(); +} + int Dashboard::mapCount() { return m_mapWidgets.count(); @@ -220,6 +226,23 @@ QStringList Dashboard::accelerometerTitles() return list; } +QStringList Dashboard::widgetTitles() +{ + // Warning: maintain same order as the view option repeaters in ViewOptions.qml! + + // clang-format off + return groupTitles() + + plotTitles() + + barTitles() + + gaugeTitles() + + thermometerTitles() + + compassTitles() + + gyroscopeTitles() + + accelerometerTitles() + + mapTitles(); + // clang-format on +} + //-------------------------------------------------------------------------------------------------- // Widget visibility access functions //-------------------------------------------------------------------------------------------------- @@ -304,11 +327,8 @@ void Dashboard::setBarVisible(const int index, const bool visible) { if (index < m_barVisibility.count()) { - if (barVisible(index) != visible) - { - m_barVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_barVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -316,11 +336,8 @@ void Dashboard::setMapVisible(const int index, const bool visible) { if (index < m_mapVisibility.count()) { - if (mapVisible(index) != visible) - { - m_mapVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_mapVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -328,11 +345,8 @@ void Dashboard::setPlotVisible(const int index, const bool visible) { if (index < m_plotVisibility.count()) { - if (plotVisible(index) != visible) - { - m_plotVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_plotVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -340,11 +354,8 @@ void Dashboard::setGroupVisible(const int index, const bool visible) { if (index < m_groupVisibility.count()) { - if (groupVisible(index) != visible) - { - m_groupVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_groupVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -352,11 +363,8 @@ void Dashboard::setGaugeVisible(const int index, const bool visible) { if (index < m_gaugeVisibility.count()) { - if (gaugeVisible(index) != visible) - { - m_gaugeVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_gaugeVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -364,11 +372,8 @@ void Dashboard::setCompassVisible(const int index, const bool visible) { if (index < m_compassVisibility.count()) { - if (compassVisible(index) != visible) - { - m_compassVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_compassVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -376,11 +381,8 @@ void Dashboard::setGyroscopeVisible(const int index, const bool visible) { if (index < m_gyroscopeVisibility.count()) { - if (gyroscopeVisible(index) != visible) - { - m_gyroscopeVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_gyroscopeVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -388,11 +390,8 @@ void Dashboard::setThermometerVisible(const int index, const bool visible) { if (index < m_thermometerVisibility.count()) { - if (thermometerVisible(index) != visible) - { - m_thermometerVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_thermometerVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } @@ -400,11 +399,8 @@ void Dashboard::setAccelerometerVisible(const int index, const bool visible) { if (index < m_accelerometerVisibility.count()) { - if (accelerometerVisible(index) != visible) - { - m_accelerometerVisibility.replace(index, visible); - emit widgetVisibilityChanged(); - } + m_accelerometerVisibility.replace(index, visible); + emit widgetVisibilityChanged(); } } diff --git a/src/UI/Dashboard.h b/src/UI/Dashboard.h index ab29e731..5bbaa793 100644 --- a/src/UI/Dashboard.h +++ b/src/UI/Dashboard.h @@ -39,6 +39,9 @@ class Dashboard : public QObject Q_PROPERTY(bool available READ available NOTIFY widgetCountChanged) + Q_PROPERTY(int totalWidgetCount + READ totalWidgetCount + NOTIFY widgetCountChanged) Q_PROPERTY(int mapCount READ mapCount NOTIFY widgetCountChanged) @@ -81,6 +84,8 @@ public: QString title(); bool available(); + int totalWidgetCount(); + int mapCount(); int barCount(); int plotCount(); @@ -100,6 +105,7 @@ public: Q_INVOKABLE QStringList gyroscopeTitles(); Q_INVOKABLE QStringList thermometerTitles(); Q_INVOKABLE QStringList accelerometerTitles(); + Q_INVOKABLE QStringList widgetTitles(); Q_INVOKABLE bool barVisible(const int index); Q_INVOKABLE bool mapVisible(const int index); diff --git a/src/Widgets/WidgetLoader.cpp b/src/Widgets/WidgetLoader.cpp new file mode 100644 index 00000000..bf730c96 --- /dev/null +++ b/src/Widgets/WidgetLoader.cpp @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2021 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "WidgetLoader.h" + +#include +#include +#include + +using namespace UI; +using namespace Widgets; + +/** + * Constructor function + */ +WidgetLoader::WidgetLoader(QQuickItem *parent) + : QQuickPaintedItem(parent) + , m_index(-1) + , m_widget(nullptr) + , m_widgetVisible(true) +{ + // Set item flags + setFlag(ItemHasContents, true); + setFlag(ItemAcceptsInputMethod, true); + setFlag(ItemIsFocusScope, true); + setAcceptedMouseButtons(Qt::AllButtons); + + // Resize widget to fit QML item size + connect(this, &QQuickPaintedItem::widthChanged, this, + &WidgetLoader::updateWidgetSize); + connect(this, &QQuickPaintedItem::heightChanged, this, + &WidgetLoader::updateWidgetSize); + + // Automatically update the widget's visibility + connect(Dashboard::getInstance(), &Dashboard::widgetVisibilityChanged, this, + &WidgetLoader::updateWidgetVisible); +} + +/** + * Delete widget on class destruction + */ +WidgetLoader::~WidgetLoader() +{ + if (m_widget) + m_widget->deleteLater(); +} + +/** + * Handle application events manually + */ +bool WidgetLoader::event(QEvent *event) +{ + if (!m_widget) + return false; + + switch (event->type()) + { + case QEvent::FocusIn: + forceActiveFocus(); + return QQuickPaintedItem::event(event); + break; + case QEvent::Wheel: + processWheelEvents(static_cast(event)); + return true; + break; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + processMouseEvents(static_cast(event)); + return true; + break; + default: + break; + } + + return QApplication::sendEvent(m_widget, event); +} + +/** + * Render the widget on the given @a painter + */ +void WidgetLoader::paint(QPainter *painter) +{ + if (m_widget && painter) + m_widget->render(painter); +} + +/** + * Custom event filter to manage redraw requests + */ +bool WidgetLoader::eventFilter(QObject *watched, QEvent *event) +{ + if (!m_widget) + return false; + + if (watched == m_widget) + { + switch (event->type()) + { + case QEvent::Paint: + case QEvent::UpdateRequest: + update(); + break; + default: + break; + } + } + + return QQuickPaintedItem::eventFilter(watched, event); +} + +int WidgetLoader::widgetIndex() const +{ + return m_index; +} + +int WidgetLoader::relativeIndex() const +{ + // + // Warning: relative widget index should be calculated using the same order as defined + // by the + // UI::Dashboard::widgetTitles() function. + // + + // Get pointer to dashboard module + auto dash = Dashboard::getInstance(); + + // Check if we should return group widget + int index = widgetIndex(); + if (index < dash->groupCount()) + return index; + + // Check if we should return plot widget + index -= dash->groupCount(); + if (index < dash->plotCount()) + return index; + + // Check if we should return bar widget + index -= dash->plotCount(); + if (index < dash->barCount()) + return index; + + // Check if we should return gauge widget + index -= dash->barCount(); + if (index < dash->gaugeCount()) + return index; + + // Check if we should return thermometer widget + index -= dash->gaugeCount(); + if (index < dash->thermometerCount()) + return index; + + // Check if we should return compass widget + index -= dash->thermometerCount(); + if (index < dash->compassCount()) + return index; + + // Check if we should return gyro widget + index -= dash->compassCount(); + if (index < dash->gyroscopeCount()) + return index; + + // Check if we should return accelerometer widget + index -= dash->gyroscopeCount(); + if (index < dash->accelerometerCount()) + return index; + + // Check if we should return map widget + index -= dash->accelerometerCount(); + if (index < dash->mapCount()) + return index; + + // Return unknown widget + return -1; +} + +bool WidgetLoader::widgetVisible() const +{ + return m_widgetVisible; +} + +QString WidgetLoader::widgetIcon() const +{ + switch (widgetType()) + { + case WidgetType::Group: + return "qrc:/icons/group.svg"; + break; + case WidgetType::Plot: + return "qrc:/icons/plot.svg"; + break; + case WidgetType::Bar: + return "qrc:/icons/bar.svg"; + break; + case WidgetType::Gauge: + return "qrc:/icons/gauge.svg"; + break; + case WidgetType::Thermometer: + return "qrc:/icons/thermometer.svg"; + break; + case WidgetType::Compass: + return "qrc:/icons/compass.svg"; + break; + case WidgetType::Gyroscope: + return "qrc:/icons/gyroscope.svg"; + break; + case WidgetType::Accelerometer: + return "qrc:/icons/accelerometer.svg"; + break; + case WidgetType::Map: + return "qrc:/icons/map.svg"; + break; + default: + return "qrc:/icons/close.svg"; + break; + } +} + +/** + * Returns the appropiate window title for the given widget + */ +QString WidgetLoader::widgetTitle() const +{ + return UI::Dashboard::getInstance()->widgetTitles().at(widgetIndex()); +} + +WidgetLoader::WidgetType WidgetLoader::widgetType() const +{ + // + // Warning: relative widget index should be calculated using the same order as defined + // by the + // UI::Dashboard::widgetTitles() function. + // + + // Unitialized widget loader class + if (widgetIndex() < 0) + return WidgetType::Unknown; + + // Get pointer to dashboard module + auto dash = UI::Dashboard::getInstance(); + + // Check if we should return group widget + int index = widgetIndex(); + if (index < dash->groupCount()) + return WidgetType::Group; + + // Check if we should return plot widget + index -= dash->groupCount(); + if (index < dash->plotCount()) + return WidgetType::Plot; + + // Check if we should return bar widget + index -= dash->plotCount(); + if (index < dash->barCount()) + return WidgetType::Bar; + + // Check if we should return gauge widget + index -= dash->barCount(); + if (index < dash->gaugeCount()) + return WidgetType::Gauge; + + // Check if we should return thermometer widget + index -= dash->gaugeCount(); + if (index < dash->thermometerCount()) + return WidgetType::Thermometer; + + // Check if we should return compass widget + index -= dash->thermometerCount(); + if (index < dash->compassCount()) + return WidgetType::Compass; + + // Check if we should return gyro widget + index -= dash->compassCount(); + if (index < dash->gyroscopeCount()) + return WidgetType::Gyroscope; + + // Check if we should return accelerometer widget + index -= dash->gyroscopeCount(); + if (index < dash->accelerometerCount()) + return WidgetType::Accelerometer; + + // Check if we should return map widget + index -= dash->accelerometerCount(); + if (index < dash->mapCount()) + return WidgetType::Map; + + // Return unknown widget + return WidgetType::Unknown; +} + +/** + * Shows a window with the current widget + */ +void WidgetLoader::displayWindow() +{ + if (m_widget) + m_widget->showNormal(); +} + +/** + * Selects & configures the appropiate widget for the given @a index + */ +void WidgetLoader::setWidgetIndex(const int index) +{ + if (m_index != index && index < Dashboard::getInstance()->totalWidgetCount() + && index >= 0) + { + // Update widget index + m_index = index; + + // Delete previous widget + if (m_widget) + { + m_widget->deleteLater(); + m_widget = nullptr; + } + + // Construct new widget + switch (widgetType()) + { + case WidgetType::Group: + m_widget = new QPushButton("Group"); + break; + case WidgetType::Plot: + m_widget = new QPushButton("Plot"); + break; + case WidgetType::Bar: + m_widget = new QPushButton("Bar"); + break; + case WidgetType::Gauge: + m_widget = new QPushButton("Gauge"); + break; + case WidgetType::Thermometer: + m_widget = new QPushButton("Thermometer"); + break; + case WidgetType::Compass: + m_widget = new QPushButton("Compass"); + break; + case WidgetType::Gyroscope: + m_widget = new QPushButton("Gyroscope"); + break; + case WidgetType::Accelerometer: + m_widget = new QPushButton("Accelerometer"); + break; + case WidgetType::Map: + m_widget = new QPushButton("Map"); + break; + default: + break; + } + + // Allow widget to receive events from the QML interface + if (m_widget) + { + m_widget->installEventFilter(this); + updateWidgetVisible(); + } + + // Update UI + emit widgetIndexChanged(); + } +} + +/** + * Resizes the widget to fit inside the QML item. + */ +void WidgetLoader::updateWidgetSize() +{ + if (m_widget && width() > 0 && height() > 0) + { + m_widget->setFixedSize(width(), height()); + update(); + } +} + +void WidgetLoader::updateWidgetVisible() +{ + bool visible = false; + auto index = relativeIndex(); + auto dash = Dashboard::getInstance(); + + switch (widgetType()) + { + case WidgetType::Group: + visible = dash->groupVisible(index); + break; + case WidgetType::Plot: + visible = dash->plotVisible(index); + break; + case WidgetType::Bar: + visible = dash->barVisible(index); + break; + case WidgetType::Gauge: + visible = dash->gaugeVisible(index); + break; + case WidgetType::Thermometer: + visible = dash->thermometerVisible(index); + break; + case WidgetType::Compass: + visible = dash->compassVisible(index); + break; + case WidgetType::Gyroscope: + visible = dash->gyroscopeVisible(index); + break; + case WidgetType::Accelerometer: + visible = dash->accelerometerVisible(index); + break; + case WidgetType::Map: + visible = dash->mapVisible(index); + break; + default: + visible = false; + break; + } + + if (widgetVisible() != visible) + { + m_widgetVisible = visible; + emit widgetVisibleChanged(); + } +} + +/** + * Hack: calls the appropiate protected mouse event handler function of the widget item + */ +void WidgetLoader::processMouseEvents(QMouseEvent *event) +{ + if (!m_widget) + return; + + class Hack : public QWidget + { + public: + using QWidget::mouseDoubleClickEvent; + using QWidget::mouseMoveEvent; + using QWidget::mousePressEvent; + using QWidget::mouseReleaseEvent; + }; + + auto hack = static_cast(m_widget); + switch (event->type()) + { + case QEvent::MouseButtonPress: + hack->mousePressEvent(event); + break; + case QEvent::MouseMove: + hack->mouseMoveEvent(event); + break; + case QEvent::MouseButtonRelease: + hack->mouseReleaseEvent(event); + break; + case QEvent::MouseButtonDblClick: + hack->mouseDoubleClickEvent(event); + break; + default: + break; + } +} + +/** + * Hack: calls the appropiate protected wheel event handler function of the widget item + */ +void WidgetLoader::processWheelEvents(QWheelEvent *event) +{ + if (!m_widget) + return; + + class Hack : public QWidget + { + public: + using QWidget::wheelEvent; + }; + + static_cast(m_widget)->wheelEvent(event); +} diff --git a/src/Widgets/WidgetLoader.h b/src/Widgets/WidgetLoader.h new file mode 100644 index 00000000..d4522db6 --- /dev/null +++ b/src/Widgets/WidgetLoader.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef WIDGETS_LOADER_H +#define WIDGETS_LOADER_H + +#include +#include +#include +#include + +namespace Widgets +{ +class WidgetLoader : public QQuickPaintedItem +{ + // clang-format off + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int widgetIndex + READ widgetIndex + WRITE setWidgetIndex + NOTIFY widgetIndexChanged) + Q_PROPERTY(int relativeIndex + READ relativeIndex + NOTIFY widgetIndexChanged) + Q_PROPERTY(WidgetType widgetType + READ widgetType + NOTIFY widgetIndexChanged) + Q_PROPERTY(QString widgetIcon + READ widgetIcon + NOTIFY widgetIndexChanged) + Q_PROPERTY(QString widgetTitle + READ widgetTitle + NOTIFY widgetIndexChanged) + Q_PROPERTY(bool widgetVisible + READ widgetVisible + NOTIFY widgetVisibleChanged) + // clang-format on + +signals: + void widgetIndexChanged(); + void widgetVisibleChanged(); + +public: + enum class WidgetType + { + Group, + Plot, + Bar, + Gauge, + Thermometer, + Compass, + Gyroscope, + Accelerometer, + Map, + Unknown + }; + Q_ENUM(WidgetType) + + WidgetLoader(QQuickItem *parent = 0); + ~WidgetLoader(); + + virtual bool event(QEvent *event) override; + virtual void paint(QPainter *painter) override; + virtual bool eventFilter(QObject *watched, QEvent *event) override; + + int widgetIndex() const; + int relativeIndex() const; + bool widgetVisible() const; + QString widgetIcon() const; + QString widgetTitle() const; + WidgetType widgetType() const; + +public slots: + void displayWindow(); + void setWidgetIndex(const int index); + +private slots: + void updateWidgetSize(); + void updateWidgetVisible(); + +protected: + void processMouseEvents(QMouseEvent *event); + void processWheelEvents(QWheelEvent *event); + +private: + int m_index; + QWidget *m_widget; + bool m_widgetVisible; +}; +} + +#endif