KDDockWidgets API Documentation  1.6
TitleBar.cpp
Go to the documentation of this file.
1 /*
2  This file is part of KDDockWidgets.
3 
4  SPDX-FileCopyrightText: 2019-2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5  Author: SĂ©rgio Martins <sergio.martins@kdab.com>
6 
7  SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
8 
9  Contact KDAB at <info@kdab.com> for commercial licensing options.
10 */
11 
12 #include "TitleBar_p.h"
13 #include "Frame_p.h"
14 #include "FloatingWindow_p.h"
15 #include "Logging_p.h"
16 #include "WindowBeingDragged_p.h"
17 #include "Utils_p.h"
18 #include "FrameworkWidgetFactory.h"
19 #include "Config.h"
20 #include "MainWindowBase.h"
21 #include "MDILayoutWidget_p.h"
22 #include "TabWidget_p.h"
23 #include "DockWidgetBase_p.h"
24 
25 #include <QTimer>
26 #include <QWindowStateChangeEvent>
27 
28 using namespace KDDockWidgets;
29 
30 TitleBar::TitleBar(Frame *parent)
31  : QWidgetAdapter(parent)
32  , Draggable(this)
33  , m_frame(parent)
34  , m_floatingWindow(nullptr)
35  , m_genericWidget(nullptr)
36  , m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
37 {
38  connect(m_frame, &Frame::numDockWidgetsChanged, this, &TitleBar::updateCloseButton);
39  connect(m_frame, &Frame::isFocusedChanged, this, &TitleBar::isFocusedChanged);
40  connect(m_frame, &Frame::isInMainWindowChanged, this, &TitleBar::updateAutoHideButton);
41 
42  init();
43 
44  if (Config::self().flags() & Config::Flag_TitleBarIsFocusable)
45  setFocusPolicy(Qt::StrongFocus);
46 }
47 
48 TitleBar::TitleBar(FloatingWindow *parent)
49  : QWidgetAdapter(parent)
50  , Draggable(this)
51  , m_frame(nullptr)
52  , m_floatingWindow(parent)
53  , m_genericWidget(nullptr)
54  , m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
55 {
56  connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateButtons);
57  connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
58  connect(m_floatingWindow, &FloatingWindow::activatedChanged, this, &TitleBar::isFocusedChanged);
59  init();
60 }
61 
62 TitleBar::TitleBar(WidgetType *parent)
63  : QWidgetAdapter(parent)
64  , Draggable(this, /*enabled=*/false) // We don't allow dragging generic windows at this time
65  , m_frame(nullptr)
66  , m_floatingWindow(nullptr)
67  , m_genericWidget(parent)
68  , m_supportsAutoHide(false)
69 {
70  init();
71 }
72 
73 void TitleBar::init()
74 {
76 
77  connect(this, &TitleBar::isFocusedChanged, this, [this] {
78  // repaint
79  update();
80  });
81 
82  updateButtons();
83  QTimer::singleShot(0, this, &TitleBar::updateAutoHideButton); // have to wait after the frame is
84  // constructed
85 }
86 
87 TitleBar::~TitleBar()
88 {
89 }
90 
91 bool TitleBar::onDoubleClicked()
92 {
93  if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes) && m_floatingWindow) {
94  // Not using isFloating(), as that can be a dock widget nested in a floating window. By convention it's floating, but it's not the title bar of the top-level window.
95  toggleMaximized();
96  return true;
97  } else if (supportsFloatingButton()) {
98  onFloatClicked();
99  return true;
100  }
101 
102  return false;
103 }
104 
105 MainWindowBase *TitleBar::mainWindow() const
106 {
107  if (m_floatingWindow || m_genericWidget)
108  return nullptr;
109 
110  if (m_frame)
111  return m_frame->mainWindow();
112 
113  qWarning() << Q_FUNC_INFO << "null frame and null floating window";
114  return nullptr;
115 }
116 
117 bool TitleBar::isMDI() const
118 {
119  QObject *p = const_cast<TitleBar *>(this);
120  while (p) {
121  if (qobject_cast<const QWindow *>(p)) {
122  // Ignore QObject hierarchies spanning though multiple windows
123  return false;
124  }
125 
126  if (qobject_cast<MDILayoutWidget *>(p))
127  return true;
128 
129  if (qobject_cast<DropArea *>(p)) {
130  // Note that the TitleBar can be inside a DropArea that's inside a MDIArea
131  // so we need this additional check
132  return false;
133  }
134 
135  p = p->parent();
136  }
137 
138  return false;
139 }
140 
141 void TitleBar::updateButtons()
142 {
143  updateCloseButton();
144  updateFloatButton();
145  updateMaximizeButton();
146  updateMinimizeButton();
147  updateAutoHideButton();
148 }
149 
150 void TitleBar::updateCloseButton()
151 {
152 
153  const bool anyNonClosable = frame() ? frame()->anyNonClosable()
154  : (floatingWindow() ? floatingWindow()->anyNonClosable()
155  : false);
156 
157  setCloseButtonEnabled(!anyNonClosable);
158 }
159 
160 void TitleBar::toggleMaximized()
161 {
162  if (!m_floatingWindow)
163  return;
164 
165  if (m_floatingWindow->isMaximizedOverride())
166  m_floatingWindow->showNormal();
167  else
168  m_floatingWindow->showMaximized();
169 }
170 
171 void TitleBar::focusInEvent(QFocusEvent *ev)
172 {
173  QWidgetAdapter::focusInEvent(ev);
174 
175  if (!m_frame || !(Config::self().flags() & Config::Flag_TitleBarIsFocusable))
176  return;
177 
178  // For some reason QWidget::setFocusProxy() isn't working, so forward manually
179  m_frame->FocusScope::focus(ev->reason());
180 }
181 
182 bool TitleBar::isOverlayed() const
183 {
184  return m_frame && m_frame->isOverlayed();
185 }
186 
187 void TitleBar::setCloseButtonEnabled(bool enabled)
188 {
189  if (enabled != m_closeButtonEnabled) {
190  m_closeButtonEnabled = enabled;
191  Q_EMIT closeButtonEnabledChanged(enabled);
192  }
193 }
194 
195 void TitleBar::setFloatButtonVisible(bool visible)
196 {
197  if (visible != m_floatButtonVisible) {
198  m_floatButtonVisible = visible;
199  Q_EMIT floatButtonVisibleChanged(visible);
200  }
201 }
202 
203 void TitleBar::setFloatButtonToolTip(const QString &tip)
204 {
205  if (tip != m_floatButtonToolTip) {
206  m_floatButtonToolTip = tip;
207  Q_EMIT floatButtonToolTipChanged(tip);
208  }
209 }
210 
211 void TitleBar::setTitle(const QString &title)
212 {
213  if (title != m_title) {
214  m_title = title;
215  update();
216  Q_EMIT titleChanged();
217  }
218 }
219 
220 void TitleBar::setIcon(const QIcon &icon)
221 {
222  m_icon = icon;
223  Q_EMIT iconChanged();
224 }
225 
226 std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
227 {
228  if (m_genericWidget)
229  return {}; // not applicable
230 
231  if (!isVisible() && window()->isVisible() && !(Config::self().flags() & Config::Flag_ShowButtonsOnTabBarIfTitleBarHidden)) {
232 
233  // When using Flag_ShowButtonsOnTabBarIfTitleBarHidden we forward the call from the tab bar's
234  // buttons to the title bar's buttons, just to reuse logic
235 
236  qWarning() << "TitleBar::makeWindow shouldn't be called on invisible title bar"
237  << this << window()->isVisible();
238  if (m_frame) {
239  qWarning() << "this=" << this << "; actual=" << m_frame->actualTitleBar();
240  } else if (m_floatingWindow) {
241  qWarning() << "Has floating window with titlebar=" << m_floatingWindow->titleBar()
242  << "; fw->isVisible=" << m_floatingWindow->isVisible();
243  }
244 
245  Q_ASSERT(false);
246  return {};
247  }
248 
249  if (m_floatingWindow) {
250  // We're already a floating window, no detach needed
251  return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(m_floatingWindow, this));
252  }
253 
254  if (FloatingWindow *fw = QWidgetAdapter::floatingWindow()) { // Already floating
255  if (m_frame->isTheOnlyFrame()) { // We don't detach. This one drags the entire window instead.
256  qCDebug(hovering) << "TitleBar::makeWindow no detach needed";
257  return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(fw, this));
258  }
259  }
260 
261  QRect r = m_frame->QWidgetAdapter::geometry();
262  r.moveTopLeft(m_frame->mapToGlobal(QPoint(0, 0)));
263 
264  auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
265  floatingWindow->setSuggestedGeometry(r, SuggestedGeometryHint_GeometryIsFromDocked);
266  floatingWindow->show();
267 
268  auto draggable = KDDockWidgets::usesNativeTitleBar() ? static_cast<Draggable *>(floatingWindow)
269  : static_cast<Draggable *>(this);
270  return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, draggable));
271 }
272 
273 DockWidgetBase *TitleBar::singleDockWidget() const
274 {
275  const DockWidgetBase::List dockWidgets = this->dockWidgets();
276  return dockWidgets.isEmpty() ? nullptr : dockWidgets.first();
277 }
278 
279 bool TitleBar::supportsFloatingButton() const
280 {
281  if (m_genericWidget)
282  return {}; // not applicable
283 
284  if (Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton) {
285  // Apps having a maximize/restore button traditionally don't have a floating one,
286  // QDockWidget style only has floating and no maximize/restore.
287  // We can add an option later if we need them to co-exist
288  return false;
289  }
290 
291  if (Config::self().flags() & Config::Flag_TitleBarNoFloatButton) {
292  // Was explicitly disabled
293  return false;
294  }
295 
296  if (DockWidgetBase *dw = singleDockWidget()) {
297  // Don't show the dock/undock button if the window is not dockable
298  if (dw->options() & DockWidgetBase::Option_NotDockable)
299  return false;
300  }
301 
302  // If we have a floating window with nested dock widgets we can't re-attach, because we don't
303  // know where to
304  return !m_floatingWindow || m_floatingWindow->hasSingleFrame();
305 }
306 
307 bool TitleBar::supportsMaximizeButton() const
308 {
309  if (!(Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton))
310  return false;
311 
312  return m_floatingWindow != nullptr;
313 }
314 
315 bool TitleBar::supportsMinimizeButton() const
316 {
317  if ((Config::self().flags() & Config::Flag_TitleBarHasMinimizeButton) != Config::Flag_TitleBarHasMinimizeButton) // this specific flag is not base^2
318  return false;
319 
320  return m_floatingWindow != nullptr;
321 }
322 
323 bool TitleBar::supportsAutoHideButton() const
324 {
325  // Only dock widgets docked into the MainWindow can minimize
326  return m_supportsAutoHide && m_frame && (m_frame->isInMainWindow() || m_frame->isOverlayed());
327 }
328 
329 bool TitleBar::hasIcon() const
330 {
331  return !m_icon.isNull();
332 }
333 
334 bool TitleBar::isFocused() const
335 {
336  if (m_frame)
337  return m_frame->isFocused();
338  else if (m_floatingWindow)
339  return m_floatingWindow->isActiveWindow();
340 #ifdef KDDOCKWIDGETS_QTWIDGETS
341  // Remove ifdef in 2.0 branch.
342  else if (m_genericWidget)
343  return m_genericWidget->isActiveWindow();
344 #endif
345  return false;
346 }
347 
348 QIcon TitleBar::icon() const
349 {
350  return m_icon;
351 }
352 
353 void TitleBar::onCloseClicked()
354 {
355  const bool closeOnlyCurrentTab = Config::self().flags() & Config::Flag_CloseOnlyCurrentTab;
356 
357  if (m_frame) {
358  if (closeOnlyCurrentTab) {
359  if (DockWidgetBase *dw = m_frame->currentDockWidget()) {
360  dw->close();
361  } else {
362  // Doesn't happen
363  qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
364  }
365  } else {
366  if (m_frame->isTheOnlyFrame() && m_frame->isInFloatingWindow()) {
367  m_frame->window()->close();
368  } else {
369  m_frame->close();
370  }
371  }
372  } else if (m_floatingWindow) {
373 
374  if (closeOnlyCurrentTab) {
375  if (Frame *f = m_floatingWindow->singleFrame()) {
376  if (DockWidgetBase *dw = f->currentDockWidget()) {
377  dw->close();
378  } else {
379  // Doesn't happen
380  qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
381  }
382  } else {
383  m_floatingWindow->close();
384  }
385  } else {
386  m_floatingWindow->close();
387  }
388  } else if (m_genericWidget) {
389  if (auto window = m_genericWidget->window())
390  window->close();
391  }
392 }
393 
394 bool TitleBar::isFloating() const
395 {
396  if (m_floatingWindow)
397  return true;
398 
399  if (m_frame)
400  return m_frame->isFloating();
401 
402  if (m_genericWidget)
403  return false; // Not applicable
404 
405  qWarning() << "TitleBar::isFloating: shouldn't happen";
406  return false;
407 }
408 
409 DockWidgetBase::List TitleBar::dockWidgets() const
410 {
411  if (m_floatingWindow) {
412  DockWidgetBase::List result;
413  for (Frame *f : m_floatingWindow->frames()) {
414  result << f->dockWidgets();
415  }
416  return result;
417  }
418 
419  if (m_frame)
420  return m_frame->dockWidgets();
421 
422  if (m_genericWidget)
423  return {}; // Not applicable
424 
425  qWarning() << "TitleBar::dockWidget: shouldn't happen";
426  return {};
427 }
428 
429 void TitleBar::onFloatClicked()
430 {
431  const DockWidgetBase::List dockWidgets = this->dockWidgets();
432  if (isFloating()) {
433  // Let's dock it
434 
435  if (dockWidgets.isEmpty()) {
436  qWarning() << "TitleBar::onFloatClicked: empty list. Shouldn't happen";
437  return;
438  }
439 
440  if (dockWidgets.size() == 1) {
441  // Case 1: Single dockwidget floating
442  dockWidgets[0]->setFloating(false);
443  } else {
444  // Case 2: Multiple dockwidgets are tabbed together and floating
445  // TODO: Just reuse the whole frame and put it back. The frame currently doesn't remember the position in the main window
446  // so use an hack for now
447  if (!dockWidgets.isEmpty()) { // could be empty during destruction, maybe
448  if (!dockWidgets.constFirst()->hasPreviousDockedLocation()) {
449  // Don't attempt, there's no previous docked location
450  return;
451  }
452 
453  int i = 0;
454  DockWidgetBase *current = nullptr;
455  for (auto dock : qAsConst(dockWidgets)) {
456 
457  if (!current && dock->isCurrentTab())
458  current = dock;
459 
460  dock->setFloating(true);
461  dock->dptr()->m_lastPosition->m_tabIndex = i;
462  dock->setFloating(false);
463  ++i;
464  }
465 
466  // Restore the current tab
467  if (current)
468  current->setAsCurrentTab();
469  }
470  }
471  } else {
472  // Let's float it
473  if (dockWidgets.size() == 1) {
474  // If there's a single dock widget, just call DockWidget::setFloating(true). The only difference
475  // is that it has logic for using the last used geometry for the floating window
476  dockWidgets[0]->setFloating(true);
477  } else {
478  makeWindow();
479  }
480  }
481 }
482 
483 void TitleBar::onMaximizeClicked()
484 {
485  toggleMaximized();
486 }
487 
488 void TitleBar::onMinimizeClicked()
489 {
490  if (!m_floatingWindow)
491  return;
492 
493  if (KDDockWidgets::usesUtilityWindows()) {
494  // Qt::Tool windows don't appear in the task bar.
495  // Unless someone tells me a good reason to allow this situation.
496  return;
497  }
498 
499  m_floatingWindow->showMinimized();
500 }
501 
502 void TitleBar::onAutoHideClicked()
503 {
504  if (!m_frame) {
505  // Doesn't happen
506  qWarning() << Q_FUNC_INFO << "Minimize not supported on floating windows";
507  return;
508  }
509 
510  const auto &dockwidgets = m_frame->dockWidgets();
511  for (DockWidgetBase *dw : dockwidgets) {
512  if (dw->isOverlayed()) {
513  // restore
514  MainWindowBase *mainWindow = dw->mainWindow();
515  mainWindow->restoreFromSideBar(dw);
516  } else {
517  dw->moveToSideBar();
518  }
519  }
520 }
521 
522 bool TitleBar::closeButtonEnabled() const
523 {
524  return m_closeButtonEnabled;
525 }
526 
527 bool TitleBar::floatButtonVisible() const
528 {
529  return m_floatButtonVisible;
530 }
531 
532 QString TitleBar::floatButtonToolTip() const
533 {
534  return m_floatButtonToolTip;
535 }
536 
537 void TitleBar::updateFloatButton()
538 {
539  setFloatButtonToolTip(floatingWindow() ? tr("Dock window") : tr("Undock window"));
540  setFloatButtonVisible(supportsFloatingButton());
541 }
542 
543 bool TitleBar::isWindow() const
544 {
545  return m_floatingWindow != nullptr;
546 }
547 
548 TabBar *TitleBar::tabBar() const
549 {
550  if (m_floatingWindow && m_floatingWindow->hasSingleFrame()) {
551  if (Frame *frame = m_floatingWindow->singleFrame()) {
552  return frame->tabWidget()->tabBar();
553  } else {
554  // Shouldn't happen
555  qWarning() << Q_FUNC_INFO << "Expected a frame";
556  }
557 
558  } else if (m_frame) {
559  return m_frame->tabWidget()->tabBar();
560  }
561 
562  return nullptr;
563 }
QRect::moveTopLeft
void moveTopLeft(const QPoint &position)
QVector::isEmpty
bool isEmpty() const const
QRect
KDDockWidgets::DockWidgetBase::setFloating
bool setFloating(bool floats)
setter to make the dock widget float or dock.
Definition: DockWidgetBase.cpp:179
QTimer::singleShot
singleShot
QWidget
QVector::first
T & first()
MainWindowBase.h
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
QSizePolicy
KDDockWidgets::DockWidgetBase::setAsCurrentTab
Q_INVOKABLE void setAsCurrentTab()
Makes this dock widget current in its tab group.
Definition: DockWidgetBase.cpp:317
QObject
QVector::constFirst
const T & constFirst() const const
QString
QFocusEvent
QFocusEvent::reason
Qt::FocusReason reason() const const
KDDockWidgets::Config
Singleton to allow to choose certain behaviours of the framework.
Definition: Config.h:75
QIcon
Config.h
Application-wide config to tune certain behaviours of the framework.
KDDockWidgets::DockWidgetBase
The DockWidget base-class. DockWidget and DockWidgetBase are only split in two so we can share some c...
Definition: DockWidgetBase.h:61
KDDockWidgets
Definition: Config.cpp:37
QVector::size
int size() const const
Draggable
KDDockWidgets::MainWindowBase
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
Definition: MainWindowBase.h:56
QVector
QSizePolicy::Minimum
Minimum
Qt::StrongFocus
StrongFocus
KDDockWidgets::SuggestedGeometryHint_GeometryIsFromDocked
@ SuggestedGeometryHint_GeometryIsFromDocked
Definition: KDDockWidgets.h:202
KDDockWidgets::MainWindowBase::restoreFromSideBar
Q_INVOKABLE void restoreFromSideBar(KDDockWidgets::DockWidgetBase *)
Removes the dock widget from the sidebar and docks it into the main window again.
Definition: MainWindowBase.cpp:516
QObject::parent
QObject * parent() const const
QPoint
FrameworkWidgetFactory.h
A factory class for allowing the user to customize some internal widgets.

© 2019-2022 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated on Thu Sep 15 2022 00:16:29 for KDDockWidgets API Documentation by doxygen 1.8.20