Implement round window with Qt-based custom decorations

This commit is contained in:
Ilya Fedin 2022-06-04 11:46:51 +04:00 committed by John Preston
parent 81b9028e1c
commit 8e35ae4407
2 changed files with 71 additions and 26 deletions

View file

@ -13,6 +13,7 @@
#include "ui/painter.h" #include "ui/painter.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/palette.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtGui/QWindow> #include <QtGui/QWindow>
@ -26,6 +27,10 @@ namespace {
return st::callShadow; return st::callShadow;
} }
[[nodiscard]] int Radius() {
return st::callRadius;
}
} // namespace } // namespace
BasicWindowHelper::BasicWindowHelper(not_null<RpWidget*> window) BasicWindowHelper::BasicWindowHelper(not_null<RpWidget*> window)
@ -181,7 +186,8 @@ void BasicWindowHelper::setupBodyTitleAreaEvents() {
DefaultWindowHelper::DefaultWindowHelper(not_null<RpWidget*> window) DefaultWindowHelper::DefaultWindowHelper(not_null<RpWidget*> window)
: BasicWindowHelper(window) : BasicWindowHelper(window)
, _title(Ui::CreateChild<DefaultTitleWidget>(window.get())) , _title(Ui::CreateChild<DefaultTitleWidget>(window.get()))
, _body(Ui::CreateChild<RpWidget>(window.get())) { , _body(Ui::CreateChild<RpWidget>(window.get()))
, _roundRect(Radius(), st::windowBg) {
init(); init();
} }
@ -230,34 +236,27 @@ void DefaultWindowHelper::init() {
area.top() + (titleShown ? titleHeight : 0)); area.top() + (titleShown ? titleHeight : 0));
_body->setGeometry(QRect(topLeft, sizeWithoutMargins)); _body->setGeometry(QRect(topLeft, sizeWithoutMargins));
updateRoundingOverlay();
}, _body->lifetime()); }, _body->lifetime());
window()->paintRequest( window()->paintRequest(
) | rpl::start_with_next([=] { ) | rpl::filter([=] {
const auto area = resizeArea(); return !hasShadow() && !resizeArea().isNull();
}) | rpl::start_with_next([=] {
if (area.isNull()) {
return;
}
Painter p(window()); Painter p(window());
paintBorders(p);
if (hasShadow()) {
Ui::Shadow::paint(
p,
QRect(QPoint(), window()->size()).marginsRemoved(area),
window()->width(),
Shadow());
} else {
paintBorders(p);
}
}, window()->lifetime()); }, window()->lifetime());
rpl::combine( rpl::combine(
window()->shownValue(), window()->shownValue(),
_title->shownValue(),
_windowState.value() _windowState.value()
) | rpl::start_with_next([=](bool shown, Qt::WindowStates windowState) { ) | rpl::start_with_next([=](
bool shown,
bool titleShown,
Qt::WindowStates windowState) {
if (shown) { if (shown) {
window()->windowHandle()->setFlag(Qt::FramelessWindowHint, titleShown);
updateWindowExtents(); updateWindowExtents();
} }
}, window()->lifetime()); }, window()->lifetime());
@ -279,6 +278,52 @@ void DefaultWindowHelper::init() {
QCoreApplication::instance()->installEventFilter(this); QCoreApplication::instance()->installEventFilter(this);
} }
void DefaultWindowHelper::updateRoundingOverlay() {
if (!hasShadow() || resizeArea().isNull()) {
_roundingOverlay.destroy();
return;
} else if (_roundingOverlay) {
return;
}
_roundingOverlay.create(window());
_roundingOverlay->setAttribute(Qt::WA_TransparentForMouseEvents);
_roundingOverlay->show();
window()->sizeValue(
) | rpl::start_with_next([=](QSize size) {
_roundingOverlay->setGeometry(QRect(QPoint(), size));
}, _roundingOverlay->lifetime());
_roundingOverlay->paintRequest(
) | rpl::filter([=](QRect clip) {
const auto rect = window()->rect().marginsRemoved(resizeArea());
const auto radius = Radius();
const auto radiusWithFix = radius - 1;
const auto radiusSize = QSize(radius, radius);
return clip.intersects(QRect(
rect.topLeft(),
radiusSize
)) || clip.intersects(QRect(
rect.topRight() - QPoint(radiusWithFix, 0),
radiusSize
)) || clip.intersects(QRect(
rect.bottomRight() - QPoint(0, radiusWithFix),
radiusSize
)) || clip.intersects(QRect(
rect.bottomRight() - QPoint(radiusWithFix, radiusWithFix),
radiusSize
)) || !rect.contains(clip);
}) | rpl::start_with_next([=] {
Painter p(_roundingOverlay);
const auto rect = window()->rect().marginsRemoved(resizeArea());
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
_roundRect.paint(p, rect, RectPart::AllCorners);
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
Shadow::paint(p, rect, window()->width(), Shadow());
}, _roundingOverlay->lifetime());
}
not_null<RpWidget*> DefaultWindowHelper::body() { not_null<RpWidget*> DefaultWindowHelper::body() {
return _body; return _body;
} }
@ -375,9 +420,7 @@ void DefaultWindowHelper::setTitleStyle(const style::WindowTitle &st) {
} }
void DefaultWindowHelper::setNativeFrame(bool enabled) { void DefaultWindowHelper::setNativeFrame(bool enabled) {
window()->windowHandle()->setFlag(Qt::FramelessWindowHint, !enabled);
_title->setVisible(!enabled); _title->setVisible(!enabled);
updateWindowExtents();
} }
void DefaultWindowHelper::setMinimumSize(QSize size) { void DefaultWindowHelper::setMinimumSize(QSize size) {
@ -441,13 +484,10 @@ void DefaultWindowHelper::paintBorders(QPainter &p) {
void DefaultWindowHelper::updateWindowExtents() { void DefaultWindowHelper::updateWindowExtents() {
if (hasShadow() && !_title->isHidden()) { if (hasShadow() && !_title->isHidden()) {
Platform::SetWindowExtents( SetWindowExtents(window()->windowHandle(), resizeArea());
window()->windowHandle(),
resizeArea());
_extentsSet = true; _extentsSet = true;
} else if (_extentsSet) { } else if (_extentsSet) {
Platform::UnsetWindowExtents(window()->windowHandle()); UnsetWindowExtents(window()->windowHandle());
_extentsSet = false; _extentsSet = false;
} }
} }

View file

@ -7,6 +7,8 @@
#pragma once #pragma once
#include "base/flags.h" #include "base/flags.h"
#include "base/object_ptr.h"
#include "ui/round_rect.h"
namespace style { namespace style {
struct WindowTitle; struct WindowTitle;
@ -95,6 +97,7 @@ protected:
private: private:
void init(); void init();
void updateRoundingOverlay();
[[nodiscard]] bool hasShadow() const; [[nodiscard]] bool hasShadow() const;
[[nodiscard]] QMargins resizeArea() const; [[nodiscard]] QMargins resizeArea() const;
[[nodiscard]] Qt::Edges edgesFromPos(const QPoint &pos) const; [[nodiscard]] Qt::Edges edgesFromPos(const QPoint &pos) const;
@ -106,6 +109,8 @@ private:
const not_null<DefaultTitleWidget*> _title; const not_null<DefaultTitleWidget*> _title;
const not_null<RpWidget*> _body; const not_null<RpWidget*> _body;
RoundRect _roundRect;
object_ptr<RpWidget> _roundingOverlay = { nullptr };
bool _extentsSet = false; bool _extentsSet = false;
rpl::variable<Qt::WindowStates> _windowState = Qt::WindowNoState; rpl::variable<Qt::WindowStates> _windowState = Qt::WindowNoState;