21#include "MainWindow_p.h"
22#include "kddockwidgets/KDDockWidgets.h"
27#include "core/Utils_p.h"
28#include "core/Logging_p.h"
29#include "core/ScopedValueRollback_p.h"
30#include "core/WidgetResizeHandler_p.h"
32#include "core/LayoutSaver_p.h"
33#include "core/layouting/Item_p.h"
35#include "core/DockWidget_p.h"
38#include "kddockwidgets/core/views/MainWindowViewInterface.h"
40#include <unordered_map>
56 , d(new Private(this, uniqueName, options))
65 d->m_persistentCentralDockWidget = d->createPersistentCentralDockWidget(d->name);
69 d->m_visibleWidgetCountConnection =
70 d->m_layout->d_ptr()->visibleWidgetCountChanged.connect([
this](
int count) { d->groupCountChanged.emit(count); });
71 view()->
d->closeRequested.connect([
this](CloseEvent *ev) { d->m_layout->onCloseEvent(ev); });
73 d->m_resizeConnection =
view()->
d->resized.connect([
this](Size
size) {
87 KDDW_DEBUG(
"dock={}", (
void * )widget);
90 KDDW_ERROR(
"Refusing to dock widget with incompatible affinity. {} {}", widget->
affinities(),
affinities());
95 KDDW_ERROR(
"Refusing to dock non-dockable widget {}", (
void * )widget);
104 if (d->supportsPersistentCentralWidget()) {
105 KDDW_ERROR(
"Not supported with MainWindowOption_HasCentralWidget."
106 "MainWindowOption_HasCentralWidget can only have 1 widget in the center.",
107 "Use MainWindowOption_HasCentralFrame instead, which is similar but supports "
109 }
else if (d->supportsCentralFrame()) {
112 KDDW_ERROR(
"Not supported without MainWindowOption_HasCentralFrame");
120 KDDW_ERROR(
"Refusing to dock non-dockable widget dw={}", (
void * )dw);
139 KDDW_ERROR(
"MainWindow::addDockWidgetToSide: A central group is required. Either MainWindowOption_HasCentralFrame or MainWindowOption_HasCentralWidget");
146 KDDW_ERROR(
"MainWindow::addDockWidgetToSide: no group");
167 Core::Item *neighbor = group->
layoutItem()->outermostNeighbor(location,
false);
169 if (neighbor->isContainer()) {
170 auto container = object_cast<ItemBoxContainer *>(neighbor);
171 const auto children = container->childItems();
172 if (children.isEmpty()) {
174 KDDW_ERROR(
"MainWindow::addDockWidgetToSide: no children");
199 return d->m_layout->asDropArea();
214 return d->m_layout->asMDILayout();
225 if (!d->affinities.isEmpty()) {
226 KDDW_ERROR(
"Affinity is already set, refusing to change."
227 "Submit a feature request with a good justification.");
236 return d->affinities;
249CursorPositions MainWindow::Private::allowedResizeSides(
SideBarLocation loc)
const
277 const Rect centralAreaGeo = q->centralAreaGeometry();
278 const Margins centerWidgetMargins = q->centerWidgetMargins();
281 const int margin = m_overlayMargin;
288 const int leftSideBarWidth =
289 (leftSideBar && leftSideBar->
isVisible()) ? leftSideBar->
width() : 0;
290 const int rightSideBarWidth =
291 (rightSideBar && rightSideBar->
isVisible()) ? rightSideBar->
width() : 0;
292 rect.setHeight(std::max(300, group->
view()->
minSize().height()));
293 rect.setWidth(centralAreaGeo.width() - margin * 2 - leftSideBarWidth - rightSideBarWidth);
294 rect.moveLeft(margin + leftSideBarWidth);
296 rect.moveTop(centralAreaGeo.bottom() - centerWidgetMargins.bottom() - rect.height()
299 rect.moveTop(centralAreaGeo.y() + sb->
height() + centerWidgetMargins.top());
307 const int topSideBarHeight =
309 const int bottomSideBarHeight =
310 (bottomSideBar && bottomSideBar->
isVisible()) ? bottomSideBar->
height() : 0;
311 rect.setWidth(std::max(300, group->
view()->
minSize().width()));
312 rect.setHeight(centralAreaGeo.height() - topSideBarHeight - bottomSideBarHeight
313 - centerWidgetMargins.top() - centerWidgetMargins.bottom());
314 rect.moveTop(sb->
view()->
mapTo(q->view(), Point(0, 0)).y() + topSideBarHeight - 1);
316 rect.moveLeft(centralAreaGeo.x() + centralAreaGeo.width() - rect.width() - sb->
width()
317 - centerWidgetMargins.right() - margin);
319 rect.moveLeft(margin + centralAreaGeo.x() + centerWidgetMargins.left() + sb->
width());
335 case Core::LayoutBorderLocation_North:
337 case Core::LayoutBorderLocation_East:
339 case Core::LayoutBorderLocation_West:
341 case Core::LayoutBorderLocation_South:
343 case Core::LayoutBorderLocation_All:
344 case Core::LayoutBorderLocation_Verticals:
345 case Core::LayoutBorderLocation_Horizontals:
346 case Core::LayoutBorderLocation_None:
350 KDDW_ERROR(
"Unknown loc={}", loc);
357 case Core::LayoutBorderLocation_North:
359 case Core::LayoutBorderLocation_East:
361 case Core::LayoutBorderLocation_West:
363 case Core::LayoutBorderLocation_South:
365 case Core::LayoutBorderLocation_All:
366 case Core::LayoutBorderLocation_Verticals:
367 case Core::LayoutBorderLocation_Horizontals:
368 case Core::LayoutBorderLocation_None:
377 Group *group = dw->
d->group();
378 Core::Item *item = q->layout()->itemForGroup(group);
380 KDDW_ERROR(
"No item for dock widget");
384 const Core::LayoutBorderLocations borders = item->adjacentLayoutBorders();
385 const double aspectRatio = group->
width() / (std::max(1, group->
height()) * 1.0);
388 if (borders == Core::LayoutBorderLocation_All) {
393 for (
auto borderLoc :
394 { Core::LayoutBorderLocation_North, Core::LayoutBorderLocation_East,
395 Core::LayoutBorderLocation_West, Core::LayoutBorderLocation_South }) {
396 if (borders == (Core::LayoutBorderLocation_All & ~borderLoc))
401 if ((borders & Core::LayoutBorderLocation_Verticals)
402 == Core::LayoutBorderLocation_Verticals) {
404 const int distanceToTop = group->
geometry().y();
405 const int distanceToBottom = q->layout()->layoutHeight() - group->
geometry().bottom();
410 if ((borders & Core::LayoutBorderLocation_Horizontals)
411 == Core::LayoutBorderLocation_Horizontals) {
413 const int distanceToLeft = group->
geometry().x();
414 const int distanceToRight = q->layout()->layoutWidth() - group->
geometry().right();
419 if (borders == (Core::LayoutBorderLocation_West | Core::LayoutBorderLocation_South)) {
422 == (Core::LayoutBorderLocation_East | Core::LayoutBorderLocation_South)) {
425 == (Core::LayoutBorderLocation_West | Core::LayoutBorderLocation_North)) {
428 == (Core::LayoutBorderLocation_East | Core::LayoutBorderLocation_North)) {
444void MainWindow::Private::updateOverlayGeometry(Size suggestedSize)
446 if (!m_overlayedDockWidget)
449 Core::SideBar *sb = q->sideBarForDockWidget(m_overlayedDockWidget);
451 KDDW_ERROR(
"Expected a sidebar");
455 const Rect defaultGeometry = rectForOverlay(m_overlayedDockWidget->d->group(), sb->
location());
456 Rect newGeometry = defaultGeometry;
458 Core::Group *group = m_overlayedDockWidget->d->group();
460 if (suggestedSize.isValid() && !suggestedSize.isEmpty()) {
464 const int maxHeight = q->height() - group->
pos().y() - 10;
465 newGeometry.setHeight(std::min(suggestedSize.height(), maxHeight));
469 const int maxHeight = sb->
pos().y() - m_layout->view()->pos().y() - 10;
470 const int bottom = newGeometry.bottom();
471 newGeometry.setHeight(std::min(suggestedSize.height(), maxHeight));
472 newGeometry.moveBottom(bottom);
476 const int maxWidth = sb->
pos().x() - m_layout->view()->pos().x() - 10;
477 const int right = newGeometry.right();
478 newGeometry.setWidth(std::min(suggestedSize.width(), maxWidth));
479 newGeometry.moveRight(right);
483 const int maxWidth = q->width() - group->
pos().x() - 10;
484 newGeometry.setWidth(std::min(suggestedSize.width(), maxWidth));
489 KDDW_ERROR(
"Unexpected sidebar value");
494 m_overlayedDockWidget->d->group()->view()->setGeometry(newGeometry);
497void MainWindow::Private::clearSideBars()
506Rect MainWindow::Private::windowGeometry()
const
514 if (Core::Window::Ptr window = q->view()->window())
515 return window->geometry();
517 return q->window()->geometry();
531 ScopedValueRollback rollback(dw->
d->m_isMovingToSideBar,
true);
537 KDDW_ERROR(
"Minimization supported, probably disabled in Config::self().flags()");
546 DockWidget::Private::UpdateActions updateActions(dw);
549 if (dw == d->m_overlayedDockWidget)
555 KDDW_ERROR(
"Dock widget isn't in any sidebar");
570 KDDW_ERROR(
"You need to add the dock widget to the sidebar before you can overlay it");
574 if (d->m_overlayedDockWidget == dw) {
584 d->m_overlayedDockWidget = dw;
586 d->updateOverlayGeometry(dw->
d->lastPosition()->lastOverlayedGeometry(sb->
location()).size());
591 dw->
d->isOverlayedChanged.emit(
true);
596 const bool wasOverlayed = d->m_overlayedDockWidget == dw;
605 if (!d->m_overlayedDockWidget)
609 d->m_overlayedDockWidget =
nullptr;
661 return d->m_overlayedDockWidget;
691 bool allClosed =
true;
693 const auto dockWidgets = d->m_layout->dockWidgets();
701 allClosed = allClosed && closed;
724 if (d->name.isEmpty()) {
726 d->uniqueNameChanged.emit();
729 KDDW_ERROR(
"Already has a name. {} {}", this->
uniqueName(), uniqueName);
733bool MainWindow::deserialize(
const LayoutSaver::MainWindow &mw)
736 KDDW_ERROR(
"Refusing to restore MainWindow with different options ; expected={}, has={}",
int(mw.options),
int(
options()));
740 if (d->affinities != mw.affinities) {
741 KDDW_ERROR(
"Affinity name changed from {} to {}", d->affinities, mw.affinities);
743 d->affinities = mw.affinities;
760 KDDW_ERROR(
"Could not find dock widget {} . Won't restore it to sidebar",
uniqueName);
777LayoutSaver::MainWindow MainWindow::serialize()
const
779 LayoutSaver::MainWindow m;
784 m.geometry = d->windowGeometry();
791 m.affinities = d->affinities;
799 m.dockWidgetsPerSideBar[loc] = dockwidgets;
808 if (!d->supportsPersistentCentralWidget()) {
809 KDDW_ERROR(
"MainWindow::setPersistentCentralWidget() requires "
810 "MainWindowOption_HasCentralWidget");
814 if (
auto dw = d->m_persistentCentralDockWidget) {
817 KDDW_ERROR(
"Unexpected null central dock widget");
823 if (
auto dw = d->m_persistentCentralDockWidget)
843 auto it = d->m_sideBars.find(loc);
844 return it == d->m_sideBars.cend() ? nullptr : it->second;
855 return d->m_overlayMargin;
860 if (margin == d->m_overlayMargin)
863 d->m_overlayMargin = margin;
864 d->overlayMarginChanged.emit(margin);
The interface that MainWindow views should implement.
virtual Rect centralAreaGeometry() const =0
virtual Margins centerWidgetMargins() const =0
virtual void setContentsMargins(int left, int top, int right, int bottom)=0
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
bool sideBarIsVisible(KDDockWidgets::SideBarLocation location) const
Returns whether the specified sidebar is visible.
void setAffinities(const Vector< QString > &names)
Sets the affinities names. Dock widgets can only dock into main windows of the same affinity.
QString uniqueName() const
Returns the unique name that was passed via constructor. Used internally by the save/restore mechanis...
void setOverlayMargin(int margin)
Sets the margin used by overlay docks. Does not modify currently overlayed docks.
void addDockWidgetAsTab(KDDockWidgets::Core::DockWidget *dockwidget)
Docks a DockWidget into the central group, tabbed.
void overlayOnSideBar(KDDockWidgets::Core::DockWidget *dw)
Shows the dock widget overlayed on top of the main window, placed next to the sidebar.
Rect centralAreaGeometry() const
friend class KDDockWidgets::QtWidgets::MainWindow
std::shared_ptr< View > persistentCentralView() const
KDDockWidgets::Core::SideBar * sideBarForDockWidget(const KDDockWidgets::Core::DockWidget *dw) const
Returns the sidebar this dockwidget is in. nullptr if not in any.
DropArea * dropArea() const
void toggleOverlayOnSideBar(KDDockWidgets::Core::DockWidget *dw)
Shows or hides an overlay. It's assumed the dock widget is already in a side-bar.
void layoutParentContainerEqually(KDDockWidgets::Core::DockWidget *dockWidget)
like layoutEqually() but starts with the container that has dockWidget. While layoutEqually() starts ...
Margins centerWidgetMargins() const
bool anySideBarIsVisible() const
Returns whether any side bar is visible.
int overlayMargin() const
Returns the margin used by overlay docks. Default: 1.
void setContentsMargins(int l, int t, int r, int b)
Sets the content's margins.
void setPersistentCentralView(std::shared_ptr< View > widget)
Sets a persistent central widget. It can't be detached.
MDILayout * mdiLayout() const
void layoutEqually()
layouts all the widgets so they have an equal size within their parent container
DropArea * multiSplitter() const
void clearSideBarOverlay(bool deleteGroup=true)
closes any overlayed dock widget. The sidebar still displays them as button.
MainWindowOptions options() const
Returns the main window options that were passed via constructor.
Core::DockWidget * overlayedDockWidget() const
returns the dock widget which is currently overlayed. nullptr if none. This is only relevant when usi...
void moveToSideBar(KDDockWidgets::Core::DockWidget *dw)
Moves the dock widget into one of the MainWindow's sidebar. Means the dock widget is removed from the...
bool closeDockWidgets(bool force=false)
Closes all dock widgets which are docked into this main window This is convenience to calling DockWid...
void init(const QString &name)
bool isMDI() const
Returns whether this main window is using an MDI layout. In other words, returns true if MainWindowOp...
Vector< QString > affinities() const
Returns the list of affinity names. Empty by default.
void addDockWidgetToSide(KDDockWidgets::Core::DockWidget *dockWidget, KDDockWidgets::Location location, const KDDockWidgets::InitialOption &initialOption={})
void restoreFromSideBar(KDDockWidgets::Core::DockWidget *dw)
Removes the dock widget from the sidebar and docks it into the main window again.
Core::SideBar * sideBar(SideBarLocation location) const
Returns the side bar at the specified location.
void addDockWidget(KDDockWidgets::Core::DockWidget *dockWidget, KDDockWidgets::Location location, KDDockWidgets::Core::DockWidget *relativeTo=nullptr, const KDDockWidgets::InitialOption &initialOption={})
Docks a DockWidget into this main window.
void setUniqueName(const QString &uniqueName)
A MultiSplitter with support for drop indicators when hovering over.
static Layout * createLayout(MainWindow *mainWindow, MainWindowOptions options)
static SideBarLocation opposedSideBarLocationForBorder(Core::LayoutBorderLocation loc)
static SideBarLocation sideBarLocationForBorder(Core::LayoutBorderLocations loc)
bool isEmpty() const const
QTextStream & right(QTextStream &stream)
QMainWindow sub-class to enable KDDockWidgets support.