/* 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. 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 "blackgui/dockwidget.h" #include "blackgui/dockwidgetinfoarea.h" #include "blackgui/guiapplication.h" #include "blackgui/guiutility.h" #include "blackgui/infoarea.h" #include "blackgui/stylesheetutility.h" #include "blackmisc/icons.h" #include "blackmisc/logmessage.h" #include "blackmisc/verify.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace BlackMisc; namespace BlackGui { CInfoArea::CInfoArea(QWidget *parent) : QMainWindow(parent), CEnableForFramelessWindow(CEnableForFramelessWindow::WindowTool, false, "framelessInfoArea", this) { this->setWholeInfoAreaFloating(m_infoAreaFloating); } CInfoArea::~CInfoArea() { } void CInfoArea::initInfoArea() { // initInfoArea() needs be called after(!) GUI is setup // Ref T184, child areas are now "cached" in m_childInfoAreas // The original version did always use "findChildInfoAreas", so if there are ever any issues T184 might be reverted m_childInfoAreas = this->findOwnChildInfoAreas(); m_dockWidgetInfoAreas = this->findOwnDockWidgetInfoAreas(); this->setDockArea(Qt::TopDockWidgetArea); this->connectTopLevelChanged(); this->setFeaturesForDockableWidgets(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable); this->tabifyAllWidgets(); // context menu this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &CInfoArea::customContextMenuRequested, this, &CInfoArea::showContextMenu); connect(sGui, &CGuiApplication::styleSheetsChanged, this, &CInfoArea::onStyleSheetChanged, Qt::QueuedConnection); // initial style sheet setting this->onStyleSheetChanged(); // status bar if (this->statusBar()) { this->statusBar()->hide(); this->statusBar()->setMaximumHeight(0); } // fire an initial status QPointer myself(this); QTimer::singleShot(5000, this, [ = ] { if (myself) { myself->emitInfoAreaStatus(); } }); } void CInfoArea::addToContextMenu(QMenu *menu) const { if (!menu) { return; } bool hasDockedWidgets = this->countDockedWidgetInfoAreas() > 0; if (hasDockedWidgets) { menu->addAction(CIcons::dockTop16(), "Dock all", this, &CInfoArea::dockAllWidgets); menu->addAction(CIcons::floatAll16(), "Float all", this, &CInfoArea::floatAllWidgets); menu->addAction(CIcons::floatOne16(), QStringLiteral("Dock / float '%1'").arg(this->windowTitle()), this, &CInfoArea::toggleFloatingWholeInfoArea); QAction *lockTabBarMenuAction = new QAction(menu); lockTabBarMenuAction->setObjectName(this->objectName().append("LockTabBar")); lockTabBarMenuAction->setIconText("Lock tab bar"); lockTabBarMenuAction->setIcon(CIcons::lockClosed16()); lockTabBarMenuAction->setCheckable(true); lockTabBarMenuAction->setChecked(m_lockTabBar); menu->addAction(lockTabBarMenuAction); connect(lockTabBarMenuAction, &QAction::toggled, this, &CInfoArea::toggleTabBarLocked); menu->addSeparator(); QMenu *subMenuToggleFloat = new QMenu("Toggle Float/Dock", menu); QMenu *subMenuDisplay = new QMenu("Display", menu); QMenu *subMenuRestore = new QMenu("Restore from settings", menu); QMenu *subMenuResetPositions = new QMenu("Reset position", menu); subMenuRestore->setIcon(CIcons::load16()); subMenuResetPositions->setIcon(CIcons::refresh16()); subMenuRestore->addActions(this->getInfoAreaRestoreActions(subMenuRestore)); subMenuDisplay->addActions(this->getInfoAreaSelectActions(false, subMenuDisplay)); subMenuResetPositions->addActions(this->getInfoAreaResetPositionActions(subMenuResetPositions)); QSignalMapper *signalMapperToggleFloating = new QSignalMapper(menu); bool c = false; // check connections for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++) { const CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(i); const QString t = dw->windowTitleBackup(); const QPixmap pm = this->indexToPixmap(i); QAction *toggleFloatingMenuAction = new QAction(menu); toggleFloatingMenuAction->setObjectName(QString(t).append("ToggleFloatingAction")); toggleFloatingMenuAction->setIconText(t); toggleFloatingMenuAction->setIcon(pm); toggleFloatingMenuAction->setData(QVariant(i)); toggleFloatingMenuAction->setCheckable(true); toggleFloatingMenuAction->setChecked(!dw->isFloating()); subMenuToggleFloat->addAction(toggleFloatingMenuAction); c = connect(toggleFloatingMenuAction, &QAction::toggled, signalMapperToggleFloating, qOverload<>(&QSignalMapper::map)); BLACK_VERIFY_X(c, Q_FUNC_INFO, "Cannot map floating action"); // do not make that shutdown reason in a release build signalMapperToggleFloating->setMapping(toggleFloatingMenuAction, i); } c = connect(signalMapperToggleFloating, qOverload(&QSignalMapper::mapped), this, &CInfoArea::toggleFloatingByIndex); BLACK_VERIFY_X(c, Q_FUNC_INFO, "Cannot connect mapper"); // do not make that shutdown reason in a release build menu->addMenu(subMenuDisplay); if (c) { menu->addMenu(subMenuToggleFloat); } menu->addMenu(subMenuResetPositions); menu->addMenu(subMenuRestore); // where and how to display tab bar menu->addSeparator(); QAction *showMenuText = new QAction(menu); showMenuText->setObjectName("ShowDockedWidgetTextAction"); showMenuText->setIconText("Show tab text"); showMenuText->setIcon(CIcons::headingOne16()); showMenuText->setCheckable(true); showMenuText->setChecked(m_showTabTexts); menu->addAction(showMenuText); connect(showMenuText, &QAction::toggled, this, &CInfoArea::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(m_showTabBar); menu->addAction(showTabbar); connect(showTabbar, &QAction::toggled, this, &CInfoArea::showTabBar); // tab bar position menu->addAction(CIcons::dockBottom16(), "Toggle tabbar position", this, &CInfoArea::toggleTabBarPosition); Q_UNUSED(c); } } bool CInfoArea::isSelectedDockWidgetInfoArea(const CDockWidgetInfoArea *infoArea) const { if (!infoArea) { return false; } if (infoArea->isFloating()) { return false; } if (!infoArea->isWidgetVisible()) { return false; } if (!m_tabBar || m_tabBar->count() < 1) { return false; } return true; } const CDockWidgetInfoArea *CInfoArea::getSelectedDockInfoArea() const { // we assume that there can only be 1, non floating info area, // which is the only visible one // selecting is tab text independent (can be hidden) if (!m_tabBar || m_tabBar->count() < 1) { return nullptr; } for (const CDockWidgetInfoArea *ia : m_dockWidgetInfoAreas) { if (ia->isFloating()) { continue; } if (ia->isWidgetVisible()) { return ia; } } return nullptr; } int CInfoArea::getSelectedDockInfoAreaIndex() const { const CDockWidgetInfoArea *sel = getSelectedDockInfoArea(); if (!sel) { return -1; } const QString t(sel->windowTitleBackup()); int ia = getAreaIndexByWindowTitle(t); return ia; } QList CInfoArea::getDockWidgetInfoAreas() const { return makeRange(m_dockWidgetInfoAreas); } QList CInfoArea::getInfoAreaSelectActions(bool withShortcut, 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()); static const QString keys("123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); QAction *action = new QAction(QIcon(pm), wt, parent); action->setData(i); action->setObjectName(this->objectName().append(":getInfoAreaSelectActions:").append(wt)); if (withShortcut && i < keys.length()) { // T600 The strings "Ctrl", "Shift", "Alt" and "Meta" are recognized // https://doc.qt.io/archives/qt-4.8/qkeysequence.html Standard shortcuts already used // "Ctrl+Shift+%1" als works action->setShortcut(QKeySequence(QStringLiteral("Ctrl+Shift+%1").arg(keys.at(i)))); } connect(action, &QAction::triggered, this, &CInfoArea::selectAreaByAction); actions.append(action); i++; } return actions; } QList CInfoArea::getInfoAreaResetPositionActions(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(":getInfoAreaResetPositionActions:").append(wt)); connect(action, &QAction::triggered, this, &CInfoArea::resetPositionByAction); actions.append(action); i++; } 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, Qt::QueuedConnection); actions.append(action); i++; } return actions; } QList CInfoArea::getInfoAreaRestoreActions(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(":getInfoAreaRestoreActions:").append(wt)); connect(action, &QAction::triggered, this, &CInfoArea::restoreDockWidgetInfoArea); actions.append(action); i++; } return actions; } QList CInfoArea::getAreaIndexesDockedOrFloating(bool floating) const { QList indexes; for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++) { if (m_dockWidgetInfoAreas.at(i)->isFloating() == floating) { indexes.append(i); } } return indexes; } void CInfoArea::paintEvent(QPaintEvent *event) { CStyleSheetUtility::useStyleSheetInDerivedWidget(this, QStyle::PE_FrameWindow); Q_UNUSED(event); } void CInfoArea::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Right) { this->selectRightTab(); event->accept(); } else if (event->key() == Qt::Key_Left) { this->selectLeftTab(); event->accept(); } else { QWidget::keyPressEvent(event); } } void CInfoArea::dockAllWidgets() { this->tabifyAllWidgets(); } void CInfoArea::adjustSizeForAllDockWidgets() { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { dw->adjustSize(); } } void CInfoArea::floatAllWidgets() { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { if (dw->isFloating()) { continue; } dw->toggleFloating(); } } void CInfoArea::allFloatingOnTop() { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { const bool f = dw->isFloating(); CGuiUtility::stayOnTop(f, dw); if (f) { dw->show(); } } } void CInfoArea::toggleFloatingWholeInfoArea() { this->setWholeInfoAreaFloating(!m_infoAreaFloating); } void CInfoArea::toggleFloatingByIndex(int areaIndex) { if (!this->isValidAreaIndex(areaIndex)) { return; } CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex); BLACK_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area"); if (!dw) { return; } dw->toggleFloating(); } void CInfoArea::toggleVisibility(int areaIndex) { if (!this->isValidAreaIndex(areaIndex)) { return; } CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex); BLACK_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area"); if (!dw) { return; } dw->toggleVisibility(); } void CInfoArea::selectArea(int areaIndex) { CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex); BLACK_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area"); if (!dw) { return; } Q_ASSERT(m_tabBar); if (m_tabBar->count() < 1) { return; } if (dw->isFloating()) { dw->show(); } else { this->selectArea(dw); } } void CInfoArea::resetPosition(int areaIndex) { CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex); BLACK_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area"); if (!dw) { return; } dw->resetPosition(); } void CInfoArea::selectAreaByAction() { const QObject *sender = QObject::sender(); const QAction *action = qobject_cast(sender); BLACK_VERIFY(action); if (!action) { return; } const int index = action->data().toInt(); this->selectArea(index); } void CInfoArea::resetPositionByAction() { const QObject *sender = QObject::sender(); const QAction *action = qobject_cast(sender); BLACK_VERIFY(action); if (!action) { return; } const int index = action->data().toInt(); this->resetPosition(index); } void CInfoArea::toggleAreaFloatingByAction() { const QObject *sender = QObject::sender(); const QAction *action = qobject_cast(sender); BLACK_VERIFY(action); if (!action) { return; } const int index = action->data().toInt(); this->toggleFloatingByIndex(index); } void CInfoArea::restoreDockWidgetInfoArea() { const QObject *sender = QObject::sender(); const QAction *action = qobject_cast(sender); BLACK_VERIFY(action); if (!action) { return; } const int index = action->data().toInt(); this->restoreDockWidgetInfoAreaByIndex(index); } void CInfoArea::restoreDockWidgetInfoAreaByIndex(int areaIndex) { if (!this->isValidAreaIndex(areaIndex)) { return; } CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex); Q_ASSERT(dw); if (!dw) { return; } dw->restoreFromSettings(); } void CInfoArea::selectLeftTab() { if (!m_tabBar) return; if (m_tabBar->count() < 2) return; if (m_tabBar->currentIndex() > 0) { m_tabBar->setCurrentIndex(m_tabBar->currentIndex() - 1); } else { m_tabBar->setCurrentIndex(m_tabBar->count() - 1); } } void CInfoArea::selectRightTab() { if (!m_tabBar) return; if (m_tabBar->count() < 2) return; if (m_tabBar->currentIndex() < m_tabBar->count() - 2) { m_tabBar->setCurrentIndex(m_tabBar->currentIndex() + 1); } else { m_tabBar->setCurrentIndex(0); } } void CInfoArea::displayStatusMessage(const CStatusMessage &statusMessage) { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { dw->displayStatusMessage(statusMessage); } for (CInfoArea *ia : as_const(m_childInfoAreas)) { ia->displayStatusMessage(statusMessage); } } void CInfoArea::displayStatusMessages(const CStatusMessageList &statusMessages) { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { dw->displayStatusMessages(statusMessages); } for (CInfoArea *ia : as_const(m_childInfoAreas)) { ia->displayStatusMessages(statusMessages); } } void CInfoArea::setDockArea(Qt::DockWidgetArea area) { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { Qt::DockWidgetAreas newAreas = static_cast(area); Qt::DockWidgetAreas oldAreas = dw->allowedAreas(); if (oldAreas == newAreas) { continue; } dw->setAllowedAreas(newAreas); this->addDockWidget(area, dw); } } void CInfoArea::setWholeInfoAreaFloating(bool floating) { // float whole info area m_infoAreaFloating = floating; if (m_infoAreaFloating) { QPoint p = CGuiUtility::mainWidgetGlobalPosition(); this->setWindowFlags(Qt::Dialog); this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); 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(m_originalParent, this->windowFlags() & ~Qt::Window); this->setWindowFlags(this->windowFlags() & ~Qt::Window); // RW: The line below is commented to prevent making this widget visible as a top window // in case it is constructed without parent or anchestor widget. Contrary to the comment, // it does not seem to be necessary https://dev.vatsim-germany.org/issues/738 // KB 2018-12 with T447 T452 re-eanled the line again, but with parent condition if (this->parentWidget()) // this line { this->setVisible(true); // after redocking this is required } } emit this->changedWholeInfoAreaFloating(floating); } void CInfoArea::tabifyAllWidgets() { if (!sGui || sGui->isShuttingDown()) { return; } this->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::East); bool init = m_tabBar ? false : true; for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++) { CDockWidgetInfoArea *first = i > 0 ? m_dockWidgetInfoAreas.at(i - 1) : nullptr; CDockWidgetInfoArea *after = m_dockWidgetInfoAreas.at(i); Q_ASSERT(after); // trick, init widget as floating // this completely initializes the tab bar and all docked widgets if (init) { // float const QPoint offset(i * 10, i * 10); // after->setVisible(false); // after->setFloating(true); after->setOffsetWhenFloating(offset); const QSize floatingSize = this->getPreferredSizeWhenFloating(i); after->setPreferredSizeWhenFloating(floatingSize); after->initialFloating(); // dock again // after->setFloating(false); // after->setVisible(true); // reset floating flag, we want new resizing and position for first real floating after->resetWasAlreadyFloating(); } else { after->setFloating(false); } if (!first) { continue; } this->tabifyDockWidget(first, after); } // as now tabified, set tab if (init) { m_tabBar = this->findChild(); // if we have > 1 docked widgets, we have a tab bar if (m_tabBar && sGui) { const QString qss = sGui->getStyleSheetUtility().style(CStyleSheetUtility::fileNameDockWidgetTab()); m_tabBar->setStyleSheet(qss); m_tabBar->setObjectName("comp_MainInfoAreaDockWidgetTab"); m_tabBar->setMovable(false); m_tabBar->setElideMode(Qt::ElideNone); m_tabBar->setUsesScrollButtons(true); // East / West does not work (shown, but area itself empty) // South does not have any effect m_tabBar->setShape(QTabBar::TriangularSouth); // signals, use Qt::QueuedConnection to avoid issues during shutdown connect(m_tabBar, &QTabBar::tabBarDoubleClicked, this, &CInfoArea::onTabBarDoubleClicked, Qt::QueuedConnection); connect(m_tabBar, &QTabBar::currentChanged, this, &CInfoArea::onTabBarIndexChanged, Qt::QueuedConnection); } else { // <= 1 dock widget m_tabBar = new QTabBar(this); m_tabBar->hide(); } } // set current index, and always set pixmaps if (this->countDockedWidgetInfoAreas() > 0) { m_tabBar->setCurrentIndex(0); } if (m_tabBar->count() > 0) { this->setTabPixmaps(); } } void CInfoArea::unTabifyAllWidgets() { if (m_dockWidgetInfoAreas.size() < 2) return; CDockWidgetInfoArea *first = m_dockWidgetInfoAreas.constFirst(); for (int i = 1; i < m_dockWidgetInfoAreas.size(); i++) { CDockWidgetInfoArea *after = m_dockWidgetInfoAreas.at(i); Q_ASSERT(after); this->splitDockWidget(first, after, Qt::Horizontal); } } bool CInfoArea::isValidAreaIndex(int areaIndex) const { if (!m_tabBar) { return false; } return areaIndex >= 0 && areaIndex < m_dockWidgetInfoAreas.size(); } void CInfoArea::connectTopLevelChanged() { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { connect(dw, &CDockWidgetInfoArea::widgetTopLevelChanged, this, &CInfoArea::onWidgetTopLevelChanged, Qt::QueuedConnection); } } QList CInfoArea::findOwnDockWidgetInfoAreas() const { // own dock widget areas without nested ones return this->findChildren(QString(), Qt::FindDirectChildrenOnly); } QList CInfoArea::findOwnChildInfoAreas() const { return this->findChildren(QString(), Qt::FindDirectChildrenOnly); } void CInfoArea::emitInfoAreaStatus() { if (!sGui || sGui->isShuttingDown()) { return; } // avoid access to deleted components const int sia = this->getSelectedDockInfoAreaIndex(); const QList floating = this->getAreaIndexesDockedOrFloating(true); const QList docked = this->getAreaIndexesDockedOrFloating(false); emit this->changedInfoAreaStatus(sia, docked, floating); } void CInfoArea::onTabBarIndexChanged(int tabBarIndex) { emit this->changedInfoAreaTabBarIndex(tabBarIndex); this->emitInfoAreaStatus(); } int CInfoArea::countDockedWidgetInfoAreas() const { if (!m_tabBar) { return 0; } return m_tabBar->count(); } CDockWidgetInfoArea *CInfoArea::getDockWidgetInfoAreaByTabBarIndex(int tabBarIndex) const { if (tabBarIndex >= m_dockWidgetInfoAreas.count() || tabBarIndex < 0) { return nullptr; } if (!m_tabBar) { return nullptr; } const QString t(m_tabBar->tabText(tabBarIndex)); // we have a title and search by that (best option, as order does not matter) if (!t.isEmpty()) { return this->getDockWidgetInfoAreaByWindowTitle(t); } // no title, we assume the n-th not floating tab is correct // this will work if the order in m_dockWidgetInfoAreas matches int c = 0; for (CDockWidgetInfoArea *dw : m_dockWidgetInfoAreas) { if (dw->isFloating()) { continue; } if (c == tabBarIndex) { return dw; } c++; } Q_ASSERT_X(false, Q_FUNC_INFO, "no dock widgte found"); return nullptr; } CDockWidgetInfoArea *CInfoArea::getDockWidgetInfoAreaByWindowTitle(const QString &title) const { Q_ASSERT_X(!title.isEmpty(), Q_FUNC_INFO, "No title"); for (CDockWidgetInfoArea *dw : m_dockWidgetInfoAreas) { if (CGuiUtility::lenientTitleComparison(dw->windowTitleOrBackup(), title)) { return dw; } } return nullptr; } int CInfoArea::getAreaIndexByWindowTitle(const QString &title) const { Q_ASSERT_X(!title.isEmpty(), Q_FUNC_INFO, "No title"); for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++) { if (CGuiUtility::lenientTitleComparison(m_dockWidgetInfoAreas.at(i)->windowTitleOrBackup(), title)) { return i; } } Q_ASSERT_X(false, Q_FUNC_INFO, "No area for title"); return -1; } int CInfoArea::getTabBarIndexByTitle(const QString &title) const { Q_ASSERT_X(!title.isEmpty(), Q_FUNC_INFO, "No title"); if (m_tabBar->count() < 1) { return -1;} for (int i = 0; i < m_tabBar->count(); i++) { if (CGuiUtility::lenientTitleComparison(m_tabBar->tabText(i), title)) { return i; } } Q_ASSERT_X(!title.isEmpty(), Q_FUNC_INFO, "Wrong title"); return -1; } int CInfoArea::dockWidgetInfoAreaToTabBarIndex(const CDockWidgetInfoArea *dockWidgetInfoArea) const { if (!dockWidgetInfoArea) { return -1; } if (dockWidgetInfoArea->isFloating()) { return -1; } return getTabBarIndexByTitle(dockWidgetInfoArea->windowTitleOrBackup()); } void CInfoArea::selectArea(const CDockWidgetInfoArea *dockWidgetInfoArea) { if (!m_tabBar) { return; } int tabIndex = this->dockWidgetInfoAreaToTabBarIndex(dockWidgetInfoArea); if (tabIndex >= 0 && tabIndex < m_tabBar->count()) { m_tabBar->setCurrentIndex(tabIndex); } } void CInfoArea::setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features) { for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { dw->setFeatures(features); } } void CInfoArea::setTabPixmaps() { if (!m_tabBar) { return; } for (int i = 0; i < m_tabBar->count(); i++) { const QString t(m_tabBar->tabText(i)); const int areaIndex = t.isEmpty() ? i : this->getAreaIndexByWindowTitle(t); m_tabBar->setTabIcon(i, indexToPixmap(areaIndex)); } } void CInfoArea::onTabBarDoubleClicked(int tabBarIndex) { if (m_lockTabBar) { CLogMessage(this).info(u"Locked, double click will not cause floating"); return; } CDockWidgetInfoArea *dw = this->getDockWidgetInfoAreaByTabBarIndex(tabBarIndex); if (!dw) { return; } dw->toggleFloating(); QPointer myself(this); QTimer::singleShot(1000, this, [ = ] { myself->emitInfoAreaStatus(); }); } void CInfoArea::onWidgetTopLevelChanged(CDockWidget *dockWidget, bool topLevel) { Q_ASSERT(dockWidget); Q_UNUSED(topLevel); if (!dockWidget) { return; } // fix pixmaps this->setTabPixmaps(); // select index if (!topLevel) { const CDockWidgetInfoArea *dwia = dynamic_cast(dockWidget); this->selectArea(dwia); } // KB commented out with T592 // when toplevel is changed, I need a round in the event loop until // current tab bar widget is visible // QTimer::singleShot(250, this, &CInfoArea::emitInfoAreaStatus); } void CInfoArea::onStyleSheetChanged() { if (m_tabBar) { if (!sGui || sGui->isShuttingDown()) { return; } const QString qss = sGui->getStyleSheetUtility().style(CStyleSheetUtility::fileNameDockWidgetTab()); m_tabBar->setStyleSheet(qss); } } void CInfoArea::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::showTabTexts(bool show) { if (show == m_showTabTexts) { return; } m_showTabTexts = show; for (CDockWidgetInfoArea *dw : as_const(m_dockWidgetInfoAreas)) { dw->showTitleWhenDocked(show); } } void CInfoArea::showTabBar(bool show) { if (show == m_showTabBar) return; m_showTabBar = show; if (!m_tabBar) return; m_tabBar->setVisible(show); // not working, but setting right value will not harm anything m_tabBar->setMaximumHeight(show ? 10000 : 0); // does the trick this->adjustSizeForAllDockWidgets(); } void CInfoArea::toggleTabBarLocked(bool locked) { m_lockTabBar = locked; } void CInfoArea::setTabBarPosition(QTabWidget::TabPosition position) { Q_ASSERT_X(position == QTabWidget::North || position == QTabWidget::South, Q_FUNC_INFO, "Wrong tabbar position"); this->setTabPosition(Qt::TopDockWidgetArea, position); } void CInfoArea::toggleTabBarPosition() { QTabWidget::TabPosition p = (this->tabPosition(Qt::TopDockWidgetArea) == QTabWidget::North) ? QTabWidget::South : QTabWidget::North; this->setTabBarPosition(p); } void CInfoArea::closeEvent(QCloseEvent *event) { if (this->isFloating()) { this->toggleFloatingWholeInfoArea(); event->setAccepted(false); // refuse -> do not close } else { QMainWindow::closeEvent(event); } } } // namespace