diff --git a/src/blackgui/components/invisibleinfoareacomponent.cpp b/src/blackgui/components/invisibleinfoareacomponent.cpp index b8ddfff96..5c90cad5a 100644 --- a/src/blackgui/components/invisibleinfoareacomponent.cpp +++ b/src/blackgui/components/invisibleinfoareacomponent.cpp @@ -57,6 +57,11 @@ namespace BlackGui } } + CNavigatorDockWidget *CInvisibleInfoAreaComponent::getNavigatorComponent() + { + return this->ui->comp_Navigator; + } + void CInvisibleInfoAreaComponent::toggleNavigator() { this->ui->comp_Navigator->toggleFloating(); diff --git a/src/blackgui/components/invisibleinfoareacomponent.h b/src/blackgui/components/invisibleinfoareacomponent.h index 010f44028..18e6978fa 100644 --- a/src/blackgui/components/invisibleinfoareacomponent.h +++ b/src/blackgui/components/invisibleinfoareacomponent.h @@ -11,6 +11,7 @@ #define BLACKGUI_COMPONENTS_INVISIBLEINFOAREACOMPONENT_H #include "blackgui/infoarea.h" +#include "blackgui/components/navigatordockwidget.h" #include namespace Ui { class CInvisibleInfoAreaComponent; } @@ -47,6 +48,9 @@ namespace BlackGui //! \copydoc CInfoArea::indexToPixmap const QPixmap &indexToPixmap(int areaIndex) const override; + //! Get navigator component + BlackGui::Components::CNavigatorDockWidget *getNavigatorComponent(); + public slots: //! Navigator floating void toggleNavigator(); diff --git a/src/blackgui/components/invisibleinfoareacomponent.ui b/src/blackgui/components/invisibleinfoareacomponent.ui index 659f0ce5a..ebeace10d 100644 --- a/src/blackgui/components/invisibleinfoareacomponent.ui +++ b/src/blackgui/components/invisibleinfoareacomponent.ui @@ -15,12 +15,6 @@ - - - 80 - 80 - - false diff --git a/src/blackgui/components/maininfoareacomponent.ui b/src/blackgui/components/maininfoareacomponent.ui index 71f65c5f4..3df7d51cc 100644 --- a/src/blackgui/components/maininfoareacomponent.ui +++ b/src/blackgui/components/maininfoareacomponent.ui @@ -281,7 +281,7 @@ 0 - 0 + 2 0 diff --git a/src/blackgui/components/navigatordockwidget.cpp b/src/blackgui/components/navigatordockwidget.cpp index 648300633..bcae84234 100644 --- a/src/blackgui/components/navigatordockwidget.cpp +++ b/src/blackgui/components/navigatordockwidget.cpp @@ -8,9 +8,17 @@ */ #include "navigatordockwidget.h" +#include "blackgui/infoarea.h" +#include "blackgui/guiutility.h" +#include "blackgui/stylesheetutility.h" #include "ui_navigatordockwidget.h" +#include +#include +#include + using namespace BlackGui; +using namespace BlackMisc; namespace BlackGui { @@ -27,5 +35,130 @@ namespace BlackGui CNavigatorDockWidget::~CNavigatorDockWidget() { } + void CNavigatorDockWidget::addAction(QAction *action) + { + if (action) + { + this->m_actions.append(action); + QToolButton *tb = new QToolButton(this->ui->fr_NavigatorDockWidgetInner); + tb->setDefaultAction(action); + tb->setObjectName(this->objectName().append(":").append(action->objectName())); + this->m_widgets.append(tb); + } + } + + void CNavigatorDockWidget::addActions(QList actions) + { + if (actions.isEmpty()) { return; } + for (QAction *a : actions) + { + this->addAction(a); + } + } + + void CNavigatorDockWidget::buildNavigator(int columns) + { + if (m_firstBuild) + { + m_firstBuild = false; + this->insertOwnActions(); + } + + // remove old layout + CGuiUtility::deleteLayout(this->ui->fr_NavigatorDockWidgetInner->layout(), false); + + // new layout + QGridLayout *gridLayout = new QGridLayout(this->ui->fr_NavigatorDockWidgetInner); + gridLayout->setObjectName("gl_CNavigatorDockWidget"); + gridLayout->setSpacing(0); + gridLayout->setMargin(0); + gridLayout->setContentsMargins(0, 0, 0, 0); + + this->ui->fr_NavigatorDockWidgetInner->setLayout(gridLayout); + int r = 0; + int c = 0; + for (int i = 0; i < this->m_widgets.size(); i++) + { + gridLayout->addWidget(this->m_widgets[i], r, c++); + this->m_widgets[i]->show(); + if (c < columns) { continue; } + c = 0; + r++; + } + + // set the real values + c = gridLayout->columnCount(); + r = gridLayout->rowCount(); + this->setMinimumSizeForWidgets(r, c); + + // sizes + QSize ws(gridLayout->sizeHint()); + this->resize(ws); + + // see documentation, required as layout was changed + // this requires setting widget again + this->ui->fr_NavigatorDockWidgetInner->show(); + this->setWidget(this->ui->fr_NavigatorDockWidgetInner); + } + + void CNavigatorDockWidget::addToContextMenu(QMenu *contextMenu) const + { + QAction *a; + a = contextMenu->addAction(CIcons::resize16(), "1 row", this, SLOT(ps_changeLayout())); + a->setData("1r"); + a = contextMenu->addAction(CIcons::resize16(), "2 rows", this, SLOT(ps_changeLayout())); + a->setData("2r"); + a = contextMenu->addAction(CIcons::resize16(), "1 column", this, SLOT(ps_changeLayout())); + a->setData("1c"); + a = contextMenu->addAction(CIcons::resize16(), "2 columns", this, SLOT(ps_changeLayout())); + a->setData("2c"); + contextMenu->addSeparator(); + CDockWidgetInfoArea::addToContextMenu(contextMenu); + } + + void CNavigatorDockWidget::ps_onStyleSheetsChanged() + { + const QString fn(CStyleSheetUtility::fileNameNavigator()); + const QString qss(CStyleSheetUtility::instance().style(fn)); + this->setStyleSheet(qss); + } + + void CNavigatorDockWidget::ps_changeLayout() + { + QAction *a = qobject_cast(QObject::sender()); + if (!a) { return; } + QString v(a->data().toString()); + if (v == "1c") { buildNavigator(1);} + else if (v == "2c") { buildNavigator(2);} + else if (v == "1r") { buildNavigator(columnsForRows(1));} + else if (v == "2r") { buildNavigator(columnsForRows(2));} + } + + void CNavigatorDockWidget::insertOwnActions() + { + QIcon i(CIcons::changeIconBackgroundColor(this->style()->standardIcon(QStyle::SP_TitleBarCloseButton), Qt::white, QSize(16, 16))); + QAction *a = new QAction(i, "Close", this); + connect(a, &QAction::triggered, this, &CNavigatorDockWidget::close); + this->addAction(a); + } + + int CNavigatorDockWidget::columnsForRows(int rows) + { + Q_ASSERT(rows >= 0); + int items = this->m_widgets.size(); + int c = items / rows; + return (c * rows) < items ? c + 1 : c; + } + + void CNavigatorDockWidget::setMinimumSizeForWidgets(int rows, int columns) + { + int w = 10 * columns; + int h = 10 * rows; + QSize min(w, h); + this->ui->qw_NavigatorDockWidgetOuter->setMinimumSize(min); + this->ui->fr_NavigatorDockWidgetInner->setMinimumSize(min); + this->setMinimumSize(min); + } + } // ns } // ns diff --git a/src/blackgui/components/navigatordockwidget.h b/src/blackgui/components/navigatordockwidget.h index e3731ce1c..0a94adae1 100644 --- a/src/blackgui/components/navigatordockwidget.h +++ b/src/blackgui/components/navigatordockwidget.h @@ -9,8 +9,8 @@ //! \file -#ifndef BLACKGUI_NAVIGATORDOCKWIDGET_H -#define BLACKGUI_NAVIGATORDOCKWIDGET_H +#ifndef BLACKGUI_COMPONENTS_NAVIGATORDOCKWIDGET_H +#define BLACKGUI_COMPONENTS_NAVIGATORDOCKWIDGET_H #include "blackgui/dockwidgetinfoarea.h" #include @@ -35,8 +35,42 @@ namespace BlackGui //! Destructor ~CNavigatorDockWidget(); + //! Add action as navigator item + void addAction(QAction * action); + + //! Add actions as navigator items + void addActions(QList actions); + + //! Navigator + void buildNavigator(int columns); + + protected: + //! \copydoc CDockWidgetInfoArea::addToContextMenu + virtual void addToContextMenu(QMenu *contextMenu) const override; + + protected: + //! \copydoc CDockWidget::ps_onStyleSheetsChanged + virtual void ps_onStyleSheetsChanged() override; + + private slots: + //! Change the layout + void ps_changeLayout(); + private: QScopedPointer ui; + QList m_widgets; + QList m_actions; + bool m_firstBuild = true; + + //! Insert own actions + void insertOwnActions(); + + //! How many columns for given rows + int columnsForRows(int rows); + + //! Set widgets to their minimum size + void setMinimumSizeForWidgets(int rows, int columns); + }; } // ns diff --git a/src/blackgui/components/navigatordockwidget.ui b/src/blackgui/components/navigatordockwidget.ui index 0bb56421e..e86fe5ce2 100644 --- a/src/blackgui/components/navigatordockwidget.ui +++ b/src/blackgui/components/navigatordockwidget.ui @@ -6,10 +6,16 @@ 0 0 - 89 - 300 + 90 + 383 + + + 0 + 0 + + true @@ -22,19 +28,33 @@ Navigator - - - - - - ... + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame - - - - - - ... + + QFrame::Plain + + + 0 diff --git a/src/blackgui/dockwidget.cpp b/src/blackgui/dockwidget.cpp index 7c3862d99..ecc5a357b 100644 --- a/src/blackgui/dockwidget.cpp +++ b/src/blackgui/dockwidget.cpp @@ -9,6 +9,7 @@ #include "dockwidget.h" #include "blackmisc/icons.h" +#include "blackmisc/logmessage.h" #include "blackgui/stylesheetutility.h" #include "blackgui/guiutility.h" #include @@ -16,14 +17,15 @@ #include #include +using namespace BlackMisc; + namespace BlackGui { CDockWidget::CDockWidget(bool allowStatusBar, QWidget *parent) : QDockWidget(parent), - CEnableForFramelessWindow(CEnableForFramelessWindow::WindowNormal, false, this), + CEnableForFramelessWindow(CEnableForFramelessWindow::WindowTool, false, "framelessDockWidget", this), m_allowStatusBar(allowStatusBar) { - this->ps_onStyleSheetsChanged(); this->initTitleBarWidgets(); @@ -35,7 +37,6 @@ namespace BlackGui connect(this, &QDockWidget::topLevelChanged, this, &CDockWidget::ps_onTopLevelChanged); connect(&CStyleSheetUtility::instance(), &CStyleSheetUtility::styleSheetsChanged, this, &CDockWidget::ps_onStyleSheetsChanged); connect(this, &QDockWidget::visibilityChanged, this, &CDockWidget::ps_onVisibilityChanged); - } CDockWidget::CDockWidget(QWidget *parent): CDockWidget(true, parent) @@ -179,10 +180,7 @@ namespace BlackGui void CDockWidget::toggleFloating() { bool floating = !this->isFloating(); - if (!floating) - { - this->setFrameless(false); - } + if (!floating) { this->setFrameless(false); } this->setFloating(floating); } @@ -263,8 +261,7 @@ namespace BlackGui void CDockWidget::initialFloating() { - - // init status bar, as we have now all structure set + // init status bar, as we have now all structures set this->initStatusBar(); // for the first time resize @@ -289,36 +286,45 @@ namespace BlackGui QString sectionUsed(section.isEmpty() ? this->objectName() : section); if (sectionUsed.isEmpty()) { return false; } const QSettings *settings = CStyleSheetUtility::instance().iniFile(); + Q_ASSERT_X(settings, "CDockWidget::setMarginsFromSettings", "Missing ini settings"); if (!settings) { return false; } - // checked if value exists as there is no way to check if key/section exist + // check if value exists as there is no way to check if key/section exist if (settings->value(sectionUsed + "/margindocked.left").toString().isEmpty()) { // no values considered as no section, now we check if an alias exists sectionUsed = settings->value("alias/" + sectionUsed).toString(); if (sectionUsed.isEmpty()) { return false; } - if (settings->value(sectionUsed + "/margindocked.left").toString().isEmpty()) { return false; } + if (settings->value(sectionUsed + "/margindocked.left").toString().isEmpty()) + { + Q_ASSERT_X(false, "CDockWidget::setMarginsFromSettings", "Wrong ini settings"); + return false; + } } - if (settings) - { - this->setMarginsWhenDocked( - settings->value(sectionUsed + "/margindocked.left", 1).toInt(), - settings->value(sectionUsed + "/margindocked.top", 1).toInt(), - settings->value(sectionUsed + "/margindocked.right", 1).toInt(), - settings->value(sectionUsed + "/margindocked.bottom", 1).toInt()); - this->setMarginsWhenFloating( - settings->value(sectionUsed + "/marginfloating.left", 10).toInt(), - settings->value(sectionUsed + "/marginfloating.top", 10).toInt(), - settings->value(sectionUsed + "/marginfloating.right", 10).toInt(), - settings->value(sectionUsed + "/marginfloating.bottom", 10).toInt()); - this->setMarginsWhenFramelessFloating( - settings->value(sectionUsed + "/marginfloating.frameless.left", 5).toInt(), - settings->value(sectionUsed + "/marginfloating.frameless.top", 5).toInt(), - settings->value(sectionUsed + "/marginfloating.frameless.right", 5).toInt(), - settings->value(sectionUsed + "/marginfloating.frameless.bottom", 5).toInt()); - } - return true; + bool ok = true, ok1, ok2, ok3, ok4; + this->setMarginsWhenDocked( + settings->value(sectionUsed + "/margindocked.left", 1).toInt(&ok1), + settings->value(sectionUsed + "/margindocked.top", 1).toInt(&ok2), + settings->value(sectionUsed + "/margindocked.right", 1).toInt(&ok3), + settings->value(sectionUsed + "/margindocked.bottom", 1).toInt(&ok4)); + if (!(ok1 && ok2 && ok3 && ok4)) { CLogMessage(this).error("Error in docked margins"); ok = false; } + + this->setMarginsWhenFloating( + settings->value(sectionUsed + "/marginfloating.left", 10).toInt(&ok1), + settings->value(sectionUsed + "/marginfloating.top", 10).toInt(&ok2), + settings->value(sectionUsed + "/marginfloating.right", 10).toInt(&ok3), + settings->value(sectionUsed + "/marginfloating.bottom", 10).toInt(&ok4)); + if (!(ok1 && ok2 && ok3 && ok4)) { CLogMessage(this).error("Error in floating margins"); ok = false; } + + this->setMarginsWhenFramelessFloating( + settings->value(sectionUsed + "/marginfloating.frameless.left", 5).toInt(&ok1), + settings->value(sectionUsed + "/marginfloating.frameless.top", 5).toInt(&ok2), + settings->value(sectionUsed + "/marginfloating.frameless.right", 5).toInt(&ok3), + settings->value(sectionUsed + "/marginfloating.frameless.bottom", 5).toInt(&ok4)); + if (!(ok1 && ok2 && ok3 && ok4)) { CLogMessage(this).error("Error in floating (frameless) margins"); ok = false; } + + return ok; } void CDockWidget::ps_onTopLevelChanged(bool topLevel) @@ -388,7 +394,7 @@ namespace BlackGui Q_ASSERT(compWidget); if (!compWidget) { return; } QSizePolicy sizePolicy = compWidget->sizePolicy(); - sizePolicy.setVerticalStretch(1); + sizePolicy.setVerticalStretch(1); // make the original widget occupying maximum space compWidget->setSizePolicy(sizePolicy); } diff --git a/src/blackgui/enableforframelesswindow.cpp b/src/blackgui/enableforframelesswindow.cpp index a85612fee..b10d77692 100644 --- a/src/blackgui/enableforframelesswindow.cpp +++ b/src/blackgui/enableforframelesswindow.cpp @@ -18,17 +18,18 @@ using namespace BlackMisc; namespace BlackGui { - CEnableForFramelessWindow::CEnableForFramelessWindow(CEnableForFramelessWindow::WindowMode mode, bool isMainApplicationWindow, QWidget *correspondingWidget) : - m_windowMode(mode), m_mainApplicationWindow(isMainApplicationWindow), m_widget(correspondingWidget) + CEnableForFramelessWindow::CEnableForFramelessWindow(CEnableForFramelessWindow::WindowMode mode, bool isMainApplicationWindow, const char *framelessPropertyName, QWidget *correspondingWidget) : + m_windowMode(mode), m_mainApplicationWindow(isMainApplicationWindow), m_widget(correspondingWidget), m_framelessPropertyName(framelessPropertyName) { Q_ASSERT(correspondingWidget); + Q_ASSERT(!m_framelessPropertyName.isEmpty()); this->setWindowAttributes(mode); } void CEnableForFramelessWindow::setMode(CEnableForFramelessWindow::WindowMode mode) { if (mode == this->m_windowMode) { return; } - // set the main window or dock widget + // set the main window or dock widget flags and attributes this->m_widget->setWindowFlags(modeToWindowFlags(mode)); this->setWindowAttributes(mode); this->m_widget->show(); @@ -37,18 +38,40 @@ namespace BlackGui void CEnableForFramelessWindow::setFrameless(bool frameless) { - setMode(frameless ? WindowFrameless : WindowNormal); + setMode(frameless ? WindowFrameless : WindowTool); } void CEnableForFramelessWindow::setWindowAttributes(CEnableForFramelessWindow::WindowMode mode) { + Q_ASSERT_X(this->m_widget, "CEnableForFramelessWindow::setWindowAttributes", "Missing widget representing window"); + Q_ASSERT_X(!this->m_framelessPropertyName.isEmpty(), "CEnableForFramelessWindow::setWindowAttributes", "Missing property name"); + bool frameless = (mode == WindowFrameless); // http://stackoverflow.com/questions/18316710/frameless-and-transparent-window-qt5 this->m_widget->setAttribute(Qt::WA_NoSystemBackground, frameless); this->m_widget->setAttribute(Qt::WA_TranslucentBackground, frameless); + // Qt::WA_PaintOnScreen leads to a warning + // setMask(QRegion(10, 10, 10, 10) would work, but requires "complex" calcs for rounded corners + //! \todo Transparent dock widget,try out void QWidget::setMask + this->setDynamicProperties(frameless); + } + + void CEnableForFramelessWindow::setDynamicProperties(bool frameless) + { + Q_ASSERT_X(this->m_widget, "CEnableForFramelessWindow::setDynamicProperties", "Missing widget representing window"); + Q_ASSERT_X(!this->m_framelessPropertyName.isEmpty(), "CEnableForFramelessWindow::setDynamicProperties", "Missing property name"); + // property selector will check on string, so I directly provide a string - this->m_widget->setProperty("frameless", BlackMisc::boolToTrueFalse(frameless)); + const QString f(BlackMisc::boolToTrueFalse(frameless)); + this->m_widget->setProperty(this->m_framelessPropertyName.constData(), f); + for (QObject *w : this->m_widget->children()) + { + if (w && w->isWidgetType()) + { + w->setProperty(this->m_framelessPropertyName.constData(), f); + } + } } bool CEnableForFramelessWindow::handleMouseMoveEvent(QMouseEvent *event) @@ -117,17 +140,35 @@ namespace BlackGui return menuBarLayout; } + void CEnableForFramelessWindow::toolToNormalWindow() + { + this->m_widget->setWindowFlags((this->m_widget->windowFlags() & (~Qt::Tool)) | Qt::Window); + } + + void CEnableForFramelessWindow::normalToToolWindow() + { + this->m_widget->setWindowFlags(this->m_widget->windowFlags() | Qt::Tool); + } + + bool CEnableForFramelessWindow::isToolWindow() const + { + + return (this->m_widget->windowFlags() & Qt::Tool) == Qt::Tool; + } + Qt::WindowFlags CEnableForFramelessWindow::modeToWindowFlags(CEnableForFramelessWindow::WindowMode mode) { switch (mode) { case WindowFrameless: return (Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); - case WindowNormal: - default: + case WindowTool: // tool window and minimized not supported on windows // tool window always with close button on windows return (Qt::Tool | Qt::WindowStaysOnTopHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); + case WindowNormal: + default: + return (Qt::Desktop | Qt::WindowStaysOnTopHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); } } diff --git a/src/blackgui/enableforframelesswindow.h b/src/blackgui/enableforframelesswindow.h index 3ff969f9f..f0cdaaca6 100644 --- a/src/blackgui/enableforframelesswindow.h +++ b/src/blackgui/enableforframelesswindow.h @@ -33,11 +33,12 @@ namespace BlackGui enum WindowMode { WindowFrameless, - WindowNormal + WindowNormal, + WindowTool }; //! Constructor - CEnableForFramelessWindow(WindowMode mode, bool isMainApplicationWindow, QWidget *correspondingWidget); + CEnableForFramelessWindow(WindowMode mode, bool isMainApplicationWindow, const char *framelessPropertyname, QWidget *correspondingWidget); //! Window mode void setMode(WindowMode mode); @@ -64,18 +65,31 @@ namespace BlackGui //! Attributes void setWindowAttributes(WindowMode mode); + //! Set dynamic properties such as frameless + void setDynamicProperties(bool frameless); + //! Close button for frameless windows QHBoxLayout *addFramelessCloseButton(QMenuBar *menuBar); + //! Remove tool and add desktop window + void toolToNormalWindow(); + + //! Remove desktop and add tool window + void normalToToolWindow(); + + //! Tool window + bool isToolWindow() const; + //! Translate mode static Qt::WindowFlags modeToWindowFlags(WindowMode mode); QPoint m_framelessDragPosition; //!< position, if moving is handled with frameless window */ QPushButton *m_framelessCloseButton = nullptr; //!< close button - WindowMode m_windowMode = WindowNormal; //!< Window mode, \sa WindowMode + WindowMode m_windowMode = WindowTool; //!< Window mode, \sa WindowMode bool m_mainApplicationWindow = false; //!< is the main application window (only 1) QWidget *m_widget = nullptr; //!< corresponding main window or dock widget QSizeGrip *m_framelessSizeGrip = nullptr; //!< size grip object + QByteArray m_framelessPropertyName; //!< property name for frameless widgets //! Mouse press, required for frameless window bool handleMousePressEvent(QMouseEvent *event); diff --git a/src/blackgui/guiutility.cpp b/src/blackgui/guiutility.cpp index e6c9e007c..bdb82b193 100644 --- a/src/blackgui/guiutility.cpp +++ b/src/blackgui/guiutility.cpp @@ -83,4 +83,32 @@ namespace BlackGui if (index < 0) { return QString(oldName).trimmed().append(v); } return QString(oldName.left(index)).trimmed().append(v); } -} + + void CGuiUtility::deleteLayout(QLayout *layout, bool deleteWidgets) + { + // http://stackoverflow.com/a/7569928/356726 + if (!layout) { return; } + QLayoutItem *item {nullptr}; + QLayout *sublayout {nullptr}; + QWidget *widget {nullptr}; + while ((item = layout->takeAt(0))) + { + if ((sublayout = item->layout())) + { + deleteLayout(sublayout, deleteWidgets); + } + else if ((widget = item->widget())) + { + widget->hide(); + if (deleteWidgets) + { + delete widget; + } + } + else {delete item;} + } + + // then finally + delete layout; + } +} // ns diff --git a/src/blackgui/guiutility.h b/src/blackgui/guiutility.h index 88bf38dc2..a8e7d8567 100644 --- a/src/blackgui/guiutility.h +++ b/src/blackgui/guiutility.h @@ -47,6 +47,9 @@ namespace BlackGui //! Replace count in name such as "stations (4)" static QString replaceTabCountValue(const QString &oldName, int count); + //! Delete hierarchy of layouts + static void deleteLayout(QLayout *layout, bool deleteWidgets); + private: //! Constructor, use static methods only CGuiUtility() {} diff --git a/src/blackgui/infoarea.cpp b/src/blackgui/infoarea.cpp index 88a271af2..0c8053715 100644 --- a/src/blackgui/infoarea.cpp +++ b/src/blackgui/infoarea.cpp @@ -27,7 +27,7 @@ using namespace BlackMisc; namespace BlackGui { CInfoArea::CInfoArea(QWidget *parent) : - QMainWindow(parent), CEnableForFramelessWindow(CEnableForFramelessWindow::WindowNormal, false, this) + QMainWindow(parent), CEnableForFramelessWindow(CEnableForFramelessWindow::WindowTool, false, "framelessInfoArea", this) { this->ps_setWholeInfoAreaFloating(this->m_infoAreaFloating); this->setWindowIcon(CIcons::swift24()); @@ -69,7 +69,7 @@ namespace BlackGui void CInfoArea::addToContextMenu(QMenu *menu) const { - if (!menu) return; + if (!menu) { return; } bool hasDockedWidgets = this->countDockedWidgetInfoAreas() > 0; if (hasDockedWidgets) { @@ -147,8 +147,8 @@ namespace BlackGui bool CInfoArea::isSelectedDockWidgetInfoArea(const CDockWidgetInfoArea *infoArea) const { - if (!infoArea) return false; - if (infoArea->isFloating()) return false; + if (!infoArea) { return false; } + if (infoArea->isFloating()) { return false; } return infoArea == this->getSelectedDockInfoArea(); } @@ -188,13 +188,16 @@ namespace BlackGui QList CInfoArea::getInfoAreaSelectActions(QWidget *parent) const { + Q_ASSERT(parent); int i = 0; QList actions; - foreach(const CDockWidgetInfoArea * dockWidgetInfoArea, m_dockWidgetInfoAreas) + for (const CDockWidgetInfoArea *dockWidgetInfoArea : m_dockWidgetInfoAreas) { const QPixmap pm = this->indexToPixmap(i); - QAction *action = new QAction(QIcon(pm), dockWidgetInfoArea->windowTitleBackup(), parent); + const QString wt(dockWidgetInfoArea->windowTitleBackup()); + QAction *action = new QAction(QIcon(pm), wt, parent); action->setData(i); + action->setObjectName(this->objectName().append(":getInfoAreaSelectActions:").append(wt)); connect(action, &QAction::triggered, this, &CInfoArea::selectAreaByAction); actions.append(action); i++; @@ -202,6 +205,25 @@ namespace BlackGui return actions; } + QList CInfoArea::getInfoAreaToggleFloatingActions(QWidget *parent) const + { + Q_ASSERT(parent); + int i = 0; + QList actions; + for (const CDockWidgetInfoArea *dockWidgetInfoArea : m_dockWidgetInfoAreas) + { + const QPixmap pm = this->indexToPixmap(i); + const QString wt(dockWidgetInfoArea->windowTitleBackup()); + QAction *action = new QAction(QIcon(pm), wt, parent); + action->setData(i); + action->setObjectName(this->objectName().append(":getInfoAreaToggleFloatingActions:").append(wt)); + connect(action, &QAction::triggered, this, &CInfoArea::toggleAreaFloatingByAction); + actions.append(action); + i++; + } + return actions; + } + QList CInfoArea::getAreaIndexesDockedOrFloating(bool floating) const { QList indexes; @@ -306,7 +328,18 @@ namespace BlackGui Q_ASSERT(sender); const QAction *action = qobject_cast(sender); Q_ASSERT(action); - this->selectArea(action->data().toInt()); + int index = action->data().toInt(); + this->selectArea(index); + } + + void CInfoArea::toggleAreaFloatingByAction() + { + const QObject *sender = QObject::sender(); + Q_ASSERT(sender); + const QAction *action = qobject_cast(sender); + Q_ASSERT(action); + int index = action->data().toInt(); + this->toggleFloatingByIndex(index); } void CInfoArea::selectLeftTab() @@ -392,7 +425,6 @@ namespace BlackGui void CInfoArea::tabifyAllWidgets() { - // this->setDockArea(Qt::LeftDockWidgetArea); this->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::East); bool init = this->m_tabBar ? false : true; diff --git a/src/blackgui/infoarea.h b/src/blackgui/infoarea.h index 3955d13a8..7923fb658 100644 --- a/src/blackgui/infoarea.h +++ b/src/blackgui/infoarea.h @@ -54,6 +54,11 @@ namespace BlackGui //! \param parent which will own the action (deletion) QList getInfoAreaSelectActions(QWidget *parent) const; + //! Create a list of actions to select the info areas and toogle its floating state. + //! This could be used in a menu or somewhere else. + //! \param parent which will own the action (deletion) + QList getInfoAreaToggleFloatingActions(QWidget *parent) const; + //! Docked area indexes QList getAreaIndexesDockedOrFloating(bool floating) const; @@ -95,6 +100,9 @@ namespace BlackGui //! Select area (sender is QAction) void selectAreaByAction(); + //! Toggle area floating (sender is QAction) + void toggleAreaFloatingByAction(); + //! Select next left tab void selectLeftTab(); diff --git a/src/blackgui/managedstatusbar.cpp b/src/blackgui/managedstatusbar.cpp index 8416c7618..97afba137 100644 --- a/src/blackgui/managedstatusbar.cpp +++ b/src/blackgui/managedstatusbar.cpp @@ -39,14 +39,14 @@ namespace BlackGui this->m_statusBarIcon = new QLabel(this->m_statusBar); this->m_statusBarLabel = new QLabel(this->m_statusBar); - this->m_statusBarLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + this->m_statusBarLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); this->m_statusBarIcon->setObjectName(QString("lbl_StatusBarIcon").append(this->m_statusBar->objectName())); this->m_statusBarLabel->setObjectName(QString("lbl_StatusBarLabel").append(this->m_statusBar->objectName())); // use insert to insert from left to right // this keeps any grip on the right size - this->m_statusBar->insertPermanentWidget(0, this->m_statusBarIcon, 0); - this->m_statusBar->insertPermanentWidget(1, this->m_statusBarLabel, 1); + this->m_statusBar->insertPermanentWidget(0, this->m_statusBarIcon, 0); // status icon + this->m_statusBar->insertPermanentWidget(1, this->m_statusBarLabel, 1); // status text // timer this->m_timerStatusBar = new QTimer(this); diff --git a/src/blackgui/qss/gui.ini b/src/blackgui/qss/gui.ini index dc24b1c0d..9c7c03c6b 100644 --- a/src/blackgui/qss/gui.ini +++ b/src/blackgui/qss/gui.ini @@ -4,39 +4,59 @@ ; this alias makes sense as long there is one object per component CMainInfoAreaComponent = comp_MainInfoArea CCockpitInfoAreaComponent = comp_CockpitInfoArea +CInvisibleInfoAreaComponent = comp_InvisibleInfoArea CInfoBarStatusComponent = dw_dw_InfoBarStatus [comp_MainInfoArea] -margindocked.left = 1 -margindocked.right = 1 -margindocked.top = 1 -margindocked.bottom = 1 +margindocked.left = 0 +margindocked.right = 0 +margindocked.top = 0 +margindocked.bottom = 0 -marginfloating.left = 5 -marginfloating.right = 20 -marginfloating.top = 5 -marginfloating.bottom = 40 +; why the odd numbers?? +marginfloating.left = 0 +marginfloating.right = 15 +marginfloating.top = 0 +marginfloating.bottom = 35 -marginfloating.frameless.left = 5 -marginfloating.frameless.right = 5 -marginfloating.frameless.top = 5 -marginfloating.frameless.bottom = 5 +marginfloating.frameless.left = 0 +marginfloating.frameless.right = 0 +marginfloating.frameless.top = 0 +marginfloating.frameless.bottom = 0 [comp_CockpitInfoArea] -margindocked.left = 1 -margindocked.right = 1 -margindocked.top = 1 -margindocked.bottom = 1 +margindocked.left = 0 +margindocked.right = 0 +margindocked.top = 0 +margindocked.bottom = 0 -marginfloating.left = 5 -marginfloating.right = 20 -marginfloating.top = 5 -marginfloating.bottom = 40 +; why the odd numbers?? +marginfloating.left = 0 +marginfloating.right = 15 +marginfloating.top = 0 +marginfloating.bottom = 35 -marginfloating.frameless.left = 5 -marginfloating.frameless.right = 5 -marginfloating.frameless.top = 5 -marginfloating.frameless.bottom = 5 +marginfloating.frameless.left = 0 +marginfloating.frameless.right = 0 +marginfloating.frameless.top = 0 +marginfloating.frameless.bottom = 0 + +[comp_InvisibleInfoArea] +margindocked.left = 0 +margindocked.right = 0 +margindocked.top = 0 +margindocked.bottom = 0 + +; why the odd numbers?? +marginfloating.left = 0 +marginfloating.right = 15 +marginfloating.top = 0 +marginfloating.bottom = 35 + +marginfloating.frameless.left = 0 +marginfloating.frameless.right = 0 +marginfloating.frameless.top = 0 +marginfloating.frameless.bottom = 0 [dw_InfoBarStatus] margindocked.left = 0 diff --git a/src/blackgui/qss/infobar.qss b/src/blackgui/qss/infobar.qss index fffd7a984..edb37527d 100644 --- a/src/blackgui/qss/infobar.qss +++ b/src/blackgui/qss/infobar.qss @@ -1,6 +1,6 @@ /** style is applied to a CDockWidgetInfoBar **/ /** frameless is dynamic property**/ -/** for frameless only use QDockWidget[frameless="true"] QFrame **/ +/** for frameless only use QDockWidget[framelessDockWidget="true"] QFrame **/ QFrame { margin: 0px; diff --git a/src/blackgui/qss/mainwindow.qss b/src/blackgui/qss/mainwindow.qss index 18f65833d..f2d8aec22 100644 --- a/src/blackgui/qss/mainwindow.qss +++ b/src/blackgui/qss/mainwindow.qss @@ -8,7 +8,7 @@ Remarks: 2) use -- instead of :: for namespaces Used dynamic properties -frameless (infobar.qss , here) +framelessMainWindow (infobar.qss , here) **/ /** Main window **/ @@ -17,14 +17,26 @@ QMainWindow { background-color: darkslategray; } -/** required when dock widget is floating **/ -/** background-image not working on QDockWidget, so I use direct children **/ -BlackGui--CDockWidgetInfoArea[frameless="false"] { - background-color: black; +/** separator between info areas and rest **/ +/** this hides them **/ +QMainWindow::separator { + background: transparent; + width: 0px; /* when vertical */ + height: 0px; /* when horizontal */ } -BlackGui--CDockWidgetInfoArea[frameless="true"] { - background-color: white; +QMainWindow::separator:hover { + background: transparent; +} + +/** + Required when dock widget is floating + 1) background-image not working on QDockWidget, so I use direct children for that + 2) seems to have only effect as normal (floating) window + 3) Borders between this widget and the inner child is the margin in gui.ini +**/ +BlackGui--CDockWidgetInfoArea { + background-color: green; /** I use green here so I can adjust the borders in gui.ini **/ } /** this is the first widget in the dock area **/ @@ -62,12 +74,12 @@ QAbstractScrollArea #qw_RemarksGenerator { background-color: transparent; backg /** in log component **/ QAbstractScrollArea #pg_StatusPageMessages { background-color: black; } -QAbstractScrollArea #pg_StatusPageConsole { background-color: black; } +QAbstractScrollArea #pg_StatusPageCons ole { background-color: black; } /** main GUI parts **/ /** style when main window is frameless **/ -#wi_CentralWidgetOutside[frameless="true"] { +#wi_CentralWidgetOutside[framelessMainWindow="true"] { background-image: url(:/textures/icons/textures/texture-outer.jpg); background-color: darkslategray; margin: 0px; diff --git a/src/blackgui/qss/navigator.qss b/src/blackgui/qss/navigator.qss new file mode 100644 index 000000000..c86497165 --- /dev/null +++ b/src/blackgui/qss/navigator.qss @@ -0,0 +1,9 @@ +#fr_NavigatorDockWidgetInner[framelessDockWidget="true"] { + margin: 0px; + border: 2px solid green; + border-radius: 20px; +} + +#fr_NavigatorDockWidgetInner QToolButton { + margin: 0px; +} diff --git a/src/blackgui/stylesheetutility.cpp b/src/blackgui/stylesheetutility.cpp index 8ef16c59b..a3c1d91e1 100644 --- a/src/blackgui/stylesheetutility.cpp +++ b/src/blackgui/stylesheetutility.cpp @@ -244,6 +244,12 @@ namespace BlackGui return f; } + const QString &CStyleSheetUtility::fileNameNavigator() + { + static const QString f("navigator.qss"); + return f; + } + const QString &CStyleSheetUtility::fileNameDockWidgetTab() { static const QString f("dockwidgettab.qss"); diff --git a/src/blackgui/stylesheetutility.h b/src/blackgui/stylesheetutility.h index a8e4fe487..dc1080445 100644 --- a/src/blackgui/stylesheetutility.h +++ b/src/blackgui/stylesheetutility.h @@ -71,6 +71,9 @@ namespace BlackGui //! File name infobar.qss static const QString &fileNameInfoBar(); + //! File name navigator.qss + static const QString &fileNameNavigator(); + //! File name dockwidgettab.qss static const QString &fileNameDockWidgetTab(); diff --git a/src/blackmisc/icons.cpp b/src/blackmisc/icons.cpp index a391cee86..b0f717770 100644 --- a/src/blackmisc/icons.cpp +++ b/src/blackmisc/icons.cpp @@ -9,6 +9,7 @@ #include "icons.h" #include +#include #include namespace BlackMisc @@ -290,6 +291,18 @@ namespace BlackMisc return pm; } + const QPixmap &CIcons::empty() + { + static const QPixmap pm; + return pm; + } + + const QPixmap &CIcons::empty16() + { + static const QPixmap pm(16, 16); + return pm; + } + const QPixmap &CIcons::arrowMediumNorth16() { static const QPixmap pm(":/diagona/icons/diagona/icons/arrow-090-medium.png"); @@ -338,6 +351,71 @@ namespace BlackMisc return pm; } + const QPixmap &CIcons::appWeather16() + { + return weatherCloudy16(); + } + + const QPixmap &CIcons::appSettings16() + { + return wrench16(); + } + + const QPixmap &CIcons::appUsers16() + { + return users16(); + } + + const QPixmap &CIcons::appFlightPlan16() + { + return tableSheet16(); + } + + const QPixmap &CIcons::appCockpit16() + { + return radio16(); + } + + const QPixmap &CIcons::appSimulator16() + { + return joystick16(); + } + + const QPixmap &CIcons::appTextMessages16() + { + return text16(); + } + + const QPixmap &CIcons::appAtc16() + { + return radar16(); + } + + const QPixmap &CIcons::appAircrafts16() + { + return paperPlane16(); + } + + const QPixmap &CIcons::appMappings16() + { + return tableRelationship16(); + } + + const QPixmap &CIcons::appLog16() + { + return monitorError16(); + } + + const QPixmap &CIcons::appAudio16() + { + return speakerNetwork16(); + } + + const QPixmap &CIcons::appVoiceRooms16() + { + return tableRelationship16(); + } + const QPixmap &CIcons::roleC1() { static const QPixmap pm(":/vatsim/icons/vatsim/C1.png"); @@ -483,7 +561,7 @@ namespace BlackMisc const QPixmap &CIcons::capabilityVoiceBackground() { - static const QPixmap pm(changeBackground(":/diagona/icons/diagona/icons/headphone.png", Qt::green)); + static const QPixmap pm(changeResourceBackgroundColor(":/diagona/icons/diagona/icons/headphone.png", Qt::green)); return pm; } @@ -495,16 +573,41 @@ namespace BlackMisc const QPixmap &CIcons::capabilityVoiceReceiveOnlyBackground() { - static const QPixmap pm(changeBackground(":/diagona/icons/diagona/icons/headphone.png", Qt::yellow)); + static const QPixmap pm(changeResourceBackgroundColor(":/diagona/icons/diagona/icons/headphone.png", Qt::yellow)); return pm; } + const QPixmap &CIcons::capabilityTextOnly() + { + return appTextMessages16(); + } + + const QPixmap &CIcons::capabilityUnknown() + { + return unknown16(); + } + const QPixmap &CIcons::attitudeIndicator16() { static const QPixmap pm(":/own/icons/own/attitude_indicator_climbing_16.png"); return pm; } + const QPixmap &CIcons::metar() + { + return weatherCloudy16(); + } + + const QPixmap &CIcons::atis() + { + return text16(); + } + + const QPixmap &CIcons::geoPosition16() + { + return globe16(); + } + const QPixmap &CIcons::pixmapByIndex(CIcons::IconIndex index) { switch (index) @@ -632,15 +735,28 @@ namespace BlackMisc return rotated; } - QPixmap CIcons::changeBackground(const QString resource, Qt::GlobalColor backgroundColor) + QImage CIcons::changeImageBackgroundColor(const QImage &imgSource, Qt::GlobalColor backgroundColor) { - QImage resSource(resource); - QImage destBackground(resSource.size(), QImage::Format_RGB32); - destBackground.fill(backgroundColor); - QPainter p(&destBackground); + QImage destBackgroundImg(imgSource.size(), QImage::Format_RGB32); + destBackgroundImg.fill(backgroundColor); + QPainter p(&destBackgroundImg); p.setCompositionMode(QPainter::CompositionMode_SourceAtop); - p.drawImage(0, 0, resSource); - return QPixmap::fromImage(destBackground); + p.drawImage(0, 0, imgSource); + return destBackgroundImg; + } + + QPixmap CIcons::changeResourceBackgroundColor(const QString &resource, Qt::GlobalColor backgroundColor) + { + QImage imgSource(resource); + QImage destBackgroundImg(changeImageBackgroundColor(imgSource, backgroundColor)); + return QPixmap::fromImage(destBackgroundImg); + } + + QIcon CIcons::changeIconBackgroundColor(const QIcon &icon, Qt::GlobalColor backgroundColor, const QSize &targetsize) + { + QImage imgSource(icon.pixmap(targetsize).toImage()); + QImage destBackgroundImg(changeImageBackgroundColor(imgSource, backgroundColor)); + return QIcon(QPixmap::fromImage(destBackgroundImg)); } } // namespace diff --git a/src/blackmisc/icons.h b/src/blackmisc/icons.h index f3cd2142b..78cf4d258 100644 --- a/src/blackmisc/icons.h +++ b/src/blackmisc/icons.h @@ -281,18 +281,10 @@ namespace BlackMisc static const QPixmap &unknown16(); //! Empty icon - static const QPixmap &empty() - { - static const QPixmap pm; - return pm; - } + static const QPixmap &empty(); //! Empty icon - static const QPixmap &empty16() - { - static const QPixmap pm(16, 16); - return pm; - } + static const QPixmap &empty16(); //! Arrow static const QPixmap &arrowMediumNorth16(); @@ -319,82 +311,43 @@ namespace BlackMisc static const QPixmap &swiftNova48(); //! Application weather - static const QPixmap &appWeather16() - { - return weatherCloudy16(); - } + static const QPixmap &appWeather16(); //! Application settings - static const QPixmap &appSettings16() - { - return wrench16(); - } + static const QPixmap &appSettings16(); //! Application users - static const QPixmap &appUsers16() - { - return users16(); - } + static const QPixmap &appUsers16(); //! Application flight plan - static const QPixmap &appFlightPlan16() - { - return tableSheet16(); - } + static const QPixmap &appFlightPlan16(); //! Application cockpit - static const QPixmap &appCockpit16() - { - return radio16(); - } + static const QPixmap &appCockpit16(); //! Application simulator - static const QPixmap &appSimulator16() - { - return joystick16(); - } + static const QPixmap &appSimulator16(); //! Application text messages - static const QPixmap &appTextMessages16() - { - return text16(); - } + static const QPixmap &appTextMessages16(); //! Application ATC - static const QPixmap &appAtc16() - { - return radar16(); - } + static const QPixmap &appAtc16(); //! Application aircrafts - static const QPixmap &appAircrafts16() - { - return paperPlane16(); - } + static const QPixmap &appAircrafts16(); //! Application mappings - static const QPixmap &appMappings16() - { - return tableRelationship16(); - } + static const QPixmap &appMappings16(); //! Application log/status messages - static const QPixmap &appLog16() - { - return monitorError16(); - } + static const QPixmap &appLog16(); //! Application audio - static const QPixmap &appAudio16() - { - return speakerNetwork16(); - } + static const QPixmap &appAudio16(); //! Voice rooms - static const QPixmap &appVoiceRooms16() - { - return tableRelationship16(); - } + static const QPixmap &appVoiceRooms16(); // ------------------------------------------------------------- // Network and aviation @@ -482,37 +435,22 @@ namespace BlackMisc static const QPixmap &capabilityVoiceReceiveOnlyBackground(); //! Text only - static const QPixmap &capabilityTextOnly() - { - return appTextMessages16(); - } + static const QPixmap &capabilityTextOnly(); //! Text only - static const QPixmap &capabilityUnknown() - { - return unknown16(); - } + static const QPixmap &capabilityUnknown(); //! Attitude indicator static const QPixmap &attitudeIndicator16(); //! METAR - static const QPixmap &metar() - { - return weatherCloudy16(); - } + static const QPixmap &metar(); //! ATIS - static const QPixmap &atis() - { - return text16(); - } + static const QPixmap &atis(); //! Geo position - static const QPixmap &geoPosition16() - { - return globe16(); - } + static const QPixmap &geoPosition16(); // ------------------------------------------------------------- // By index @@ -524,8 +462,19 @@ namespace BlackMisc //! Pixmap by given index rotated static QPixmap pixmapByIndex(IconIndex index, int rotateDegrees); + // ------------------------------------------------------------- + // Utility functions + // ------------------------------------------------------------- + //! Change color of resource - static QPixmap changeBackground(const QString resource, Qt::GlobalColor backgroundColor); + static QPixmap changeResourceBackgroundColor(const QString &resource, Qt::GlobalColor backgroundColor); + + //! Change color of icon + static QIcon changeIconBackgroundColor(const QIcon &icon, Qt::GlobalColor backgroundColor, const QSize &targetsize); + + //! Change image background color + static QImage changeImageBackgroundColor(const QImage &imgSource, Qt::GlobalColor backgroundColor); + }; } #endif // guard diff --git a/src/swiftgui_standard/introwindow.cpp b/src/swiftgui_standard/introwindow.cpp index 27d1c0257..e6f866347 100644 --- a/src/swiftgui_standard/introwindow.cpp +++ b/src/swiftgui_standard/introwindow.cpp @@ -49,7 +49,7 @@ BlackGui::CEnableForFramelessWindow::WindowMode CIntroWindow::getWindowMode() co if (this->ui->rb_WindowFrameless->isChecked()) return BlackGui::CEnableForFramelessWindow::WindowFrameless; else - return BlackGui::CEnableForFramelessWindow::WindowNormal; + return BlackGui::CEnableForFramelessWindow::WindowTool; } /* diff --git a/src/swiftgui_standard/swiftguistd.cpp b/src/swiftgui_standard/swiftguistd.cpp index 1d8d13e91..a9241a3de 100644 --- a/src/swiftgui_standard/swiftguistd.cpp +++ b/src/swiftgui_standard/swiftguistd.cpp @@ -41,34 +41,39 @@ using namespace BlackMisc::Hardware; */ SwiftGuiStd::SwiftGuiStd(BlackGui::CEnableForFramelessWindow::WindowMode windowMode, QWidget *parent) : QMainWindow(parent, CEnableForFramelessWindow::modeToWindowFlags(windowMode)), - CEnableForFramelessWindow(windowMode, true, this), + CEnableForFramelessWindow(windowMode, true, "framelessMainWindow", this), ui(new Ui::SwiftGuiStd) { // GUI ui->setupUi(this); - this->ui->wi_CentralWidgetOutside->setProperty("frameless", this->isFrameless()); + this->setDynamicProperties(windowMode == CEnableForFramelessWindow::WindowFrameless); this->m_compInfoWindow = new CInfoWindowComponent(this); // setupUi has to be first! } -/* - * Destructor - */ SwiftGuiStd::~SwiftGuiStd() { } +void SwiftGuiStd::mouseMoveEvent(QMouseEvent *event) +{ + if (!handleMouseMoveEvent(event)) + { + QMainWindow::mouseMoveEvent(event); + } +} + +void SwiftGuiStd::mousePressEvent(QMouseEvent *event) +{ + if (!handleMousePressEvent(event)) + { + QMainWindow::mousePressEvent(event); + } +} + void SwiftGuiStd::performGracefulShutdown() { - if (!this->m_init) { return; } - this->m_init = false; - - // tell GUI components to shut down - emit requestGracefulShutdown(); - - // tell context GUI is going down - if (this->getIContextApplication()) - { - this->getIContextApplication()->notifyAboutComponentChange(IContextApplication::ApplicationGui, IContextApplication::ApplicationStops); - } + // clean up GUI + this->ui->comp_MainInfoArea->dockAllWidgets(); + this->ui->comp_InvisibleInfoArea->dockAllWidgets(); // close info window if (this->m_compInfoWindow) @@ -77,9 +82,21 @@ void SwiftGuiStd::performGracefulShutdown() this->m_compInfoWindow = nullptr; } + if (!this->m_init) { return; } + this->m_init = false; + + // tell GUI components to shut down + emit requestGracefulShutdown(); + // shut down all timers this->stopAllTimers(true); + // tell context GUI is going down + if (this->getIContextApplication()) + { + this->getIContextApplication()->notifyAboutComponentChange(IContextApplication::ApplicationGui, IContextApplication::ApplicationStops); + } + // if we have a context, we shut some things down if (this->m_contextNetworkAvailable) { @@ -103,6 +120,73 @@ void SwiftGuiStd::closeEvent(QCloseEvent *event) QApplication::exit(); } +void SwiftGuiStd::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange) + { + // make sure a tool window is changed to Normal window so it is show in taskbar + if (!this->isFrameless()) + { + bool toolWindow(this->isToolWindow()); + if (isMinimized()) + { + if (toolWindow) + { + // still tool, force normal window + BlackMisc::singleShot(0, QThread::currentThread(), [ = ]() + { + this->ps_showMinimized(); + }); + } + } + else + { + if (!toolWindow) + { + // not tool, force tool window + BlackMisc::singleShot(0, QThread::currentThread(), [ = ]() + { + this->ps_showNormal(); + }); + } + } + } // frameless? + } + QMainWindow::changeEvent(event); +} + +QAction *SwiftGuiStd::getWindowMinimizeAction(QObject *parent) +{ + QIcon i(CIcons::changeIconBackgroundColor(this->style()->standardIcon(QStyle::SP_TitleBarMinButton), Qt::white, QSize(16, 16))); + QAction *a = new QAction(i, "Window minimized", parent); + connect(a, &QAction::triggered, this, &SwiftGuiStd::ps_showMinimized); + return a; +} + +QAction *SwiftGuiStd::getWindowNormalAction(QObject *parent) +{ + QIcon i(CIcons::changeIconBackgroundColor(this->style()->standardIcon(QStyle::SP_TitleBarNormalButton), Qt::white, QSize(16, 16))); + QAction *a = new QAction(i, "Window normal", parent); + connect(a, &QAction::triggered, this, &SwiftGuiStd::ps_showNormal); + return a; +} + +QAction *SwiftGuiStd::getToggleWindowVisibilityAction(QObject *parent) +{ + QIcon i(CIcons::changeIconBackgroundColor(this->style()->standardIcon(QStyle::SP_TitleBarShadeButton), Qt::white, QSize(16, 16))); + QAction *a = new QAction(i, "Toogle main window visibility", parent); + connect(a, &QAction::triggered, this, &SwiftGuiStd::ps_toggleWindowVisibility); + return a; +} + +QAction *SwiftGuiStd::getToggleStayOnTopAction(QObject *parent) +{ + QIcon i(CIcons::changeIconBackgroundColor(this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton), Qt::white, QSize(16, 16))); + QAction *a = new QAction(i, "Toogle main window on top", parent); + connect(a, &QAction::triggered, this, &SwiftGuiStd::ps_toogleWindowStayOnTop); + return a; +} + void SwiftGuiStd::ps_setMainPage(SwiftGuiStd::MainPageIndex mainPage) { this->ui->sw_MainMiddle->setCurrentIndex(mainPage); @@ -228,7 +312,7 @@ void SwiftGuiStd::updateGuiStatusInformation() if (this->m_contextNetworkAvailable) { bool dbus = !this->getIContextNetwork()->isUsingImplementingObject(); - network = dbus ? now : "local"; + network = dbus ? now : "local"; this->ui->comp_InfoBarStatus->setDBusStatus(dbus); } @@ -253,18 +337,30 @@ void SwiftGuiStd::ps_toogleWindowStayOnTop() { flags ^= Qt::WindowStaysOnTopHint; flags |= Qt::WindowStaysOnBottomHint; - this->ps_displayStatusMessageInGui(CLogMessage(this).info("Window on bottom")); + CLogMessage(this).info("Window on bottom"); } else { flags ^= Qt::WindowStaysOnBottomHint; flags |= Qt::WindowStaysOnTopHint; - this->ps_displayStatusMessageInGui(CLogMessage(this).info("Window on top")); + CLogMessage(this).info("Window on top"); } this->setWindowFlags(flags); this->show(); } +void SwiftGuiStd::ps_toggleWindowVisibility() +{ + if (this->isVisible()) + { + this->hide(); + } + else + { + this->show(); + } +} + void SwiftGuiStd::ps_registerHotkeyFunctions() { CInputManager *m_inputManager = BlackCore::CInputManager::getInstance(); @@ -308,6 +404,18 @@ void SwiftGuiStd::ps_onChangedMainInfoAreaFloating(bool floating) Q_UNUSED(floating); } +void SwiftGuiStd::ps_showMinimized() +{ + if (!this->isFrameless()) { toolToNormalWindow(); } + this->showMinimized(); +} + +void SwiftGuiStd::ps_showNormal() +{ + if (!this->isFrameless()) { normalToToolWindow(); } + this->showNormal(); +} + void SwiftGuiStd::playNotifcationSound(CNotificationSounds::Notification notification) const { if (!this->m_contextAudioAvailable) return; diff --git a/src/swiftgui_standard/swiftguistd.h b/src/swiftgui_standard/swiftguistd.h index 92d829e73..3d99d04cb 100644 --- a/src/swiftgui_standard/swiftguistd.h +++ b/src/swiftgui_standard/swiftguistd.h @@ -84,14 +84,29 @@ signals: protected: //! \copydoc QMainWindow::mouseMoveEvent - virtual void mouseMoveEvent(QMouseEvent *event) override { if (!handleMouseMoveEvent(event)) { QMainWindow::mouseMoveEvent(event); } ; } + virtual void mouseMoveEvent(QMouseEvent *event) override; //! \copydoc QMainWindow::mousePressEvent - virtual void mousePressEvent(QMouseEvent *event) override { if (!handleMousePressEvent(event)) { QMainWindow::mousePressEvent(event); } } + virtual void mousePressEvent(QMouseEvent *event) override; //! \copydoc QMainWindow::closeEvent virtual void closeEvent(QCloseEvent *event) override; + //! \copydoc QMainWindow::changeEvent + virtual void changeEvent(QEvent *event) override; + + //! Get a minimize action which minimizes the window + QAction *getWindowMinimizeAction(QObject *parent); + + //! Get a normal window action which minimizes the window + QAction *getWindowNormalAction(QObject *parent); + + //! Toggle window visibility action + QAction *getToggleWindowVisibilityAction(QObject *parent); + + //! Toggle window stay on top action + QAction *getToggleStayOnTopAction(QObject *parent); + private: QScopedPointer ui; bool m_init = false; @@ -125,6 +140,9 @@ private: //! Init dynamic menus void initDynamicMenus(); + //! Menu icons where required + void initMenuIcons(); + //! Graceful shutdown void performGracefulShutdown(); @@ -140,7 +158,7 @@ private: //! Context availability, used by watchdog void setContextAvailability(); - //! \brief Position of own plane for testing + //! Position of own plane for testing //! \param wgsLatitude WGS latitude //! \param wgsLongitude WGS longitude //! \param altitude @@ -148,7 +166,6 @@ private: //! Is given main page selected? //! \param mainPage index to be checked - //! \return bool isMainPageSelected(MainPageIndex mainPage) const; //! Start all update timers @@ -157,11 +174,9 @@ private: //! Stop all update timers void stopUpdateTimersWhenDisconnected(); - /*! - * \brief Stop all timers - * \param disconnect also disconnect signal/slots - */ - void stopAllTimers(bool disconnect); + //! \brief Stop all timers + //! \param disconnect also disconnect signal/slots + void stopAllTimers(bool disconnectSignalSlots); //! Play notifcation sound void playNotifcationSound(BlackSound::CNotificationSounds::Notification notification) const; @@ -189,11 +204,9 @@ private slots: //! Settings have been changed void ps_onChangedSetttings(uint typeValue); - /*! - * \brief Connection status changed - * \param from old status, as int so it is compliant with DBus - * \param to new status, as int so it is compliant with DBus - */ + //! Connection status changed + //! \param from old status, as int so it is compliant with DBus + //! \param to new status, as int so it is compliant with DBus void ps_onConnectionStatusChanged(int from, int to); // @@ -224,9 +237,12 @@ private slots: //! Change opacity 0-100 void ps_onChangedWindowOpacity(int opacity = -1); - //! Toogle Windows stay on top + //! Toogle if windows stays on top void ps_toogleWindowStayOnTop(); + //! Toggle window visibility + void ps_toggleWindowVisibility(); + //! Set the hotkey functions void ps_registerHotkeyFunctions(); @@ -238,6 +254,12 @@ private slots: //! Whole main info area floating void ps_onChangedMainInfoAreaFloating(bool floating); + + //! Show window minimized + void ps_showMinimized(); + + //! Show window normal + void ps_showNormal(); }; #pragma pop_macro("interface") diff --git a/src/swiftgui_standard/swiftguistd.ui b/src/swiftgui_standard/swiftguistd.ui index 3ff1b4ee2..3ab0397e1 100644 --- a/src/swiftgui_standard/swiftguistd.ui +++ b/src/swiftgui_standard/swiftguistd.ui @@ -124,7 +124,7 @@ QFrame::NoFrame - 0 + 3 diff --git a/src/swiftgui_standard/swiftguistd_init.cpp b/src/swiftgui_standard/swiftguistd_init.cpp index c12be8724..48a1a37c1 100644 --- a/src/swiftgui_standard/swiftguistd_init.cpp +++ b/src/swiftgui_standard/swiftguistd_init.cpp @@ -15,6 +15,7 @@ #include "blackgui/guiutility.h" #include "blackgui/components/textmessagecomponent.h" #include "blackgui/components/cockpitcomponent.h" +#include "blackgui/components/navigatordockwidget.h" #include "blackgui/models/atcstationlistmodel.h" #include "blackgui/models/keyboardkeylistmodel.h" #include "blackmisc/icons.h" @@ -32,7 +33,6 @@ using namespace BlackMisc::Hardware; using namespace BlackGui; using namespace BlackGui::Components; - /* * Init data */ @@ -46,6 +46,7 @@ void SwiftGuiStd::init(const CRuntimeConfig &runtimeConfig) // icon, initial position where intro was before this->setWindowIcon(CIcons::swift24()); this->setWindowTitle(CProject::systemNameAndVersion()); + this->setObjectName("SwiftGuiStd"); QPoint pos = CGuiUtility::introWindowPosition(); this->move(pos); @@ -70,7 +71,11 @@ void SwiftGuiStd::init(const CRuntimeConfig &runtimeConfig) } // timers - if (this->m_timerContextWatchdog == nullptr) { this->m_timerContextWatchdog = new QTimer(this) ; } + if (this->m_timerContextWatchdog == nullptr) + { + this->m_timerContextWatchdog = new QTimer(this); + this->m_timerContextWatchdog->setObjectName(this->objectName().append(":m_timerContextWatchdog")); + } // context this->createRuntime(runtimeConfig, this); @@ -81,6 +86,17 @@ void SwiftGuiStd::init(const CRuntimeConfig &runtimeConfig) this->ui->dw_InfoBarStatus->allowStatusBar(false); this->ui->dw_InfoBarStatus->setPreferredSizeWhenFloating(this->ui->dw_InfoBarStatus->size()); // set floating size + // navigator + CNavigatorDockWidget *nav = this->ui->comp_InvisibleInfoArea->getNavigatorComponent(); + Q_ASSERT(nav); + nav->addAction(this->getToggleWindowVisibilityAction(nav)); + nav->addActions(this->ui->comp_MainInfoArea->getInfoAreaToggleFloatingActions(nav)); + nav->addAction(this->getWindowNormalAction(nav)); + nav->addAction(this->getWindowMinimizeAction(nav)); + nav->addAction(this->getToggleStayOnTopAction(nav)); + + this->ui->comp_InvisibleInfoArea->getNavigatorComponent()->buildNavigator(1); + // wire GUI signals this->initGuiSignals(); @@ -107,6 +123,7 @@ void SwiftGuiStd::init(const CRuntimeConfig &runtimeConfig) // start screen and complete menu this->ps_setMainPageToInfoArea(); this->initDynamicMenus(); + this->initMenuIcons(); // starting this->getIContextApplication()->notifyAboutComponentChange(IContextApplication::ApplicationGui, IContextApplication::ApplicationStarts); @@ -237,10 +254,10 @@ void SwiftGuiStd::stopUpdateTimersWhenDisconnected() /* * Stop all timers */ -void SwiftGuiStd::stopAllTimers(bool disconnect) +void SwiftGuiStd::stopAllTimers(bool disconnectSignalSlots) { this->m_timerContextWatchdog->stop(); this->stopUpdateTimersWhenDisconnected(); - if (!disconnect) return; + if (!disconnectSignalSlots) { return; } this->disconnect(this->m_timerContextWatchdog); } diff --git a/src/swiftgui_standard/swiftguistd_menus.cpp b/src/swiftgui_standard/swiftguistd_menus.cpp index ab21c31c5..8eaa2e366 100644 --- a/src/swiftgui_standard/swiftguistd_menus.cpp +++ b/src/swiftgui_standard/swiftguistd_menus.cpp @@ -70,7 +70,7 @@ void SwiftGuiStd::ps_onMenuClicked() } else if (sender == this->ui->menu_WindowMinimize) { - this->showMinimized(); + this->ps_showMinimized(); } else if (sender == this->ui->menu_WindowToggleOnTop) { @@ -107,3 +107,8 @@ void SwiftGuiStd::initDynamicMenus() this->ui->menu_InfoAreas->addActions(this->ui->comp_MainInfoArea->getInfoAreaSelectActions(this->ui->menu_InfoAreas)); } +void SwiftGuiStd::initMenuIcons() +{ + this->ui->menu_WindowMinimize->setIcon(this->style()->standardIcon(QStyle::SP_TitleBarMinButton)); +} +