diff --git a/Serial-Studio.pro b/Serial-Studio.pro index 67b01e41..a4a93ebb 100644 --- a/Serial-Studio.pro +++ b/Serial-Studio.pro @@ -69,6 +69,7 @@ DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050F00 QMAKE_CXXFLAGS *= -MP } +CONFIG += ltcg CONFIG += c++11 CONFIG += silent diff --git a/assets/qml/Dashboard/WidgetDelegate.qml b/assets/qml/Dashboard/WidgetDelegate.qml index 15c6ec88..0e804ece 100644 --- a/assets/qml/Dashboard/WidgetDelegate.qml +++ b/assets/qml/Dashboard/WidgetDelegate.qml @@ -21,105 +21,47 @@ */ import QtQuick 2.12 -import QtQuick.Layouts 1.12 -import QtQuick.Controls 2.12 - import SerialStudio 1.0 + import "../Widgets" as Widgets -import "../FramelessWindow" as FramelessWindow Item { id: root + visible: false property int widgetIndex: -1 - property FramelessWindow.CustomWindow externalWindow: null - Widgets.Window { - id: window - anchors.fill: parent - title: loader.widgetTitle - icon.source: loader.widgetIcon - headerDoubleClickEnabled: true - borderColor: Cpp_ThemeManager.widgetWindowBorder - onHeaderDoubleClicked: { - if (root.externalWindow !== null) - root.externalWindow.showNormal() - else - externalWindowLoader.active = true - } + Connections { + target: Cpp_UI_Dashboard - WidgetLoader { - id: loader - widgetIndex: root.widgetIndex - anchors { - fill: parent - leftMargin: window.borderWidth - rightMargin: window.borderWidth - bottomMargin: window.borderWidth - } + function onWidgetVisibilityChanged() { + if (loader.status == Loader.Ready) + root.visible = Cpp_UI_Dashboard.widgetVisible(root.widgetIndex) } } Loader { - id: externalWindowLoader + id: loader + anchors.fill: parent + onLoaded: root.visible = Cpp_UI_Dashboard.widgetVisible(root.widgetIndex) - active: false - asynchronous: true + sourceComponent: Widgets.Window { + id: window + title: widget.widgetTitle + icon.source: widget.widgetIcon + headerDoubleClickEnabled: true + borderColor: Cpp_ThemeManager.widgetWindowBorder + onHeaderDoubleClicked: widget.showExternalWindow() - sourceComponent: FramelessWindow.CustomWindow { - id: _window - minimumWidth: 640 + shadowMargin - minimumHeight: 480 + shadowMargin - title: externalLoader.widgetTitle - extraFlags: Qt.WindowStaysOnTopHint - titlebarText: Cpp_ThemeManager.text - titlebarColor: Cpp_ThemeManager.widgetWindowBackground - backgroundColor: Cpp_ThemeManager.widgetWindowBackground - borderColor: isMaximized ? backgroundColor : Cpp_ThemeManager.highlight - - Timer { - id: timer - interval: 200 - onTriggered: _window.showNormal() - } - - Component.onCompleted: { - root.externalWindow = this - timer.start() - } - - Rectangle { - clip: true - anchors.fill: parent - radius: _window.radius - anchors.margins: _window.shadowMargin - color: Cpp_ThemeManager.widgetWindowBackground - anchors.topMargin: _window.titlebar.height + _window.shadowMargin - - Rectangle { - height: _window.radius - color: Cpp_ThemeManager.widgetWindowBackground - anchors { - top: parent.top - left: parent.left - right: parent.right - } + WidgetLoader { + id: widget + widgetIndex: root.widgetIndex + anchors { + fill: parent + leftMargin: window.borderWidth + rightMargin: window.borderWidth + bottomMargin: window.borderWidth } - - WidgetLoader { - id: externalLoader - anchors.fill: parent - isExternalWindow: true - widgetIndex: root.widgetIndex - widgetVisible: _window.visible - anchors.margins: _window.radius - } - } - - FramelessWindow.ResizeHandles { - window: _window - anchors.fill: parent - handleSize: _window.handleSize } } } diff --git a/assets/qml/Dashboard/WidgetModel.qml b/assets/qml/Dashboard/WidgetModel.qml index ba487fff..46460086 100644 --- a/assets/qml/Dashboard/WidgetModel.qml +++ b/assets/qml/Dashboard/WidgetModel.qml @@ -29,23 +29,9 @@ Repeater { property real cellWidth: 0 property real cellHeight: 0 - delegate: Loader { - id: loader - asynchronous: true + delegate: WidgetDelegate { + widgetIndex: index width: root.cellWidth height: root.cellHeight - - sourceComponent: WidgetDelegate { - widgetIndex: index - anchors.fill: parent - } - - Connections { - target: Cpp_UI_Dashboard - - function onWidgetVisibilityChanged() { - loader.visible = Cpp_UI_Dashboard.widgetVisible(index) - } - } } } diff --git a/assets/qml/PlatformDependent/Menubar.qml b/assets/qml/PlatformDependent/Menubar.qml index 6a7417ed..6fbba7a8 100644 --- a/assets/qml/PlatformDependent/Menubar.qml +++ b/assets/qml/PlatformDependent/Menubar.qml @@ -295,7 +295,7 @@ MenuBar { title: qsTr("Help") DecentMenuItem { - onTriggered: app.about.show() + onTriggered: app.aboutDialog.show() text: qsTr("About %1").arg(Cpp_AppName) } diff --git a/assets/qml/PlatformDependent/MenubarMacOS.qml b/assets/qml/PlatformDependent/MenubarMacOS.qml index ea646b5c..9468d836 100644 --- a/assets/qml/PlatformDependent/MenubarMacOS.qml +++ b/assets/qml/PlatformDependent/MenubarMacOS.qml @@ -277,7 +277,7 @@ MenuBar { title: qsTr("Help") MenuItem { - onTriggered: app.about.show() + onTriggered: app.aboutDialog.show() text: qsTr("About %1").arg(Cpp_AppName) } diff --git a/assets/qml/Windows/About.qml b/assets/qml/Windows/About.qml index 5bc16ef1..65048c82 100644 --- a/assets/qml/Windows/About.qml +++ b/assets/qml/Windows/About.qml @@ -173,7 +173,7 @@ FramelessWindow.CustomWindow { Button { Layout.fillWidth: true text: qsTr("Make a donation") - onClicked: app.donations.show() + onClicked: app.donateDialog.show() } Button { @@ -191,7 +191,7 @@ FramelessWindow.CustomWindow { Button { Layout.fillWidth: true text: qsTr("Acknowledgements") - onClicked: acknowledgements.show() + onClicked: acknowledgementsDialog.show() } Item { diff --git a/assets/qml/Windows/MainWindow.qml b/assets/qml/Windows/MainWindow.qml index bdaab897..c71fb718 100644 --- a/assets/qml/Windows/MainWindow.qml +++ b/assets/qml/Windows/MainWindow.qml @@ -122,8 +122,8 @@ FramelessWindow.CustomWindow { root.requestUpdate() // Show donations dialog every 15 launches - if (root.appLaunchCount % 15 == 0 && !app.donations.doNotShowAgain) - app.donations.showAutomatically() + if (root.appLaunchCount % 15 == 0 && !app.donateDialog.doNotShowAgain) + app.donateDialog.showAutomatically() // Ask user if he/she wants to enable automatic updates if (root.appLaunchCount == 2 && Cpp_UpdaterEnabled) { @@ -269,7 +269,7 @@ FramelessWindow.CustomWindow { setupChecked: root.setupVisible consoleChecked: root.consoleVisible dashboardChecked: root.dashboardVisible - onJsonEditorClicked: app.jsonEditor.show() + onJsonEditorClicked: app.jsonEditorWindow.show() onSetupClicked: setup.visible ? setup.hide() : setup.show() onDashboardClicked: { diff --git a/assets/qml/main.qml b/assets/qml/main.qml index 2ab7bc95..14b75915 100644 --- a/assets/qml/main.qml +++ b/assets/qml/main.qml @@ -38,12 +38,12 @@ Item { // // Access to dialogs & windows // - property Windows.About about: null - property Windows.Donate donations: null - property Windows.CsvPlayer csvPlayer: null + property Windows.About aboutDialog: null + property Windows.Donate donateDialog: null property Windows.MainWindow mainWindow: null - property Windows.JsonEditor jsonEditor: null - property Windows.Acknowledgements acknowledgements: null + property Windows.CsvPlayer csvPlayerDialog: null + property Windows.JsonEditor jsonEditorWindow: null + property Windows.Acknowledgements acknowledgementsDialog: null // // Check for updates (non-silent mode) @@ -73,7 +73,7 @@ Item { Loader { asynchronous: true sourceComponent: Windows.About { - Component.onCompleted: app.about = this + Component.onCompleted: app.aboutDialog = this } } @@ -83,7 +83,7 @@ Item { Loader { asynchronous: true sourceComponent: Windows.CsvPlayer { - Component.onCompleted: app.csvPlayer = this + Component.onCompleted: app.csvPlayerDialog = this } } @@ -93,7 +93,7 @@ Item { Loader { asynchronous: true sourceComponent: Windows.JsonEditor { - Component.onCompleted: app.jsonEditor = this + Component.onCompleted: app.jsonEditorWindow = this } } @@ -103,7 +103,7 @@ Item { Loader { asynchronous: false sourceComponent: Windows.Donate { - Component.onCompleted: app.donations = this + Component.onCompleted: app.donateDialog = this } } @@ -113,7 +113,7 @@ Item { Loader { asynchronous: true sourceComponent: Windows.Acknowledgements { - Component.onCompleted: app.acknowledgements = this + Component.onCompleted: app.acknowledgementsDialog = this } } } diff --git a/src/UI/WidgetLoader.cpp b/src/UI/WidgetLoader.cpp index 24ea067b..9f9245d0 100644 --- a/src/UI/WidgetLoader.cpp +++ b/src/UI/WidgetLoader.cpp @@ -49,7 +49,6 @@ WidgetLoader::WidgetLoader(QQuickItem *parent) , m_index(-1) , m_widget(nullptr) , m_widgetVisible(false) - , m_isExternalWindow(false) { // Set item flags setFlag(ItemHasContents, true); @@ -63,6 +62,20 @@ WidgetLoader::WidgetLoader(QQuickItem *parent) connect(this, &QQuickPaintedItem::heightChanged, this, &WidgetLoader::updateWidgetSize); + // Configure external window + m_window.setMinimumWidth(640); + m_window.setMinimumHeight(480); + + // Set window palette + QPalette palette; + auto theme = Misc::ThemeManager::getInstance(); + palette.setColor(QPalette::Base, theme->widgetWindowBackground()); + palette.setColor(QPalette::Window, theme->widgetWindowBackground()); + m_window.setPalette(palette); + + // Enable/disable the external window widget automatically + connect(&m_window, SIGNAL(visibleChanged()), this, SLOT(updateExternalWindow())); + // Automatically update the widget's visibility connect(Dashboard::getInstance(), &Dashboard::widgetVisibilityChanged, this, &WidgetLoader::updateWidgetVisible); @@ -75,6 +88,9 @@ WidgetLoader::~WidgetLoader() { if (m_widget) delete m_widget; + + if (m_window.centralWidget()) + delete m_window.centralWidget(); } /** @@ -200,18 +216,6 @@ QString WidgetLoader::widgetTitle() const return tr("Invalid"); } -/** - * If set to @c true, then the widget visibility shall be controlled - * directly by the QML interface. - * - * If set to @c false, then the widget visbility shall be controlled - * by the UI::Dashboard class via the SIGNAL/SLOT system. - */ -bool WidgetLoader::isExternalWindow() const -{ - return m_isExternalWindow; -} - /** * Returns the type of the current widget (e.g. group, plot, bar, gauge, etc...) */ @@ -220,6 +224,15 @@ UI::Dashboard::WidgetType WidgetLoader::widgetType() const return UI::Dashboard::getInstance()->widgetType(widgetIndex()); } +/** + * Displays the external window + */ +void WidgetLoader::showExternalWindow() +{ + if (m_window.centralWidget()) + m_window.showNormal(); +} + /** * Changes the visibility & enabled status of the widget */ @@ -251,87 +264,69 @@ void WidgetLoader::setWidgetIndex(const int index) { case UI::Dashboard::WidgetType::Group: m_widget = new Widgets::DataGroup(relativeIndex()); + m_window.setCentralWidget(new Widgets::DataGroup(relativeIndex())); break; case UI::Dashboard::WidgetType::MultiPlot: m_widget = new Widgets::MultiPlot(relativeIndex()); + m_window.setCentralWidget(new Widgets::MultiPlot(relativeIndex())); break; case UI::Dashboard::WidgetType::FFT: m_widget = new Widgets::FFTPlot(relativeIndex()); + m_window.setCentralWidget(new Widgets::FFTPlot(relativeIndex())); break; case UI::Dashboard::WidgetType::Plot: m_widget = new Widgets::Plot(relativeIndex()); + m_window.setCentralWidget(new Widgets::Plot(relativeIndex())); break; case UI::Dashboard::WidgetType::Bar: m_widget = new Widgets::Bar(relativeIndex()); + m_window.setCentralWidget(new Widgets::Bar(relativeIndex())); break; case UI::Dashboard::WidgetType::Gauge: m_widget = new Widgets::Gauge(relativeIndex()); + m_window.setCentralWidget(new Widgets::Gauge(relativeIndex())); break; case UI::Dashboard::WidgetType::Compass: m_widget = new Widgets::Compass(relativeIndex()); + m_window.setCentralWidget(new Widgets::Compass(relativeIndex())); break; case UI::Dashboard::WidgetType::Gyroscope: m_widget = new Widgets::Gyroscope(relativeIndex()); + m_window.setCentralWidget(new Widgets::Gyroscope(relativeIndex())); break; case UI::Dashboard::WidgetType::Accelerometer: m_widget = new Widgets::Accelerometer(relativeIndex()); + m_window.setCentralWidget(new Widgets::Accelerometer(relativeIndex())); break; case UI::Dashboard::WidgetType::GPS: m_widget = new Widgets::GPS(relativeIndex()); + m_window.setCentralWidget(new Widgets::GPS(relativeIndex())); break; case UI::Dashboard::WidgetType::LED: m_widget = new Widgets::LEDPanel(relativeIndex()); + m_window.setCentralWidget(new Widgets::LEDPanel(relativeIndex())); break; default: break; } + // Configure external window + if (m_window.centralWidget()) + { + m_window.setWindowTitle(widgetTitle()); + m_window.centralWidget()->setEnabled(false); + } + // Allow widget to receive events from the QML interface if (m_widget) { - m_widget->setEnabled(true); m_widget->installEventFilter(this); + QTimer::singleShot(100, this, SLOT(updateWidgetVisible())); emit widgetIndexChanged(); - updateWidgetVisible(); } } } -/** - * Changes the widget visibility controller source. - * - * If set to @c true, then the widget visibility shall be controlled - * directly by the QML interface. - * - * If set to @c false, then the widget visbility shall be controlled - * by the UI::Dashboard class via the SIGNAL/SLOT system. - */ -void WidgetLoader::setIsExternalWindow(const bool isWindow) -{ - m_isExternalWindow = isWindow; - emit isExternalWindowChanged(); -} - -/** - * This function must be called directly by a QML MouseArea item. - * Unfortunatelly, enter/leave events cannot be processed - * directly by the @c WidgetLoader::event(QEvent *event) function. - */ -void WidgetLoader::processMouseHover(const bool containsMouse) -{ - if (containsMouse) - { - QEnterEvent event(QPoint(0, 0), QPoint(0, 0), QPoint(0, 0)); - processEnterEvent(&event); - } - - else - { - QEvent event(QEvent::Leave); - processLeaveEvent(&event); - } -} - /** * Resizes the widget to fit inside the QML item. */ @@ -352,7 +347,7 @@ void WidgetLoader::updateWidgetVisible() { bool visible = UI::Dashboard::getInstance()->widgetVisible(widgetIndex()); - if (widgetVisible() != visible && !isExternalWindow()) + if (widgetVisible() != visible) { m_widgetVisible = visible; @@ -363,6 +358,16 @@ void WidgetLoader::updateWidgetVisible() } } +/** + * Enables/disables the widget updates of the external window when the window + * is shown or hidden. + */ +void WidgetLoader::updateExternalWindow() +{ + if (m_window.centralWidget()) + m_window.centralWidget()->setEnabled(m_window.isVisible()); +} + /** * Lets the widget handle the mouse leave events */ diff --git a/src/UI/WidgetLoader.h b/src/UI/WidgetLoader.h index 5b0ce50b..e07a83ef 100644 --- a/src/UI/WidgetLoader.h +++ b/src/UI/WidgetLoader.h @@ -25,12 +25,34 @@ #include #include #include +#include #include #include namespace UI { +class WidgetWindow : public QMainWindow +{ + Q_OBJECT + +signals: + void visibleChanged(); + +private: + void showEvent(QShowEvent *event) + { + event->accept(); + emit visibleChanged(); + } + + void hideEvent(QHideEvent *event) + { + event->accept(); + emit visibleChanged(); + } +}; + /** * @brief The WidgetLoader class * @@ -76,10 +98,6 @@ class WidgetLoader : public QQuickPaintedItem Q_PROPERTY(QString widgetTitle READ widgetTitle NOTIFY widgetIndexChanged) - Q_PROPERTY(bool isExternalWindow - READ isExternalWindow - WRITE setIsExternalWindow - NOTIFY isExternalWindowChanged) Q_PROPERTY(bool widgetVisible READ widgetVisible WRITE setVisible @@ -89,7 +107,6 @@ class WidgetLoader : public QQuickPaintedItem signals: void widgetIndexChanged(); void widgetVisibleChanged(); - void isExternalWindowChanged(); public: WidgetLoader(QQuickItem *parent = 0); @@ -104,18 +121,17 @@ public: bool widgetVisible() const; QString widgetIcon() const; QString widgetTitle() const; - bool isExternalWindow() const; UI::Dashboard::WidgetType widgetType() const; public slots: + void showExternalWindow(); void setVisible(const bool visible); void setWidgetIndex(const int index); - void setIsExternalWindow(const bool isWindow); - void processMouseHover(const bool containsMouse); private slots: void updateWidgetSize(); void updateWidgetVisible(); + void updateExternalWindow(); protected: void processLeaveEvent(QEvent *event); @@ -127,6 +143,6 @@ private: int m_index; QWidget *m_widget; bool m_widgetVisible; - bool m_isExternalWindow; + WidgetWindow m_window; }; }