Improve pin video button design and controls hiding.

This commit is contained in:
John Preston 2021-05-18 17:07:19 +04:00
parent c48c4d4283
commit 445c798bbc
7 changed files with 160 additions and 54 deletions

View file

@ -1192,6 +1192,8 @@ GroupCallLargeVideo {
shadowHeight: pixels; shadowHeight: pixels;
namePosition: point; namePosition: point;
pinPosition: point; pinPosition: point;
pinPadding: margins;
pinTextPosition: point;
iconPosition: point; iconPosition: point;
} }
@ -1199,6 +1201,8 @@ groupCallLargeVideoWide: GroupCallLargeVideo {
shadowHeight: 40px; shadowHeight: 40px;
namePosition: point(15px, 8px); namePosition: point(15px, 8px);
pinPosition: point(18px, 18px); pinPosition: point(18px, 18px);
pinPadding: margins(6px, 2px, 12px, 1px);
pinTextPosition: point(1px, 3px);
iconPosition: point(10px, 5px); iconPosition: point(10px, 5px);
} }
groupCallLargeVideoNarrow: GroupCallLargeVideo(groupCallLargeVideoWide) { groupCallLargeVideoNarrow: GroupCallLargeVideo(groupCallLargeVideoWide) {
@ -1214,9 +1218,10 @@ groupCallLargeVideoNarrow: GroupCallLargeVideo(groupCallLargeVideoWide) {
groupCallLargeVideoPin: CrossLineAnimation { groupCallLargeVideoPin: CrossLineAnimation {
fg: groupCallVideoTextFg; fg: groupCallVideoTextFg;
icon: icon {{ "calls/video_over_pin", groupCallVideoTextFg }}; icon: icon {{ "calls/video_over_pin", groupCallVideoTextFg }};
startPosition: point(5px, 2px); startPosition: point(7px, 4px);
endPosition: point(20px, 17px); endPosition: point(17px, 14px);
stroke: 2px; stroke: 3px;
strokeDenominator: 2;
} }
groupCallVideoSmallSkip: 4px; groupCallVideoSmallSkip: 4px;

View file

@ -804,10 +804,14 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) {
if (!endpoint) { if (!endpoint) {
return; return;
} }
const auto i = _activeVideoTracks.find(endpoint);
const auto changed = active const auto changed = active
? !_activeVideoTracks.contains(endpoint) ? (i == end(_activeVideoTracks))
: _activeVideoTracks.remove(endpoint); : (i != end(_activeVideoTracks));
if (active && changed) { if (!changed) {
return;
}
if (active) {
const auto i = _activeVideoTracks.emplace( const auto i = _activeVideoTracks.emplace(
endpoint, endpoint,
VideoTrack{ VideoTrack{
@ -816,13 +820,14 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) {
.peer = endpoint.peer, .peer = endpoint.peer,
}).first; }).first;
addVideoOutput(i->first.id, { i->second.track->sink() }); addVideoOutput(i->first.id, { i->second.track->sink() });
} else if (!active && _videoEndpointPinned.current() == endpoint) { } else {
_videoEndpointPinned = VideoEndpoint(); if (_videoEndpointPinned.current() == endpoint) {
_videoEndpointPinned = VideoEndpoint();
}
_activeVideoTracks.erase(i);
} }
updateRequestedVideoChannelsDelayed(); updateRequestedVideoChannelsDelayed();
if (changed) { _videoStreamActiveUpdates.fire(std::move(endpoint));
_videoStreamActiveUpdates.fire(std::move(endpoint));
}
} }
void GroupCall::rejoin() { void GroupCall::rejoin() {

View file

@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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 "ui/abstract_button.h"
#include "ui/effects/animations.h"
#include "ui/effects/cross_line.h"
#include "lang/lang_keys.h"
#include "styles/style_calls.h" #include "styles/style_calls.h"
namespace Calls::Group { namespace Calls::Group {
@ -22,6 +25,32 @@ constexpr auto kShadowMaxAlpha = 80;
} // namespace } // namespace
struct LargeVideo::PinButton {
PinButton(
not_null<QWidget*> parent,
const style::GroupCallLargeVideo &st);
Ui::AbstractButton area;
Ui::CrossLineAnimation icon;
Ui::RoundRect background;
Ui::Text::String text;
QRect rect;
Ui::Animations::Simple shownAnimation;
bool shown = false;
};
LargeVideo::PinButton::PinButton(
not_null<QWidget*> parent,
const style::GroupCallLargeVideo &st)
: area(parent)
, icon(st::groupCallLargeVideoPin)
, background(
(st.pinPadding.top()
+ st::groupCallLargeVideoPin.icon.height()
+ st.pinPadding.bottom()) / 2,
st::radialBg) {
}
LargeVideo::LargeVideo( LargeVideo::LargeVideo(
QWidget *parent, QWidget *parent,
const style::GroupCallLargeVideo &st, const style::GroupCallLargeVideo &st,
@ -30,9 +59,8 @@ LargeVideo::LargeVideo(
rpl::producer<bool> pinned) rpl::producer<bool> pinned)
: _content(parent, [=](QRect clip) { paint(clip); }) : _content(parent, [=](QRect clip) { paint(clip); })
, _st(st) , _st(st)
, _pin(st::groupCallLargeVideoPin)
, _pinButton((_st.pinPosition.x() >= 0) , _pinButton((_st.pinPosition.x() >= 0)
? std::make_unique<Ui::AbstractButton>(&_content) ? std::make_unique<PinButton>(&_content, st)
: nullptr) : nullptr)
, _smallLayout(!_pinButton) { , _smallLayout(!_pinButton) {
_content.setVisible(visible); _content.setVisible(visible);
@ -42,6 +70,8 @@ LargeVideo::LargeVideo(
setup(std::move(track), std::move(pinned)); setup(std::move(track), std::move(pinned));
} }
LargeVideo::~LargeVideo() = default;
void LargeVideo::raise() { void LargeVideo::raise() {
_content.raise(); _content.raise();
} }
@ -74,7 +104,7 @@ void LargeVideo::setControlsShown(float64 shown) {
rpl::producer<bool> LargeVideo::pinToggled() const { rpl::producer<bool> LargeVideo::pinToggled() const {
return _pinButton return _pinButton
? _pinButton->clicks() | rpl::map([=] { return !_pinned; }) ? _pinButton->area.clicks() | rpl::map([=] { return !_pinned; })
: rpl::never<bool>() | rpl::type_erased(); : rpl::never<bool>() | rpl::type_erased();
} }
@ -103,11 +133,16 @@ void LargeVideo::setup(
_content.events( _content.events(
) | rpl::start_with_next([=](not_null<QEvent*> e) { ) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::MouseButtonPress const auto type = e->type();
if (type == QEvent::Enter && _pinButton) {
togglePinShown(true);
} else if (type == QEvent::Leave && _pinButton) {
togglePinShown(false);
} else if (type == QEvent::MouseButtonPress
&& static_cast<QMouseEvent*>( && static_cast<QMouseEvent*>(
e.get())->button() == Qt::LeftButton) { e.get())->button() == Qt::LeftButton) {
_mouseDown = true; _mouseDown = true;
} else if (e->type() == QEvent::MouseButtonRelease } else if (type == QEvent::MouseButtonRelease
&& static_cast<QMouseEvent*>( && static_cast<QMouseEvent*>(
e.get())->button() == Qt::LeftButton e.get())->button() == Qt::LeftButton
&& _mouseDown) { && _mouseDown) {
@ -151,9 +186,31 @@ void LargeVideo::setup(
setupControls(std::move(pinned)); setupControls(std::move(pinned));
} }
void LargeVideo::togglePinShown(bool shown) {
Expects(_pinButton != nullptr);
if (_pinButton->shown == shown) {
return;
}
_pinButton->shown = shown;
_pinButton->shownAnimation.start(
[=] { updateControlsGeometry(); _content.update(); },
shown ? 0. : 1.,
shown ? 1. : 0.,
st::slideWrapDuration);
}
void LargeVideo::setupControls(rpl::producer<bool> pinned) { void LargeVideo::setupControls(rpl::producer<bool> pinned) {
std::move(pinned) | rpl::start_with_next([=](bool pinned) { std::move(pinned) | rpl::start_with_next([=](bool pinned) {
_pinned = pinned; _pinned = pinned;
if (_pinButton) {
_pinButton->text.setText(
st::semiboldTextStyle,
(pinned
? tr::lng_pinned_unpin
: tr::lng_pinned_pin)(tr::now));
updateControlsGeometry();
}
_content.update(); _content.update();
}, _content.lifetime()); }, _content.lifetime());
@ -165,14 +222,29 @@ void LargeVideo::setupControls(rpl::producer<bool> pinned) {
void LargeVideo::updateControlsGeometry() { void LargeVideo::updateControlsGeometry() {
if (_pinButton) { if (_pinButton) {
const auto &pin = st::groupCallLargeVideoPin.icon; const auto &icon = st::groupCallLargeVideoPin.icon;
const auto buttonWidth = pin.width() + 2 * _st.pinPosition.x(); const auto innerWidth = icon.width()
const auto buttonHeight = pin.height() + 2 * _st.pinPosition.y(); + _st.pinTextPosition.x()
_pinButton->setGeometry( + _pinButton->text.maxWidth();
_content.width() - buttonWidth, const auto innerHeight = icon.height();
const auto buttonWidth = _st.pinPadding.left() + innerWidth + _st.pinPadding.right();
const auto buttonHeight = _st.pinPadding.top() + innerHeight + _st.pinPadding.bottom();
const auto fullWidth = _st.pinPosition.x() * 2 + buttonWidth;
const auto fullHeight = _st.pinPosition.y() * 2 + buttonHeight;
const auto slide = anim::interpolate(
_st.pinPosition.y() + buttonHeight,
0, 0,
_pinButton->shownAnimation.value(_pinButton->shown ? 1. : 0.));
_pinButton->rect = QRect(
_content.width() - _st.pinPosition.x() - buttonWidth,
_st.pinPosition.y() - slide,
buttonWidth, buttonWidth,
buttonHeight); buttonHeight);
_pinButton->area.setGeometry(
_content.width() - fullWidth,
-slide,
fullWidth,
fullHeight);
} }
} }
@ -235,14 +307,37 @@ void LargeVideo::paint(QRect clip) {
} }
void LargeVideo::paintControls(Painter &p, QRect clip) { void LargeVideo::paintControls(Painter &p, QRect clip) {
const auto width = _content.width();
const auto height = _content.height();
// Pin.
if (_pinButton && _pinButton->rect.intersects(clip)) {
const auto &icon = st::groupCallLargeVideoPin.icon;
_pinButton->background.paint(p, _pinButton->rect);
_pinButton->icon.paint(
p,
_pinButton->rect.marginsRemoved(_st.pinPadding).topLeft(),
_pinned ? 1. : 0.);
p.setPen(st::groupCallVideoTextFg);
_pinButton->text.drawLeft(
p,
(_pinButton->rect.x()
+ _st.pinPadding.left()
+ icon.width()
+ _st.pinTextPosition.x()),
(_pinButton->rect.y()
+ _st.pinPadding.top()
+ _st.pinTextPosition.y()),
_pinButton->text.maxWidth(),
width);
}
const auto fullShift = _st.namePosition.y() + st::normalFont->height;
const auto shown = _controlsShownRatio; const auto shown = _controlsShownRatio;
if (shown == 0.) { if (shown == 0.) {
return; return;
} }
const auto width = _content.width();
const auto height = _content.height();
const auto fullShift = _st.namePosition.y() + st::normalFont->height;
const auto shift = anim::interpolate(fullShift, 0, shown); const auto shift = anim::interpolate(fullShift, 0, shown);
// Shadow. // Shadow.
@ -255,7 +350,7 @@ void LargeVideo::paintControls(Painter &p, QRect clip) {
width, width,
_st.shadowHeight); _st.shadowHeight);
const auto shadowFill = shadowRect.intersected(clip); const auto shadowFill = shadowRect.intersected(clip);
if (shadowFill.isEmpty() && _smallLayout) { if (shadowFill.isEmpty()) {
return; return;
} }
const auto factor = style::DevicePixelRatio(); const auto factor = style::DevicePixelRatio();
@ -292,18 +387,6 @@ void LargeVideo::paintControls(Painter &p, QRect clip) {
- st::semiboldFont->height - st::semiboldFont->height
+ shift); + shift);
_track.row->name().drawLeftElided(p, nameLeft, nameTop, hasWidth, width); _track.row->name().drawLeftElided(p, nameLeft, nameTop, hasWidth, width);
// Pin.
if (_pinButton) {
const auto &pin = st::groupCallLargeVideoPin.icon;
const auto pinLeft = (width - _st.pinPosition.x() - pin.width());
const auto pinShift = anim::interpolate(
_st.pinPosition.y() + pin.height(),
0,
shown);
const auto pinTop = (_st.pinPosition.y() - pinShift);
_pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.);
}
} }
QImage GenerateShadow(int height, int topAlpha, int bottomAlpha) { QImage GenerateShadow(int height, int topAlpha, int bottomAlpha) {

View file

@ -8,8 +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/effects/cross_line.h"
#include "ui/effects/animations.h"
#if 1 #if 1
#define USE_OPENGL_LARGE_VIDEO 1 #define USE_OPENGL_LARGE_VIDEO 1
@ -63,6 +61,7 @@ public:
bool visible, bool visible,
rpl::producer<LargeVideoTrack> track, rpl::producer<LargeVideoTrack> track,
rpl::producer<bool> pinned); rpl::producer<bool> pinned);
~LargeVideo();
void raise(); void raise();
void setVisible(bool visible); void setVisible(bool visible);
@ -105,6 +104,8 @@ private:
}; };
struct PinButton;
void setup( void setup(
rpl::producer<LargeVideoTrack> track, rpl::producer<LargeVideoTrack> track,
rpl::producer<bool> pinned); rpl::producer<bool> pinned);
@ -112,13 +113,13 @@ private:
void paint(QRect clip); void paint(QRect clip);
void paintControls(Painter &p, QRect clip); void paintControls(Painter &p, QRect clip);
void updateControlsGeometry(); void updateControlsGeometry();
void togglePinShown(bool shown);
Content _content; Content _content;
const style::GroupCallLargeVideo &_st; const style::GroupCallLargeVideo &_st;
LargeVideoTrack _track; LargeVideoTrack _track;
QImage _shadow; QImage _shadow;
Ui::CrossLineAnimation _pin; std::unique_ptr<PinButton> _pinButton;
std::unique_ptr<Ui::AbstractButton> _pinButton;
rpl::event_stream<> _clicks; rpl::event_stream<> _clicks;
const bool _smallLayout = true; const bool _smallLayout = true;
bool _pinned = false; bool _pinned = false;

View file

@ -1085,10 +1085,8 @@ void Panel::refreshTilesGeometry() {
if (_videoTiles.empty() if (_videoTiles.empty()
|| outer.isEmpty() || outer.isEmpty()
|| _mode == PanelMode::Default) { || _mode == PanelMode::Default) {
trackControls(false);
return; return;
} }
trackControls(true);
struct Geometry { struct Geometry {
QSize size; QSize size;
QRect columns; QRect columns;
@ -1265,9 +1263,11 @@ void Panel::setupPinnedVideo() {
raw->events( raw->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) {
LOG(("Track Enter"));
Ui::Integration::Instance().registerLeaveSubscription(raw); Ui::Integration::Instance().registerLeaveSubscription(raw);
toggleWideControls(true); toggleWideControls(true);
} else if (e->type() == QEvent::Leave) { } else if (e->type() == QEvent::Leave) {
LOG(("Track Leave"));
Ui::Integration::Instance().unregisterLeaveSubscription(raw); Ui::Integration::Instance().unregisterLeaveSubscription(raw);
toggleWideControls(false); toggleWideControls(false);
} }
@ -1277,15 +1277,23 @@ void Panel::setupPinnedVideo() {
} }
void Panel::toggleWideControls(bool shown) { void Panel::toggleWideControls(bool shown) {
if (_wideControlsShown == shown) { if (_showWideControls == shown) {
return; return;
} }
_wideControlsShown = shown; _showWideControls = shown;
_wideControlsAnimation.start( LOG(("On Main Scheduled"));
[=] { updateButtonsGeometry(); }, crl::on_main(widget(), [=] {
shown ? 0. : 1., if (_wideControlsShown == _showWideControls) {
shown ? 1. : 0., return;
st::slideWrapDuration); }
LOG(("On Main Fired: %1").arg(Logs::b(_showWideControls)));
_wideControlsShown = _showWideControls;
_wideControlsAnimation.start(
[=] { updateButtonsGeometry(); },
_wideControlsShown ? 0. : 1.,
_wideControlsShown ? 1. : 0.,
st::slideWrapDuration);
});
} }
void Panel::setupJoinAsChangedToasts() { void Panel::setupJoinAsChangedToasts() {
@ -1795,8 +1803,8 @@ bool Panel::updateMode() {
_members->setMode(mode); _members->setMode(mode);
} }
if (_pinnedVideoWrap) { if (_pinnedVideoWrap) {
_wideControlsShown = _showWideControls = true;
_wideControlsAnimation.stop(); _wideControlsAnimation.stop();
_wideControlsShown = true;
_pinnedVideoWrap->setVisible(mode == PanelMode::Wide); _pinnedVideoWrap->setVisible(mode == PanelMode::Wide);
for (const auto &tile : _videoTiles) { for (const auto &tile : _videoTiles) {
tile.video->setVisible(mode == PanelMode::Wide); tile.video->setVisible(mode == PanelMode::Wide);
@ -1810,7 +1818,7 @@ bool Panel::updateMode() {
void Panel::refreshControlsBackground() { void Panel::refreshControlsBackground() {
if (_mode != PanelMode::Wide) { if (_mode != PanelMode::Wide) {
_trackControlsLifetime.destroy(); trackControls(false);
_controlsBackground.destroy(); _controlsBackground.destroy();
} else if (_controlsBackground) { } else if (_controlsBackground) {
return; return;
@ -1832,6 +1840,7 @@ void Panel::refreshControlsBackground() {
corners->paint(p, _controlsBackground->rect()); corners->paint(p, _controlsBackground->rect());
}, lifetime); }, lifetime);
trackControls(true);
raiseControls(); raiseControls();
} }
@ -1857,8 +1866,10 @@ void Panel::trackControls(bool track) {
raw->events( raw->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) {
LOG(("Track Enter"));
toggleWideControls(true); toggleWideControls(true);
} else if (e->type() == QEvent::Leave) { } else if (e->type() == QEvent::Leave) {
LOG(("Track Leave"));
toggleWideControls(false); toggleWideControls(false);
} }
}, _trackControlsOverStateLifetime); }, _trackControlsOverStateLifetime);

View file

@ -162,8 +162,9 @@ private:
std::optional<QRect> _lastSmallGeometry; std::optional<QRect> _lastSmallGeometry;
std::optional<QRect> _lastLargeGeometry; std::optional<QRect> _lastLargeGeometry;
bool _lastLargeMaximized = false; bool _lastLargeMaximized = false;
bool _wideControlsShown = false; bool _showWideControls = false;
bool _trackControls = false; bool _trackControls = false;
bool _wideControlsShown = false;
Ui::Animations::Simple _wideControlsAnimation; Ui::Animations::Simple _wideControlsAnimation;
object_ptr<Ui::RpWidget> _controlsBackground = { nullptr }; object_ptr<Ui::RpWidget> _controlsBackground = { nullptr };

@ -1 +1 @@
Subproject commit aeeb13bd029597da8cf5104c2e7c56f4641cd6b6 Subproject commit e9fcbfcbacfe9f2f454a7bd34f1a3c5403245523