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