mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support enlarge / minimize of video.
This commit is contained in:
parent
64c34b7029
commit
2a5977e97f
7 changed files with 158 additions and 9 deletions
|
@ -1139,7 +1139,7 @@ groupCallNarrowNameTop: 65px;
|
||||||
groupCallNarrowIconTop: 62px;
|
groupCallNarrowIconTop: 62px;
|
||||||
groupCallNarrowIconLess: 5px;
|
groupCallNarrowIconLess: 5px;
|
||||||
groupCallWideModeWidthMin: 550px;
|
groupCallWideModeWidthMin: 550px;
|
||||||
groupCallWideModeSize: size(720px, 480px);
|
groupCallWideModeSize: size(960px, 580px);
|
||||||
groupCallNarrowAddMemberHeight: 32px;
|
groupCallNarrowAddMemberHeight: 32px;
|
||||||
groupCallNarrowOutline: 2px;
|
groupCallNarrowOutline: 2px;
|
||||||
groupCallNarrowShadowHeight: 36px;
|
groupCallNarrowShadowHeight: 36px;
|
||||||
|
@ -1184,6 +1184,7 @@ GroupCallLargeVideo {
|
||||||
statusPosition: point;
|
statusPosition: point;
|
||||||
pinPosition: point;
|
pinPosition: point;
|
||||||
iconPosition: point;
|
iconPosition: point;
|
||||||
|
minimizePosition: point;
|
||||||
}
|
}
|
||||||
|
|
||||||
groupCallLargeVideoWide: GroupCallLargeVideo {
|
groupCallLargeVideoWide: GroupCallLargeVideo {
|
||||||
|
@ -1193,6 +1194,7 @@ groupCallLargeVideoWide: GroupCallLargeVideo {
|
||||||
statusPosition: point(15px, 28px);
|
statusPosition: point(15px, 28px);
|
||||||
pinPosition: point(52px, 16px);
|
pinPosition: point(52px, 16px);
|
||||||
iconPosition: point(14px, 16px);
|
iconPosition: point(14px, 16px);
|
||||||
|
minimizePosition: point(94px, 16px);
|
||||||
}
|
}
|
||||||
groupCallLargeVideoNarrow: GroupCallLargeVideo {
|
groupCallLargeVideoNarrow: GroupCallLargeVideo {
|
||||||
shadowHeight: 80px;
|
shadowHeight: 80px;
|
||||||
|
@ -1216,3 +1218,5 @@ groupCallLargeVideoPin: CrossLineAnimation {
|
||||||
endPosition: point(20px, 17px);
|
endPosition: point(20px, 17px);
|
||||||
stroke: 2px;
|
stroke: 2px;
|
||||||
}
|
}
|
||||||
|
groupCallVideoEnlarge: icon {{ "calls/voice_enlarge", mediaviewPipControlsFgOver }};
|
||||||
|
groupCallVideoMinimize: icon {{ "calls/voice_minimize", groupCallVideoSubTextFg }};
|
||||||
|
|
|
@ -30,6 +30,9 @@ LargeVideo::LargeVideo(
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _pin(st::groupCallLargeVideoPin)
|
, _pin(st::groupCallLargeVideoPin)
|
||||||
, _pinButton(&_content)
|
, _pinButton(&_content)
|
||||||
|
, _minimizeButton((_st.controlsAlign == style::al_top)
|
||||||
|
? std::make_unique<Ui::AbstractButton>(&_content)
|
||||||
|
: nullptr)
|
||||||
, _controlsShown(_st.controlsAlign == style::al_top)
|
, _controlsShown(_st.controlsAlign == style::al_top)
|
||||||
, _topControls(_st.controlsAlign == style::al_top)
|
, _topControls(_st.controlsAlign == style::al_top)
|
||||||
, _controlsShownRatio(_controlsShown.current() ? 1. : 0.) {
|
, _controlsShownRatio(_controlsShown.current() ? 1. : 0.) {
|
||||||
|
@ -64,6 +67,12 @@ rpl::producer<bool> LargeVideo::pinToggled() const {
|
||||||
return _pinButton.clicks() | rpl::map([=] { return !_pinned; });
|
return _pinButton.clicks() | rpl::map([=] { return !_pinned; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> LargeVideo::minimizeClicks() const {
|
||||||
|
return _minimizeButton
|
||||||
|
? (_minimizeButton->clicks() | rpl::to_empty)
|
||||||
|
: (rpl::never<rpl::empty_value>() | rpl::type_erased());
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<float64> LargeVideo::controlsShown() const {
|
rpl::producer<float64> LargeVideo::controlsShown() const {
|
||||||
return _controlsShownRatio.value();
|
return _controlsShownRatio.value();
|
||||||
}
|
}
|
||||||
|
@ -86,6 +95,18 @@ void LargeVideo::setup(
|
||||||
Ui::Integration::Instance().unregisterLeaveSubscription(
|
Ui::Integration::Instance().unregisterLeaveSubscription(
|
||||||
&_content);
|
&_content);
|
||||||
setControlsShown(false);
|
setControlsShown(false);
|
||||||
|
} else if (e->type() == QEvent::MouseButtonPress
|
||||||
|
&& static_cast<QMouseEvent*>(
|
||||||
|
e.get())->button() == Qt::LeftButton) {
|
||||||
|
_mouseDown = true;
|
||||||
|
} else if (e->type() == QEvent::MouseButtonRelease
|
||||||
|
&& static_cast<QMouseEvent*>(
|
||||||
|
e.get())->button() == Qt::LeftButton
|
||||||
|
&& _mouseDown) {
|
||||||
|
_mouseDown = false;
|
||||||
|
if (!_content.isHidden()) {
|
||||||
|
_clicks.fire({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, _content.lifetime());
|
}, _content.lifetime());
|
||||||
|
|
||||||
|
@ -118,6 +139,9 @@ void LargeVideo::setup(
|
||||||
}
|
}
|
||||||
_content.update();
|
_content.update();
|
||||||
}, _trackLifetime);
|
}, _trackLifetime);
|
||||||
|
if (const auto size = track.track->frameSize(); !size.isEmpty()) {
|
||||||
|
_trackSize = size;
|
||||||
|
}
|
||||||
}, _content.lifetime());
|
}, _content.lifetime());
|
||||||
|
|
||||||
setupControls(std::move(pinned));
|
setupControls(std::move(pinned));
|
||||||
|
@ -147,7 +171,7 @@ void LargeVideo::toggleControls() {
|
||||||
callback,
|
callback,
|
||||||
shown ? 0. : 1.,
|
shown ? 0. : 1.,
|
||||||
shown ? 1. : 0.,
|
shown ? 1. : 0.,
|
||||||
140);
|
st::slideWrapDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,18 +190,26 @@ void LargeVideo::setupControls(rpl::producer<bool> pinned) {
|
||||||
void LargeVideo::updateControlsGeometry() {
|
void LargeVideo::updateControlsGeometry() {
|
||||||
if (_topControls) {
|
if (_topControls) {
|
||||||
const auto &pin = st::groupCallLargeVideoPin.icon;
|
const auto &pin = st::groupCallLargeVideoPin.icon;
|
||||||
const auto pinWidth = pin.width();
|
|
||||||
const auto pinRight = (_content.width() - _st.pinPosition.x());
|
const auto pinRight = (_content.width() - _st.pinPosition.x());
|
||||||
|
const auto pinLeft = pinRight - pin.width();
|
||||||
const auto pinTop = _st.pinPosition.y();
|
const auto pinTop = _st.pinPosition.y();
|
||||||
const auto &icon = st::groupCallLargeVideoCrossLine.icon;
|
const auto &icon = st::groupCallLargeVideoCrossLine.icon;
|
||||||
const auto iconLeft = _content.width()
|
const auto iconLeft = _content.width()
|
||||||
- _st.iconPosition.x()
|
- _st.iconPosition.x()
|
||||||
- icon.width();
|
- icon.width();
|
||||||
const auto skip = iconLeft - pinRight;
|
const auto skip1 = iconLeft - pinRight;
|
||||||
|
const auto &min = st::groupCallVideoMinimize;
|
||||||
|
const auto minRight = _content.width() - _st.minimizePosition.x();
|
||||||
|
const auto skip2 = pinLeft - minRight;
|
||||||
_pinButton.setGeometry(
|
_pinButton.setGeometry(
|
||||||
pinRight - pin.width() - (skip / 2),
|
pinLeft - (skip2 / 2),
|
||||||
0,
|
0,
|
||||||
pin.width() + skip,
|
pin.width() + (skip2 / 2) + (skip1 / 2),
|
||||||
|
pinTop * 2 + pin.height());
|
||||||
|
_minimizeButton->setGeometry(
|
||||||
|
minRight - min.width() - (skip2 / 2),
|
||||||
|
0,
|
||||||
|
min.width() + skip2,
|
||||||
pinTop * 2 + pin.height());
|
pinTop * 2 + pin.height());
|
||||||
} else {
|
} else {
|
||||||
_pinButton.setGeometry(
|
_pinButton.setGeometry(
|
||||||
|
@ -271,11 +303,11 @@ void LargeVideo::paintControls(Painter &p, QRect clip) {
|
||||||
0,
|
0,
|
||||||
(_topControls
|
(_topControls
|
||||||
? anim::interpolate(-_st.shadowHeight, 0, shown)
|
? anim::interpolate(-_st.shadowHeight, 0, shown)
|
||||||
: (height - _st.shadowHeight)),
|
: (height - anim::interpolate(_st.shadowHeight, 0, shown))),
|
||||||
width,
|
width,
|
||||||
_st.shadowHeight);
|
_st.shadowHeight);
|
||||||
const auto shadowFill = shadowRect.intersected(clip);
|
const auto shadowFill = shadowRect.intersected(clip);
|
||||||
if (shadowFill.isEmpty()) {
|
if (shadowFill.isEmpty() && (_topControls || shown == 0.)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto factor = style::DevicePixelRatio();
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
@ -287,6 +319,16 @@ void LargeVideo::paintControls(Painter &p, QRect clip) {
|
||||||
(shadowFill.y() - shadowRect.y()) * factor,
|
(shadowFill.y() - shadowRect.y()) * factor,
|
||||||
_shadow.width(),
|
_shadow.width(),
|
||||||
shadowFill.height() * factor));
|
shadowFill.height() * factor));
|
||||||
|
if (!_topControls && shown > 0.) {
|
||||||
|
auto color = st::radialBg->c;
|
||||||
|
color.setAlphaF(color.alphaF() * shown);
|
||||||
|
p.fillRect(clip, color);
|
||||||
|
|
||||||
|
p.setOpacity(shown);
|
||||||
|
st::groupCallVideoEnlarge.paintInCenter(p, _content.rect());
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
|
||||||
_track.row->lazyInitialize(st::groupCallMembersListItem);
|
_track.row->lazyInitialize(st::groupCallMembersListItem);
|
||||||
|
|
||||||
// Name.
|
// Name.
|
||||||
|
@ -338,6 +380,14 @@ void LargeVideo::paintControls(Painter &p, QRect clip) {
|
||||||
? (_st.pinPosition.y() + shift)
|
? (_st.pinPosition.y() + shift)
|
||||||
: (height - _st.pinPosition.y() - pin.height());
|
: (height - _st.pinPosition.y() - pin.height());
|
||||||
_pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.);
|
_pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.);
|
||||||
|
|
||||||
|
// Minimize.
|
||||||
|
if (_topControls) {
|
||||||
|
const auto &min = st::groupCallVideoMinimize;
|
||||||
|
const auto minLeft = width - _st.minimizePosition.x() - min.width();
|
||||||
|
const auto minTop = _st.minimizePosition.y() + shift;
|
||||||
|
min.paint(p, minLeft, minTop, width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage GenerateShadow(int height, int topAlpha, int bottomAlpha) {
|
QImage GenerateShadow(int height, int topAlpha, int bottomAlpha) {
|
||||||
|
|
|
@ -64,8 +64,12 @@ public:
|
||||||
void setControlsShown(bool shown);
|
void setControlsShown(bool shown);
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<bool> pinToggled() const;
|
[[nodiscard]] rpl::producer<bool> pinToggled() const;
|
||||||
|
[[nodiscard]] rpl::producer<> minimizeClicks() const;
|
||||||
[[nodiscard]] rpl::producer<float64> controlsShown() const;
|
[[nodiscard]] rpl::producer<float64> controlsShown() const;
|
||||||
[[nodiscard]] rpl::producer<QSize> trackSizeValue() const;
|
[[nodiscard]] rpl::producer<QSize> trackSizeValue() const;
|
||||||
|
[[nodiscard]] rpl::producer<> clicks() const {
|
||||||
|
return _clicks.events();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _content.lifetime();
|
return _content.lifetime();
|
||||||
|
@ -109,11 +113,14 @@ private:
|
||||||
QImage _shadow;
|
QImage _shadow;
|
||||||
Ui::CrossLineAnimation _pin;
|
Ui::CrossLineAnimation _pin;
|
||||||
Ui::AbstractButton _pinButton;
|
Ui::AbstractButton _pinButton;
|
||||||
|
std::unique_ptr<Ui::AbstractButton> _minimizeButton;
|
||||||
Ui::Animations::Simple _controlsAnimation;
|
Ui::Animations::Simple _controlsAnimation;
|
||||||
rpl::variable<bool> _controlsShown = true;
|
rpl::variable<bool> _controlsShown = true;
|
||||||
|
rpl::event_stream<> _clicks;
|
||||||
const bool _topControls = true;
|
const bool _topControls = true;
|
||||||
bool _pinned = false;
|
bool _pinned = false;
|
||||||
bool _mouseInside = false;
|
bool _mouseInside = false;
|
||||||
|
bool _mouseDown = false;
|
||||||
bool _toggleControlsScheduled = false;
|
bool _toggleControlsScheduled = false;
|
||||||
rpl::variable<float64> _controlsShownRatio = 1.;
|
rpl::variable<float64> _controlsShownRatio = 1.;
|
||||||
rpl::variable<QSize> _trackSize;
|
rpl::variable<QSize> _trackSize;
|
||||||
|
|
|
@ -1605,6 +1605,10 @@ void Members::setupAddMember(not_null<GroupCall*> call) {
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<> Members::enlargeVideo() const {
|
||||||
|
return _pinnedVideo->clicks();
|
||||||
|
}
|
||||||
|
|
||||||
Row *Members::lookupRow(not_null<PeerData*> peer) const {
|
Row *Members::lookupRow(not_null<PeerData*> peer) const {
|
||||||
return _listController->findRow(peer);
|
return _listController->findRow(peer);
|
||||||
}
|
}
|
||||||
|
@ -1681,8 +1685,8 @@ void Members::setupPinnedVideo() {
|
||||||
QSize(width, heightMax),
|
QSize(width, heightMax),
|
||||||
Qt::KeepAspectRatio);
|
Qt::KeepAspectRatio);
|
||||||
const auto height = std::max(scaled.height(), heightMin);
|
const auto height = std::max(scaled.height(), heightMin);
|
||||||
_pinnedVideoWrap->resize(width, height);
|
|
||||||
_pinnedVideo->setGeometry(0, 0, width, height);
|
_pinnedVideo->setGeometry(0, 0, width, height);
|
||||||
|
_pinnedVideoWrap->resize(width, height);
|
||||||
}, _pinnedVideo->lifetime());
|
}, _pinnedVideo->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> addMembersRequests() const {
|
[[nodiscard]] rpl::producer<> addMembersRequests() const {
|
||||||
return _addMemberRequests.events();
|
return _addMemberRequests.events();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] rpl::producer<> enlargeVideo() const;
|
||||||
|
|
||||||
[[nodiscard]] MembersRow *lookupRow(not_null<PeerData*> peer) const;
|
[[nodiscard]] MembersRow *lookupRow(not_null<PeerData*> peer) const;
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtWidgets/QDesktopWidget>
|
#include <QtWidgets/QDesktopWidget>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtGui/QWindow>
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QScreen>
|
||||||
|
|
||||||
namespace Calls::Group {
|
namespace Calls::Group {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -970,6 +971,79 @@ void Panel::setupMembers() {
|
||||||
addMembers();
|
addMembers();
|
||||||
}
|
}
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
|
|
||||||
|
_members->enlargeVideo(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
enlargeVideo();
|
||||||
|
}, _callLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::enlargeVideo() {
|
||||||
|
_lastSmallGeometry = _window->geometry();
|
||||||
|
|
||||||
|
const auto available = _window->screen()->availableGeometry();
|
||||||
|
const auto width = std::max(
|
||||||
|
_window->width(),
|
||||||
|
std::max(
|
||||||
|
std::min(available.width(), st::groupCallWideModeSize.width()),
|
||||||
|
st::groupCallWideModeWidthMin));
|
||||||
|
const auto height = std::max(
|
||||||
|
_window->height(),
|
||||||
|
std::min(available.height(), st::groupCallWideModeSize.height()));
|
||||||
|
auto geometry = QRect(
|
||||||
|
_window->x() - (width - _window->width()) / 2,
|
||||||
|
_window->y() - (height - _window->height()) / 2,
|
||||||
|
width,
|
||||||
|
height);
|
||||||
|
if (geometry.x() < available.x()) {
|
||||||
|
geometry.setX(std::min(available.x(), _window->x()));
|
||||||
|
}
|
||||||
|
if (geometry.x() + geometry.width()
|
||||||
|
> available.x() + available.width()) {
|
||||||
|
geometry.setX(std::max(
|
||||||
|
available.x() + available.width(),
|
||||||
|
_window->x() + _window->width()) - geometry.width());
|
||||||
|
}
|
||||||
|
if (geometry.y() < available.y()) {
|
||||||
|
geometry.setY(std::min(available.y(), _window->y()));
|
||||||
|
}
|
||||||
|
if (geometry.y() + geometry.height() > available.y() + available.height()) {
|
||||||
|
geometry.setY(std::max(
|
||||||
|
available.y() + available.height(),
|
||||||
|
_window->y() + _window->height()) - geometry.height());
|
||||||
|
}
|
||||||
|
if (_lastLargeMaximized) {
|
||||||
|
_window->setWindowState(
|
||||||
|
_window->windowState() | Qt::WindowMaximized);
|
||||||
|
} else {
|
||||||
|
_window->setGeometry((_lastLargeGeometry
|
||||||
|
&& available.intersects(*_lastLargeGeometry))
|
||||||
|
? *_lastLargeGeometry
|
||||||
|
: geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::minimizeVideo() {
|
||||||
|
if (_window->windowState() & Qt::WindowMaximized) {
|
||||||
|
_lastLargeMaximized = true;
|
||||||
|
_window->setWindowState(
|
||||||
|
_window->windowState() & ~Qt::WindowMaximized);
|
||||||
|
} else {
|
||||||
|
_lastLargeMaximized = false;
|
||||||
|
_lastLargeGeometry = _window->geometry();
|
||||||
|
}
|
||||||
|
const auto available = _window->screen()->availableGeometry();
|
||||||
|
const auto width = st::groupCallWidth;
|
||||||
|
const auto height = st::groupCallHeight;
|
||||||
|
auto geometry = QRect(
|
||||||
|
_window->x() + (_window->width() - width) / 2,
|
||||||
|
_window->y() + (_window->height() - height) / 2,
|
||||||
|
width,
|
||||||
|
height);
|
||||||
|
_window->setGeometry((_lastSmallGeometry
|
||||||
|
&& available.intersects(*_lastSmallGeometry))
|
||||||
|
? *_lastSmallGeometry
|
||||||
|
: geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::raiseControls() {
|
void Panel::raiseControls() {
|
||||||
|
@ -1008,6 +1082,10 @@ void Panel::setupPinnedVideo() {
|
||||||
visible,
|
visible,
|
||||||
std::move(track),
|
std::move(track),
|
||||||
_call->videoEndpointPinnedValue());
|
_call->videoEndpointPinnedValue());
|
||||||
|
_pinnedVideo->minimizeClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
minimizeVideo();
|
||||||
|
}, _pinnedVideo->lifetime());
|
||||||
_pinnedVideo->pinToggled(
|
_pinnedVideo->pinToggled(
|
||||||
) | rpl::start_with_next([=](bool pinned) {
|
) | rpl::start_with_next([=](bool pinned) {
|
||||||
if (!pinned) {
|
if (!pinned) {
|
||||||
|
|
|
@ -93,6 +93,8 @@ private:
|
||||||
void startScheduledNow();
|
void startScheduledNow();
|
||||||
void trackControls();
|
void trackControls();
|
||||||
void raiseControls();
|
void raiseControls();
|
||||||
|
void enlargeVideo();
|
||||||
|
void minimizeVideo();
|
||||||
|
|
||||||
bool updateMode();
|
bool updateMode();
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
|
@ -153,6 +155,9 @@ private:
|
||||||
object_ptr<Ui::FlatLabel> _startsWhen = { nullptr };
|
object_ptr<Ui::FlatLabel> _startsWhen = { nullptr };
|
||||||
ChooseJoinAsProcess _joinAsProcess;
|
ChooseJoinAsProcess _joinAsProcess;
|
||||||
rpl::variable<bool> _videoMode;
|
rpl::variable<bool> _videoMode;
|
||||||
|
std::optional<QRect> _lastSmallGeometry;
|
||||||
|
std::optional<QRect> _lastLargeGeometry;
|
||||||
|
bool _lastLargeMaximized = false;
|
||||||
|
|
||||||
object_ptr<Ui::RpWidget> _controlsBackground = { nullptr };
|
object_ptr<Ui::RpWidget> _controlsBackground = { nullptr };
|
||||||
object_ptr<Ui::CallButton> _settings = { nullptr };
|
object_ptr<Ui::CallButton> _settings = { nullptr };
|
||||||
|
|
Loading…
Add table
Reference in a new issue