diff --git a/src/blackgui/components/maininfoareacomponent.cpp b/src/blackgui/components/maininfoareacomponent.cpp new file mode 100644 index 000000000..d34486987 --- /dev/null +++ b/src/blackgui/components/maininfoareacomponent.cpp @@ -0,0 +1,535 @@ +/* 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 "maininfoareacomponent.h" +#include "ui_maininfoareacomponent.h" +#include "../stylesheetutility.h" +#include "../guiutility.h" +#include "blackmisc/iconsstandard.h" +#include +#include +#include +#include + +using namespace BlackMisc; + +namespace BlackGui +{ + namespace Components + { + CMainInfoAreaComponent::CMainInfoAreaComponent(QWidget *parent) : + QMainWindow(parent), ui(new Ui::CMainInfoAreaComponent) + { + this->ps_setInfoAreaFloating(this->m_infoAreaFloating); + ui->setupUi(this); + this->setWindowIcon(CIconsStandard::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(); + } + + CMainInfoAreaComponent::~CMainInfoAreaComponent() + { + delete ui; + } + + void CMainInfoAreaComponent::addToContextMenu(QMenu *menu) const + { + if (!menu) return; + menu->addAction(CIconsStandard::dockTop16(), "Dock all", this, SLOT(dockAllWidgets())); + menu->addAction(CIconsStandard::floatAll16(), "Float all", this, SLOT(floatAllWidgets())); + menu->addAction(CIconsStandard::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 = infoAreaToIcon(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); + } + 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(CIconsStandard::headingOne16()); + showMenuText->setCheckable(true); + showMenuText->setChecked(this->m_showTabTexts); + menu->addAction(showMenuText); + connect(showMenuText, &QAction::toggled, this, &CMainInfoAreaComponent::ps_showTabTexts); + + menu->addAction(CIconsStandard::dockBottom16(), "Toogle tabbar position", this, SLOT(ps_toggleTabBarPosition())); + } + } + + CAtcStationComponent *CMainInfoAreaComponent::getAtcStationComponent() + { + return this->ui->comp_AtcStations; + } + + CAircraftComponent *CMainInfoAreaComponent::getAircraftComponent() + { + return this->ui->comp_Aircrafts; + } + + CUserComponent *CMainInfoAreaComponent::getUserComponent() + { + return this->ui->comp_Users; + } + + CFlightPlanComponent *CMainInfoAreaComponent::getFlightPlanComponent() + { + return this->ui->comp_FlightPlan; + } + + CSettingsComponent *CMainInfoAreaComponent::getSettingsComponent() + { + return this->ui->comp_Settings; + } + + CLogComponent *CMainInfoAreaComponent::getLogComponent() + { + return this->ui->comp_Log; + } + + CSimulatorComponent *CMainInfoAreaComponent::getSimulatorComponent() + { + return this->ui->comp_Simulator; + } + + CTextMessageComponent *CMainInfoAreaComponent::getTextMessageComponent() + { + return this->ui->comp_TextMessages; + } + + void CMainInfoAreaComponent::dockAllWidgets() + { + this->tabifyAllWidgets(); + } + + 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); + } + } + + 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::ps_setDockArea(Qt::DockWidgetArea area) + { + QList::iterator i; + for (i = this->m_dockableWidgets.begin(); i != this->m_dockableWidgets.end(); ++i) + { + CDockWidgetInfoArea *dw = (*i); + 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) + { + 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; + QPoint pos = CGuiUtility::assumedMainWindowPosition(); //main window pos. not initialized yet + + 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 + if (init) + { + QPoint initPoint(pos.rx() + i * 25, pos.ry() + i * 20); + after->setVisible(false); + after->setFloating(true); + after->move(initPoint); + after->setVisible(true); + } + 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->m_tabBar->setShape(QTabBar::TriangularEast); + + // 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); + } + + this->setTabPixmaps(); + 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() + { + QList::iterator i; + for (i = this->m_dockableWidgets.begin(); i != this->m_dockableWidgets.end(); ++i) + { + connect(*i, &CDockWidgetInfoArea::widgetTopLevelChanged, this, &CMainInfoAreaComponent::ps_onWidgetTopLevelChanged); + } + } + + void CMainInfoAreaComponent::setMarginsWhenFloating(int left, int top, int right, int bottom) + { + QList::iterator i; + for (i = this->m_dockableWidgets.begin(); i != this->m_dockableWidgets.end(); ++i) + { + //! Margins when window is floating + (*i)->setMarginsWhenFloating(left, top, right, bottom); + } + } + + void CMainInfoAreaComponent::setMarginsWhenDocked(int left, int top, int right, int bottom) + { + QList::iterator i; + for (i = this->m_dockableWidgets.begin(); i != this->m_dockableWidgets.end(); ++i) + { + //! Margins when window is floating + (*i)->setMarginsWhenDocked(left, top, right, bottom); + } + } + + const QPixmap &CMainInfoAreaComponent::infoAreaToIcon(CMainInfoAreaComponent::InfoArea infoArea) + { + switch (infoArea) + { + case InfoAreaUsers: + return CIconsStandard::appUsers16(); + case InfoAreaWeather: + return CIconsStandard::appWeather16(); + case InfoAreaAtc: + return CIconsStandard::appAtc16(); + case InfoAreaAircrafts: + return CIconsStandard::appAircrafts16(); + case InfoAreaSettings: + return CIconsStandard::appSettings16(); + case InfoAreaFlightPlan: + return CIconsStandard::appFlightplan16(); + case InfoAreaTextMessages: + return CIconsStandard::appTextMessages16(); + case InfoAreaSimulator: + return CIconsStandard::appSimulator16(); + case InfoAreaMappings: + return CIconsStandard::appMappings16(); + case InfoAreaLog: + return CIconsStandard::appLog16(); + default: + return CIconsStandard::empty(); + } + } + + CDockWidgetInfoArea *CMainInfoAreaComponent::getDockableWidgetByIndex(int index) const + { + if (index >= this->m_dockableWidgets.count() || index < 0) return nullptr; + return this->m_dockableWidgets.at(index); + } + + CDockWidgetInfoArea *CMainInfoAreaComponent::selectedDockableWidget() const + { + if (!this->m_tabBar) return nullptr; + int i = this->m_tabBar->currentIndex(); + return getDockableWidgetByIndex(i); + } + + int CMainInfoAreaComponent::countDockedWidgets() const + { + if (!this->m_tabBar) return 0; + return this->m_tabBar->count(); + } + + int CMainInfoAreaComponent::widgetToTabBarIndex(const CDockWidgetInfoArea *dockWidget) + { + if (!dockWidget) return -1; + if (dockWidget->isFloating()) return -1; + int tabBarIndex = 0; + QList::iterator i; + for (i = this->m_dockableWidgets.begin(); i != this->m_dockableWidgets.end(); ++i) + { + CDockWidgetInfoArea *dw = (*i); + if (dw->isFloating()) continue; // not in tab bar + if (dw == dockWidget) return tabBarIndex; + tabBarIndex++; + } + return -1; + } + + void CMainInfoAreaComponent::setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features) + { + for (int i = 0; i < this->m_dockableWidgets.size(); i++) + { + CDockWidgetInfoArea *dw = this->m_dockableWidgets.at(i); + 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(infoAreaToIcon(area)); + this->m_tabBar->setTabIcon(i, p); + } + } + + void CMainInfoAreaComponent::ps_tabBarDoubleClicked(int index) + { + CDockWidgetInfoArea *dw = (index >= 0) ? + this->getDockableWidgetByIndex(index) : + this->selectedDockableWidget(); + 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; + QList::iterator i; + for (i = this->m_dockableWidgets.begin(); i != this->m_dockableWidgets.end(); ++i) + { + CDockWidgetInfoArea *dw = (*i); + dw->showTitleWhenDocked(show); + } + } + + 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 new file mode 100644 index 000000000..33b09320f --- /dev/null +++ b/src/blackgui/components/maininfoareacomponent.h @@ -0,0 +1,200 @@ +/* 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. + */ + +#ifndef BLACKGUI_MAININFOAREACOMPONENT_H +#define BLACKGUI_MAININFOAREACOMPONENT_H + +//! \file + +#include "../dockwidgetinfoarea.h" +#include "atcstationcomponent.h" +#include "aircraftcomponent.h" +#include "usercomponent.h" +#include "textmessagecomponent.h" +#include "simulatorcomponent.h" +#include "flightplancomponent.h" +#include "settingscomponent.h" +#include "logcomponent.h" +#include +#include +#include + +namespace Ui { class CMainInfoAreaComponent; } + +namespace BlackGui +{ + namespace Components + { + + //! Main info area + class CMainInfoAreaComponent : public QMainWindow + { + Q_OBJECT + + public: + //! Constructor + explicit CMainInfoAreaComponent(QWidget *parent = nullptr); + + //! Destructor + ~CMainInfoAreaComponent(); + + //! Info areas + enum InfoArea + { + // index must match tab index! + InfoAreaAircrafts = 0, + InfoAreaAtc = 1, + InfoAreaUsers = 2, + InfoAreaTextMessages = 3, + InfoAreaSimulator = 4, + InfoAreaFlightPlan = 5, + InfoAreaWeather = 6, + InfoAreaMappings = 7, + InfoAreaLog = 8, + InfoAreaSettings = 9 + }; + + //! 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(); + + //! Aircrafts + CAircraftComponent *getAircraftComponent(); + + //! User component + CUserComponent *getUserComponent(); + + //! Flight plan + CFlightPlanComponent *getFlightPlanComponent(); + + //! Settings + CSettingsComponent *getSettingsComponent(); + + //! Log messages + CLogComponent *getLogComponent(); + + //! Simulator + CSimulatorComponent *getSimulatorComponent(); + + //! Text messages + CTextMessageComponent *getTextMessageComponent(); + + public slots: + //! Dock all widgets + void dockAllWidgets(); + + //! 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); + + //! Select area + void selectArea(InfoArea infoArea); + + //! Select area + void selectArea(int index); + + protected: + //! Override close event + virtual void closeEvent(QCloseEvent *event) 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 + + //! 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 + CDockWidgetInfoArea *getDockableWidgetByIndex(int index) const; + + //! Selected dockable widget + CDockWidgetInfoArea *selectedDockableWidget() 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); + + //! Info area to icon + static const QPixmap &infoAreaToIcon(InfoArea infoArea); + + private slots: + //! Tab bar has been double clicked + void ps_tabBarDoubleClicked(int index); + + //! 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); + + //! 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 diff --git a/src/blackgui/components/maininfoareacomponent.ui b/src/blackgui/components/maininfoareacomponent.ui new file mode 100644 index 000000000..2b62cd0af --- /dev/null +++ b/src/blackgui/components/maininfoareacomponent.ui @@ -0,0 +1,494 @@ + + + CMainInfoAreaComponent + + + + 0 + 0 + 1207 + 97 + + + + + 0 + 0 + + + + Main info area + + + + :/own/icons/own/swift/swift24.png:/own/icons/own/swift/swift24.png + + + + + + true + + + true + + + false + + + QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks|QMainWindow::ForceTabbedDocks + + + + + + 149 + 80 + + + + QDockWidget::AllDockWidgetFeatures + + + Qt::TopDockWidgetArea + + + Aircrafts + + + 4 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + 127 + 38 + + + + false + + + Qt::TopDockWidgetArea + + + ATC stations + + + 4 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + Qt::TopDockWidgetArea + + + Users + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + Qt::TopDockWidgetArea + + + Text messages + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + Qt::TopDockWidgetArea + + + Simulator + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + Qt::TopDockWidgetArea + + + Flight plan + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + Qt::TopDockWidgetArea + + + Weather + + + 4 + + + + + 0 + 0 + + + + + 0 + + + 2 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + Qt::TopDockWidgetArea + + + Mappings + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + Qt::TopDockWidgetArea + + + Log + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + Qt::TopDockWidgetArea + + + Settings + + + 4 + + + + + 0 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + + + + + + + BlackGui::Components::CAtcStationComponent + QTabWidget +
blackgui/components/atcstationcomponent.h
+ 1 +
+ + BlackGui::Components::CAircraftComponent + QTabWidget +
blackgui/components/aircraftcomponent.h
+ 1 +
+ + BlackGui::Components::CFlightPlanComponent + QTabWidget +
blackgui/components/flightplancomponent.h
+ 1 +
+ + BlackGui::Components::CUserComponent + QTabWidget +
blackgui/components/usercomponent.h
+ 1 +
+ + BlackGui::Components::CTextMessageComponent + QTabWidget +
blackgui/components/textmessagecomponent.h
+ 1 +
+ + BlackGui::Components::CSettingsComponent + QTabWidget +
blackgui/components/settingscomponent.h
+ 1 +
+ + BlackGui::CDockWidgetInfoArea + QDockWidget +
blackgui/dockwidgetinfoarea.h
+ 1 +
+ + BlackGui::Components::CLogComponent + QFrame +
blackgui/components/logcomponent.h
+ 1 +
+ + BlackGui::Components::CSimulatorComponent + QTabWidget +
blackgui/components/simulatorcomponent.h
+ 1 +
+
+ + + + +
diff --git a/src/blackgui/dockwidget.cpp b/src/blackgui/dockwidget.cpp new file mode 100644 index 000000000..e1a2d4eeb --- /dev/null +++ b/src/blackgui/dockwidget.cpp @@ -0,0 +1,136 @@ +/* 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 "dockwidget.h" +#include "blackmisc/iconsstandard.h" +#include + +namespace BlackGui +{ + CDockWidget::CDockWidget(QWidget *parent) : QDockWidget(parent) + { + this->initTitleBarWidgets(); + + // connect + connect(this, &QDockWidget::topLevelChanged, this, &CDockWidget::ps_onTopLevelChanged); + + // context menu + this->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &CDockWidget::customContextMenuRequested, this, &CDockWidget::ps_showContextMenu); + } + + void CDockWidget::setOriginalTitleBar() + { + if (!this->m_titleBarOriginal) { this->initTitleBarWidgets(); } + if (this->titleBarWidget() == this->m_titleBarOriginal) return; // on purpose, as I do not know what happens when I call setTitleBar + this->setTitleBarWidget(this->m_titleBarOriginal); + } + + void CDockWidget::setEmptyTitleBar() + { + if (!this->m_titleBarOriginal) { this->initTitleBarWidgets(); } + if (this->titleBarWidget() == this->m_emptyTitleBar) return; // on purpose, as I do not know what happens when I call setTitleBar + this->setTitleBarWidget(this->m_emptyTitleBar); + } + + void CDockWidget::setNullTitleBar() + { + this->setTitleBarWidget(nullptr); + } + + void CDockWidget::setWindowTitle(const QString &title) + { + this->m_windowTitleBackup = title; + QDockWidget::setWindowTitle(title); + } + + void CDockWidget::showTitleWhenDocked(bool show) + { + this->m_windowTitleWhenDocked = show; + if (show) + { + QDockWidget::setWindowTitle(this->m_windowTitleBackup); + } + else + { + QDockWidget::setWindowTitle(""); + } + } + + void CDockWidget::toggleFloating() + { + this->setFloating(!this->isFloating()); + } + + void CDockWidget::closeEvent(QCloseEvent *event) + { + if (this->isFloating()) + { + this->toggleFloating(); + event->setAccepted(false); // refuse -> do not close + } + else + { + QDockWidget::closeEvent(event); + } + } + + void CDockWidget::addToContextMenu(QMenu *contextMenu) const + { + if (this->isFloating()) + { + contextMenu->addAction(BlackMisc::CIconsStandard::dockTop16(), "Dock", this, SLOT(toggleFloating())); + } + else + { + contextMenu->addAction(BlackMisc::CIconsStandard::floatOne16(), "Float", this, SLOT(toggleFloating())); + } + } + + void CDockWidget::ps_onTopLevelChanged(bool topLevel) + { + if (topLevel) + { + if (this->m_windowTitleBackup != QDockWidget::windowTitle()) + { + QDockWidget::setWindowTitle(this->m_windowTitleBackup); + } + this->setNullTitleBar(); + this->setContentsMargins(this->m_marginsWhenFloating); + } + else + { + if (!this->m_windowTitleWhenDocked) + { + QDockWidget::setWindowTitle(""); + } + + this->setEmptyTitleBar(); + this->setContentsMargins(this->m_marginsWhenDocked); + } + emit this->widgetTopLevelChanged(this, topLevel); + } + + void CDockWidget::initTitleBarWidgets() + { + this->m_titleBarOriginal = this->titleBarWidget(); + this->m_emptyTitleBar = new QWidget(this); + this->setTitleBarWidget(this->m_emptyTitleBar); + } + + void CDockWidget::ps_showContextMenu(const QPoint &pos) + { + // for most widgets + QPoint globalPos = this->mapToGlobal(pos); + QScopedPointer contextMenu(new QMenu(this)); + this->addToContextMenu(contextMenu.data()); + QAction *selectedItem = contextMenu.data()->exec(globalPos); + Q_UNUSED(selectedItem); + } +} diff --git a/src/blackgui/dockwidget.h b/src/blackgui/dockwidget.h new file mode 100644 index 000000000..60f998e7a --- /dev/null +++ b/src/blackgui/dockwidget.h @@ -0,0 +1,102 @@ +/* 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. + */ + +#ifndef BLACKGUI_DOCKWIDGET_H +#define BLACKGUI_DOCKWIDGET_H + +//! \file + +#include +#include +#include +#include + +namespace BlackGui +{ + + //! Our base class for dock widgets, containing some specialized functionality + class CDockWidget : public QDockWidget + { + Q_OBJECT + + public: + //! Constructor + explicit CDockWidget(QWidget *parent = nullptr); + + //! Set original title bar + void setOriginalTitleBar(); + + //! Set empty title bar + void setEmptyTitleBar(); + + //! Set null title bar + void setNullTitleBar(); + + //! Margins when window is floating + void setMarginsWhenFloating(const QMargins &margins) { this->m_marginsWhenFloating = margins; } + + //! Margins when window is floating + void setMarginsWhenFloating(int left, int top, int right, int bottom) { this->m_marginsWhenFloating = QMargins(left, top, right, bottom); } + + //! Margins when widget is floating + void setMarginsWhenDocked(const QMargins &margins) { this->m_marginsWhenDocked = margins; } + + //! Margins when widget is floating + void setMarginsWhenDocked(int left, int top, int right, int bottom) { this->m_marginsWhenDocked = QMargins(left, top, right, bottom); } + + //! Window title backup + const QString &windowTitleBackup() const { return this->m_windowTitleBackup; } + + //! Window title when window is docked + bool showTitleWhenDocked() const { return this->m_windowTitleWhenDocked; } + + //! Show the window title when docked + void showTitleWhenDocked(bool show); + + public slots: + //! Toggle floating + void toggleFloating(); + + //! Set title and internally keep a backup + void setWindowTitle(const QString &title); + + signals: + //! Top level has changed for given widget + void widgetTopLevelChanged(CDockWidget *, bool topLevel); + + protected: + //! Override close event + virtual void closeEvent(QCloseEvent *event) override; + + //! Contribute to menu + virtual void addToContextMenu(QMenu *contextMenu) const; + + private slots: + //! Top level has been chaged + virtual void ps_onTopLevelChanged(bool topLevel); + + //! Context menu + void ps_showContextMenu(const QPoint &pos); + + private: + 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 + bool m_windowTitleWhenDocked = true; + + //! Empty widget with no size + void initTitleBarWidgets(); + + }; + +} // namespace + +#endif // guard diff --git a/src/blackgui/dockwidgetinfoarea.cpp b/src/blackgui/dockwidgetinfoarea.cpp new file mode 100644 index 000000000..77324b5f3 --- /dev/null +++ b/src/blackgui/dockwidgetinfoarea.cpp @@ -0,0 +1,33 @@ +/* Copyright (C) 2013 + * 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 "dockwidgetinfoarea.h" +#include "components/maininfoareacomponent.h" +#include "blackmisc/iconsstandard.h" + +using namespace BlackGui::Components; + +namespace BlackGui +{ + CDockWidgetInfoArea::CDockWidgetInfoArea(QWidget *parent) : CDockWidget(parent) { } + + void CDockWidgetInfoArea::addToContextMenu(QMenu *contextMenu) const + { + Components::CMainInfoAreaComponent *mainWidget = qobject_cast(parentWidget()); + Q_ASSERT(mainWidget); + if (!mainWidget) return; + + // Dockable widget's context menu + CDockWidget::addToContextMenu(contextMenu); + + // from main component + contextMenu->addSeparator(); + mainWidget->addToContextMenu(contextMenu); + } +} diff --git a/src/blackgui/dockwidgetinfoarea.h b/src/blackgui/dockwidgetinfoarea.h new file mode 100644 index 000000000..32c08f867 --- /dev/null +++ b/src/blackgui/dockwidgetinfoarea.h @@ -0,0 +1,37 @@ +/* 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. + */ + +#ifndef BLACKGUI_DOCKWIDGETINFOAREA_H +#define BLACKGUI_DOCKWIDGETINFOAREA_H + +//! \file + +#include "blackgui/dockwidget.h" + +namespace BlackGui +{ + + //! Class for dock widgets in the info area, containing some specialized functionality + class CDockWidgetInfoArea : public CDockWidget + { + Q_OBJECT + + public: + //! Constructor + explicit CDockWidgetInfoArea(QWidget *parent = nullptr); + + protected: + //! Contribute to menu + virtual void addToContextMenu(QMenu *contextMenu) const override; + + }; + +} // namespace + +#endif // guard diff --git a/src/blackgui/qss/dockwidgettab.qss b/src/blackgui/qss/dockwidgettab.qss new file mode 100644 index 000000000..c6ac37ada --- /dev/null +++ b/src/blackgui/qss/dockwidgettab.qss @@ -0,0 +1,10 @@ +QTabBar::tab { + margin-top: 2px; + margin-bottom: 2px; + margin-left: 2px; + margin-right: 2px; + padding-top: 2px; + padding-bottom: 2px; + border: 0px; + border-radius: 0px; +}