FreeCAD

Форк
0
/
OverlayWidgets.cpp 
2532 строки · 79.0 Кб
1
/****************************************************************************
2
 *   Copyright (c) 2020 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
3
 *                                                                          *
4
 *   This file is part of the FreeCAD CAx development system.               *
5
 *                                                                          *
6
 *   This library is free software; you can redistribute it and/or          *
7
 *   modify it under the terms of the GNU Library General Public            *
8
 *   License as published by the Free Software Foundation; either           *
9
 *   version 2 of the License, or (at your option) any later version.       *
10
 *                                                                          *
11
 *   This library  is distributed in the hope that it will be useful,       *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14
 *   GNU Library General Public License for more details.                   *
15
 *                                                                          *
16
 *   You should have received a copy of the GNU Library General Public      *
17
 *   License along with this library; see the file COPYING.LIB. If not,     *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,          *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                 *
20
 *                                                                          *
21
 ****************************************************************************/
22

23
#include "PreCompiled.h"
24

25
#ifndef _PreComp_
26
# include <QAction>
27
# include <QApplication>
28
# include <QBoxLayout>
29
# include <QComboBox>
30
# include <QDockWidget>
31
# include <QHeaderView>
32
# include <QKeyEvent>
33
# include <QMdiArea>
34
# include <QMenu>
35
# include <QPainter>
36
# include <QPointer>
37
# include <QSpacerItem>
38
# include <QSplitter>
39
# include <QTabBar>
40
# include <QTextStream>
41
# include <QTimerEvent>
42
# include <QToolTip>
43
# include <QTreeView>
44
# include <QScrollBar>
45
#endif
46

47
#include <QPainterPath>
48
#include <QPropertyAnimation>
49

50
#include <array>
51
#include <unordered_map>
52

53
#include "OverlayWidgets.h"
54

55
#include <Base/Tools.h>
56
#include <Base/Console.h>
57
#include <App/Application.h>
58
#include "Application.h"
59
#include "BitmapFactory.h"
60
#include "Clipping.h"
61
#include "ComboView.h"
62
#include "Command.h"
63
#include "Control.h"
64
#include "MainWindow.h"
65
#include "MDIView.h"
66
#include "NaviCube.h"
67
#include "OverlayManager.h"
68
#include "OverlayParams.h"
69
#include "TaskView/TaskView.h"
70
#include "Tree.h"
71
#include "TreeParams.h"
72
#include "propertyeditor/PropertyEditor.h"
73

74
FC_LOG_LEVEL_INIT("Dock", true, true);
75

76
using namespace Gui;
77

78
OverlayDragFrame *OverlayTabWidget::_DragFrame;
79
QDockWidget *OverlayTabWidget::_DragFloating;
80
QWidget *OverlayTabWidget::_Dragging;
81
OverlayTabWidget *OverlayTabWidget::_LeftOverlay;
82
OverlayTabWidget *OverlayTabWidget::_RightOverlay;
83
OverlayTabWidget *OverlayTabWidget::_TopOverlay;
84
OverlayTabWidget *OverlayTabWidget::_BottomOverlay;
85

86
static inline int widgetMinSize(const QWidget *widget, bool margin=false)
87
{
88
    return widget->fontMetrics().ascent()
89
        + widget->fontMetrics().descent() + (margin?4:0);
90
}
91

92
// -----------------------------------------------------------
93

94
OverlayProxyWidget::OverlayProxyWidget(OverlayTabWidget *tabOverlay)
95
    :QWidget(tabOverlay->parentWidget()), owner(tabOverlay), _hintColor(QColor(50,50,50,150))
96
{
97
    dockArea = owner->getDockArea();
98
    timer.setSingleShot(true);
99
    QObject::connect(&timer, &QTimer::timeout, this, &OverlayProxyWidget::onTimer);
100
    setAttribute(Qt::WA_TransparentForMouseEvents, true);
101
}
102

103
bool OverlayProxyWidget::isActivated() const
104
{
105
    return drawLine && isVisible();
106
}
107

108
OverlayProxyWidget::HitTest OverlayProxyWidget::hitTest(const QPoint &globalPt, bool delay)
109
{
110
    if (!isVisible() || !owner->count())
111
        return HitTest::HitNone;
112

113
    auto pt = mapFromGlobal(globalPt);
114

115
    QTabBar *tabbar = owner->tabBar();
116
    if (tabbar->isVisible() && tabbar->tabAt(pt)>=0) {
117
        ToolTip::showText(globalPt, QObject::tr("Press ESC to hide hint"), this);
118
        return HitTest::HitOuter;
119
    }
120

121
    HitTest hit = HitTest::HitNone;
122
    QRect rect = this->getRect();
123
    QSize s = this->size();
124
    int hintSize = OverlayParams::getDockOverlayHintTriggerSize();
125
    // if (owner->getState() == OverlayTabWidget::State::HintHidden)
126
    //     hintSize *= 2;
127
    switch(dockArea) {
128
    case Qt::LeftDockWidgetArea:
129
        if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() > 0 && pt.x() < hintSize)
130
            hit = HitTest::HitOuter;
131
        break;
132
    case Qt::RightDockWidgetArea:
133
        if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() < s.width() && pt.x() > -hintSize)
134
            hit = HitTest::HitOuter;
135
        break;
136
    case Qt::TopDockWidgetArea:
137
        if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() > 0 && pt.y() < hintSize)
138
            hit = HitTest::HitOuter;
139
        break;
140
    case Qt::BottomDockWidgetArea:
141
        if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() < s.height() && pt.y() > -hintSize)
142
            hit = HitTest::HitOuter;
143
        break;
144
    }
145
    if (rect.contains(pt)) {
146
        hit = HitTest::HitInner;
147
        ToolTip::showText(globalPt, QObject::tr("Press ESC to hide hint"), this);
148
    } else if (drawLine)
149
        ToolTip::hideText();
150

151
    if (owner->getState() == OverlayTabWidget::State::HintHidden) {
152
        if (hit == HitTest::HitNone)
153
            owner->setState(OverlayTabWidget::State::Normal);
154
        else {
155
            hit = HitTest::HitNone;
156
            ToolTip::hideText();
157
        }
158
    }
159
    if (hit != HitTest::HitNone) {
160
        if (drawLine)
161
            timer.stop();
162
        else if (delay) {
163
            if (!timer.isActive())
164
                timer.start(OverlayParams::getDockOverlayHintDelay());
165
            return hit;
166
        } else {
167
            timer.stop();
168
            owner->setState(OverlayTabWidget::State::Hint);
169
            drawLine = true;
170
            update();
171
        }
172
        if(owner->getState() != OverlayTabWidget::State::Hidden
173
                && hit == HitTest::HitOuter
174
                && OverlayParams::getDockOverlayActivateOnHover()) {
175
            if (owner->isVisible() && owner->tabBar()->isVisible()) {
176
                QSize size = owner->tabBar()->size();
177
                QPoint pt = owner->tabBar()->mapToGlobal(
178
                                QPoint(size.width(), size.height()));
179
                QPoint pos = QCursor::pos();
180
                switch(this->dockArea) {
181
                case Qt::LeftDockWidgetArea:
182
                case Qt::RightDockWidgetArea:
183
                    if (pos.y() < pt.y())
184
                        return HitTest::HitNone;
185
                    break;
186
                case Qt::TopDockWidgetArea:
187
                case Qt::BottomDockWidgetArea:
188
                    if (pos.x() < pt.x())
189
                        return HitTest::HitNone;
190
                    break;
191
                default:
192
                    break;
193
                }
194
            }
195
            owner->setState(OverlayTabWidget::State::Showing);
196
        }
197

198
    } else if (!drawLine) {
199
        timer.stop();
200
    } else if (delay) {
201
        if (!timer.isActive())
202
            timer.start(OverlayParams::getDockOverlayHintDelay());
203
    } else {
204
        timer.stop();
205
        owner->setState(OverlayTabWidget::State::Normal);
206
        drawLine = false;
207
        ToolTip::hideText();
208
        update();
209
    }
210
    return hit;
211
}
212

213
void OverlayProxyWidget::onTimer()
214
{
215
    hitTest(QCursor::pos(), false);
216
}
217

218
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
219
void OverlayProxyWidget::enterEvent(QEvent *)
220
#else
221
void OverlayProxyWidget::enterEvent(QEnterEvent *)
222
#endif
223
{
224
    if(!owner->count())
225
        return;
226

227
    if (!drawLine) {
228
        if (!timer.isActive())
229
            timer.start(OverlayParams::getDockOverlayHintDelay());
230
    }
231
}
232

233
void OverlayProxyWidget::hideEvent(QHideEvent *)
234
{
235
    drawLine = false;
236
}
237

238
void OverlayProxyWidget::onMousePress()
239
{
240
    if(!owner->count())
241
        return;
242

243
    if (owner->getState() == OverlayTabWidget::State::HintHidden)
244
        return;
245

246
    owner->setState(OverlayTabWidget::State::Showing);
247
}
248

249
QBrush OverlayProxyWidget::hintColor() const
250
{
251
    return _hintColor;
252
}
253

254
void OverlayProxyWidget::setHintColor(const QBrush &brush)
255
{
256
    _hintColor = brush;
257
}
258

259
QRect OverlayProxyWidget::getRect() const
260
{
261
    QRect rect = this->rect();
262
    if (owner->isVisible() && owner->tabBar()->isVisible()) {
263
        QSize size = owner->tabBar()->size();
264
        QPoint pt = owner->tabBar()->mapToGlobal(
265
                        QPoint(size.width(), size.height()));
266
        pt = this->mapFromGlobal(pt);
267
        switch(this->dockArea) {
268
            case Qt::LeftDockWidgetArea:
269
            case Qt::RightDockWidgetArea:
270
                rect.setTop(pt.y());
271
                break;
272
            case Qt::TopDockWidgetArea:
273
            case Qt::BottomDockWidgetArea:
274
                rect.setLeft(pt.x());
275
                break;
276
            default:
277
                break;
278
        }
279
    }
280
    switch(this->dockArea) {
281
        case Qt::LeftDockWidgetArea:
282
            if (int offset = OverlayParams::getDockOverlayHintLeftOffset())
283
                rect.moveTop(std::max(rect.top()+offset, rect.bottom()-10));
284
            if (int length = OverlayParams::getDockOverlayHintLeftLength())
285
                 rect.setHeight(std::min(length, rect.height()));
286
            break;
287
        case Qt::RightDockWidgetArea:
288
            if (int offset = OverlayParams::getDockOverlayHintRightOffset())
289
                rect.moveTop(std::max(rect.top()+offset, rect.bottom()-10));
290
            if (int length = OverlayParams::getDockOverlayHintRightLength())
291
                 rect.setHeight(std::min(length, rect.height()));
292
            break;
293
        case Qt::TopDockWidgetArea:
294
            if (int offset = OverlayParams::getDockOverlayHintTopOffset())
295
                rect.moveLeft(std::max(rect.left()+offset, rect.right()-10));
296
            if (int length = OverlayParams::getDockOverlayHintTopLength())
297
                 rect.setWidth(std::min(length, rect.width()));
298
            break;
299
        case Qt::BottomDockWidgetArea:
300
            if (int offset = OverlayParams::getDockOverlayHintBottomOffset())
301
                rect.moveLeft(std::max(rect.left()+offset, rect.right()-10));
302
            if (int length = OverlayParams::getDockOverlayHintBottomLength())
303
                 rect.setWidth(std::min(length, rect.width()));
304
            break;
305
        default:
306
            break;
307
    }
308
    return rect;
309
}
310

311
void OverlayProxyWidget::paintEvent(QPaintEvent *)
312
{
313
    if(!drawLine)
314
        return;
315
    QPainter painter(this);
316
    painter.setOpacity(_hintColor.color().alphaF());
317
    painter.setPen(Qt::transparent);
318
    painter.setBrush(_hintColor);
319

320
    QRect rect = this->getRect();
321
    painter.drawRect(rect);
322
}
323

