swift
infoarea.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2014 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "gui/infoarea.h"
5 
6 #include <QAction>
7 #include <QCloseEvent>
8 #include <QFlags>
9 #include <QIcon>
10 #include <QKeyEvent>
11 #include <QMenu>
12 #include <QPixmap>
13 #include <QPoint>
14 #include <QPointer>
15 #include <QScopedPointer>
16 #include <QSignalMapper>
17 #include <QStatusBar>
18 #include <QStyle>
19 #include <QTabBar>
20 #include <QTimer>
21 #include <QVariant>
22 #include <QWidget>
23 #include <QtGlobal>
24 
25 #include "gui/dockwidget.h"
26 #include "gui/dockwidgetinfoarea.h"
27 #include "gui/guiapplication.h"
28 #include "gui/guiutility.h"
29 #include "gui/stylesheetutility.h"
30 #include "misc/icons.h"
31 #include "misc/logmessage.h"
32 #include "misc/verify.h"
33 
34 using namespace swift::misc;
35 
36 namespace swift::gui
37 {
38  CInfoArea::CInfoArea(QWidget *parent)
39  : QMainWindow(parent),
40  CEnableForFramelessWindow(CEnableForFramelessWindow::WindowTool, false, "framelessInfoArea", this)
41  {
42  this->setWholeInfoAreaFloating(m_infoAreaFloating);
43  }
44 
46 
48  {
49  // initInfoArea() needs be called after(!) GUI is setup
50 
51  // Ref T184, child areas are now "cached" in m_childInfoAreas
52  // The original version did always use "findChildInfoAreas", so if there are ever any issues T184 might be
53  // reverted
54  m_childInfoAreas = this->findOwnChildInfoAreas();
55  m_dockWidgetInfoAreas = this->findOwnDockWidgetInfoAreas();
56 
57  this->setDockArea(Qt::TopDockWidgetArea);
58  this->connectTopLevelChanged();
59  this->setFeaturesForDockableWidgets(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable |
60  QDockWidget::DockWidgetClosable);
61  this->tabifyAllWidgets();
62 
63  // context menu
64  this->setContextMenuPolicy(Qt::CustomContextMenu);
65  connect(this, &CInfoArea::customContextMenuRequested, this, &CInfoArea::showContextMenu);
66  connect(sGui, &CGuiApplication::styleSheetsChanged, this, &CInfoArea::onStyleSheetChanged,
67  Qt::QueuedConnection);
68 
69  // initial style sheet setting
70  this->onStyleSheetChanged();
71 
72  // status bar
73  if (this->statusBar())
74  {
75  this->statusBar()->hide();
76  this->statusBar()->setMaximumHeight(0);
77  }
78 
79  // fire an initial status
80  QPointer<CInfoArea> myself(this);
81  QTimer::singleShot(5000, this, [=] {
82  if (myself) { myself->emitInfoAreaStatus(); }
83  });
84  }
85 
86  void CInfoArea::addToContextMenu(QMenu *menu) const
87  {
88  if (!menu) { return; }
89  bool hasDockedWidgets = this->countDockedWidgetInfoAreas() > 0;
90  if (hasDockedWidgets)
91  {
92  menu->addAction(CIcons::dockTop16(), "Dock all", this, &CInfoArea::dockAllWidgets);
93  menu->addAction(CIcons::floatAll16(), "Float all", this, &CInfoArea::floatAllWidgets);
94  menu->addAction(CIcons::refresh16(), "Reset all floating to defaults", this,
96  menu->addAction(CIcons::refresh16(), "Reset all to defaults", this, &CInfoArea::resetAllWidgetSettings);
97 
98  menu->addAction(CIcons::floatOne16(), QStringLiteral("Dock / float '%1'").arg(this->windowTitle()), this,
100  QAction *lockTabBarMenuAction = new QAction(menu);
101  lockTabBarMenuAction->setObjectName(this->objectName().append("LockTabBar"));
102  lockTabBarMenuAction->setIconText("Lock tab bar");
103  lockTabBarMenuAction->setIcon(CIcons::lockClosed16());
104  lockTabBarMenuAction->setCheckable(true);
105  lockTabBarMenuAction->setChecked(m_lockTabBar);
106  menu->addAction(lockTabBarMenuAction);
107  connect(lockTabBarMenuAction, &QAction::toggled, this, &CInfoArea::toggleTabBarLocked);
108 
109  menu->addSeparator();
110  QMenu *subMenuToggleFloat = new QMenu("Toggle Float/Dock", menu);
111  QMenu *subMenuDisplay = new QMenu("Display", menu);
112  QMenu *subMenuRestore = new QMenu("Restore from settings", menu);
113  QMenu *subMenuResetPositions = new QMenu("Reset position", menu);
114  subMenuRestore->setIcon(CIcons::load16());
115  subMenuResetPositions->setIcon(CIcons::refresh16());
116  subMenuRestore->addActions(this->getInfoAreaRestoreActions(subMenuRestore));
117  subMenuDisplay->addActions(this->getInfoAreaSelectActions(false, subMenuDisplay));
118  subMenuResetPositions->addActions(this->getInfoAreaResetPositionActions(subMenuResetPositions));
119 
120  QSignalMapper *signalMapperToggleFloating = new QSignalMapper(menu);
121  bool c = false; // check connections
122 
123  for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++)
124  {
125  const CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(i);
126  const QString t = dw->windowTitleBackup();
127  const QPixmap pm = this->indexToPixmap(i);
128  QAction *toggleFloatingMenuAction = new QAction(menu);
129  toggleFloatingMenuAction->setObjectName(QString(t).append("ToggleFloatingAction"));
130  toggleFloatingMenuAction->setIconText(t);
131  toggleFloatingMenuAction->setIcon(pm);
132  toggleFloatingMenuAction->setData(QVariant(i));
133  toggleFloatingMenuAction->setCheckable(true);
134  toggleFloatingMenuAction->setChecked(!dw->isFloating());
135  subMenuToggleFloat->addAction(toggleFloatingMenuAction);
136  c = connect(toggleFloatingMenuAction, &QAction::toggled, signalMapperToggleFloating,
137  qOverload<>(&QSignalMapper::map));
138  SWIFT_VERIFY_X(c, Q_FUNC_INFO,
139  "Cannot map floating action"); // do not make that shutdown reason in a release build
140  signalMapperToggleFloating->setMapping(toggleFloatingMenuAction, i);
141  }
142 
143  c = connect(signalMapperToggleFloating, &QSignalMapper::mappedInt, this, &CInfoArea::toggleFloatingByIndex);
144  SWIFT_VERIFY_X(c, Q_FUNC_INFO,
145  "Cannot connect mapper"); // do not make that shutdown reason in a release build
146 
147  menu->addMenu(subMenuDisplay);
148  if (c) { menu->addMenu(subMenuToggleFloat); }
149  menu->addMenu(subMenuResetPositions);
150  menu->addMenu(subMenuRestore);
151 
152  // where and how to display tab bar
153  menu->addSeparator();
154  QAction *showMenuText = new QAction(menu);
155  showMenuText->setObjectName("ShowDockedWidgetTextAction");
156  showMenuText->setIconText("Show tab text");
157  showMenuText->setIcon(CIcons::headingOne16());
158  showMenuText->setCheckable(true);
159  showMenuText->setChecked(m_showTabTexts);
160  menu->addAction(showMenuText);
161  connect(showMenuText, &QAction::toggled, this, &CInfoArea::showTabTexts);
162 
163  // auto adjust floating widgets
164  QAction *showTabbar = new QAction(menu);
165  showTabbar->setObjectName("ShowTabBar");
166  showTabbar->setIconText("Show tab bar");
167  showTabbar->setIcon(CIcons::dockBottom16());
168  showTabbar->setCheckable(true);
169  showTabbar->setChecked(m_showTabBar);
170  menu->addAction(showTabbar);
171  connect(showTabbar, &QAction::toggled, this, &CInfoArea::showTabBar);
172 
173  // tab bar position
174  menu->addAction(CIcons::dockBottom16(), "Toggle tabbar position", this, &CInfoArea::toggleTabBarPosition);
175  Q_UNUSED(c)
176  }
177  }
178 
180  {
181  if (!infoArea) { return false; }
182  if (infoArea->isFloating()) { return false; }
183  if (!infoArea->isWidgetVisible()) { return false; }
184  if (!m_tabBar || m_tabBar->count() < 1) { return false; }
185  return true;
186  }
187 
189  {
190  // we assume that there can only be 1, non floating info area,
191  // which is the only visible one
192  // selecting is tab text independent (can be hidden)
193  if (!m_tabBar || m_tabBar->count() < 1) { return nullptr; }
194  for (const CDockWidgetInfoArea *ia : m_dockWidgetInfoAreas)
195  {
196  if (ia->isFloating()) { continue; }
197  if (ia->isWidgetVisible()) { return ia; }
198  }
199  return nullptr;
200  }
201 
203  {
205  if (!sel) { return -1; }
206  const QString t(sel->windowTitleBackup());
207  int ia = getAreaIndexByWindowTitle(t);
208  return ia;
209  }
210 
211  QList<const CDockWidgetInfoArea *> CInfoArea::getDockWidgetInfoAreas() const
212  {
213  return makeRange(m_dockWidgetInfoAreas);
214  }
215 
216  QList<QAction *> CInfoArea::getInfoAreaSelectActions(bool withShortcut, QWidget *parent) const
217  {
218  Q_ASSERT(parent);
219  int i = 0;
220  QList<QAction *> actions;
221  for (const CDockWidgetInfoArea *dockWidgetInfoArea : m_dockWidgetInfoAreas)
222  {
223  const QPixmap pm = this->indexToPixmap(i);
224  const QString wt(dockWidgetInfoArea->windowTitleBackup());
225  static const QString keys("123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
226  QAction *action = new QAction(QIcon(pm), wt, parent);
227  action->setData(i);
228  action->setObjectName(this->objectName().append(":getInfoAreaSelectActions:").append(wt));
229  if (withShortcut && i < keys.length())
230  {
231  // T600 The strings "Ctrl", "Shift", "Alt" and "Meta" are recognized
232  // https://doc.qt.io/archives/qt-4.8/qkeysequence.html Standard shortcuts already used
233  // "Ctrl+Shift+%1" als works
234  action->setShortcut(QKeySequence(QStringLiteral("Ctrl+Shift+%1").arg(keys.at(i))));
235  }
236 
237  connect(action, &QAction::triggered, this, &CInfoArea::selectAreaByAction);
238  actions.append(action);
239  i++;
240  }
241  return actions;
242  }
243 
244  QList<QAction *> CInfoArea::getInfoAreaResetPositionActions(QWidget *parent) const
245  {
246  Q_ASSERT(parent);
247  int i = 0;
248  QList<QAction *> actions;
249  for (const CDockWidgetInfoArea *dockWidgetInfoArea : m_dockWidgetInfoAreas)
250  {
251  const QPixmap pm = this->indexToPixmap(i);
252  const QString wt(dockWidgetInfoArea->windowTitleBackup());
253  QAction *action = new QAction(QIcon(pm), wt, parent);
254  action->setData(i);
255  action->setObjectName(this->objectName().append(":getInfoAreaResetPositionActions:").append(wt));
256  connect(action, &QAction::triggered, this, &CInfoArea::resetPositionByAction);
257  actions.append(action);
258  i++;
259  }
260  return actions;
261  }
262 
263  QList<QAction *> CInfoArea::getInfoAreaToggleFloatingActions(QWidget *parent) const
264  {
265  Q_ASSERT(parent);
266  int i = 0;
267  QList<QAction *> actions;
268  for (const CDockWidgetInfoArea *dockWidgetInfoArea : m_dockWidgetInfoAreas)
269  {
270  const QPixmap pm = this->indexToPixmap(i);
271  const QString wt(dockWidgetInfoArea->windowTitleBackup());
272  QAction *action = new QAction(QIcon(pm), wt, parent);
273  action->setData(i);
274  action->setObjectName(this->objectName().append(":getInfoAreaToggleFloatingActions:").append(wt));
275  connect(action, &QAction::triggered, this, &CInfoArea::toggleAreaFloatingByAction, Qt::QueuedConnection);
276  actions.append(action);
277  i++;
278  }
279  return actions;
280  }
281 
282  QList<QAction *> CInfoArea::getInfoAreaRestoreActions(QWidget *parent) const
283  {
284  Q_ASSERT(parent);
285  int i = 0;
286  QList<QAction *> actions;
287  for (const CDockWidgetInfoArea *dockWidgetInfoArea : m_dockWidgetInfoAreas)
288  {
289  const QPixmap pm = this->indexToPixmap(i);
290  const QString wt(dockWidgetInfoArea->windowTitleBackup());
291  QAction *action = new QAction(QIcon(pm), wt, parent);
292  action->setData(i);
293  action->setObjectName(this->objectName().append(":getInfoAreaRestoreActions:").append(wt));
294  connect(action, &QAction::triggered, this, &CInfoArea::restoreDockWidgetInfoArea);
295  actions.append(action);
296  i++;
297  }
298  return actions;
299  }
300 
301  QList<int> CInfoArea::getAreaIndexesDockedOrFloating(bool floating) const
302  {
303  QList<int> indexes;
304  for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++)
305  {
306  if (m_dockWidgetInfoAreas.at(i)->isFloating() == floating) { indexes.append(i); }
307  }
308  return indexes;
309  }
310 
311  void CInfoArea::paintEvent(QPaintEvent *event)
312  {
313  CStyleSheetUtility::useStyleSheetInDerivedWidget(this, QStyle::PE_FrameWindow);
314  Q_UNUSED(event)
315  }
316 
317  void CInfoArea::keyPressEvent(QKeyEvent *event)
318  {
319  if (event->key() == Qt::Key_Right)
320  {
321  this->selectRightTab();
322  event->accept();
323  }
324  else if (event->key() == Qt::Key_Left)
325  {
326  this->selectLeftTab();
327  event->accept();
328  }
329  else { QWidget::keyPressEvent(event); }
330  }
331 
332  void CInfoArea::dockAllWidgets() { this->tabifyAllWidgets(); }
333 
335  {
336  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas)) { dw->adjustSize(); }
337  }
338 
340  {
341  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
342  {
343  if (dw->isFloating()) { continue; }
344  dw->toggleFloating();
345  }
346  }
347 
349  {
350  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
351  {
352  if (!dw || !dw->isFloating()) { continue; }
353  dw->resetSettings();
354  }
355  }
356 
358  {
359  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
360  {
361  if (!dw || !dw->isFloating()) { continue; }
362  dw->resetSettings();
363  }
364  }
365 
367  {
368  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
369  {
370  const bool f = dw->isFloating();
371  CGuiUtility::stayOnTop(f, dw);
372  if (f) { dw->show(); }
373  }
374  }
375 
376  void CInfoArea::toggleFloatingWholeInfoArea() { this->setWholeInfoAreaFloating(!m_infoAreaFloating); }
377 
379  {
380  if (!this->isValidAreaIndex(areaIndex)) { return; }
381  CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex);
382  SWIFT_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area");
383  if (!dw) { return; }
384  dw->toggleFloating();
385  }
386 
387  void CInfoArea::toggleVisibility(int areaIndex)
388  {
389  if (!this->isValidAreaIndex(areaIndex)) { return; }
390  CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex);
391  SWIFT_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area");
392  if (!dw) { return; }
393  dw->toggleVisibility();
394  }
395 
396  void CInfoArea::selectArea(int areaIndex)
397  {
398  CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex);
399  SWIFT_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area");
400  if (!dw) { return; }
401  Q_ASSERT(m_tabBar);
402  if (m_tabBar->count() < 1) { return; }
403 
404  if (dw->isFloating()) { dw->show(); }
405  else { this->selectArea(dw); }
406  }
407 
408  void CInfoArea::resetPosition(int areaIndex)
409  {
410  CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex);
411  SWIFT_VERIFY_X(dw, Q_FUNC_INFO, "Missing info area");
412  if (!dw) { return; }
413  dw->resetPosition();
414  }
415 
417  {
418  const QObject *sender = QObject::sender();
419  const QAction *action = qobject_cast<const QAction *>(sender);
420  SWIFT_VERIFY(action);
421  if (!action) { return; }
422  const int index = action->data().toInt();
423  this->selectArea(index);
424  }
425 
427  {
428  const QObject *sender = QObject::sender();
429  const QAction *action = qobject_cast<const QAction *>(sender);
430  SWIFT_VERIFY(action);
431  if (!action) { return; }
432  const int index = action->data().toInt();
433  this->resetPosition(index);
434  }
435 
437  {
438  const QObject *sender = QObject::sender();
439  const QAction *action = qobject_cast<const QAction *>(sender);
440  SWIFT_VERIFY(action);
441  if (!action) { return; }
442  const int index = action->data().toInt();
443  this->toggleFloatingByIndex(index);
444  }
445 
447  {
448  const QObject *sender = QObject::sender();
449  const QAction *action = qobject_cast<const QAction *>(sender);
450  SWIFT_VERIFY(action);
451  if (!action) { return; }
452  const int index = action->data().toInt();
454  }
455 
457  {
458  if (!this->isValidAreaIndex(areaIndex)) { return; }
459  CDockWidgetInfoArea *dw = m_dockWidgetInfoAreas.at(areaIndex);
460  Q_ASSERT(dw);
461  if (!dw) { return; }
462  dw->restoreFromSettings();
463  }
464 
466  {
467  if (!m_tabBar) return;
468  if (m_tabBar->count() < 2) return;
469  if (m_tabBar->currentIndex() > 0) { m_tabBar->setCurrentIndex(m_tabBar->currentIndex() - 1); }
470  else { m_tabBar->setCurrentIndex(m_tabBar->count() - 1); }
471  }
472 
474  {
475  if (!m_tabBar) return;
476  if (m_tabBar->count() < 2) return;
477  if (m_tabBar->currentIndex() < m_tabBar->count() - 2)
478  {
479  m_tabBar->setCurrentIndex(m_tabBar->currentIndex() + 1);
480  }
481  else { m_tabBar->setCurrentIndex(0); }
482  }
483 
485  {
486  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
487  {
488  dw->displayStatusMessage(statusMessage);
489  }
490  for (CInfoArea *ia : std::as_const(m_childInfoAreas)) { ia->displayStatusMessage(statusMessage); }
491  }
492 
494  {
495  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
496  {
497  dw->displayStatusMessages(statusMessages);
498  }
499  for (CInfoArea *ia : std::as_const(m_childInfoAreas)) { ia->displayStatusMessages(statusMessages); }
500  }
501 
502  void CInfoArea::setDockArea(Qt::DockWidgetArea area)
503  {
504  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
505  {
506  Qt::DockWidgetAreas newAreas = static_cast<Qt::DockWidgetAreas>(area);
507  Qt::DockWidgetAreas oldAreas = dw->allowedAreas();
508  if (oldAreas == newAreas) { continue; }
509  dw->setAllowedAreas(newAreas);
510  this->addDockWidget(area, dw);
511  }
512  }
513 
514  void CInfoArea::setWholeInfoAreaFloating(bool floating)
515  {
516  // float whole info area
517  m_infoAreaFloating = floating;
518  if (m_infoAreaFloating)
519  {
521  this->setWindowFlags(Qt::Dialog);
522  this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
523  this->move(p.rx() + 20, p.ry() + 20);
524  this->show(); // not working without show
525  }
526  else
527  {
528  // make this compliant as QWidget
529  // https://qt-project.org/forums/viewthread/17519
530  // http://www.qtcentre.org/threads/12569-QMainWindow-as-a-child-of-QMainWindow
531 
532  // this->setParent(m_originalParent, this->windowFlags() & ~Qt::Window);
533  this->setWindowFlags(this->windowFlags() & ~Qt::Window);
534 
535  if (this->parentWidget())
536  {
537  this->setVisible(true); // after redocking this is required
538  }
539  }
540 
541  emit this->changedWholeInfoAreaFloating(floating);
542  }
543 
544  void CInfoArea::tabifyAllWidgets()
545  {
546  if (!sGui || sGui->isShuttingDown()) { return; }
547  this->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::East);
548  const bool init = m_tabBar ? false : true;
549 
550  for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++)
551  {
552  CDockWidgetInfoArea *first = i > 0 ? m_dockWidgetInfoAreas.at(i - 1) : nullptr;
553  CDockWidgetInfoArea *after = m_dockWidgetInfoAreas.at(i);
554  Q_ASSERT(after);
555 
556  // trick, init widget as floating
557  // this completely initializes the tab bar and all docked widgets
558  if (init)
559  {
560  // float
561  const QPoint offset(i * 10, i * 10);
562  // after->setVisible(false);
563  // after->setFloating(true);
564  after->setOffsetWhenFloating(offset, after->isFrameless());
565  const QSize floatingSize = this->getPreferredSizeWhenFloating(i);
566  after->setPreferredSizeWhenFloating(floatingSize);
567  after->initialFloating();
568 
569  // dock again
570  // after->setFloating(false);
571  // after->setVisible(true);
572 
573  // reset floating flag, we want new resizing and position for first real floating
574  after->resetWasAlreadyFloating();
575  }
576  else { after->setFloating(false); }
577 
578  // we can not tabify first
579  if (!first) { continue; }
580 
581  this->tabifyDockWidget(first, after);
582  }
583 
584  // as now tabified, set tab
585  if (init)
586  {
587  m_tabBar = this->findChild<QTabBar *>();
588 
589  // if we have > 1 docked widgets, we have a tab bar
590  if (m_tabBar && sGui)
591  {
593  m_tabBar->setStyleSheet(qss);
594  m_tabBar->setObjectName("comp_MainInfoAreaDockWidgetTab");
595  m_tabBar->setMovable(false);
596  m_tabBar->setElideMode(Qt::ElideNone);
597  m_tabBar->setUsesScrollButtons(true);
598 
599  // East / West does not work (shown, but area itself empty)
600  // South does not have any effect
601  m_tabBar->setShape(QTabBar::TriangularSouth);
602 
603  // signals, use Qt::QueuedConnection to avoid issues during shutdown
604  connect(m_tabBar, &QTabBar::tabBarDoubleClicked, this, &CInfoArea::onTabBarDoubleClicked,
605  Qt::QueuedConnection);
606  connect(m_tabBar, &QTabBar::currentChanged, this, &CInfoArea::onTabBarIndexChanged,
607  Qt::QueuedConnection);
608  }
609  else
610  {
611  // <= 1 dock widget
612  m_tabBar = new QTabBar(this);
613  m_tabBar->hide();
614  }
615  }
616 
617  // set current index, and always set pixmaps
618  if (this->countDockedWidgetInfoAreas() > 0) { m_tabBar->setCurrentIndex(0); }
619  if (m_tabBar->count() > 0) { this->setTabPixmaps(); }
620  }
621 
622  void CInfoArea::unTabifyAllWidgets()
623  {
624  if (m_dockWidgetInfoAreas.size() < 2) return;
625  CDockWidgetInfoArea *first = m_dockWidgetInfoAreas.constFirst();
626  for (int i = 1; i < m_dockWidgetInfoAreas.size(); i++)
627  {
628  CDockWidgetInfoArea *after = m_dockWidgetInfoAreas.at(i);
629  Q_ASSERT(after);
630  this->splitDockWidget(first, after, Qt::Horizontal);
631  }
632  }
633 
634  bool CInfoArea::isValidAreaIndex(int areaIndex) const
635  {
636  if (!m_tabBar) { return false; }
637  return areaIndex >= 0 && areaIndex < m_dockWidgetInfoAreas.size();
638  }
639 
640  void CInfoArea::connectTopLevelChanged()
641  {
642  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas))
643  {
644  connect(dw, &CDockWidgetInfoArea::widgetTopLevelChanged, this, &CInfoArea::onWidgetTopLevelChanged,
645  Qt::QueuedConnection);
646  }
647  }
648 
649  QList<CDockWidgetInfoArea *> CInfoArea::findOwnDockWidgetInfoAreas() const
650  {
651  // own dock widget areas without nested ones
652  return this->findChildren<CDockWidgetInfoArea *>(QString(), Qt::FindDirectChildrenOnly);
653  }
654 
655  QList<CInfoArea *> CInfoArea::findOwnChildInfoAreas() const
656  {
657  return this->findChildren<CInfoArea *>(QString(), Qt::FindDirectChildrenOnly);
658  }
659 
660  void CInfoArea::emitInfoAreaStatus()
661  {
662  if (!sGui || sGui->isShuttingDown()) { return; } // avoid access to deleted components
663  const int sia = this->getSelectedDockInfoAreaIndex();
664  const QList<int> floating = this->getAreaIndexesDockedOrFloating(true);
665  const QList<int> docked = this->getAreaIndexesDockedOrFloating(false);
666  emit this->changedInfoAreaStatus(sia, docked, floating);
667  }
668 
669  void CInfoArea::onTabBarIndexChanged(int tabBarIndex)
670  {
671  emit this->changedInfoAreaTabBarIndex(tabBarIndex);
672  this->emitInfoAreaStatus();
673  }
674 
675  int CInfoArea::countDockedWidgetInfoAreas() const
676  {
677  if (!m_tabBar) { return 0; }
678  return m_tabBar->count();
679  }
680 
681  CDockWidgetInfoArea *CInfoArea::getDockWidgetInfoAreaByTabBarIndex(int tabBarIndex) const
682  {
683  if (tabBarIndex >= m_dockWidgetInfoAreas.count() || tabBarIndex < 0) { return nullptr; }
684  if (!m_tabBar) { return nullptr; }
685  const QString t(m_tabBar->tabText(tabBarIndex));
686 
687  // we have a title and search by that (best option, as order does not matter)
688  if (!t.isEmpty()) { return this->getDockWidgetInfoAreaByWindowTitle(t); }
689 
690  // no title, we assume the n-th not floating tab is correct
691  // this will work if the order in m_dockWidgetInfoAreas matches
692  int c = 0;
693  for (CDockWidgetInfoArea *dw : m_dockWidgetInfoAreas)
694  {
695  if (dw->isFloating()) { continue; }
696  if (c == tabBarIndex) { return dw; }
697  c++;
698  }
699  Q_ASSERT_X(false, Q_FUNC_INFO, "no dock widgte found");
700  return nullptr;
701  }
702 
703  CDockWidgetInfoArea *CInfoArea::getDockWidgetInfoAreaByWindowTitle(const QString &title) const
704  {
705  Q_ASSERT_X(!title.isEmpty(), Q_FUNC_INFO, "No title");
706  for (CDockWidgetInfoArea *dw : m_dockWidgetInfoAreas)
707  {
708  if (CGuiUtility::lenientTitleComparison(dw->windowTitleOrBackup(), title)) { return dw; }
709  }
710  return nullptr;
711  }
712 
713  int CInfoArea::getAreaIndexByWindowTitle(const QString &title) const
714  {
715  Q_ASSERT_X(!title.isEmpty(), Q_FUNC_INFO, "No title");
716 
717  for (int i = 0; i < m_dockWidgetInfoAreas.size(); i++)
718  {
719  if (CGuiUtility::lenientTitleComparison(m_dockWidgetInfoAreas.at(i)->windowTitleOrBackup(), title))
720  {
721  return i;
722  }
723  }
724  Q_ASSERT_X(false, Q_FUNC_INFO, "No area for title");
725  return -1;
726  }
727 
728  int CInfoArea::getTabBarIndexByTitle(const QString &title) const
729  {
730  SWIFT_VERIFY_X(!title.isEmpty(), Q_FUNC_INFO, "No title");
731  if (title.isEmpty()) { return -1; }
732 
733  if (m_tabBar->count() < 1) { return -1; }
734  for (int i = 0; i < m_tabBar->count(); i++)
735  {
736  QString tt = m_tabBar->tabText(i);
737  if (tt.isEmpty()) { tt = m_tabBar->tabToolTip(i); }
738  if (tt.isEmpty()) { continue; } // cannot find by empty text
739  if (CGuiUtility::lenientTitleComparison(tt, title)) { return i; }
740  }
741  return -1;
742  }
743 
744  int CInfoArea::dockWidgetInfoAreaToTabBarIndex(const CDockWidgetInfoArea *dockWidgetInfoArea) const
745  {
746  if (!dockWidgetInfoArea) { return -1; }
747  if (dockWidgetInfoArea->isFloating()) { return -1; }
748  int index = this->getTabBarIndexByTitle(dockWidgetInfoArea->windowTitleOrBackup());
749  if (index < 0)
750  {
751  // fallback if the tab has no name and cannot be found
752  int i = 0;
753  for (const CDockWidgetInfoArea *a : m_dockWidgetInfoAreas)
754  {
755  if (a == dockWidgetInfoArea)
756  {
757  index = i;
758  break;
759  }
760  ++i;
761  }
762  }
763 
764  // sanity check
765  if (index >= m_tabBar->count()) { index = -1; }
766 
767  // bye
768  return index;
769  }
770 
771  void CInfoArea::selectArea(const CDockWidgetInfoArea *dockWidgetInfoArea)
772  {
773  if (!m_tabBar) { return; }
774  int tabIndex = this->dockWidgetInfoAreaToTabBarIndex(dockWidgetInfoArea);
775  if (tabIndex >= 0 && tabIndex < m_tabBar->count()) { m_tabBar->setCurrentIndex(tabIndex); }
776  }
777 
778  void CInfoArea::setFeaturesForDockableWidgets(QDockWidget::DockWidgetFeatures features)
779  {
780  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas)) { dw->setFeatures(features); }
781  }
782 
783  void CInfoArea::setTabPixmaps()
784  {
785  if (!m_tabBar) { return; }
786  for (int i = 0; i < m_tabBar->count(); i++)
787  {
788  const QString t(m_tabBar->tabText(i));
789  const int areaIndex = t.isEmpty() ? i : this->getAreaIndexByWindowTitle(t);
790  m_tabBar->setTabIcon(i, indexToPixmap(areaIndex));
791  m_tabBar->setTabToolTip(i, t);
792  }
793  }
794 
795  void CInfoArea::onTabBarDoubleClicked(int tabBarIndex)
796  {
797  if (m_lockTabBar)
798  {
799  CLogMessage(this).info(u"Locked, double click will not cause floating");
800  return;
801  }
802  CDockWidgetInfoArea *dw = this->getDockWidgetInfoAreaByTabBarIndex(tabBarIndex);
803  if (!dw) { return; }
804  dw->toggleFloating();
805 
806  QPointer<CInfoArea> myself(this);
807  QTimer::singleShot(1000, this, [=] {
808  if (!myself) { return; }
809  myself->emitInfoAreaStatus();
810  });
811  }
812 
813  void CInfoArea::onWidgetTopLevelChanged(CDockWidget *dockWidget, bool topLevel)
814  {
815  Q_ASSERT(dockWidget);
816  Q_UNUSED(topLevel)
817  if (!dockWidget) { return; }
818 
819  // fix pixmaps
820  this->setTabPixmaps();
821 
822  // select index
823  if (!topLevel)
824  {
825  const CDockWidgetInfoArea *dwia = dynamic_cast<CDockWidgetInfoArea *>(dockWidget);
826  this->selectArea(dwia);
827  }
828 
829  // KB commented out with T592
830  // when toplevel is changed, I need a round in the event loop until
831  // current tab bar widget is visible
832  // QTimer::singleShot(250, this, &CInfoArea::emitInfoAreaStatus);
833  }
834 
835  void CInfoArea::onStyleSheetChanged()
836  {
837  if (m_tabBar)
838  {
839  if (!sGui || sGui->isShuttingDown()) { return; }
841  m_tabBar->setStyleSheet(qss);
842  }
843  }
844 
845  void CInfoArea::showContextMenu(const QPoint &pos)
846  {
847  QPoint globalPos = this->mapToGlobal(pos);
848  QScopedPointer<QMenu> contextMenu(new QMenu(this));
849  this->addToContextMenu(contextMenu.data());
850 
851  QAction *selectedItem = contextMenu.data()->exec(globalPos);
852  Q_UNUSED(selectedItem)
853  }
854 
855  void CInfoArea::showTabTexts(bool show)
856  {
857  if (show == m_showTabTexts) { return; }
858  m_showTabTexts = show;
859  for (CDockWidgetInfoArea *dw : std::as_const(m_dockWidgetInfoAreas)) { dw->showTitleWhenDocked(show); }
860  }
861 
862  void CInfoArea::showTabBar(bool show)
863  {
864  if (show == m_showTabBar) return;
865  m_showTabBar = show;
866  if (!m_tabBar) return;
867  m_tabBar->setVisible(show); // not working, but setting right value will not harm anything
868  m_tabBar->setMaximumHeight(show ? 10000 : 0); // does the trick
870  }
871 
872  void CInfoArea::toggleTabBarLocked(bool locked) { m_lockTabBar = locked; }
873 
874  void CInfoArea::setTabBarPosition(QTabWidget::TabPosition position)
875  {
876  Q_ASSERT_X(position == QTabWidget::North || position == QTabWidget::South, Q_FUNC_INFO,
877  "Wrong tabbar position");
878  this->setTabPosition(Qt::TopDockWidgetArea, position);
879  }
880 
881  void CInfoArea::toggleTabBarPosition()
882  {
883  QTabWidget::TabPosition p =
884  (this->tabPosition(Qt::TopDockWidgetArea) == QTabWidget::North) ? QTabWidget::South : QTabWidget::North;
885  this->setTabBarPosition(p);
886  }
887 
888  void CInfoArea::closeEvent(QCloseEvent *event)
889  {
890  if (this->isFloating())
891  {
893  event->setAccepted(false); // refuse -> do not close
894  }
895  else { QMainWindow::closeEvent(event); }
896  }
897 } // namespace swift::gui
bool isShuttingDown() const
Is application shutting down?
void toggleVisibility()
Toggle visibility.
Definition: dockwidget.cpp:259
void widgetTopLevelChanged(CDockWidget *, bool topLevel)
Top level has changed for given widget.
bool restoreFromSettings()
Restore from settings.
Definition: dockwidget.cpp:302
bool isWidgetVisible() const
Is widget visible? Not to be confused with.
Definition: dockwidget.cpp:154
void toggleFloating()
Toggle floating.
Definition: dockwidget.cpp:230
void resetPosition()
Reset window position.
Definition: dockwidget.cpp:678
const QString & windowTitleBackup() const
Window title backup.
Definition: dockwidget.h:66
Specialized class for dock widgets serving as info area.
Main window which can be frameless.
const CStyleSheetUtility & getStyleSheetUtility() const
Style sheet handling.
void styleSheetsChanged()
Style sheet changed.
static bool stayOnTop(bool onTop, QWidget *widget)
Window flags / stay on top.
Definition: guiutility.cpp:594
static QPoint mainWidgetGlobalPosition()
Position of main widget.
Definition: guiutility.cpp:511
static bool lenientTitleComparison(const QString &title, const QString &comparison)
Lenient / relaxed.
Definition: guiutility.cpp:274
Info area, hosting dockable widgets.
Definition: infoarea.h:41
virtual void toggleFloatingWholeInfoArea()
Toggle dock / floating of the whole info area.
Definition: infoarea.cpp:376
void selectLeftTab()
Select next left tab.
Definition: infoarea.cpp:465
void changedInfoAreaStatus(int currentTabIndex, QList< int > dockedAreas, QList< int > floatingAreas)
Status of info area changed.
void addToContextMenu(QMenu *menu) const
Add items to context menu.
Definition: infoarea.cpp:86
void toggleFloatingByIndex(int areaIndex)
Toggle floating of index.
Definition: infoarea.cpp:378
virtual void closeEvent(QCloseEvent *event)
Definition: infoarea.cpp:888
void toggleVisibility(int areaIndex)
Toggle visibilty.
Definition: infoarea.cpp:387
void selectArea(int areaIndex)
Select area.
Definition: infoarea.cpp:396
void toggleAreaFloatingByAction()
Toggle area floating (sender is QAction)
Definition: infoarea.cpp:436
QList< QAction * > getInfoAreaRestoreActions(QWidget *parent) const
Create a list of actions to restore the info areas. This could be used in a menu or somewhere else.
Definition: infoarea.cpp:282
void floatAllWidgets()
All widgets floating.
Definition: infoarea.cpp:339
QList< QAction * > getInfoAreaToggleFloatingActions(QWidget *parent) const
Create a list of actions to select the info areas and toogle its floating state. This could be used i...
Definition: infoarea.cpp:263
void adjustSizeForAllDockWidgets()
Adjust size for all dock widgets.
Definition: infoarea.cpp:334
void setTabBarPosition(QTabWidget::TabPosition position)
Tab position for docked widgets tab.
Definition: infoarea.cpp:874
void restoreDockWidgetInfoArea()
Restore dock widget`s state (from settings)
Definition: infoarea.cpp:446
virtual void paintEvent(QPaintEvent *event)
Definition: infoarea.cpp:311
void selectRightTab()
Select next right tab.
Definition: infoarea.cpp:473
void selectAreaByAction()
Select area (sender is QAction)
Definition: infoarea.cpp:416
void resetAllWidgetSettings()
Reset all widget settings.
Definition: infoarea.cpp:357
void initInfoArea()
Init area after(!) GUI is initialized.
Definition: infoarea.cpp:47
void toggleTabBarLocked(bool locked)
Toogle lock tabbar.
Definition: infoarea.cpp:872
virtual ~CInfoArea()
Destructor.
Definition: infoarea.cpp:45
void changedInfoAreaTabBarIndex(int index)
Tab bar changed.
QList< const CDockWidgetInfoArea * > getDockWidgetInfoAreas() const
Own dockable widgets.
Definition: infoarea.cpp:211
virtual QSize getPreferredSizeWhenFloating(int areaIndex) const =0
Preferred size when floating (size hint)
void changedWholeInfoAreaFloating(bool floating)
Whole info area floating.
void dockAllWidgets()
Dock all widgets.
Definition: infoarea.cpp:332
QList< int > getAreaIndexesDockedOrFloating(bool floating) const
Docked area indexes.
Definition: infoarea.cpp:301
void restoreDockWidgetInfoAreaByIndex(int areaIndex)
Restore dock widget`s state (from settings)
Definition: infoarea.cpp:456
QList< QAction * > getInfoAreaResetPositionActions(QWidget *parent) const
Create a list of actions to reset the position the info areas. This could be used in a menu or somewh...
Definition: infoarea.cpp:244
void displayStatusMessages(const swift::misc::CStatusMessageList &statusMessages)
Display status messages in all info areas (according their state)
Definition: infoarea.cpp:493
bool isFloating() const
Is the area floating?
Definition: infoarea.h:52
bool isSelectedDockWidgetInfoArea(const CDockWidgetInfoArea *infoArea) const
Is given widget selected. Means it is not floating, and the one selected.
Definition: infoarea.cpp:179
void resetAllFloatingWidgetSettings()
Reset all floating widget settings.
Definition: infoarea.cpp:348
virtual const QPixmap & indexToPixmap(int areaIndex) const =0
Info area (index) to icon.
QList< QAction * > getInfoAreaSelectActions(bool withShortcut, QWidget *parent) const
Create a list of actions to select the info areas. This could be used in a menu or somewhere else.
Definition: infoarea.cpp:216
void displayStatusMessage(const swift::misc::CStatusMessage &statusMessage)
Display status message in all info areas (according their state)
Definition: infoarea.cpp:484
const CDockWidgetInfoArea * getSelectedDockInfoArea() const
Get the selected info area (non floating, selected in tabbar)
Definition: infoarea.cpp:188
void resetPosition(int areaIndex)
Reset position.
Definition: infoarea.cpp:408
int getSelectedDockInfoAreaIndex() const
Get the selected info area (non floating, selected in tabbar)
Definition: infoarea.cpp:202
virtual void keyPressEvent(QKeyEvent *event)
Definition: infoarea.cpp:317
void resetPositionByAction()
Reset window position of area (sender is QAction)
Definition: infoarea.cpp:426
void allFloatingOnTop()
All floating info areas on top.
Definition: infoarea.cpp:366
static const QString & fileNameDockWidgetTab()
File name dockwidgettab.qss.
static bool useStyleSheetInDerivedWidget(QWidget *derivedWidget, QStyle::PrimitiveElement element=QStyle::PE_Widget)
Use style sheets in derived widgets.
QString style(const QString &fileName) const
Style for given file name.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Streamable status message, e.g.
Status messages, e.g. from Core -> GUI.
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
GUI related classes.
Free functions in swift::misc.
auto makeRange(I begin, I2 end) -> CRange< I >
Returns a CRange constructed from begin and end iterators of deduced types.
Definition: range.h:316
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
Definition: threadutils.h:30
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26
#define SWIFT_VERIFY(COND)
A weaker kind of assert.
Definition: verify.h:29