KDDockWidgets API Documentation  1.5
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 
22 #include <QTimer>
23 #include <QWindowStateChangeEvent>
24 
25 using namespace KDDockWidgets;
26 
27 TitleBar::TitleBar(Frame *parent)
28  : QWidgetAdapter(parent)
29  , Draggable(this)
30  , m_frame(parent)
31  , m_floatingWindow(nullptr)
32  , m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
33 {
34  connect(m_frame, &Frame::numDockWidgetsChanged, this, &TitleBar::updateCloseButton);
35  connect(m_frame, &Frame::isFocusedChanged, this, &TitleBar::isFocusedChanged);
36  connect(m_frame, &Frame::isInMainWindowChanged, this, &TitleBar::updateAutoHideButton);
37 
38  init();
39 
40  if (Config::self().flags() & Config::Flag_TitleBarIsFocusable)
41  setFocusPolicy(Qt::StrongFocus);
42 }
43 
44 TitleBar::TitleBar(FloatingWindow *parent)
45  : QWidgetAdapter(parent)
46  , Draggable(this)
47  , m_frame(nullptr)
48  , m_floatingWindow(parent)
49  , m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
50 {
51  connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateButtons);
52  connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
53  connect(m_floatingWindow, &FloatingWindow::activatedChanged, this, &TitleBar::isFocusedChanged);
54  init();
55 }
56 
57 void TitleBar::init()
58 {
60 
61  connect(this, &TitleBar::isFocusedChanged, this, [this] {
62  // repaint
63  update();
64  });
65 
66  updateButtons();
67  QTimer::singleShot(0, this, &TitleBar::updateAutoHideButton); // have to wait after the frame is
68  // constructed
69 }
70 
71 TitleBar::~TitleBar()
72 {
73 }
74 
75 bool TitleBar::onDoubleClicked()
76 {
77  if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes) && m_floatingWindow) {
78  // 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.
79  toggleMaximized();
80  return true;
81  } else if (supportsFloatingButton()) {
82  onFloatClicked();
83  return true;
84  }
85 
86  return false;
87 }
88 
89 MainWindowBase *TitleBar::mainWindow() const
90 {
91  if (m_floatingWindow)
92  return nullptr;
93 
94  if (m_frame)
95  return m_frame->mainWindow();
96 
97  qWarning() << Q_FUNC_INFO << "null frame and null floating window";
98  return nullptr;
99 }
100 
101 bool TitleBar::isMDI() const
102 {
103  if (auto mw = mainWindow())
104  return mw->isMDI();
105 
106  return false;
107 }
108 
109 void TitleBar::updateButtons()
110 {
111  updateCloseButton();
112  updateFloatButton();
113  updateMaximizeButton();
114  updateMinimizeButton();
115  updateAutoHideButton();
116 }
117 
118 void TitleBar::updateCloseButton()
119 {
120 
121  const bool anyNonClosable = frame() ? frame()->anyNonClosable()
122  : (floatingWindow() ? floatingWindow()->anyNonClosable()
123  : false);
124 
125  setCloseButtonEnabled(!anyNonClosable);
126 }
127 
128 void TitleBar::toggleMaximized()
129 {
130  if (!m_floatingWindow)
131  return;
132 
133  if (m_floatingWindow->isMaximizedOverride())
134  m_floatingWindow->showNormal();
135  else
136  m_floatingWindow->showMaximized();
137 }
138 
139 void TitleBar::focusInEvent(QFocusEvent *ev)
140 {
141  QWidgetAdapter::focusInEvent(ev);
142 
143  if (!m_frame || !(Config::self().flags() & Config::Flag_TitleBarIsFocusable))
144  return;
145 
146  // For some reason QWidget::setFocusProxy() isn't working, so forward manually
147  m_frame->FocusScope::focus(ev->reason());
148 }
149 
150 bool TitleBar::isOverlayed() const
151 {
152  return m_frame && m_frame->isOverlayed();
153 }
154 
155 void TitleBar::setCloseButtonEnabled(bool enabled)
156 {
157  if (enabled != m_closeButtonEnabled) {
158  m_closeButtonEnabled = enabled;
159  Q_EMIT closeButtonEnabledChanged(enabled);
160  }
161 }
162 
163 void TitleBar::setFloatButtonVisible(bool visible)
164 {
165  if (visible != m_floatButtonVisible) {
166  m_floatButtonVisible = visible;
167  Q_EMIT floatButtonVisibleChanged(visible);
168  }
169 }
170 
171 void TitleBar::setFloatButtonToolTip(const QString &tip)
172 {
173  if (tip != m_floatButtonToolTip) {
174  m_floatButtonToolTip = tip;
175  Q_EMIT floatButtonToolTipChanged(tip);
176  }
177 }
178 
179 void TitleBar::setTitle(const QString &title)
180 {
181  if (title != m_title) {
182  m_title = title;
183  update();
184  Q_EMIT titleChanged();
185  }
186 }
187 
188 void TitleBar::setIcon(const QIcon &icon)
189 {
190  m_icon = icon;
191  Q_EMIT iconChanged();
192 }
193 
194 std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
195 {
196  if (!isVisible() && window()->isVisible() && !(Config::self().flags() & Config::Flag_ShowButtonsOnTabBarIfTitleBarHidden)) {
197 
198  // When using Flag_ShowButtonsOnTabBarIfTitleBarHidden we forward the call from the tab bar's
199  // buttons to the title bar's buttons, just to reuse logic
200 
201  qWarning() << "TitleBar::makeWindow shouldn't be called on invisible title bar"
202  << this << window()->isVisible();
203  if (m_frame) {
204  qWarning() << "this=" << this << "; actual=" << m_frame->actualTitleBar();
205  } else if (m_floatingWindow) {
206  qWarning() << "Has floating window with titlebar=" << m_floatingWindow->titleBar()
207  << "; fw->isVisible=" << m_floatingWindow->isVisible();
208  }
209 
210  Q_ASSERT(false);
211  return {};
212  }
213 
214  if (m_floatingWindow) {
215  // We're already a floating window, no detach needed
216  return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(m_floatingWindow, this));
217  }
218 
219  if (FloatingWindow *fw = QWidgetAdapter::floatingWindow()) { // Already floating
220  if (m_frame->isTheOnlyFrame()) { // We don't detach. This one drags the entire window instead.
221  qCDebug(hovering) << "TitleBar::makeWindow no detach needed";
222  return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(fw, this));
223  }
224  }
225 
226  QRect r = m_frame->QWidgetAdapter::geometry();
227  r.moveTopLeft(m_frame->mapToGlobal(QPoint(0, 0)));
228 
229  auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
230  floatingWindow->setSuggestedGeometry(r, SuggestedGeometryHint_GeometryIsFromDocked);
231  floatingWindow->show();
232 
233  auto draggable = KDDockWidgets::usesNativeTitleBar() ? static_cast<Draggable *>(floatingWindow)
234  : static_cast<Draggable *>(this);
235  return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, draggable));
236 }
237 
238 DockWidgetBase *TitleBar::singleDockWidget() const
239 {
240  const DockWidgetBase::List dockWidgets = this->dockWidgets();
241  return dockWidgets.isEmpty() ? nullptr : dockWidgets.first();
242 }
243 
244 bool TitleBar::supportsFloatingButton() const
245 {
246  if (Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton) {
247  // Apps having a maximize/restore button traditionally don't have a floating one,
248  // QDockWidget style only has floating and no maximize/restore.
249  // We can add an option later if we need them to co-exist
250  return false;
251  }
252 
253  if (Config::self().flags() & Config::Flag_TitleBarNoFloatButton) {
254  // Was explicitly disabled
255  return false;
256  }
257 
258  if (DockWidgetBase *dw = singleDockWidget()) {
259  // Don't show the dock/undock button if the window is not dockable
260  if (dw->options() & DockWidgetBase::Option_NotDockable)
261  return false;
262  }
263 
264  // If we have a floating window with nested dock widgets we can't re-attach, because we don't
265  // know where to
266  return !m_floatingWindow || m_floatingWindow->hasSingleFrame();
267 }
268 
269 bool TitleBar::supportsMaximizeButton() const
270 {
271  if (!(Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton))
272  return false;
273 
274  return m_floatingWindow != nullptr;
275 }
276 
277 bool TitleBar::supportsMinimizeButton() const
278 {
279  if ((Config::self().flags() & Config::Flag_TitleBarHasMinimizeButton) != Config::Flag_TitleBarHasMinimizeButton) // this specific flag is not base^2
280  return false;
281 
282  return m_floatingWindow != nullptr;
283 }
284 
285 bool TitleBar::supportsAutoHideButton() const
286 {
287  // Only dock widgets docked into the MainWindow can minimize
288  return m_supportsAutoHide && m_frame && (m_frame->isInMainWindow() || m_frame->isOverlayed());
289 }
290 
291 bool TitleBar::hasIcon() const
292 {
293  return !m_icon.isNull();
294 }
295 
296 bool TitleBar::isFocused() const
297 {
298  if (m_frame)
299  return m_frame->isFocused();
300  else if (m_floatingWindow)
301  return m_floatingWindow->isActiveWindow();
302 
303  return false;
304 }
305 
306 QIcon TitleBar::icon() const
307 {
308  return m_icon;
309 }
310 
311 void TitleBar::onCloseClicked()
312 {
313  const bool closeOnlyCurrentTab = Config::self().flags() & Config::Flag_CloseOnlyCurrentTab;
314 
315  if (m_frame) {
316  if (closeOnlyCurrentTab) {
317  if (DockWidgetBase *dw = m_frame->currentDockWidget()) {
318  dw->close();
319  } else {
320  // Doesn't happen
321  qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
322  }
323  } else {
324  if (m_frame->isTheOnlyFrame() && !m_frame->isInMainWindow()) {
325  m_frame->window()->close();
326  } else {
327  m_frame->close();
328  }
329  }
330  } else if (m_floatingWindow) {
331 
332  if (closeOnlyCurrentTab) {
333  if (Frame *f = m_floatingWindow->singleFrame()) {
334  if (DockWidgetBase *dw = f->currentDockWidget()) {
335  dw->close();
336  } else {
337  // Doesn't happen
338  qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
339  }
340  } else {
341  m_floatingWindow->close();
342  }
343  } else {
344  m_floatingWindow->close();
345  }
346  }
347 }
348 
349 bool TitleBar::isFloating() const
350 {
351  if (m_floatingWindow)
352  return m_floatingWindow->hasSingleDockWidget(); // Debatable! Maybe it's always floating.
353 
354  if (m_frame)
355  return m_frame->isFloating();
356 
357  qWarning() << "TitleBar::isFloating: shouldn't happen";
358  return false;
359 }
360 
361 DockWidgetBase::List TitleBar::dockWidgets() const
362 {
363  if (m_floatingWindow) {
364  DockWidgetBase::List result;
365  for (Frame *f : m_floatingWindow->frames()) {
366  result << f->dockWidgets();
367  }
368  return result;
369  }
370 
371  if (m_frame)
372  return m_frame->dockWidgets();
373 
374  qWarning() << "TitleBar::dockWidget: shouldn't happen";
375  return {};
376 }
377 
378 void TitleBar::onFloatClicked()
379 {
380  const DockWidgetBase::List dockWidgets = this->dockWidgets();
381  if (isFloating()) {
382  // Let's dock it
383 
384  if (dockWidgets.isEmpty()) {
385  qWarning() << "TitleBar::onFloatClicked: empty list. Shouldn't happen";
386  return;
387  }
388 
389  if (dockWidgets.size() == 1) {
390  // Case 1: Single dockwidget floating
391  dockWidgets[0]->setFloating(false);
392  } else {
393  // Case 2: Multiple dockwidgets are tabbed together and floating
394  // TODO: Just reuse the whole frame and put it back. The frame currently doesn't remember the position in the main window
395  // so use an hack for now
396  for (auto dock : qAsConst(dockWidgets)) {
397  dock->setFloating(true);
398  dock->setFloating(false);
399  }
400  }
401  } else {
402  // Let's float it
403  if (dockWidgets.size() == 1) {
404  // If there's a single dock widget, just call DockWidget::setFloating(true). The only difference
405  // is that it has logic for using the last used geometry for the floating window
406  dockWidgets[0]->setFloating(true);
407  } else {
408  makeWindow();
409  }
410  }
411 }
412 
413 void TitleBar::onMaximizeClicked()
414 {
415  toggleMaximized();
416 }
417 
418 void TitleBar::onMinimizeClicked()
419 {
420  if (!m_floatingWindow)
421  return;
422 
423  if (KDDockWidgets::usesUtilityWindows()) {
424  // Qt::Tool windows don't appear in the task bar.
425  // Unless someone tells me a good reason to allow this situation.
426  return;
427  }
428 
429  m_floatingWindow->showMinimized();
430 }
431 
432 void TitleBar::onAutoHideClicked()
433 {
434  if (!m_frame) {
435  // Doesn't happen
436  qWarning() << Q_FUNC_INFO << "Minimize not supported on floating windows";
437  return;
438  }
439 
440  const auto &dockwidgets = m_frame->dockWidgets();
441  for (DockWidgetBase *dw : dockwidgets) {
442  if (dw->isOverlayed()) {
443  // restore
444  MainWindowBase *mainWindow = dw->mainWindow();
445  mainWindow->restoreFromSideBar(dw);
446  } else {
447  dw->moveToSideBar();
448  }
449  }
450 }
451 
452 bool TitleBar::closeButtonEnabled() const
453 {
454  return m_closeButtonEnabled;
455 }
456 
457 bool TitleBar::floatButtonVisible() const
458 {
459  return m_floatButtonVisible;
460 }
461 
462 QString TitleBar::floatButtonToolTip() const
463 {
464  return m_floatButtonToolTip;
465 }
466 
467 void TitleBar::updateFloatButton()
468 {
469  setFloatButtonToolTip(floatingWindow() ? tr("Dock window") : tr("Undock window"));
470  setFloatButtonVisible(supportsFloatingButton());
471 }
472 
473 bool TitleBar::isWindow() const
474 {
475  return m_floatingWindow != nullptr;
476 }
QRect::moveTopLeft
void moveTopLeft(const QPoint &position)
QVector::isEmpty
bool isEmpty() const const
KDDockWidgets::MainWindowBase::isMDI
bool isMDI
Definition: MainWindowBase.h:61
QRect
QTimer::singleShot
singleShot
QVector::first
T & first()
MainWindowBase.h
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
QSizePolicy
QString
QFocusEvent
QFocusEvent::reason
Qt::FocusReason reason() const const
KDDockWidgets::Config
Singleton to allow to choose certain behaviours of the framework.
Definition: Config.h:55
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:36
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:204
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:511
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 Mon Mar 7 2022 02:01:21 for KDDockWidgets API Documentation by doxygen 1.8.20