324
OverlayToolButton::OverlayToolButton(QWidget *parent)
325
    :QToolButton(parent)
326
{
327
    setCursor(Qt::ArrowCursor);
328
}
329

330
// --------------------------------------------------------------------
331

332
OverlayTabWidget::OverlayTabWidget(QWidget *parent, Qt::DockWidgetArea pos)
333
    :QTabWidget(parent), dockArea(pos)
334
{
335
    // This is necessary to capture any focus lost from switching the tab,
336
    // otherwise the lost focus will leak to the parent, i.e. MdiArea, which may
337
    // cause unexpected Mdi sub window switching.
338
    // setFocusPolicy(Qt::StrongFocus);
339

340
    _imageScale = 0.0;
341

342
    splitter = new OverlaySplitter(this);
343

344
    _graphicsEffect = new OverlayGraphicsEffect(splitter);
345
    splitter->setGraphicsEffect(_graphicsEffect);
346

347
    _graphicsEffectTab = new OverlayGraphicsEffect(this);
348
    _graphicsEffectTab->setEnabled(false);
349
    tabBar()->setGraphicsEffect(_graphicsEffectTab);
350

351
    Command *cmdHide = nullptr;
352
    switch(pos) {
353
    case Qt::LeftDockWidgetArea:
354
        _LeftOverlay = this;
355
        setTabPosition(QTabWidget::West);
356
        splitter->setOrientation(Qt::Vertical);
357
        cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleLeft");
358
        break;
359
    case Qt::RightDockWidgetArea:
360
        _RightOverlay = this;
361
        setTabPosition(QTabWidget::East);
362
        splitter->setOrientation(Qt::Vertical);
363
        cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleRight");
364
        break;
365
    case Qt::TopDockWidgetArea:
366
        _TopOverlay = this;
367
        setTabPosition(QTabWidget::North);
368
        splitter->setOrientation(Qt::Horizontal);
369
        cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleTop");
370
        break;
371
    case Qt::BottomDockWidgetArea:
372
        _BottomOverlay = this;
373
        setTabPosition(QTabWidget::South);
374
        splitter->setOrientation(Qt::Horizontal);
375
        cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleBottom");
376
        break;
377
    default:
378
        break;
379
    }
380

381
    proxyWidget = new OverlayProxyWidget(this);
382
    proxyWidget->hide();
383
    _setOverlayMode(proxyWidget,OverlayOption::Enable);
384

385
    setOverlayMode(true);
386
    hide();
387

388
    actTransparent.setCheckable(true);
389
    actTransparent.setData(QStringLiteral("OBTN Transparent"));
390
    actTransparent.setParent(this);
391
    addAction(&actTransparent);
392

393
    actAutoHide.setData(QStringLiteral("OBTN AutoHide"));
394

395
    actEditHide.setData(QStringLiteral("OBTN EditHide"));
396

397
    actEditShow.setData(QStringLiteral("OBTN EditShow"));
398

399
    actTaskShow.setData(QStringLiteral("OBTN TaskShow"));
400

401
    actNoAutoMode.setData(QStringLiteral("OBTN NoAutoMode"));
402

403
    actAutoMode.setData(QStringLiteral("OBTN AutoMode"));
404
    actAutoMode.setParent(this);
405
    autoModeMenu.hide();
406
    autoModeMenu.setToolTipsVisible(true);
407
    autoModeMenu.addAction(&actNoAutoMode);
408
    autoModeMenu.addAction(&actAutoHide);
409
    autoModeMenu.addAction(&actEditShow);
410
    autoModeMenu.addAction(&actEditHide);
411
    autoModeMenu.addAction(&actTaskShow);
412
    addAction(&actAutoMode);
413

414
    actOverlay.setData(QStringLiteral("OBTN Overlay"));
415
    actOverlay.setParent(this);
416
    addAction(&actOverlay);
417

418
    if (cmdHide)
419
        cmdHide->addTo(this);
420

421
    retranslate();
422
    refreshIcons();
423

424
    connect(tabBar(), &QTabBar::tabBarClicked, this, &OverlayTabWidget::onCurrentChanged);
425
    connect(tabBar(), &QTabBar::tabMoved, this, &OverlayTabWidget::onTabMoved);
426
    tabBar()->installEventFilter(this);
427

428
    timer.setSingleShot(true);
429
    connect(&timer, &QTimer::timeout, this, &OverlayTabWidget::setupLayout);
430

431
    repaintTimer.setSingleShot(true);
432
    connect(&repaintTimer, &QTimer::timeout, this, &OverlayTabWidget::onRepaint);
433

434
    _animator = new QPropertyAnimation(this, "animation", this);
435
    _animator->setStartValue(0.0);
436
    _animator->setEndValue(1.0);
437
    connect(_animator, &QAbstractAnimation::stateChanged,
438
            this, &OverlayTabWidget::onAnimationStateChanged);
439
}
440

441
void OverlayTabWidget::refreshIcons()
442
{
443
    actOverlay.setIcon(BitmapFactory().pixmap("qss:overlay/overlay.svg"));
444
    actNoAutoMode.setIcon(BitmapFactory().pixmap("qss:overlay/mode.svg"));
445
    actTaskShow.setIcon(BitmapFactory().pixmap("qss:overlay/taskshow.svg"));
446
    actEditShow.setIcon(BitmapFactory().pixmap("qss:overlay/editshow.svg"));
447
    actEditHide.setIcon(BitmapFactory().pixmap("qss:overlay/edithide.svg"));
448
    actTransparent.setIcon(BitmapFactory().pixmap("qss:overlay/transparent.svg"));
449
    QPixmap pxAutoHide = BitmapFactory().pixmap("qss:overlay/autohide.svg");
450
    switch(dockArea) {
451
    case Qt::LeftDockWidgetArea:
452
        actAutoHide.setIcon(pxAutoHide);
453
        break;
454
    case Qt::RightDockWidgetArea:
455
        actAutoHide.setIcon(pxAutoHide.transformed(QTransform().scale(-1,1)));
456
        break;
457
    case Qt::TopDockWidgetArea:
458
        actAutoHide.setIcon(pxAutoHide.transformed(QTransform().rotate(90)));
459
        break;
460
    case Qt::BottomDockWidgetArea:
461
        actAutoHide.setIcon(pxAutoHide.transformed(QTransform().rotate(-90)));
462
        break;
463
    default:
464
        break;
465
    }
466
    syncAutoMode();
467
}
468

469
void OverlayTabWidget::onAnimationStateChanged()
470
{
471
    if (_animator->state() != QAbstractAnimation::Running) {
472
        setAnimation(0);
473
        if (_animator->startValue().toReal() == 0.0) {
474
            hide();
475
            OverlayManager::instance()->refresh();
476
        }
477
        if (_state == State::Showing)
478
            setState(State::Normal);
479
    }
480
}
481

482
void OverlayTabWidget::setAnimation(qreal t)
483
{
484
    if (t != _animation) {
485
        _animation = t;
486
        setupLayout();
487
    }
488
}
489

490
void OverlayTabWidget::startShow()
491
{
492
    if (isVisible() || _state > State::Normal)
493
        return;
494
    int duration = OverlayParams::getDockOverlayAnimationDuration();
495
    bool setmode = _state != State::Showing;
496
    if (duration) {
497
        _animator->setStartValue(1.0);
498
        _animator->setEndValue(0.0);
499
        _animator->setDuration(duration);
500
        _animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve());
501
        _animator->start();
502
    }
503
    else if (_state == State::Showing)
504
        setState(State::Normal);
505
    proxyWidget->hide();
506
    show();
507
    raise();
508
    if (setmode)
509
        setOverlayMode(overlaid);
510
}
511

512
QWidget *OverlayTabWidget::createTitleButton(QAction *action, int size)
513
{
514
    auto button = new OverlayToolButton(nullptr);
515
    button->setObjectName(action->data().toString());
516
    button->setDefaultAction(action);
517
    button->setAutoRaise(true);
518
    button->setContentsMargins(0,0,0,0);
519
    button->setFixedSize(size,size);
520
    return button;
521
}
522

523
void OverlayTabWidget::startHide()
524
{
525
    if (!isVisible()
526
            || _state > State::Normal
527
            || (_animator->state() == QAbstractAnimation::Running
528
                && _animator->startValue().toReal() == 0.0))
529
        return;
530
    int duration = OverlayParams::getDockOverlayAnimationDuration();
531
    if (!duration)
532
        hide();
533
    else {
534
        _animator->setStartValue(0.0);
535
        _animator->setEndValue(1.0);
536
        _animator->setDuration(duration);
537
        _animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve());
538
        _animator->start();
539
    }
540
}
541

542
bool OverlayTabWidget::event(QEvent *ev)
543
{
544
    switch(ev->type()) {
545
    case QEvent::MouseButtonRelease:
546
        if(mouseGrabber() == this) {
547
            releaseMouse();
548
            ev->accept();
549
            return true;
550
        }
551
        break;
552
    case QEvent::MouseMove:
553
    case QEvent::ContextMenu:
554
        if(QApplication::mouseButtons() == Qt::NoButton && mouseGrabber() == this) {
555
            releaseMouse();
556
            ev->accept();
557
            return true;
558
        }
559
        break;
560
    case QEvent::MouseButtonPress:
561
        ev->accept();
562
        return true;
563
    default:
564
        break;
565
    }
566
    return QTabWidget::event(ev);
567
}
568

569
int OverlayTabWidget::testAlpha(const QPoint &_pos, int radiusScale)
570
{
571
    if (!count() || (!isOverlaid() && !isTransparent()) || !isVisible())
572
        return -1;
573

574
    if (tabBar()->isVisible() && tabBar()->tabAt(tabBar()->mapFromGlobal(_pos))>=0)
575
        return -1;
576

577
    if (titleBar->isVisible() && titleBar->rect().contains(titleBar->mapFromGlobal(_pos)))
578
        return -1;
579

580
    if (!splitter->isVisible())
581
        return 0;
582

583
    auto pos = splitter->mapFromGlobal(_pos);
584
    QSize size = splitter->size();
585
    if (pos.x() < 0 || pos.y() < 0
586
            || pos.x() >= size.width()
587
            || pos.y() >= size.height())
588
    {
589
        if (this->rect().contains(this->mapFromGlobal(_pos)))
590
            return 0;
591
        return -1;
592
    }
593

594
    if (_image.isNull()) {
595
        auto pixmap = splitter->grab();
596
        _imageScale = pixmap.devicePixelRatio();
597
        _image = pixmap.toImage();
598
    }
599

600
    int res = qAlpha(_image.pixel(pos*_imageScale));
601
    int radius = OverlayParams::getDockOverlayAlphaRadius() * radiusScale;
602
    if (res || radius<=0 )
603
        return res;
604

605
    radius *= _imageScale;
606
    for (int i=-radius; i<radius; ++i) {
607
        for (int j=-radius; j<radius; ++j) {
608
            if (pos.x()+i < 0 || pos.y()+j < 0
609
                    || pos.x()+i >= size.width()
610
                    || pos.y()+j >= size.height())
611
                continue;
612
            res = qAlpha(_image.pixel(pos*_imageScale + QPoint(i,j)));
613
            if (res)
614
                return res;
615
        }
616
    }
617
    return 0;
618
}
619

620
void OverlayTabWidget::paintEvent(QPaintEvent *ev)
621
{
622
    Base::StateLocker guard(repainting);
623
    repaintTimer.stop();
624
    if (!_image.isNull())
625
        _image = QImage();
626
    QTabWidget::paintEvent(ev);
627
}
628

