diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 4750f37ce..7c539e141 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1260,3 +1260,17 @@ groupCallTooltip: Tooltip(defaultTooltip) { textFg: groupCallMembersFg; textBorder: groupCallMembersBgOver; } +groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) { + bg: importantTooltipBg; + padding: margins(10px, 3px, 10px, 5px); + radius: 4px; + arrow: 4px; +} +groupCallNiceTooltipLabel: FlatLabel(defaultImportantTooltipLabel) { + style: TextStyle(defaultTextStyle) { + font: font(11px); + linkFont: font(11px); + linkFontOver: font(11px underline); + } +} +groupCallNiceTooltipTop: 4px; diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 95d460623..6ebf8a3ba 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -500,7 +500,6 @@ rpl::producer GroupCall::canManageValue() const { void GroupCall::toggleVideo(bool active) { if (!_instance || !_id - || (active && mutedByAdmin()) || (!active && !_cameraOutgoing)) { return; } @@ -513,7 +512,6 @@ void GroupCall::toggleVideo(bool active) { void GroupCall::toggleScreenSharing(std::optional uniqueId) { if (!_instance || !_id - || (uniqueId && mutedByAdmin()) || (!uniqueId && !_screenOutgoing)) { return; } diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 44ac9d4ee..7ceede4fb 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/dropdown_menu.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/tooltip.h" #include "ui/chat/group_call_bar.h" #include "ui/layers/layer_manager.h" #include "ui/layers/generic_box.h" @@ -45,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_lists_box.h" #include "boxes/confirm_box.h" #include "base/unixtime.h" +#include "base/qt_signal_producer.h" #include "base/timer_rpl.h" #include "app.h" #include "apiwrap.h" // api().kickParticipant. @@ -1792,6 +1794,7 @@ bool Panel::updateMode() { _call->pinVideoEndpoint({}); } refreshVideoButtons(wide); + _niceTooltip.destroy(); _mode = mode; if (_title) { _title->setTextColorOverride(wide @@ -1983,33 +1986,111 @@ void Panel::setupControlsBackgroundWide() { trackControls(true); } -template -void Panel::trackControl(WidgetPointer &widget, rpl::lifetime &lifetime) { - if (widget) { - const auto raw = &*widget; - raw->events( - ) | rpl::start_with_next([=](not_null e) { - using Type = std::remove_cvref_t; - constexpr auto mute = std::is_same_v; - if (e->type() == QEvent::Enter) { - auto &integration = Ui::Integration::Instance(); - if constexpr (mute) { - integration.registerLeaveSubscription(raw->outer()); - } else { - integration.registerLeaveSubscription(raw); - } - toggleWideControls(true); - } else if (e->type() == QEvent::Leave) { - auto &integration = Ui::Integration::Instance(); - if constexpr (mute) { - integration.unregisterLeaveSubscription(raw->outer()); - } else { - integration.unregisterLeaveSubscription(raw); - } - toggleWideControls(false); - } - }, lifetime); +void Panel::trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime) { + if (!widget) { + return; } + widget->events( + ) | rpl::start_with_next([=](not_null e) { + if (e->type() == QEvent::Enter) { + trackControlOver(widget, true); + } else if (e->type() == QEvent::Leave) { + trackControlOver(widget, false); + } + }, lifetime); +} + +void Panel::trackControlOver(not_null control, bool over) { + if (_niceTooltip) { + _niceTooltip.release()->toggleAnimated(false); + } + if (over) { + Ui::Integration::Instance().registerLeaveSubscription(control); + showNiceTooltip(control); + } else { + Ui::Integration::Instance().unregisterLeaveSubscription(control); + } + toggleWideControls(over); +} + +void Panel::showNiceTooltip(not_null control) { + auto text = [&]() -> rpl::producer { + if (control == _screenShare.data()) { + if (_call->mutedByAdmin()) { + return nullptr; + } + return tr::lng_group_call_tooltip_screen(); + } else if (control == _video.data()) { + if (_call->mutedByAdmin()) { + return nullptr; + } + return _call->isSharingCameraValue( + ) | rpl::map([=](bool sharing) { + return sharing + ? tr::lng_group_call_tooltip_camera_off() + : tr::lng_group_call_tooltip_camera(); + }) | rpl::flatten_latest(); + } else if (control == _settings.data()) { + return tr::lng_group_call_settings(); + } else if (control == _mute->outer()) { + return MuteButtonTooltip(_call); + } else if (control == _hangup.data()) { + return tr::lng_group_call_leave(); + } + return rpl::producer(); + }(); + if (!text + || _wideControlsAnimation.animating() + || !_wideControlsShown) { + return; + } + _niceTooltip.create( + widget().get(), + object_ptr( + widget().get(), + std::move(text), + st::groupCallNiceTooltipLabel), + st::groupCallNiceTooltip); + const auto tooltip = _niceTooltip.data(); + const auto weak = QPointer(tooltip); + const auto destroy = [=] { + delete weak.data(); + }; + tooltip->setAttribute(Qt::WA_TransparentForMouseEvents); + tooltip->setHiddenCallback(destroy); + base::qt_signal_producer( + control.get(), + &QObject::destroyed + ) | rpl::start_with_next(destroy, tooltip->lifetime()); + + const auto geometry = control->geometry(); + const auto countPosition = [=](QSize size) { + const auto strong = weak.data(); + if (!strong) { + return QPoint(); + } + const auto top = geometry.y() + - st::groupCallNiceTooltipTop + - size.height(); + const auto middle = geometry.center().x(); + const auto back = _controlsBackgroundWide.data(); + if (size.width() >= _viewport->widget()->width()) { + return QPoint(_viewport->widget()->x(), top); + } else if (back && size.width() >= back->width()) { + return QPoint( + back->x() - (size.width() - back->width()) / 2, + top); + } else if (back && (middle - back->x() < size.width() / 2)) { + return QPoint(back->x(), top); + } else if (back + && (back->x() + back->width() - middle < size.width() / 2)) { + return QPoint(back->x() + back->width() - size.width(), top); + } else { + return QPoint(middle - size.width() / 2, top); + } + }; + tooltip->pointAt(geometry, RectPart::Top, countPosition); + tooltip->toggleAnimated(true); } void Panel::trackControls(bool track) { @@ -2032,7 +2113,7 @@ void Panel::trackControls(bool track) { const auto trackOne = [=](auto &&widget) { trackControl(widget, _trackControlsOverStateLifetime); }; - trackOne(_mute); + trackOne(_mute->outer()); trackOne(_video); trackOne(_screenShare); trackOne(_wideMenu); @@ -2106,13 +2187,13 @@ void Panel::updateButtonsGeometry() { const auto skip = st::groupCallButtonSkipSmall; const auto fullWidth = (_video->width() + skip) + (_screenShare->width() + skip) - + muteSize + + (muteSize + skip) + (_settings ->width() + skip) - + _hangup->width() + skip; + + _hangup->width(); const auto membersSkip = st::groupCallNarrowSkip; const auto membersWidth = st::groupCallNarrowMembersWidth + 2 * membersSkip; - auto left = (widget()->width() + auto left = membersSkip + (widget()->width() - membersWidth - membersSkip - fullWidth) / 2; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index 500286122..27b0606a3 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -26,6 +26,7 @@ class GroupCall; namespace Ui { class AbstractButton; +class ImportantTooltip; class DropdownMenu; class CallButton; class CallMuteButton; @@ -102,8 +103,9 @@ private: void enlargeVideo(); void minimizeVideo(); - template - void trackControl(WidgetPointer &widget, rpl::lifetime &lifetime); + void trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime); + void trackControlOver(not_null control, bool over); + void showNiceTooltip(not_null control); bool updateMode(); void updateControlsGeometry(); @@ -191,6 +193,7 @@ private: object_ptr _screenShare = { nullptr }; std::unique_ptr _mute; object_ptr _hangup; + object_ptr _niceTooltip = { nullptr }; Fn _callShareLinkCallback; rpl::lifetime _peerLifetime; diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp index 54dc70d86..5717e6433 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/gl/gl_surface.h" #include "ui/effects/animations.h" #include "ui/effects/cross_line.h" +#include "data/data_group_call.h" // MuteButtonTooltip. #include "lang/lang_keys.h" #include "styles/style_calls.h" @@ -603,4 +604,51 @@ QImage GenerateShadow( return result; } +rpl::producer MuteButtonTooltip(not_null call) { + //return rpl::single(std::make_tuple( + // (Data::GroupCall*)nullptr, + // call->scheduleDate() + //)) | rpl::then(call->real( + //) | rpl::map([](not_null real) { + // using namespace rpl::mappers; + // return real->scheduleDateValue( + // ) | rpl::map([=](TimeId scheduleDate) { + // return std::make_tuple(real.get(), scheduleDate); + // }); + //}) | rpl::flatten_latest( + //)) | rpl::map([=]( + // Data::GroupCall *real, + // TimeId scheduleDate) -> rpl::producer { + // if (scheduleDate) { + // return rpl::combine( + // call->canManageValue(), + // (real + // ? real->scheduleStartSubscribedValue() + // : rpl::single(false)) + // ) | rpl::map([](bool canManage, bool subscribed) { + // return canManage + // ? tr::lng_group_call_start_now() + // : subscribed + // ? tr::lng_group_call_cancel_reminder() + // : tr::lng_group_call_set_reminder(); + // }) | rpl::flatten_latest(); + // } + return call->mutedValue( + ) | rpl::map([](MuteState muted) { + switch (muted) { + case MuteState::Active: + case MuteState::PushToTalk: + return tr::lng_group_call_you_are_live(); + case MuteState::ForceMuted: + return tr::lng_group_call_tooltip_force_muted(); + case MuteState::RaisedHand: + return tr::lng_group_call_tooltip_raised_hand(); + case MuteState::Muted: + return tr::lng_group_call_tooltip_microphone(); + } + Unexpected("Value in MuteState in showNiceTooltip."); + }) | rpl::flatten_latest(); + //}) | rpl::flatten_latest(); +} + } // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport.h b/Telegram/SourceFiles/calls/group/calls_group_viewport.h index 1c021c1cd..a2dfb7fbf 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport.h +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport.h @@ -23,6 +23,7 @@ struct ChosenRenderer; } // namespace Ui namespace Calls { +class GroupCall; struct VideoEndpoint; struct VideoPinToggle; struct VideoQualityRequest; @@ -158,4 +159,7 @@ private: int bottomAlpha, QColor color = QColor(0, 0, 0)); +[[nodiscard]] rpl::producer MuteButtonTooltip( + not_null call); + } // namespace Calls::Group diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp index 51e4747b6..8d6f62e28 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp @@ -1068,10 +1068,6 @@ rpl::producer CallMuteButton::clicks() { }); } -rpl::producer> CallMuteButton::events() const { - return _content->events(); -} - QSize CallMuteButton::innerSize() const { return innerGeometry().size(); } @@ -1169,7 +1165,7 @@ rpl::producer CallMuteButton::colorOverrides() const { return _colorOverrides.events(); } -not_null CallMuteButton::outer() const { +not_null CallMuteButton::outer() const { return _content.get(); } diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.h b/Telegram/SourceFiles/ui/controls/call_mute_button.h index 109cb2c28..828c44db7 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.h +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.h @@ -64,7 +64,6 @@ public: void setStyle(const style::CallMuteButton &st); void setLevel(float level); [[nodiscard]] rpl::producer clicks(); - [[nodiscard]] rpl::producer> events() const; [[nodiscard]] QSize innerSize() const; [[nodiscard]] QRect innerGeometry() const; @@ -83,7 +82,7 @@ public: void raise(); void lower(); - [[nodiscard]] not_null outer() const; + [[nodiscard]] not_null outer() const; [[nodiscard]] rpl::producer colorOverrides() const; [[nodiscard]] rpl::lifetime &lifetime(); diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index 11cd08b14..19543aaf4 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit 11cd08b14206a2ce3669cebafb6e693a30ef14ab +Subproject commit 19543aaf43d97e254f9b379cc3bc77491009f9d4 diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index c84317f67..d27f98c4f 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -91,6 +91,8 @@ PRIVATE # iOS / macOS platform/darwin/DarwinInterface.h platform/darwin/DarwinInterface.mm + platform/darwin/DarwinVideoSource.h + platform/darwin/DarwinVideoSource.mm platform/darwin/DesktopCaptureSourceView.h platform/darwin/DesktopCaptureSourceView.mm platform/darwin/DesktopSharingCapturer.h diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 8fbeb7f50..f475fe28e 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 8fbeb7f5032e588dfd134c9fe80e1880f888f12f +Subproject commit f475fe28e47af4408bc96ba63e63f599c02538ca