Implement video pin / controls hiding in video tiles.

This commit is contained in:
John Preston 2021-05-13 14:38:59 +04:00
parent 7f739065e8
commit 316f0537c4
6 changed files with 172 additions and 193 deletions

View file

@ -831,7 +831,7 @@ groupCallButtonSkip: 43px;
groupCallButtonSkipSmall: 4px; groupCallButtonSkipSmall: 4px;
groupCallButtonBottomSkip: 145px; groupCallButtonBottomSkip: 145px;
groupCallButtonBottomSkipSmall: 95px; groupCallButtonBottomSkipSmall: 95px;
groupCallButtonBottomSkipWide: 89px; groupCallButtonBottomSkipWide: 122px;
groupCallMembersBottomSkipSmall: 72px; groupCallMembersBottomSkipSmall: 72px;
groupCallControlsBackMargin: margins(2px, 2px, 2px, 2px); groupCallControlsBackMargin: margins(2px, 2px, 2px, 2px);
groupCallControlsBackRadius: 12px; groupCallControlsBackRadius: 12px;
@ -1179,30 +1179,22 @@ groupCallLargeVideoCrossLine: CrossLineAnimation(groupCallMemberColoredCrossLine
GroupCallLargeVideo { GroupCallLargeVideo {
shadowHeight: pixels; shadowHeight: pixels;
controlsAlign: align; enlargeAlign: align;
namePosition: point; namePosition: point;
statusPosition: point;
pinPosition: point; pinPosition: point;
iconPosition: point; iconPosition: point;
minimizePosition: point;
} }
groupCallLargeVideoWide: GroupCallLargeVideo { groupCallLargeVideoWide: GroupCallLargeVideo {
shadowHeight: 100px; shadowHeight: 40px;
controlsAlign: align(top); enlargeAlign: align(topright);
namePosition: point(15px, 8px); namePosition: point(15px, 8px);
statusPosition: point(15px, 28px); pinPosition: point(18px, 18px);
pinPosition: point(52px, 16px); iconPosition: point(10px, 5px);
iconPosition: point(14px, 16px);
minimizePosition: point(94px, 16px);
} }
groupCallLargeVideoNarrow: GroupCallLargeVideo { groupCallLargeVideoNarrow: GroupCallLargeVideo(groupCallLargeVideoWide) {
shadowHeight: 80px; enlargeAlign: align(center);
controlsAlign: align(bottom); pinPosition: point(-1px, -1px);
namePosition: point(64px, 44px);
statusPosition: point(64px, 25px);
pinPosition: point(20px, 12px);
iconPosition: point(18px, 12px);
} }
groupCallLargeVideoListItem: PeerListItem(groupCallMembersListItem) { groupCallLargeVideoListItem: PeerListItem(groupCallMembersListItem) {
nameFg: groupCallVideoTextFg; nameFg: groupCallVideoTextFg;
@ -1219,7 +1211,6 @@ groupCallLargeVideoPin: CrossLineAnimation {
stroke: 2px; stroke: 2px;
} }
groupCallVideoEnlarge: icon {{ "calls/voice_enlarge", mediaviewPipControlsFgOver }}; groupCallVideoEnlarge: icon {{ "calls/voice_enlarge", mediaviewPipControlsFgOver }};
groupCallVideoMinimize: icon {{ "calls/voice_minimize", groupCallVideoSubTextFg }};
groupCallVideoSmallSkip: 4px; groupCallVideoSmallSkip: 4px;
groupCallVideoLargeSkip: 6px; groupCallVideoLargeSkip: 6px;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/view/media_view_pip.h" #include "media/view/media_view_pip.h"
#include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_video_track.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/abstract_button.h"
#include "styles/style_calls.h" #include "styles/style_calls.h"
namespace Calls::Group { namespace Calls::Group {
@ -30,12 +31,11 @@ LargeVideo::LargeVideo(
: _content(parent, [=](QRect clip) { paint(clip); }) : _content(parent, [=](QRect clip) { paint(clip); })
, _st(st) , _st(st)
, _pin(st::groupCallLargeVideoPin) , _pin(st::groupCallLargeVideoPin)
, _pinButton(&_content) , _pinButton((_st.pinPosition.x() >= 0)
, _minimizeButton((_st.controlsAlign == style::al_top)
? std::make_unique<Ui::AbstractButton>(&_content) ? std::make_unique<Ui::AbstractButton>(&_content)
: nullptr) : nullptr)
, _controlsShown(_st.controlsAlign == style::al_top) , _controlsShown(_st.enlargeAlign != style::al_center)
, _topControls(_st.controlsAlign == style::al_top) , _hasEnlarge(_st.enlargeAlign == style::al_center)
, _controlsShownRatio(_controlsShown.current() ? 1. : 0.) { , _controlsShownRatio(_controlsShown.current() ? 1. : 0.) {
_content.setVisible(visible); _content.setVisible(visible);
setup(std::move(track), std::move(pinned)); setup(std::move(track), std::move(pinned));
@ -74,13 +74,9 @@ void LargeVideo::setControlsShown(bool shown) {
} }
rpl::producer<bool> LargeVideo::pinToggled() const { rpl::producer<bool> LargeVideo::pinToggled() const {
return _pinButton.clicks() | rpl::map([=] { return !_pinned; }); return _pinButton
} ? _pinButton->clicks() | rpl::map([=] { return !_pinned; })
: rpl::never<bool>() | rpl::type_erased();
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 {
@ -140,7 +136,7 @@ void LargeVideo::setup(
) | rpl::map([=](bool shown, LargeVideoTrack track) { ) | rpl::map([=](bool shown, LargeVideoTrack track) {
if (!shown) { if (!shown) {
_controlsAnimation.stop(); _controlsAnimation.stop();
if (!_topControls) { if (_hasEnlarge) {
_controlsShown = _mouseInside = false; _controlsShown = _mouseInside = false;
} }
_controlsShownRatio = _controlsShown.current() ? 1. : 0.; _controlsShownRatio = _controlsShown.current() ? 1. : 0.;
@ -174,9 +170,18 @@ void LargeVideo::setup(
setupControls(std::move(pinned)); setupControls(std::move(pinned));
} }
void LargeVideo::toggleControlsHidingEnabled(bool enabled) {
if (_controlsHidingEnabled == enabled) {
return;
}
_controlsHidingEnabled = enabled;
toggleControls();
}
void LargeVideo::toggleControls() { void LargeVideo::toggleControls() {
_toggleControlsScheduled = false; _toggleControlsScheduled = false;
const auto shown = _mouseInside; const auto shown = _mouseInside
|| (!_hasEnlarge && !_controlsHidingEnabled);
if (_controlsShown.current() == shown) { if (_controlsShown.current() == shown) {
return; return;
} }
@ -184,11 +189,7 @@ void LargeVideo::toggleControls() {
const auto callback = [=] { const auto callback = [=] {
_controlsShownRatio = _controlsAnimation.value( _controlsShownRatio = _controlsAnimation.value(
_controlsShown.current() ? 1. : 0.); _controlsShown.current() ? 1. : 0.);
if (_topControls) { _content.update();
_content.update(0, 0, _content.width(), _st.shadowHeight);
} else {
_content.update();
}
updateControlsGeometry(); updateControlsGeometry();
}; };
if (_content.isHidden()) { if (_content.isHidden()) {
@ -215,45 +216,30 @@ void LargeVideo::setupControls(rpl::producer<bool> pinned) {
} }
void LargeVideo::updateControlsGeometry() { void LargeVideo::updateControlsGeometry() {
if (_topControls) { if (_pinButton) {
const auto &pin = st::groupCallLargeVideoPin.icon; const auto &pin = st::groupCallLargeVideoPin.icon;
const auto pinRight = (_content.width() - _st.pinPosition.x()); const auto buttonWidth = pin.width() + 2 * _st.pinPosition.x();
const auto pinLeft = pinRight - pin.width(); const auto buttonHeight = pin.height() + 2 * _st.pinPosition.y();
const auto pinTop = _st.pinPosition.y(); _pinButton->setGeometry(
const auto &icon = st::groupCallLargeVideoCrossLine.icon; _content.width() - buttonWidth,
const auto iconLeft = _content.width()
- _st.iconPosition.x()
- icon.width();
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(
pinLeft - (skip2 / 2),
0, 0,
pin.width() + (skip2 / 2) + (skip1 / 2), buttonWidth,
pinTop * 2 + pin.height()); buttonHeight);
_minimizeButton->setGeometry(
minRight - min.width() - (skip2 / 2),
0,
min.width() + skip2,
pinTop * 2 + pin.height());
} else {
_pinButton.setGeometry(
0,
_content.height() - _st.namePosition.y(),
_st.namePosition.x(),
_st.namePosition.y());
} }
} }
void LargeVideo::paint(QRect clip) { void LargeVideo::paint(QRect clip) {
auto p = Painter(&_content); auto p = Painter(&_content);
const auto fill = [&](QRect rect) {
if (rect.intersects(clip)) {
p.fillRect(rect.intersected(clip), st::groupCallMembersBg);
}
};
const auto [image, rotation] = _track const auto [image, rotation] = _track
? _track.track->frameOriginalWithRotation() ? _track.track->frameOriginalWithRotation()
: std::pair<QImage, int>(); : std::pair<QImage, int>();
if (image.isNull()) { if (image.isNull()) {
p.fillRect(clip, Qt::black); fill(clip);
return; return;
} }
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
@ -282,11 +268,6 @@ void LargeVideo::paint(QRect clip) {
} }
_track.track->markFrameShown(); _track.track->markFrameShown();
const auto fill = [&](QRect rect) {
if (rect.intersects(clip)) {
p.fillRect(rect.intersected(clip), Qt::black);
}
};
if (left > 0) { if (left > 0) {
fill({ 0, 0, left, size.height() }); fill({ 0, 0, left, size.height() });
} }
@ -306,35 +287,29 @@ void LargeVideo::paint(QRect clip) {
} }
void LargeVideo::paintControls(Painter &p, QRect clip) { void LargeVideo::paintControls(Painter &p, QRect clip) {
const auto shown = _controlsShownRatio.current(); const auto ratio = _controlsShownRatio.current();
if (shown == 0. && _topControls) { const auto shown = _hasEnlarge ? 1. : ratio;
const auto enlarge = _hasEnlarge ? ratio : 0.;
if (shown == 0.) {
return; return;
} }
const auto width = _content.width(); const auto width = _content.width();
const auto height = _content.height(); const auto height = _content.height();
const auto fullShift = _st.statusPosition.y() + st::normalFont->height; const auto fullShift = _st.namePosition.y() + st::normalFont->height;
const auto shift = _topControls const auto shift = anim::interpolate(fullShift, 0, shown);
? anim::interpolate(-fullShift, 0, shown)
: 0;
// Shadow. // Shadow.
if (_shadow.isNull()) { if (_shadow.isNull()) {
if (_topControls) { _shadow = GenerateShadow(_st.shadowHeight, 0, kShadowMaxAlpha);
_shadow = GenerateShadow(_st.shadowHeight, kShadowMaxAlpha, 0);
} else {
_shadow = GenerateShadow(_st.shadowHeight, 0, kShadowMaxAlpha);
}
} }
const auto shadowRect = QRect( const auto shadowRect = QRect(
0, 0,
(_topControls (height - anim::interpolate(0, _st.shadowHeight, shown)),
? anim::interpolate(-_st.shadowHeight, 0, shown)
: (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() && (_topControls || shown == 0.)) { if (shadowFill.isEmpty() && enlarge == 0. && !_pinButton) {
return; return;
} }
const auto factor = style::DevicePixelRatio(); const auto factor = style::DevicePixelRatio();
@ -346,74 +321,52 @@ 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.) { if (enlarge > 0.) {
auto color = st::radialBg->c; auto color = st::radialBg->c;
color.setAlphaF(color.alphaF() * shown); color.setAlphaF(color.alphaF() * enlarge);
p.fillRect(clip, color); p.fillRect(clip, color);
p.setOpacity(shown); p.setOpacity(enlarge);
st::groupCallVideoEnlarge.paintInCenter(p, _content.rect()); st::groupCallVideoEnlarge.paintInCenter(p, _content.rect());
p.setOpacity(1.); p.setOpacity(1.);
} }
_track.row->lazyInitialize(st::groupCallMembersListItem); _track.row->lazyInitialize(st::groupCallMembersListItem);
// Name.
p.setPen(_topControls
? st::groupCallVideoTextFg
: st::groupCallVideoSubTextFg);
const auto hasWidth = width
- (_topControls ? _st.pinPosition.x() : _st.iconPosition.x())
- _st.namePosition.x();
const auto nameLeft = _st.namePosition.x();
const auto nameTop = _topControls
? (_st.namePosition.y() + shift)
: (height - _st.namePosition.y());
_track.row->name().drawLeftElided(p, nameLeft, nameTop, hasWidth, width);
// Status.
p.setPen(st::groupCallVideoSubTextFg);
const auto statusLeft = _st.statusPosition.x();
const auto statusTop = _topControls
? (_st.statusPosition.y() + shift)
: (height - _st.statusPosition.y());
_track.row->paintComplexStatusText(
p,
st::groupCallLargeVideoListItem,
statusLeft,
statusTop,
hasWidth,
width,
false,
MembersRowStyle::LargeVideo);
// Mute. // Mute.
const auto &icon = st::groupCallLargeVideoCrossLine.icon; const auto &icon = st::groupCallLargeVideoCrossLine.icon;
const auto iconLeft = width - _st.iconPosition.x() - icon.width(); const auto iconLeft = width - _st.iconPosition.x() - icon.width();
const auto iconTop = _topControls const auto iconTop = (height
? (_st.iconPosition.y() + shift) - _st.iconPosition.y()
: (height - _st.iconPosition.y() - icon.height()); - icon.height()
+ shift);
_track.row->paintMuteIcon( _track.row->paintMuteIcon(
p, p,
{ iconLeft, iconTop, icon.width(), icon.height() }, { iconLeft, iconTop, icon.width(), icon.height() },
MembersRowStyle::LargeVideo); MembersRowStyle::LargeVideo);
// Pin. // Name.
const auto &pin = st::groupCallLargeVideoPin.icon; p.setPen(st::groupCallVideoTextFg);
const auto pinLeft = _topControls const auto hasWidth = width
? (width - _st.pinPosition.x() - pin.width()) - _st.iconPosition.x() - icon.width()
: _st.pinPosition.x(); - _st.namePosition.x();
const auto pinTop = _topControls const auto nameLeft = _st.namePosition.x();
? (_st.pinPosition.y() + shift) const auto nameTop = (height
: (height - _st.pinPosition.y() - pin.height()); - _st.namePosition.y()
_pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.); - st::semiboldFont->height
+ shift);
_track.row->name().drawLeftElided(p, nameLeft, nameTop, hasWidth, width);
// Minimize. // Pin.
if (_topControls) { if (_st.pinPosition.x() >= 0) {
const auto &min = st::groupCallVideoMinimize; const auto &pin = st::groupCallLargeVideoPin.icon;
const auto minLeft = width - _st.minimizePosition.x() - min.width(); const auto pinLeft = (width - _st.pinPosition.x() - pin.width());
const auto minTop = _st.minimizePosition.y() + shift; const auto pinShift = anim::interpolate(
min.paint(p, minLeft, minTop, width); _st.pinPosition.y() + pin.height(),
0,
shown);
const auto pinTop = (_st.pinPosition.y() - pinShift);
_pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.);
} }
} }

View file

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/abstract_button.h"
#include "ui/effects/cross_line.h" #include "ui/effects/cross_line.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
@ -24,6 +23,10 @@ namespace Webrtc {
class VideoTrack; class VideoTrack;
} // namespace Webrtc } // namespace Webrtc
namespace Ui {
class AbstractButton;
} // namespace Ui
namespace Calls::Group { namespace Calls::Group {
class MembersRow; class MembersRow;
@ -63,9 +66,9 @@ public:
void setVisible(bool visible); void setVisible(bool visible);
void setGeometry(int x, int y, int width, int height); void setGeometry(int x, int y, int width, int height);
void setControlsShown(bool shown); void setControlsShown(bool shown);
void toggleControlsHidingEnabled(bool enabled);
[[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<> clicks() const { [[nodiscard]] rpl::producer<> clicks() const {
return _clicks.events(); return _clicks.events();
@ -116,13 +119,13 @@ private:
LargeVideoTrack _track; LargeVideoTrack _track;
QImage _shadow; QImage _shadow;
Ui::CrossLineAnimation _pin; Ui::CrossLineAnimation _pin;
Ui::AbstractButton _pinButton; std::unique_ptr<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; rpl::event_stream<> _clicks;
const bool _topControls = true; const bool _hasEnlarge = true;
bool _pinned = false; bool _pinned = false;
bool _controlsHidingEnabled = false;
bool _mouseInside = false; bool _mouseInside = false;
bool _mouseDown = false; bool _mouseDown = false;
bool _toggleControlsScheduled = false; bool _toggleControlsScheduled = false;

View file

@ -1686,14 +1686,18 @@ void Members::refreshTilesGeometry() {
_pinnedVideoWrap->resize(width, height); _pinnedVideoWrap->resize(width, height);
return; return;
} }
const auto min = (st::groupCallWidth
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right()
- st::groupCallVideoSmallSkip) / 2;
const auto square = (width - st::groupCallVideoSmallSkip) / 2; const auto square = (width - st::groupCallVideoSmallSkip) / 2;
const auto skip = (width - 2 * square); const auto skip = (width - 2 * square);
const auto put = [&](not_null<LargeVideo*> video, int column, int row) { const auto put = [&](not_null<LargeVideo*> video, int column, int row) {
video->setGeometry( video->setGeometry(
(column == 2) ? 0 : column ? (width - square) : 0, (column == 2) ? 0 : column ? (width - square) : 0,
row * (square + skip), row * (min + skip),
(column == 2) ? width : square, (column == 2) ? width : square,
square); min);
}; };
const auto rows = (sizes.size() + 1) / 2; const auto rows = (sizes.size() + 1) / 2;
if (sizes.size() == 3) { if (sizes.size() == 3) {
@ -1713,7 +1717,7 @@ void Members::refreshTilesGeometry() {
} }
} }
} }
_pinnedVideoWrap->resize(width, rows * (square + skip) - skip); _pinnedVideoWrap->resize(width, rows * (min + skip) - skip);
} }
void Members::setupPinnedVideo() { void Members::setupPinnedVideo() {
@ -1746,6 +1750,9 @@ void Members::setupPinnedVideo() {
refreshTilesGeometry(); refreshTilesGeometry();
}, video->lifetime()); }, video->lifetime());
video->clicks(
) | rpl::start_to_stream(_enlargeVideoClicks, video->lifetime());
return VideoTile{ return VideoTile{
.video = std::move(video), .video = std::move(video),
.endpoint = endpoint, .endpoint = endpoint,

View file

@ -435,6 +435,7 @@ Panel::~Panel() {
if (_menu) { if (_menu) {
_menu.destroy(); _menu.destroy();
} }
_videoTiles.clear();
} }
void Panel::setupRealCallViewers() { void Panel::setupRealCallViewers() {
@ -983,10 +984,12 @@ void Panel::setupMembers() {
}, _callLifetime); }, _callLifetime);
_call->videoEndpointPinnedValue( _call->videoEndpointPinnedValue(
) | rpl::filter([=](const VideoEndpoint &pinned) { ) | rpl::start_with_next([=](const VideoEndpoint &pinned) {
return pinned && (_mode == PanelMode::Default); if (_mode == PanelMode::Wide) {
}) | rpl::start_with_next([=] { refreshTilesGeometry();
enlargeVideo(); } else if (pinned) {
enlargeVideo();
}
}, _callLifetime); }, _callLifetime);
} }
@ -1082,6 +1085,7 @@ void Panel::refreshTilesGeometry() {
if (_videoTiles.empty() if (_videoTiles.empty()
|| outer.isEmpty() || outer.isEmpty()
|| _mode == PanelMode::Default) { || _mode == PanelMode::Default) {
trackControls(nullptr);
return; return;
} }
struct Geometry { struct Geometry {
@ -1089,23 +1093,30 @@ void Panel::refreshTilesGeometry() {
QRect columns; QRect columns;
QRect rows; QRect rows;
}; };
const auto &pinned = _call->videoEndpointPinned();
auto sizes = base::flat_map<not_null<LargeVideo*>, Geometry>(); auto sizes = base::flat_map<not_null<LargeVideo*>, Geometry>();
sizes.reserve(_videoTiles.size()); sizes.reserve(_videoTiles.size());
for (const auto &tile : _videoTiles) { for (const auto &tile : _videoTiles) {
const auto video = tile.video.get(); const auto video = tile.video.get();
const auto size = video->trackSize(); const auto size = (pinned && tile.endpoint != pinned)
? QSize()
: video->trackSize();
if (size.isEmpty()) { if (size.isEmpty()) {
video->toggleControlsHidingEnabled(false);
video->setGeometry(0, 0, outer.width(), 0); video->setGeometry(0, 0, outer.width(), 0);
} else { } else {
sizes.emplace(video, Geometry{ size }); sizes.emplace(video, Geometry{ size });
} }
} }
if (sizes.empty()) { if (sizes.size() == 1) {
return; trackControls(sizes.front().first);
} else if (sizes.size() == 1) { sizes.front().first->toggleControlsHidingEnabled(true);
sizes.front().first->setGeometry(0, 0, outer.width(), outer.height()); sizes.front().first->setGeometry(0, 0, outer.width(), outer.height());
return; return;
} }
if (sizes.empty()) {
return;
}
auto columnsBlack = uint64(); auto columnsBlack = uint64();
auto rowsBlack = uint64(); auto rowsBlack = uint64();
@ -1180,6 +1191,7 @@ void Panel::refreshTilesGeometry() {
const auto &rect = (columnsBlack < rowsBlack) const auto &rect = (columnsBlack < rowsBlack)
? geometry.columns ? geometry.columns
: geometry.rows; : geometry.rows;
video->toggleControlsHidingEnabled(false);
video->setGeometry(rect.x(), rect.y(), rect.width(), rect.height()); video->setGeometry(rect.x(), rect.y(), rect.width(), rect.height());
} }
} }
@ -1195,7 +1207,7 @@ void Panel::setupPinnedVideo() {
Assert(row != nullptr); Assert(row != nullptr);
auto video = std::make_unique<LargeVideo>( auto video = std::make_unique<LargeVideo>(
_pinnedVideoWrap.get(), _pinnedVideoWrap.get(),
st::groupCallLargeVideoNarrow, st::groupCallLargeVideoWide,
(_mode == PanelMode::Wide), (_mode == PanelMode::Wide),
rpl::single(LargeVideoTrack{ track.track.get(), row }), rpl::single(LargeVideoTrack{ track.track.get(), row }),
_call->videoEndpointPinnedValue() | rpl::map(_1 == endpoint)); _call->videoEndpointPinnedValue() | rpl::map(_1 == endpoint));
@ -1210,31 +1222,16 @@ void Panel::setupPinnedVideo() {
_call->requestVideoQuality(endpoint, quality); _call->requestVideoQuality(endpoint, quality);
}, video->lifetime()); }, video->lifetime());
video->minimizeClicks(
) | rpl::start_with_next([=] {
minimizeVideo();
}, video->lifetime());
video->trackSizeValue( video->trackSizeValue(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
refreshTilesGeometry(); refreshTilesGeometry();
}, video->lifetime()); }, video->lifetime());
video->controlsShown( video->lifetime().add([=, video = video.get()] {
) | rpl::filter([=](float64 shown) { if (_trackControlsTile == video) {
return (_pinnedVideoControlsShown != shown); trackControls(nullptr);
}) | rpl::start_with_next([=](float64 shown) {
const auto hiding = (shown <= _pinnedVideoControlsShown);
_pinnedVideoControlsShown = shown;
if (_mode == PanelMode::Wide) {
if (hiding && _trackControlsLifetime) {
_trackControlsLifetime.destroy();
} else if (!hiding && !_trackControlsLifetime) {
trackControls();
}
updateButtonsGeometry();
} }
}, video->lifetime()); });
return VideoTile{ return VideoTile{
.video = std::move(video), .video = std::move(video),
@ -1806,32 +1803,59 @@ void Panel::refreshControlsBackground() {
corners->paint(p, _controlsBackground->rect()); corners->paint(p, _controlsBackground->rect());
}, lifetime); }, lifetime);
if (_pinnedVideoControlsShown > 0.) {
trackControls();
}
raiseControls(); raiseControls();
} }
void Panel::trackControls() { void Panel::trackControls(LargeVideo *video) {
const auto trackOne = [&](auto &&widget) { if (_trackControlsTile == video) {
return;
}
_trackControlsTile = video;
if (!video) {
_trackControlsLifetime.destroy();
_trackControlsOverStateLifetime.destroy();
if (_pinnedVideoControlsShown != 1.) {
_pinnedVideoControlsShown = 1.;
updateButtonsGeometry();
}
return;
}
const auto trackOne = [=](auto &&widget) {
if (widget) { if (widget) {
widget->events( widget->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) { ) | rpl::start_with_next([=](not_null<QEvent*> e) {
//if (e->type() == QEvent::Enter) { if (e->type() == QEvent::Enter) {
// _pinnedVideo->setControlsShown(true); video->setControlsShown(true);
//} else if (e->type() == QEvent::Leave) { } else if (e->type() == QEvent::Leave) {
// _pinnedVideo->setControlsShown(false); video->setControlsShown(false);
//} }
}, _trackControlsLifetime); }, _trackControlsOverStateLifetime);
} }
}; };
trackOne(_mute); const auto trackOverState = [=] {
trackOne(_video); trackOne(_mute);
trackOne(_screenShare); trackOne(_video);
trackOne(_settings); trackOne(_screenShare);
trackOne(_callShare); trackOne(_settings);
trackOne(_hangup); trackOne(_callShare);
trackOne(_controlsBackground); trackOne(_hangup);
trackOne(_controlsBackground);
};
video->controlsShown(
) | rpl::filter([=](float64 shown) {
return (_pinnedVideoControlsShown != shown);
}) | rpl::start_with_next([=](float64 shown) {
const auto hiding = (shown <= _pinnedVideoControlsShown);
_pinnedVideoControlsShown = shown;
if (hiding && _trackControlsLifetime) {
_trackControlsOverStateLifetime.destroy();
} else if (!hiding && !_trackControlsOverStateLifetime) {
trackOverState();
}
updateButtonsGeometry();
}, _trackControlsLifetime);
} }
void Panel::updateControlsGeometry() { void Panel::updateControlsGeometry() {
@ -1884,13 +1908,12 @@ void Panel::updateButtonsGeometry() {
toggle(_mode != PanelMode::Wide || _pinnedVideoControlsShown > 0.); toggle(_mode != PanelMode::Wide || _pinnedVideoControlsShown > 0.);
const auto buttonsTop = widget()->height() const auto buttonsTop = widget()->height()
- st::groupCallButtonBottomSkipSmall - (_mode == PanelMode::Wide
+ (_mode == PanelMode::Wide
? anim::interpolate( ? anim::interpolate(
st::groupCallButtonBottomSkipSmall,
0, 0,
st::groupCallButtonBottomSkipWide,
_pinnedVideoControlsShown) _pinnedVideoControlsShown)
: 0); : st::groupCallButtonBottomSkipSmall);
const auto addSkip = st::callMuteButtonSmall.active.outerRadius; const auto addSkip = st::callMuteButtonSmall.active.outerRadius;
const auto muteSize = _mute->innerSize().width() + 2 * addSkip; const auto muteSize = _mute->innerSize().width() + 2 * addSkip;
const auto skip = (_video ? 1 : 2) * st::groupCallButtonSkipSmall; const auto skip = (_video ? 1 : 2) * st::groupCallButtonSkipSmall;

View file

@ -92,7 +92,7 @@ private:
bool handleClose(); bool handleClose();
void startScheduledNow(); void startScheduledNow();
void trackControls(); void trackControls(LargeVideo *video);
void raiseControls(); void raiseControls();
void enlargeVideo(); void enlargeVideo();
void minimizeVideo(); void minimizeVideo();
@ -151,7 +151,9 @@ private:
std::unique_ptr<Ui::RpWidget> _pinnedVideoWrap; std::unique_ptr<Ui::RpWidget> _pinnedVideoWrap;
float64 _pinnedVideoControlsShown = 1.; float64 _pinnedVideoControlsShown = 1.;
std::vector<VideoTile> _videoTiles; std::vector<VideoTile> _videoTiles;
LargeVideo *_trackControlsTile = nullptr;
rpl::lifetime _trackControlsLifetime; rpl::lifetime _trackControlsLifetime;
rpl::lifetime _trackControlsOverStateLifetime;
object_ptr<Ui::FlatLabel> _startsIn = { nullptr }; object_ptr<Ui::FlatLabel> _startsIn = { nullptr };
object_ptr<Ui::RpWidget> _countdown = { nullptr }; object_ptr<Ui::RpWidget> _countdown = { nullptr };
std::shared_ptr<Ui::GroupCallScheduledLeft> _countdownData; std::shared_ptr<Ui::GroupCallScheduledLeft> _countdownData;