629
void OverlayTabWidget::onRepaint()
630
{
631
    Base::StateLocker guard(repainting);
632
    repaintTimer.stop();
633
    if (!_image.isNull())
634
        _image = QImage();
635
    splitter->repaint();
636
}
637

638
void OverlayTabWidget::scheduleRepaint()
639
{
640
    if(!repainting
641
            && isVisible() 
642
            && _graphicsEffect)
643
    {
644
        repaintTimer.start(100);
645
    }
646
}
647

648
QColor OverlayTabWidget::effectColor() const
649
{
650
    return _graphicsEffect->color();
651
}
652

653
void OverlayTabWidget::setEffectColor(const QColor &color)
654
{
655
    _graphicsEffect->setColor(color);
656
    _graphicsEffectTab->setColor(color);
657
}
658

659
int OverlayTabWidget::effectWidth() const
660
{
661
    return _graphicsEffect->size().width();
662
}
663

664
void OverlayTabWidget::setEffectWidth(int s)
665
{
666
    auto size = _graphicsEffect->size();
667
    size.setWidth(s);
668
    _graphicsEffect->setSize(size);
669
    _graphicsEffectTab->setSize(size);
670
}
671

672
int OverlayTabWidget::effectHeight() const
673
{
674
    return _graphicsEffect->size().height();
675
}
676

677
void OverlayTabWidget::setEffectHeight(int s)
678
{
679
    auto size = _graphicsEffect->size();
680
    size.setHeight(s);
681
    _graphicsEffect->setSize(size);
682
    _graphicsEffectTab->setSize(size);
683
}
684

685
qreal OverlayTabWidget::effectOffsetX() const
686
{
687
    return _graphicsEffect->offset().x();
688
}
689

690
void OverlayTabWidget::setEffectOffsetX(qreal d)
691
{
692
    auto offset = _graphicsEffect->offset();
693
    offset.setX(d);
694
    _graphicsEffect->setOffset(offset);
695
    _graphicsEffectTab->setOffset(offset);
696
}
697

698
qreal OverlayTabWidget::effectOffsetY() const
699
{
700
    return _graphicsEffect->offset().y();
701
}
702

703
void OverlayTabWidget::setEffectOffsetY(qreal d)
704
{
705
    auto offset = _graphicsEffect->offset();
706
    offset.setY(d);
707
    _graphicsEffect->setOffset(offset);
708
    _graphicsEffectTab->setOffset(offset);
709
}
710

711
qreal OverlayTabWidget::effectBlurRadius() const
712
{
713
    return _graphicsEffect->blurRadius();
714
}
715

716
void OverlayTabWidget::setEffectBlurRadius(qreal r)
717
{
718
    _graphicsEffect->setBlurRadius(r);
719
    _graphicsEffectTab->setBlurRadius(r);
720
}
721

722
bool OverlayTabWidget::effectEnabled() const
723
{
724
    return _effectEnabled;
725
}
726

727
void OverlayTabWidget::setEffectEnabled(bool enable)
728
{
729
    _effectEnabled = enable;
730
}
731

732
bool OverlayTabWidget::eventFilter(QObject *o, QEvent *ev)
733
{
734
    if(ev->type() == QEvent::Resize && o == tabBar()) {
735
        if (_state <= State::Normal)
736
            timer.start(10);
737
    }
738
    return QTabWidget::eventFilter(o, ev);
739
}
740

741
void OverlayTabWidget::restore(ParameterGrp::handle handle)
742
{
743
    if (!handle) {
744
        hGrp = handle;
745
        return;
746
    }
747
    if (!parentWidget())
748
        return;
749
    std::string widgets = handle->GetASCII("Widgets","");
750
    for(auto &name : QString::fromUtf8(widgets.c_str()).split(QLatin1Char(','))) {
751
        if(name.isEmpty())
752
            continue;
753
        OverlayManager::instance()->registerDockWidget(name, this);
754
        auto dock = getMainWindow()->findChild<QDockWidget*>(name);
755
        if(dock)
756
            addWidget(dock, dock->windowTitle());
757
    }
758
    int width = handle->GetInt("Width", 0);
759
    int height = handle->GetInt("Height", 0);
760
    int offset1 = handle->GetInt("Offset1", 0);
761
    int offset2 = handle->GetInt("Offset3", 0);
762
    setOffset(QSize(offset1,offset2));
763
    setSizeDelta(handle->GetInt("Offset2", 0));
764
    if(width && height) {
765
        QRect rect(0, 0, width, height);
766
        switch(dockArea) {
767
        case Qt::RightDockWidgetArea:
768
            rect.moveRight(parentWidget()->size().width());
769
            break;
770
        case Qt::BottomDockWidgetArea:
771
            rect.moveBottom(parentWidget()->size().height());
772
            break;
773
        default:
774
            break;
775
        }
776
        setRect(rect);
777
    }
778
    if (handle->GetBool("AutoHide", false))
779
        setAutoMode(AutoMode::AutoHide);
780
    else if (handle->GetBool("EditHide", false))
781
        setAutoMode(AutoMode::EditHide);
782
    else if (handle->GetBool("EditShow", false))
783
        setAutoMode(AutoMode::EditShow);
784
    else if (handle->GetBool("TaskShow", false))
785
        setAutoMode(AutoMode::TaskShow);
786
    else
787
        setAutoMode(AutoMode::NoAutoMode);
788

789
    setTransparent(handle->GetBool("Transparent", false));
790

791
    _sizemap.clear();
792
    std::string savedSizes = handle->GetASCII("Sizes","");
793
    QList<int> sizes;
794
    int idx = 0;
795
    for(auto &size : QString::fromUtf8(savedSizes.c_str()).split(QLatin1Char(','))) {
796
        sizes.append(size.toInt());
797
        _sizemap[dockWidget(idx++)] = sizes.back();
798
    }
799

800
    FC_LOG("restore " << objectName().toUtf8().constData() << " " << savedSizes);
801

802
    getSplitter()->setSizes(sizes);
803
    hGrp = handle;
804
}
805

806
void OverlayTabWidget::saveTabs()
807
{
808
    if(!hGrp)
809
        return;
810

811
    std::ostringstream os, os2;
812
    _sizemap.clear();
813
    auto sizes = splitter->sizes();
814
    bool first = true;
815
    for(int i=0,c=splitter->count(); i<c; ++i) {
816
        auto dock = dockWidget(i);
817
        if (!dock)
818
            continue;
819
        if(dock->objectName().size()) {
820
            os << dock->objectName().toUtf8().constData() << ",";
821
            if (first)
822
                first = false;
823
            else
824
                os2 << ",";
825
            os2 << sizes[i];
826
        }
827
        _sizemap[dock] = sizes[i];
828
    }
829
    Base::StateLocker lock(_saving);
830
    hGrp->SetASCII("Widgets", os.str().c_str());
831
    hGrp->SetASCII("Sizes", os2.str().c_str());
832
    FC_LOG("save " << objectName().toUtf8().constData() << " " << os2.str());
833
}
834

835
void OverlayTabWidget::onTabMoved(int from, int to)
836
{
837
    QWidget *w = splitter->widget(from);
838
    splitter->insertWidget(to,w);
839
    saveTabs();
840
}
841

842
void OverlayTabWidget::setTitleBar(QWidget *w)
843
{
844
    titleBar = w;
845
}
846

847
void OverlayTabWidget::changeEvent(QEvent *e)
848
{
849
    QTabWidget::changeEvent(e);
850
    if (e->type() == QEvent::LanguageChange)
851
        retranslate();
852
}
853

854
void OverlayTabWidget::retranslate()
855
{
856
    actTransparent.setToolTip(tr("Toggle transparent mode"));
857
    actNoAutoMode.setText(tr("None"));
858
    actNoAutoMode.setToolTip(tr("Turn off auto hide/show"));
859
    actAutoHide.setText(tr("Auto hide"));
860
    actAutoHide.setToolTip(tr("Auto hide docked widgets on leave"));
861
    actEditHide.setText(tr("Hide on edit"));
862
    actEditHide.setToolTip(tr("Auto hide docked widgets on editing"));
863
    actEditShow.setText(tr("Show on edit"));
864
    actEditShow.setToolTip(tr("Auto show docked widgets on editing"));
865
    actTaskShow.setText(tr("Auto task"));
866
    actTaskShow.setToolTip(tr("Auto show task view for any current task, and hide the view when there is no task."));
867
    actOverlay.setToolTip(tr("Toggle overlay"));
868
    syncAutoMode();
869
}
870

871
void OverlayTabWidget::syncAutoMode()
872
{
873
    QAction *action = nullptr;
874
    switch(autoMode) {
875
    case AutoMode::AutoHide:
876
        action = &actAutoHide;
877
        break;
878
    case AutoMode::EditShow:
879
        action = &actEditShow;
880
        break;
881
    case AutoMode::TaskShow:
882
        action = &actTaskShow;
883
        break;
884
    case AutoMode::EditHide:
885
        action = &actEditHide;
886
        break;
887
    default:
888
        action = &actNoAutoMode;
889
        break;
890
    }
891
    actAutoMode.setIcon(action->icon());
892
    if (action == &actNoAutoMode)
893
        actAutoMode.setToolTip(tr("Select auto show/hide mode"));
894
    else
895
        actAutoMode.setToolTip(action->toolTip());
896
}
897

898
void OverlayTabWidget::onAction(QAction *action)
899
{
900
    if (action == &actAutoMode) {
901
        action = autoModeMenu.exec(QCursor::pos());
902
        if (action == &actNoAutoMode)
903
            setAutoMode(AutoMode::NoAutoMode);
904
        else if (action == &actAutoHide)
905
            setAutoMode(AutoMode::AutoHide);
906
        else if (action == &actEditShow)
907
            setAutoMode(AutoMode::EditShow);
908
        else if (action == &actTaskShow)
909
            setAutoMode(AutoMode::TaskShow);
910
        else if (action == &actEditHide)
911
            setAutoMode(AutoMode::EditHide);
912
        return;
913
    }
914
    else if(action == &actOverlay) {
915
        OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive);
916
        return;
917
    } else if(action == &actTransparent) {
918
        if(hGrp) {
919
            Base::StateLocker lock(_saving);
920
            hGrp->SetBool("Transparent", actTransparent.isChecked());
921
        }
922
    }
923
    OverlayManager::instance()->refresh(this);
924
}
925

926
void OverlayTabWidget::setState(State state)
927
{
928
    if (_state == state)
929
        return;
930
    switch(state) {
931
    case State::Normal:
932
        if (_state == State::Hidden) {
933
            // Only unhide through State::Showing, not State::Normal
934
            return;
935
        }
936
        else if (_state == State::Showing) {
937
            _state = state;
938
            return;
939
        }
940
        // fall through
941
    case State::Showing:
942
        _state = state;
943
        hide();
944
        if (dockArea == Qt::RightDockWidgetArea)
945
            setTabPosition(East);
946
        else if (dockArea == Qt::BottomDockWidgetArea)
947
            setTabPosition(South);
948
        if (this->count() == 1)
949
            tabBar()->hide();
950
        _graphicsEffectTab->setEnabled(false);
951
        titleBar->show();
952
        splitter->show();
953
        if (state == State::Showing)
954
            OverlayManager::instance()->refresh(this);
955
        break;
956
    case State::Hint:
957
        if (_state == State::HintHidden || _state == State::Hidden)
958
            break;
959
        _state = state;
960
        if (this->count() && OverlayParams::getDockOverlayHintTabBar()) {
961
            tabBar()->setToolTip(proxyWidget->toolTip());
962
            tabBar()->show();
963
            titleBar->hide();
964
            splitter->hide();
965
            _graphicsEffectTab->setEnabled(true);
966
            show();
967
            raise();
968
            proxyWidget->raise();
969
            if (dockArea == Qt::RightDockWidgetArea)
970
                setTabPosition(West);
971
            else if (dockArea == Qt::BottomDockWidgetArea)
972
                setTabPosition(North);
973
            OverlayManager::instance()->refresh(this);
974
        }
975
        break;
976
    case State::HintHidden:
977
        if (_state != State::Hidden)
978
            _state = state;
979
        proxyWidget->hide();
980
        hide();
981
        _graphicsEffectTab->setEnabled(true);
982
        break;
983
    case State::Hidden:
984
        startHide();
985
        _state = state;
986
        break;
987
    default:
988
        break;
989
    }
990
}
991

