From 936cc7c147158396048340d3039a3c5219014be3 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 12 Sep 2014 12:38:06 +0200 Subject: [PATCH] refs #325, as preparation to reduce loading I need to find out when an info area is visible. Existing functions visible() / isHidden() are not suitable, as those represent "technical visibility". We need a logical visibility telling us if the user can see / has selected the corresponing dockable widget. * New base class info area (where dockable widgets are placed) * Adjusted derived component (currently 1) * Cleaned up old code in floatAllWidgets --- .../components/maininfoareacomponent.cpp | 480 +----------------- .../components/maininfoareacomponent.h | 142 +----- src/blackgui/dockwidget.cpp | 63 ++- src/blackgui/dockwidget.h | 60 ++- src/blackgui/dockwidgetinfoarea.cpp | 70 ++- src/blackgui/dockwidgetinfoarea.h | 26 +- src/blackgui/dockwidgetinfobar.h | 2 + src/blackgui/infoarea.cpp | 477 +++++++++++++++++ src/blackgui/infoarea.h | 162 ++++++ 9 files changed, 854 insertions(+), 628 deletions(-) create mode 100644 src/blackgui/infoarea.cpp create mode 100644 src/blackgui/infoarea.h diff --git a/src/blackgui/components/maininfoareacomponent.cpp b/src/blackgui/components/maininfoareacomponent.cpp index d18743ec2..df12e78ec 100644 --- a/src/blackgui/components/maininfoareacomponent.cpp +++ b/src/blackgui/components/maininfoareacomponent.cpp @@ -18,39 +18,17 @@ #include using namespace BlackMisc; +using namespace BlackGui; namespace BlackGui { namespace Components { CMainInfoAreaComponent::CMainInfoAreaComponent(QWidget *parent) : - QMainWindow(parent), ui(new Ui::CMainInfoAreaComponent) + CInfoArea(parent), ui(new Ui::CMainInfoAreaComponent) { - this->ps_setInfoAreaFloating(this->m_infoAreaFloating); ui->setupUi(this); - this->setWindowIcon(CIcons::swift24()); - - // after setup, GUI established - if (this->m_dockableWidgets.isEmpty()) - { - this->m_dockableWidgets = this->findChildren(); - Q_ASSERT(!this->m_dockableWidgets.isEmpty()); - } - - this->ps_setDockArea(Qt::TopDockWidgetArea); - this->setMarginsWhenFloating(5, 5, 5, 5); // left, top, right bottom - this->setMarginsWhenDocked(1, 1, 1, 1); // top has no effect - this->connectAllWidgets(); - this->setFeaturesForDockableWidgets(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable); - this->tabifyAllWidgets(); - - // context menu - this->setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &CMainInfoAreaComponent::customContextMenuRequested, this, &CMainInfoAreaComponent::ps_showContextMenu); - connect(&CStyleSheetUtility::instance(), &CStyleSheetUtility::styleSheetsChanged, this, &CMainInfoAreaComponent::ps_onStyleSheetChanged); - - // initial style sheet setting - this->ps_onStyleSheetChanged(); + initInfoArea(); } CMainInfoAreaComponent::~CMainInfoAreaComponent() @@ -58,87 +36,6 @@ namespace BlackGui delete ui; } - void CMainInfoAreaComponent::addToContextMenu(QMenu *menu) const - { - if (!menu) return; - menu->addAction(CIcons::dockTop16(), "Dock all", this, SLOT(dockAllWidgets())); - menu->addAction(CIcons::floatAll16(), "Float all", this, SLOT(floatAllWidgets())); - menu->addAction(CIcons::floatOne16(), "Dock / float info area", this, SLOT(toggleFloating())); - - bool c = false; - if (!this->m_dockableWidgets.isEmpty()) - { - menu->addSeparator(); - QMenu *subMenuToggleFloat = new QMenu("Toggle Float/Dock", menu); - QMenu *subMenuDisplay = new QMenu("Display", menu); - - QSignalMapper *signalMapperToggleFloating = new QSignalMapper(menu); - QSignalMapper *signalMapperDisplay = new QSignalMapper(menu); - - for (int i = 0; i < this->m_dockableWidgets.size(); i++) - { - const CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(i); - const QPixmap pm = infoAreaToPixmap(static_cast(i)); - const QString t = dw->windowTitleBackup(); - QAction *checkableMenuAction = new QAction(menu); - checkableMenuAction->setObjectName(QString(t).append("ToggleFloatingAction")); - checkableMenuAction->setIconText(t); - checkableMenuAction->setIcon(pm); - checkableMenuAction->setData(QVariant(i)); - checkableMenuAction->setCheckable(true); - checkableMenuAction->setChecked(!dw->isFloating()); - subMenuToggleFloat->addAction(checkableMenuAction); - c = connect(checkableMenuAction, SIGNAL(toggled(bool)), signalMapperToggleFloating, SLOT(map())); - Q_ASSERT(c); - signalMapperToggleFloating->setMapping(checkableMenuAction, i); - - QAction *displayMenuAction = new QAction(menu); - displayMenuAction->setObjectName(QString(t).append("DisplayAction")); - displayMenuAction->setIconText(t); - displayMenuAction->setIcon(pm); - displayMenuAction->setData(QVariant(i)); - displayMenuAction->setCheckable(false); - - subMenuDisplay->addAction(displayMenuAction); - c = connect(displayMenuAction, SIGNAL(triggered(bool)), signalMapperDisplay, SLOT(map())); - Q_ASSERT(c); - signalMapperDisplay->setMapping(displayMenuAction, i); // action to index - } - c = connect(signalMapperToggleFloating, SIGNAL(mapped(int)), this, SLOT(toggleFloating(int))); - Q_ASSERT(c); - - c = connect(signalMapperDisplay, SIGNAL(mapped(int)), this, SLOT(selectArea(int))); - Q_ASSERT(c); - - menu->addMenu(subMenuDisplay); - menu->addMenu(subMenuToggleFloat); - - // where and how to display tab - menu->addSeparator(); - QAction *showMenuText = new QAction(menu); - showMenuText->setObjectName("ShowDockedWidgetTextAction"); - showMenuText->setIconText("Show tab text"); - showMenuText->setIcon(CIcons::headingOne16()); - showMenuText->setCheckable(true); - showMenuText->setChecked(this->m_showTabTexts); - menu->addAction(showMenuText); - connect(showMenuText, &QAction::toggled, this, &CMainInfoAreaComponent::ps_showTabTexts); - - // auto adjust floating widgets - QAction *showTabbar = new QAction(menu); - showTabbar->setObjectName("ShowTabBar"); - showTabbar->setIconText("Show tab bar"); - showTabbar->setIcon(CIcons::dockBottom16()); - showTabbar->setCheckable(true); - showTabbar->setChecked(this->m_showTabBar); - menu->addAction(showTabbar); - connect(showTabbar, &QAction::toggled, this, &CMainInfoAreaComponent::ps_showTabBar); - - // tab bar position - menu->addAction(CIcons::dockBottom16(), "Toogle tabbar position", this, SLOT(ps_toggleTabBarPosition())); - } - } - CAtcStationComponent *CMainInfoAreaComponent::getAtcStationComponent() { return this->ui->comp_AtcStations; @@ -179,230 +76,9 @@ namespace BlackGui return this->ui->comp_TextMessages; } - void CMainInfoAreaComponent::dockAllWidgets() - { - this->tabifyAllWidgets(); - } - - void CMainInfoAreaComponent::adjustSizeForAllDockWidgets() - { - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - dw->adjustSize(); - } - } - - void CMainInfoAreaComponent::floatAllWidgets() - { - // I fake the double click here, which queues the events in the queue - // and hence fires all depending signals in order -// if (!this->m_tabBar) return; -// for (int i = 0; i < this->m_tabBar->count(); i++) -// { -// // emit this->m_tabBar->tabBarDoubleClicked(i); -// this->toggleFloating(i); -// } - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - if (dw->isFloating()) continue; - dw->toggleFloating(); - } - - } - - void CMainInfoAreaComponent::toggleFloating() - { - this->ps_setInfoAreaFloating(!this->m_infoAreaFloating); - } - - void CMainInfoAreaComponent::toggleFloating(CMainInfoAreaComponent::InfoArea infoArea) - { - CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(static_cast(infoArea)); - Q_ASSERT(dw); - if (!dw) return; - dw->toggleFloating(); - } - - void CMainInfoAreaComponent::toggleFloating(int index) - { - if (index < 0 || index >= this->m_dockableWidgets.size()) return; - CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(index); - Q_ASSERT(dw); - if (!dw) return; - dw->toggleFloating(); - } - - void CMainInfoAreaComponent::selectArea(CMainInfoAreaComponent::InfoArea infoArea) - { - CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(static_cast(infoArea)); - Q_ASSERT(dw); - if (!dw) return; - Q_ASSERT(this->m_tabBar); - if (m_tabBar->count() < 1) return; - - if (dw->isFloating()) - { - dw->show(); - } - else - { - int index = this->widgetToTabBarIndex(dw); - Q_ASSERT(index >= 0); - if (index >= 0 && index < m_tabBar->count()) - { - m_tabBar->setCurrentIndex(index); - } - } - } - - void CMainInfoAreaComponent::selectArea(int index) - { - if (index < 0 || index >= this->m_dockableWidgets.count()) return; - this->selectArea(static_cast(index)); - } - - void CMainInfoAreaComponent::selectSettingsTab(int index) - { - this->selectArea(InfoAreaSettings); - this->ui->comp_Settings->setSettingsTab(static_cast(index)); - } - - void CMainInfoAreaComponent::ps_setDockArea(Qt::DockWidgetArea area) - { - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - Qt::DockWidgetAreas newAreas = static_cast(area); - Qt::DockWidgetAreas oldAreas = dw->allowedAreas(); - if (oldAreas == newAreas) continue; - dw->setAllowedAreas(newAreas); - this->addDockWidget(area, dw); - } - } - - void CMainInfoAreaComponent::ps_setInfoAreaFloating(bool floating) - { - // float whole info area - this->m_infoAreaFloating = floating; - if (this->m_infoAreaFloating) - { - QPoint p = CGuiUtility::mainWindowPosition(); - this->setWindowFlags(Qt::Dialog); - this->move(p.rx() + 20, p.ry() + 20); - this->show(); // not working without show - } - else - { - // make this compliant as QWidget - // https://qt-project.org/forums/viewthread/17519 - // http://www.qtcentre.org/threads/12569-QMainWindow-as-a-child-of-QMainWindow - - // this->setParent(this->m_originalParent, this->windowFlags() & ~Qt::Window); - this->setWindowFlags(this->windowFlags() & ~Qt::Window); - this->setVisible(true); // after redocking this is required - } - } - - void CMainInfoAreaComponent::tabifyAllWidgets() - { - // this->setDockArea(Qt::LeftDockWidgetArea); - this->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::East); - bool init = this->m_tabBar ? false : true; - - if (!this->m_dockableWidgets.isEmpty()) - { - for (int i = 0; i < this->m_dockableWidgets.size(); i++) - { - CDockWidgetInfoArea *first = i > 0 ? this->m_dockableWidgets.at(i - 1) : nullptr; - CDockWidgetInfoArea *after = this->m_dockableWidgets.at(i); - Q_ASSERT(after); - - // trick, init widget as floating - // this completely initializes the tab bar and all docked widgets - if (init) - { - QPoint offset(i * 25, i * 20); - after->setVisible(false); - after->setFloating(true); - after->setOffsetWhenFloating(offset); - after->setPreferredSizeWhenFloating(CMainInfoAreaComponent::getPreferredSizeWhenFloating(static_cast(i))); - after->setFloating(false); - after->setVisible(true); - after->resetWasAlreadyFLoating(); - } - else - { - after->setFloating(false); - } - if (!first) { continue; } - this->tabifyDockWidget(first, after); - } - } - - // as now tabified, now set tab - if (!this->m_tabBar) - { - this->m_tabBar = this->findChild(); - Q_ASSERT(m_tabBar); - QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameDockWidgetTab()); - this->m_tabBar->setStyleSheet(qss); - this->m_tabBar->setObjectName("comp_MainInfoAreaDockWidgetTab"); - this->m_tabBar->setMovable(false); - this->m_tabBar->setElideMode(Qt::ElideNone); - this->setDocumentMode(true); // did not notice any effect - this->setTabPixmaps(); - - // East / West does not work (shown, but area itself empty) - // South does not have any effect - this->m_tabBar->setShape(QTabBar::TriangularSouth); - connect(this->m_tabBar, &QTabBar::tabBarDoubleClicked, this, &CMainInfoAreaComponent::ps_tabBarDoubleClicked); - } - - if (this->countDockedWidgets() > 0) - { - this->m_tabBar->setCurrentIndex(0); - } - } - - void CMainInfoAreaComponent::unTabifyAllWidgets() - { - if (this->m_dockableWidgets.size() < 2) return; - CDockWidgetInfoArea *first = this->m_dockableWidgets.first(); - for (int i = 1; i < this->m_dockableWidgets.size(); i++) - { - CDockWidgetInfoArea *after = this->m_dockableWidgets.at(i); - Q_ASSERT(after); - this->splitDockWidget(first, after, Qt::Horizontal); - } - } - - void CMainInfoAreaComponent::connectAllWidgets() - { - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - connect(dw, &CDockWidgetInfoArea::widgetTopLevelChanged, this, &CMainInfoAreaComponent::ps_onWidgetTopLevelChanged); - } - } - - void CMainInfoAreaComponent::setMarginsWhenFloating(int left, int top, int right, int bottom) - { - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - //! Margins when window is floating - dw->setMarginsWhenFloating(left, top, right, bottom); - } - } - - void CMainInfoAreaComponent::setMarginsWhenDocked(int left, int top, int right, int bottom) - { - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - //! Margins when window is docked - dw->setMarginsWhenDocked(left, top, right, bottom); - } - } - - QSize CMainInfoAreaComponent::getPreferredSizeWhenFloating(InfoArea area) + QSize CMainInfoAreaComponent::getPreferredSizeWhenFloating(int areaIndex) const { + InfoArea area = static_cast(areaIndex); switch (area) { case InfoAreaAircrafts: @@ -424,9 +100,16 @@ namespace BlackGui } } - const QPixmap &CMainInfoAreaComponent::infoAreaToPixmap(CMainInfoAreaComponent::InfoArea infoArea) + void CMainInfoAreaComponent::selectSettingsTab(int index) { - switch (infoArea) + this->selectArea(InfoAreaSettings); + this->ui->comp_Settings->setSettingsTab(static_cast(index)); + } + + const QPixmap &CMainInfoAreaComponent::indexToPixmap(int areaIndex) const + { + InfoArea area = static_cast(areaIndex); + switch (area) { case InfoAreaUsers: return CIcons::appUsers16(); @@ -452,140 +135,5 @@ namespace BlackGui return CIcons::empty(); } } - - int CMainInfoAreaComponent::countDockedWidgets() const - { - if (!this->m_tabBar) return 0; - return this->m_tabBar->count(); - } - - CDockWidgetInfoArea *CMainInfoAreaComponent::getDockableWidgetByTabIndex(int tabBarIndex) const - { - if (tabBarIndex >= this->m_dockableWidgets.count() || tabBarIndex < 0) return nullptr; - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - if (dw->isFloating()) continue; // not in tab bar - if (tabBarIndex == 0) return dw; - tabBarIndex--; - } - return nullptr; - } - - int CMainInfoAreaComponent::widgetToTabBarIndex(const CDockWidgetInfoArea *dockWidget) - { - if (!dockWidget) return -1; - if (dockWidget->isFloating()) return -1; - int tabBarIndex = 0; - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - if (dw->isFloating()) continue; // not in tab bar - if (dw == dockWidget) return tabBarIndex; - tabBarIndex++; - } - return -1; - } - - void CMainInfoAreaComponent::setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features) - { - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - dw->setFeatures(features); - } - } - - void CMainInfoAreaComponent::setTabPixmaps() - { - if (!this->m_tabBar) return; - for (int i = 0; i < this->m_tabBar->count(); i++) - { - InfoArea area = static_cast(i); - const QPixmap p(infoAreaToPixmap(area)); - this->m_tabBar->setTabIcon(i, p); - } - } - - void CMainInfoAreaComponent::ps_tabBarDoubleClicked(int tabBarIndex) - { - CDockWidgetInfoArea *dw = this->getDockableWidgetByTabIndex(tabBarIndex); - if (!dw) return; - dw->toggleFloating(); - } - - void CMainInfoAreaComponent::ps_onWidgetTopLevelChanged(CDockWidget *widget, bool topLevel) - { - Q_ASSERT(widget); - Q_UNUSED(topLevel); - if (!widget) return; - - // fix pixmaps - this->setTabPixmaps(); - } - - void CMainInfoAreaComponent::ps_onStyleSheetChanged() - { - QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameMainInfoArea()); - this->setStyleSheet(qss); - if (this->m_tabBar) - { - QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameDockWidgetTab()); - this->m_tabBar->setStyleSheet(qss); - } - } - - void CMainInfoAreaComponent::ps_showContextMenu(const QPoint &pos) - { - QPoint globalPos = this->mapToGlobal(pos); - QScopedPointer contextMenu(new QMenu(this)); - this->addToContextMenu(contextMenu.data()); - - QAction *selectedItem = contextMenu.data()->exec(globalPos); - Q_UNUSED(selectedItem); - } - - void CMainInfoAreaComponent::ps_showTabTexts(bool show) - { - if (show == this->m_showTabTexts) return; - this->m_showTabTexts = show; - for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) - { - dw->showTitleWhenDocked(show); - } - } - - void CMainInfoAreaComponent::ps_showTabBar(bool show) - { - if (show == this->m_showTabBar) return; - this->m_showTabBar = show; - if (!this->m_tabBar) return; - this->m_tabBar->setVisible(show); // not working, but setting right value will not harm anything - this->m_tabBar->setMaximumHeight(show ? 10000 : 0); // does the trick - this->adjustSizeForAllDockWidgets(); - } - - void CMainInfoAreaComponent::ps_setTabBarPosition(QTabWidget::TabPosition position) - { - Q_ASSERT(position == QTabWidget::North || position == QTabWidget::South); - this->setTabPosition(Qt::TopDockWidgetArea, position); - } - - void CMainInfoAreaComponent::ps_toggleTabBarPosition() - { - QTabWidget::TabPosition p = (this->tabPosition(Qt::TopDockWidgetArea) == QTabWidget::North) ? - QTabWidget::South : QTabWidget::North; - this->ps_setTabBarPosition(p); - } - - void CMainInfoAreaComponent::closeEvent(QCloseEvent *event) - { - if (this->isFloating()) - { - this->toggleFloating(); - event->setAccepted(false); // refuse -> do not close - } - else - { - QMainWindow::closeEvent(event); - } - } } } diff --git a/src/blackgui/components/maininfoareacomponent.h b/src/blackgui/components/maininfoareacomponent.h index 2337b3bf0..e52b414ab 100644 --- a/src/blackgui/components/maininfoareacomponent.h +++ b/src/blackgui/components/maininfoareacomponent.h @@ -12,7 +12,7 @@ #ifndef BLACKGUI_MAININFOAREACOMPONENT_H #define BLACKGUI_MAININFOAREACOMPONENT_H -#include "../dockwidgetinfoarea.h" +#include "../infoarea.h" #include "atcstationcomponent.h" #include "aircraftcomponent.h" #include "usercomponent.h" @@ -21,7 +21,6 @@ #include "flightplancomponent.h" #include "settingscomponent.h" #include "logcomponent.h" -#include #include #include @@ -33,7 +32,7 @@ namespace BlackGui { //! Main info area - class CMainInfoAreaComponent : public QMainWindow + class CMainInfoAreaComponent : public BlackGui::CInfoArea { Q_OBJECT @@ -42,30 +41,25 @@ namespace BlackGui explicit CMainInfoAreaComponent(QWidget *parent = nullptr); //! Destructor - ~CMainInfoAreaComponent(); + virtual ~CMainInfoAreaComponent(); //! Info areas enum InfoArea { // index must match tab index! - InfoAreaAircrafts = 0, - InfoAreaAtc = 1, - InfoAreaUsers = 2, + InfoAreaAircrafts = 0, + InfoAreaAtc = 1, + InfoAreaUsers = 2, InfoAreaTextMessages = 3, - InfoAreaSimulator = 4, - InfoAreaFlightPlan = 5, - InfoAreaWeather = 6, - InfoAreaMappings = 7, - InfoAreaLog = 8, - InfoAreaSettings = 9 + InfoAreaSimulator = 4, + InfoAreaFlightPlan = 5, + InfoAreaWeather = 6, + InfoAreaMappings = 7, + InfoAreaLog = 8, + InfoAreaSettings = 9, + InfoAreaNone = -1 }; - //! Add items to context menu - void addToContextMenu(QMenu *menu) const; - - //! Is this area floating? - bool isFloating() const { return this->m_infoAreaFloating; } - //! ATC stations CAtcStationComponent *getAtcStationComponent(); @@ -90,119 +84,29 @@ namespace BlackGui //! Text messages CTextMessageComponent *getTextMessageComponent(); + //! Selected area of non floating areas + InfoArea getSelectedInfoArea() const { return static_cast(getSelectedInfoAreaIndex()); } + public slots: - //! Dock all widgets - void dockAllWidgets(); - - //! Adjust size for all dock widgets - void adjustSizeForAllDockWidgets(); - - //! All widgets floating - void floatAllWidgets(); - - //! Toggle dock / floating of the whole info area - void toggleFloating(); - //! Toggle floating of given area - void toggleFloating(InfoArea infoArea); - - //! Toggle floating of index - void toggleFloating(int index); + void toggleFloating(InfoArea infoArea) { CInfoArea::toggleFloating(static_cast(infoArea)); } //! Select area - void selectArea(InfoArea infoArea); - - //! Select area - void selectArea(int index); + void selectArea(InfoArea infoArea) { CInfoArea::selectArea(static_cast(infoArea)); } //! Select settings with given area void selectSettingsTab(int index); protected: - //! Override close event - virtual void closeEvent(QCloseEvent *event) override; + //! \copydoc CInfoArea::getPreferredSizeWhenFloating + virtual QSize getPreferredSizeWhenFloating(int areaIndex) const override; + + //! \copydoc CInfoArea::indexToPixmap + virtual const QPixmap &indexToPixmap(int areaIndex) const override; private: Ui::CMainInfoAreaComponent *ui = nullptr; - QList m_dockableWidgets ; - QTabBar *m_tabBar = nullptr; - bool m_showTabTexts = true; - bool m_infoAreaFloating = false; //!< whole info area floating - bool m_showTabBar = true; //!< auto ajdust the floating widgets - //! Tabify the widgets - void tabifyAllWidgets(); - - //! Untabify - void unTabifyAllWidgets(); - - //! The tab bar of the docked widgets - QTabBar *tabBarDockedWidgets() const; - - //! Corresponding dockable widgets - QList dockableWidgets() const; - - //! Corresponding dockable widget for given tab index - CDockWidgetInfoArea *getDockableWidgetByTabIndex(int tabBarIndex) const; - - //! Features of the dockable widgets - void setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features); - - //! Number of tabbed widgets - int countDockedWidgets() const; - - //! Widget to tab bar index - int widgetToTabBarIndex(const CDockWidgetInfoArea *dockWidget); - - //! Set the tab's icons - void setTabPixmaps(); - - //! Connect all widgets - void connectAllWidgets(); - - //! Margins for the floating widgets - void setMarginsWhenFloating(int left, int top, int right, int bottom); - - //! Margins for the dockable widgets - void setMarginsWhenDocked(int left, int top, int right, int bottom); - - //! Set window sizes when floating - static QSize getPreferredSizeWhenFloating(InfoArea area); - - //! Info area to icon - static const QPixmap &infoAreaToPixmap(InfoArea infoArea); - - private slots: - //! Tab bar has been double clicked - void ps_tabBarDoubleClicked(int tabBarIndex); - - //! A widget has changed its top level - void ps_onWidgetTopLevelChanged(CDockWidget *widget, bool topLevel); - - //! Style sheet has been changed - void ps_onStyleSheetChanged(); - - //! Context menu - void ps_showContextMenu(const QPoint &pos); - - //! Show the tab texts, or just the icons - void ps_showTabTexts(bool show); - - //! Show tab bar - void ps_showTabBar(bool show); - - //! Tab position for docked widgets tab - //! \remarks North or South working, East / West not - void ps_setTabBarPosition(QTabWidget::TabPosition position); - - //! Toggle tab position North - South - void ps_toggleTabBarPosition(); - - //! Set dock area used - void ps_setDockArea(Qt::DockWidgetArea area); - - //! Dock / floating of the whole info area - void ps_setInfoAreaFloating(bool floating); }; } } diff --git a/src/blackgui/dockwidget.cpp b/src/blackgui/dockwidget.cpp index 4dbce3293..fbb14eae2 100644 --- a/src/blackgui/dockwidget.cpp +++ b/src/blackgui/dockwidget.cpp @@ -87,6 +87,29 @@ namespace BlackGui } } + void CDockWidget::paintEvent(QPaintEvent *event) + { + // KB: Should give me style sheet compliance, however I did not notice any difference + // included for style sheet compliance + // QStyleOption opt; + // opt.init(this); + // QPainter p(this); + // style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QDockWidget::paintEvent(event); + } + + void CDockWidget::hideEvent(QHideEvent *event) + { + qDebug() << "hide" << this->objectName() << "v:" << isVisible() << "h:" << isHidden(); + QDockWidget::hideEvent(event); + } + + void CDockWidget::showEvent(QShowEvent *event) + { + qDebug() << "show" << this->objectName() << "v:" << isVisible() << "h:" << isHidden(); + QDockWidget::showEvent(event); + } + void CDockWidget::addToContextMenu(QMenu *contextMenu) const { if (this->isFloating()) @@ -99,15 +122,22 @@ namespace BlackGui } } - void CDockWidget::paintEvent(QPaintEvent *event) + void CDockWidget::initalFloating() { - // KB: Should give me style sheet compliance, however I did not notice any difference - // included for style sheet compliance - // QStyleOption opt; - // opt.init(this); - // QPainter p(this); - // style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - QDockWidget::paintEvent(event); + // for the first time resize + if (!this->m_preferredSizeWhenFloating.isNull()) + { + this->resize(this->m_preferredSizeWhenFloating); + } + + // and move + QPoint mainWindowPos = BlackGui::CGuiUtility::mainWindowPosition(); + if (!mainWindowPos.isNull()) + { + int x = mainWindowPos.x() + this->m_offsetWhenFloating.x(); + int y = mainWindowPos.y() + this->m_offsetWhenFloating.y(); + this->move(x, y); + } } void CDockWidget::ps_onTopLevelChanged(bool topLevel) @@ -122,20 +152,7 @@ namespace BlackGui this->setContentsMargins(this->m_marginsWhenFloating); if (!this->m_wasAlreadyFloating) { - // for the first time resize - if (!this->m_preferredSizeWhenFloating.isNull()) - { - this->resize(this->m_preferredSizeWhenFloating); - } - - // and move - QPoint mainWindowPos = BlackGui::CGuiUtility::mainWindowPosition(); - if (!mainWindowPos.isNull()) - { - int x = mainWindowPos.x() + this->m_offsetWhenFloating.x(); - int y = mainWindowPos.y() + this->m_offsetWhenFloating.y(); - this->move(x, y); - } + this->initalFloating(); } this->m_wasAlreadyFloating = true; } @@ -182,7 +199,7 @@ namespace BlackGui Q_UNUSED(selectedItem); } - void CDockWidget::ps_onStyleSheetsChanged() + void CDockWidget::onStyleSheetsChanged() { // void } diff --git a/src/blackgui/dockwidget.h b/src/blackgui/dockwidget.h index 7d3e3aa18..00c329b51 100644 --- a/src/blackgui/dockwidget.h +++ b/src/blackgui/dockwidget.h @@ -1,5 +1,5 @@ /* Copyright (C) 2014 - * Swift Project Community / Contributors + * swift Project Community / Contributors * * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, @@ -21,22 +21,27 @@ namespace BlackGui { - //! Our base class for dock widgets, containing some specialized functionality + //! \brief Our base class for dockable widgets containing some specialized functionality on top of QWidget. + //! \details We currently use dockable widgets either as "info area" or "info bar" dockable widget. + //! Info area widgets reside in an info are and represent a larger piece of information (e.g. all ATC stations, all aircrafts in range). + //! An info bar is meant to be a small info window displaying information about status, menu state etc. + //! + //! \sa CInfoArea + //! \sa CDockWidgetInfoArea + //! \sa CDockWidgetInfoBar class CDockWidget : public QDockWidget { + // KB TODO: Make this an interface, IDockWidget Q_OBJECT public: - //! Constructor - explicit CDockWidget(QWidget *parent = nullptr); - //! Set original title bar void setOriginalTitleBar(); - //! Set empty title bar + //! Set empty title bar (empty widget as title bar) void setEmptyTitleBar(); - //! Set null title bar + //! Set null (nullptr) title bar void setNullTitleBar(); //! Margins when window is floating @@ -57,13 +62,16 @@ namespace BlackGui //! Window title when window is docked bool showTitleWhenDocked() const { return this->m_windowTitleWhenDocked; } + //! Selected when tabbed + bool isSelected() const { return this->m_selected; } + //! Show the window title when docked void showTitleWhenDocked(bool show); - //! Reset first time floating + //! Reset first time floating, marked as never floated before void resetWasAlreadyFLoating() { this->m_wasAlreadyFloating = false; } - //! Was widget already floating + //! Was widget already floating? bool wasAlreadyFloating() const { return this->m_wasAlreadyFloating; } //! Size when floating first time @@ -84,14 +92,28 @@ namespace BlackGui void widgetTopLevelChanged(CDockWidget *, bool topLevel); protected: + //! Constructor + explicit CDockWidget(QWidget *parent = nullptr); + //! Override close event virtual void closeEvent(QCloseEvent *event) override; + //! Paint event + virtual void paintEvent(QPaintEvent *event) override; + + //! Hide event + void hideEvent(QHideEvent *event) override; + + //! Show event + void showEvent(QShowEvent *event) override; + //! Contribute to menu virtual void addToContextMenu(QMenu *contextMenu) const; - //! Paint event - virtual void paintEvent(QPaintEvent *event) override; + //! Widget is initialized by being a floating window for a shot period. + //! \details Place where - when overidden - post initializations can take place. + //! The GUI is already initialized, so all widget data are available. + virtual void initalFloating(); protected slots: //! Style sheet has changed @@ -105,15 +127,17 @@ namespace BlackGui virtual void ps_showContextMenu(const QPoint &pos); private: - QWidget *m_emptyTitleBar = nullptr; //!< replacing default title bar + QWidget *m_emptyTitleBar = nullptr; //!< replacing default title bar QWidget *m_titleBarOriginal = nullptr; //!< the original title bar - QMargins m_marginsWhenFloating; //!< Offsets when window is floating - QMargins m_marginsWhenDocked; //!< Offsets when window is floating - QString m_windowTitleBackup; //!< original title, even if the widget title is deleted for layout purposes + QMargins m_marginsWhenFloating; //!< Offsets when window is floating + QMargins m_marginsWhenDocked; //!< Offsets when window is floating + QString m_windowTitleBackup; //!< original title, even if the widget title is deleted for layout purposes + QSize m_preferredSizeWhenFloating; //!< preferred size men floating 1st time + QPoint m_offsetWhenFloating; //!< initial offset to main window when floating first time bool m_windowTitleWhenDocked = true; - bool m_wasAlreadyFloating = false; - QSize m_preferredSizeWhenFloating; //!< preferred size men floating 1st time - QPoint m_offsetWhenFloating; //!< initial offset to main window when floating first time + bool m_wasAlreadyFloating = false; + bool m_selected = false; //!< selected when tabbed + //! Empty widget with no size void initTitleBarWidgets(); diff --git a/src/blackgui/dockwidgetinfoarea.cpp b/src/blackgui/dockwidgetinfoarea.cpp index c50b0301c..6eeb7fa27 100644 --- a/src/blackgui/dockwidgetinfoarea.cpp +++ b/src/blackgui/dockwidgetinfoarea.cpp @@ -8,6 +8,7 @@ */ #include "dockwidgetinfoarea.h" +#include "infoarea.h" #include "components/maininfoareacomponent.h" #include "blackmisc/icons.h" @@ -15,7 +16,49 @@ using namespace BlackGui::Components; namespace BlackGui { - CDockWidgetInfoArea::CDockWidgetInfoArea(QWidget *parent) : CDockWidget(parent) { } + CDockWidgetInfoArea::CDockWidgetInfoArea(QWidget *parent) : CDockWidget(parent) + { + // void + } + + const CInfoArea *CDockWidgetInfoArea::getParentInfoArea() const + { + const CInfoArea *ia = dynamic_cast(this->parent()); + Q_ASSERT(ia); + return ia; + } + + CInfoArea *CDockWidgetInfoArea::getParentInfoArea() + { + CInfoArea *ia = dynamic_cast(this->parent()); + Q_ASSERT(ia); + return ia; + } + + bool CDockWidgetInfoArea::isSelectedDockWidget() const + { + const CInfoArea *ia = getParentInfoArea(); + if (!ia) return false; + return ia->isSelectedInfoArea(this); + } + + bool CDockWidgetInfoArea::isVisibleWidget() const + { + // if the widget is invisible we are done + // but if it is visible, there is no guarantee it can be seen by the user + if (!this->isVisible()) return false; + + // further checks + if (this->isFloating()) + { + if (this->isMinimized()) return false; + return true; + } + else + { + return isSelectedDockWidget(); + } + } void CDockWidgetInfoArea::addToContextMenu(QMenu *contextMenu) const { @@ -30,4 +73,29 @@ namespace BlackGui contextMenu->addSeparator(); mainWidget->addToContextMenu(contextMenu); } + + void CDockWidgetInfoArea::initalFloating() + { + CDockWidget::initalFloating(); + QList infoAreaDockWidgets = this->findEmbeddedDockWidgetInfoAreaComponents(); + foreach(CDockWidgetInfoAreaComponent * dwia, infoAreaDockWidgets) + { + dwia->setParentDockableWidget(this); + } + } + + QList CDockWidgetInfoArea::findEmbeddedDockWidgetInfoAreaComponents() const + { + QList widgets = this->findChildren(); + QList widgetsWithDockWidgetInfoAreaComponent; + foreach(QWidget * w, widgets) + { + CDockWidgetInfoAreaComponent *dwc = dynamic_cast(w); + if (dwc) + { + widgetsWithDockWidgetInfoAreaComponent.append(dwc); + } + } + return widgetsWithDockWidgetInfoAreaComponent; + } } diff --git a/src/blackgui/dockwidgetinfoarea.h b/src/blackgui/dockwidgetinfoarea.h index 42bc36f14..70a326d72 100644 --- a/src/blackgui/dockwidgetinfoarea.h +++ b/src/blackgui/dockwidgetinfoarea.h @@ -16,8 +16,13 @@ namespace BlackGui { + //! Forward declarations + class CInfoArea; + namespace Components { class CDockWidgetInfoAreaComponent; } - //! Class for dock widgets serving as info bar + //! Specialized class for dock widgets serving as info area + //! \sa CDockWidgetInfoBar + //! \sa CInfoArea class CDockWidgetInfoArea : public CDockWidget { Q_OBJECT @@ -26,9 +31,28 @@ namespace BlackGui //! Constructor explicit CDockWidgetInfoArea(QWidget *parent = nullptr); + //! The parent info area + const CInfoArea *getParentInfoArea() const; + + //! The parent info area + CInfoArea *getParentInfoArea(); + + //! Is this the selected widget, means it is not floating, and the one selected + bool isSelectedDockWidget() const; + + //! Visible widget + bool isVisibleWidget() const; + protected: //! Contribute to menu virtual void addToContextMenu(QMenu *contextMenu) const override; + + //! \copydoc CDockWidget::initalFloating + virtual void initalFloating() override; + + private: + //! Find all embedded runtime components + QList findEmbeddedDockWidgetInfoAreaComponents() const; }; } // namespace diff --git a/src/blackgui/dockwidgetinfobar.h b/src/blackgui/dockwidgetinfobar.h index c4936386f..b6082dad6 100644 --- a/src/blackgui/dockwidgetinfobar.h +++ b/src/blackgui/dockwidgetinfobar.h @@ -18,6 +18,8 @@ namespace BlackGui { //! Class for dock widgets in the info area, containing some specialized functionality + //! \sa CDockWidgetInfoArea + //! \sa CInfoArea class CDockWidgetInfoBar : public CDockWidget { Q_OBJECT diff --git a/src/blackgui/infoarea.cpp b/src/blackgui/infoarea.cpp new file mode 100644 index 000000000..76cddc94c --- /dev/null +++ b/src/blackgui/infoarea.cpp @@ -0,0 +1,477 @@ +/* Copyright (C) 2014 + * swift project Community / Contributors + * + * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "infoarea.h" +#include "stylesheetutility.h" +#include "guiutility.h" +#include "blackmisc/icons.h" +#include +#include +#include +#include + +using namespace BlackMisc; + +namespace BlackGui +{ + CInfoArea::CInfoArea(QWidget *parent) : QMainWindow(parent) + { + this->ps_setInfoAreaFloating(this->m_infoAreaFloating); + this->setWindowIcon(CIcons::swift24()); + } + + void CInfoArea::initInfoArea() + { + // after(!) GUI established + if (this->m_dockableWidgets.isEmpty()) + { + this->m_dockableWidgets = this->findChildren(); + Q_ASSERT(!this->m_dockableWidgets.isEmpty()); + } + + this->ps_setDockArea(Qt::TopDockWidgetArea); + this->setMarginsWhenFloating(5, 5, 5, 5); // left, top, right bottom + this->setMarginsWhenDocked(1, 1, 1, 1); // top has no effect + this->connectAllWidgets(); + this->setFeaturesForDockableWidgets(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable); + this->tabifyAllWidgets(); + + // context menu + this->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &CInfoArea::customContextMenuRequested, this, &CInfoArea::ps_showContextMenu); + connect(&CStyleSheetUtility::instance(), &CStyleSheetUtility::styleSheetsChanged, this, &CInfoArea::ps_onStyleSheetChanged); + + // initial style sheet setting + this->ps_onStyleSheetChanged(); + } + + void CInfoArea::addToContextMenu(QMenu *menu) const + { + if (!menu) return; + menu->addAction(CIcons::dockTop16(), "Dock all", this, SLOT(dockAllWidgets())); + menu->addAction(CIcons::floatAll16(), "Float all", this, SLOT(floatAllWidgets())); + menu->addAction(CIcons::floatOne16(), "Dock / float info area", this, SLOT(toggleFloating())); + + bool c = false; + if (!this->m_dockableWidgets.isEmpty()) + { + menu->addSeparator(); + QMenu *subMenuToggleFloat = new QMenu("Toggle Float/Dock", menu); + QMenu *subMenuDisplay = new QMenu("Display", menu); + + QSignalMapper *signalMapperToggleFloating = new QSignalMapper(menu); + QSignalMapper *signalMapperDisplay = new QSignalMapper(menu); + + for (int i = 0; i < this->m_dockableWidgets.size(); i++) + { + const CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(i); + const QPixmap pm = indexToPixmap(i); + const QString t = dw->windowTitleBackup(); + QAction *checkableMenuAction = new QAction(menu); + checkableMenuAction->setObjectName(QString(t).append("ToggleFloatingAction")); + checkableMenuAction->setIconText(t); + checkableMenuAction->setIcon(pm); + checkableMenuAction->setData(QVariant(i)); + checkableMenuAction->setCheckable(true); + checkableMenuAction->setChecked(!dw->isFloating()); + subMenuToggleFloat->addAction(checkableMenuAction); + c = connect(checkableMenuAction, SIGNAL(toggled(bool)), signalMapperToggleFloating, SLOT(map())); + Q_ASSERT(c); + signalMapperToggleFloating->setMapping(checkableMenuAction, i); + + QAction *displayMenuAction = new QAction(menu); + displayMenuAction->setObjectName(QString(t).append("DisplayAction")); + displayMenuAction->setIconText(t); + displayMenuAction->setIcon(pm); + displayMenuAction->setData(QVariant(i)); + displayMenuAction->setCheckable(false); + + subMenuDisplay->addAction(displayMenuAction); + c = connect(displayMenuAction, SIGNAL(triggered(bool)), signalMapperDisplay, SLOT(map())); + Q_ASSERT(c); + signalMapperDisplay->setMapping(displayMenuAction, i); // action to index + } + c = connect(signalMapperToggleFloating, SIGNAL(mapped(int)), this, SLOT(toggleFloating(int))); + Q_ASSERT(c); + + c = connect(signalMapperDisplay, SIGNAL(mapped(int)), this, SLOT(selectArea(int))); + Q_ASSERT(c); + + menu->addMenu(subMenuDisplay); + menu->addMenu(subMenuToggleFloat); + + // where and how to display tab + menu->addSeparator(); + QAction *showMenuText = new QAction(menu); + showMenuText->setObjectName("ShowDockedWidgetTextAction"); + showMenuText->setIconText("Show tab text"); + showMenuText->setIcon(CIcons::headingOne16()); + showMenuText->setCheckable(true); + showMenuText->setChecked(this->m_showTabTexts); + menu->addAction(showMenuText); + connect(showMenuText, &QAction::toggled, this, &CInfoArea::ps_showTabTexts); + + // auto adjust floating widgets + QAction *showTabbar = new QAction(menu); + showTabbar->setObjectName("ShowTabBar"); + showTabbar->setIconText("Show tab bar"); + showTabbar->setIcon(CIcons::dockBottom16()); + showTabbar->setCheckable(true); + showTabbar->setChecked(this->m_showTabBar); + menu->addAction(showTabbar); + connect(showTabbar, &QAction::toggled, this, &CInfoArea::ps_showTabBar); + + // tab bar position + menu->addAction(CIcons::dockBottom16(), "Toogle tabbar position", this, SLOT(ps_toggleTabBarPosition())); + } + } + + int CInfoArea::getSelectedInfoAreaIndex() const + { + Q_ASSERT(this->m_tabBar); + if (!this->m_tabBar || this->m_tabBar->count() < 1) return -1; + return this->m_tabBar->currentIndex(); + } + + bool CInfoArea::isSelectedInfoArea(const CDockWidgetInfoArea *infoArea) const + { + if (!infoArea) return false; + if (infoArea->isFloating()) return false; + int i = getSelectedInfoAreaIndex(); + if (i < 0 || i >= this->m_dockableWidgets.size()) return false; + return this->m_dockableWidgets.at(i) == infoArea; + } + + void CInfoArea::dockAllWidgets() + { + this->tabifyAllWidgets(); + } + + void CInfoArea::adjustSizeForAllDockWidgets() + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + dw->adjustSize(); + } + } + + void CInfoArea::floatAllWidgets() + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + if (dw->isFloating()) continue; + dw->toggleFloating(); + } + } + + void CInfoArea::toggleFloating() + { + this->ps_setInfoAreaFloating(!this->m_infoAreaFloating); + } + + void CInfoArea::toggleFloating(int areaIndex) + { + if (areaIndex < 0 || areaIndex >= this->m_dockableWidgets.size()) return; + CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(areaIndex); + Q_ASSERT(dw); + if (!dw) return; + dw->toggleFloating(); + } + + void CInfoArea::selectArea(int areaIndex) + { + CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(areaIndex); + Q_ASSERT(dw); + if (!dw) return; + Q_ASSERT(this->m_tabBar); + if (m_tabBar->count() < 1) return; + + if (dw->isFloating()) + { + dw->show(); + } + else + { + int index = this->widgetToTabBarIndex(dw); + Q_ASSERT(index >= 0); + if (index >= 0 && index < m_tabBar->count()) + { + m_tabBar->setCurrentIndex(index); + } + } + } + + void CInfoArea::ps_setDockArea(Qt::DockWidgetArea area) + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + Qt::DockWidgetAreas newAreas = static_cast(area); + Qt::DockWidgetAreas oldAreas = dw->allowedAreas(); + if (oldAreas == newAreas) continue; + dw->setAllowedAreas(newAreas); + this->addDockWidget(area, dw); + } + } + + void CInfoArea::ps_setInfoAreaFloating(bool floating) + { + // float whole info area + this->m_infoAreaFloating = floating; + if (this->m_infoAreaFloating) + { + QPoint p = CGuiUtility::mainWindowPosition(); + this->setWindowFlags(Qt::Dialog); + this->move(p.rx() + 20, p.ry() + 20); + this->show(); // not working without show + } + else + { + // make this compliant as QWidget + // https://qt-project.org/forums/viewthread/17519 + // http://www.qtcentre.org/threads/12569-QMainWindow-as-a-child-of-QMainWindow + + // this->setParent(this->m_originalParent, this->windowFlags() & ~Qt::Window); + this->setWindowFlags(this->windowFlags() & ~Qt::Window); + this->setVisible(true); // after redocking this is required + } + } + + void CInfoArea::tabifyAllWidgets() + { + // this->setDockArea(Qt::LeftDockWidgetArea); + this->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::East); + bool init = this->m_tabBar ? false : true; + + if (!this->m_dockableWidgets.isEmpty()) + { + for (int i = 0; i < this->m_dockableWidgets.size(); i++) + { + CDockWidgetInfoArea *first = i > 0 ? this->m_dockableWidgets.at(i - 1) : nullptr; + CDockWidgetInfoArea *after = this->m_dockableWidgets.at(i); + Q_ASSERT(after); + + // trick, init widget as floating + // this completely initializes the tab bar and all docked widgets + if (init) + { + QPoint offset(i * 25, i * 20); + after->setVisible(false); + after->setFloating(true); + after->setOffsetWhenFloating(offset); + after->setPreferredSizeWhenFloating(getPreferredSizeWhenFloating(i)); + after->setFloating(false); + after->setVisible(true); + after->resetWasAlreadyFLoating(); + } + else + { + after->setFloating(false); + } + if (!first) { continue; } + this->tabifyDockWidget(first, after); + } + } + + // as now tabified, now set tab + if (!this->m_tabBar) + { + this->m_tabBar = this->findChild(); + Q_ASSERT(m_tabBar); + QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameDockWidgetTab()); + this->m_tabBar->setStyleSheet(qss); + this->m_tabBar->setObjectName("comp_MainInfoAreaDockWidgetTab"); + this->m_tabBar->setMovable(false); + this->m_tabBar->setElideMode(Qt::ElideNone); + this->setDocumentMode(true); // did not notice any effect + this->setTabPixmaps(); + + // East / West does not work (shown, but area itself empty) + // South does not have any effect + this->m_tabBar->setShape(QTabBar::TriangularSouth); + connect(this->m_tabBar, &QTabBar::tabBarDoubleClicked, this, &CInfoArea::ps_tabBarDoubleClicked); + } + + if (this->countDockedWidgets() > 0) + { + this->m_tabBar->setCurrentIndex(0); + } + } + + void CInfoArea::unTabifyAllWidgets() + { + if (this->m_dockableWidgets.size() < 2) return; + CDockWidgetInfoArea *first = this->m_dockableWidgets.first(); + for (int i = 1; i < this->m_dockableWidgets.size(); i++) + { + CDockWidgetInfoArea *after = this->m_dockableWidgets.at(i); + Q_ASSERT(after); + this->splitDockWidget(first, after, Qt::Horizontal); + } + } + + void CInfoArea::connectAllWidgets() + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + connect(dw, &CDockWidgetInfoArea::widgetTopLevelChanged, this, &CInfoArea::ps_onWidgetTopLevelChanged); + } + } + + void CInfoArea::setMarginsWhenFloating(int left, int top, int right, int bottom) + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + //! Margins when window is floating + dw->setMarginsWhenFloating(left, top, right, bottom); + } + } + + void CInfoArea::setMarginsWhenDocked(int left, int top, int right, int bottom) + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + //! Margins when window is docked + dw->setMarginsWhenDocked(left, top, right, bottom); + } + } + + int CInfoArea::countDockedWidgets() const + { + if (!this->m_tabBar) return 0; + return this->m_tabBar->count(); + } + + CDockWidgetInfoArea *CInfoArea::getDockableWidgetByTabIndex(int tabBarIndex) const + { + if (tabBarIndex >= this->m_dockableWidgets.count() || tabBarIndex < 0) return nullptr; + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + if (dw->isFloating()) continue; // not in tab bar + if (tabBarIndex == 0) return dw; + tabBarIndex--; + } + return nullptr; + } + + int CInfoArea::widgetToTabBarIndex(const CDockWidgetInfoArea *dockWidget) + { + if (!dockWidget) return -1; + if (dockWidget->isFloating()) return -1; + int tabBarIndex = 0; + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + if (dw->isFloating()) continue; // not in tab bar + if (dw == dockWidget) return tabBarIndex; + tabBarIndex++; + } + return -1; + } + + void CInfoArea::setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features) + { + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + dw->setFeatures(features); + } + } + + void CInfoArea::setTabPixmaps() + { + if (!this->m_tabBar) return; + for (int i = 0; i < this->m_tabBar->count(); i++) + { + const QPixmap p = indexToPixmap(i); + this->m_tabBar->setTabIcon(i, p); + } + } + + void CInfoArea::ps_tabBarDoubleClicked(int tabBarIndex) + { + CDockWidgetInfoArea *dw = this->getDockableWidgetByTabIndex(tabBarIndex); + if (!dw) return; + dw->toggleFloating(); + } + + void CInfoArea::ps_onWidgetTopLevelChanged(CDockWidget *widget, bool topLevel) + { + Q_ASSERT(widget); + Q_UNUSED(topLevel); + if (!widget) return; + + // fix pixmaps + this->setTabPixmaps(); + } + + void CInfoArea::ps_onStyleSheetChanged() + { + QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameMainInfoArea()); + this->setStyleSheet(qss); + if (this->m_tabBar) + { + QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameDockWidgetTab()); + this->m_tabBar->setStyleSheet(qss); + } + } + + void CInfoArea::ps_showContextMenu(const QPoint &pos) + { + QPoint globalPos = this->mapToGlobal(pos); + QScopedPointer contextMenu(new QMenu(this)); + this->addToContextMenu(contextMenu.data()); + + QAction *selectedItem = contextMenu.data()->exec(globalPos); + Q_UNUSED(selectedItem); + } + + void CInfoArea::ps_showTabTexts(bool show) + { + if (show == this->m_showTabTexts) return; + this->m_showTabTexts = show; + for (CDockWidgetInfoArea *dw : this->m_dockableWidgets) + { + dw->showTitleWhenDocked(show); + } + } + + void CInfoArea::ps_showTabBar(bool show) + { + if (show == this->m_showTabBar) return; + this->m_showTabBar = show; + if (!this->m_tabBar) return; + this->m_tabBar->setVisible(show); // not working, but setting right value will not harm anything + this->m_tabBar->setMaximumHeight(show ? 10000 : 0); // does the trick + this->adjustSizeForAllDockWidgets(); + } + + void CInfoArea::ps_setTabBarPosition(QTabWidget::TabPosition position) + { + Q_ASSERT(position == QTabWidget::North || position == QTabWidget::South); + this->setTabPosition(Qt::TopDockWidgetArea, position); + } + + void CInfoArea::ps_toggleTabBarPosition() + { + QTabWidget::TabPosition p = (this->tabPosition(Qt::TopDockWidgetArea) == QTabWidget::North) ? + QTabWidget::South : QTabWidget::North; + this->ps_setTabBarPosition(p); + } + + void CInfoArea::closeEvent(QCloseEvent *event) + { + if (this->isFloating()) + { + this->toggleFloating(); + event->setAccepted(false); // refuse -> do not close + } + else + { + QMainWindow::closeEvent(event); + } + } +} // namespace diff --git a/src/blackgui/infoarea.h b/src/blackgui/infoarea.h new file mode 100644 index 000000000..f74831990 --- /dev/null +++ b/src/blackgui/infoarea.h @@ -0,0 +1,162 @@ +/* Copyright (C) 2014 + * swift project Community / Contributors + * + * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_INFOAREA_H +#define BLACKGUI_INFOAREA_H + +#include "dockwidgetinfoarea.h" +#include +#include +#include + +namespace Ui { class CInfoArea; } + +namespace BlackGui +{ + //! Info area, hosting dockable widgets + //! \sa CDockWidgetInfoArea + class CInfoArea : public QMainWindow + { + // KB: TODO, make this an interface, IInfoArea? + Q_OBJECT + + public: + + //! Destructor + virtual ~CInfoArea() {} + + //! Add items to context menu + void addToContextMenu(QMenu *menu) const; + + //! Is the area floating? + bool isFloating() const { return this->m_infoAreaFloating; } + + //! Selected area of non floating areas + //! \remarks -1 for no area + int getSelectedInfoAreaIndex() const; + + //! Is given widget selected. Means it is not floating, and the one selected + bool isSelectedInfoArea(const CDockWidgetInfoArea *infoArea) const; + + public slots: + //! Dock all widgets + void dockAllWidgets(); + + //! Adjust size for all dock widgets + void adjustSizeForAllDockWidgets(); + + //! All widgets floating + void floatAllWidgets(); + + //! Toggle dock / floating of the whole info area + void toggleFloating(); + + //! Toggle floating of index + void toggleFloating(int areaIndex); + + //! Select area + void selectArea(int areaIndex); + + protected: + //! Constructor + explicit CInfoArea(QWidget *parent = nullptr); + + //! Override close event + virtual void closeEvent(QCloseEvent *event) override; + + //! Preferred size when floating + virtual QSize getPreferredSizeWhenFloating(int areaIndex) const = 0; + + //! Info area (index) to icon + virtual const QPixmap &indexToPixmap(int areaIndex) const = 0; + + //! Init area after(!) GUI is initialized + void initInfoArea(); + + private: + Ui::CInfoArea *ui = nullptr; + QList m_dockableWidgets ; + QTabBar *m_tabBar = nullptr; + bool m_showTabTexts = true; + bool m_infoAreaFloating = false; //!< whole info area floating + bool m_showTabBar = true; //!< auto ajdust the floating widgets + + //! Tabify the widgets + void tabifyAllWidgets(); + + //! Untabify + void unTabifyAllWidgets(); + + //! The tab bar of the docked widgets + QTabBar *tabBarDockedWidgets() const; + + //! Corresponding dockable widgets + QList dockableWidgets() const; + + //! Corresponding dockable widget for given tab index + CDockWidgetInfoArea *getDockableWidgetByTabIndex(int tabBarIndex) const; + + //! Features of the dockable widgets + void setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features); + + //! Number of tabbed widgets + int countDockedWidgets() const; + + //! Widget to tab bar index + int widgetToTabBarIndex(const CDockWidgetInfoArea *dockWidget); + + //! Set the tab's icons + void setTabPixmaps(); + + //! Connect all widgets + void connectAllWidgets(); + + //! Margins for the floating widgets + void setMarginsWhenFloating(int left, int top, int right, int bottom); + + //! Margins for the dockable widgets + void setMarginsWhenDocked(int left, int top, int right, int bottom); + + private slots: + //! Tab bar has been double clicked + void ps_tabBarDoubleClicked(int tabBarIndex); + + //! A widget has changed its top level + void ps_onWidgetTopLevelChanged(CDockWidget *widget, bool topLevel); + + //! Style sheet has been changed + void ps_onStyleSheetChanged(); + + //! Context menu + void ps_showContextMenu(const QPoint &pos); + + //! Show the tab texts, or just the icons + void ps_showTabTexts(bool show); + + //! Show tab bar + void ps_showTabBar(bool show); + + //! Tab position for docked widgets tab + //! \remarks North or South working, East / West not + void ps_setTabBarPosition(QTabWidget::TabPosition position); + + //! Toggle tab position North - South + void ps_toggleTabBarPosition(); + + //! Set dock area used + void ps_setDockArea(Qt::DockWidgetArea area); + + //! Dock / floating of the whole info area + void ps_setInfoAreaFloating(bool floating); + }; +} + +#endif // guard