You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
310 lines
10 KiB
C++
310 lines
10 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include <AzQtComponents/Components/DockMainWindow.h>
|
|
#include <AzQtComponents/Components/StyledDockWidget.h>
|
|
#include <AzQtComponents/Components/DockMainWindow.h>
|
|
#include <AzQtComponents/Components/Titlebar.h>
|
|
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
|
|
#include <AzQtComponents/Components/TitleBarOverdrawHandler.h>
|
|
|
|
#include <QGuiApplication>
|
|
#include <QMainWindow>
|
|
#include <QMouseEvent>
|
|
#include <QOperatingSystemVersion>
|
|
#include <QPainter>
|
|
#include <QStyleOptionFrame>
|
|
#include <QStylePainter>
|
|
#include <QWindow>
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 6, 1) && defined(Q_OS_WIN32)
|
|
#include <QtGui/private/qwindow_p.h>
|
|
#endif
|
|
|
|
namespace AzQtComponents
|
|
{
|
|
namespace Platform
|
|
{
|
|
// Forward declare these since they will be defined per platform
|
|
void HandleFloatingWindow(QWidget* floatingWindow);
|
|
bool FloatingWindowsSupportMinimize();
|
|
}
|
|
|
|
static bool forceSkipTitleBarOverdraw()
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
if ((QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10)))
|
|
{
|
|
// non-win10 never uses title bar overdraw
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
// Non-windows never uses title bar overdraw
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
StyledDockWidget::StyledDockWidget(const QString& name, QWidget* parent)
|
|
: StyledDockWidget(name, false, parent)
|
|
{
|
|
}
|
|
|
|
StyledDockWidget::StyledDockWidget(const QString& name, bool skipTitleBarDrawing, QWidget* parent)
|
|
: QDockWidget(name, parent)
|
|
, m_skipTitleBarOverdraw(skipTitleBarDrawing || forceSkipTitleBarOverdraw())
|
|
{
|
|
init();
|
|
}
|
|
|
|
StyledDockWidget::StyledDockWidget(QWidget* parent)
|
|
: StyledDockWidget(QString(), parent)
|
|
{
|
|
}
|
|
|
|
void StyledDockWidget::init()
|
|
{
|
|
if (doesTitleBarOverdraw() && TitleBarOverdrawHandler::getInstance())
|
|
{
|
|
TitleBarOverdrawHandler::getInstance()->addTitleBarOverdrawWidget(this);
|
|
}
|
|
|
|
connect(this, &QDockWidget::topLevelChanged, this, &StyledDockWidget::onFloatingChanged);
|
|
createCustomTitleBar();
|
|
}
|
|
|
|
StyledDockWidget::~StyledDockWidget()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Checks if this dock widget is the only visible dock widget in a floating
|
|
* main window
|
|
*/
|
|
bool StyledDockWidget::isSingleFloatingChild()
|
|
{
|
|
// Check if our parent is a fancy docking QMainWindow with no central
|
|
// widget, which means it is one of the floating main windows
|
|
DockMainWindow* parentMainWindow = qobject_cast<DockMainWindow*>(parentWidget());
|
|
if (parentMainWindow && parentMainWindow->HasFancyDocking() && !parentMainWindow->centralWidget())
|
|
{
|
|
// Make sure the parent dock widget of the main window is floating to handle
|
|
// cases where there are nested main windows
|
|
StyledDockWidget* parentDockWidget = qobject_cast<StyledDockWidget*>(parentMainWindow->parentWidget());
|
|
if (parentDockWidget && parentDockWidget->isFloating())
|
|
{
|
|
bool singleFloating = true;
|
|
for (QDockWidget* dockWidget : parentMainWindow->findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly))
|
|
{
|
|
if (dockWidget->isVisible() && dockWidget != this)
|
|
{
|
|
singleFloating = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return singleFloating;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StyledDockWidget::closeEvent(QCloseEvent* event)
|
|
{
|
|
// give the sub-widget a chance to veto the close; necessary for the UI Editor, among other things
|
|
QCloseEvent closeEvent;
|
|
QCoreApplication::sendEvent(widget(), &closeEvent);
|
|
|
|
// If widget accepted the close event, we delete the dockwidget, which will also delete the child widget in case it doesn't have Qt::WA_DeleteOnClose
|
|
if (!closeEvent.isAccepted())
|
|
{
|
|
// Widget doesn't want to close
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
Q_EMIT aboutToClose();
|
|
QDockWidget::closeEvent(event);
|
|
}
|
|
|
|
void StyledDockWidget::showEvent(QShowEvent* event)
|
|
{
|
|
if (auto titleBar = qobject_cast<TitleBar*>(titleBarWidget()))
|
|
{
|
|
// When docked, we don't have a window frame, so draw the left and right border
|
|
titleBar->setDrawSideBorders(!isFloating());
|
|
}
|
|
|
|
if (isFloating())
|
|
{
|
|
fixFramelessFlags();
|
|
}
|
|
|
|
QDockWidget::showEvent(event);
|
|
}
|
|
|
|
bool StyledDockWidget::nativeEvent(const QByteArray& eventType, void* message, long* result)
|
|
{
|
|
return WindowDecorationWrapper::handleNativeEvent(eventType, message, result, this);
|
|
}
|
|
|
|
/**
|
|
* Override of event handler so that we can ignore the Mouse events on our dock widgets.
|
|
* This fixes an issue where the QDockWidget only respects the movable feature on mouse press,
|
|
* not on non client area events (e.g. resizing); this also fixes another issue affecting
|
|
* DockWidgets in a standalone QWindow, that could be detached from said Window through
|
|
* the legacy Qt dragging.
|
|
* We disable the movable feature on our dock widgets so that we can use our own custom
|
|
* docking solution instead of the default Qt docking, but we need to override these events
|
|
* that get triggered when resizing otherwise it will activate the default qt docking.
|
|
*/
|
|
bool StyledDockWidget::event(QEvent* event)
|
|
{
|
|
switch (event->type())
|
|
{
|
|
case QEvent::NonClientAreaMouseMove:
|
|
case QEvent::NonClientAreaMouseButtonPress:
|
|
case QEvent::NonClientAreaMouseButtonRelease:
|
|
case QEvent::NonClientAreaMouseButtonDblClick:
|
|
{
|
|
return true;
|
|
}
|
|
case QEvent::MouseButtonPress:
|
|
case QEvent::MouseMove:
|
|
case QEvent::MouseButtonRelease:
|
|
{
|
|
// For these events, make sure FancyDocking is being used or it will disable
|
|
// Mouse events for the whole Widget
|
|
DockMainWindow* parentMainWindow = qobject_cast<DockMainWindow*>(parentWidget());
|
|
if (parentMainWindow && parentMainWindow->HasFancyDocking())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return QDockWidget::event(event);
|
|
}
|
|
|
|
void StyledDockWidget::paintEvent(QPaintEvent*)
|
|
{
|
|
// By default QDockWidget::paintEvent only draws the frame if the dock widget doesn't have
|
|
// a custom title bar and does not have native window decorations. As a result, QDockWidget
|
|
// cannot be styled using QSS when floating.
|
|
QStylePainter p(this);
|
|
QStyleOptionFrame framOpt;
|
|
framOpt.init(this);
|
|
p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
|
|
}
|
|
|
|
bool StyledDockWidget::doesTitleBarOverdraw() const
|
|
{
|
|
return !m_skipTitleBarOverdraw;
|
|
}
|
|
|
|
bool StyledDockWidget::skipTitleBarOverdraw() const
|
|
{
|
|
return m_skipTitleBarOverdraw;
|
|
}
|
|
|
|
void StyledDockWidget::fixFramelessFlags()
|
|
{
|
|
// This ensures we have native frames (but no native titlebar)
|
|
QWindow* w = windowHandle();
|
|
if (doesTitleBarOverdraw() && w && (w->flags() & Qt::FramelessWindowHint) && isFloating())
|
|
{
|
|
w->setFlags(WindowDecorationWrapper::specialFlagsForOS() | Qt::Tool);
|
|
}
|
|
}
|
|
|
|
void StyledDockWidget::onFloatingChanged(bool floating)
|
|
{
|
|
if (floating)
|
|
{
|
|
fixFramelessFlags();
|
|
|
|
// Perform platform-specific handling for floating windows (e.g. minimizing into the taskbar)
|
|
Platform::HandleFloatingWindow(window());
|
|
}
|
|
|
|
// If we have a custom title bar, then we need to enable the dragging
|
|
// to reposition our dock widget if floating is enabled, change it
|
|
// to be drawn in simple mode, and update the buttons
|
|
DockMainWindow* parentMainWindow = qobject_cast<DockMainWindow*>(parentWidget());
|
|
if (parentMainWindow && parentMainWindow->HasFancyDocking())
|
|
{
|
|
TitleBar* titleBar = customTitleBar();
|
|
if (titleBar)
|
|
{
|
|
titleBar->setDragEnabled(floating);
|
|
titleBar->setDrawSimple(floating);
|
|
if (floating)
|
|
{
|
|
TitleBar::WindowDecorationButtons buttons = { DockBarButton::MaximizeButton, DockBarButton::CloseButton };
|
|
|
|
if (Platform::FloatingWindowsSupportMinimize())
|
|
{
|
|
buttons.prepend(DockBarButton::MinimizeButton);
|
|
}
|
|
|
|
titleBar->setButtons(buttons);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StyledDockWidget::createCustomTitleBar()
|
|
{
|
|
QWidget* tw = titleBarWidget();
|
|
if (tw)
|
|
{
|
|
tw->deleteLater();
|
|
}
|
|
|
|
TitleBar* titleBar = new TitleBar(this);
|
|
titleBar->setTearEnabled(true);
|
|
titleBar->setDrawSideBorders(false);
|
|
QObject::connect(titleBar, &TitleBar::undockAction, this, &StyledDockWidget::undock);
|
|
QObject::connect(this, &QDockWidget::windowTitleChanged, titleBar, &TitleBar::setWindowTitleOverride);
|
|
setTitleBarWidget(titleBar);
|
|
}
|
|
|
|
/** static */
|
|
void StyledDockWidget::drawFrame(QPainter& p, QRect rect, bool drawTop)
|
|
{
|
|
p.save();
|
|
p.setPen(QColor(33, 34, 35));
|
|
|
|
rect.adjust(0, p.pen().width(), 0, 0);
|
|
if (drawTop)
|
|
{
|
|
p.drawLine(QLine(rect.topLeft(), rect.topRight()));
|
|
}
|
|
|
|
p.drawLine(QLine(rect.topLeft(), rect.bottomLeft()));
|
|
p.drawLine(QLine(rect.topRight(), rect.bottomRight()));
|
|
p.drawLine(QLine(rect.bottomLeft(), rect.bottomRight()));
|
|
|
|
p.restore();
|
|
}
|
|
|
|
TitleBar* StyledDockWidget::customTitleBar() const
|
|
{
|
|
return qobject_cast<TitleBar*>(titleBarWidget());
|
|
}
|
|
|
|
} // namespace AzQtComponents
|
|
|
|
#include "Components/moc_StyledDockWidget.cpp"
|