992
bool OverlayTabWidget::checkAutoHide() const
993
{
994
    if(autoMode == AutoMode::AutoHide)
995
        return true;
996

997
    if(OverlayParams::getDockOverlayAutoView()) {
998
        auto view = getMainWindow()->activeWindow();
999
        if (!view) return true;
1000
        if(!view->onHasMsg("CanPan")
1001
                && view->parentWidget()
1002
                && view->parentWidget()->isMaximized())
1003
            return true;
1004
    }
1005

1006
    if(autoMode == AutoMode::EditShow) {
1007
        return !Application::Instance->editDocument() 
1008
            && (!Control().taskPanel() || Control().taskPanel()->isEmpty(false));
1009
    }
1010

1011
    if(autoMode == AutoMode::EditHide && Application::Instance->editDocument())
1012
        return true;
1013

1014
    return false;
1015
}
1016

1017
void OverlayTabWidget::leaveEvent(QEvent*)
1018
{
1019
    if (titleBar && QWidget::mouseGrabber() == titleBar)
1020
        return;
1021
    OverlayManager::instance()->refresh();
1022
}
1023

1024
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1025
void OverlayTabWidget::enterEvent(QEvent*)
1026
#else
1027
void OverlayTabWidget::enterEvent(QEnterEvent*)
1028
#endif
1029
{
1030
    revealTime = QTime();
1031
    OverlayManager::instance()->refresh();
1032
}
1033

1034
void OverlayTabWidget::setRevealTime(const QTime &time)
1035
{
1036
    revealTime = time;
1037
}
1038

1039
void OverlayTabWidget::_setOverlayMode(QWidget *widget, OverlayOption option)
1040
{
1041
    if(!widget)
1042
        return;
1043

1044
#if QT_VERSION>QT_VERSION_CHECK(5,12,2) && QT_VERSION < QT_VERSION_CHECK(5,12,6)
1045
    // Work around Qt bug https://bugreports.qt.io/browse/QTBUG-77006
1046
    widget->setStyleSheet(OverlayManager::instance()->getStyleSheet());
1047
#endif
1048

1049
    if (qobject_cast<QScrollBar*>(widget)) {
1050
        auto parent = widget->parentWidget();
1051
        if (parent) {
1052
            parent = parent->parentWidget();
1053
            if (qobject_cast<PropertyEditor::PropertyEditor*>(parent)) {
1054
                auto scrollArea = static_cast<QAbstractScrollArea*>(parent);
1055
                if (scrollArea->verticalScrollBar() == widget) {
1056
                    if (!OverlayParams::getDockOverlayHidePropertyViewScrollBar()
1057
                            || option == OverlayOption::Disable)
1058
                        widget->setStyleSheet(QString());
1059
                    else {
1060
                        static QString _style = QStringLiteral("*{width:0}");
1061
                        widget->setStyleSheet(_style);
1062
                    }
1063
                }
1064
            }
1065
            auto treeView = qobject_cast<TreeWidget*>(parent);
1066
            if (treeView) {
1067
                auto scrollArea = static_cast<QAbstractScrollArea*>(parent);
1068
                if (scrollArea->verticalScrollBar() == widget) {
1069
                    if (!TreeParams::getHideScrollBar() || option == OverlayOption::Disable)
1070
                        widget->setStyleSheet(QString());
1071
                    else {
1072
                        static QString _style = QStringLiteral("*{width:0}");
1073
                        widget->setStyleSheet(_style);
1074
                    }
1075
                }
1076
            }
1077

1078
            if (treeView) {
1079
                auto header = treeView->header();
1080
                if (!TreeParams::getHideHeaderView() || option==OverlayOption::Disable)
1081
                    header->setStyleSheet(QString());
1082
                else {
1083
                    static QString _style = QStringLiteral(
1084
                            "QHeaderView:section {"
1085
                              "height: 0px;"
1086
                              "background-color: transparent;"
1087
                              "padding: 0px;"
1088
                              "border: transparent;}");
1089
                    header->setStyleSheet(_style);
1090
                }
1091
            }
1092
        }
1093
    }
1094

1095
    auto tabbar = qobject_cast<QTabBar*>(widget);
1096
    if(tabbar) {
1097
        if(!tabbar->autoHide() || tabbar->count()>1) {
1098
            if(!OverlayManager::instance()->getHideTab())
1099
                tabbar->setVisible(true);
1100
            else
1101
                tabbar->setVisible(option == OverlayOption::Disable
1102
                        || (option == OverlayOption::ShowTab && tabbar->count()>1));
1103
            return;
1104
        }
1105
    }
1106

1107
    if (!qobject_cast<QScrollArea*>(widget)
1108
            || !qobject_cast<Dialog::Clipping*>(widget->parentWidget())) {
1109
        if(option != OverlayOption::Disable) {
1110
            widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint);
1111
        } else {
1112
            widget->setWindowFlags(widget->windowFlags() & ~Qt::FramelessWindowHint);
1113
        }
1114
        widget->setAttribute(Qt::WA_NoSystemBackground, option != OverlayOption::Disable);
1115
        widget->setAttribute(Qt::WA_TranslucentBackground, option != OverlayOption::Disable);
1116
    }
1117
}
1118

1119
void OverlayTabWidget::setOverlayMode(QWidget *widget, OverlayOption option)
1120
{
1121
    if(!widget || (qobject_cast<QDialog*>(widget)
1122
                        && !qobject_cast<Dialog::Clipping*>(widget))
1123
               || qobject_cast<TaskView::TaskBox*>(widget))
1124
        return;
1125

1126
    if(widget != tabBar()) {
1127
        if(OverlayParams::getDockOverlayAutoMouseThrough()
1128
                && option == OverlayOption::ShowTab) {
1129
            widget->setMouseTracking(true);
1130
        }
1131
    }
1132

1133
    _setOverlayMode(widget, option);
1134

1135
    if(qobject_cast<QComboBox*>(widget)) {
1136
        // do not set child QAbstractItemView of QComboBox, otherwise the drop down box
1137
        // won't be shown
1138
        return;
1139
    }
1140
    for(auto child : widget->children())
1141
        setOverlayMode(qobject_cast<QWidget*>(child), option);
1142
}
1143

1144
void OverlayTabWidget::setTransparent(bool enable)
1145
{
1146
    if(actTransparent.isChecked() == enable)
1147
        return;
1148
    if(hGrp) {
1149
        Base::StateLocker lock(_saving);
1150
        hGrp->SetBool("Transparent", enable);
1151
    }
1152
    actTransparent.setChecked(enable);
1153
    OverlayManager::instance()->refresh(this);
1154
}
1155

1156
bool OverlayTabWidget::isTransparent() const
1157
{
1158
    if (!actTransparent.isChecked())
1159
        return false;
1160
    if(OverlayParams::getDockOverlayAutoView()) {
1161
        auto view = getMainWindow()->activeWindow();
1162
        if (!view) return false;
1163
        if(!view->onHasMsg("CanPan")
1164
                && view->parentWidget()
1165
                && view->parentWidget()->isMaximized())
1166
            return false;
1167
    }
1168
    return true;
1169
}
1170

1171
bool OverlayTabWidget::isOverlaid(QueryOption option) const
1172
{
1173
    if (option != QueryOption::QueryOverlay
1174
            && currentTransparent != isTransparent())
1175
        return option == QueryOption::TransparencyChanged;
1176
    return overlaid;
1177
}
1178

1179
void OverlayTabWidget::setAutoMode(AutoMode mode)
1180
{
1181
    if (autoMode == mode)
1182
        return;
1183
    autoMode = mode;
1184

1185
    if (hGrp) {
1186
        bool autohide = false, editshow = false, edithide = false, taskshow = false;
1187
        switch (mode) {
1188
        case AutoMode::AutoHide:
1189
            autohide = true;
1190
            break;
1191
        case AutoMode::EditShow:
1192
            editshow = true;
1193
            break;
1194
        case AutoMode::EditHide:
1195
            edithide = true;
1196
            break;
1197
        case AutoMode::TaskShow:
1198
            taskshow = true;
1199
            break;
1200
        default:
1201
            break;
1202
        }
1203
        Base::StateLocker lock(_saving);
1204
        hGrp->SetBool("AutoHide", autohide);
1205
        hGrp->SetBool("EditShow", editshow);
1206
        hGrp->SetBool("EditHide", edithide);
1207
        hGrp->SetBool("TaskShow", taskshow);
1208
    }
1209
    syncAutoMode();
1210
    OverlayManager::instance()->refresh(this);
1211
}
1212

1213
QDockWidget *OverlayTabWidget::currentDockWidget() const
1214
{
1215
    int index = -1;
1216
    for(int size : splitter->sizes()) {
1217
        ++index;
1218
        if(size>0)
1219
            return dockWidget(index);
1220
    }
1221
    return dockWidget(currentIndex());
1222
}
1223

1224
QDockWidget *OverlayTabWidget::dockWidget(int index) const
1225
{
1226
    if(index < 0 || index >= splitter->count())
1227
        return nullptr;
1228
    return qobject_cast<QDockWidget*>(splitter->widget(index));
1229
}
1230

1231
void OverlayTabWidget::updateSplitterHandles()
1232
{
1233
    if (overlaid || _state > State::Normal)
1234
        return;
1235
    for (int i=0, c=splitter->count(); i<c; ++i) {
1236
        auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
1237
        if (handle)
1238
            handle->showTitle(true);
1239
    }
1240
}
1241

1242
bool OverlayTabWidget::onEscape()
1243
{
1244
    if (getState() == OverlayTabWidget::State::Hint
1245
            || getState() == OverlayTabWidget::State::Hidden) {
1246
        setState(OverlayTabWidget::State::HintHidden);
1247
        return true;
1248
    }
1249
    if (!isVisible())
1250
        return false;
1251
    if (titleBar->isVisible() && titleBar->underMouse()) {
1252
        titleBar->hide();
1253
        return true;
1254
    }
1255
    for (int i=0, c=splitter->count(); i<c; ++i) {
1256
        auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
1257
        if (handle->isVisible() && handle->underMouse()) {
1258
            handle->showTitle(false);
1259
            return true;
1260
        }
1261
    }
1262
    return false;
1263
}
1264

1265
void OverlayTabWidget::setOverlayMode(bool enable)
1266
{
1267
    overlaid = enable;
1268

1269
    if(!isVisible() || !count())
1270
        return;
1271

1272
    touched = false;
1273

1274
    if (_state <= State::Normal) {
1275
        titleBar->setVisible(!enable || OverlayManager::instance()->isMouseTransparent());
1276
        for (int i=0, c=splitter->count(); i<c; ++i) {
1277
            auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
1278
            if (handle)
1279
                handle->showTitle(!enable);
1280
        }
1281
    }
1282

1283
    QString stylesheet;
1284
    stylesheet = OverlayManager::instance()->getStyleSheet();
1285
    currentTransparent = isTransparent();
1286

1287
    OverlayOption option;
1288
    if(!enable && isTransparent()) {
1289
        option = OverlayOption::ShowTab;
1290
    } else if (enable
1291
            && !isTransparent()
1292
            && (autoMode == AutoMode::EditShow || autoMode == AutoMode::AutoHide)) {
1293
        option = OverlayOption::Disable;
1294
    } else {
1295
        option = enable?OverlayOption::Enable:OverlayOption::Disable;
1296
    }
1297

1298
    proxyWidget->setStyleSheet(stylesheet);
1299
    this->setStyleSheet(stylesheet);
1300
    setOverlayMode(this, option);
1301

1302
    _graphicsEffect->setEnabled(effectEnabled() && (enable || isTransparent()));
1303

1304
    if (_state == State::Hint && OverlayParams::getDockOverlayHintTabBar()) {
1305
        tabBar()->setToolTip(proxyWidget->toolTip());
1306
        tabBar()->show();
1307
    } else if (OverlayParams::getDockOverlayHideTabBar() || count()==1) {
1308
        tabBar()->hide();
1309
    } else {
1310
        tabBar()->setToolTip(QString());
1311
        tabBar()->setVisible(!enable || !OverlayManager::instance()->getHideTab());
1312
    }
1313

1314
    setRect(rectOverlay);
1315
}
1316

