Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "dockbase.h"
24 :
25 : #include <QRect>
26 : #include <QTimer>
27 :
28 : #include "log.h"
29 :
30 : #include "kddockwidgets/src/core/DockWidget.h"
31 : #include "kddockwidgets/src/core/FloatingWindow.h"
32 : #include "kddockwidgets/src/core/Group.h"
33 : #include "kddockwidgets/src/qtquick/views/DockWidget.h"
34 : #include "kddockwidgets/src/qtquick/views/Group.h"
35 : #include "kddockwidgets/src/qtquick/views/View.h"
36 : #include "kddockwidgets/src/qtquick/Action.h"
37 :
38 : // This include pulls in kdbindings/signal.h which is incompatible with
39 : // Qt's `emit` macro being defined. Temporarily undefine it, then restore it.
40 : #ifdef emit
41 : #undef emit
42 : #include "kddockwidgets/src/core/layouting/Item_p.h"
43 : #define emit
44 : #else
45 : #include "kddockwidgets/src/core/layouting/Item_p.h"
46 : #endif
47 :
48 : #include "ui/qml/Muse/Ui/navigationsection.h"
49 :
50 : #include "muse_framework_config.h"
51 :
52 : namespace muse::dock {
53 0 : static QSize adjustSizeByConstraints(const QSize& size, const QSize& min, const QSize& max)
54 : {
55 0 : return size.expandedTo(min).boundedTo(max);
56 : }
57 :
58 0 : static bool sizeInRange(const QSize& size, const QSize& min, const QSize& max)
59 : {
60 0 : bool widthInRange = size.width() >= min.width() && size.width() <= max.width();
61 0 : bool heightInRange = size.height() >= min.height() && size.height() <= max.height();
62 :
63 0 : return widthInRange && heightInRange;
64 : }
65 :
66 276 : static KDDockWidgets::Core::Group* groupForDockWidget(KDDockWidgets::Core::DockWidget* dw)
67 : {
68 276 : if (!dw) {
69 0 : return nullptr;
70 : }
71 552 : auto* view = qobject_cast<KDDockWidgets::QtQuick::DockWidget*>(
72 276 : KDDockWidgets::QtQuick::asQQuickItem(dw));
73 276 : return view ? view->group() : nullptr;
74 : }
75 :
76 12 : static KDDockWidgets::QtQuick::Group* groupViewFor(KDDockWidgets::Core::Group* group)
77 : {
78 12 : if (!group) {
79 0 : return nullptr;
80 : }
81 24 : return qobject_cast<KDDockWidgets::QtQuick::Group*>(
82 12 : KDDockWidgets::QtQuick::asQQuickItem(group));
83 : }
84 : }
85 :
86 : using namespace muse::dock;
87 :
88 52 : DockBase::DockBase(DockType type, QQuickItem* parent)
89 52 : : QQuickItem(parent), Contextable(muse::iocCtxForQmlObject(this))
90 : {
91 52 : Q_ASSERT(type != DockType::Undefined);
92 :
93 52 : m_properties.type = type;
94 52 : m_properties.floatable = true;
95 52 : m_properties.closable = true;
96 52 : m_properties.resizable = true;
97 52 : m_properties.separatorsVisible = true;
98 52 : }
99 :
100 0 : QString DockBase::title() const
101 : {
102 0 : return m_title;
103 : }
104 :
105 68 : int DockBase::minimumWidth() const
106 : {
107 68 : return m_minimumWidth;
108 : }
109 :
110 6 : int DockBase::minimumHeight() const
111 : {
112 6 : return m_minimumHeight;
113 : }
114 :
115 68 : int DockBase::maximumWidth() const
116 : {
117 68 : return m_maximumWidth;
118 : }
119 :
120 6 : int DockBase::maximumHeight() const
121 : {
122 6 : return m_maximumHeight;
123 : }
124 :
125 133 : int DockBase::contentWidth() const
126 : {
127 133 : return m_contentWidth;
128 : }
129 :
130 0 : int DockBase::contentHeight() const
131 : {
132 0 : return m_contentHeight;
133 : }
134 :
135 12 : QSize DockBase::preferredSize() const
136 : {
137 12 : return QSize(width(), height());
138 : }
139 :
140 0 : int DockBase::locationProperty() const
141 : {
142 0 : return static_cast<int>(m_properties.location);
143 : }
144 :
145 3 : Location DockBase::location() const
146 : {
147 3 : return m_properties.location;
148 : }
149 :
150 0 : QPoint DockBase::globalPosition() const
151 : {
152 0 : if (!m_dockWidget) {
153 0 : return QPoint();
154 : }
155 :
156 0 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
157 0 : if (!group) {
158 0 : return QPoint();
159 : }
160 :
161 0 : QQuickItem* groupView = KDDockWidgets::QtQuick::asQQuickItem(group);
162 0 : if (!groupView) {
163 0 : return QPoint();
164 : }
165 :
166 0 : return groupView->mapToGlobal(QPointF(0, 0)).toPoint();
167 : }
168 :
169 0 : QVariantList DockBase::dropDestinationsProperty() const
170 : {
171 0 : return m_dropDestinations;
172 : }
173 :
174 6 : QList<DropDestination> DockBase::dropDestinations() const
175 : {
176 6 : QList<DropDestination> result;
177 :
178 6 : for (const QVariant& obj : m_dropDestinations) {
179 0 : QVariantMap map = obj.toMap();
180 :
181 0 : DropDestination destination;
182 0 : destination.dock = map["dock"].value<DockBase*>();
183 :
184 0 : if (map.contains("dropLocation")) {
185 0 : destination.dropLocation = static_cast<Location>(map["dropLocation"].toInt());
186 : } else {
187 0 : destination.dropLocation = Location::Left;
188 : }
189 :
190 0 : if (map.contains("dropDistance")) {
191 0 : destination.dropDistance = map["dropDistance"].toInt();
192 : }
193 :
194 0 : result << destination;
195 0 : }
196 :
197 6 : return result;
198 0 : }
199 :
200 0 : bool DockBase::closable() const
201 : {
202 0 : return m_properties.closable;
203 : }
204 :
205 0 : bool DockBase::resizable() const
206 : {
207 0 : return m_properties.resizable;
208 : }
209 :
210 0 : bool DockBase::separatorsVisible() const
211 : {
212 0 : return m_properties.separatorsVisible;
213 : }
214 :
215 12 : bool DockBase::defaultVisibility() const
216 : {
217 12 : return m_defaultVisibility;
218 : }
219 :
220 30 : bool DockBase::isCompact() const
221 : {
222 30 : return m_isCompact;
223 : }
224 :
225 69 : int DockBase::compactPriorityOrder() const
226 : {
227 69 : return m_compactPriorityOrder;
228 : }
229 :
230 7 : int DockBase::nonCompactWidth() const
231 : {
232 7 : return m_nonCompactWidth;
233 : }
234 :
235 15 : bool DockBase::floatable() const
236 : {
237 15 : return m_properties.floatable;
238 : }
239 :
240 101 : bool DockBase::floating() const
241 : {
242 101 : return m_floating;
243 : }
244 :
245 127 : bool DockBase::inited() const
246 : {
247 127 : return m_inited;
248 : }
249 :
250 0 : DockType DockBase::type() const
251 : {
252 0 : return m_properties.type;
253 : }
254 :
255 54 : KDDockWidgets::Core::DockWidget* DockBase::dockWidget() const
256 : {
257 54 : return m_dockWidget;
258 : }
259 :
260 0 : void DockBase::setTitle(const QString& title)
261 : {
262 0 : if (title == m_title) {
263 0 : return;
264 : }
265 :
266 0 : m_title = title;
267 0 : emit titleChanged();
268 : }
269 :
270 49 : void DockBase::setMinimumWidth(int width)
271 : {
272 49 : if (width == minimumWidth()) {
273 13 : return;
274 : }
275 :
276 36 : m_minimumWidth = width;
277 36 : emit minimumSizeChanged();
278 : }
279 :
280 6 : void DockBase::setMinimumHeight(int height)
281 : {
282 6 : if (height == minimumHeight()) {
283 0 : return;
284 : }
285 :
286 6 : m_minimumHeight = height;
287 6 : emit minimumSizeChanged();
288 : }
289 :
290 49 : void DockBase::setMaximumWidth(int width)
291 : {
292 49 : if (width == maximumWidth()) {
293 1 : return;
294 : }
295 :
296 48 : m_maximumWidth = width;
297 48 : emit maximumSizeChanged();
298 : }
299 :
300 6 : void DockBase::setMaximumHeight(int height)
301 : {
302 6 : if (height == maximumHeight()) {
303 0 : return;
304 : }
305 :
306 6 : m_maximumHeight = height;
307 6 : emit maximumSizeChanged();
308 : }
309 :
310 53 : void DockBase::setContentWidth(int width)
311 : {
312 53 : if (m_contentWidth == width) {
313 0 : return;
314 : }
315 :
316 53 : m_contentWidth = width;
317 53 : emit contentSizeChanged();
318 : }
319 :
320 6 : void DockBase::setContentHeight(int height)
321 : {
322 6 : if (m_contentHeight == height) {
323 0 : return;
324 : }
325 :
326 6 : m_contentHeight = height;
327 6 : emit contentSizeChanged();
328 : }
329 :
330 49 : void DockBase::setLocation(int location)
331 : {
332 49 : if (location == m_properties.location) {
333 0 : return;
334 : }
335 :
336 49 : m_properties.location = static_cast<Location>(location);
337 49 : emit locationChanged();
338 : }
339 :
340 0 : void DockBase::setDropDestinations(const QVariantList& destinations)
341 : {
342 0 : if (m_dropDestinations == destinations) {
343 0 : return;
344 : }
345 :
346 0 : m_dropDestinations = destinations;
347 0 : emit dropDestinationsChanged();
348 : }
349 :
350 3 : void DockBase::setFloatable(bool floatable)
351 : {
352 3 : if (floatable == m_properties.floatable) {
353 0 : return;
354 : }
355 :
356 3 : m_properties.floatable = floatable;
357 3 : emit floatableChanged();
358 : }
359 :
360 3 : void DockBase::setClosable(bool closable)
361 : {
362 3 : if (closable == m_properties.closable) {
363 0 : return;
364 : }
365 :
366 3 : m_properties.closable = closable;
367 3 : emit closableChanged();
368 : }
369 :
370 0 : void DockBase::setResizable(bool resizable)
371 : {
372 0 : if (resizable == m_properties.resizable) {
373 0 : return;
374 : }
375 :
376 0 : m_properties.resizable = resizable;
377 0 : emit resizableChanged();
378 : }
379 :
380 0 : void DockBase::setSeparatorsVisible(bool visible)
381 : {
382 0 : if (visible == m_properties.separatorsVisible) {
383 0 : return;
384 : }
385 :
386 0 : m_properties.separatorsVisible = visible;
387 0 : emit separatorsVisibleChanged();
388 : }
389 :
390 11 : void DockBase::setIsCompact(bool compact)
391 : {
392 11 : if (m_isCompact == compact) {
393 0 : return;
394 : }
395 :
396 11 : if (compact) {
397 8 : m_nonCompactWidth = width();
398 : }
399 :
400 11 : m_isCompact = compact;
401 11 : emit isCompactChanged();
402 : }
403 :
404 40 : void DockBase::setCompactPriorityOrder(int order)
405 : {
406 40 : if (m_compactPriorityOrder == order) {
407 24 : return;
408 : }
409 :
410 16 : m_compactPriorityOrder = order;
411 16 : emit compactPriorityOrderChanged();
412 : }
413 :
414 0 : void DockBase::setFloating(bool floating)
415 : {
416 0 : IF_ASSERT_FAILED(m_dockWidget) {
417 0 : return;
418 : }
419 :
420 0 : m_dockWidget->setFloating(floating);
421 : }
422 :
423 0 : void DockBase::setNavigationSection(ui::NavigationSection* section)
424 : {
425 0 : if (m_navigationSection == section) {
426 0 : return;
427 : }
428 :
429 0 : m_navigationSection = section;
430 0 : emit navigationSectionChanged();
431 : }
432 :
433 0 : void DockBase::setContentNavigationPanelOrderStart(int order)
434 : {
435 0 : if (m_contentNavigationPanelOrderStart == order) {
436 0 : return;
437 : }
438 :
439 0 : m_contentNavigationPanelOrderStart = order;
440 0 : emit contentNavigationPanelOrderStartChanged();
441 : }
442 :
443 12 : void DockBase::init()
444 : {
445 12 : IF_ASSERT_FAILED(m_dockWidget) {
446 0 : return;
447 : }
448 :
449 12 : setVisible(m_dockWidget->isOpen());
450 12 : setInited(true);
451 :
452 12 : applySizeConstraints();
453 12 : updateFloatingStatus();
454 : }
455 :
456 0 : void DockBase::deinit()
457 : {
458 0 : setInited(false);
459 0 : }
460 :
461 12 : bool DockBase::isOpen() const
462 : {
463 12 : IF_ASSERT_FAILED(m_dockWidget) {
464 0 : return false;
465 : }
466 :
467 12 : return m_dockWidget->isOpen();
468 : }
469 :
470 0 : void DockBase::open()
471 : {
472 0 : TRACEFUNC;
473 :
474 0 : IF_ASSERT_FAILED(m_dockWidget) {
475 0 : return;
476 : }
477 :
478 0 : setVisible(true);
479 0 : m_dockWidget->open();
480 :
481 0 : applySizeConstraints();
482 0 : }
483 :
484 0 : void DockBase::close()
485 : {
486 0 : TRACEFUNC;
487 :
488 0 : IF_ASSERT_FAILED(m_dockWidget) {
489 0 : return;
490 : }
491 :
492 0 : m_dockWidget->close();
493 0 : setVisible(false);
494 0 : }
495 :
496 0 : void DockBase::showHighlighting(const QRect& highlightingRect)
497 : {
498 0 : if (highlightingRect == m_properties.highlightingRect) {
499 0 : return;
500 : }
501 :
502 0 : m_properties.highlightingRect = highlightingRect;
503 0 : writeProperties();
504 : }
505 :
506 0 : void DockBase::hideHighlighting()
507 : {
508 0 : showHighlighting(QRect());
509 0 : }
510 :
511 0 : QRect DockBase::frameGeometry() const
512 : {
513 0 : if (m_dockWidget && m_dockWidget->isVisible()) {
514 0 : return m_dockWidget->groupGeometry();
515 : }
516 :
517 0 : return QRect();
518 : }
519 :
520 0 : bool DockBase::isInSameFrame(const DockBase* other) const
521 : {
522 0 : IF_ASSERT_FAILED(other) {
523 0 : return false;
524 : }
525 :
526 0 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
527 0 : KDDockWidgets::Core::Group* otherGroup = groupForDockWidget(other->dockWidget());
528 :
529 0 : if (!group || !otherGroup) {
530 0 : return false;
531 : }
532 :
533 0 : return group == otherGroup;
534 : }
535 :
536 0 : void DockBase::setFramePanelOrder(int order)
537 : {
538 0 : if (!m_dockWidget) {
539 0 : return;
540 : }
541 :
542 0 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
543 0 : if (!group) {
544 0 : return;
545 : }
546 :
547 0 : if (group->beingDeletedLater()) {
548 0 : return;
549 : }
550 :
551 0 : KDDockWidgets::QtQuick::Group* groupView = groupViewFor(group);
552 0 : if (!groupView) {
553 0 : return;
554 : }
555 :
556 0 : QQuickItem* frameVisualItem = groupView->visualItem();
557 0 : if (!frameVisualItem) {
558 0 : return;
559 : }
560 :
561 0 : frameVisualItem->setProperty("titleBarNavigationPanelOrder", order);
562 : }
563 :
564 0 : void DockBase::resetToDefault()
565 : {
566 0 : setVisible(m_defaultVisibility);
567 0 : }
568 :
569 0 : void DockBase::resize(int width, int height)
570 : {
571 0 : TRACEFUNC;
572 :
573 0 : if (!m_dockWidget) {
574 0 : return;
575 : }
576 :
577 0 : width = std::max(width, m_minimumWidth);
578 0 : height = std::max(height, m_minimumHeight);
579 :
580 0 : if (m_maximumWidth > 0) {
581 0 : width = std::min(width, std::max(m_maximumWidth, m_minimumWidth));
582 : }
583 :
584 0 : if (m_maximumHeight > 0) {
585 0 : height = std::min(height, std::max(m_maximumHeight, m_minimumHeight));
586 : }
587 :
588 : //! NOTE: Use the actual size in the layout, the item size may be overridden from QML
589 0 : const int deltaWidth = width - m_dockWidget->width();
590 0 : const int deltaHeight = height - m_dockWidget->height();
591 0 : if (deltaWidth == 0 && deltaHeight == 0) {
592 0 : return;
593 : }
594 :
595 0 : int left = 0;
596 0 : int top = 0;
597 0 : int right = 0;
598 0 : int bottom = 0;
599 :
600 0 : switch (location()) {
601 0 : case Location::Bottom: top = deltaHeight;
602 0 : break;
603 0 : case Location::Top: bottom = deltaHeight;
604 0 : break;
605 0 : case Location::Left: right = deltaWidth;
606 0 : break;
607 0 : case Location::Right: left = deltaWidth;
608 0 : break;
609 0 : case Location::Center:
610 0 : case Location::Undefined: break;
611 : }
612 :
613 0 : m_dockWidget->resizeInLayout(left, top, right, bottom);
614 0 : }
615 :
616 0 : muse::ui::INavigationSection* DockBase::navigationSection() const
617 : {
618 0 : return m_navigationSection;
619 : }
620 :
621 0 : muse::ui::NavigationSection* DockBase::navigationSection_property() const
622 : {
623 0 : return m_navigationSection;
624 : }
625 :
626 0 : int DockBase::contentNavigationPanelOrderStart() const
627 : {
628 0 : return m_contentNavigationPanelOrderStart;
629 : }
630 :
631 12 : void DockBase::componentComplete()
632 : {
633 13 : TRACEFUNC;
634 :
635 12 : QQuickItem::componentComplete();
636 :
637 12 : auto children = childItems();
638 12 : IF_ASSERT_FAILED_X(children.size() == 1, "Dock must have only one child as its content!") {
639 0 : return;
640 : }
641 :
642 12 : QQuickItem* content = children.first();
643 12 : IF_ASSERT_FAILED(content) {
644 0 : return;
645 : }
646 :
647 12 : const QString name = objectName();
648 :
649 12 : if (content->objectName().isEmpty()) {
650 12 : content->setObjectName(name + "_content");
651 : }
652 :
653 12 : auto* dockWidgetView = new KDDockWidgets::QtQuick::DockWidget(iocContext()->id, name);
654 12 : dockWidgetView->setObjectName(name);
655 12 : m_dockWidget = dockWidgetView->dockWidget();
656 12 : dockWidgetView->setGuestItem(content);
657 12 : m_dockWidget->setTitle(m_title);
658 :
659 12 : writeProperties();
660 12 : setUpFrameConnections();
661 :
662 12 : QQuickItem* dockWidgetItem = KDDockWidgets::QtQuick::asQQuickItem(m_dockWidget);
663 12 : connect(dockWidgetItem, &QQuickItem::widthChanged, this, [this]() {
664 58 : if (m_dockWidget) {
665 58 : setWidth(m_dockWidget->width());
666 58 : if (inited()) {
667 16 : applySizeConstraints();
668 : }
669 : }
670 58 : });
671 :
672 12 : connect(dockWidgetItem, &QQuickItem::heightChanged, this, [this]() {
673 69 : if (m_dockWidget) {
674 69 : setHeight(m_dockWidget->height());
675 69 : if (inited()) {
676 24 : applySizeConstraints();
677 : }
678 : }
679 69 : });
680 :
681 12 : connect(this, &DockBase::minimumSizeChanged, this, &DockBase::applySizeConstraints);
682 12 : connect(this, &DockBase::maximumSizeChanged, this, &DockBase::applySizeConstraints);
683 :
684 12 : m_defaultVisibility = isVisible();
685 12 : }
686 :
687 91 : void DockBase::applySizeConstraints()
688 : {
689 91 : if (!m_dockWidget) {
690 0 : return;
691 : }
692 :
693 92 : TRACEFUNC;
694 :
695 91 : KDDockWidgets::Core::View* dockWidgetView = m_dockWidget->view();
696 :
697 91 : int minWidth = m_minimumWidth > 0 ? m_minimumWidth : dockWidgetView->minSize().width();
698 91 : int minHeight = m_minimumHeight > 0 ? m_minimumHeight : dockWidgetView->minSize().height();
699 91 : int maxWidth = m_maximumWidth > 0 ? m_maximumWidth : dockWidgetView->maxSizeHint().width();
700 91 : int maxHeight = m_maximumHeight > 0 ? m_maximumHeight : dockWidgetView->maxSizeHint().height();
701 :
702 91 : minWidth = std::min(minWidth, maxWidth);
703 91 : minHeight = std::min(minHeight, maxHeight);
704 91 : maxWidth = std::max(maxWidth, minWidth);
705 91 : maxHeight = std::max(maxHeight, minHeight);
706 :
707 91 : QSize minimumSize(minWidth, minHeight);
708 91 : QSize maximumSize(maxWidth, maxHeight);
709 :
710 91 : if (!m_properties.resizable) {
711 0 : maximumSize = minimumSize;
712 : }
713 :
714 91 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
715 :
716 : //! NOTE: Skip applying floating size constraints while the dock's group is still in the main
717 : //! layout - fixes the top row growing taller on undock.
718 91 : if (m_floating && group && group->isInMainWindow()) {
719 0 : return;
720 : }
721 :
722 91 : if (group) {
723 85 : group->view()->setMinimumSize(minimumSize);
724 85 : group->view()->setMaximumSize(maximumSize);
725 : }
726 :
727 91 : dockWidgetView->setMinimumSize(minimumSize);
728 91 : dockWidgetView->setMaximumSize(maximumSize);
729 :
730 91 : if (KDDockWidgets::Core::FloatingWindow* window = m_dockWidget->floatingWindow()) {
731 : //! NOTE: The floating window draws shadow around the dock content (see DockFloatingWindow.qml),
732 : //! so the window must be larger than the content by that shadow on each side.
733 0 : const int shadow = 2 * (DOCK_WINDOW_SHADOW + 1 /*border*/);
734 0 : const QSize windowMinimumSize = minimumSize + QSize(shadow, shadow);
735 0 : const QSize windowMaximumSize = maximumSize + QSize(shadow, shadow);
736 :
737 0 : window->view()->setMinimumSize(windowMinimumSize);
738 0 : window->view()->setMaximumSize(windowMaximumSize);
739 :
740 0 : const QSize winSize = adjustSizeByConstraints(window->view()->geometry().size(), windowMinimumSize, windowMaximumSize);
741 0 : const QRect winRect(window->dragRect().topLeft(), winSize);
742 0 : window->view()->setGeometry(winRect);
743 : }
744 :
745 : //! NOTE: The group derives its minimum size from its dock widgets, so the layout
746 : //! picks up the old minimum when the group constraints are applied
747 91 : syncLayoutItemMinSize();
748 :
749 : //! NOTE: The layout doesn't shrink the item when the maximum size decreases
750 91 : fitSizeIntoMaximum();
751 91 : }
752 :
753 91 : void DockBase::fitSizeIntoMaximum()
754 : {
755 91 : if (!m_dockWidget || !m_inited) {
756 18 : return;
757 : }
758 :
759 73 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
760 73 : if (!group || !group->isInMainWindow()) {
761 0 : return;
762 : }
763 :
764 : //! NOTE: The layout treats the maximum size only as a hint and doesn't shrink the item
765 : //! when it decreases, so shrink it explicitly. The side at the edge of the layout
766 : //! isn't changed, so shrink the opposite side if the excess remains
767 152 : auto excessWidth = [this]() {
768 152 : return m_maximumWidth > 0 ? m_dockWidget->width() - std::max(m_maximumWidth, m_minimumWidth) : 0;
769 73 : };
770 :
771 236 : auto excessHeight = [this]() {
772 236 : return m_maximumHeight > 0 ? m_dockWidget->height() - std::max(m_maximumHeight, m_minimumHeight) : 0;
773 73 : };
774 :
775 : //! NOTE: Left side
776 73 : if (excessWidth() > 0) {
777 5 : m_dockWidget->resizeInLayout(-excessWidth(), 0, 0, 0);
778 : }
779 :
780 : //! NOTE: Right side
781 73 : if (excessWidth() > 0) {
782 1 : m_dockWidget->resizeInLayout(0, 0, -excessWidth(), 0);
783 : }
784 :
785 : //! NOTE: Top side
786 73 : if (excessHeight() > 0) {
787 45 : m_dockWidget->resizeInLayout(0, -excessHeight(), 0, 0);
788 : }
789 :
790 : //! NOTE: Bottom side
791 73 : if (excessHeight() > 0) {
792 45 : m_dockWidget->resizeInLayout(0, 0, 0, -excessHeight());
793 : }
794 : }
795 :
796 100 : void DockBase::syncLayoutItemMinSize()
797 : {
798 100 : if (!m_dockWidget) {
799 18 : return;
800 : }
801 :
802 100 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
803 100 : if (!group) {
804 6 : return;
805 : }
806 :
807 94 : KDDockWidgets::Core::Item* item = group->layoutItem();
808 94 : if (!item) {
809 12 : return;
810 : }
811 :
812 : //! NOTE: The layouting item caches the guest minimum; force it to match the group's current
813 : //! minimum so the container's minimum size reflects
814 82 : const QSize min = group->view()->minSize();
815 82 : if (item->minSize() != min) {
816 9 : item->setMinSize(min);
817 : }
818 : }
819 :
820 12 : void DockBase::setUpFrameConnections()
821 : {
822 12 : IF_ASSERT_FAILED(m_dockWidget) {
823 0 : return;
824 : }
825 :
826 12 : QQuickItem* dockWidgetItem = KDDockWidgets::QtQuick::asQQuickItem(m_dockWidget);
827 12 : IF_ASSERT_FAILED(dockWidgetItem) {
828 0 : return;
829 : }
830 :
831 12 : auto floatingChangedConnection = std::make_shared<QMetaObject::Connection>();
832 12 : auto widgetChangedConnection = std::make_shared<QMetaObject::Connection>();
833 :
834 12 : connect(dockWidgetItem, &QQuickItem::parentChanged, this,
835 24 : [this, floatingChangedConnection, widgetChangedConnection]()
836 : {
837 12 : if (floatingChangedConnection) {
838 12 : disconnect(*floatingChangedConnection);
839 12 : doSetFloating(false);
840 : }
841 :
842 12 : if (widgetChangedConnection) {
843 12 : disconnect(*widgetChangedConnection);
844 : }
845 :
846 12 : QQuickItem* dockWidgetItem = KDDockWidgets::QtQuick::asQQuickItem(m_dockWidget);
847 12 : if (!m_dockWidget || !dockWidgetItem || !dockWidgetItem->parentItem()) {
848 0 : return;
849 : }
850 :
851 12 : KDDockWidgets::Core::Group* group = groupForDockWidget(m_dockWidget);
852 12 : if (!group) {
853 0 : return;
854 : }
855 :
856 : //! NOTE: window will be available later
857 : //! So it is important to apply size constraints
858 : //! and emit floatingChanged() after that
859 12 : QTimer::singleShot(0, this, [this]() {
860 12 : updateFloatingStatus();
861 :
862 12 : if (m_floating) {
863 0 : applySizeConstraints();
864 : }
865 12 : });
866 :
867 12 : KDDockWidgets::QtQuick::Group* groupView = groupViewFor(group);
868 12 : if (groupView) {
869 24 : *floatingChangedConnection = connect(groupView, &QQuickItem::parentChanged,
870 12 : this, &DockBase::onIsInMainWindowChanged,
871 12 : Qt::UniqueConnection);
872 24 : *widgetChangedConnection = connect(groupView, &KDDockWidgets::QtQuick::Group::currentDockWidgetChanged,
873 24 : this, &DockBase::frameCurrentWidgetChanged,
874 12 : Qt::UniqueConnection);
875 : }
876 : });
877 :
878 12 : auto* action = static_cast<KDDockWidgets::QtQuick::Action*>(m_dockWidget->toggleAction());
879 12 : connect(action, &KDDockWidgets::QtQuick::Action::toggled, this, [this]() {
880 12 : if (!isOpen()) {
881 0 : doSetFloating(false);
882 : }
883 12 : });
884 12 : }
885 :
886 36 : void DockBase::updateFloatingStatus()
887 : {
888 36 : bool floating = m_dockWidget && m_dockWidget->floatingWindow();
889 :
890 36 : doSetFloating(floating);
891 36 : }
892 :
893 12 : void DockBase::onIsInMainWindowChanged()
894 : {
895 12 : updateFloatingStatus();
896 12 : applySizeConstraints();
897 12 : }
898 :
899 51 : void DockBase::doSetFloating(bool floating)
900 : {
901 51 : if (m_floating == floating) {
902 48 : return;
903 : }
904 :
905 3 : m_floating = floating;
906 3 : emit floatingChanged();
907 : }
908 :
909 12 : void DockBase::writeProperties()
910 : {
911 12 : if (m_dockWidget) {
912 12 : writePropertiesToObject(m_properties, *m_dockWidget);
913 : }
914 12 : }
915 :
916 12 : void DockBase::setInited(bool inited)
917 : {
918 12 : if (m_inited == inited) {
919 0 : return;
920 : }
921 :
922 12 : m_inited = inited;
923 12 : emit initedChanged();
924 : }
925 :
926 0 : bool DropDestination::operator==(const DropDestination& dest) const
927 : {
928 0 : return dock == dest.dock && dropLocation == dest.dropLocation && dropDistance == dest.dropDistance;
929 : }
930 :
931 0 : bool DropDestination::isValid() const
932 : {
933 0 : return dock != nullptr;
934 : }
935 :
936 0 : void DropDestination::clear()
937 : {
938 0 : dock = nullptr;
939 0 : dropLocation = Location::Undefined;
940 0 : dropDistance = 0;
941 0 : }
|