mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add a hint to turn on the camera.
This commit is contained in:
parent
5827d6ffdb
commit
1cb1f1cbc1
9 changed files with 293 additions and 79 deletions
BIN
Telegram/Resources/icons/calls/video_tooltip.png
Normal file
BIN
Telegram/Resources/icons/calls/video_tooltip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 360 B |
BIN
Telegram/Resources/icons/calls/video_tooltip@2x.png
Normal file
BIN
Telegram/Resources/icons/calls/video_tooltip@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 622 B |
BIN
Telegram/Resources/icons/calls/video_tooltip@3x.png
Normal file
BIN
Telegram/Resources/icons/calls/video_tooltip@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 967 B |
|
@ -1251,7 +1251,7 @@ groupCallTooltip: Tooltip(defaultTooltip) {
|
||||||
}
|
}
|
||||||
groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) {
|
groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) {
|
||||||
bg: importantTooltipBg;
|
bg: importantTooltipBg;
|
||||||
padding: margins(10px, 3px, 10px, 5px);
|
padding: margins(10px, 1px, 10px, 3px);
|
||||||
radius: 4px;
|
radius: 4px;
|
||||||
arrow: 4px;
|
arrow: 4px;
|
||||||
}
|
}
|
||||||
|
@ -1262,5 +1262,16 @@ groupCallNiceTooltipLabel: FlatLabel(defaultImportantTooltipLabel) {
|
||||||
linkFontOver: font(11px underline);
|
linkFontOver: font(11px underline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
groupCallStickedTooltip: ImportantTooltip(groupCallNiceTooltip) {
|
||||||
|
padding: margins(10px, 1px, 6px, 3px);
|
||||||
|
}
|
||||||
|
groupCallStickedTooltipClose: IconButton(defaultIconButton) {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
iconPosition: point(4px, 3px);
|
||||||
|
icon: icon {{ "calls/video_tooltip", importantTooltipFg }};
|
||||||
|
iconOver: icon {{ "calls/video_tooltip", importantTooltipFg }};
|
||||||
|
ripple: emptyRippleAnimation;
|
||||||
|
}
|
||||||
groupCallNiceTooltipTop: 4px;
|
groupCallNiceTooltipTop: 4px;
|
||||||
groupCallPaused: icon {{ "calls/video_large_paused", groupCallVideoTextFg }};
|
groupCallPaused: icon {{ "calls/video_large_paused", groupCallVideoTextFg }};
|
||||||
|
|
|
@ -187,6 +187,28 @@ struct GroupCall::SinkPointer {
|
||||||
std::weak_ptr<Webrtc::SinkInterface> data;
|
std::weak_ptr<Webrtc::SinkInterface> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GroupCall::VideoTrack {
|
||||||
|
VideoTrack(bool paused, bool requireARGB32, not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
Webrtc::VideoTrack track;
|
||||||
|
rpl::variable<QSize> trackSize;
|
||||||
|
not_null<PeerData*> peer;
|
||||||
|
rpl::lifetime lifetime;
|
||||||
|
Group::VideoQuality quality = Group::VideoQuality();
|
||||||
|
bool shown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
GroupCall::VideoTrack::VideoTrack(
|
||||||
|
bool paused,
|
||||||
|
bool requireARGB32,
|
||||||
|
not_null<PeerData*> peer)
|
||||||
|
: track((paused
|
||||||
|
? Webrtc::VideoState::Paused
|
||||||
|
: Webrtc::VideoState::Active),
|
||||||
|
requireARGB32)
|
||||||
|
, peer(peer) {
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsGroupCallAdmin(
|
[[nodiscard]] bool IsGroupCallAdmin(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<PeerData*> participantPeer) {
|
not_null<PeerData*> participantPeer) {
|
||||||
|
@ -451,6 +473,21 @@ void GroupCall::MediaChannelDescriptionsTask::cancel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<PeerData*> GroupCall::TrackPeer(
|
||||||
|
const std::unique_ptr<VideoTrack> &track) {
|
||||||
|
return track->peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<Webrtc::VideoTrack*> GroupCall::TrackPointer(
|
||||||
|
const std::unique_ptr<VideoTrack> &track) {
|
||||||
|
return &track->track;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QSize> GroupCall::TrackSizeValue(
|
||||||
|
const std::unique_ptr<VideoTrack> &track) {
|
||||||
|
return track->trackSize.value();
|
||||||
|
}
|
||||||
|
|
||||||
GroupCall::GroupCall(
|
GroupCall::GroupCall(
|
||||||
not_null<Delegate*> delegate,
|
not_null<Delegate*> delegate,
|
||||||
Group::JoinInfo info,
|
Group::JoinInfo info,
|
||||||
|
@ -1064,43 +1101,39 @@ void GroupCall::markEndpointActive(
|
||||||
if (active) {
|
if (active) {
|
||||||
const auto i = _activeVideoTracks.emplace(
|
const auto i = _activeVideoTracks.emplace(
|
||||||
endpoint,
|
endpoint,
|
||||||
VideoTrack{
|
std::make_unique<VideoTrack>(
|
||||||
.track = std::make_unique<Webrtc::VideoTrack>(
|
paused,
|
||||||
(paused
|
_requireARGB32,
|
||||||
? Webrtc::VideoState::Paused
|
endpoint.peer)).first;
|
||||||
: Webrtc::VideoState::Active),
|
const auto track = &i->second->track;
|
||||||
_requireARGB32),
|
|
||||||
.peer = endpoint.peer,
|
|
||||||
}).first;
|
|
||||||
const auto track = i->second.track.get();
|
|
||||||
|
|
||||||
track->renderNextFrame(
|
track->renderNextFrame(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
auto &activeTrack = _activeVideoTracks[endpoint];
|
const auto activeTrack = _activeVideoTracks[endpoint].get();
|
||||||
const auto size = track->frameSize();
|
const auto size = track->frameSize();
|
||||||
if (size.isEmpty()) {
|
if (size.isEmpty()) {
|
||||||
track->markFrameShown();
|
track->markFrameShown();
|
||||||
} else if (!activeTrack.shown) {
|
} else if (!activeTrack->shown) {
|
||||||
activeTrack.shown = true;
|
activeTrack->shown = true;
|
||||||
markTrackShown(endpoint, true);
|
markTrackShown(endpoint, true);
|
||||||
}
|
}
|
||||||
activeTrack.trackSize = size;
|
activeTrack->trackSize = size;
|
||||||
}, i->second.lifetime);
|
}, i->second->lifetime);
|
||||||
|
|
||||||
const auto size = track->frameSize();
|
const auto size = track->frameSize();
|
||||||
i->second.trackSize = size;
|
i->second->trackSize = size;
|
||||||
if (!size.isEmpty() || paused) {
|
if (!size.isEmpty() || paused) {
|
||||||
i->second.shown = true;
|
i->second->shown = true;
|
||||||
shown = true;
|
shown = true;
|
||||||
} else {
|
} else {
|
||||||
track->stateValue(
|
track->stateValue(
|
||||||
) | rpl::filter([=](Webrtc::VideoState state) {
|
) | rpl::filter([=](Webrtc::VideoState state) {
|
||||||
return (state == Webrtc::VideoState::Paused)
|
return (state == Webrtc::VideoState::Paused)
|
||||||
&& !_activeVideoTracks[endpoint].shown;
|
&& !_activeVideoTracks[endpoint]->shown;
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
_activeVideoTracks[endpoint].shown = true;
|
_activeVideoTracks[endpoint]->shown = true;
|
||||||
markTrackShown(endpoint, true);
|
markTrackShown(endpoint, true);
|
||||||
}, i->second.lifetime);
|
}, i->second->lifetime);
|
||||||
}
|
}
|
||||||
addVideoOutput(i->first.id, { track->sink() });
|
addVideoOutput(i->first.id, { track->sink() });
|
||||||
} else {
|
} else {
|
||||||
|
@ -1144,7 +1177,7 @@ void GroupCall::markTrackPaused(const VideoEndpoint &endpoint, bool paused) {
|
||||||
const auto i = _activeVideoTracks.find(endpoint);
|
const auto i = _activeVideoTracks.find(endpoint);
|
||||||
Assert(i != end(_activeVideoTracks));
|
Assert(i != end(_activeVideoTracks));
|
||||||
|
|
||||||
i->second.track->setState(paused
|
i->second->track.setState(paused
|
||||||
? Webrtc::VideoState::Paused
|
? Webrtc::VideoState::Paused
|
||||||
: Webrtc::VideoState::Active);
|
: Webrtc::VideoState::Active);
|
||||||
}
|
}
|
||||||
|
@ -2420,13 +2453,13 @@ void GroupCall::updateRequestedVideoChannels() {
|
||||||
.ssrcGroups = (params->camera.endpointId == endpointId
|
.ssrcGroups = (params->camera.endpointId == endpointId
|
||||||
? params->camera.ssrcGroups
|
? params->camera.ssrcGroups
|
||||||
: params->screen.ssrcGroups),
|
: params->screen.ssrcGroups),
|
||||||
.minQuality = ((video.quality == Group::VideoQuality::Full
|
.minQuality = ((video->quality == Group::VideoQuality::Full
|
||||||
&& endpoint.type == VideoEndpointType::Screen)
|
&& endpoint.type == VideoEndpointType::Screen)
|
||||||
? Quality::Full
|
? Quality::Full
|
||||||
: Quality::Thumbnail),
|
: Quality::Thumbnail),
|
||||||
.maxQuality = ((video.quality == Group::VideoQuality::Full)
|
.maxQuality = ((video->quality == Group::VideoQuality::Full)
|
||||||
? Quality::Full
|
? Quality::Full
|
||||||
: (video.quality == Group::VideoQuality::Medium
|
: (video->quality == Group::VideoQuality::Medium
|
||||||
&& endpoint.type != VideoEndpointType::Screen)
|
&& endpoint.type != VideoEndpointType::Screen)
|
||||||
? Quality::Medium
|
? Quality::Medium
|
||||||
: Quality::Thumbnail),
|
: Quality::Thumbnail),
|
||||||
|
@ -2911,10 +2944,10 @@ void GroupCall::requestVideoQuality(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto i = _activeVideoTracks.find(endpoint);
|
const auto i = _activeVideoTracks.find(endpoint);
|
||||||
if (i == end(_activeVideoTracks) || i->second.quality == quality) {
|
if (i == end(_activeVideoTracks) || i->second->quality == quality) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
i->second.quality = quality;
|
i->second->quality = quality;
|
||||||
updateRequestedVideoChannelsDelayed();
|
updateRequestedVideoChannelsDelayed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,8 @@ struct VideoEndpoint {
|
||||||
std::string id;
|
std::string id;
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const noexcept {
|
[[nodiscard]] bool empty() const noexcept {
|
||||||
|
Expects(id.empty() || peer != nullptr);
|
||||||
|
|
||||||
return id.empty();
|
return id.empty();
|
||||||
}
|
}
|
||||||
[[nodiscard]] explicit operator bool() const noexcept {
|
[[nodiscard]] explicit operator bool() const noexcept {
|
||||||
|
@ -194,6 +196,15 @@ public:
|
||||||
|
|
||||||
using GlobalShortcutManager = base::GlobalShortcutManager;
|
using GlobalShortcutManager = base::GlobalShortcutManager;
|
||||||
|
|
||||||
|
struct VideoTrack;
|
||||||
|
|
||||||
|
[[nodiscard]] static not_null<PeerData*> TrackPeer(
|
||||||
|
const std::unique_ptr<VideoTrack> &track);
|
||||||
|
[[nodiscard]] static not_null<Webrtc::VideoTrack*> TrackPointer(
|
||||||
|
const std::unique_ptr<VideoTrack> &track);
|
||||||
|
[[nodiscard]] static rpl::producer<QSize> TrackSizeValue(
|
||||||
|
const std::unique_ptr<VideoTrack> &track);
|
||||||
|
|
||||||
GroupCall(
|
GroupCall(
|
||||||
not_null<Delegate*> delegate,
|
not_null<Delegate*> delegate,
|
||||||
Group::JoinInfo info,
|
Group::JoinInfo info,
|
||||||
|
@ -321,27 +332,8 @@ public:
|
||||||
-> rpl::producer<VideoEndpoint> {
|
-> rpl::producer<VideoEndpoint> {
|
||||||
return _videoEndpointLarge.value();
|
return _videoEndpointLarge.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VideoTrack {
|
|
||||||
std::unique_ptr<Webrtc::VideoTrack> track;
|
|
||||||
rpl::variable<QSize> trackSize;
|
|
||||||
PeerData *peer = nullptr;
|
|
||||||
rpl::lifetime lifetime;
|
|
||||||
Group::VideoQuality quality = Group::VideoQuality();
|
|
||||||
bool shown = false;
|
|
||||||
|
|
||||||
[[nodiscard]] explicit operator bool() const {
|
|
||||||
return (track != nullptr);
|
|
||||||
}
|
|
||||||
[[nodiscard]] bool operator==(const VideoTrack &other) const {
|
|
||||||
return (track == other.track) && (peer == other.peer);
|
|
||||||
}
|
|
||||||
[[nodiscard]] bool operator!=(const VideoTrack &other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
[[nodiscard]] auto activeVideoTracks() const
|
[[nodiscard]] auto activeVideoTracks() const
|
||||||
-> const base::flat_map<VideoEndpoint, VideoTrack> & {
|
-> const base::flat_map<VideoEndpoint, std::unique_ptr<VideoTrack>> & {
|
||||||
return _activeVideoTracks;
|
return _activeVideoTracks;
|
||||||
}
|
}
|
||||||
[[nodiscard]] auto shownVideoTracks() const
|
[[nodiscard]] auto shownVideoTracks() const
|
||||||
|
@ -625,7 +617,9 @@ private:
|
||||||
rpl::event_stream<VideoStateToggle> _videoStreamActiveUpdates;
|
rpl::event_stream<VideoStateToggle> _videoStreamActiveUpdates;
|
||||||
rpl::event_stream<VideoStateToggle> _videoStreamPausedUpdates;
|
rpl::event_stream<VideoStateToggle> _videoStreamPausedUpdates;
|
||||||
rpl::event_stream<VideoStateToggle> _videoStreamShownUpdates;
|
rpl::event_stream<VideoStateToggle> _videoStreamShownUpdates;
|
||||||
base::flat_map<VideoEndpoint, VideoTrack> _activeVideoTracks;
|
base::flat_map<
|
||||||
|
VideoEndpoint,
|
||||||
|
std::unique_ptr<VideoTrack>> _activeVideoTracks;
|
||||||
base::flat_set<VideoEndpoint> _shownVideoTracks;
|
base::flat_set<VideoEndpoint> _shownVideoTracks;
|
||||||
rpl::variable<VideoEndpoint> _videoEndpointLarge;
|
rpl::variable<VideoEndpoint> _videoEndpointLarge;
|
||||||
rpl::variable<bool> _videoEndpointPinned = false;
|
rpl::variable<bool> _videoEndpointPinned = false;
|
||||||
|
|
|
@ -499,17 +499,26 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
|
||||||
&st::groupCallVideoActiveSmall);
|
&st::groupCallVideoActiveSmall);
|
||||||
_video->show();
|
_video->show();
|
||||||
_video->setClickedCallback([=] {
|
_video->setClickedCallback([=] {
|
||||||
|
hideStickedTooltip(
|
||||||
|
StickedTooltip::Camera,
|
||||||
|
StickedTooltipHide::Activated);
|
||||||
_call->toggleVideo(!_call->isSharingCamera());
|
_call->toggleVideo(!_call->isSharingCamera());
|
||||||
});
|
});
|
||||||
_video->setColorOverrides(
|
_video->setColorOverrides(
|
||||||
toggleableOverrides(_call->isSharingCameraValue()));
|
toggleableOverrides(_call->isSharingCameraValue()));
|
||||||
_call->isSharingCameraValue(
|
_call->isSharingCameraValue(
|
||||||
) | rpl::start_with_next([=](bool sharing) {
|
) | rpl::start_with_next([=](bool sharing) {
|
||||||
|
if (sharing) {
|
||||||
|
hideStickedTooltip(
|
||||||
|
StickedTooltip::Camera,
|
||||||
|
StickedTooltipHide::Activated);
|
||||||
|
}
|
||||||
_video->setProgress(sharing ? 1. : 0.);
|
_video->setProgress(sharing ? 1. : 0.);
|
||||||
}, _video->lifetime());
|
}, _video->lifetime());
|
||||||
_call->mutedValue(
|
_call->mutedValue(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
updateButtonsGeometry();
|
updateButtonsGeometry();
|
||||||
|
showStickedTooltip();
|
||||||
}, _video->lifetime());
|
}, _video->lifetime());
|
||||||
}
|
}
|
||||||
if (!_screenShare) {
|
if (!_screenShare) {
|
||||||
|
@ -536,6 +545,45 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
|
||||||
updateButtonsGeometry();
|
updateButtonsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::hideStickedTooltip(StickedTooltipHide hide) {
|
||||||
|
if (!_stickedTooltipClose || !_niceTooltipControl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_niceTooltipControl.data() == _video.data()) {
|
||||||
|
hideStickedTooltip(StickedTooltip::Camera, hide);
|
||||||
|
} else if (_niceTooltipControl.data() == _mute->outer().get()) {
|
||||||
|
hideStickedTooltip(StickedTooltip::Microphone, hide);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::hideStickedTooltip(
|
||||||
|
StickedTooltip type,
|
||||||
|
StickedTooltipHide hide) {
|
||||||
|
if (hide != StickedTooltipHide::Unavailable) {
|
||||||
|
_stickedTooltipsShown |= type;
|
||||||
|
if (hide == StickedTooltipHide::Discarded) {
|
||||||
|
// #TODO calls save to settings.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto control = (type == StickedTooltip::Camera)
|
||||||
|
? _video.data()
|
||||||
|
: (type == StickedTooltip::Microphone)
|
||||||
|
? _mute->outer().get()
|
||||||
|
: nullptr;
|
||||||
|
if (_niceTooltipControl.data() == control) {
|
||||||
|
hideNiceTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::hideNiceTooltip() {
|
||||||
|
if (!_niceTooltip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_stickedTooltipClose = nullptr;
|
||||||
|
_niceTooltip.release()->toggleAnimated(false);
|
||||||
|
_niceTooltipControl = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::initShareAction() {
|
void Panel::initShareAction() {
|
||||||
const auto showBoxCallback = [=](object_ptr<Ui::BoxContent> next) {
|
const auto showBoxCallback = [=](object_ptr<Ui::BoxContent> next) {
|
||||||
_layerBg->showBox(std::move(next));
|
_layerBg->showBox(std::move(next));
|
||||||
|
@ -843,9 +891,9 @@ void Panel::raiseControls() {
|
||||||
void Panel::setupVideo(not_null<Viewport*> viewport) {
|
void Panel::setupVideo(not_null<Viewport*> viewport) {
|
||||||
const auto setupTile = [=](
|
const auto setupTile = [=](
|
||||||
const VideoEndpoint &endpoint,
|
const VideoEndpoint &endpoint,
|
||||||
const GroupCall::VideoTrack &track) {
|
const std::unique_ptr<GroupCall::VideoTrack> &track) {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
const auto row = _members->lookupRow(track.peer);
|
const auto row = _members->lookupRow(GroupCall::TrackPeer(track));
|
||||||
Assert(row != nullptr);
|
Assert(row != nullptr);
|
||||||
auto pinned = rpl::combine(
|
auto pinned = rpl::combine(
|
||||||
_call->videoEndpointLargeValue(),
|
_call->videoEndpointLargeValue(),
|
||||||
|
@ -853,8 +901,8 @@ void Panel::setupVideo(not_null<Viewport*> viewport) {
|
||||||
) | rpl::map(_1 == endpoint && _2);
|
) | rpl::map(_1 == endpoint && _2);
|
||||||
viewport->add(
|
viewport->add(
|
||||||
endpoint,
|
endpoint,
|
||||||
VideoTileTrack{ track.track.get(), row },
|
VideoTileTrack{ GroupCall::TrackPointer(track), row },
|
||||||
track.trackSize.value(),
|
GroupCall::TrackSizeValue(track),
|
||||||
std::move(pinned));
|
std::move(pinned));
|
||||||
};
|
};
|
||||||
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
|
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
|
||||||
|
@ -908,18 +956,24 @@ void Panel::toggleWideControls(bool shown) {
|
||||||
}
|
}
|
||||||
_showWideControls = shown;
|
_showWideControls = shown;
|
||||||
crl::on_main(widget(), [=] {
|
crl::on_main(widget(), [=] {
|
||||||
if (_wideControlsShown == _showWideControls) {
|
updateWideControlsVisibility();
|
||||||
return;
|
|
||||||
}
|
|
||||||
_wideControlsShown = _showWideControls;
|
|
||||||
_wideControlsAnimation.start(
|
|
||||||
[=] { updateButtonsGeometry(); },
|
|
||||||
_wideControlsShown ? 0. : 1.,
|
|
||||||
_wideControlsShown ? 1. : 0.,
|
|
||||||
st::slideWrapDuration);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::updateWideControlsVisibility() {
|
||||||
|
const auto shown = _showWideControls
|
||||||
|
|| (_stickedTooltipClose != nullptr);
|
||||||
|
if (_wideControlsShown == shown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_wideControlsShown = shown;
|
||||||
|
_wideControlsAnimation.start(
|
||||||
|
[=] { updateButtonsGeometry(); },
|
||||||
|
_wideControlsShown ? 0. : 1.,
|
||||||
|
_wideControlsShown ? 1. : 0.,
|
||||||
|
st::slideWrapDuration);
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||||
const auto validateRecordingMark = [=](bool recording) {
|
const auto validateRecordingMark = [=](bool recording) {
|
||||||
if (!recording && _recordingMark) {
|
if (!recording && _recordingMark) {
|
||||||
|
@ -988,6 +1042,7 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||||
_call->isSharingCameraValue()
|
_call->isSharingCameraValue()
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
refreshVideoButtons();
|
refreshVideoButtons();
|
||||||
|
showStickedTooltip();
|
||||||
}, widget()->lifetime());
|
}, widget()->lifetime());
|
||||||
|
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
|
@ -1373,6 +1428,7 @@ bool Panel::updateMode() {
|
||||||
updateButtonsStyles();
|
updateButtonsStyles();
|
||||||
refreshControlsBackground();
|
refreshControlsBackground();
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
|
showStickedTooltip();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1557,8 +1613,12 @@ void Panel::trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
|
void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
|
||||||
if (_niceTooltip) {
|
if (_stickedTooltipClose) {
|
||||||
_niceTooltip.release()->toggleAnimated(false);
|
if (!over) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hideNiceTooltip();
|
||||||
}
|
}
|
||||||
if (over) {
|
if (over) {
|
||||||
Ui::Integration::Instance().registerLeaveSubscription(control);
|
Ui::Integration::Instance().registerLeaveSubscription(control);
|
||||||
|
@ -1569,7 +1629,37 @@ void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
|
||||||
toggleWideControls(over);
|
toggleWideControls(over);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::showNiceTooltip(not_null<Ui::RpWidget*> control) {
|
void Panel::showStickedTooltip() {
|
||||||
|
static const auto kHasCamera = !Webrtc::GetVideoInputList().empty();
|
||||||
|
if (!(_stickedTooltipsShown & StickedTooltip::Camera)
|
||||||
|
&& (_mode.current() == PanelMode::Wide)
|
||||||
|
&& _video
|
||||||
|
&& _call->videoIsWorking()
|
||||||
|
&& !_call->mutedByAdmin()
|
||||||
|
&& kHasCamera) { // Don't recount this every time for now.
|
||||||
|
showNiceTooltip(_video, NiceTooltipType::Sticked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hideStickedTooltip(
|
||||||
|
StickedTooltip::Camera,
|
||||||
|
StickedTooltipHide::Unavailable);
|
||||||
|
|
||||||
|
if (!(_stickedTooltipsShown & StickedTooltip::Microphone)
|
||||||
|
&& (_mode.current() == PanelMode::Wide)
|
||||||
|
&& _mute
|
||||||
|
&& !_call->mutedByAdmin()
|
||||||
|
&& false) { // Check if there is incoming sound.
|
||||||
|
showNiceTooltip(_mute->outer(), NiceTooltipType::Sticked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hideStickedTooltip(
|
||||||
|
StickedTooltip::Microphone,
|
||||||
|
StickedTooltipHide::Unavailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::showNiceTooltip(
|
||||||
|
not_null<Ui::RpWidget*> control,
|
||||||
|
NiceTooltipType type) {
|
||||||
auto text = [&]() -> rpl::producer<QString> {
|
auto text = [&]() -> rpl::producer<QString> {
|
||||||
if (control == _screenShare.data()) {
|
if (control == _screenShare.data()) {
|
||||||
if (_call->mutedByAdmin()) {
|
if (_call->mutedByAdmin()) {
|
||||||
|
@ -1597,38 +1687,95 @@ void Panel::showNiceTooltip(not_null<Ui::RpWidget*> control) {
|
||||||
}();
|
}();
|
||||||
if (!text
|
if (!text
|
||||||
|| _wideControlsAnimation.animating()
|
|| _wideControlsAnimation.animating()
|
||||||
|| !_wideControlsShown) {
|
|| !_wideControlsShown
|
||||||
|
|| _stickedTooltipClose) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto inner = [&]() -> Ui::RpWidget* {
|
||||||
|
const auto normal = (type == NiceTooltipType::Normal);
|
||||||
|
auto container = normal
|
||||||
|
? nullptr
|
||||||
|
: Ui::CreateChild<Ui::RpWidget>(widget().get());
|
||||||
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
|
(normal ? widget().get() : container),
|
||||||
|
std::move(text),
|
||||||
|
st::groupCallNiceTooltipLabel);
|
||||||
|
if (normal) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
const auto button = Ui::CreateChild<Ui::IconButton>(
|
||||||
|
container,
|
||||||
|
st::groupCallStickedTooltipClose);
|
||||||
|
rpl::combine(
|
||||||
|
label->sizeValue(),
|
||||||
|
button->sizeValue()
|
||||||
|
) | rpl::start_with_next([=](QSize text, QSize close) {
|
||||||
|
const auto height = std::max(text.height(), close.height());
|
||||||
|
container->resize(text.width() + close.width(), height);
|
||||||
|
label->move(0, (height - text.height()) / 2);
|
||||||
|
button->move(text.width(), (height - close.height()) / 2);
|
||||||
|
}, container->lifetime());
|
||||||
|
button->setClickedCallback([=] {
|
||||||
|
hideStickedTooltip(StickedTooltipHide::Discarded);
|
||||||
|
});
|
||||||
|
_stickedTooltipClose = button;
|
||||||
|
updateWideControlsVisibility();
|
||||||
|
return container;
|
||||||
|
}();
|
||||||
_niceTooltip.create(
|
_niceTooltip.create(
|
||||||
widget().get(),
|
widget().get(),
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::RpWidget>::fromRaw(inner),
|
||||||
widget().get(),
|
(type == NiceTooltipType::Sticked
|
||||||
std::move(text),
|
? st::groupCallStickedTooltip
|
||||||
st::groupCallNiceTooltipLabel),
|
: st::groupCallNiceTooltip));
|
||||||
st::groupCallNiceTooltip);
|
|
||||||
const auto tooltip = _niceTooltip.data();
|
const auto tooltip = _niceTooltip.data();
|
||||||
const auto weak = QPointer<QWidget>(tooltip);
|
const auto weak = QPointer<QWidget>(tooltip);
|
||||||
const auto destroy = [=] {
|
const auto destroy = [=] {
|
||||||
delete weak.data();
|
delete weak.data();
|
||||||
};
|
};
|
||||||
tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
|
if (type != NiceTooltipType::Sticked) {
|
||||||
|
tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
}
|
||||||
tooltip->setHiddenCallback(destroy);
|
tooltip->setHiddenCallback(destroy);
|
||||||
base::qt_signal_producer(
|
base::qt_signal_producer(
|
||||||
control.get(),
|
control.get(),
|
||||||
&QObject::destroyed
|
&QObject::destroyed
|
||||||
) | rpl::start_with_next(destroy, tooltip->lifetime());
|
) | rpl::start_with_next(destroy, tooltip->lifetime());
|
||||||
|
|
||||||
const auto geometry = control->geometry();
|
_niceTooltipControl = control;
|
||||||
|
updateTooltipGeometry();
|
||||||
|
tooltip->toggleAnimated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::updateTooltipGeometry() {
|
||||||
|
if (!_niceTooltip) {
|
||||||
|
return;
|
||||||
|
} else if (!_niceTooltipControl) {
|
||||||
|
hideNiceTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto geometry = _niceTooltipControl->geometry();
|
||||||
|
const auto weak = QPointer<QWidget>(_niceTooltip);
|
||||||
const auto countPosition = [=](QSize size) {
|
const auto countPosition = [=](QSize size) {
|
||||||
const auto strong = weak.data();
|
const auto strong = weak.data();
|
||||||
if (!strong) {
|
const auto wide = (_mode.current() == PanelMode::Wide);
|
||||||
return QPoint();
|
|
||||||
}
|
|
||||||
const auto top = geometry.y()
|
const auto top = geometry.y()
|
||||||
- st::groupCallNiceTooltipTop
|
- (wide ? st::groupCallNiceTooltipTop : 0)
|
||||||
- size.height();
|
- size.height();
|
||||||
const auto middle = geometry.center().x();
|
const auto middle = geometry.center().x();
|
||||||
|
if (!strong) {
|
||||||
|
return QPoint();
|
||||||
|
} else if (!wide) {
|
||||||
|
return QPoint(
|
||||||
|
std::max(
|
||||||
|
std::min(
|
||||||
|
middle - size.width() / 2,
|
||||||
|
(widget()->width()
|
||||||
|
- st::groupCallMembersMargin.right()
|
||||||
|
- size.width())),
|
||||||
|
st::groupCallMembersMargin.left()),
|
||||||
|
top);
|
||||||
|
}
|
||||||
const auto back = _controlsBackgroundWide.data();
|
const auto back = _controlsBackgroundWide.data();
|
||||||
if (size.width() >= _viewport->widget()->width()) {
|
if (size.width() >= _viewport->widget()->width()) {
|
||||||
return QPoint(_viewport->widget()->x(), top);
|
return QPoint(_viewport->widget()->x(), top);
|
||||||
|
@ -1645,8 +1792,7 @@ void Panel::showNiceTooltip(not_null<Ui::RpWidget*> control) {
|
||||||
return QPoint(middle - size.width() / 2, top);
|
return QPoint(middle - size.width() / 2, top);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
tooltip->pointAt(geometry, RectPart::Top, countPosition);
|
_niceTooltip->pointAt(geometry, RectPart::Top, countPosition);
|
||||||
tooltip->toggleAnimated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::trackControls(bool track) {
|
void Panel::trackControls(bool track) {
|
||||||
|
@ -1832,6 +1978,7 @@ void Panel::updateButtonsGeometry() {
|
||||||
width,
|
width,
|
||||||
st::groupCallMembersBottomSkip);
|
st::groupCallMembersBottomSkip);
|
||||||
}
|
}
|
||||||
|
updateTooltipGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Panel::videoButtonInNarrowMode() const {
|
bool Panel::videoButtonInNarrowMode() const {
|
||||||
|
|
|
@ -84,6 +84,24 @@ private:
|
||||||
using State = GroupCall::State;
|
using State = GroupCall::State;
|
||||||
struct ControlsBackgroundNarrow;
|
struct ControlsBackgroundNarrow;
|
||||||
|
|
||||||
|
enum class NiceTooltipType {
|
||||||
|
Normal,
|
||||||
|
Sticked,
|
||||||
|
};
|
||||||
|
enum class StickedTooltip {
|
||||||
|
Camera = 0x01,
|
||||||
|
Microphone = 0x02,
|
||||||
|
};
|
||||||
|
friend constexpr inline bool is_flag_type(StickedTooltip) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
using StickedTooltips = base::flags<StickedTooltip>;
|
||||||
|
enum class StickedTooltipHide {
|
||||||
|
Unavailable,
|
||||||
|
Activated,
|
||||||
|
Discarded,
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<Ui::Window> createWindow();
|
std::unique_ptr<Ui::Window> createWindow();
|
||||||
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
|
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
|
||||||
|
|
||||||
|
@ -111,11 +129,18 @@ private:
|
||||||
|
|
||||||
void trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime);
|
void trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime);
|
||||||
void trackControlOver(not_null<Ui::RpWidget*> control, bool over);
|
void trackControlOver(not_null<Ui::RpWidget*> control, bool over);
|
||||||
void showNiceTooltip(not_null<Ui::RpWidget*> control);
|
void showNiceTooltip(
|
||||||
|
not_null<Ui::RpWidget*> control,
|
||||||
|
NiceTooltipType type = NiceTooltipType::Normal);
|
||||||
|
void showStickedTooltip();
|
||||||
|
void hideStickedTooltip(StickedTooltipHide hide);
|
||||||
|
void hideStickedTooltip(StickedTooltip type, StickedTooltipHide hide);
|
||||||
|
void hideNiceTooltip();
|
||||||
|
|
||||||
bool updateMode();
|
bool updateMode();
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void updateButtonsGeometry();
|
void updateButtonsGeometry();
|
||||||
|
void updateTooltipGeometry();
|
||||||
void updateButtonsStyles();
|
void updateButtonsStyles();
|
||||||
void updateMembersGeometry();
|
void updateMembersGeometry();
|
||||||
void refreshControlsBackground();
|
void refreshControlsBackground();
|
||||||
|
@ -127,6 +152,7 @@ private:
|
||||||
std::optional<bool> overrideWideMode = std::nullopt);
|
std::optional<bool> overrideWideMode = std::nullopt);
|
||||||
void refreshTopButton();
|
void refreshTopButton();
|
||||||
void toggleWideControls(bool shown);
|
void toggleWideControls(bool shown);
|
||||||
|
void updateWideControlsVisibility();
|
||||||
[[nodiscard]] bool videoButtonInNarrowMode() const;
|
[[nodiscard]] bool videoButtonInNarrowMode() const;
|
||||||
|
|
||||||
void endCall();
|
void endCall();
|
||||||
|
@ -202,6 +228,9 @@ private:
|
||||||
std::unique_ptr<Ui::CallMuteButton> _mute;
|
std::unique_ptr<Ui::CallMuteButton> _mute;
|
||||||
object_ptr<Ui::CallButton> _hangup;
|
object_ptr<Ui::CallButton> _hangup;
|
||||||
object_ptr<Ui::ImportantTooltip> _niceTooltip = { nullptr };
|
object_ptr<Ui::ImportantTooltip> _niceTooltip = { nullptr };
|
||||||
|
QPointer<Ui::IconButton> _stickedTooltipClose;
|
||||||
|
QPointer<Ui::RpWidget> _niceTooltipControl;
|
||||||
|
StickedTooltips _stickedTooltipsShown;
|
||||||
Fn<void()> _callShareLinkCallback;
|
Fn<void()> _callShareLinkCallback;
|
||||||
|
|
||||||
const std::unique_ptr<Toasts> _toasts;
|
const std::unique_ptr<Toasts> _toasts;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d8abc60245236e388206e2021bfeb58e83e504fc
|
Subproject commit 825ef11f1a5c6b00cd571c24525c84dca431eaa2
|
Loading…
Add table
Reference in a new issue