1317
const QRect &OverlayTabWidget::getRect()
1318
{
1319
    return rectOverlay;
1320
}
1321

1322
bool OverlayTabWidget::getAutoHideRect(QRect &rect) const
1323
{
1324
    rect = rectOverlay;
1325
    int hintWidth = OverlayParams::getDockOverlayHintSize();
1326
    switch(dockArea) {
1327
    case Qt::LeftDockWidgetArea:
1328
    case Qt::RightDockWidgetArea:
1329
        if (_TopOverlay->isVisible() && _TopOverlay->_state <= State::Normal)
1330
            rect.setTop(std::max(rect.top(), _TopOverlay->rectOverlay.bottom()));
1331
        if (dockArea == Qt::RightDockWidgetArea)
1332
            rect.setLeft(rect.left() + std::max(rect.width()-hintWidth,0));
1333
        else
1334
            rect.setRight(rect.right() - std::max(rect.width()-hintWidth,0));
1335
        break;
1336
    case Qt::TopDockWidgetArea:
1337
    case Qt::BottomDockWidgetArea:
1338
        if (_LeftOverlay->isVisible() && _LeftOverlay->_state <= State::Normal)
1339
            rect.setLeft(std::max(rect.left(),_LeftOverlay->rectOverlay.right()));
1340
        if (dockArea == Qt::TopDockWidgetArea)
1341
            rect.setBottom(rect.bottom() - std::max(rect.height()-hintWidth,0));
1342
        else {
1343
            rect.setTop(rect.top() + std::max(rect.height()-hintWidth,0));
1344
            if (_RightOverlay->isVisible() && _RightOverlay->_state <= State::Normal) {
1345
                QPoint offset = getMainWindow()->getMdiArea()->pos();
1346
                rect.setRight(std::min(rect.right(), _RightOverlay->x()-offset.x()));
1347
            }
1348
        }
1349
        break;
1350
    default:
1351
        break;
1352
    }
1353
    return _state != State::Showing && overlaid && checkAutoHide();
1354
}
1355

1356
void OverlayTabWidget::setOffset(const QSize &ofs)
1357
{
1358
    if(offset != ofs) {
1359
        offset = ofs;
1360
        if(hGrp) {
1361
            Base::StateLocker lock(_saving);
1362
            hGrp->SetInt("Offset1", ofs.width());
1363
            hGrp->SetInt("Offset3", ofs.height());
1364
        }
1365
    }
1366
}
1367

1368
void OverlayTabWidget::setSizeDelta(int delta)
1369
{
1370
    if(sizeDelta != delta) {
1371
        if(hGrp) {
1372
            Base::StateLocker lock(_saving);
1373
            hGrp->SetInt("Offset2", delta);
1374
        }
1375
        sizeDelta = delta;
1376
    }
1377
}
1378

1379
void OverlayTabWidget::setRect(QRect rect)
1380
{
1381
    if(busy || !parentWidget() || !getMainWindow() || !getMainWindow()->getMdiArea())
1382
        return;
1383

1384
    if (rect.width() == 0)
1385
        rect.setWidth(OverlayParams::getDockOverlayMinimumSize()*3);
1386
    if (rect.height() == 0)
1387
        rect.setHeight(OverlayParams::getDockOverlayMinimumSize()*3);
1388

1389
    switch(dockArea) {
1390
    case Qt::LeftDockWidgetArea:
1391
        rect.moveLeft(0);
1392
        if (rect.width() < OverlayParams::getDockOverlayMinimumSize())
1393
            rect.setWidth(OverlayParams::getDockOverlayMinimumSize());
1394
        break;
1395
    case Qt::RightDockWidgetArea:
1396
        if (rect.width() < OverlayParams::getDockOverlayMinimumSize())
1397
            rect.setLeft(rect.right()-OverlayParams::getDockOverlayMinimumSize());
1398
        break;
1399
    case Qt::TopDockWidgetArea:
1400
        rect.moveTop(0);
1401
        if (rect.height() < OverlayParams::getDockOverlayMinimumSize())
1402
            rect.setHeight(OverlayParams::getDockOverlayMinimumSize());
1403
        break;
1404
    case Qt::BottomDockWidgetArea:
1405
        if (rect.height() < OverlayParams::getDockOverlayMinimumSize())
1406
            rect.setTop(rect.bottom()-OverlayParams::getDockOverlayMinimumSize());
1407
        break;
1408
    default:
1409
        break;
1410
    }
1411

1412
    if(hGrp && rect.size() != rectOverlay.size()) {
1413
        Base::StateLocker lock(_saving);
1414
        hGrp->SetInt("Width", rect.width());
1415
        hGrp->SetInt("Height", rect.height());
1416
    }
1417
    rectOverlay = rect;
1418

1419
    QPoint offset = getMainWindow()->getMdiArea()->pos();
1420

1421
    if(getAutoHideRect(rect) || _state == State::Hint || _state == State::Hidden) {
1422
        QRect rectHint = rect;
1423
        if (_state != State::Hint && _state != State::Hidden)
1424
            startHide();
1425
        else if (count() && OverlayParams::getDockOverlayHintTabBar()) {
1426
            switch(dockArea) {
1427
            case Qt::LeftDockWidgetArea: 
1428
            case Qt::RightDockWidgetArea: 
1429
                if (dockArea == Qt::LeftDockWidgetArea)
1430
                    rect.setWidth(tabBar()->width());
1431
                else
1432
                    rect.setLeft(rect.left() + rect.width() - tabBar()->width());
1433
                rect.setHeight(std::max(rect.height(), 
1434
                            tabBar()->y() + tabBar()->sizeHint().height() + 5));
1435
                break;
1436
            case Qt::BottomDockWidgetArea: 
1437
            case Qt::TopDockWidgetArea: 
1438
                if (dockArea == Qt::TopDockWidgetArea)
1439
                    rect.setHeight(tabBar()->height());
1440
                else
1441
                    rect.setTop(rect.top() + rect.height() - tabBar()->height());
1442
                rect.setWidth(std::max(rect.width(),
1443
                            tabBar()->x() + tabBar()->sizeHint().width() + 5));
1444
                break;
1445
            default:
1446
                break;
1447
            }
1448

1449
            setGeometry(rect.translated(offset));
1450
        }
1451
        proxyWidget->setGeometry(rectHint.translated(offset));
1452
        if (count()) {
1453
            proxyWidget->show();
1454
            proxyWidget->raise();
1455
        } else
1456
            proxyWidget->hide();
1457

1458
    } else {
1459
        setGeometry(rectOverlay.translated(offset));
1460

1461
        for(int i=0, count=splitter->count(); i<count; ++i)
1462
            splitter->widget(i)->show();
1463

1464
        if(!isVisible() && count()) {
1465
            proxyWidget->hide();
1466
            startShow();
1467
        }
1468
    }
1469
}
1470

1471
void OverlayTabWidget::addWidget(QDockWidget *dock, const QString &title)
1472
{
1473
    if (!getMainWindow() || !getMainWindow()->getMdiArea())
1474
        return;
1475

1476
    OverlayManager::instance()->registerDockWidget(dock->objectName(), this);
1477

1478
    OverlayManager::setFocusView();
1479
    getMainWindow()->removeDockWidget(dock);
1480

1481
    auto titleWidget = dock->titleBarWidget();
1482
    if(titleWidget && titleWidget->objectName()==QStringLiteral("OverlayTitle")) {
1483
        // replace the title bar with an invisible widget to hide it. The
1484
        // OverlayTabWidget uses its own title bar for all docks.
1485
        auto w = new QWidget();
1486
        w->setObjectName(QStringLiteral("OverlayTitle"));
1487
        dock->setTitleBarWidget(w);
1488
        w->hide();
1489
        titleWidget->deleteLater();
1490
    }
1491

1492
    dock->show();
1493
    splitter->addWidget(dock);
1494
    auto dummyWidget = new QWidget(this);
1495
    addTab(dummyWidget, title);
1496
    connect(dock, &QObject::destroyed, dummyWidget, &QObject::deleteLater);
1497

1498
    dock->setFeatures(dock->features() & ~QDockWidget::DockWidgetFloatable);
1499
    if(count() == 1) {
1500
        QRect rect = dock->geometry();
1501
        QSize sizeMain = getMainWindow()->getMdiArea()->size();
1502
        switch(dockArea) {
1503
        case Qt::LeftDockWidgetArea:
1504
        case Qt::RightDockWidgetArea:
1505
            if (rect.width() > sizeMain.width()/3)
1506
                rect.setWidth(sizeMain.width()/3);
1507
            break;
1508
        case Qt::TopDockWidgetArea:
1509
        case Qt::BottomDockWidgetArea:
1510
            if (rect.height() > sizeMain.height()/3)
1511
                rect.setHeight(sizeMain.height()/3);
1512
            break;
1513
        default:
1514
            break;
1515
        }
1516
        setRect(rect);
1517
    }
1518

1519
    saveTabs();
1520
}
1521

1522
int OverlayTabWidget::dockWidgetIndex(QDockWidget *dock) const
1523
{
1524
    return splitter->indexOf(dock);
1525
}
1526

1527
void OverlayTabWidget::removeWidget(QDockWidget *dock, QDockWidget *lastDock)
1528
{
1529
    int index = dockWidgetIndex(dock);
1530
    if(index < 0)
1531
        return;
1532

1533
    OverlayManager::instance()->unregisterDockWidget(dock->objectName(), this);
1534

1535
    OverlayManager::setFocusView();
1536
    dock->show();
1537
    if(lastDock)
1538
        getMainWindow()->tabifyDockWidget(lastDock, dock);
1539
    else
1540
        getMainWindow()->addDockWidget(dockArea, dock);
1541

1542
    auto w = this->widget(index);
1543
    removeTab(index);
1544
    w->deleteLater();
1545

1546
    if(!count())
1547
        hide();
1548

1549
    w = dock->titleBarWidget();
1550
    if(w && w->objectName() == QStringLiteral("OverlayTitle")) {
1551
        dock->setTitleBarWidget(nullptr);
1552
        w->deleteLater();
1553
    }
1554
    OverlayManager::instance()->setupTitleBar(dock);
1555

1556
    dock->setFeatures(dock->features() | QDockWidget::DockWidgetFloatable);
1557

1558
    setOverlayMode(dock, OverlayOption::Disable);
1559

1560
    saveTabs();
1561
}
1562

1563
void OverlayTabWidget::resizeEvent(QResizeEvent *ev)
1564
{
1565
    QTabWidget::resizeEvent(ev);
1566
    if (_state <= State::Normal)
1567
        timer.start(10);
1568
}
1569

