2023-02-14 13:27:52 +00:00
|
|
|
/*
|
|
|
|
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 "platform/platform_overlay_widget.h"
|
|
|
|
|
2023-02-24 07:43:02 +00:00
|
|
|
#include "ui/effects/animations.h"
|
2023-02-14 13:27:52 +00:00
|
|
|
#include "ui/platform/ui_platform_window_title.h"
|
2023-02-17 09:21:19 +00:00
|
|
|
#include "ui/widgets/rp_window.h"
|
2023-02-24 07:43:02 +00:00
|
|
|
#include "ui/abstract_button.h"
|
|
|
|
#include "styles/style_media_view.h"
|
2023-02-14 13:27:52 +00:00
|
|
|
|
2023-02-28 13:49:18 +00:00
|
|
|
namespace Media::View {
|
|
|
|
|
|
|
|
QColor OverBackgroundColor() {
|
|
|
|
auto c1 = st::mediaviewBg->c;
|
|
|
|
auto c2 = QColor(255, 255, 255);
|
|
|
|
const auto mix = [&](int a, int b) {
|
|
|
|
constexpr auto k1 = 0.15 * 0.85 / (1. - 0.85 * 0.85);
|
|
|
|
constexpr auto k2 = 0.15 / (1. - 0.85 * 0.85);
|
|
|
|
return int(a * k1 + b * k2);
|
|
|
|
};
|
|
|
|
return QColor(
|
|
|
|
mix(c1.red(), c2.red()),
|
|
|
|
mix(c1.green(), c2.green()),
|
|
|
|
mix(c1.blue(), c2.blue()));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Media::View
|
|
|
|
|
2023-02-14 13:27:52 +00:00
|
|
|
namespace Platform {
|
2023-02-24 07:43:02 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
using namespace Media::View;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
class DefaultOverlayWidgetHelper::Buttons final
|
|
|
|
: public Ui::Platform::AbstractTitleButtons {
|
|
|
|
public:
|
|
|
|
using Control = Ui::Platform::TitleControl;
|
|
|
|
|
|
|
|
object_ptr<Ui::AbstractButton> create(
|
|
|
|
not_null<QWidget*> parent,
|
|
|
|
Control control,
|
|
|
|
const style::WindowTitle &st) override;
|
|
|
|
void updateState(
|
|
|
|
bool active,
|
|
|
|
bool maximized,
|
|
|
|
const style::WindowTitle &st) override;
|
|
|
|
void notifySynteticOver(Control control, bool over) override;
|
|
|
|
|
|
|
|
void setMasterOpacity(float64 opacity);
|
|
|
|
[[nodiscard]] rpl::producer<> activations() const;
|
|
|
|
|
|
|
|
void clearState();
|
|
|
|
|
|
|
|
private:
|
|
|
|
rpl::event_stream<> _activations;
|
|
|
|
rpl::variable<float64> _masterOpacity = 1.;
|
|
|
|
rpl::variable<bool> _maximized = false;
|
|
|
|
rpl::event_stream<> _clearStateRequests;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
object_ptr<Ui::AbstractButton> DefaultOverlayWidgetHelper::Buttons::create(
|
|
|
|
not_null<QWidget*> parent,
|
|
|
|
Control control,
|
|
|
|
const style::WindowTitle &st) {
|
|
|
|
auto result = object_ptr<Ui::AbstractButton>(parent);
|
|
|
|
const auto raw = result.data();
|
|
|
|
|
|
|
|
struct State {
|
|
|
|
Ui::Animations::Simple animation;
|
|
|
|
float64 progress = -1.;
|
|
|
|
QImage frame;
|
|
|
|
bool maximized = false;
|
|
|
|
bool over = false;
|
|
|
|
};
|
|
|
|
const auto state = raw->lifetime().make_state<State>();
|
|
|
|
|
|
|
|
rpl::merge(
|
|
|
|
_masterOpacity.changes() | rpl::to_empty,
|
|
|
|
_maximized.changes() | rpl::to_empty
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
raw->update();
|
|
|
|
}, raw->lifetime());
|
|
|
|
|
|
|
|
_clearStateRequests.events(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
raw->clearState();
|
|
|
|
raw->update();
|
|
|
|
state->over = raw->isOver();
|
|
|
|
state->animation.stop();
|
|
|
|
}, raw->lifetime());
|
|
|
|
|
|
|
|
const auto icon = [&] {
|
|
|
|
switch (control) {
|
|
|
|
case Control::Minimize: return &st::mediaviewTitleMinimize;
|
|
|
|
case Control::Maximize: return &st::mediaviewTitleMaximize;
|
|
|
|
case Control::Close: return &st::mediaviewTitleClose;
|
|
|
|
}
|
|
|
|
Unexpected("Value in DefaultOverlayWidgetHelper::Buttons::create.");
|
|
|
|
}();
|
|
|
|
|
|
|
|
raw->resize(icon->size());
|
|
|
|
state->frame = QImage(
|
|
|
|
icon->size() * style::DevicePixelRatio(),
|
|
|
|
QImage::Format_ARGB32_Premultiplied);
|
|
|
|
state->frame.setDevicePixelRatio(style::DevicePixelRatio());
|
|
|
|
|
|
|
|
const auto updateOver = [=] {
|
|
|
|
const auto over = raw->isOver();
|
|
|
|
if (state->over == over) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
state->over = over;
|
|
|
|
state->animation.start(
|
|
|
|
[=] { raw->update(); },
|
|
|
|
state->over ? 0. : 1.,
|
|
|
|
state->over ? 1. : 0.,
|
|
|
|
st::mediaviewFadeDuration);
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto prepareFrame = [=] {
|
|
|
|
const auto progress = state->animation.value(state->over ? 1. : 0.);
|
|
|
|
const auto maximized = _maximized.current();
|
|
|
|
if (state->progress == progress && state->maximized == maximized) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
state->progress = progress;
|
|
|
|
state->maximized = maximized;
|
|
|
|
auto current = icon;
|
|
|
|
if (control == Control::Maximize) {
|
|
|
|
current = maximized ? &st::mediaviewTitleRestore : icon;
|
|
|
|
}
|
|
|
|
const auto alpha = progress * kOverBackgroundOpacity;
|
2023-02-28 13:49:18 +00:00
|
|
|
auto color = OverBackgroundColor();
|
|
|
|
color.setAlpha(anim::interpolate(0, 255, alpha));
|
|
|
|
state->frame.fill(color);
|
2023-02-24 07:43:02 +00:00
|
|
|
|
|
|
|
auto q = QPainter(&state->frame);
|
|
|
|
const auto normal = maximized
|
|
|
|
? kMaximizedIconOpacity
|
|
|
|
: kNormalIconOpacity;
|
|
|
|
q.setOpacity(progress + (1 - progress) * normal);
|
|
|
|
current->paint(q, 0, 0, raw->width());
|
|
|
|
q.end();
|
|
|
|
};
|
|
|
|
|
|
|
|
raw->paintRequest(
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
updateOver();
|
|
|
|
prepareFrame();
|
|
|
|
|
|
|
|
auto p = QPainter(raw);
|
|
|
|
p.setOpacity(_masterOpacity.current());
|
|
|
|
p.drawImage(0, 0, state->frame);
|
|
|
|
}, raw->lifetime());
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::Buttons::updateState(
|
|
|
|
bool active,
|
|
|
|
bool maximized,
|
|
|
|
const style::WindowTitle &st) {
|
|
|
|
_maximized = maximized;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::Buttons::notifySynteticOver(
|
|
|
|
Ui::Platform::TitleControl control,
|
|
|
|
bool over) {
|
|
|
|
if (over) {
|
|
|
|
_activations.fire({});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::Buttons::clearState() {
|
|
|
|
_clearStateRequests.fire({});
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::Buttons::setMasterOpacity(float64 opacity) {
|
|
|
|
_masterOpacity = opacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl::producer<> DefaultOverlayWidgetHelper::Buttons::activations() const {
|
|
|
|
return _activations.events();
|
|
|
|
}
|
2023-02-14 13:27:52 +00:00
|
|
|
|
2023-02-17 09:21:19 +00:00
|
|
|
void OverlayWidgetHelper::minimize(not_null<Ui::RpWindow*> window) {
|
|
|
|
window->setWindowState(window->windowState() | Qt::WindowMinimized);
|
|
|
|
}
|
|
|
|
|
2023-02-14 13:27:52 +00:00
|
|
|
DefaultOverlayWidgetHelper::DefaultOverlayWidgetHelper(
|
|
|
|
not_null<Ui::RpWindow*> window,
|
|
|
|
Fn<void(bool)> maximize)
|
2023-02-24 07:43:02 +00:00
|
|
|
: _buttons(new DefaultOverlayWidgetHelper::Buttons())
|
|
|
|
, _controls(Ui::Platform::SetupSeparateTitleControls(
|
2023-02-14 13:27:52 +00:00
|
|
|
window,
|
2023-02-24 07:43:02 +00:00
|
|
|
std::make_unique<Ui::Platform::SeparateTitleControls>(
|
|
|
|
window->body(),
|
|
|
|
st::mediaviewTitle,
|
|
|
|
std::unique_ptr<DefaultOverlayWidgetHelper::Buttons>(_buttons.get()),
|
|
|
|
std::move(maximize)))) {
|
2023-02-14 13:27:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DefaultOverlayWidgetHelper::~DefaultOverlayWidgetHelper() = default;
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::orderWidgets() {
|
|
|
|
_controls->wrap.raise();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DefaultOverlayWidgetHelper::skipTitleHitTest(QPoint position) {
|
|
|
|
return _controls->controls.geometry().contains(position);
|
|
|
|
}
|
|
|
|
|
2023-02-24 07:43:02 +00:00
|
|
|
rpl::producer<> DefaultOverlayWidgetHelper::controlsActivations() {
|
|
|
|
return _buttons->activations();
|
|
|
|
}
|
|
|
|
|
2023-03-13 11:58:10 +00:00
|
|
|
rpl::producer<bool> DefaultOverlayWidgetHelper::controlsSideRightValue() {
|
2023-03-29 13:23:21 +00:00
|
|
|
using namespace Ui::Platform;
|
|
|
|
|
|
|
|
return TitleControlsLayoutValue(
|
|
|
|
) | rpl::map([=](const TitleControls::Layout &layout) {
|
2023-07-19 10:20:14 +00:00
|
|
|
return !TitleControlsOnLeft(layout);
|
2023-03-13 11:58:10 +00:00
|
|
|
}) | rpl::distinct_until_changed();
|
|
|
|
}
|
|
|
|
|
2023-02-24 07:43:02 +00:00
|
|
|
void DefaultOverlayWidgetHelper::beforeShow(bool fullscreen) {
|
|
|
|
_buttons->clearState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::clearState() {
|
|
|
|
_buttons->clearState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultOverlayWidgetHelper::setControlsOpacity(float64 opacity) {
|
|
|
|
_buttons->setMasterOpacity(opacity);
|
|
|
|
}
|
|
|
|
|
2023-03-09 10:13:12 +00:00
|
|
|
auto DefaultOverlayWidgetHelper::mouseEvents() const
|
|
|
|
-> rpl::producer<not_null<QMouseEvent*>> {
|
|
|
|
return _controls->wrap.events(
|
|
|
|
) | rpl::filter([](not_null<QEvent*> e) {
|
|
|
|
const auto type = e->type();
|
|
|
|
return (type == QEvent::MouseButtonPress)
|
|
|
|
|| (type == QEvent::MouseButtonRelease)
|
|
|
|
|| (type == QEvent::MouseMove)
|
|
|
|
|| (type == QEvent::MouseButtonDblClick);
|
|
|
|
}) | rpl::map([](not_null<QEvent*> e) {
|
|
|
|
return not_null{ static_cast<QMouseEvent*>(e.get()) };
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-14 13:27:52 +00:00
|
|
|
} // namespace Platform
|