diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 8110afce9..bac31208c 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1192,6 +1192,8 @@ GroupCallLargeVideo { shadowHeight: pixels; namePosition: point; pinPosition: point; + pinPadding: margins; + pinTextPosition: point; iconPosition: point; } @@ -1199,6 +1201,8 @@ groupCallLargeVideoWide: GroupCallLargeVideo { shadowHeight: 40px; namePosition: point(15px, 8px); pinPosition: point(18px, 18px); + pinPadding: margins(6px, 2px, 12px, 1px); + pinTextPosition: point(1px, 3px); iconPosition: point(10px, 5px); } groupCallLargeVideoNarrow: GroupCallLargeVideo(groupCallLargeVideoWide) { @@ -1214,9 +1218,10 @@ groupCallLargeVideoNarrow: GroupCallLargeVideo(groupCallLargeVideoWide) { groupCallLargeVideoPin: CrossLineAnimation { fg: groupCallVideoTextFg; icon: icon {{ "calls/video_over_pin", groupCallVideoTextFg }}; - startPosition: point(5px, 2px); - endPosition: point(20px, 17px); - stroke: 2px; + startPosition: point(7px, 4px); + endPosition: point(17px, 14px); + stroke: 3px; + strokeDenominator: 2; } groupCallVideoSmallSkip: 4px; diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 78dc57830..6ea0f5ac9 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -804,10 +804,14 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) { if (!endpoint) { return; } + const auto i = _activeVideoTracks.find(endpoint); const auto changed = active - ? !_activeVideoTracks.contains(endpoint) - : _activeVideoTracks.remove(endpoint); - if (active && changed) { + ? (i == end(_activeVideoTracks)) + : (i != end(_activeVideoTracks)); + if (!changed) { + return; + } + if (active) { const auto i = _activeVideoTracks.emplace( endpoint, VideoTrack{ @@ -816,13 +820,14 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) { .peer = endpoint.peer, }).first; addVideoOutput(i->first.id, { i->second.track->sink() }); - } else if (!active && _videoEndpointPinned.current() == endpoint) { - _videoEndpointPinned = VideoEndpoint(); + } else { + if (_videoEndpointPinned.current() == endpoint) { + _videoEndpointPinned = VideoEndpoint(); + } + _activeVideoTracks.erase(i); } updateRequestedVideoChannelsDelayed(); - if (changed) { - _videoStreamActiveUpdates.fire(std::move(endpoint)); - } + _videoStreamActiveUpdates.fire(std::move(endpoint)); } void GroupCall::rejoin() { diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp index 687ee7d43..0b5a7736b 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp @@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "webrtc/webrtc_video_track.h" #include "ui/painter.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" namespace Calls::Group { @@ -22,6 +25,32 @@ constexpr auto kShadowMaxAlpha = 80; } // namespace +struct LargeVideo::PinButton { + PinButton( + not_null 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 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( QWidget *parent, const style::GroupCallLargeVideo &st, @@ -30,9 +59,8 @@ LargeVideo::LargeVideo( rpl::producer pinned) : _content(parent, [=](QRect clip) { paint(clip); }) , _st(st) -, _pin(st::groupCallLargeVideoPin) , _pinButton((_st.pinPosition.x() >= 0) - ? std::make_unique(&_content) + ? std::make_unique(&_content, st) : nullptr) , _smallLayout(!_pinButton) { _content.setVisible(visible); @@ -42,6 +70,8 @@ LargeVideo::LargeVideo( setup(std::move(track), std::move(pinned)); } +LargeVideo::~LargeVideo() = default; + void LargeVideo::raise() { _content.raise(); } @@ -74,7 +104,7 @@ void LargeVideo::setControlsShown(float64 shown) { rpl::producer LargeVideo::pinToggled() const { return _pinButton - ? _pinButton->clicks() | rpl::map([=] { return !_pinned; }) + ? _pinButton->area.clicks() | rpl::map([=] { return !_pinned; }) : rpl::never() | rpl::type_erased(); } @@ -103,11 +133,16 @@ void LargeVideo::setup( _content.events( ) | rpl::start_with_next([=](not_null 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( e.get())->button() == Qt::LeftButton) { _mouseDown = true; - } else if (e->type() == QEvent::MouseButtonRelease + } else if (type == QEvent::MouseButtonRelease && static_cast( e.get())->button() == Qt::LeftButton && _mouseDown) { @@ -151,9 +186,31 @@ void LargeVideo::setup( 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 pinned) { std::move(pinned) | rpl::start_with_next([=](bool 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.lifetime()); @@ -165,14 +222,29 @@ void LargeVideo::setupControls(rpl::producer pinned) { void LargeVideo::updateControlsGeometry() { if (_pinButton) { - const auto &pin = st::groupCallLargeVideoPin.icon; - const auto buttonWidth = pin.width() + 2 * _st.pinPosition.x(); - const auto buttonHeight = pin.height() + 2 * _st.pinPosition.y(); - _pinButton->setGeometry( - _content.width() - buttonWidth, + const auto &icon = st::groupCallLargeVideoPin.icon; + const auto innerWidth = icon.width() + + _st.pinTextPosition.x() + + _pinButton->text.maxWidth(); + 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, + _pinButton->shownAnimation.value(_pinButton->shown ? 1. : 0.)); + _pinButton->rect = QRect( + _content.width() - _st.pinPosition.x() - buttonWidth, + _st.pinPosition.y() - slide, buttonWidth, 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) { + 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; if (shown == 0.) { 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); // Shadow. @@ -255,7 +350,7 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { width, _st.shadowHeight); const auto shadowFill = shadowRect.intersected(clip); - if (shadowFill.isEmpty() && _smallLayout) { + if (shadowFill.isEmpty()) { return; } const auto factor = style::DevicePixelRatio(); @@ -292,18 +387,6 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { - st::semiboldFont->height + shift); _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) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.h b/Telegram/SourceFiles/calls/group/calls_group_large_video.h index 0f7d7a069..c9805d948 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.h +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.h @@ -8,8 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/rp_widget.h" -#include "ui/effects/cross_line.h" -#include "ui/effects/animations.h" #if 1 #define USE_OPENGL_LARGE_VIDEO 1 @@ -63,6 +61,7 @@ public: bool visible, rpl::producer track, rpl::producer pinned); + ~LargeVideo(); void raise(); void setVisible(bool visible); @@ -105,6 +104,8 @@ private: }; + struct PinButton; + void setup( rpl::producer track, rpl::producer pinned); @@ -112,13 +113,13 @@ private: void paint(QRect clip); void paintControls(Painter &p, QRect clip); void updateControlsGeometry(); + void togglePinShown(bool shown); Content _content; const style::GroupCallLargeVideo &_st; LargeVideoTrack _track; QImage _shadow; - Ui::CrossLineAnimation _pin; - std::unique_ptr _pinButton; + std::unique_ptr _pinButton; rpl::event_stream<> _clicks; const bool _smallLayout = true; bool _pinned = false; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 1f6887ab4..e06e5a40e 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -1085,10 +1085,8 @@ void Panel::refreshTilesGeometry() { if (_videoTiles.empty() || outer.isEmpty() || _mode == PanelMode::Default) { - trackControls(false); return; } - trackControls(true); struct Geometry { QSize size; QRect columns; @@ -1265,9 +1263,11 @@ void Panel::setupPinnedVideo() { raw->events( ) | rpl::start_with_next([=](not_null e) { if (e->type() == QEvent::Enter) { + LOG(("Track Enter")); Ui::Integration::Instance().registerLeaveSubscription(raw); toggleWideControls(true); } else if (e->type() == QEvent::Leave) { + LOG(("Track Leave")); Ui::Integration::Instance().unregisterLeaveSubscription(raw); toggleWideControls(false); } @@ -1277,15 +1277,23 @@ void Panel::setupPinnedVideo() { } void Panel::toggleWideControls(bool shown) { - if (_wideControlsShown == shown) { + if (_showWideControls == shown) { return; } - _wideControlsShown = shown; - _wideControlsAnimation.start( - [=] { updateButtonsGeometry(); }, - shown ? 0. : 1., - shown ? 1. : 0., - st::slideWrapDuration); + _showWideControls = shown; + LOG(("On Main Scheduled")); + crl::on_main(widget(), [=] { + if (_wideControlsShown == _showWideControls) { + return; + } + 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() { @@ -1795,8 +1803,8 @@ bool Panel::updateMode() { _members->setMode(mode); } if (_pinnedVideoWrap) { + _wideControlsShown = _showWideControls = true; _wideControlsAnimation.stop(); - _wideControlsShown = true; _pinnedVideoWrap->setVisible(mode == PanelMode::Wide); for (const auto &tile : _videoTiles) { tile.video->setVisible(mode == PanelMode::Wide); @@ -1810,7 +1818,7 @@ bool Panel::updateMode() { void Panel::refreshControlsBackground() { if (_mode != PanelMode::Wide) { - _trackControlsLifetime.destroy(); + trackControls(false); _controlsBackground.destroy(); } else if (_controlsBackground) { return; @@ -1832,6 +1840,7 @@ void Panel::refreshControlsBackground() { corners->paint(p, _controlsBackground->rect()); }, lifetime); + trackControls(true); raiseControls(); } @@ -1857,8 +1866,10 @@ void Panel::trackControls(bool track) { raw->events( ) | rpl::start_with_next([=](not_null e) { if (e->type() == QEvent::Enter) { + LOG(("Track Enter")); toggleWideControls(true); } else if (e->type() == QEvent::Leave) { + LOG(("Track Leave")); toggleWideControls(false); } }, _trackControlsOverStateLifetime); diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index 0c9ea5860..f3efbb88d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -162,8 +162,9 @@ private: std::optional _lastSmallGeometry; std::optional _lastLargeGeometry; bool _lastLargeMaximized = false; - bool _wideControlsShown = false; + bool _showWideControls = false; bool _trackControls = false; + bool _wideControlsShown = false; Ui::Animations::Simple _wideControlsAnimation; object_ptr _controlsBackground = { nullptr }; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index aeeb13bd0..e9fcbfcba 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit aeeb13bd029597da8cf5104c2e7c56f4641cd6b6 +Subproject commit e9fcbfcbacfe9f2f454a7bd34f1a3c5403245523