1570
void OverlayTabWidget::setupLayout()
1571
{
1572
    if (_state > State::Normal)
1573
        return;
1574

1575
    if(count() == 1)
1576
        tabSize = 0;
1577
    else {
1578
        int tsize;
1579
        if(dockArea==Qt::LeftDockWidgetArea || dockArea==Qt::RightDockWidgetArea)
1580
            tsize = tabBar()->width();
1581
        else
1582
            tsize = tabBar()->height();
1583
        tabSize = tsize;
1584
    }
1585
    int titleBarSize = widgetMinSize(this, true);
1586
    QRect rect, rectTitle;
1587
    switch(tabPosition()) {
1588
    case West:
1589
        rectTitle = QRect(tabSize, 0, this->width()-tabSize, titleBarSize);
1590
        rect = QRect(rectTitle.left(), rectTitle.bottom(),
1591
                     rectTitle.width(), this->height()-rectTitle.height());
1592
        break;
1593
    case East:
1594
        rectTitle = QRect(0, 0, this->width()-tabSize, titleBarSize);
1595
        rect = QRect(rectTitle.left(), rectTitle.bottom(),
1596
                     rectTitle.width(), this->height()-rectTitle.height());
1597
        break;
1598
    case North:
1599
        rectTitle = QRect(0, tabSize, titleBarSize, this->height()-tabSize);
1600
        rect = QRect(rectTitle.right(), rectTitle.top(),
1601
                     this->width()-rectTitle.width(), rectTitle.height());
1602
        break;
1603
    case South:
1604
        rectTitle = QRect(0, 0, titleBarSize, this->height()-tabSize);
1605
        rect = QRect(rectTitle.right(), rectTitle.top(),
1606
                     this->width()-rectTitle.width(), rectTitle.height());
1607
        break;
1608
    }
1609
    if (_animation != 0.0) {
1610
        switch(dockArea) {
1611
        case Qt::LeftDockWidgetArea:
1612
            rect.moveLeft(rect.left() - _animation * rect.width());
1613
            break;
1614
        case Qt::RightDockWidgetArea:
1615
            rect.moveLeft(rect.left() + _animation * rect.width());
1616
            break;
1617
        case Qt::TopDockWidgetArea:
1618
            rect.moveTop(rect.top() - _animation * rect.height());
1619
            break;
1620
        case Qt::BottomDockWidgetArea:
1621
            rect.moveTop(rect.top() + _animation * rect.height());
1622
            break;
1623
        default:
1624
            break;
1625
        }
1626
    }
1627
    splitter->setGeometry(rect);
1628
    titleBar->setGeometry(rectTitle);
1629
}
1630

1631
void OverlayTabWidget::setCurrent(QDockWidget *widget)
1632
{
1633
    int index = dockWidgetIndex(widget);
1634
    if(index >= 0) 
1635
        setCurrentIndex(index);
1636
}
1637

1638
void OverlayTabWidget::onSplitterResize(int index)
1639
{
1640
    const auto &sizes = splitter->sizes();
1641
    if (index >= 0 && index < sizes.count()) {
1642
        if (sizes[index] == 0) {
1643
            if (currentIndex() == index) {
1644
                bool done = false;
1645
                for (int i=index+1; i<sizes.count(); ++i) {
1646
                    if (sizes[i] > 0) {
1647
                        setCurrentIndex(i);
1648
                        done = true;
1649
                        break;
1650
                    }
1651
                }
1652
                if (!done) {
1653
                    for (int i=index-1; i>=0 ;--i) {
1654
                        if (sizes[i] > 0) {
1655
                            setCurrentIndex(i);
1656
                            break;
1657
                        }
1658
                    }
1659
                }
1660
            }
1661
        } else
1662
            setCurrentIndex(index);
1663
    }
1664

1665
    saveTabs();
1666
}
1667

1668
void OverlayTabWidget::onCurrentChanged(int index)
1669
{
1670
    setState(State::Showing);
1671

1672
    auto sizes = splitter->sizes();
1673
    int i=0;
1674
    int size = splitter->orientation()==Qt::Vertical ? 
1675
                    height()-tabBar()->height() : width()-tabBar()->width();
1676
    for(auto &s : sizes) {
1677
        if(i++ == index)
1678
            s = size;
1679
        else
1680
            s = 0;
1681
    }
1682
    splitter->setSizes(sizes);
1683
    onSplitterResize(index);
1684
    saveTabs();
1685
}
1686

1687
void OverlayTabWidget::onSizeGripMove(const QPoint &p)
1688
{
1689
    if (!getMainWindow() || !getMainWindow()->getMdiArea())
1690
        return;
1691

1692
    QPoint pos = mapFromGlobal(p) + this->pos();
1693
    QPoint offset = getMainWindow()->getMdiArea()->pos();
1694
    QRect rect = this->rectOverlay.translated(offset);
1695

1696
    switch(dockArea) {
1697
    case Qt::LeftDockWidgetArea:
1698
        if (pos.x() - rect.left() < OverlayParams::getDockOverlayMinimumSize())
1699
            return;
1700
        rect.setRight(pos.x());
1701
        break;
1702
    case Qt::RightDockWidgetArea:
1703
        if (rect.right() - pos.x() < OverlayParams::getDockOverlayMinimumSize())
1704
            return;
1705
        rect.setLeft(pos.x());
1706
        break;
1707
    case Qt::TopDockWidgetArea:
1708
        if (pos.y() - rect.top() < OverlayParams::getDockOverlayMinimumSize())
1709
            return;
1710
        rect.setBottom(pos.y());
1711
        break;
1712
    default:
1713
        if (rect.bottom() - pos.y() < OverlayParams::getDockOverlayMinimumSize())
1714
            return;
1715
        rect.setTop(pos.y());
1716
        break;
1717
    }
1718
    this->setRect(rect.translated(-offset));
1719
    OverlayManager::instance()->refresh();
1720
}
1721

1722
QLayoutItem *OverlayTabWidget::prepareTitleWidget(QWidget *widget, const QList<QAction*> &actions)
1723
{
1724
    bool vertical = false;
1725
    QBoxLayout *layout = nullptr;
1726
    auto tabWidget = qobject_cast<OverlayTabWidget*>(widget->parentWidget());
1727
    if(!tabWidget) {
1728
        layout = new QBoxLayout(QBoxLayout::LeftToRight, widget); 
1729
    } else {
1730
        switch(tabWidget->getDockArea()) {
1731
            case Qt::LeftDockWidgetArea:
1732
                layout = new QBoxLayout(QBoxLayout::LeftToRight, widget); 
1733
                break;
1734
            case Qt::RightDockWidgetArea:
1735
                layout = new QBoxLayout(QBoxLayout::RightToLeft, widget); 
1736
                break;
1737
            case Qt::TopDockWidgetArea:
1738
                layout = new QBoxLayout(QBoxLayout::TopToBottom, widget); 
1739
                vertical = true;
1740
                break;
1741
            case Qt::BottomDockWidgetArea:
1742
                layout = new QBoxLayout(QBoxLayout::BottomToTop, widget); 
1743
                vertical = true;
1744
                break;
1745
            default:
1746
                break;
1747
        }
1748
    }
1749

1750
    layout->addSpacing(5);
1751
    layout->setContentsMargins(1,1,1,1);
1752
    int buttonSize = widgetMinSize(widget);
1753
    auto spacer = new QSpacerItem(buttonSize,buttonSize,
1754
            vertical?QSizePolicy::Minimum:QSizePolicy::Expanding,
1755
            vertical?QSizePolicy::Expanding:QSizePolicy::Minimum);
1756
    layout->addSpacerItem(spacer);
1757

1758
    for(auto action : actions)
1759
        layout->addWidget(OverlayTabWidget::createTitleButton(action, buttonSize));
1760

1761
    if (tabWidget) {
1762
        auto grip = new OverlaySizeGrip(tabWidget, vertical);
1763
        QObject::connect(grip, &OverlaySizeGrip::dragMove,
1764
                tabWidget, &OverlayTabWidget::onSizeGripMove);
1765
        layout->addWidget(grip);
1766
        grip->raise();
1767
    }
1768

1769
    return spacer;
1770
}
1771

1772
// -----------------------------------------------------------
1773

1774
OverlayTitleBar::OverlayTitleBar(QWidget * parent)
1775
    :QWidget(parent) 
1776
{
1777
    setFocusPolicy(Qt::ClickFocus);
1778
    setMouseTracking(true);
1779
    setCursor(Qt::OpenHandCursor);
1780
}
1781

1782
void OverlayTitleBar::setTitleItem(QLayoutItem *item)
1783
{
1784
    titleItem = item;
1785
}
1786

1787
void OverlayTitleBar::paintEvent(QPaintEvent *)
1788
{
1789
    if (!titleItem)
1790
        return;
1791

1792
    QDockWidget *dock = qobject_cast<QDockWidget*>(parentWidget());
1793
    int vertical = false;
1794
    int flags = Qt::AlignCenter;
1795
    if (!dock) {
1796
        OverlayTabWidget *tabWidget = qobject_cast<OverlayTabWidget*>(parentWidget());
1797
        if (tabWidget) {
1798
            switch(tabWidget->getDockArea()) {
1799
            case Qt::TopDockWidgetArea:
1800
                vertical = true;
1801
            // fallthrough
1802
            case Qt::RightDockWidgetArea:
1803
                flags = Qt::AlignRight;
1804
                break;
1805
            case Qt::BottomDockWidgetArea:
1806
                vertical = true;
1807
            // fallthrough
1808
            case Qt::LeftDockWidgetArea:
1809
                flags = Qt::AlignLeft;
1810
                break;
1811
            default:
1812
                break;
1813
            }
1814
            dock = tabWidget->dockWidget(0);
1815
        }
1816
    }
1817
    if (!dock)
1818
        return;
1819

1820
    QPainter painter(this);
1821
    if (qobject_cast<OverlayTabWidget*>(parentWidget()))
1822
        painter.fillRect(this->rect(), painter.background());
1823

1824
    QRect r = titleItem->geometry();
1825
    if (vertical) {
1826
        r = r.transposed();
1827
        painter.translate(r.left(), r.top() + r.width());
1828
        painter.rotate(-90);
1829
        painter.translate(-r.left(), -r.top());
1830
    }
1831

1832
    QString title;
1833
    if (OverlayManager::instance()->isMouseTransparent()) {
1834
        if (timerId == 0)
1835
            timerId = startTimer(500);
1836
        title = blink ? tr("Mouse pass through, ESC to stop") : dock->windowTitle();
1837
    } else {
1838
        if (timerId != 0) {
1839
            killTimer(timerId);
1840
            timerId = 0;
1841
        }
1842
        title = dock->windowTitle();
1843
    }
1844
    QString text = painter.fontMetrics().elidedText(
1845
            title, Qt::ElideRight, r.width());
1846
    painter.drawText(r, flags, text);
1847
}
1848

1849
void OverlayTitleBar::timerEvent(QTimerEvent *ev)
1850
{
1851
    if (timerId == ev->timerId()) {
1852
        update();
1853
        blink = !blink;
1854
    }
1855
}
1856

1857
static inline bool
1858
isNear(const QPoint &a, const QPoint &b, int tol = 16)
1859
{
1860
    QPoint d = a - b;
1861
    return d.x()*d.x() + d.y()*d.y() < tol;
1862
}
1863

1864
void OverlayTitleBar::endDrag()
1865
{
1866
    if (OverlayTabWidget::_Dragging == this) {
1867
        OverlayTabWidget::_Dragging = nullptr;
1868
        setCursor(Qt::OpenHandCursor);
1869
        if (OverlayTabWidget::_DragFrame)
1870
            OverlayTabWidget::_DragFrame->hide();
1871
        if (OverlayTabWidget::_DragFloating)
1872
            OverlayTabWidget::_DragFrame->hide();
1873
    }
1874
}
1875

