From 5e8e654324443d9e7e20fe0d65e2279150299306 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 4 Jun 2020 18:26:51 +0400 Subject: [PATCH] Add cross-platform TitleWidget implementation based on startSystemMove/startSystemResize --- Telegram/CMakeLists.txt | 2 + .../platform/platform_window_title.h | 9 +- Telegram/SourceFiles/window/window.style | 1 + .../SourceFiles/window/window_title_qt.cpp | 228 ++++++++++++++++++ Telegram/SourceFiles/window/window_title_qt.h | 58 +++++ 5 files changed, 296 insertions(+), 2 deletions(-) create mode 100644 Telegram/SourceFiles/window/window_title_qt.cpp create mode 100644 Telegram/SourceFiles/window/window_title_qt.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d0abe76713..29c5b88884 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1049,6 +1049,8 @@ PRIVATE window/window_session_controller.h window/window_slide_animation.cpp window/window_slide_animation.h + window/window_title_qt.cpp + window/window_title_qt.h window/window_title.h window/window_top_bar_wrap.h window/themes/window_theme.cpp diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h index d8d57cbb33..465db8d736 100644 --- a/Telegram/SourceFiles/platform/platform_window_title.h +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "window/window_title.h" +#include "window/window_title_qt.h" #include "window/themes/window_theme_preview.h" #include "base/object_ptr.h" @@ -26,11 +27,15 @@ void PreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRe #include "platform/mac/window_title_mac.h" #elif defined Q_OS_WIN // Q_OS_MAC #include "platform/win/window_title_win.h" -#elif defined Q_OS_WINRT || defined Q_OS_UNIX // Q_OS_MAC || Q_OS_WIN +#else // Q_OS_MAC || Q_OS_WIN namespace Platform { inline object_ptr CreateTitleWidget(QWidget *parent) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED + return object_ptr(parent); +#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED + return { nullptr }; } @@ -44,4 +49,4 @@ inline void PreviewWindowFramePaint(QImage &preview, const style::palette &palet } // namespace Platform -#endif // Q_OS_MAC || Q_OS_WIN || Q_OS_WINRT || Q_OS_UNIX +#endif // Q_OS_MAC || Q_OS_WIN diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 7e75ef5896..0b1d8fe0ab 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -16,6 +16,7 @@ windowDefaultWidth: 800px; windowDefaultHeight: 600px; windowBigDefaultWidth: 1024px; windowBigDefaultHeight: 768px; +windowResizeArea: 4px; columnMinimalWidthLeft: 260px; columnMaximalWidthLeft: 540px; diff --git a/Telegram/SourceFiles/window/window_title_qt.cpp b/Telegram/SourceFiles/window/window_title_qt.cpp new file mode 100644 index 0000000000..17edd2368a --- /dev/null +++ b/Telegram/SourceFiles/window/window_title_qt.cpp @@ -0,0 +1,228 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "window/window_title_qt.h" + +#include "ui/widgets/buttons.h" +#include "ui/widgets/shadow.h" +#include "styles/style_window.h" + +#include +#include + +namespace Window { + +TitleWidgetQt::TitleWidgetQt(QWidget *parent) +: TitleWidget(parent) +, _st(st::defaultWindowTitle) +, _minimize(this, _st.minimize) +, _maximizeRestore(this, _st.maximize) +, _close(this, _st.close) +, _shadow(this, st::titleShadow) +, _maximizedState(parent->window()->windowState() & Qt::WindowMaximized) { + _minimize->setClickedCallback([=] { + window()->setWindowState( + window()->windowState() | Qt::WindowMinimized); + _minimize->clearState(); + }); + _minimize->setPointerCursor(false); + _maximizeRestore->setClickedCallback([=] { + window()->setWindowState(_maximizedState + ? Qt::WindowNoState + : Qt::WindowMaximized); + _maximizeRestore->clearState(); + }); + _maximizeRestore->setPointerCursor(false); + _close->setClickedCallback([=] { + window()->close(); + _close->clearState(); + }); + _close->setPointerCursor(false); + + QCoreApplication::instance()->installEventFilter(this); + + window()->setWindowFlag(Qt::FramelessWindowHint, true); + setAttribute(Qt::WA_OpaquePaintEvent); + resize(width(), _st.height); +} + +void TitleWidgetQt::init() { + connect( + window()->windowHandle(), + &QWindow::windowStateChanged, + this, + [=](Qt::WindowState state) { windowStateChanged(state); }); + _maximizedState = (window()->windowState() & Qt::WindowMaximized); + _activeState = isActiveWindow(); + updateButtonsState(); +} + +void TitleWidgetQt::paintEvent(QPaintEvent *e) { + auto active = isActiveWindow(); + if (_activeState != active) { + _activeState = active; + updateButtonsState(); + } + Painter(this).fillRect(rect(), active ? _st.bgActive : _st.bg); +} + +void TitleWidgetQt::updateControlsPosition() { + auto right = 0; + _close->moveToRight(right, 0); right += _close->width(); + _maximizeRestore->moveToRight(right, 0); right += _maximizeRestore->width(); + _minimize->moveToRight(right, 0); +} + +void TitleWidgetQt::resizeEvent(QResizeEvent *e) { + updateControlsPosition(); + _shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth); +} + +void TitleWidgetQt::mousePressEvent(QMouseEvent *e) { + if (e->button() != Qt::LeftButton) { + return; + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED + window()->windowHandle()->startSystemMove(); +#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED +} + +void TitleWidgetQt::mouseDoubleClickEvent(QMouseEvent *e) { + if (window()->windowState() == Qt::WindowMaximized) { + window()->setWindowState(Qt::WindowNoState); + } else { + window()->setWindowState(Qt::WindowMaximized); + } +} + +bool TitleWidgetQt::eventFilter(QObject *obj, QEvent *e) { + if (e->type() == QEvent::MouseMove + || e->type() == QEvent::MouseButtonPress) { + if(window()->isAncestorOf(static_cast(obj))) { + const auto mouseEvent = static_cast(e); + + if (e->type() == QEvent::MouseMove) { + updateCursor(mouseEvent->windowPos().toPoint()); + } + + if(e->type() == QEvent::MouseButtonPress + && mouseEvent->button() == Qt::LeftButton + && window()->windowState() != Qt::WindowMaximized) { + return startResize(mouseEvent->windowPos().toPoint()); + } + } + } + + return TitleWidget::eventFilter(obj, e); +} + +void TitleWidgetQt::windowStateChanged(Qt::WindowState state) { + if (state == Qt::WindowMinimized) { + return; + } + + const auto maximized = (state == Qt::WindowMaximized); + if (_maximizedState != maximized) { + _maximizedState = maximized; + updateButtonsState(); + } +} + +void TitleWidgetQt::updateButtonsState() { + _minimize->setIconOverride(_activeState + ? &_st.minimizeIconActive + : nullptr, + _activeState + ? &_st.minimizeIconActiveOver + : nullptr); + if (_maximizedState) { + _maximizeRestore->setIconOverride( + _activeState + ? &_st.restoreIconActive : &_st.restoreIcon, + _activeState + ? &_st.restoreIconActiveOver + : &_st.restoreIconOver); + } else { + _maximizeRestore->setIconOverride(_activeState + ? &_st.maximizeIconActive + : nullptr, + _activeState + ? &_st.maximizeIconActiveOver + : nullptr); + } + _close->setIconOverride(_activeState + ? &_st.closeIconActive + : nullptr, + _activeState + ? &_st.closeIconActiveOver + : nullptr); +} + +Qt::Edges TitleWidgetQt::edgesFromPos(const QPoint &pos) { + if (pos.x() <= st::windowResizeArea) { + if (pos.y() <= st::windowResizeArea) { + return Qt::LeftEdge | Qt::TopEdge; + } else if (pos.y() >= (window()->height() - st::windowResizeArea)) { + return Qt::LeftEdge | Qt::BottomEdge; + } + + return Qt::LeftEdge; + } else if (pos.x() >= (window()->width() - st::windowResizeArea)) { + if (pos.y() <= st::windowResizeArea) { + return Qt::RightEdge | Qt::TopEdge; + } else if (pos.y() >= (window()->height() - st::windowResizeArea)) { + return Qt::RightEdge | Qt::BottomEdge; + } + + return Qt::RightEdge; + } else if (pos.y() <= st::windowResizeArea) { + return Qt::TopEdge; + } else if (pos.y() >= (window()->height() - st::windowResizeArea)) { + return Qt::BottomEdge; + } else { + return 0; + } +} + +void TitleWidgetQt::updateCursor(const QPoint &pos) { + const auto edges = edgesFromPos(pos); + + if (!edges || window()->windowState() == Qt::WindowMaximized) { + while (QGuiApplication::overrideCursor()) { + QGuiApplication::restoreOverrideCursor(); + } + + return; + } else if (!QGuiApplication::overrideCursor()) { + QGuiApplication::setOverrideCursor(QCursor()); + } + + if (((edges & Qt::LeftEdge) && (edges & Qt::TopEdge)) + || ((edges & Qt::RightEdge) && (edges & Qt::BottomEdge))) { + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeFDiagCursor)); + } else if (((edges & Qt::LeftEdge) && (edges & Qt::BottomEdge)) + || ((edges & Qt::RightEdge) && (edges & Qt::TopEdge))) { + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeBDiagCursor)); + } else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) { + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeHorCursor)); + } else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) { + QGuiApplication::changeOverrideCursor(QCursor(Qt::SizeVerCursor)); + } +} + +bool TitleWidgetQt::startResize(const QPoint &pos) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED + if (const auto edges = edgesFromPos(pos)) { + return window()->windowHandle()->startSystemResize(edges); + } +#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED + + return false; +} + +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_title_qt.h b/Telegram/SourceFiles/window/window_title_qt.h new file mode 100644 index 0000000000..31a84a9b0e --- /dev/null +++ b/Telegram/SourceFiles/window/window_title_qt.h @@ -0,0 +1,58 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "window/window_title.h" +#include "base/object_ptr.h" + +namespace style { +struct WindowTitle; +} // namespace style + +namespace Ui { +class IconButton; +class PlainShadow; +} // namespace Ui + +namespace Window { + +class TitleWidgetQt : public TitleWidget { +public: + TitleWidgetQt(QWidget *parent); + + void init() override; + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseDoubleClickEvent(QMouseEvent *e) override; + + bool eventFilter(QObject *obj, QEvent *e) override; + +private: + void windowStateChanged(Qt::WindowState state = Qt::WindowNoState); + void updateButtonsState(); + void updateControlsPosition(); + + Qt::Edges edgesFromPos(const QPoint &pos); + void updateCursor(const QPoint &pos); + bool startResize(const QPoint &pos); + + const style::WindowTitle &_st; + object_ptr _minimize; + object_ptr _maximizeRestore; + object_ptr _close; + object_ptr _shadow; + + bool _maximizedState = false; + bool _activeState = false; + +}; + +} // namespace Window