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.
1278 lines
43 KiB
C++
1278 lines
43 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzQtComponents/Components/Titlebar.h>
|
|
#include <AzQtComponents/Components/ButtonDivider.h>
|
|
#include <AzQtComponents/Components/ConfigHelpers.h>
|
|
#include <AzQtComponents/Components/StyledDockWidget.h>
|
|
#include <AzQtComponents/Components/DockBar.h>
|
|
#include <AzQtComponents/Components/DockMainWindow.h>
|
|
#include <AzQtComponents/Components/StyleHelpers.h>
|
|
#include <AzQtComponents/Components/DockTabBar.h>
|
|
#include <AzQtComponents/Components/Widgets/ElidingLabel.h>
|
|
|
|
#include <QApplication>
|
|
#include <QDesktopWidget>
|
|
#include <QDockWidget>
|
|
#include <QLabel>
|
|
#include <QMenu>
|
|
#include <QMouseEvent>
|
|
#include <QHBoxLayout>
|
|
#include <QOperatingSystemVersion>
|
|
#include <QStackedLayout>
|
|
#include <QVector>
|
|
#include <QWindow>
|
|
#include <QtGui/private/qhighdpiscaling_p.h>
|
|
|
|
namespace AzQtComponents
|
|
{
|
|
// Works like QWidget::window() but also considers a docked QDockWidget.
|
|
// This is used when clicking on the custom titlebar, if the custom titlebar is on a dock widget
|
|
// we don't want to close the host, but only the dock.
|
|
QWidget* actualTopLevelFor(QWidget* w)
|
|
{
|
|
if (!w)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (w->isWindow())
|
|
{
|
|
return w;
|
|
}
|
|
|
|
if (qobject_cast<QDockWidget*>(w))
|
|
{
|
|
return w;
|
|
}
|
|
|
|
return actualTopLevelFor(w->parentWidget());
|
|
}
|
|
|
|
TitleBar::Config TitleBar::loadConfig(QSettings& settings)
|
|
{
|
|
Config config = defaultConfig();
|
|
|
|
// TitleBar
|
|
{
|
|
ConfigHelpers::GroupGuard title(&settings, QStringLiteral("TitleBar"));
|
|
ConfigHelpers::read<int>(settings, QStringLiteral("Height"), config.titleBar.height);
|
|
ConfigHelpers::read<int>(settings, QStringLiteral("SimpleHeight"), config.titleBar.simpleHeight);
|
|
ConfigHelpers::read<bool>(settings, QStringLiteral("AppearAsTabBar"), config.titleBar.appearAsTabBar);
|
|
}
|
|
|
|
// Icon
|
|
{
|
|
ConfigHelpers::GroupGuard icon(&settings, QStringLiteral("Icon"));
|
|
ConfigHelpers::read<bool>(settings, QStringLiteral("Visible"), config.icon.visible);
|
|
}
|
|
|
|
// Title
|
|
{
|
|
ConfigHelpers::GroupGuard title(&settings, QStringLiteral("Title"));
|
|
ConfigHelpers::read<int>(settings, QStringLiteral("Indent"), config.title.indent);
|
|
ConfigHelpers::read<bool>(settings, QStringLiteral("VisibleWhenSimple"), config.title.visibleWhenSimple);
|
|
}
|
|
|
|
// Event Log Details
|
|
{
|
|
ConfigHelpers::GroupGuard buttons(&settings, QStringLiteral("Buttons"));
|
|
ConfigHelpers::read<bool>(settings, QStringLiteral("ShowDividerButtons"), config.buttons.showDividerButtons);
|
|
ConfigHelpers::read<int>(settings, QStringLiteral("Spacing"), config.buttons.spacing);
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
TitleBar::Config TitleBar::defaultConfig()
|
|
{
|
|
Config config;
|
|
|
|
config.titleBar.height = 32;
|
|
config.titleBar.simpleHeight = 14;
|
|
config.titleBar.appearAsTabBar = true;
|
|
|
|
config.icon.visible = false;
|
|
|
|
config.title.indent = 0;
|
|
config.title.visibleWhenSimple = false;
|
|
|
|
config.buttons.showDividerButtons = false;
|
|
config.buttons.spacing = 0;
|
|
|
|
return config;
|
|
}
|
|
|
|
TitleBar::TitleBar(QWidget* parent)
|
|
: QFrame(parent)
|
|
{
|
|
// To allow drawSimple and tearEnabled to be used in the style sheets, ensure the widget is
|
|
// repolished when the properties change.
|
|
StyleHelpers::repolishWhenPropertyChanges(this, &TitleBar::drawSimpleChanged);
|
|
StyleHelpers::repolishWhenPropertyChanges(this, &TitleBar::tearEnabledChanged);
|
|
|
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
|
|
m_stackedLayout = new QStackedLayout(this);
|
|
m_stackedLayout->setContentsMargins(0,0,0,0);
|
|
m_stackedLayout->setSpacing(0);
|
|
|
|
auto tabBarContainer = new QWidget(this);
|
|
auto tabBarlayout = new QHBoxLayout(tabBarContainer);
|
|
tabBarlayout->setContentsMargins(0,0,0,0);
|
|
tabBarlayout->setSpacing(0);
|
|
|
|
m_tabBar = new DockTabBar(tabBarContainer);
|
|
m_tabBar->installEventFilter(this);
|
|
m_tabBar->addTab({});
|
|
connect(m_tabBar, &DockTabBar::closeTab, this, &TitleBar::handleClose);
|
|
connect(m_tabBar, &DockTabBar::undockTab, this, &TitleBar::undockAction);
|
|
m_tabBar->setDrawBase(false);
|
|
|
|
tabBarlayout->addWidget(m_tabBar);
|
|
tabBarlayout->addStretch();
|
|
|
|
m_tabButtonsContainer = new QFrame(tabBarContainer);
|
|
m_tabButtonsContainer->setObjectName(QStringLiteral("tabButtons"));
|
|
m_tabButtonsLayout = new QHBoxLayout(m_tabButtonsContainer);
|
|
m_tabButtonsLayout->setContentsMargins(0, 0, 0, 0);
|
|
m_tabButtonsLayout->setSpacing(DockBar::ButtonsSpacing);
|
|
tabBarlayout->addWidget(m_tabButtonsContainer);
|
|
|
|
m_stackedLayout->addWidget(tabBarContainer);
|
|
|
|
auto container = new QWidget(this);
|
|
auto layout = new QHBoxLayout(container);
|
|
layout->setContentsMargins(0,0,0,0);
|
|
layout->setSpacing(0);
|
|
|
|
m_stackedLayout->addWidget(container);
|
|
|
|
m_icon = new QLabel(container);
|
|
m_icon->setObjectName(QStringLiteral("icon"));
|
|
layout->addWidget(m_icon);
|
|
|
|
m_label = new ElidingLabel(container);
|
|
m_label->setObjectName(QStringLiteral("title"));
|
|
layout->addWidget(m_label, 1);
|
|
|
|
layout->addStretch();
|
|
|
|
m_buttonsContainer = new QFrame(container);
|
|
m_buttonsContainer->setObjectName(QStringLiteral("buttons"));
|
|
m_buttonsLayout = new QHBoxLayout(m_buttonsContainer);
|
|
m_buttonsLayout->setContentsMargins(0,0,0,0);
|
|
m_buttonsLayout->setSpacing(DockBar::ButtonsSpacing);
|
|
layout->addWidget(m_buttonsContainer);
|
|
|
|
setCursor(m_originalCursor);
|
|
setButtons({});
|
|
setMouseTracking(true);
|
|
|
|
connect(this, &TitleBar::drawSimpleChanged, this, &TitleBar::updateTitle);
|
|
connect(this, &TitleBar::drawSimpleChanged, this, &TitleBar::updateTitleBar);
|
|
connect(this, &TitleBar::windowTitleOverrideChanged, this, &TitleBar::updateTitle);
|
|
updateTitle();
|
|
updateTitleBar();
|
|
|
|
// We have to do the lowering of the title bar whenever the widget goes from being docked in a QDockWidgetGroupWindow, but
|
|
// there's no reliable way to figure out when that is, so we use a timer instead and just lower it ever time through.
|
|
const int FIX_STACK_ORDER_INTERVAL_IN_MILLISECONDS = 500;
|
|
startTimer(FIX_STACK_ORDER_INTERVAL_IN_MILLISECONDS);
|
|
|
|
connect(&m_enableMouseTrackingTimer, &QTimer::timeout, this, &TitleBar::checkEnableMouseTracking);
|
|
m_enableMouseTrackingTimer.setInterval(500);
|
|
}
|
|
|
|
TitleBar::~TitleBar()
|
|
{
|
|
}
|
|
|
|
void TitleBar::handleClose()
|
|
{
|
|
actualTopLevelFor(this)->close();
|
|
}
|
|
|
|
void TitleBar::handleMaximize()
|
|
{
|
|
QWidget* w = window();
|
|
if (isMaximized())
|
|
{
|
|
w->showNormal();
|
|
}
|
|
else
|
|
{
|
|
w->showMaximized();
|
|
|
|
// Need to separately resize based on the available geometry for
|
|
// the screen because since floating windows are frameless, on
|
|
// Windows 10 they end up taking up the entire screen when maximized
|
|
// instead of respecting the available space (e.g. taskbar)
|
|
w->setGeometry(QApplication::desktop()->availableGeometry(w));
|
|
}
|
|
}
|
|
|
|
void TitleBar::handleMinimize()
|
|
{
|
|
window()->showMinimized();
|
|
}
|
|
|
|
bool TitleBar::hasButton(DockBarButton::WindowDecorationButton buttonType) const
|
|
{
|
|
return m_buttons.contains(buttonType);
|
|
}
|
|
|
|
bool TitleBar::buttonIsEnabled(DockBarButton::WindowDecorationButton buttonType) const
|
|
{
|
|
if (hasButton(buttonType))
|
|
{
|
|
DockBarButton* button = findButton(buttonType);
|
|
if (button)
|
|
{
|
|
return button->isEnabled();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TitleBar::handleMoveRequest()
|
|
{
|
|
delete m_interactiveWindowGeometryChanger;
|
|
if (auto topLevelWidget = window())
|
|
{
|
|
m_interactiveWindowGeometryChanger = new InteractiveWindowMover(topLevelWidget->windowHandle(), this);
|
|
}
|
|
}
|
|
|
|
void TitleBar::handleSizeRequest()
|
|
{
|
|
delete m_interactiveWindowGeometryChanger;
|
|
if (auto topLevelWidget = window())
|
|
{
|
|
m_interactiveWindowGeometryChanger = new InteractiveWindowResizer(topLevelWidget->windowHandle(), this);
|
|
}
|
|
}
|
|
|
|
void TitleBar::setDrawSideBorders(bool enable)
|
|
{
|
|
if (m_drawSideBorders != enable)
|
|
{
|
|
m_drawSideBorders = enable;
|
|
update();
|
|
emit drawSideBordersChanged(m_drawSideBorders);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the title bar drawing mode
|
|
*/
|
|
void TitleBar::setDrawMode(TitleBarDrawMode drawMode)
|
|
{
|
|
if (m_drawMode != drawMode)
|
|
{
|
|
m_drawMode = drawMode;
|
|
setupButtons();
|
|
update();
|
|
emit drawSimpleChanged(m_drawMode == TitleBarDrawMode::Simple);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change the title bar drawing mode between default and simple
|
|
*/
|
|
void TitleBar::setDrawSimple(bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
setDrawMode(TitleBarDrawMode::Simple);
|
|
}
|
|
else
|
|
{
|
|
setDrawMode(TitleBarDrawMode::Main);
|
|
}
|
|
}
|
|
|
|
void TitleBar::setDragEnabled(bool enable)
|
|
{
|
|
m_dragEnabled = enable;
|
|
}
|
|
|
|
void TitleBar::setTearEnabled(bool enable)
|
|
{
|
|
if (enable != m_tearEnabled)
|
|
{
|
|
m_tearEnabled = enable;
|
|
update();
|
|
emit tearEnabledChanged(m_tearEnabled);
|
|
}
|
|
}
|
|
|
|
void TitleBar::setDrawAsTabBar(bool enable)
|
|
{
|
|
if (enable != m_appearAsTabBar)
|
|
{
|
|
m_appearAsTabBar = enable;
|
|
update();
|
|
emit drawAsTabBarChanged(m_appearAsTabBar);
|
|
}
|
|
}
|
|
|
|
QSize TitleBar::sizeHint() const
|
|
{
|
|
const int width = QFrame::sizeHint().width();
|
|
const int height = style()->pixelMetric(QStyle::PM_TitleBarHeight, nullptr, this);
|
|
return QSize(width, height);
|
|
}
|
|
|
|
void TitleBar::setWindowTitleOverride(const QString& titleOverride)
|
|
{
|
|
if (m_titleOverride != titleOverride)
|
|
{
|
|
m_titleOverride = titleOverride;
|
|
update();
|
|
emit windowTitleOverrideChanged(m_titleOverride);
|
|
}
|
|
}
|
|
|
|
bool TitleBar::event(QEvent* event)
|
|
{
|
|
switch (event->type())
|
|
{
|
|
case QEvent::ParentChange:
|
|
case QEvent::Show:
|
|
case QEvent::WindowTitleChange:
|
|
updateTitle();
|
|
break;
|
|
case QEvent::StyleChange:
|
|
// Reset the TitleBar height when the style changes. It is still too early to do it
|
|
// in TitleBar::unpolish because the widget doesn't have the new style yet.
|
|
updateTitleBar();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return QFrame::event(event);
|
|
}
|
|
|
|
DockBarButton* TitleBar::findButton(DockBarButton::WindowDecorationButton buttonType) const
|
|
{
|
|
// Use the layout to get the right button widget, instead of the buttonContainer,
|
|
// as old buttons get deleteLater'd, which means they might still be around.
|
|
// Buttons still in the layout are guaranteed to be the right ones.
|
|
int itemsInLayout = m_buttonsLayout->count();
|
|
for (int i = 0; i < itemsInLayout; i++)
|
|
{
|
|
QLayoutItem* item = m_buttonsLayout->itemAt(i);
|
|
DockBarButton* button = qobject_cast<DockBarButton*>(item->widget());
|
|
if (button && button->buttonType() == buttonType)
|
|
{
|
|
return button;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void TitleBar::disableButton(DockBarButton::WindowDecorationButton buttonType)
|
|
{
|
|
DockBarButton* button = findButton(buttonType);
|
|
if (button)
|
|
{
|
|
button->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void TitleBar::enableButton(DockBarButton::WindowDecorationButton buttonType)
|
|
{
|
|
DockBarButton* button = findButton(buttonType);
|
|
if (button)
|
|
{
|
|
button->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
QString TitleBar::title() const
|
|
{
|
|
QString result;
|
|
if (!m_titleOverride.isEmpty())
|
|
{
|
|
// title override takes precedence.
|
|
result = m_titleOverride;
|
|
}
|
|
else if (auto dock = qobject_cast<QDockWidget*>(parentWidget()))
|
|
{
|
|
// A docked QDockWidget has the title of the dockwidget, not of the top-level QWidget
|
|
|
|
result = dock->windowTitle();
|
|
}
|
|
else if (auto w = window())
|
|
{
|
|
result = w->windowTitle();
|
|
}
|
|
|
|
if (result.isEmpty())
|
|
{
|
|
result = QApplication::applicationName();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void TitleBar::updateTitle()
|
|
{
|
|
// The configured title needs to be simplified since it could have line breaks or
|
|
// carriage returns that should be replaced with spaces so that all the text will
|
|
// be on a single line
|
|
const auto text = drawSimple() ? QApplication::applicationName() : title().simplified();
|
|
m_label->setText(text);
|
|
m_tabBar->setTabText(0, text);
|
|
}
|
|
|
|
void TitleBar::updateTitleBar()
|
|
{
|
|
setFixedHeight(style()->pixelMetric(QStyle::PM_TitleBarHeight, nullptr, this));
|
|
m_label->setVisible(drawSimple() ? m_showLabelWhenSimple : true);
|
|
const int currentIndex = !drawSimple() && m_appearAsTabBar && isTitleBarForDockWidget() ? 0 : 1;
|
|
m_stackedLayout->setCurrentIndex(currentIndex);
|
|
}
|
|
|
|
void TitleBar::setIsShowingWindowControls(bool show)
|
|
{
|
|
m_isShowingWindowControls = show;
|
|
}
|
|
|
|
void TitleBar::contextMenuEvent(QContextMenuEvent*)
|
|
{
|
|
// If this titlebar is for the main editor window or one of the floating
|
|
// title bars, then use the standard context menu with min/max/close/etc... actions
|
|
StyledDockWidget* dockWidgetParent = qobject_cast<StyledDockWidget*>(parentWidget());
|
|
// Main Window title bar, old title bar in old docking, and new title bar will use standard context menu
|
|
if (!dockWidgetParent || drawSimple() || m_isShowingWindowControls)
|
|
{
|
|
updateStandardContextMenu();
|
|
m_windowContextMenu->exec(QCursor::pos());
|
|
}
|
|
// the old title bar in the new docking will use new context menu
|
|
else
|
|
{
|
|
updateDockedContextMenu();
|
|
m_tabsContextMenu->exec(QCursor::pos());
|
|
}
|
|
}
|
|
|
|
bool TitleBar::eventFilter(QObject* watched, QEvent* event)
|
|
{
|
|
if (watched != m_tabBar)
|
|
{
|
|
return QFrame::eventFilter(watched, event);
|
|
}
|
|
|
|
switch (event->type())
|
|
{
|
|
case QEvent::MouseButtonPress:
|
|
case QEvent::MouseButtonRelease:
|
|
case QEvent::MouseButtonDblClick:
|
|
case QEvent::MouseMove:
|
|
// Filter our these events, but make sure we ignore them so they get propagated past
|
|
// the tab bar.
|
|
event->ignore();
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QFrame::eventFilter(watched, event);
|
|
}
|
|
|
|
bool TitleBar::polish(Style* style, QWidget* widget, const Config& config)
|
|
{
|
|
Q_UNUSED(style);
|
|
Q_UNUSED(config);
|
|
|
|
auto titleBar = qobject_cast<TitleBar*>(widget);
|
|
if (!titleBar)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
titleBar->m_icon->setVisible(config.icon.visible);
|
|
titleBar->m_label->setIndent(config.title.indent);
|
|
titleBar->m_showLabelWhenSimple = config.title.visibleWhenSimple;
|
|
titleBar->setDrawAsTabBar(config.titleBar.appearAsTabBar);
|
|
titleBar->m_buttonsLayout->setSpacing(config.buttons.spacing);
|
|
titleBar->setupButtons(config.buttons.showDividerButtons);
|
|
|
|
titleBar->updateTitleBar();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TitleBar::unpolish(Style* style, QWidget* widget, const Config& config)
|
|
{
|
|
Q_UNUSED(style);
|
|
|
|
auto titleBar = qobject_cast<TitleBar*>(widget);
|
|
if (!titleBar)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
titleBar->m_icon->setVisible(true);
|
|
titleBar->m_label->setIndent(-1);
|
|
titleBar->m_showLabelWhenSimple = true;
|
|
titleBar->setDrawAsTabBar(false);
|
|
titleBar->m_buttonsLayout->setSpacing(DockBar::ButtonsSpacing);
|
|
titleBar->setupButtons(config.buttons.showDividerButtons);
|
|
|
|
titleBar->updateTitleBar();
|
|
|
|
return true;
|
|
}
|
|
|
|
int TitleBar::titleBarHeight(const Style* style, const QStyleOption* option, const QWidget* widget, const Config& config, const TabWidget::Config& tabConfig)
|
|
{
|
|
Q_UNUSED(style);
|
|
Q_UNUSED(option);
|
|
|
|
if (auto titleBar = qobject_cast<const TitleBar*>(widget))
|
|
{
|
|
switch (titleBar->drawMode())
|
|
{
|
|
case TitleBarDrawMode::Hidden:
|
|
return 0;
|
|
case TitleBarDrawMode::Simple:
|
|
return config.titleBar.simpleHeight;
|
|
case TitleBarDrawMode::Main:
|
|
default:
|
|
{
|
|
if (titleBar->drawAsTabBar() && titleBar->isTitleBarForDockWidget())
|
|
{
|
|
return tabConfig.tabHeight;
|
|
}
|
|
else
|
|
{
|
|
return config.titleBar.height;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool TitleBar::usesCustomTopBorderResizing() const
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
// On Win < 10 we're not overlapping the titlebar, removing it works fine there.
|
|
// On Win < 10 we use native resizing of the top border.
|
|
return QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void TitleBar::checkEnableMouseTracking()
|
|
{
|
|
// We don't get a mouse release when detaching a dock widget, so much do it with a workaround
|
|
if (!hasMouseTracking())
|
|
{
|
|
if (!isLeftButtonDown() && (!isInDockWidget() || isInFloatingDockWidget()))
|
|
{
|
|
setMouseTracking(true);
|
|
updateMouseCursor(QCursor::pos());
|
|
}
|
|
else
|
|
{
|
|
m_enableMouseTrackingTimer.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
QWidget* TitleBar::dockWidget() const
|
|
{
|
|
// If this titlebar is being used in a dock widget, returns that dockwidget
|
|
// The result can either be a QDockWidget or a QDockWidgetGroupWindow
|
|
if (auto dock = qobject_cast<QDockWidget*>(actualTopLevelFor(const_cast<TitleBar*>(this))))
|
|
{
|
|
return dock;
|
|
}
|
|
else if (isInDockWidgetWindowGroup())
|
|
{
|
|
return window();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool TitleBar::isInDockWidget() const
|
|
{
|
|
return dockWidget() != nullptr || isInDockWidgetWindowGroup();
|
|
}
|
|
|
|
bool TitleBar::isInFloatingDockWidget() const
|
|
{
|
|
if (QWidget *win = window())
|
|
{
|
|
return qobject_cast<QDockWidget*>(win)
|
|
|| strcmp(win->metaObject()->className(), "QDockWidgetGroupWindow") == 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Setup our context menu for the main editor window and floating title bars
|
|
*/
|
|
void TitleBar::updateStandardContextMenu()
|
|
{
|
|
if (!m_windowContextMenu)
|
|
{
|
|
m_windowContextMenu = new QMenu(this);
|
|
|
|
m_restoreMenuAction = m_windowContextMenu->addAction(tr("Restore"));
|
|
QIcon restoreIcon;
|
|
restoreIcon.addFile(":/stylesheet/img/titlebarmenu/restore.png");
|
|
restoreIcon.addFile(":/stylesheet/img/titlebarmenu/restore_disabled.png", QSize(), QIcon::Disabled);
|
|
|
|
m_restoreMenuAction->setIcon(restoreIcon);
|
|
connect(m_restoreMenuAction, &QAction::triggered, this, &TitleBar::handleMaximize);
|
|
|
|
m_moveMenuAction = m_windowContextMenu->addAction(tr("Move"));
|
|
connect(m_moveMenuAction, &QAction::triggered, this, &TitleBar::handleMoveRequest);
|
|
|
|
m_sizeMenuAction = m_windowContextMenu->addAction(tr("Size"));
|
|
connect(m_sizeMenuAction, &QAction::triggered, this, &TitleBar::handleSizeRequest);
|
|
|
|
m_minimizeMenuAction = m_windowContextMenu->addAction(tr("Minimize"));
|
|
QIcon minimizeIcon;
|
|
minimizeIcon.addFile(":/stylesheet/img/titlebarmenu/minimize.png");
|
|
minimizeIcon.addFile(":/stylesheet/img/titlebarmenu/minimize_disabled.png", QSize(), QIcon::Disabled);
|
|
|
|
m_minimizeMenuAction->setIcon(minimizeIcon);
|
|
connect(m_minimizeMenuAction, &QAction::triggered, this, &TitleBar::handleMinimize);
|
|
|
|
m_maximizeMenuAction = m_windowContextMenu->addAction(tr("Maximize"));
|
|
QIcon maximizeIcon;
|
|
maximizeIcon.addFile(":/stylesheet/img/titlebarmenu/maximize.png");
|
|
maximizeIcon.addFile(":/stylesheet/img/titlebarmenu/maximize_disabled.png", QSize(), QIcon::Disabled);
|
|
|
|
m_maximizeMenuAction->setIcon(QIcon(maximizeIcon));
|
|
connect(m_maximizeMenuAction, &QAction::triggered, this, &TitleBar::handleMaximize);
|
|
|
|
m_windowContextMenu->addSeparator();
|
|
|
|
m_closeMenuAction = m_windowContextMenu->addAction(tr("Close"));
|
|
QIcon closeIcon;
|
|
closeIcon.addFile(":/stylesheet/img/titlebarmenu/close.png", QSize());
|
|
closeIcon.addFile(":/stylesheet/img/titlebarmenu/close_disabled.png", QSize(), QIcon::Disabled);
|
|
|
|
m_closeMenuAction->setIcon(QIcon(closeIcon));
|
|
m_closeMenuAction->setShortcut(QString("Alt+F4"));
|
|
connect(m_closeMenuAction, &QAction::triggered, this, &TitleBar::handleClose);
|
|
}
|
|
|
|
QWidget *topLevelWidget = window();
|
|
if (!topLevelWidget)
|
|
{
|
|
// Defensive, doesn't happen
|
|
return;
|
|
}
|
|
|
|
m_restoreMenuAction->setEnabled(buttonIsEnabled(DockBarButton::MaximizeButton) && isMaximized());
|
|
m_moveMenuAction->setEnabled(!isMaximized());
|
|
const bool isFixedSize = topLevelWidget->minimumSize() == topLevelWidget->maximumSize();
|
|
m_sizeMenuAction->setEnabled(!isFixedSize && !isMaximized());
|
|
m_minimizeMenuAction->setEnabled(buttonIsEnabled(DockBarButton::MinimizeButton));
|
|
m_maximizeMenuAction->setEnabled(buttonIsEnabled(DockBarButton::MaximizeButton) && !isMaximized());
|
|
m_closeMenuAction->setEnabled(buttonIsEnabled(DockBarButton::CloseButton));
|
|
}
|
|
|
|
/**
|
|
* Setup our context menu for all docked panes
|
|
*/
|
|
void TitleBar::updateDockedContextMenu()
|
|
{
|
|
if (!m_tabsContextMenu)
|
|
{
|
|
m_tabsContextMenu = new QMenu(this);
|
|
|
|
// Action to close our dock widget, and leave the text blank since
|
|
// it will be dynamically set using the title of the dock widget
|
|
m_closeTabMenuAction = m_tabsContextMenu->addAction(QString());
|
|
QObject::connect(m_closeTabMenuAction, &QAction::triggered, this, &TitleBar::handleClose);
|
|
|
|
// Unused in this context, but still here for consistency
|
|
m_closeGroupMenuAction = m_tabsContextMenu->addAction(tr("Close Tab Group"));
|
|
|
|
// Separate the close actions from the undock actions
|
|
m_tabsContextMenu->addSeparator();
|
|
|
|
// Action to undock our dock widget, and leave the text blank since
|
|
// it will be dynamically set using the title of the dock widget
|
|
m_undockMenuAction = m_tabsContextMenu->addAction(QString());
|
|
QObject::connect(m_undockMenuAction, &QAction::triggered, this, &TitleBar::undockAction);
|
|
|
|
// Unused in this context, but still here for consistency
|
|
m_undockGroupMenuAction = m_tabsContextMenu->addAction(tr("Undock Tab Group"));
|
|
}
|
|
|
|
// Update the menu labels for the close/undock actions
|
|
// We need to check where we should get the title text from based on whether
|
|
// or not the tab bar or the label are currently being shown
|
|
QString titleLabel = m_tabBar->isVisible() ? m_tabBar->tabText(0) : m_label->ElidedText();
|
|
m_closeTabMenuAction->setText(tr("Close %1").arg(titleLabel));
|
|
m_undockMenuAction->setText(tr("Undock %1").arg(titleLabel));
|
|
|
|
// Don't enable the undock action if this dock widget is the only pane
|
|
// in a floating window or if it isn't docked in one of our dock main windows
|
|
StyledDockWidget* dockWidgetParent = qobject_cast<StyledDockWidget*>(parentWidget());
|
|
DockMainWindow* dockMainWindow = nullptr;
|
|
if (dockWidgetParent)
|
|
{
|
|
dockMainWindow = qobject_cast<DockMainWindow*>(dockWidgetParent->parentWidget());
|
|
}
|
|
m_undockMenuAction->setEnabled(dockMainWindow && dockWidgetParent && !dockWidgetParent->isSingleFloatingChild());
|
|
|
|
// The group actions are always disabled, they are only provided for
|
|
// menu consistency with the tab bar
|
|
m_closeGroupMenuAction->setEnabled(false);
|
|
m_undockGroupMenuAction->setEnabled(false);
|
|
}
|
|
|
|
void TitleBar::mousePressEvent(QMouseEvent* ev)
|
|
{
|
|
// use QCursor::pos(); in scenarios with multiple screens and different scale factors,
|
|
// it's much more reliable about actually reporting a global position.
|
|
QPoint globalPos = QCursor::pos();
|
|
|
|
if (canResize() && (isTopResizeArea(globalPos) || isLeftResizeArea(globalPos) || isRightResizeArea(globalPos)))
|
|
{
|
|
m_resizingTop = isTopResizeArea(globalPos);
|
|
m_resizingLeft = isLeftResizeArea(globalPos);
|
|
m_resizingRight = isRightResizeArea(globalPos);
|
|
}
|
|
else if (canDragWindow())
|
|
{
|
|
QWidget *topLevel = window();
|
|
auto topLevelWidth = topLevel->width();
|
|
m_dragPos = topLevel->mapFromGlobal(globalPos);
|
|
m_relativeDragPos = (double)m_dragPos.x() / ((double)topLevel->width());
|
|
|
|
topLevelWidth++;
|
|
}
|
|
else
|
|
{
|
|
if (!isInFloatingDockWidget())
|
|
{
|
|
// Workaround Qt internal crash when detaching docked window group. Crashes if tracking enabled
|
|
setMouseTracking(false);
|
|
m_enableMouseTrackingTimer.start();
|
|
}
|
|
|
|
QWidget::mousePressEvent(ev);
|
|
}
|
|
}
|
|
|
|
void TitleBar::mouseReleaseEvent(QMouseEvent* ev)
|
|
{
|
|
m_resizingTop = false;
|
|
m_resizingLeft = false;
|
|
m_resizingRight = false;
|
|
m_pendingRepositioning = false;
|
|
m_dragPos = QPoint();
|
|
QWidget::mouseReleaseEvent(ev);
|
|
}
|
|
|
|
QWindow* TitleBar::topLevelWindow() const
|
|
{
|
|
if (QWidget* topLevelWidget = window())
|
|
{
|
|
// TitleBar can be native instead of alien so calling this->window() would return
|
|
// an unrelated window.
|
|
return topLevelWidget->windowHandle();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool TitleBar::isTopResizeArea(const QPoint &globalPos) const
|
|
{
|
|
if (window() != parentWidget() && !isInDockWidgetWindowGroup())
|
|
{
|
|
// The immediate parent of the TitleBar must be a top level
|
|
// if it's not then we're docked and we're not interested in resizing the top.
|
|
return false;
|
|
}
|
|
|
|
if (QWindow* topLevelWin = topLevelWindow())
|
|
{
|
|
QPoint pt = mapFromGlobal(globalPos);
|
|
const bool fixedHeight = topLevelWin->maximumHeight() == topLevelWin->minimumHeight();
|
|
const bool maximized = topLevelWin->windowState() == Qt::WindowMaximized;
|
|
return !maximized && !fixedHeight && pt.y() < DockBar::ResizeTopMargin;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TitleBar::isLeftResizeArea(const QPoint& globalPos) const
|
|
{
|
|
#ifdef Q_OS_MAC
|
|
if (window() != parentWidget() && !isInDockWidgetWindowGroup())
|
|
{
|
|
// The immediate parent of the TitleBar must be a top level
|
|
// if it's not then we're docked and we're not interested in resizing the top.
|
|
return false;
|
|
}
|
|
|
|
if (QWindow* topLevelWin = topLevelWindow())
|
|
{
|
|
QPoint pt = mapFromGlobal(globalPos);
|
|
const bool fixedWidth = topLevelWin->maximumWidth() == topLevelWin->minimumWidth();
|
|
const bool maximized = topLevelWin->windowState() == Qt::WindowMaximized;
|
|
return !maximized && !fixedWidth && pt.x() < DockBar::ResizeTopMargin;
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
Q_UNUSED(globalPos);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool TitleBar::isRightResizeArea(const QPoint& globalPos) const
|
|
{
|
|
#ifdef Q_OS_MAC
|
|
if (window() != parentWidget() && !isInDockWidgetWindowGroup())
|
|
{
|
|
// The immediate parent of the TitleBar must be a top level
|
|
// if it's not then we're docked and we're not interested in resizing the top.
|
|
return false;
|
|
}
|
|
|
|
if (QWindow* topLevelWin = topLevelWindow())
|
|
{
|
|
QPoint pt = mapFromGlobal(globalPos);
|
|
const bool fixedWidth = topLevelWin->maximumWidth() == topLevelWin->minimumWidth();
|
|
const bool maximized = topLevelWin->windowState() == Qt::WindowMaximized;
|
|
return !maximized && !fixedWidth && pt.x() > width() - DockBar::ResizeTopMargin;
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
Q_UNUSED(globalPos);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
QRect TitleBar::draggableRect() const
|
|
{
|
|
// This is rect() - the button rect, so we can enable aero-snap dragging in that space
|
|
QRect r = rect();
|
|
const QPoint firstButtonPos = mapFromGlobal(m_firstButton->mapToGlobal(m_firstButton->pos()));
|
|
r.setWidth(firstButtonPos.x() - layout()->spacing());
|
|
return r;
|
|
}
|
|
|
|
bool TitleBar::canResize() const
|
|
{
|
|
const QWidget *w = window();
|
|
if (!w)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (isInDockWidget() && !isInFloatingDockWidget())
|
|
{
|
|
// We return false for all embedded dock widgets
|
|
return false;
|
|
}
|
|
|
|
return w && usesCustomTopBorderResizing() &&
|
|
w->minimumHeight() < w->maximumHeight() && !w->isMaximized();
|
|
}
|
|
|
|
void TitleBar::updateMouseCursor(const QPoint& globalPos)
|
|
{
|
|
if (!usesCustomTopBorderResizing())
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool usesResizeCursor = false;
|
|
switch (cursor().shape()) {
|
|
case Qt::SizeVerCursor:
|
|
case Qt::SizeHorCursor:
|
|
case Qt::SizeFDiagCursor:
|
|
case Qt::SizeBDiagCursor:
|
|
usesResizeCursor = true;
|
|
break;
|
|
default:
|
|
usesResizeCursor = false;
|
|
break;
|
|
}
|
|
|
|
if (!usesResizeCursor)
|
|
{
|
|
m_originalCursor = cursor().shape();
|
|
}
|
|
|
|
if (isTopResizeArea(globalPos) && isLeftResizeArea(globalPos))
|
|
{
|
|
setCursor(Qt::SizeFDiagCursor);
|
|
}
|
|
else if (isTopResizeArea(globalPos) && isRightResizeArea(globalPos))
|
|
{
|
|
setCursor(Qt::SizeBDiagCursor);
|
|
}
|
|
else if (isLeftResizeArea(globalPos) || isRightResizeArea(globalPos))
|
|
{
|
|
setCursor(Qt::SizeHorCursor);
|
|
}
|
|
else if (isTopResizeArea(globalPos))
|
|
{
|
|
setCursor(Qt::SizeVerCursor);
|
|
}
|
|
else
|
|
{
|
|
setCursor(m_originalCursor);
|
|
}
|
|
}
|
|
|
|
void TitleBar::resizeWindow(const QPoint& globalPos)
|
|
{
|
|
QWindow *w = topLevelWindow();
|
|
QRect geo = w->geometry();
|
|
|
|
// maxGeo has all sides of the rectangle expanded as far as allowed
|
|
const QRect maxGeo = QRect(QPoint(geo.right() - w->maximumWidth(),
|
|
geo.bottom() - w->maximumHeight()),
|
|
QPoint(geo.left() + w->maximumWidth(),
|
|
geo.top() + w->maximumHeight()));
|
|
// same for minGeo, we just have to take care that the size doesn't become "negative"
|
|
const QRect minGeo = QRect(QPoint(m_resizingLeft ? geo.right() - w->minimumWidth() : geo.left(),
|
|
m_resizingTop ? geo.bottom() - w->minimumHeight() : geo.top()),
|
|
QPoint(m_resizingRight ? geo.left() + w->minimumWidth() : geo.right(),
|
|
geo.bottom()));
|
|
|
|
if (m_resizingTop)
|
|
{
|
|
geo.setTop(qBound(maxGeo.top(), globalPos.y(), minGeo.top()));
|
|
}
|
|
if (m_resizingLeft)
|
|
{
|
|
geo.setLeft(qBound(maxGeo.left(), globalPos.x(), minGeo.left()));
|
|
}
|
|
if (m_resizingRight)
|
|
{
|
|
geo.setRight(qBound(minGeo.right(), globalPos.x(), maxGeo.right()));
|
|
}
|
|
|
|
geo = geo.intersected(maxGeo);
|
|
geo = geo.united(minGeo);
|
|
|
|
if (geo != w->geometry())
|
|
{
|
|
w->setGeometry(geo);
|
|
}
|
|
}
|
|
|
|
void TitleBar::dragWindow(const QPoint& globalPos)
|
|
{
|
|
QWidget* topLevel = window();
|
|
if (!topLevel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (isMaximized())
|
|
{
|
|
m_pendingRepositioning = true;
|
|
|
|
// have to refresh this value, as the width might have changed
|
|
m_lastLocalPosX = topLevel->mapFromGlobal(globalPos).x() / (topLevel->width() * 1.0);
|
|
|
|
handleMaximize();
|
|
return;
|
|
}
|
|
|
|
// Workaround QTBUG-47543
|
|
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10))
|
|
{
|
|
update();
|
|
}
|
|
|
|
if (m_pendingRepositioning)
|
|
{
|
|
// This is the case when moving a maximized window, window is restored and placed at 0,0
|
|
// or whatever is the top-left corner of your screen.
|
|
// We try to maintain the titlebar at the same relative position as when you clicked it.
|
|
// So, if you click on the beginning of a maximized titlebar, it will be restored and
|
|
// your mouse continues to be at the beginning of the titlebar.
|
|
// m_lastLocalPosX holds the place you clicked as a percentage of the width, because
|
|
// width will be smaller when you restore.
|
|
if (topLevel)
|
|
{
|
|
const int offset = static_cast<int>(m_lastLocalPosX) * width();
|
|
topLevel->move(globalPos - QPoint(offset, 0));
|
|
m_dragPos.setX(offset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is the normal case, we move the window
|
|
QWindow* wind = topLevel->windowHandle();
|
|
|
|
if (wind->width() < m_dragPos.x())
|
|
{
|
|
// The window was resized while we were dragging to a screen with different (dpi) scale factor
|
|
// It shrunk, so calculate a new sensible drag pos, because the old is out of screen
|
|
m_dragPos.setX(static_cast<int>(m_relativeDragPos) * wind->width());
|
|
}
|
|
|
|
// (Don't cache the margins, they are be different when moving to screens with different scale factors)
|
|
const QPoint newPoint = globalPos - (m_dragPos + QPoint(wind->frameMargins().left(), wind->frameMargins().top()));
|
|
topLevel->move(newPoint);
|
|
}
|
|
|
|
m_pendingRepositioning = false;
|
|
}
|
|
|
|
bool TitleBar::isTitleBarForDockWidget() const
|
|
{
|
|
return qobject_cast<StyledDockWidget*>(parent()) != nullptr;
|
|
}
|
|
|
|
void TitleBar::mouseMoveEvent(QMouseEvent* ev)
|
|
{
|
|
// use QCursor::pos(); in scenarios with multiple screens and different scale factors,
|
|
// it's much more reliable about actually reporting a global position.
|
|
QPoint globalPos = QCursor::pos();
|
|
|
|
if (isResizingWindow())
|
|
{
|
|
resizeWindow(globalPos);
|
|
}
|
|
else if (isDraggingWindow())
|
|
{
|
|
dragWindow(globalPos);
|
|
}
|
|
else
|
|
{
|
|
updateMouseCursor(globalPos);
|
|
QWidget::mouseMoveEvent(ev);
|
|
m_pendingRepositioning = false;
|
|
}
|
|
}
|
|
|
|
void TitleBar::mouseDoubleClickEvent(QMouseEvent*)
|
|
{
|
|
StyledDockWidget* dockWidgetParent = qobject_cast<StyledDockWidget*>(parentWidget());
|
|
if (m_buttons.contains(DockBarButton::MaximizeButton) || (dockWidgetParent && dockWidgetParent->isFloating()))
|
|
{
|
|
handleMaximize();
|
|
}
|
|
}
|
|
|
|
void TitleBar::timerEvent(QTimerEvent*)
|
|
{
|
|
fixEnabled();
|
|
}
|
|
|
|
bool TitleBar::isInDockWidgetWindowGroup() const
|
|
{
|
|
// DockWidgetGroupWindow is not exposed as a public API class from Qt, so we have to check for it
|
|
// based on the className instead.
|
|
return window() && strcmp(window()->metaObject()->className(), "QDockWidgetGroupWindow") == 0;
|
|
}
|
|
|
|
void TitleBar::fixEnabled()
|
|
{
|
|
StyledDockWidget* dockWidgetParent = qobject_cast<StyledDockWidget*>(parentWidget());
|
|
QWidget* groupWindowParent = dockWidgetParent ? dockWidgetParent->parentWidget() : nullptr;
|
|
|
|
// DockWidgetGroupWindow has issues. It renders the TitleBar when floating (and only when it's floating), but renders everything else over top of it.
|
|
// In that case, the TitleBar still gets mouse clicks, so if it's under a menu bar, the menu bar doesn't work.
|
|
// The following is a workaround
|
|
bool shouldBeLowered = false;
|
|
if (isInDockWidgetWindowGroup() && groupWindowParent)
|
|
{
|
|
shouldBeLowered = true;
|
|
}
|
|
|
|
// if we're in a group, our title bar should never be over top of anything else
|
|
// But! we don't want to muck with the order in any other case, because it could screw something up.
|
|
// So we only lower it ever.
|
|
|
|
if (shouldBeLowered)
|
|
{
|
|
lower();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle button press signals from our DockBarButtons based on their type
|
|
*/
|
|
void TitleBar::handleButtonClicked(const DockBarButton::WindowDecorationButton type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case DockBarButton::CloseButton:
|
|
handleClose();
|
|
break;
|
|
case DockBarButton::MinimizeButton:
|
|
handleMinimize();
|
|
break;
|
|
case DockBarButton::MaximizeButton:
|
|
handleMaximize();
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool TitleBar::isMaximized() const
|
|
{
|
|
return window() && window()->isMaximized();
|
|
}
|
|
|
|
static QVector<DockBarButton::WindowDecorationButton> findDisabledButtons(QLayout* layout)
|
|
{
|
|
QVector<DockBarButton::WindowDecorationButton> disabledButtons;
|
|
int itemsInLayout = layout->count();
|
|
for (int i = 0; i < itemsInLayout; i++)
|
|
{
|
|
QLayoutItem* item = layout->itemAt(i);
|
|
DockBarButton* dockBarButton = qobject_cast<DockBarButton*>(item->widget());
|
|
if (dockBarButton && !dockBarButton->isEnabled())
|
|
{
|
|
disabledButtons.push_back(dockBarButton->buttonType());
|
|
}
|
|
}
|
|
|
|
return disabledButtons;
|
|
}
|
|
|
|
void TitleBar::setupButtons(bool useDividerButtons /*= true */)
|
|
{
|
|
setupButtonsHelper(m_tabButtonsContainer, m_tabButtonsLayout, useDividerButtons);
|
|
setupButtonsHelper(m_buttonsContainer, m_buttonsLayout, useDividerButtons);
|
|
}
|
|
|
|
void TitleBar::setupButtonsHelper(QFrame* container, QHBoxLayout* layout, bool useDividerButtons)
|
|
{
|
|
// Before we do anything else, figure out if any existing buttons were disabled.
|
|
// Only trust items already in the layout.
|
|
QVector<DockBarButton::WindowDecorationButton> disabledButtons = findDisabledButtons(layout);
|
|
|
|
// Remove the old buttons from our layout.
|
|
const auto oldButtons = container->findChildren<QWidget*>();
|
|
for (auto button : oldButtons)
|
|
{
|
|
button->hide();
|
|
layout->removeWidget(button);
|
|
|
|
// Use QObject::deleteLater to make this function safe to call whilst the application is
|
|
// being polished/unpolished.
|
|
button->deleteLater();
|
|
}
|
|
|
|
m_firstButton = nullptr;
|
|
|
|
for (auto buttonType : m_buttons)
|
|
{
|
|
QWidget* w = nullptr;
|
|
if (buttonType == DockBarButton::DividerButton)
|
|
{
|
|
if (!useDividerButtons)
|
|
{
|
|
continue;
|
|
}
|
|
w = new ButtonDivider(container);
|
|
}
|
|
else
|
|
{
|
|
// Use the dark style of buttons for the titlebars on floating
|
|
// dock widget containers since they are a lighter color
|
|
StyledDockWidget* dockWidgetParent = qobject_cast<StyledDockWidget*>(parentWidget());
|
|
bool isDarkStyle = dockWidgetParent && dockWidgetParent->isFloating();
|
|
|
|
DockBarButton* button = new DockBarButton(buttonType, container, isDarkStyle);
|
|
QObject::connect(button, &DockBarButton::buttonPressed, this, &TitleBar::handleButtonClicked);
|
|
w = button;
|
|
|
|
if (disabledButtons.contains(buttonType))
|
|
{
|
|
button->setDisabled(true);
|
|
}
|
|
}
|
|
|
|
if (!m_firstButton)
|
|
{
|
|
m_firstButton = w;
|
|
}
|
|
|
|
layout->addWidget(w);
|
|
}
|
|
}
|
|
|
|
bool TitleBar::isLeftButtonDown() const
|
|
{
|
|
return (QApplication::mouseButtons() & Qt::LeftButton) != 0;
|
|
}
|
|
|
|
bool TitleBar::canDragWindow() const
|
|
{
|
|
// Dock widgets use the internal Qt drag implementation
|
|
return m_dragEnabled;
|
|
}
|
|
|
|
/**
|
|
* Helper function to determine if we are currently resizing our title bar
|
|
* from the top of our widget
|
|
*/
|
|
bool TitleBar::isResizingWindow() const
|
|
{
|
|
return isLeftButtonDown() && (m_resizingTop || m_resizingLeft || m_resizingRight) && canResize();
|
|
}
|
|
|
|
/**
|
|
* Helper function to determine if we are currently in the state of click+dragging
|
|
* our title bar to reposition it
|
|
*/
|
|
bool TitleBar::isDraggingWindow() const
|
|
{
|
|
return isLeftButtonDown() && !m_dragPos.isNull() && canDragWindow();
|
|
}
|
|
|
|
void TitleBar::setButtons(WindowDecorationButtons buttons)
|
|
{
|
|
if (buttons != m_buttons)
|
|
{
|
|
m_buttons = buttons;
|
|
setupButtons(false);
|
|
}
|
|
}
|
|
|
|
int TitleBar::numButtons() const
|
|
{
|
|
return m_buttons.size();
|
|
}
|
|
|
|
void TitleBar::setForceInactive(bool force)
|
|
{
|
|
if (m_forceInactive != force)
|
|
{
|
|
m_forceInactive = force;
|
|
update();
|
|
emit forceInactiveChanged(m_forceInactive);
|
|
}
|
|
}
|
|
|
|
} // namespace AzQtComponents
|
|
|
|
#include "Components/moc_Titlebar.cpp"
|