1876
void OverlayTitleBar::mouseMoveEvent(QMouseEvent *me)
1877
{
1878
    if (ignoreMouse) {
1879
        if (!(me->buttons() & Qt::LeftButton))
1880
            ignoreMouse = false;
1881
        else {
1882
            me->ignore();
1883
            return;
1884
        }
1885
    }
1886

1887
    if (OverlayTabWidget::_Dragging != this && mouseMovePending && (me->buttons() & Qt::LeftButton)) {
1888
        if (isNear(dragOffset, me->pos()))
1889
            return;
1890
        mouseMovePending = false;
1891
        OverlayTabWidget::_Dragging = this;
1892
    }
1893

1894
    if (OverlayTabWidget::_Dragging != this)
1895
        return;
1896

1897
    if (!(me->buttons() & Qt::LeftButton)) {
1898
        endDrag();
1899
        return;
1900
    }
1901

1902
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1903
    QPoint point = me->globalPos();
1904
#else
1905
    QPoint point = me->globalPosition().toPoint();
1906
#endif
1907

1908
    OverlayManager::instance()->dragDockWidget(point,
1909
                                               parentWidget(),
1910
                                               dragOffset,
1911
                                               dragSize);
1912
}
1913

1914
void OverlayTitleBar::mousePressEvent(QMouseEvent *me)
1915
{
1916
    mouseMovePending = false;
1917
    QWidget *parent = parentWidget();
1918
    if (OverlayTabWidget::_Dragging || !parent || !getMainWindow() || me->button() != Qt::LeftButton)
1919
        return;
1920

1921
    dragSize = parent->size();
1922
    OverlayTabWidget *tabWidget = qobject_cast<OverlayTabWidget*>(parent);
1923
    if (!tabWidget) {
1924
        if(QApplication::queryKeyboardModifiers() == Qt::ShiftModifier) {
1925
            ignoreMouse = true;
1926
            me->ignore();
1927
            return;
1928
        }
1929
    }
1930
    else {
1931
        for (int s : tabWidget->getSplitter()->sizes()) {
1932
            if (!s)
1933
                continue;
1934
            if (tabWidget == OverlayTabWidget::_TopOverlay
1935
                    || tabWidget == OverlayTabWidget::_BottomOverlay) {
1936
                dragSize.setWidth(s + this->width());
1937
                dragSize.setHeight(tabWidget->height());
1938
            }
1939
            else {
1940
                dragSize.setHeight(s + this->height());
1941
                dragSize.setWidth(tabWidget->width());
1942
            }
1943
        }
1944
    }
1945
    ignoreMouse = false;
1946
    QSize mwSize = getMainWindow()->size();
1947
    dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(),
1948
                               static_cast<long>(std::min(mwSize.width()/2, dragSize.width()))));
1949
    dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(),
1950
                               static_cast<long>(std::min(mwSize.height()/2, dragSize.height()))));
1951

1952
    dragOffset = me->pos();
1953
    setCursor(Qt::ClosedHandCursor);
1954
    mouseMovePending = true;
1955
}
1956

1957
void OverlayTitleBar::mouseReleaseEvent(QMouseEvent *me)
1958
{
1959
    if (ignoreMouse) {
1960
        me->ignore();
1961
        return;
1962
    }
1963

1964
    setCursor(Qt::OpenHandCursor);
1965
    mouseMovePending = false;
1966
    if (OverlayTabWidget::_Dragging != this)
1967
        return;
1968

1969
    if (me->button() != Qt::LeftButton)
1970
        return;
1971

1972
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1973
    QPoint point = me->globalPos();
1974
#else
1975
    QPoint point = me->globalPosition().toPoint();
1976
#endif
1977

1978
    OverlayTabWidget::_Dragging = nullptr;
1979
    OverlayManager::instance()->dragDockWidget(point,
1980
                                               parentWidget(),
1981
                                               dragOffset,
1982
                                               dragSize,
1983
                                               true);
1984
    if (OverlayTabWidget::_DragFrame)
1985
        OverlayTabWidget::_DragFrame->hide();
1986
    if (OverlayTabWidget::_DragFloating)
1987
        OverlayTabWidget::_DragFloating->hide();
1988
}
1989

1990
void OverlayTitleBar::keyPressEvent(QKeyEvent *ke)
1991
{
1992
    if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape)
1993
        endDrag();
1994
}
1995

1996

1997
// -----------------------------------------------------------
1998

1999
OverlayDragFrame::OverlayDragFrame(QWidget * parent)
2000
    :QWidget(parent)
2001
{
2002
}
2003

2004
void OverlayDragFrame::paintEvent(QPaintEvent *)
2005
{
2006
    QPainter painter(this);
2007
    painter.drawRect(0, 0, this->width()-1, this->height()-1);
2008
    painter.setOpacity(0.3);
2009
    painter.setBrush(QBrush(Qt::blue));
2010
    painter.drawRect(0, 0, this->width()-1, this->height()-1);
2011
}
2012

2013
QSize OverlayDragFrame::sizeHint() const
2014
{
2015
    return size();
2016
}
2017

2018
QSize OverlayDragFrame::minimumSizeHint() const
2019
{
2020
    return minimumSize();
2021
}
2022

2023
// -----------------------------------------------------------
2024

2025
OverlaySizeGrip::OverlaySizeGrip(QWidget * parent, bool vertical)
2026
    :QWidget(parent), vertical(vertical)
2027
{
2028
    if (vertical) {
2029
        this->setFixedHeight(6);
2030
        this->setMinimumWidth(widgetMinSize(this,true));
2031
        this->setCursor(Qt::SizeVerCursor);
2032
    }
2033
    else {
2034
        this->setFixedWidth(6);
2035
        this->setMinimumHeight(widgetMinSize(this,true));
2036
        this->setCursor(Qt::SizeHorCursor);
2037
    }
2038
    setMouseTracking(true);
2039
}
2040

2041
void OverlaySizeGrip::paintEvent(QPaintEvent*)
2042
{
2043
    QPainter painter(this);
2044
    painter.setPen(Qt::transparent);
2045
    painter.setOpacity(0.5);
2046
    painter.setBrush(QBrush(Qt::black, Qt::Dense6Pattern));
2047
    QRect rect(this->rect());
2048
    painter.drawRect(rect);
2049
}
2050

2051
void OverlaySizeGrip::mouseMoveEvent(QMouseEvent *me)
2052
{
2053
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2054
    QPoint point = me->globalPos();
2055
#else
2056
    QPoint point = me->globalPosition().toPoint();
2057
#endif
2058

2059
    if ((me->buttons() & Qt::LeftButton)) {
2060
        Q_EMIT dragMove(point);
2061
    }
2062
}
2063

2064
void OverlaySizeGrip::mousePressEvent(QMouseEvent *)
2065
{
2066
}
2067

2068
void OverlaySizeGrip::mouseReleaseEvent(QMouseEvent *)
2069
{
2070
}
2071

2072
// -----------------------------------------------------------
2073

2074
OverlaySplitter::OverlaySplitter(QWidget *parent)
2075
    : QSplitter(parent)
2076
{
2077
}
2078

2079
QSplitterHandle * OverlaySplitter::createHandle()
2080
{
2081
    auto widget = new OverlaySplitterHandle(this->orientation(), this);
2082
    widget->setObjectName(QStringLiteral("OverlaySplitHandle"));
2083
    QList<QAction*> actions;
2084
    actions.append(&widget->actFloat);
2085
    widget->setTitleItem(OverlayTabWidget::prepareTitleWidget(widget, actions));
2086
    return widget;
2087
}
2088

2089
// -----------------------------------------------------------
2090

2091
OverlaySplitterHandle::OverlaySplitterHandle(Qt::Orientation orientation, QSplitter *parent)
2092
    : QSplitterHandle(orientation, parent)
2093
{
2094
    setMouseTracking(true);
2095
    setFocusPolicy(Qt::ClickFocus);
2096
    retranslate();
2097
    refreshIcons();
2098
    QObject::connect(&actFloat, &QAction::triggered, this, &OverlaySplitterHandle::onAction);
2099
    timer.setSingleShot(true);
2100
    QObject::connect(&timer, &QTimer::timeout, this, &OverlaySplitterHandle::onTimer);
2101
}
2102

2103
void OverlaySplitterHandle::refreshIcons()
2104
{
2105
    actFloat.setIcon(BitmapFactory().pixmap("qss:overlay/float.svg"));
2106
}
2107

2108
void OverlaySplitterHandle::onTimer()
2109
{
2110
    if (isVisible() && qApp->widgetAt(QCursor::pos()) != this)
2111
        showTitle(false);
2112
}
2113

2114
void OverlaySplitterHandle::showEvent(QShowEvent *ev)
2115
{
2116
    if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0
2117
            && qApp->widgetAt(QCursor::pos()) != this)
2118
        timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
2119
    QSplitterHandle::showEvent(ev);
2120
}
2121

2122
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2123
void OverlaySplitterHandle::enterEvent(QEvent *ev)
2124
#else
2125
void OverlaySplitterHandle::enterEvent(QEnterEvent *ev)
2126
#endif
2127
{
2128
    timer.stop();
2129
    QSplitterHandle::enterEvent(ev);
2130
}
2131

2132
void OverlaySplitterHandle::leaveEvent(QEvent *ev)
2133
{
2134
    if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0)
2135
        timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
2136
    QSplitterHandle::leaveEvent(ev);
2137
}
2138

2139
QSize OverlaySplitterHandle::sizeHint() const
2140
{ 
2141
    QSize size = QSplitterHandle::sizeHint();
2142
    int minSize = widgetMinSize(this,true);
2143
    if (this->orientation() == Qt::Vertical)
2144
        size.setHeight(std::max(minSize, size.height()));
2145
    else
2146
        size.setWidth(std::max(minSize, size.width()));
2147
    return size;
2148
}
2149

2150
void OverlaySplitterHandle::onAction()
2151
{
2152
    auto action = qobject_cast<QAction*>(sender());
2153
    if(action == &actFloat) {
2154
        QDockWidget *dock = dockWidget();
2155
        if (dock)
2156
            OverlayManager::instance()->floatDockWidget(dock);
2157
    }
2158
}
2159

2160
QDockWidget *OverlaySplitterHandle::dockWidget()
2161
{
2162
    QSplitter *parent = splitter();
2163
    if (!parent)
2164
        return nullptr;
2165

2166
    if (parent->handle(this->idx) != this) {
2167
        this->idx = -1;
2168
        for (int i=0, c=parent->count(); i<c; ++i) {
2169
            if (parent->handle(i) == this) {
2170
                this->idx = i;
2171
                break;
2172
            }
2173
        }
2174
    }
2175
    return qobject_cast<QDockWidget*>(parent->widget(this->idx));
2176
}
2177

2178
void OverlaySplitterHandle::retranslate()
2179
{
2180
    actFloat.setToolTip(QObject::tr("Toggle floating window"));
2181
}
2182

2183
void OverlaySplitterHandle::changeEvent(QEvent *e)
2184
{
2185
    QSplitterHandle::changeEvent(e);
2186
    if (e->type() == QEvent::LanguageChange)
2187
        retranslate();
2188
}
2189

2190
void OverlaySplitterHandle::setTitleItem(QLayoutItem *item)
2191
{
2192
    titleItem = item;
2193
}
2194

2195
void OverlaySplitterHandle::showTitle(bool enable)
2196
{
2197
    if (_showTitle == enable)
2198
        return;
2199
    if (!enable)
2200
        unsetCursor();
2201
    else {
2202
        setCursor(this->orientation() == Qt::Horizontal
2203
                ?  Qt::SizeHorCursor : Qt::SizeVerCursor);
2204
        if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0
2205
                && qApp->widgetAt(QCursor::pos()) != this)
2206
            timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
2207
    }
2208
    _showTitle = enable;
2209
    for (auto child : findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly))
2210
        child->setVisible(enable);
2211
    update();
2212
}
2213

