1
2
3 // TnzQt includes
4 #include "toonzqt/menubarcommand.h"
5 #include "docklayout.h"
6
7 // Qt includes
8 #include <QEvent>
9 #include <QMouseEvent>
10 #include <QApplication>
11 #include <QDesktopWidget>
12
13 // STD includes
14 #include <assert.h>
15 #include <math.h>
16 #include <algorithm>
17
18 //========================================================
19
20 //-----------------------
21 // Dock Lock Check
22 //-----------------------
23
instance()24 DockingCheck *DockingCheck::instance() {
25 static DockingCheck _instance;
26 return &_instance;
27 }
28
29 //-------------------------------------
30
setToggle(QAction * toggle)31 void DockingCheck::setToggle(QAction *toggle) { m_toggle = toggle; }
32
33 //-------------------------------------
34
setIsEnabled(bool on)35 void DockingCheck::setIsEnabled(bool on) {
36 m_enabled = on;
37 if (m_toggle) m_toggle->setChecked(on);
38 }
39
40 //========================================================
41
42 class DockingToggleCommand final : public MenuItemHandler {
43 public:
DockingToggleCommand()44 DockingToggleCommand() : MenuItemHandler("MI_DockingCheck") {}
45
execute()46 void execute() override {
47 DockingCheck *dc = DockingCheck::instance();
48 dc->setIsEnabled(!dc->isEnabled());
49 }
50
51 } dockingToggleCommand;
52
53 //========================================================
54
55 //------------------------
56 // Geometry inlines
57 //------------------------
58
59 //! Calculates infinite-norm distance between points \b a and \b b.
absoluteDistance(const QPoint & a,const QPoint & b)60 inline int absoluteDistance(const QPoint &a, const QPoint &b) {
61 return std::max(abs(a.x() - b.x()), abs(a.y() - b.y()));
62 }
63
64 //-------------------------------------
65
66 // Il metodo QRectF::toRect pare che faccia la cosa commentata. Ovviamente non
67 // e' la cosa piu' simpatica - considera
68 // che in questo modo i bordi del rect di input *non vengono approssimati alle
69 // piu' vicine coordinate intere*!
70 // es: topLeft= (1/3, 1/3); width= 4/3, height= 4/3 => left= top= right=
71 // bottom= 0.
toRect(const QRectF & rect)72 inline QRect toRect(const QRectF &rect) {
73 // return QRect(qRound(rect.left()), qRound(rect.top()), qRound(rect.width()),
74 // qRound(rect.height()));
75 return QRect(rect.topLeft().toPoint(),
76 rect.bottomRight().toPoint() -= QPoint(1, 1));
77 }
78
79 //========================================================
80
81 // Forward declaration
82 namespace {
83 QDesktopWidget *desktop;
84 void getClosestAvailableMousePosition(QPoint &globalPos);
85 }
86
87 //========================================================
88
89 //----------------------
90 // Layout inlines
91 //----------------------
92
update()93 inline void DockLayout::update() {
94 // E' necessario?
95 applyGeometry();
96 }
97
98 //========================================================
99
100 //------------------------------
101 // My Dock Widget Methods
102 //------------------------------
103
104 //! Constructs a dock Widget; every newly constructed dock widget is floating
105 //! (i.e. not docked
106 //! into the layout).
DockWidget(QWidget * parent,Qt::WindowFlags flags)107 DockWidget::DockWidget(QWidget *parent, Qt::WindowFlags flags)
108 : QFrame(parent, flags)
109 , m_dragging(false)
110 , m_resizing(false)
111 , m_floating(true)
112 , m_undocking(false)
113 , m_parentLayout(0)
114 , m_selectedPlace(0)
115 , m_maximized(0) {
116 // Don't let this widget inherit the parent's backround color
117 // setAutoFillBackground(true);
118 // setBackgroundRole(QPalette::Background);
119
120 // setAttribute(Qt::WA_DeleteOnClose); //Since Toonz just hides panels...
121 setAttribute(Qt::WA_Hover);
122
123 // Set default minimum and maximum sizes.
124 setMinimumSize(QSize(50, 50));
125 setMaximumSize(QSize(10000, 10000));
126
127 m_decoAllocator = new DockDecoAllocator;
128
129 // Make sure the desktop is initialized and known
130 desktop = qApp->desktop();
131 }
132
133 //-------------------------------------
134
~DockWidget()135 DockWidget::~DockWidget() {
136 // Since close button lies on the title bar, make sure mouse is released if
137 // that is pressed.
138 if (QWidget::mouseGrabber() == this) releaseMouse();
139 clearDockPlaceholders();
140
141 // Delete deco allocator
142 delete m_decoAllocator;
143 }
144
145 //-------------------------------------
146
147 //! Clears dock placeholders for this dockwidget. This is automatically called
148 //! at drag ends.
clearDockPlaceholders()149 void DockWidget::clearDockPlaceholders() {
150 unsigned int i;
151 for (i = 0; i < m_placeholders.size(); ++i) delete m_placeholders[i];
152 m_placeholders.clear();
153 }
154
155 //-------------------------------------
156
157 //! Shows dock widget in docked mode.
158
159 //! Returns true or false whether input \b QPoint is inside the dragging grip
160 //! for \b this DockWidget, or not.
161 //! Typically (and by default), true is returned whenever p lies inside the
162 //! widget's title bar when floating;
163 //! however, you are free to place the drag grip anywhere reimplementing this
164 //! method in custom dockwidgets.
isDragGrip(QPoint p)165 bool DockWidget::isDragGrip(QPoint p) {
166 if (isFloating()) {
167 QRect frame = frameGeometry();
168 QRect conts = geometry();
169 int margin = conts.left() - frame.left();
170 int titleBarHeight = conts.top() - frame.top();
171
172 QRect titleArea(QPoint(0, margin - titleBarHeight),
173 QPoint(width() - 1, -1));
174
175 return titleArea.contains(p);
176 }
177
178 return 0;
179 }
180
181 //-------------------------------------
182
event(QEvent * e)183 bool DockWidget::event(QEvent *e) {
184 // qDebug("Dock Widget - Event type: %d", e->type());
185
186 // Little deviation or type specifications for received events
187 switch (e->type()) {
188 // Dock widgets are hover widgets - since their cursor may change on resize
189 // grips.
190 case QEvent::HoverMove:
191 hoverMoveEvent(static_cast<QHoverEvent *>(e));
192 return true;
193
194 // Native titlebar press events must be redirected to common ones
195 // - and, in this case mouse has to grabbed
196 case QEvent::NonClientAreaMouseButtonPress:
197 // grabMouse(); //Cannot not go here or resizes cannot be natively handled
198 mousePressEvent(static_cast<QMouseEvent *>(e));
199 return true;
200
201 // Titlebars are not natively handled in this raw class - their responsibility
202 // falls
203 // to user implementation of DockWidget class
204 case QEvent::WindowTitleChange:
205 windowTitleEvent(e);
206 return true;
207
208 default:
209 return QWidget::event(e);
210 }
211 }
212
213 //-------------------------------------
214
215 //! Adjusts widget cursor depending on \b isResizeGrip()
216 // NOTA: Da migliorare: ricordare il cursor settato o se era unset e riapplicare
217 // quando fuori dal resize grip.
hoverMoveEvent(QHoverEvent * he)218 void DockWidget::hoverMoveEvent(QHoverEvent *he) {
219 if (m_floating && !m_resizing && !m_undocking) {
220 QCursor newCursor = Qt::ArrowCursor;
221
222 if ((m_marginType = isResizeGrip(he->pos()))) {
223 // Hovering a margin - update cursor shape
224 if (m_marginType & leftMargin) {
225 if (m_marginType & topMargin)
226 newCursor = Qt::SizeFDiagCursor;
227 else if (m_marginType & bottomMargin)
228 newCursor = Qt::SizeBDiagCursor;
229 else
230 newCursor = Qt::SizeHorCursor;
231 } else if (m_marginType & rightMargin) {
232 if (m_marginType & topMargin)
233 newCursor = Qt::SizeBDiagCursor;
234 else if (m_marginType & bottomMargin)
235 newCursor = Qt::SizeFDiagCursor;
236 else
237 newCursor = Qt::SizeHorCursor;
238 } else
239 newCursor = Qt::SizeVerCursor;
240 }
241
242 if (newCursor.shape() != cursor().shape()) setCursor(newCursor);
243 }
244 }
245
246 //-------------------------------------
247
248 //! Dispatches mouse presses for particular purposes...
249
250 //! a) Trigger a window resize (or none with native deco)
251 //! if press is over a resize grip
252
253 //! b) Trigger a window drag if press is over a drag grip
mousePressEvent(QMouseEvent * me)254 void DockWidget::mousePressEvent(QMouseEvent *me) {
255 if ((m_marginType = m_floating ? isResizeGrip(me->pos()) : 0)) {
256 // Resize begins
257
258 // NOTE: It is better to assume that resize grips dominate over drag grips:
259 // this ensures
260 // that mouse cursor changes are always consistent with resize events.
261
262 m_resizing = true;
263 m_dragMouseInitialPos = me->globalPos(); // Re-used as old position
264 } else if (isDragGrip(me->pos())) {
265 // Dragging begins
266 DockingCheck *lock = DockingCheck::instance(); // Docking system lock
267
268 m_dragMouseInitialPos = me->globalPos();
269 m_dragInitialPos = pos();
270 // Grab mouse inputs - useful if widget gets under placeholders
271 if (me->type() == QEvent::NonClientAreaMouseButtonPress)
272 // If can receive double-clicks, avoid grabbing mouse
273 grabMouse();
274
275 if (m_floating) {
276 m_dragging = true;
277 // Do not allow docking if there is a maximized widget or the layout is
278 // locked
279 if (m_parentLayout && !m_parentLayout->getMaximized() &&
280 !lock->isEnabled())
281 m_parentLayout->calculateDockPlaceholders(this);
282 } else {
283 if (!lock->isEnabled()) m_undocking = true;
284 m_dragInitialPos = parentWidget()->mapToGlobal(m_dragInitialPos);
285 }
286 }
287 }
288
289 //-------------------------------------
290
mouseMoveEvent(QMouseEvent * me)291 void DockWidget::mouseMoveEvent(QMouseEvent *me) {
292 QPoint correctedGlobalPos(me->globalPos());
293 getClosestAvailableMousePosition(correctedGlobalPos);
294
295 if (m_resizing) {
296 // m_dragMouseInitialPos re-used as old position
297 int dx = correctedGlobalPos.x() - m_dragMouseInitialPos.x();
298 int dy = correctedGlobalPos.y() - m_dragMouseInitialPos.y();
299
300 QRect geom = geometry();
301
302 if (m_marginType & leftMargin) {
303 int newWidth = geom.width() - dx;
304 if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
305 geom.setLeft(geom.left() + dx);
306 } else if (m_marginType & rightMargin)
307 geom.setRight(geom.right() + dx);
308
309 if (m_marginType & topMargin) {
310 int newHeight = geom.height() - dy;
311 if (newHeight >= minimumHeight() && newHeight <= maximumHeight())
312 geom.setTop(geom.top() + dy);
313 } else if (m_marginType & bottomMargin)
314 geom.setBottom(geom.bottom() + dy);
315
316 setGeometry(geom);
317
318 m_dragMouseInitialPos = correctedGlobalPos;
319 } else if (m_dragging) {
320 move(m_dragInitialPos + correctedGlobalPos - m_dragMouseInitialPos);
321 selectDockPlaceholder(me);
322 } else if (m_undocking) {
323 int distance = absoluteDistance(me->globalPos(), m_dragMouseInitialPos);
324 if (distance > 8) {
325 m_undocking = false;
326
327 // Attempt undocking
328 if (m_parentLayout->undockItem(this)) {
329 m_dragging = true;
330
331 // Then, move dock widget under cursor, as if drag actually begun at
332 // button press.
333 move(m_dragInitialPos + correctedGlobalPos - m_dragMouseInitialPos);
334 show(); // Dock widget is not automatically shown after undock.
335
336 // Re-grab mouse inputs. Seems that making the window float (i.e.:
337 // reparenting) breaks old grab.
338 // NOTE: mouse *must* be grabbed only when visible - see Qt manual.
339 grabMouse();
340
341 // After undocking takes place, docking possibilities have to be
342 // recalculated
343 m_parentLayout->calculateDockPlaceholders(this);
344 selectDockPlaceholder(me);
345 }
346 }
347 }
348 }
349
350 //-------------------------------------
351
mouseReleaseEvent(QMouseEvent * me)352 void DockWidget::mouseReleaseEvent(QMouseEvent *me) {
353 // Ensure mouse is released
354 releaseMouse();
355
356 if (m_dragging) {
357 m_dragging = false;
358
359 if (m_floating && m_selectedPlace) {
360 m_parentLayout->dockItem(this, m_selectedPlace);
361 } else {
362 // qDebug("Dock failed");
363 }
364
365 // Clear dock placeholders
366 clearDockPlaceholders();
367 m_selectedPlace = 0;
368 } else if (m_undocking) {
369 // qDebug("Undock failed");
370 m_undocking = false;
371 } else if (m_resizing) {
372 m_resizing = false;
373 }
374 }
375
376 //-------------------------------------
377
378 //! DockWidgets respond to title bar double clicks maximizing the widget in
379 //! layout's contents rect.
mouseDoubleClickEvent(QMouseEvent * me)380 void DockWidget::mouseDoubleClickEvent(QMouseEvent *me) {
381 if (!m_floating && isDragGrip(me->pos())) {
382 parentLayout()->setMaximized(this, !m_maximized);
383 }
384 }
385
386 //-------------------------------------
387
maximizeDock()388 void DockWidget::maximizeDock() {
389 if (!m_floating) {
390 parentLayout()->setMaximized(this, !m_maximized);
391 }
392 }
393
394 //-------------------------------------
395 //! Switch in selected dock placeholder's hierarchy.
wheelEvent(QWheelEvent * we)396 void DockWidget::wheelEvent(QWheelEvent *we) {
397 if (m_dragging) {
398 if (m_selectedPlace) {
399 DockPlaceholder *newSelected =
400 (we->delta() > 0)
401 ? m_selectedPlace->parentPlaceholder()
402 : m_selectedPlace->childPlaceholder(
403 parentWidget()->mapFromGlobal(we->globalPos()));
404
405 if (newSelected != m_selectedPlace) {
406 m_selectedPlace->hide();
407 newSelected->show();
408 m_selectedPlace = newSelected;
409 }
410 }
411 }
412 }
413
414 //-------------------------------------
415
416 //! Returns widget (separator or docked widget) containing input \point, or 0 if
417 //! none.
418 //! Convenience function combining parentLayout() and containerOf(\b point).
419
420 //!\b NOTE: Observe that, in any case, the use of QEnterEvents is discouraged
421 //! for this purpose:
422 //! remember that we forcedly remain within its boundaries when dragging a dock
423 //! widget;
424 //! instead, we are rather interested about entering the dock widgets *below*.
hoveredWidget(QMouseEvent * me)425 QWidget *DockWidget::hoveredWidget(QMouseEvent *me) {
426 if (!m_parentLayout) return 0;
427
428 QPoint point = parentWidget()->mapFromGlobal(me->globalPos());
429 return m_parentLayout->containerOf(point);
430 }
431
432 //-------------------------------------
433
434 //! Returns adjacent placeholders to hovered widget. If hovered is a separator,
435 //! just return associated placeholder.
placeAdjacentTo(DockWidget * dockWidget,int boundary)436 DockPlaceholder *DockWidget::placeAdjacentTo(DockWidget *dockWidget,
437 int boundary) {
438 Region *r = parentLayout()->find(dockWidget);
439
440 if (((boundary == DockPlaceholder::left ||
441 boundary == DockPlaceholder::right) &&
442 r->getOrientation() == Region::horizontal) ||
443 ((boundary == DockPlaceholder::top ||
444 boundary == DockPlaceholder::bottom) &&
445 r->getOrientation() == Region::vertical)) {
446 // Placeholder is coherent with region orientation
447 return r->placeholders().size() ? r->placeholder(boundary % 2) : 0;
448 } else {
449 // Search in parent region
450 Region *parent = r->getParent();
451 if (parent) {
452 unsigned int i = parent->find(r);
453 return parent->placeholders().size()
454 ? parent->placeholder(i + (boundary % 2))
455 : 0;
456 } else {
457 // No parent region - dockWidget is the only widget of the whole layout;
458 // Then check the first 2 elements of placeholders vector.
459 if (!m_placeholders[boundary % 2]->getParentRegion()) {
460 return m_placeholders.size() ? m_placeholders[boundary % 2] : 0;
461 }
462 }
463 }
464
465 return 0;
466 }
467
468 //-------------------------------------
469
470 //! Returns placeholder associated with input separator.
placeOfSeparator(DockSeparator * sep)471 DockPlaceholder *DockWidget::placeOfSeparator(DockSeparator *sep) {
472 Region *r = sep->getParentRegion();
473 int idx = sep->getIndex();
474
475 return r->placeholders().size() ? r->placeholder(idx + 1) : 0;
476 }
477
478 //-------------------------------------
479
480 //! Processes an input mouse event to select the active placeholder among the
481 //! possible ones
482 //! for \b this dock widget. The selected placeholder is also evidenced with
483 //! respect to the
484 //! other (or these are kept hidden) according to the body of this function.
selectDockPlaceholder(QMouseEvent * me)485 void DockWidget::selectDockPlaceholder(QMouseEvent *me) {
486 // const int inf= 1000000;
487 DockPlaceholder *selected = 0;
488
489 // Search placeholders cotaining muose position
490 unsigned int i;
491 for (i = 0; i < m_placeholders.size(); ++i) {
492 if (m_placeholders[i]->geometry().contains(me->globalPos())) {
493 selected = m_placeholders[i];
494 }
495 }
496
497 // In order to avoid flickering
498 if (m_selectedPlace != selected) {
499 if (m_selectedPlace) m_selectedPlace->hide();
500 if (selected) selected->show();
501 }
502
503 m_selectedPlace = selected;
504 }
505
506 //========================================================
507
508 //-------------------------
509 // Dock Placeholders
510 //-------------------------
511
parentGeometry() const512 inline QRect DockPlaceholder::parentGeometry() const {
513 return m_region ? toRect(m_region->getGeometry())
514 : m_owner->parentLayout()->contentsRect();
515 }
516
517 //------------------------------------------------------
518
519 //! Assigns the geometry of \b this placeholder.
520
521 //! Once a placeholder is created, in response to a user window drag,
522 //! dock placeholders are calculated. After a placeholder is created,
523 //! its geometry is built according to this function. It is possible to
524 //! reimplement it in order to build custom placeholder styles.
buildGeometry()525 inline void DockPlaceholder::buildGeometry() {
526 QRect relativeToMainRect;
527
528 if (m_separator)
529 relativeToMainRect = m_separator->geometry();
530 else {
531 QRect parentRect = parentGeometry();
532 DockLayout *layout = m_owner->parentLayout();
533 QRect mainRect = layout->contentsRect();
534 int sepWidth = layout->spacing();
535 int margin = 6; // layout->margin(); //Purtroppo questa info e' assegnata
536 // prima delle Room...
537
538 if (isRoot()) {
539 // Set the whole contents rect
540 relativeToMainRect = parentRect;
541
542 // Set a square at middle of parent geometry
543 // QPoint center= parentRect.center();
544 // relativeToMainRect= QRect(center - QPoint(50,50), center +
545 // QPoint(50,50));
546 } else if (getParentRegion() == 0 ||
547 getParentRegion() == layout->rootRegion()) {
548 // Outer insertion case
549 switch (getAttribute()) {
550 int leftBound, upperBound;
551
552 case left:
553 leftBound = parentRect.left() - margin;
554 relativeToMainRect =
555 QRect(leftBound, parentRect.top(), margin, parentRect.height());
556 break;
557 case right:
558 leftBound = parentRect.right() + 1;
559 relativeToMainRect =
560 QRect(leftBound, parentRect.top(), margin, parentRect.height());
561 break;
562 case top:
563 upperBound = parentRect.top() - margin;
564 relativeToMainRect =
565 QRect(parentRect.left(), upperBound, parentRect.width(), margin);
566 break;
567 default:
568 upperBound = parentRect.bottom() + 1;
569 relativeToMainRect =
570 QRect(parentRect.left(), upperBound, parentRect.width(), margin);
571 break;
572 }
573 } else
574 switch (getAttribute()) {
575 int leftBound, upperBound;
576
577 case left:
578 leftBound = parentRect.left();
579 relativeToMainRect =
580 QRect(leftBound, parentRect.top(), sepWidth, parentRect.height());
581 break;
582 case right:
583 leftBound = parentRect.right() - sepWidth + 1;
584 relativeToMainRect =
585 QRect(leftBound, parentRect.top(), sepWidth, parentRect.height());
586 break;
587 case top:
588 upperBound = parentRect.top();
589 relativeToMainRect =
590 QRect(parentRect.left(), upperBound, parentRect.width(), sepWidth);
591 break;
592 default:
593 upperBound = parentRect.bottom() - sepWidth + 1;
594 relativeToMainRect =
595 QRect(parentRect.left(), upperBound, parentRect.width(), sepWidth);
596 break;
597 }
598 }
599
600 QPoint topLeft =
601 m_owner->parentWidget()->mapToGlobal(relativeToMainRect.topLeft());
602 QPoint bottomRight =
603 m_owner->parentWidget()->mapToGlobal(relativeToMainRect.bottomRight());
604 setGeometry(QRect(topLeft, bottomRight));
605 }
606
607 //------------------------------------------------------
608
DockPlaceholder(DockWidget * owner,Region * r,int idx,int attributes)609 DockPlaceholder::DockPlaceholder(DockWidget *owner, Region *r, int idx,
610 int attributes)
611 : QWidget(owner)
612 , m_owner(owner)
613 , m_separator(0)
614 , m_region(r)
615 , m_idx(idx)
616 , m_attributes(attributes) {
617 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
618
619 // Set separators
620 if (r && idx && idx < (int)r->getChildList().size()) {
621 m_separator = r->separators()[idx - 1];
622 }
623 }
624
625 //------------------------------------------------------
626
getSeparator() const627 inline DockSeparator *DockPlaceholder::getSeparator() const {
628 return m_separator;
629 }
630
631 //------------------------------------------------------
632
633 //! Find the 'parent' of this placeholder (same side/orientation, on grandParent
634 //! region)
635
636 //! NOTE: If no grandParent exists \b this is the root, which is then returned.
parentPlaceholder()637 DockPlaceholder *DockPlaceholder::parentPlaceholder() {
638 // Placeholders covering a whole separator or roots have no parent (return
639 // itself)
640 if (m_attributes == sepHor || m_attributes == sepVert || isRoot())
641 return this;
642
643 // Now, check if owner Region has a parent.
644 Region *grandParent;
645 if (!m_region || !m_region->getParent()) return this;
646
647 if ((grandParent = m_region->getParent()->getParent())) {
648 // Good, we finally have to search in grandParent's region our direct
649 // parent.
650 unsigned int idx = grandParent->find(m_region->getParent());
651
652 // Since placeholders are built ordered, just use the found idx.
653 if (m_attributes == left || m_attributes == top)
654 return grandParent->placeholders().size() ? grandParent->placeholder(idx)
655 : this;
656 else
657 return grandParent->placeholders().size()
658 ? grandParent->placeholder(idx + 1)
659 : this;
660 } else {
661 // GrandParent would be a new root. Then, take the first two possible
662 // placeholders
663 // of the entire dockWidget.
664 if (m_owner->m_placeholders.size()) {
665 DockPlaceholder *result = m_owner->m_placeholders[m_attributes % 2];
666 if (!result->m_region) return result;
667 }
668
669 return this;
670 }
671 }
672
673 //------------------------------------------------------
674
675 //! The opposite of parentPlaceholder() - but a point belonging to the child
676 //! region to be selected is requested as univoque key among all children.
childPlaceholder(QPoint p)677 DockPlaceholder *DockPlaceholder::childPlaceholder(QPoint p) {
678 // Ensure this is not a root placeholder
679 if (m_attributes == root) return this;
680
681 // Define children list and ensure it is not void.
682 Region *r;
683 unsigned int i;
684 bool lastExtremity;
685
686 if (m_region) {
687 if (!m_region->getChildList().size()) return this;
688
689 // Search the subregion containing p
690 for (i = 0; i < m_region->getChildList().size(); ++i)
691 if (m_region->childRegion(i)->getGeometry().contains(p)) break;
692
693 if (i == m_region->getChildList().size())
694 return this; // No subregion found...
695
696 lastExtremity = (m_idx > (int)i);
697
698 // Ensure it has subRegions.
699 r = m_region->childRegion(i);
700 if (!r->getChildList().size()) return this;
701 } else {
702 r = m_owner->parentLayout()->rootRegion();
703 lastExtremity = m_attributes % 2;
704 }
705
706 // Now, take the 'grandson' region as above.
707 for (i = 0; i < r->getChildList().size(); ++i)
708 if (r->childRegion(i)->getGeometry().contains(p)) break;
709
710 if (i == r->getChildList().size()) return this; // No subregion found...
711
712 r = r->childRegion(i);
713
714 // Finally, return child placeholder found.
715 return r->placeholders().size()
716 ? lastExtremity ? r->placeholders().back()
717 : r->placeholders().front()
718 : this;
719 }
720
721 //------------------------------------------------------
722
723 //! Returns greatest placeholder which contains this one.
greatestPlaceholder()724 inline DockPlaceholder *DockPlaceholder::greatestPlaceholder() {
725 DockPlaceholder *old = this, *current = parentPlaceholder();
726
727 while (current != old) {
728 old = current;
729 current = old->parentPlaceholder();
730 }
731
732 return current;
733 }
734
735 //------------------------------------------------------
736
737 //! Returns smallest placeholder contained by this one - a point belonging to
738 //! the child
739 //! region to be selected is requested as univoque key among all children.
smallestPlaceholder(QPoint p)740 inline DockPlaceholder *DockPlaceholder::smallestPlaceholder(QPoint p) {
741 DockPlaceholder *old = this, *current = childPlaceholder(p);
742
743 while (current != old) {
744 old = current;
745 current = old->childPlaceholder(p);
746 }
747
748 return current;
749 }
750
751 //========================================================================
752
753 //------------------
754 // Separators
755 //------------------
756
DockSeparator(DockLayout * owner,bool orientation,Region * parentRegion)757 DockSeparator::DockSeparator(DockLayout *owner, bool orientation,
758 Region *parentRegion)
759 : QWidget(owner->parentWidget())
760 , m_owner(owner)
761 , m_orientation(orientation)
762 , m_parentRegion(parentRegion)
763 , m_pressed(false) {
764 setObjectName("DockSeparator");
765 setWindowFlags(Qt::SubWindow);
766 setAutoFillBackground(false);
767
768 // Set appropriate widget cursor
769 if (m_orientation == Region::horizontal)
770 setCursor(Qt::SplitHCursor);
771 else
772 setCursor(Qt::SplitVCursor);
773
774 show();
775 }
776
777 //-------------------------------------
778
mousePressEvent(QMouseEvent * me)779 void DockSeparator::mousePressEvent(QMouseEvent *me) {
780 m_pressed = true;
781
782 m_oldPos = me->globalPos();
783
784 const std::deque<DockSeparator *> &sepList = m_parentRegion->separators();
785 const std::deque<Region *> &childList = m_parentRegion->getChildList();
786
787 // Find separated Regions (separator index)
788 unsigned int i;
789
790 for (i = 0; i < sepList.size(); ++i)
791 if (sepList[i] == this) break;
792
793 // Calculate bounds for separator shift
794 // First ensure extremal sizes are recalculated
795 m_parentRegion->calculateExtremalSizes();
796
797 int sepWidth = m_owner->spacing();
798 Region *r = getParentRegion();
799 double parentLeft, parentRight;
800 int leftSepCount = m_index;
801 int rightSepCount =
802 r->separators().size() - leftSepCount; // This sep included
803
804 if (m_orientation == Region::horizontal) {
805 parentLeft = r->getGeometry().left();
806 parentRight = r->getGeometry().right();
807 } else {
808 parentLeft = r->getGeometry().top();
809 parentRight = r->getGeometry().bottom();
810 }
811
812 // Calculate left and right extremal sizes
813 int j, leftMinSize = 0, rightMinSize = 0, leftMaxSize = 0, rightMaxSize = 0;
814
815 for (j = 0; j <= m_index; ++j) {
816 leftMinSize += r->childRegion(j)->getMinimumSize(m_orientation);
817 leftMaxSize += r->childRegion(j)->getMaximumSize(m_orientation);
818 }
819
820 int size = r->getChildList().size();
821 for (j = m_index + 1; j < size; ++j) {
822 rightMinSize += r->childRegion(j)->getMinimumSize(m_orientation);
823 rightMaxSize += r->childRegion(j)->getMaximumSize(m_orientation);
824 }
825
826 // Sull'intera regione padre
827 m_leftBound = std::max(parentLeft + leftMinSize + leftSepCount * sepWidth,
828 parentRight - rightMaxSize - rightSepCount * sepWidth);
829
830 m_rightBound =
831 std::min(parentLeft + leftMaxSize + leftSepCount * sepWidth,
832 parentRight - rightMinSize - rightSepCount * sepWidth);
833 }
834
835 //-------------------------------------
836
mouseReleaseEvent(QMouseEvent * me)837 inline void DockSeparator::mouseReleaseEvent(QMouseEvent *me) {
838 m_pressed = false;
839 }
840
841 //-------------------------------------
842
mouseMoveEvent(QMouseEvent * me)843 void DockSeparator::mouseMoveEvent(QMouseEvent *me) {
844 if (m_pressed) {
845 double movedPosition, newPosition;
846
847 if (m_orientation == Region::horizontal) {
848 double dx = me->globalX() - m_oldPos.x();
849 movedPosition =
850 getParentRegion()->childRegion(m_index)->getGeometry().right() + dx;
851 newPosition =
852 std::min(std::max(movedPosition, m_leftBound), m_rightBound);
853 dx += newPosition - movedPosition;
854
855 if (dx) {
856 Region *r;
857 QRectF newGeometry;
858 double newWidth, dxTemp = dx;
859 int i;
860
861 // Propagate dx in left-adjacent regions
862 for (i = m_index; dx != 0 && i >= 0; --i) {
863 r = getParentRegion()->childRegion(i);
864 newGeometry = r->getGeometry();
865
866 // Right margin is shifted by dx
867 newGeometry.adjust(0, 0, dx, 0);
868 newWidth = newGeometry.width();
869 // New width absorbs part of dx according to constraints
870 newWidth = std::min(
871 std::max(newWidth, (double)r->getMinimumSize(Region::horizontal)),
872 (double)r->getMaximumSize(Region::horizontal));
873 newGeometry.adjust(dx = newGeometry.width() - newWidth, 0, 0, 0);
874 r->setGeometry(newGeometry);
875 r->redistribute();
876 }
877
878 dx = dxTemp;
879 int size = getParentRegion()->getChildList().size();
880 // Propagate dx in right-adjacent regions
881 for (i = m_index + 1; dx != 0 && i < size; ++i) {
882 r = getParentRegion()->childRegion(i);
883 newGeometry = r->getGeometry();
884
885 newGeometry.adjust(dx, 0, 0, 0);
886 newWidth = newGeometry.width();
887 newWidth = std::min(
888 std::max(newWidth, (double)r->getMinimumSize(Region::horizontal)),
889 (double)r->getMaximumSize(Region::horizontal));
890 newGeometry.adjust(0, 0, dx = newWidth - newGeometry.width(), 0);
891 r->setGeometry(newGeometry);
892 r->redistribute();
893 }
894
895 m_owner->update();
896 }
897 } else {
898 double dy = me->globalY() - m_oldPos.y();
899 movedPosition =
900 getParentRegion()->childRegion(m_index)->getGeometry().bottom() + dy;
901 newPosition =
902 std::min(std::max(movedPosition, m_leftBound), m_rightBound);
903 dy += newPosition - movedPosition;
904
905 if (dy) {
906 Region *r;
907 QRectF newGeometry;
908 double newHeight, dyTemp = dy;
909 int i;
910
911 for (i = m_index; dy != 0 && i >= 0; --i) {
912 r = getParentRegion()->childRegion(i);
913 newGeometry = r->getGeometry();
914 QRectF oldGeometry = newGeometry;
915
916 newGeometry.adjust(0, 0, 0, dy);
917 newHeight = newGeometry.height();
918 newHeight = std::min(
919 std::max(newHeight, (double)r->getMinimumSize(Region::vertical)),
920 (double)r->getMaximumSize(Region::vertical));
921 newGeometry.adjust(0, dy = newGeometry.height() - newHeight, 0, 0);
922 r->setGeometry(newGeometry);
923 r->redistribute();
924 }
925
926 dy = dyTemp;
927 int size = getParentRegion()->getChildList().size();
928 for (i = m_index + 1; dy != 0 && i < size; ++i) {
929 r = getParentRegion()->childRegion(i);
930 newGeometry = r->getGeometry();
931
932 newGeometry.adjust(0, dy, 0, 0);
933 newHeight = newGeometry.height();
934 newHeight = std::min(
935 std::max(newHeight, (double)r->getMinimumSize(Region::vertical)),
936 (double)r->getMaximumSize(Region::vertical));
937 newGeometry.adjust(0, 0, 0, dy = newHeight - newGeometry.height());
938 r->setGeometry(newGeometry);
939 r->redistribute();
940 }
941
942 m_owner->update();
943 }
944 }
945
946 m_oldPos = QPoint(me->globalX(), me->globalY());
947 }
948 }
949
950 //========================================================
951
952 //----------------------------------
953 // Available geometries code
954 //----------------------------------
955
956 namespace {
957
958 //-------------------------------------
959
960 // Finds the closest mouse point belonging to some available geometry.
getClosestAvailableMousePosition(QPoint & globalPos)961 void getClosestAvailableMousePosition(QPoint &globalPos) {
962 // Search the screen rect containing the mouse position
963 int i, screens = desktop->numScreens();
964 for (i = 0; i < screens; ++i)
965 if (desktop->screenGeometry(i).contains(globalPos)) break;
966
967 // Find the closest point to the corresponding available region
968 QRect rect(desktop->availableGeometry(i));
969 if (rect.contains(globalPos)) return;
970
971 // Return the closest point to the available geometry
972 QPoint result;
973 if (globalPos.x() < rect.left())
974 globalPos.setX(rect.left());
975 else if (globalPos.x() > rect.right())
976 globalPos.setX(rect.right());
977
978 if (globalPos.y() < rect.top())
979 globalPos.setY(rect.top());
980 else if (globalPos.y() > rect.bottom())
981 globalPos.setY(rect.bottom());
982 }
983
984 } // Local namespace
985