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

View file

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