2214
void OverlaySplitterHandle::paintEvent(QPaintEvent *e)
2215
{
2216
    if (!_showTitle)
2217
        return;
2218

2219
    if (!titleItem) {
2220
        QSplitterHandle::paintEvent(e);
2221
        return;
2222
    }
2223

2224
    int flags = Qt::AlignCenter;
2225
    auto tabWidget = qobject_cast<OverlayTabWidget*>(
2226
            splitter() ? splitter()->parentWidget() : nullptr);
2227

2228
    if (tabWidget) {
2229
        switch(tabWidget->getDockArea()) {
2230
        case Qt::TopDockWidgetArea:
2231
        case Qt::RightDockWidgetArea:
2232
            flags = Qt::AlignRight;
2233
            break;
2234
        case Qt::BottomDockWidgetArea:
2235
        case Qt::LeftDockWidgetArea:
2236
            flags = Qt::AlignLeft;
2237
            break;
2238
        default:
2239
            break;
2240
        }
2241
    }
2242

2243
    QDockWidget *dock = dockWidget();
2244
    if (!dock) {
2245
        QSplitterHandle::paintEvent(e);
2246
        return;
2247
    }
2248
    
2249
    QPainter painter(this);
2250
    painter.fillRect(this->rect(), painter.background());
2251

2252
    QRect r = titleItem->geometry();
2253
    if (this->orientation() != Qt::Vertical) {
2254
        r = r.transposed();
2255
        painter.translate(r.left(), r.top() + r.width());
2256
        painter.rotate(-90);
2257
        painter.translate(-r.left(), -r.top());
2258
    }
2259
    QString text = painter.fontMetrics().elidedText(
2260
            dock->windowTitle(), Qt::ElideRight, r.width());
2261

2262
    painter.drawText(r, flags, text);
2263
}
2264

2265
void OverlaySplitterHandle::endDrag()
2266
{
2267
    auto tabWidget = qobject_cast<OverlayTabWidget*>(splitter()->parentWidget());
2268
    if (tabWidget) {
2269
        dockWidget();
2270
        tabWidget->onSplitterResize(this->idx);
2271
    }
2272
    OverlayTabWidget::_Dragging = nullptr;
2273
    setCursor(this->orientation() == Qt::Horizontal
2274
            ?  Qt::SizeHorCursor : Qt::SizeVerCursor);
2275
    if (OverlayTabWidget::_DragFrame)
2276
        OverlayTabWidget::_DragFrame->hide();
2277
    if (OverlayTabWidget::_DragFloating)
2278
        OverlayTabWidget::_DragFloating->hide();
2279
}
2280

2281
void OverlaySplitterHandle::keyPressEvent(QKeyEvent *ke)
2282
{
2283
    if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape)
2284
        endDrag();
2285
}
2286

2287
void OverlaySplitterHandle::mouseMoveEvent(QMouseEvent *me)
2288
{
2289
    if (OverlayTabWidget::_Dragging != this)
2290
        return;
2291

2292
    if (!(me->buttons() & Qt::LeftButton)) {
2293
        endDrag();
2294
        return;
2295
    }
2296

2297
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2298
    QPoint point = me->globalPos();
2299
#else
2300
    QPoint point = me->globalPosition().toPoint();
2301
#endif
2302

2303
    if (dragging == 1) {
2304
        OverlayTabWidget *overlay = qobject_cast<OverlayTabWidget*>(
2305
                splitter()->parentWidget());
2306
        QPoint pos = me->pos();
2307
        if (overlay) {
2308
            switch(overlay->getDockArea()) {
2309
            case Qt::LeftDockWidgetArea:
2310
            case Qt::RightDockWidgetArea:
2311
                if (pos.x() < 0 || pos.x() > overlay->width())
2312
                    dragging = 2;
2313
                break;
2314
            case Qt::TopDockWidgetArea:
2315
            case Qt::BottomDockWidgetArea:
2316
                if (pos.y() < 0 || pos.y() > overlay->height())
2317
                    dragging = 2;
2318
                break;
2319
            default:
2320
                break;
2321
            }
2322
        }
2323
        if (dragging == 1) {
2324
            QPoint offset = parentWidget()->mapFromGlobal(point) - dragOffset;
2325
            moveSplitter(this->orientation() == Qt::Horizontal ? offset.x() : offset.y());
2326
            return;
2327
        }
2328
        setCursor(Qt::ClosedHandCursor);
2329
    }
2330

2331
    OverlayManager::instance()->dragDockWidget(point,
2332
                                               dockWidget(),
2333
                                               dragOffset,
2334
                                               dragSize);
2335
}
2336

2337
void OverlaySplitterHandle::mousePressEvent(QMouseEvent *me)
2338
{
2339
    if (OverlayTabWidget::_Dragging || !getMainWindow() || me->button() != Qt::LeftButton)
2340
        return;
2341

2342
    OverlayTabWidget::_Dragging = this;
2343
    dragging = 1;
2344
    dragOffset = me->pos();
2345
    auto dock = dockWidget();
2346
    if (dock) {
2347
        dragSize = dock->size();
2348
        dock->show();
2349
    } else
2350
        dragSize = QSize();
2351

2352
    QSize mwSize = getMainWindow()->size();
2353
    dragSize.setWidth(std::max(OverlayParams::getDockOverlayMinimumSize(),
2354
                               static_cast<long>(std::min(mwSize.width()/2, dragSize.width()))));
2355
    dragSize.setHeight(std::max(OverlayParams::getDockOverlayMinimumSize(),
2356
                                static_cast<long>(std::min(mwSize.height()/2, dragSize.height()))));
2357

2358
}
2359

2360
void OverlaySplitterHandle::mouseReleaseEvent(QMouseEvent *me)
2361
{
2362
    if (OverlayTabWidget::_Dragging != this || me->button() != Qt::LeftButton) 
2363
        return;
2364

2365
    if (dragging == 1) {
2366
        endDrag();
2367
        return;
2368
    }
2369
    endDrag();
2370

2371
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2372
    QPoint point = me->globalPos();
2373
#else
2374
    QPoint point = me->globalPosition().toPoint();
2375
#endif
2376

2377
    OverlayManager::instance()->dragDockWidget(point,
2378
                                               dockWidget(),
2379
                                               dragOffset,
2380
                                               dragSize,
2381
                                               true);
2382
    // Warning! the handle itself maybe deleted after return from
2383
    // dragDockWidget().
2384
}
2385

2386
// -----------------------------------------------------------
2387

2388
OverlayGraphicsEffect::OverlayGraphicsEffect(QObject *parent) :
2389
    QGraphicsEffect(parent),
2390
    _enabled(false),
2391
    _size(1,1),
2392
    _blurRadius(2.0f),
2393
    _color(0, 0, 0, 80)
2394
{
2395
}
2396

2397
QT_BEGIN_NAMESPACE
2398
  extern Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0 );
2399
QT_END_NAMESPACE
2400

2401
void OverlayGraphicsEffect::draw(QPainter* painter)
2402
{
2403
    // if nothing to show outside the item, just draw source
2404
    if (!_enabled || _blurRadius + _size.height() <= 0 || _blurRadius + _size.width() <= 0) {
2405
        drawSource(painter);
2406
        return;
2407
    }
2408

2409
    PixmapPadMode mode = QGraphicsEffect::PadToEffectiveBoundingRect;
2410
    QPoint offset;
2411
    QPixmap px = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);
2412

2413
    // return if no source
2414
    if (px.isNull())
2415
        return;
2416

2417
#if 0
2418
    if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
2419
        static int count;
2420
        getMainWindow()->showMessage(
2421
                QStringLiteral("dock overlay redraw %1").arg(count++));
2422
    }
2423
#endif
2424

2425
    QTransform restoreTransform = painter->worldTransform();
2426
    painter->setWorldTransform(QTransform());
2427

2428
    // Calculate size for the background image
2429
    QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
2430
    tmp.setDevicePixelRatio(px.devicePixelRatioF());
2431
    tmp.fill(0);
2432
    QPainter tmpPainter(&tmp);
2433
    QPainterPath clip;
2434
    tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
2435
    if(_size.width() == 0 && _size.height() == 0)
2436
        tmpPainter.drawPixmap(QPoint(0, 0), px);
2437
    else {
2438
        // exclude splitter handles
2439
        auto splitter = qobject_cast<QSplitter*>(parent());
2440
        if (splitter) {
2441
            int i = -1;
2442
            for (int size : splitter->sizes()) {
2443
                ++i;
2444
                if (!size)
2445
                    continue;
2446
                QWidget *w = splitter->widget(i);
2447
                if (w->findChild<TaskView::TaskView*>())
2448
                    continue;
2449
                QRect rect = w->geometry();
2450
                if (splitter->orientation() == Qt::Vertical)
2451
                    clip.addRect(rect.x(), rect.y()+4,
2452
                                rect.width(), rect.height()-4);
2453
                else
2454
                    clip.addRect(rect.x()+4, rect.y(),
2455
                                rect.width()-4, rect.height());
2456
            }
2457
            if (clip.isEmpty()) {
2458
                drawSource(painter);
2459
                return;
2460
            }
2461
            tmpPainter.setClipPath(clip);
2462
        }
2463

2464
        for (int x=-_size.width();x<=_size.width();++x) {
2465
            for (int y=-_size.height();y<=_size.height();++y) {
2466
                if (x || y) {
2467
                    tmpPainter.drawPixmap(QPoint(x, y), px);
2468
                    tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
2469
                }
2470
            }
2471
        }
2472
    }
2473
    tmpPainter.end();
2474

2475
    // blur the alpha channel
2476
    QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
2477
    blurred.setDevicePixelRatio(px.devicePixelRatioF());
2478
    blurred.fill(0);
2479
    QPainter blurPainter(&blurred);
2480
    qt_blurImage(&blurPainter, tmp, blurRadius(), false, true);
2481
    blurPainter.end();
2482

2483
    tmp = blurred;
2484

2485
    // blacken the image...
2486
    tmpPainter.begin(&tmp);
2487
    tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
2488
    tmpPainter.fillRect(tmp.rect(), color());
2489
    tmpPainter.end();
2490

2491
    // draw the blurred shadow...
2492
    painter->drawImage(QPointF(offset.x()+_offset.x(), offset.y()+_offset.y()), tmp);
2493

2494
    // draw the actual pixmap...
2495
    painter->drawPixmap(offset, px, QRectF());
2496

2497
#if 0
2498
    QWidget *focus = qApp->focusWidget();
2499
    if (focus) {
2500
        QWidget *widget = qobject_cast<QWidget*>(this->parent());
2501
        if (auto *edit = qobject_cast<QPlainTextEdit*>(focus)) {
2502
            if (!edit->isReadOnly() && edit->isEnabled()) {
2503
                for(auto w=edit->parentWidget(); w; w=w->parentWidget()) {
2504
                    if (w == widget) {
2505
                        QRect r = edit->cursorRect();
2506
                        QRect rect(edit->viewport()->mapTo(widget, r.topLeft()), 
2507
                                edit->viewport()->mapTo(widget, r.bottomRight()));
2508
                        // painter->fillRect(rect, edit->textColor());
2509
                        // painter->fillRect(rect, edit->currentCharFormat().foreground());
2510
                        painter->fillRect(rect.translated(offset), Qt::white);
2511
                    }
2512
                }
2513
            }
2514
        }
2515
    }
2516
#endif
2517

2518
    // restore world transform
2519
    painter->setWorldTransform(restoreTransform);
2520
}
2521

2522
QRectF OverlayGraphicsEffect::boundingRectFor(const QRectF& rect) const
2523
{
2524
    if (!_enabled)
2525
        return rect;
2526
    return rect.united(rect.adjusted(-_blurRadius - _size.width() + _offset.x(), 
2527
                                     -_blurRadius - _size.height()+ _offset.y(), 
2528
                                     _blurRadius + _size.width() + _offset.x(),
2529
                                     _blurRadius + _size.height() + _offset.y()));
2530
}
2531

2532
#include "moc_OverlayWidgets.cpp"
2533

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.