1
/****************************************************************************
2
* Copyright (c) 2020 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
4
* This file is part of the FreeCAD CAx development system. *
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. *
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. *
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 *
21
****************************************************************************/
23
#include "PreCompiled.h"
27
# include <QApplication>
30
# include <QDockWidget>
31
# include <QHeaderView>
37
# include <QSpacerItem>
40
# include <QTextStream>
41
# include <QTimerEvent>
47
#include <QPainterPath>
48
#include <QPropertyAnimation>
51
#include <unordered_map>
53
#include "OverlayWidgets.h"
55
#include <Base/Tools.h>
56
#include <Base/Console.h>
57
#include <App/Application.h>
58
#include "Application.h"
59
#include "BitmapFactory.h"
64
#include "MainWindow.h"
67
#include "OverlayManager.h"
68
#include "OverlayParams.h"
69
#include "TaskView/TaskView.h"
71
#include "TreeParams.h"
72
#include "propertyeditor/PropertyEditor.h"
74
FC_LOG_LEVEL_INIT("Dock", true, true);
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;
86
static inline int widgetMinSize(const QWidget *widget, bool margin=false)
88
return widget->fontMetrics().ascent()
89
+ widget->fontMetrics().descent() + (margin?4:0);
92
// -----------------------------------------------------------
94
OverlayProxyWidget::OverlayProxyWidget(OverlayTabWidget *tabOverlay)
95
:QWidget(tabOverlay->parentWidget()), owner(tabOverlay), _hintColor(QColor(50,50,50,150))
97
dockArea = owner->getDockArea();
98
timer.setSingleShot(true);
99
QObject::connect(&timer, &QTimer::timeout, this, &OverlayProxyWidget::onTimer);
100
setAttribute(Qt::WA_TransparentForMouseEvents, true);
103
bool OverlayProxyWidget::isActivated() const
105
return drawLine && isVisible();
108
OverlayProxyWidget::HitTest OverlayProxyWidget::hitTest(const QPoint &globalPt, bool delay)
110
if (!isVisible() || !owner->count())
111
return HitTest::HitNone;
113
auto pt = mapFromGlobal(globalPt);
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;
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)
128
case Qt::LeftDockWidgetArea:
129
if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() > 0 && pt.x() < hintSize)
130
hit = HitTest::HitOuter;
132
case Qt::RightDockWidgetArea:
133
if (pt.y() >= 0 && pt.y() <= s.height() && pt.x() < s.width() && pt.x() > -hintSize)
134
hit = HitTest::HitOuter;
136
case Qt::TopDockWidgetArea:
137
if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() > 0 && pt.y() < hintSize)
138
hit = HitTest::HitOuter;
140
case Qt::BottomDockWidgetArea:
141
if (pt.x() >= 0 && pt.x() <= s.width() && pt.y() < s.height() && pt.y() > -hintSize)
142
hit = HitTest::HitOuter;
145
if (rect.contains(pt)) {
146
hit = HitTest::HitInner;
147
ToolTip::showText(globalPt, QObject::tr("Press ESC to hide hint"), this);
151
if (owner->getState() == OverlayTabWidget::State::HintHidden) {
152
if (hit == HitTest::HitNone)
153
owner->setState(OverlayTabWidget::State::Normal);
155
hit = HitTest::HitNone;
159
if (hit != HitTest::HitNone) {
163
if (!timer.isActive())
164
timer.start(OverlayParams::getDockOverlayHintDelay());
168
owner->setState(OverlayTabWidget::State::Hint);
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;
186
case Qt::TopDockWidgetArea:
187
case Qt::BottomDockWidgetArea:
188
if (pos.x() < pt.x())
189
return HitTest::HitNone;
195
owner->setState(OverlayTabWidget::State::Showing);
198
} else if (!drawLine) {
201
if (!timer.isActive())
202
timer.start(OverlayParams::getDockOverlayHintDelay());
205
owner->setState(OverlayTabWidget::State::Normal);
213
void OverlayProxyWidget::onTimer()
215
hitTest(QCursor::pos(), false);
218
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
219
void OverlayProxyWidget::enterEvent(QEvent *)
221
void OverlayProxyWidget::enterEvent(QEnterEvent *)
228
if (!timer.isActive())
229
timer.start(OverlayParams::getDockOverlayHintDelay());
233
void OverlayProxyWidget::hideEvent(QHideEvent *)
238
void OverlayProxyWidget::onMousePress()
243
if (owner->getState() == OverlayTabWidget::State::HintHidden)
246
owner->setState(OverlayTabWidget::State::Showing);
249
QBrush OverlayProxyWidget::hintColor() const
254
void OverlayProxyWidget::setHintColor(const QBrush &brush)
259
QRect OverlayProxyWidget::getRect() const
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:
272
case Qt::TopDockWidgetArea:
273
case Qt::BottomDockWidgetArea:
274
rect.setLeft(pt.x());
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()));
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()));
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()));
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()));
311
void OverlayProxyWidget::paintEvent(QPaintEvent *)
315
QPainter painter(this);
316
painter.setOpacity(_hintColor.color().alphaF());
317
painter.setPen(Qt::transparent);
318
painter.setBrush(_hintColor);
320
QRect rect = this->getRect();
321
painter.drawRect(rect);
324
OverlayToolButton::OverlayToolButton(QWidget *parent)
327
setCursor(Qt::ArrowCursor);
330
// --------------------------------------------------------------------
332
OverlayTabWidget::OverlayTabWidget(QWidget *parent, Qt::DockWidgetArea pos)
333
:QTabWidget(parent), dockArea(pos)
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);
342
splitter = new OverlaySplitter(this);
344
_graphicsEffect = new OverlayGraphicsEffect(splitter);
345
splitter->setGraphicsEffect(_graphicsEffect);
347
_graphicsEffectTab = new OverlayGraphicsEffect(this);
348
_graphicsEffectTab->setEnabled(false);
349
tabBar()->setGraphicsEffect(_graphicsEffectTab);
351
Command *cmdHide = nullptr;
353
case Qt::LeftDockWidgetArea:
355
setTabPosition(QTabWidget::West);
356
splitter->setOrientation(Qt::Vertical);
357
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleLeft");
359
case Qt::RightDockWidgetArea:
360
_RightOverlay = this;
361
setTabPosition(QTabWidget::East);
362
splitter->setOrientation(Qt::Vertical);
363
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleRight");
365
case Qt::TopDockWidgetArea:
367
setTabPosition(QTabWidget::North);
368
splitter->setOrientation(Qt::Horizontal);
369
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleTop");
371
case Qt::BottomDockWidgetArea:
372
_BottomOverlay = this;
373
setTabPosition(QTabWidget::South);
374
splitter->setOrientation(Qt::Horizontal);
375
cmdHide = Application::Instance->commandManager().getCommandByName("Std_DockOverlayToggleBottom");
381
proxyWidget = new OverlayProxyWidget(this);
383
_setOverlayMode(proxyWidget,OverlayOption::Enable);
385
setOverlayMode(true);
388
actTransparent.setCheckable(true);
389
actTransparent.setData(QStringLiteral("OBTN Transparent"));
390
actTransparent.setParent(this);
391
addAction(&actTransparent);
393
actAutoHide.setData(QStringLiteral("OBTN AutoHide"));
395
actEditHide.setData(QStringLiteral("OBTN EditHide"));
397
actEditShow.setData(QStringLiteral("OBTN EditShow"));
399
actTaskShow.setData(QStringLiteral("OBTN TaskShow"));
401
actNoAutoMode.setData(QStringLiteral("OBTN NoAutoMode"));
403
actAutoMode.setData(QStringLiteral("OBTN AutoMode"));
404
actAutoMode.setParent(this);
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);
414
actOverlay.setData(QStringLiteral("OBTN Overlay"));
415
actOverlay.setParent(this);
416
addAction(&actOverlay);
419
cmdHide->addTo(this);
424
connect(tabBar(), &QTabBar::tabBarClicked, this, &OverlayTabWidget::onCurrentChanged);
425
connect(tabBar(), &QTabBar::tabMoved, this, &OverlayTabWidget::onTabMoved);
426
tabBar()->installEventFilter(this);
428
timer.setSingleShot(true);
429
connect(&timer, &QTimer::timeout, this, &OverlayTabWidget::setupLayout);
431
repaintTimer.setSingleShot(true);
432
connect(&repaintTimer, &QTimer::timeout, this, &OverlayTabWidget::onRepaint);
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);
441
void OverlayTabWidget::refreshIcons()
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");
451
case Qt::LeftDockWidgetArea:
452
actAutoHide.setIcon(pxAutoHide);
454
case Qt::RightDockWidgetArea:
455
actAutoHide.setIcon(pxAutoHide.transformed(QTransform().scale(-1,1)));
457
case Qt::TopDockWidgetArea:
458
actAutoHide.setIcon(pxAutoHide.transformed(QTransform().rotate(90)));
460
case Qt::BottomDockWidgetArea:
461
actAutoHide.setIcon(pxAutoHide.transformed(QTransform().rotate(-90)));
469
void OverlayTabWidget::onAnimationStateChanged()
471
if (_animator->state() != QAbstractAnimation::Running) {
473
if (_animator->startValue().toReal() == 0.0) {
475
OverlayManager::instance()->refresh();
477
if (_state == State::Showing)
478
setState(State::Normal);
482
void OverlayTabWidget::setAnimation(qreal t)
484
if (t != _animation) {
490
void OverlayTabWidget::startShow()
492
if (isVisible() || _state > State::Normal)
494
int duration = OverlayParams::getDockOverlayAnimationDuration();
495
bool setmode = _state != State::Showing;
497
_animator->setStartValue(1.0);
498
_animator->setEndValue(0.0);
499
_animator->setDuration(duration);
500
_animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve());
503
else if (_state == State::Showing)
504
setState(State::Normal);
509
setOverlayMode(overlaid);
512
QWidget *OverlayTabWidget::createTitleButton(QAction *action, int size)
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);
523
void OverlayTabWidget::startHide()
526
|| _state > State::Normal
527
|| (_animator->state() == QAbstractAnimation::Running
528
&& _animator->startValue().toReal() == 0.0))
530
int duration = OverlayParams::getDockOverlayAnimationDuration();
534
_animator->setStartValue(0.0);
535
_animator->setEndValue(1.0);
536
_animator->setDuration(duration);
537
_animator->setEasingCurve((QEasingCurve::Type)OverlayParams::getDockOverlayAnimationCurve());
542
bool OverlayTabWidget::event(QEvent *ev)
545
case QEvent::MouseButtonRelease:
546
if(mouseGrabber() == this) {
552
case QEvent::MouseMove:
553
case QEvent::ContextMenu:
554
if(QApplication::mouseButtons() == Qt::NoButton && mouseGrabber() == this) {
560
case QEvent::MouseButtonPress:
566
return QTabWidget::event(ev);
569
int OverlayTabWidget::testAlpha(const QPoint &_pos, int radiusScale)
571
if (!count() || (!isOverlaid() && !isTransparent()) || !isVisible())
574
if (tabBar()->isVisible() && tabBar()->tabAt(tabBar()->mapFromGlobal(_pos))>=0)
577
if (titleBar->isVisible() && titleBar->rect().contains(titleBar->mapFromGlobal(_pos)))
580
if (!splitter->isVisible())
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())
589
if (this->rect().contains(this->mapFromGlobal(_pos)))
594
if (_image.isNull()) {
595
auto pixmap = splitter->grab();
596
_imageScale = pixmap.devicePixelRatio();
597
_image = pixmap.toImage();
600
int res = qAlpha(_image.pixel(pos*_imageScale));
601
int radius = OverlayParams::getDockOverlayAlphaRadius() * radiusScale;
602
if (res || radius<=0 )
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())
612
res = qAlpha(_image.pixel(pos*_imageScale + QPoint(i,j)));
620
void OverlayTabWidget::paintEvent(QPaintEvent *ev)
622
Base::StateLocker guard(repainting);
624
if (!_image.isNull())
626
QTabWidget::paintEvent(ev);
629
void OverlayTabWidget::onRepaint()
631
Base::StateLocker guard(repainting);
633
if (!_image.isNull())
638
void OverlayTabWidget::scheduleRepaint()
644
repaintTimer.start(100);
648
QColor OverlayTabWidget::effectColor() const
650
return _graphicsEffect->color();
653
void OverlayTabWidget::setEffectColor(const QColor &color)
655
_graphicsEffect->setColor(color);
656
_graphicsEffectTab->setColor(color);
659
int OverlayTabWidget::effectWidth() const
661
return _graphicsEffect->size().width();
664
void OverlayTabWidget::setEffectWidth(int s)
666
auto size = _graphicsEffect->size();
668
_graphicsEffect->setSize(size);
669
_graphicsEffectTab->setSize(size);
672
int OverlayTabWidget::effectHeight() const
674
return _graphicsEffect->size().height();
677
void OverlayTabWidget::setEffectHeight(int s)
679
auto size = _graphicsEffect->size();
681
_graphicsEffect->setSize(size);
682
_graphicsEffectTab->setSize(size);
685
qreal OverlayTabWidget::effectOffsetX() const
687
return _graphicsEffect->offset().x();
690
void OverlayTabWidget::setEffectOffsetX(qreal d)
692
auto offset = _graphicsEffect->offset();
694
_graphicsEffect->setOffset(offset);
695
_graphicsEffectTab->setOffset(offset);
698
qreal OverlayTabWidget::effectOffsetY() const
700
return _graphicsEffect->offset().y();
703
void OverlayTabWidget::setEffectOffsetY(qreal d)
705
auto offset = _graphicsEffect->offset();
707
_graphicsEffect->setOffset(offset);
708
_graphicsEffectTab->setOffset(offset);
711
qreal OverlayTabWidget::effectBlurRadius() const
713
return _graphicsEffect->blurRadius();
716
void OverlayTabWidget::setEffectBlurRadius(qreal r)
718
_graphicsEffect->setBlurRadius(r);
719
_graphicsEffectTab->setBlurRadius(r);
722
bool OverlayTabWidget::effectEnabled() const
724
return _effectEnabled;
727
void OverlayTabWidget::setEffectEnabled(bool enable)
729
_effectEnabled = enable;
732
bool OverlayTabWidget::eventFilter(QObject *o, QEvent *ev)
734
if(ev->type() == QEvent::Resize && o == tabBar()) {
735
if (_state <= State::Normal)
738
return QTabWidget::eventFilter(o, ev);
741
void OverlayTabWidget::restore(ParameterGrp::handle handle)
749
std::string widgets = handle->GetASCII("Widgets","");
750
for(auto &name : QString::fromUtf8(widgets.c_str()).split(QLatin1Char(','))) {
753
OverlayManager::instance()->registerDockWidget(name, this);
754
auto dock = getMainWindow()->findChild<QDockWidget*>(name);
756
addWidget(dock, dock->windowTitle());
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);
767
case Qt::RightDockWidgetArea:
768
rect.moveRight(parentWidget()->size().width());
770
case Qt::BottomDockWidgetArea:
771
rect.moveBottom(parentWidget()->size().height());
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);
787
setAutoMode(AutoMode::NoAutoMode);
789
setTransparent(handle->GetBool("Transparent", false));
792
std::string savedSizes = handle->GetASCII("Sizes","");
795
for(auto &size : QString::fromUtf8(savedSizes.c_str()).split(QLatin1Char(','))) {
796
sizes.append(size.toInt());
797
_sizemap[dockWidget(idx++)] = sizes.back();
800
FC_LOG("restore " << objectName().toUtf8().constData() << " " << savedSizes);
802
getSplitter()->setSizes(sizes);
806
void OverlayTabWidget::saveTabs()
811
std::ostringstream os, os2;
813
auto sizes = splitter->sizes();
815
for(int i=0,c=splitter->count(); i<c; ++i) {
816
auto dock = dockWidget(i);
819
if(dock->objectName().size()) {
820
os << dock->objectName().toUtf8().constData() << ",";
827
_sizemap[dock] = sizes[i];
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());
835
void OverlayTabWidget::onTabMoved(int from, int to)
837
QWidget *w = splitter->widget(from);
838
splitter->insertWidget(to,w);
842
void OverlayTabWidget::setTitleBar(QWidget *w)
847
void OverlayTabWidget::changeEvent(QEvent *e)
849
QTabWidget::changeEvent(e);
850
if (e->type() == QEvent::LanguageChange)
854
void OverlayTabWidget::retranslate()
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"));
871
void OverlayTabWidget::syncAutoMode()
873
QAction *action = nullptr;
875
case AutoMode::AutoHide:
876
action = &actAutoHide;
878
case AutoMode::EditShow:
879
action = &actEditShow;
881
case AutoMode::TaskShow:
882
action = &actTaskShow;
884
case AutoMode::EditHide:
885
action = &actEditHide;
888
action = &actNoAutoMode;
891
actAutoMode.setIcon(action->icon());
892
if (action == &actNoAutoMode)
893
actAutoMode.setToolTip(tr("Select auto show/hide mode"));
895
actAutoMode.setToolTip(action->toolTip());
898
void OverlayTabWidget::onAction(QAction *action)
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);
914
else if(action == &actOverlay) {
915
OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive);
917
} else if(action == &actTransparent) {
919
Base::StateLocker lock(_saving);
920
hGrp->SetBool("Transparent", actTransparent.isChecked());
923
OverlayManager::instance()->refresh(this);
926
void OverlayTabWidget::setState(State state)
932
if (_state == State::Hidden) {
933
// Only unhide through State::Showing, not State::Normal
936
else if (_state == State::Showing) {
944
if (dockArea == Qt::RightDockWidgetArea)
945
setTabPosition(East);
946
else if (dockArea == Qt::BottomDockWidgetArea)
947
setTabPosition(South);
948
if (this->count() == 1)
950
_graphicsEffectTab->setEnabled(false);
953
if (state == State::Showing)
954
OverlayManager::instance()->refresh(this);
957
if (_state == State::HintHidden || _state == State::Hidden)
960
if (this->count() && OverlayParams::getDockOverlayHintTabBar()) {
961
tabBar()->setToolTip(proxyWidget->toolTip());
965
_graphicsEffectTab->setEnabled(true);
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);
976
case State::HintHidden:
977
if (_state != State::Hidden)
981
_graphicsEffectTab->setEnabled(true);
992
bool OverlayTabWidget::checkAutoHide() const
994
if(autoMode == AutoMode::AutoHide)
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())
1006
if(autoMode == AutoMode::EditShow) {
1007
return !Application::Instance->editDocument()
1008
&& (!Control().taskPanel() || Control().taskPanel()->isEmpty(false));
1011
if(autoMode == AutoMode::EditHide && Application::Instance->editDocument())
1017
void OverlayTabWidget::leaveEvent(QEvent*)
1019
if (titleBar && QWidget::mouseGrabber() == titleBar)
1021
OverlayManager::instance()->refresh();
1024
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1025
void OverlayTabWidget::enterEvent(QEvent*)
1027
void OverlayTabWidget::enterEvent(QEnterEvent*)
1030
revealTime = QTime();
1031
OverlayManager::instance()->refresh();
1034
void OverlayTabWidget::setRevealTime(const QTime &time)
1039
void OverlayTabWidget::_setOverlayMode(QWidget *widget, OverlayOption option)
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());
1049
if (qobject_cast<QScrollBar*>(widget)) {
1050
auto parent = widget->parentWidget();
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());
1060
static QString _style = QStringLiteral("*{width:0}");
1061
widget->setStyleSheet(_style);
1065
auto treeView = qobject_cast<TreeWidget*>(parent);
1067
auto scrollArea = static_cast<QAbstractScrollArea*>(parent);
1068
if (scrollArea->verticalScrollBar() == widget) {
1069
if (!TreeParams::getHideScrollBar() || option == OverlayOption::Disable)
1070
widget->setStyleSheet(QString());
1072
static QString _style = QStringLiteral("*{width:0}");
1073
widget->setStyleSheet(_style);
1079
auto header = treeView->header();
1080
if (!TreeParams::getHideHeaderView() || option==OverlayOption::Disable)
1081
header->setStyleSheet(QString());
1083
static QString _style = QStringLiteral(
1084
"QHeaderView:section {"
1086
"background-color: transparent;"
1088
"border: transparent;}");
1089
header->setStyleSheet(_style);
1095
auto tabbar = qobject_cast<QTabBar*>(widget);
1097
if(!tabbar->autoHide() || tabbar->count()>1) {
1098
if(!OverlayManager::instance()->getHideTab())
1099
tabbar->setVisible(true);
1101
tabbar->setVisible(option == OverlayOption::Disable
1102
|| (option == OverlayOption::ShowTab && tabbar->count()>1));
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);
1112
widget->setWindowFlags(widget->windowFlags() & ~Qt::FramelessWindowHint);
1114
widget->setAttribute(Qt::WA_NoSystemBackground, option != OverlayOption::Disable);
1115
widget->setAttribute(Qt::WA_TranslucentBackground, option != OverlayOption::Disable);
1119
void OverlayTabWidget::setOverlayMode(QWidget *widget, OverlayOption option)
1121
if(!widget || (qobject_cast<QDialog*>(widget)
1122
&& !qobject_cast<Dialog::Clipping*>(widget))
1123
|| qobject_cast<TaskView::TaskBox*>(widget))
1126
if(widget != tabBar()) {
1127
if(OverlayParams::getDockOverlayAutoMouseThrough()
1128
&& option == OverlayOption::ShowTab) {
1129
widget->setMouseTracking(true);
1133
_setOverlayMode(widget, option);
1135
if(qobject_cast<QComboBox*>(widget)) {
1136
// do not set child QAbstractItemView of QComboBox, otherwise the drop down box
1140
for(auto child : widget->children())
1141
setOverlayMode(qobject_cast<QWidget*>(child), option);
1144
void OverlayTabWidget::setTransparent(bool enable)
1146
if(actTransparent.isChecked() == enable)
1149
Base::StateLocker lock(_saving);
1150
hGrp->SetBool("Transparent", enable);
1152
actTransparent.setChecked(enable);
1153
OverlayManager::instance()->refresh(this);
1156
bool OverlayTabWidget::isTransparent() const
1158
if (!actTransparent.isChecked())
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())
1171
bool OverlayTabWidget::isOverlaid(QueryOption option) const
1173
if (option != QueryOption::QueryOverlay
1174
&& currentTransparent != isTransparent())
1175
return option == QueryOption::TransparencyChanged;
1179
void OverlayTabWidget::setAutoMode(AutoMode mode)
1181
if (autoMode == mode)
1186
bool autohide = false, editshow = false, edithide = false, taskshow = false;
1188
case AutoMode::AutoHide:
1191
case AutoMode::EditShow:
1194
case AutoMode::EditHide:
1197
case AutoMode::TaskShow:
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);
1210
OverlayManager::instance()->refresh(this);
1213
QDockWidget *OverlayTabWidget::currentDockWidget() const
1216
for(int size : splitter->sizes()) {
1219
return dockWidget(index);
1221
return dockWidget(currentIndex());
1224
QDockWidget *OverlayTabWidget::dockWidget(int index) const
1226
if(index < 0 || index >= splitter->count())
1228
return qobject_cast<QDockWidget*>(splitter->widget(index));
1231
void OverlayTabWidget::updateSplitterHandles()
1233
if (overlaid || _state > State::Normal)
1235
for (int i=0, c=splitter->count(); i<c; ++i) {
1236
auto handle = qobject_cast<OverlaySplitterHandle*>(splitter->handle(i));
1238
handle->showTitle(true);
1242
bool OverlayTabWidget::onEscape()
1244
if (getState() == OverlayTabWidget::State::Hint
1245
|| getState() == OverlayTabWidget::State::Hidden) {
1246
setState(OverlayTabWidget::State::HintHidden);
1251
if (titleBar->isVisible() && titleBar->underMouse()) {
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);
1265
void OverlayTabWidget::setOverlayMode(bool enable)
1269
if(!isVisible() || !count())
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));
1279
handle->showTitle(!enable);
1284
stylesheet = OverlayManager::instance()->getStyleSheet();
1285
currentTransparent = isTransparent();
1287
OverlayOption option;
1288
if(!enable && isTransparent()) {
1289
option = OverlayOption::ShowTab;
1292
&& (autoMode == AutoMode::EditShow || autoMode == AutoMode::AutoHide)) {
1293
option = OverlayOption::Disable;
1295
option = enable?OverlayOption::Enable:OverlayOption::Disable;
1298
proxyWidget->setStyleSheet(stylesheet);
1299
this->setStyleSheet(stylesheet);
1300
setOverlayMode(this, option);
1302
_graphicsEffect->setEnabled(effectEnabled() && (enable || isTransparent()));
1304
if (_state == State::Hint && OverlayParams::getDockOverlayHintTabBar()) {
1305
tabBar()->setToolTip(proxyWidget->toolTip());
1307
} else if (OverlayParams::getDockOverlayHideTabBar() || count()==1) {
1310
tabBar()->setToolTip(QString());
1311
tabBar()->setVisible(!enable || !OverlayManager::instance()->getHideTab());
1314
setRect(rectOverlay);
1317
const QRect &OverlayTabWidget::getRect()
1322
bool OverlayTabWidget::getAutoHideRect(QRect &rect) const
1325
int hintWidth = OverlayParams::getDockOverlayHintSize();
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));
1334
rect.setRight(rect.right() - std::max(rect.width()-hintWidth,0));
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));
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()));
1353
return _state != State::Showing && overlaid && checkAutoHide();
1356
void OverlayTabWidget::setOffset(const QSize &ofs)
1361
Base::StateLocker lock(_saving);
1362
hGrp->SetInt("Offset1", ofs.width());
1363
hGrp->SetInt("Offset3", ofs.height());
1368
void OverlayTabWidget::setSizeDelta(int delta)
1370
if(sizeDelta != delta) {
1372
Base::StateLocker lock(_saving);
1373
hGrp->SetInt("Offset2", delta);
1379
void OverlayTabWidget::setRect(QRect rect)
1381
if(busy || !parentWidget() || !getMainWindow() || !getMainWindow()->getMdiArea())
1384
if (rect.width() == 0)
1385
rect.setWidth(OverlayParams::getDockOverlayMinimumSize()*3);
1386
if (rect.height() == 0)
1387
rect.setHeight(OverlayParams::getDockOverlayMinimumSize()*3);
1390
case Qt::LeftDockWidgetArea:
1392
if (rect.width() < OverlayParams::getDockOverlayMinimumSize())
1393
rect.setWidth(OverlayParams::getDockOverlayMinimumSize());
1395
case Qt::RightDockWidgetArea:
1396
if (rect.width() < OverlayParams::getDockOverlayMinimumSize())
1397
rect.setLeft(rect.right()-OverlayParams::getDockOverlayMinimumSize());
1399
case Qt::TopDockWidgetArea:
1401
if (rect.height() < OverlayParams::getDockOverlayMinimumSize())
1402
rect.setHeight(OverlayParams::getDockOverlayMinimumSize());
1404
case Qt::BottomDockWidgetArea:
1405
if (rect.height() < OverlayParams::getDockOverlayMinimumSize())
1406
rect.setTop(rect.bottom()-OverlayParams::getDockOverlayMinimumSize());
1412
if(hGrp && rect.size() != rectOverlay.size()) {
1413
Base::StateLocker lock(_saving);
1414
hGrp->SetInt("Width", rect.width());
1415
hGrp->SetInt("Height", rect.height());
1419
QPoint offset = getMainWindow()->getMdiArea()->pos();
1421
if(getAutoHideRect(rect) || _state == State::Hint || _state == State::Hidden) {
1422
QRect rectHint = rect;
1423
if (_state != State::Hint && _state != State::Hidden)
1425
else if (count() && OverlayParams::getDockOverlayHintTabBar()) {
1427
case Qt::LeftDockWidgetArea:
1428
case Qt::RightDockWidgetArea:
1429
if (dockArea == Qt::LeftDockWidgetArea)
1430
rect.setWidth(tabBar()->width());
1432
rect.setLeft(rect.left() + rect.width() - tabBar()->width());
1433
rect.setHeight(std::max(rect.height(),
1434
tabBar()->y() + tabBar()->sizeHint().height() + 5));
1436
case Qt::BottomDockWidgetArea:
1437
case Qt::TopDockWidgetArea:
1438
if (dockArea == Qt::TopDockWidgetArea)
1439
rect.setHeight(tabBar()->height());
1441
rect.setTop(rect.top() + rect.height() - tabBar()->height());
1442
rect.setWidth(std::max(rect.width(),
1443
tabBar()->x() + tabBar()->sizeHint().width() + 5));
1449
setGeometry(rect.translated(offset));
1451
proxyWidget->setGeometry(rectHint.translated(offset));
1453
proxyWidget->show();
1454
proxyWidget->raise();
1456
proxyWidget->hide();
1459
setGeometry(rectOverlay.translated(offset));
1461
for(int i=0, count=splitter->count(); i<count; ++i)
1462
splitter->widget(i)->show();
1464
if(!isVisible() && count()) {
1465
proxyWidget->hide();
1471
void OverlayTabWidget::addWidget(QDockWidget *dock, const QString &title)
1473
if (!getMainWindow() || !getMainWindow()->getMdiArea())
1476
OverlayManager::instance()->registerDockWidget(dock->objectName(), this);
1478
OverlayManager::setFocusView();
1479
getMainWindow()->removeDockWidget(dock);
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);
1489
titleWidget->deleteLater();
1493
splitter->addWidget(dock);
1494
auto dummyWidget = new QWidget(this);
1495
addTab(dummyWidget, title);
1496
connect(dock, &QObject::destroyed, dummyWidget, &QObject::deleteLater);
1498
dock->setFeatures(dock->features() & ~QDockWidget::DockWidgetFloatable);
1500
QRect rect = dock->geometry();
1501
QSize sizeMain = getMainWindow()->getMdiArea()->size();
1503
case Qt::LeftDockWidgetArea:
1504
case Qt::RightDockWidgetArea:
1505
if (rect.width() > sizeMain.width()/3)
1506
rect.setWidth(sizeMain.width()/3);
1508
case Qt::TopDockWidgetArea:
1509
case Qt::BottomDockWidgetArea:
1510
if (rect.height() > sizeMain.height()/3)
1511
rect.setHeight(sizeMain.height()/3);
1522
int OverlayTabWidget::dockWidgetIndex(QDockWidget *dock) const
1524
return splitter->indexOf(dock);
1527
void OverlayTabWidget::removeWidget(QDockWidget *dock, QDockWidget *lastDock)
1529
int index = dockWidgetIndex(dock);
1533
OverlayManager::instance()->unregisterDockWidget(dock->objectName(), this);
1535
OverlayManager::setFocusView();
1538
getMainWindow()->tabifyDockWidget(lastDock, dock);
1540
getMainWindow()->addDockWidget(dockArea, dock);
1542
auto w = this->widget(index);
1549
w = dock->titleBarWidget();
1550
if(w && w->objectName() == QStringLiteral("OverlayTitle")) {
1551
dock->setTitleBarWidget(nullptr);
1554
OverlayManager::instance()->setupTitleBar(dock);
1556
dock->setFeatures(dock->features() | QDockWidget::DockWidgetFloatable);
1558
setOverlayMode(dock, OverlayOption::Disable);
1563
void OverlayTabWidget::resizeEvent(QResizeEvent *ev)
1565
QTabWidget::resizeEvent(ev);
1566
if (_state <= State::Normal)
1570
void OverlayTabWidget::setupLayout()
1572
if (_state > State::Normal)
1579
if(dockArea==Qt::LeftDockWidgetArea || dockArea==Qt::RightDockWidgetArea)
1580
tsize = tabBar()->width();
1582
tsize = tabBar()->height();
1585
int titleBarSize = widgetMinSize(this, true);
1586
QRect rect, rectTitle;
1587
switch(tabPosition()) {
1589
rectTitle = QRect(tabSize, 0, this->width()-tabSize, titleBarSize);
1590
rect = QRect(rectTitle.left(), rectTitle.bottom(),
1591
rectTitle.width(), this->height()-rectTitle.height());
1594
rectTitle = QRect(0, 0, this->width()-tabSize, titleBarSize);
1595
rect = QRect(rectTitle.left(), rectTitle.bottom(),
1596
rectTitle.width(), this->height()-rectTitle.height());
1599
rectTitle = QRect(0, tabSize, titleBarSize, this->height()-tabSize);
1600
rect = QRect(rectTitle.right(), rectTitle.top(),
1601
this->width()-rectTitle.width(), rectTitle.height());
1604
rectTitle = QRect(0, 0, titleBarSize, this->height()-tabSize);
1605
rect = QRect(rectTitle.right(), rectTitle.top(),
1606
this->width()-rectTitle.width(), rectTitle.height());
1609
if (_animation != 0.0) {
1611
case Qt::LeftDockWidgetArea:
1612
rect.moveLeft(rect.left() - _animation * rect.width());
1614
case Qt::RightDockWidgetArea:
1615
rect.moveLeft(rect.left() + _animation * rect.width());
1617
case Qt::TopDockWidgetArea:
1618
rect.moveTop(rect.top() - _animation * rect.height());
1620
case Qt::BottomDockWidgetArea:
1621
rect.moveTop(rect.top() + _animation * rect.height());
1627
splitter->setGeometry(rect);
1628
titleBar->setGeometry(rectTitle);
1631
void OverlayTabWidget::setCurrent(QDockWidget *widget)
1633
int index = dockWidgetIndex(widget);
1635
setCurrentIndex(index);
1638
void OverlayTabWidget::onSplitterResize(int index)
1640
const auto &sizes = splitter->sizes();
1641
if (index >= 0 && index < sizes.count()) {
1642
if (sizes[index] == 0) {
1643
if (currentIndex() == index) {
1645
for (int i=index+1; i<sizes.count(); ++i) {
1653
for (int i=index-1; i>=0 ;--i) {
1662
setCurrentIndex(index);
1668
void OverlayTabWidget::onCurrentChanged(int index)
1670
setState(State::Showing);
1672
auto sizes = splitter->sizes();
1674
int size = splitter->orientation()==Qt::Vertical ?
1675
height()-tabBar()->height() : width()-tabBar()->width();
1676
for(auto &s : sizes) {
1682
splitter->setSizes(sizes);
1683
onSplitterResize(index);
1687
void OverlayTabWidget::onSizeGripMove(const QPoint &p)
1689
if (!getMainWindow() || !getMainWindow()->getMdiArea())
1692
QPoint pos = mapFromGlobal(p) + this->pos();
1693
QPoint offset = getMainWindow()->getMdiArea()->pos();
1694
QRect rect = this->rectOverlay.translated(offset);
1697
case Qt::LeftDockWidgetArea:
1698
if (pos.x() - rect.left() < OverlayParams::getDockOverlayMinimumSize())
1700
rect.setRight(pos.x());
1702
case Qt::RightDockWidgetArea:
1703
if (rect.right() - pos.x() < OverlayParams::getDockOverlayMinimumSize())
1705
rect.setLeft(pos.x());
1707
case Qt::TopDockWidgetArea:
1708
if (pos.y() - rect.top() < OverlayParams::getDockOverlayMinimumSize())
1710
rect.setBottom(pos.y());
1713
if (rect.bottom() - pos.y() < OverlayParams::getDockOverlayMinimumSize())
1715
rect.setTop(pos.y());
1718
this->setRect(rect.translated(-offset));
1719
OverlayManager::instance()->refresh();
1722
QLayoutItem *OverlayTabWidget::prepareTitleWidget(QWidget *widget, const QList<QAction*> &actions)
1724
bool vertical = false;
1725
QBoxLayout *layout = nullptr;
1726
auto tabWidget = qobject_cast<OverlayTabWidget*>(widget->parentWidget());
1728
layout = new QBoxLayout(QBoxLayout::LeftToRight, widget);
1730
switch(tabWidget->getDockArea()) {
1731
case Qt::LeftDockWidgetArea:
1732
layout = new QBoxLayout(QBoxLayout::LeftToRight, widget);
1734
case Qt::RightDockWidgetArea:
1735
layout = new QBoxLayout(QBoxLayout::RightToLeft, widget);
1737
case Qt::TopDockWidgetArea:
1738
layout = new QBoxLayout(QBoxLayout::TopToBottom, widget);
1741
case Qt::BottomDockWidgetArea:
1742
layout = new QBoxLayout(QBoxLayout::BottomToTop, widget);
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);
1758
for(auto action : actions)
1759
layout->addWidget(OverlayTabWidget::createTitleButton(action, buttonSize));
1762
auto grip = new OverlaySizeGrip(tabWidget, vertical);
1763
QObject::connect(grip, &OverlaySizeGrip::dragMove,
1764
tabWidget, &OverlayTabWidget::onSizeGripMove);
1765
layout->addWidget(grip);
1772
// -----------------------------------------------------------
1774
OverlayTitleBar::OverlayTitleBar(QWidget * parent)
1777
setFocusPolicy(Qt::ClickFocus);
1778
setMouseTracking(true);
1779
setCursor(Qt::OpenHandCursor);
1782
void OverlayTitleBar::setTitleItem(QLayoutItem *item)
1787
void OverlayTitleBar::paintEvent(QPaintEvent *)
1792
QDockWidget *dock = qobject_cast<QDockWidget*>(parentWidget());
1793
int vertical = false;
1794
int flags = Qt::AlignCenter;
1796
OverlayTabWidget *tabWidget = qobject_cast<OverlayTabWidget*>(parentWidget());
1798
switch(tabWidget->getDockArea()) {
1799
case Qt::TopDockWidgetArea:
1802
case Qt::RightDockWidgetArea:
1803
flags = Qt::AlignRight;
1805
case Qt::BottomDockWidgetArea:
1808
case Qt::LeftDockWidgetArea:
1809
flags = Qt::AlignLeft;
1814
dock = tabWidget->dockWidget(0);
1820
QPainter painter(this);
1821
if (qobject_cast<OverlayTabWidget*>(parentWidget()))
1822
painter.fillRect(this->rect(), painter.background());
1824
QRect r = titleItem->geometry();
1827
painter.translate(r.left(), r.top() + r.width());
1828
painter.rotate(-90);
1829
painter.translate(-r.left(), -r.top());
1833
if (OverlayManager::instance()->isMouseTransparent()) {
1835
timerId = startTimer(500);
1836
title = blink ? tr("Mouse pass through, ESC to stop") : dock->windowTitle();
1842
title = dock->windowTitle();
1844
QString text = painter.fontMetrics().elidedText(
1845
title, Qt::ElideRight, r.width());
1846
painter.drawText(r, flags, text);
1849
void OverlayTitleBar::timerEvent(QTimerEvent *ev)
1851
if (timerId == ev->timerId()) {
1858
isNear(const QPoint &a, const QPoint &b, int tol = 16)
1861
return d.x()*d.x() + d.y()*d.y() < tol;
1864
void OverlayTitleBar::endDrag()
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();
1876
void OverlayTitleBar::mouseMoveEvent(QMouseEvent *me)
1879
if (!(me->buttons() & Qt::LeftButton))
1880
ignoreMouse = false;
1887
if (OverlayTabWidget::_Dragging != this && mouseMovePending && (me->buttons() & Qt::LeftButton)) {
1888
if (isNear(dragOffset, me->pos()))
1890
mouseMovePending = false;
1891
OverlayTabWidget::_Dragging = this;
1894
if (OverlayTabWidget::_Dragging != this)
1897
if (!(me->buttons() & Qt::LeftButton)) {
1902
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1903
QPoint point = me->globalPos();
1905
QPoint point = me->globalPosition().toPoint();
1908
OverlayManager::instance()->dragDockWidget(point,
1914
void OverlayTitleBar::mousePressEvent(QMouseEvent *me)
1916
mouseMovePending = false;
1917
QWidget *parent = parentWidget();
1918
if (OverlayTabWidget::_Dragging || !parent || !getMainWindow() || me->button() != Qt::LeftButton)
1921
dragSize = parent->size();
1922
OverlayTabWidget *tabWidget = qobject_cast<OverlayTabWidget*>(parent);
1924
if(QApplication::queryKeyboardModifiers() == Qt::ShiftModifier) {
1931
for (int s : tabWidget->getSplitter()->sizes()) {
1934
if (tabWidget == OverlayTabWidget::_TopOverlay
1935
|| tabWidget == OverlayTabWidget::_BottomOverlay) {
1936
dragSize.setWidth(s + this->width());
1937
dragSize.setHeight(tabWidget->height());
1940
dragSize.setHeight(s + this->height());
1941
dragSize.setWidth(tabWidget->width());
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()))));
1952
dragOffset = me->pos();
1953
setCursor(Qt::ClosedHandCursor);
1954
mouseMovePending = true;
1957
void OverlayTitleBar::mouseReleaseEvent(QMouseEvent *me)
1964
setCursor(Qt::OpenHandCursor);
1965
mouseMovePending = false;
1966
if (OverlayTabWidget::_Dragging != this)
1969
if (me->button() != Qt::LeftButton)
1972
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1973
QPoint point = me->globalPos();
1975
QPoint point = me->globalPosition().toPoint();
1978
OverlayTabWidget::_Dragging = nullptr;
1979
OverlayManager::instance()->dragDockWidget(point,
1984
if (OverlayTabWidget::_DragFrame)
1985
OverlayTabWidget::_DragFrame->hide();
1986
if (OverlayTabWidget::_DragFloating)
1987
OverlayTabWidget::_DragFloating->hide();
1990
void OverlayTitleBar::keyPressEvent(QKeyEvent *ke)
1992
if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape)
1997
// -----------------------------------------------------------
1999
OverlayDragFrame::OverlayDragFrame(QWidget * parent)
2004
void OverlayDragFrame::paintEvent(QPaintEvent *)
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);
2013
QSize OverlayDragFrame::sizeHint() const
2018
QSize OverlayDragFrame::minimumSizeHint() const
2020
return minimumSize();
2023
// -----------------------------------------------------------
2025
OverlaySizeGrip::OverlaySizeGrip(QWidget * parent, bool vertical)
2026
:QWidget(parent), vertical(vertical)
2029
this->setFixedHeight(6);
2030
this->setMinimumWidth(widgetMinSize(this,true));
2031
this->setCursor(Qt::SizeVerCursor);
2034
this->setFixedWidth(6);
2035
this->setMinimumHeight(widgetMinSize(this,true));
2036
this->setCursor(Qt::SizeHorCursor);
2038
setMouseTracking(true);
2041
void OverlaySizeGrip::paintEvent(QPaintEvent*)
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);
2051
void OverlaySizeGrip::mouseMoveEvent(QMouseEvent *me)
2053
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2054
QPoint point = me->globalPos();
2056
QPoint point = me->globalPosition().toPoint();
2059
if ((me->buttons() & Qt::LeftButton)) {
2060
Q_EMIT dragMove(point);
2064
void OverlaySizeGrip::mousePressEvent(QMouseEvent *)
2068
void OverlaySizeGrip::mouseReleaseEvent(QMouseEvent *)
2072
// -----------------------------------------------------------
2074
OverlaySplitter::OverlaySplitter(QWidget *parent)
2079
QSplitterHandle * OverlaySplitter::createHandle()
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));
2089
// -----------------------------------------------------------
2091
OverlaySplitterHandle::OverlaySplitterHandle(Qt::Orientation orientation, QSplitter *parent)
2092
: QSplitterHandle(orientation, parent)
2094
setMouseTracking(true);
2095
setFocusPolicy(Qt::ClickFocus);
2098
QObject::connect(&actFloat, &QAction::triggered, this, &OverlaySplitterHandle::onAction);
2099
timer.setSingleShot(true);
2100
QObject::connect(&timer, &QTimer::timeout, this, &OverlaySplitterHandle::onTimer);
2103
void OverlaySplitterHandle::refreshIcons()
2105
actFloat.setIcon(BitmapFactory().pixmap("qss:overlay/float.svg"));
2108
void OverlaySplitterHandle::onTimer()
2110
if (isVisible() && qApp->widgetAt(QCursor::pos()) != this)
2114
void OverlaySplitterHandle::showEvent(QShowEvent *ev)
2116
if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0
2117
&& qApp->widgetAt(QCursor::pos()) != this)
2118
timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
2119
QSplitterHandle::showEvent(ev);
2122
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2123
void OverlaySplitterHandle::enterEvent(QEvent *ev)
2125
void OverlaySplitterHandle::enterEvent(QEnterEvent *ev)
2129
QSplitterHandle::enterEvent(ev);
2132
void OverlaySplitterHandle::leaveEvent(QEvent *ev)
2134
if (OverlayParams::getDockOverlaySplitterHandleTimeout() > 0)
2135
timer.start(OverlayParams::getDockOverlaySplitterHandleTimeout());
2136
QSplitterHandle::leaveEvent(ev);
2139
QSize OverlaySplitterHandle::sizeHint() const
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()));
2146
size.setWidth(std::max(minSize, size.width()));
2150
void OverlaySplitterHandle::onAction()
2152
auto action = qobject_cast<QAction*>(sender());
2153
if(action == &actFloat) {
2154
QDockWidget *dock = dockWidget();
2156
OverlayManager::instance()->floatDockWidget(dock);
2160
QDockWidget *OverlaySplitterHandle::dockWidget()
2162
QSplitter *parent = splitter();
2166
if (parent->handle(this->idx) != this) {
2168
for (int i=0, c=parent->count(); i<c; ++i) {
2169
if (parent->handle(i) == this) {
2175
return qobject_cast<QDockWidget*>(parent->widget(this->idx));
2178
void OverlaySplitterHandle::retranslate()
2180
actFloat.setToolTip(QObject::tr("Toggle floating window"));
2183
void OverlaySplitterHandle::changeEvent(QEvent *e)
2185
QSplitterHandle::changeEvent(e);
2186
if (e->type() == QEvent::LanguageChange)
2190
void OverlaySplitterHandle::setTitleItem(QLayoutItem *item)
2195
void OverlaySplitterHandle::showTitle(bool enable)
2197
if (_showTitle == enable)
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());
2208
_showTitle = enable;
2209
for (auto child : findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly))
2210
child->setVisible(enable);
2214
void OverlaySplitterHandle::paintEvent(QPaintEvent *e)
2220
QSplitterHandle::paintEvent(e);
2224
int flags = Qt::AlignCenter;
2225
auto tabWidget = qobject_cast<OverlayTabWidget*>(
2226
splitter() ? splitter()->parentWidget() : nullptr);
2229
switch(tabWidget->getDockArea()) {
2230
case Qt::TopDockWidgetArea:
2231
case Qt::RightDockWidgetArea:
2232
flags = Qt::AlignRight;
2234
case Qt::BottomDockWidgetArea:
2235
case Qt::LeftDockWidgetArea:
2236
flags = Qt::AlignLeft;
2243
QDockWidget *dock = dockWidget();
2245
QSplitterHandle::paintEvent(e);
2249
QPainter painter(this);
2250
painter.fillRect(this->rect(), painter.background());
2252
QRect r = titleItem->geometry();
2253
if (this->orientation() != Qt::Vertical) {
2255
painter.translate(r.left(), r.top() + r.width());
2256
painter.rotate(-90);
2257
painter.translate(-r.left(), -r.top());
2259
QString text = painter.fontMetrics().elidedText(
2260
dock->windowTitle(), Qt::ElideRight, r.width());
2262
painter.drawText(r, flags, text);
2265
void OverlaySplitterHandle::endDrag()
2267
auto tabWidget = qobject_cast<OverlayTabWidget*>(splitter()->parentWidget());
2270
tabWidget->onSplitterResize(this->idx);
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();
2281
void OverlaySplitterHandle::keyPressEvent(QKeyEvent *ke)
2283
if (OverlayTabWidget::_Dragging == this && ke->key() == Qt::Key_Escape)
2287
void OverlaySplitterHandle::mouseMoveEvent(QMouseEvent *me)
2289
if (OverlayTabWidget::_Dragging != this)
2292
if (!(me->buttons() & Qt::LeftButton)) {
2297
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2298
QPoint point = me->globalPos();
2300
QPoint point = me->globalPosition().toPoint();
2303
if (dragging == 1) {
2304
OverlayTabWidget *overlay = qobject_cast<OverlayTabWidget*>(
2305
splitter()->parentWidget());
2306
QPoint pos = me->pos();
2308
switch(overlay->getDockArea()) {
2309
case Qt::LeftDockWidgetArea:
2310
case Qt::RightDockWidgetArea:
2311
if (pos.x() < 0 || pos.x() > overlay->width())
2314
case Qt::TopDockWidgetArea:
2315
case Qt::BottomDockWidgetArea:
2316
if (pos.y() < 0 || pos.y() > overlay->height())
2323
if (dragging == 1) {
2324
QPoint offset = parentWidget()->mapFromGlobal(point) - dragOffset;
2325
moveSplitter(this->orientation() == Qt::Horizontal ? offset.x() : offset.y());
2328
setCursor(Qt::ClosedHandCursor);
2331
OverlayManager::instance()->dragDockWidget(point,
2337
void OverlaySplitterHandle::mousePressEvent(QMouseEvent *me)
2339
if (OverlayTabWidget::_Dragging || !getMainWindow() || me->button() != Qt::LeftButton)
2342
OverlayTabWidget::_Dragging = this;
2344
dragOffset = me->pos();
2345
auto dock = dockWidget();
2347
dragSize = dock->size();
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()))));
2360
void OverlaySplitterHandle::mouseReleaseEvent(QMouseEvent *me)
2362
if (OverlayTabWidget::_Dragging != this || me->button() != Qt::LeftButton)
2365
if (dragging == 1) {
2371
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2372
QPoint point = me->globalPos();
2374
QPoint point = me->globalPosition().toPoint();
2377
OverlayManager::instance()->dragDockWidget(point,
2382
// Warning! the handle itself maybe deleted after return from
2383
// dragDockWidget().
2386
// -----------------------------------------------------------
2388
OverlayGraphicsEffect::OverlayGraphicsEffect(QObject *parent) :
2389
QGraphicsEffect(parent),
2398
extern Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0 );
2401
void OverlayGraphicsEffect::draw(QPainter* painter)
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);
2409
PixmapPadMode mode = QGraphicsEffect::PadToEffectiveBoundingRect;
2411
QPixmap px = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);
2413
// return if no source
2418
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
2420
getMainWindow()->showMessage(
2421
QStringLiteral("dock overlay redraw %1").arg(count++));
2425
QTransform restoreTransform = painter->worldTransform();
2426
painter->setWorldTransform(QTransform());
2428
// Calculate size for the background image
2429
QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
2430
tmp.setDevicePixelRatio(px.devicePixelRatioF());
2432
QPainter tmpPainter(&tmp);
2434
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
2435
if(_size.width() == 0 && _size.height() == 0)
2436
tmpPainter.drawPixmap(QPoint(0, 0), px);
2438
// exclude splitter handles
2439
auto splitter = qobject_cast<QSplitter*>(parent());
2442
for (int size : splitter->sizes()) {
2446
QWidget *w = splitter->widget(i);
2447
if (w->findChild<TaskView::TaskView*>())
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);
2454
clip.addRect(rect.x()+4, rect.y(),
2455
rect.width()-4, rect.height());
2457
if (clip.isEmpty()) {
2458
drawSource(painter);
2461
tmpPainter.setClipPath(clip);
2464
for (int x=-_size.width();x<=_size.width();++x) {
2465
for (int y=-_size.height();y<=_size.height();++y) {
2467
tmpPainter.drawPixmap(QPoint(x, y), px);
2468
tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
2475
// blur the alpha channel
2476
QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
2477
blurred.setDevicePixelRatio(px.devicePixelRatioF());
2479
QPainter blurPainter(&blurred);
2480
qt_blurImage(&blurPainter, tmp, blurRadius(), false, true);
2485
// blacken the image...
2486
tmpPainter.begin(&tmp);
2487
tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
2488
tmpPainter.fillRect(tmp.rect(), color());
2491
// draw the blurred shadow...
2492
painter->drawImage(QPointF(offset.x()+_offset.x(), offset.y()+_offset.y()), tmp);
2494
// draw the actual pixmap...
2495
painter->drawPixmap(offset, px, QRectF());
2498
QWidget *focus = qApp->focusWidget();
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()) {
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);
2518
// restore world transform
2519
painter->setWorldTransform(restoreTransform);
2522
QRectF OverlayGraphicsEffect::boundingRectFor(const QRectF& rect) const
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()));
2532
#include "moc_OverlayWidgets.cpp"