Hide member rows with active small videos.

This commit is contained in:
John Preston 2021-05-30 17:51:04 +04:00
parent 8a693bc932
commit 38506d27a1
14 changed files with 359 additions and 85 deletions

View file

@ -776,6 +776,10 @@ void PeerListContent::appendRow(std::unique_ptr<PeerListRow> row) {
if (_rowsById.find(row->id()) == _rowsById.cend()) {
row->setAbsoluteIndex(_rows.size());
addRowEntry(row.get());
if (!_hiddenRows.empty()) {
Assert(!row->hidden());
_filterResults.push_back(row.get());
}
_rows.push_back(std::move(row));
}
}
@ -813,6 +817,17 @@ void PeerListContent::changeCheckState(
[=] { updateRow(row); });
}
void PeerListContent::setRowHidden(not_null<PeerListRow*> row, bool hidden) {
Expects(!row->isSearchResult());
row->setHidden(hidden);
if (hidden) {
_hiddenRows.emplace(row);
} else {
_hiddenRows.remove(row);
}
}
void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
if (_controller->respectSavedMessagesChat() && !row->special()) {
if (row->peer()->isSelf()) {
@ -879,6 +894,10 @@ void PeerListContent::prependRow(std::unique_ptr<PeerListRow> row) {
if (_rowsById.find(row->id()) == _rowsById.cend()) {
addRowEntry(row.get());
if (!_hiddenRows.empty()) {
Assert(!row->hidden());
_filterResults.insert(_filterResults.begin(), row.get());
}
_rows.insert(_rows.begin(), std::move(row));
refreshIndices();
}
@ -894,6 +913,10 @@ void PeerListContent::prependRowFromSearchResult(not_null<PeerListRow*> row) {
Assert(_searchRows[index].get() == row);
row->setIsSearchResult(false);
if (!_hiddenRows.empty()) {
Assert(!row->hidden());
_filterResults.insert(_filterResults.begin(), row);
}
_rows.insert(_rows.begin(), std::move(_searchRows[index]));
refreshIndices();
removeRowAtIndex(_searchRows, index);
@ -947,6 +970,7 @@ void PeerListContent::removeRow(not_null<PeerListRow*> row) {
_filterResults.erase(
ranges::remove(_filterResults, row),
end(_filterResults));
_hiddenRows.remove(row);
removeRowAtIndex(eraseFrom, index);
restoreSelection();
@ -984,7 +1008,9 @@ void PeerListContent::convertRowToSearchResult(not_null<PeerListRow*> row) {
removeFromSearchIndex(row);
row->setIsSearchResult(true);
row->setHidden(false);
row->setAbsoluteIndex(_searchRows.size());
_hiddenRows.remove(row);
_searchRows.push_back(std::move(_rows[index]));
removeRowAtIndex(_rows, index);
}
@ -1069,6 +1095,14 @@ int PeerListContent::labelHeight() const {
}
void PeerListContent::refreshRows() {
if (!_hiddenRows.empty()) {
_filterResults.clear();
for (const auto &row : _rows) {
if (!row->hidden()) {
_filterResults.push_back(row.get());
}
}
}
resizeToWidth(width());
if (_visibleBottom > 0) {
checkScrollForPreload();
@ -1082,7 +1116,7 @@ void PeerListContent::refreshRows() {
void PeerListContent::setSearchMode(PeerListSearchMode mode) {
if (_searchMode != mode) {
if (!addingToSearchIndex()) {
for_const (auto &row, _rows) {
for (const auto &row : _rows) {
addToSearchIndex(row.get());
}
}
@ -1284,13 +1318,22 @@ void PeerListContent::mousePressReleased(Qt::MouseButton button) {
void PeerListContent::showRowMenu(
not_null<PeerListRow*> row,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed) {
showRowMenu(findRowIndex(row), QCursor::pos(), std::move(destroyed));
const auto index = findRowIndex(row);
showRowMenu(
index,
row,
QCursor::pos(),
highlightRow,
std::move(destroyed));
}
bool PeerListContent::showRowMenu(
RowIndex index,
PeerListRow *row,
QPoint globalPos,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed) {
if (_contextMenu) {
_contextMenu->setDestroyedCallback(nullptr);
@ -1301,7 +1344,9 @@ bool PeerListContent::showRowMenu(
mousePressReleased(_pressButton);
}
const auto row = getRow(index);
if (highlightRow) {
row = getRow(index);
}
if (!row) {
return false;
}
@ -1312,11 +1357,15 @@ bool PeerListContent::showRowMenu(
return false;
}
setContexted({ index, false });
if (highlightRow) {
setContexted({ index, false });
}
raw->setDestroyedCallback(crl::guard(
this,
[=] {
setContexted(Selected());
if (highlightRow) {
setContexted(Selected());
}
handleMouseMove(QCursor::pos());
if (destroyed) {
destroyed(raw);
@ -1330,7 +1379,7 @@ void PeerListContent::contextMenuEvent(QContextMenuEvent *e) {
if (e->reason() == QContextMenuEvent::Mouse) {
handleMouseMove(e->globalPos());
}
if (showRowMenu(_selected.index, e->globalPos())) {
if (showRowMenu(_selected.index, nullptr, e->globalPos(), true)) {
e->accept();
}
}
@ -1607,6 +1656,8 @@ void PeerListContent::searchQueryChanged(QString query) {
if (_normalizedSearchQuery != normalizedQuery) {
setSearchQuery(query, normalizedQuery);
if (_controller->searchInLocal() && !searchWordsList.isEmpty()) {
Assert(_hiddenRows.empty());
auto minimalList = (const std::vector<not_null<PeerListRow*>>*)nullptr;
for (const auto &searchWord : searchWordsList) {
auto searchWordStart = searchWord[0].toLower();
@ -1656,15 +1707,17 @@ void PeerListContent::searchQueryChanged(QString query) {
}
std::unique_ptr<PeerListState> PeerListContent::saveState() const {
Expects(_hiddenRows.empty());
auto result = std::make_unique<PeerListState>();
result->controllerState
= std::make_unique<PeerListController::SavedStateBase>();
result->list.reserve(_rows.size());
for (auto &row : _rows) {
for (const auto &row : _rows) {
result->list.push_back(row->peer());
}
result->filterResults.reserve(_filterResults.size());
for (auto &row : _filterResults) {
for (const auto &row : _filterResults) {
result->filterResults.push_back(row->peer());
}
result->searchQuery = _searchQuery;
@ -1844,8 +1897,7 @@ void PeerListContent::updateRow(RowIndex index) {
if (index.value < 0) {
return;
}
auto row = getRow(index);
if (row->disabled()) {
if (const auto row = getRow(index); row && row->disabled()) {
if (index == _selected.index) {
setSelected(Selected());
}
@ -1940,3 +1992,10 @@ PeerListContent::~PeerListContent() {
_contextMenu->setDestroyedCallback(nullptr);
}
}
void PeerListContentDelegate::peerListShowRowMenu(
not_null<PeerListRow*> row,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu *>)> destroyed) {
_content->showRowMenu(row, highlightRow, std::move(destroyed));
}

View file

@ -161,7 +161,7 @@ public:
template <typename UpdateCallback>
void setChecked(
bool checked,
const style::RoundImageCheckbox &st,
const style::RoundImageCheckbox &st,
anim::type animated,
UpdateCallback callback) {
if (checked && !_checkbox) {
@ -169,6 +169,12 @@ public:
}
setCheckedInternal(checked, animated);
}
void setHidden(bool hidden) {
_hidden = hidden;
}
[[nodiscard]] bool hidden() const {
return _hidden;
}
void finishCheckedAnimation();
void invalidatePixmapsCache();
@ -237,6 +243,7 @@ private:
base::flat_set<QChar> _nameFirstLetters;
int _absoluteIndex = -1;
State _disabledState = State::Active;
bool _hidden = false;
bool _initialized : 1;
bool _isSearchResult : 1;
bool _isSavedMessagesChat : 1;
@ -274,6 +281,7 @@ public:
virtual void peerListConvertRowToSearchResult(not_null<PeerListRow*> row) = 0;
virtual bool peerListIsRowChecked(not_null<PeerListRow*> row) = 0;
virtual void peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) = 0;
virtual void peerListSetRowHidden(not_null<PeerListRow*> row, bool hidden) = 0;
virtual void peerListSetForeignRowChecked(
not_null<PeerListRow*> row,
bool checked,
@ -304,6 +312,7 @@ public:
virtual void peerListShowRowMenu(
not_null<PeerListRow*> row,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) = 0;
virtual int peerListSelectedRowsCount() = 0;
virtual std::unique_ptr<PeerListState> peerListSaveState() const = 0;
@ -577,6 +586,9 @@ public:
not_null<PeerListRow*> row,
bool checked,
anim::type animated);
void setRowHidden(
not_null<PeerListRow*> row,
bool hidden);
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback) {
@ -585,6 +597,9 @@ public:
callback(searchEntity.second.begin(), searchEntity.second.end());
}
refreshIndices();
if (!_hiddenRows.empty()) {
callback(_filterResults.begin(), _filterResults.end());
}
update();
}
@ -593,6 +608,7 @@ public:
void showRowMenu(
not_null<PeerListRow*> row,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed);
auto scrollToRequests() const {
@ -680,7 +696,9 @@ private:
bool showRowMenu(
RowIndex index,
PeerListRow *row,
QPoint globalPos,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr);
crl::time paintRow(Painter &p, crl::time now, RowIndex index);
@ -691,7 +709,7 @@ private:
void removeFromSearchIndex(not_null<PeerListRow*> row);
void setSearchQuery(const QString &query, const QString &normalizedQuery);
bool showingSearch() const {
return !_searchQuery.isEmpty();
return !_hiddenRows.empty() || !_searchQuery.isEmpty();
}
int shownRowsCount() const {
return showingSearch() ? _filterResults.size() : _rows.size();
@ -737,6 +755,7 @@ private:
QString _normalizedSearchQuery;
QString _mentionHighlight;
std::vector<not_null<PeerListRow*>> _filterResults;
base::flat_set<not_null<PeerListRow*>> _hiddenRows;
int _aboveHeight = 0;
int _belowHeight = 0;
@ -801,6 +820,11 @@ public:
bool checked) override {
_content->changeCheckState(row, checked, anim::type::normal);
}
void peerListSetRowHidden(
not_null<PeerListRow*> row,
bool hidden) override {
_content->setRowHidden(row, hidden);
}
void peerListSetForeignRowChecked(
not_null<PeerListRow*> row,
bool checked,
@ -871,10 +895,9 @@ public:
_content->restoreState(std::move(state));
}
void peerListShowRowMenu(
not_null<PeerListRow*> row,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override {
_content->showRowMenu(row, std::move(destroyed));
}
not_null<PeerListRow*> row,
bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
protected:
not_null<PeerListContent*> content() const {

View file

@ -533,7 +533,7 @@ void LinksController::rowClicked(not_null<PeerListRow*> row) {
}
void LinksController::rowActionClicked(not_null<PeerListRow*> row) {
delegate()->peerListShowRowMenu(row, nullptr);
delegate()->peerListShowRowMenu(row, true);
}
base::unique_qptr<Ui::PopupMenu> LinksController::rowContextMenu(

View file

@ -446,7 +446,7 @@ void Panel::refreshIncomingGeometry() {
to,
Qt::KeepAspectRatioByExpanding);
// If we cut out no more than 0.33 of the original, let's use expanding.
// If we cut out no more than 0.25 of the original, let's use expanding.
const auto use = ((big.width() * 3 <= to.width() * 4)
&& (big.height() * 3 <= to.height() * 4))
? big

View file

@ -529,11 +529,15 @@ void GroupCall::toggleScreenSharing(std::optional<QString> uniqueId) {
}
bool GroupCall::hasVideoWithFrames() const {
return _hasVideoWithFrames.current();
return !_shownVideoTracks.empty();
}
rpl::producer<bool> GroupCall::hasVideoWithFramesValue() const {
return _hasVideoWithFrames.value();
return _videoStreamShownUpdates.events_starting_with(
VideoActiveToggle()
) | rpl::map([=] {
return hasVideoWithFrames();
}) | rpl::distinct_until_changed();
}
void GroupCall::setScheduledDate(TimeId date) {
@ -586,8 +590,16 @@ void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
? regularEndpoint(data.now->cameraEndpoint())
: EmptyString();
if (wasCameraEndpoint != nowCameraEndpoint) {
markEndpointActive({ peer, nowCameraEndpoint }, true);
markEndpointActive({ peer, wasCameraEndpoint }, false);
markEndpointActive({
VideoEndpointType::Camera,
peer,
nowCameraEndpoint
}, true);
markEndpointActive({
VideoEndpointType::Camera,
peer,
wasCameraEndpoint
}, false);
}
const auto &wasScreenEndpoint = data.was
? regularEndpoint(data.was->screenEndpoint())
@ -596,8 +608,16 @@ void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
? regularEndpoint(data.now->screenEndpoint())
: EmptyString();
if (wasScreenEndpoint != nowScreenEndpoint) {
markEndpointActive({ peer, nowScreenEndpoint }, true);
markEndpointActive({ peer, wasScreenEndpoint }, false);
markEndpointActive({
VideoEndpointType::Screen,
peer,
nowScreenEndpoint
}, true);
markEndpointActive({
VideoEndpointType::Screen,
peer,
wasScreenEndpoint
}, false);
}
}, _lifetime);
@ -792,14 +812,22 @@ void GroupCall::setScreenEndpoint(std::string endpoint) {
return;
}
if (!_screenEndpoint.empty()) {
markEndpointActive({ _joinAs, _screenEndpoint }, false);
markEndpointActive({
VideoEndpointType::Screen,
_joinAs,
_screenEndpoint
}, false);
}
_screenEndpoint = std::move(endpoint);
if (_screenEndpoint.empty()) {
return;
}
if (isSharingScreen()) {
markEndpointActive({ _joinAs, _screenEndpoint }, true);
markEndpointActive({
VideoEndpointType::Screen,
_joinAs,
_screenEndpoint
}, true);
}
}
@ -808,14 +836,22 @@ void GroupCall::setCameraEndpoint(std::string endpoint) {
return;
}
if (!_cameraEndpoint.empty()) {
markEndpointActive({ _joinAs, _cameraEndpoint }, false);
markEndpointActive({
VideoEndpointType::Camera,
_joinAs,
_cameraEndpoint
}, false);
}
_cameraEndpoint = std::move(endpoint);
if (_cameraEndpoint.empty()) {
return;
}
if (isSharingCamera()) {
markEndpointActive({ _joinAs, _cameraEndpoint }, true);
markEndpointActive({
VideoEndpointType::Camera,
_joinAs,
_cameraEndpoint
}, true);
}
}
@ -848,7 +884,7 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) {
if (!changed) {
return;
}
auto hasVideoWithFrames = _hasVideoWithFrames.current();
auto shown = false;
if (active) {
const auto i = _activeVideoTracks.emplace(
endpoint,
@ -862,11 +898,11 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) {
track->renderNextFrame(
) | rpl::start_with_next([=] {
if (!track->frameSize().isEmpty()) {
_hasVideoWithFrames = true;
markTrackShown(endpoint, true);
}
}, i->second.lifetime);
if (!track->frameSize().isEmpty()) {
hasVideoWithFrames = true;
shown = true;
}
addVideoOutput(i->first.id, { track->sink() });
} else {
@ -874,17 +910,17 @@ void GroupCall::markEndpointActive(VideoEndpoint endpoint, bool active) {
_videoEndpointPinned = VideoEndpoint();
}
_activeVideoTracks.erase(i);
hasVideoWithFrames = false;
for (const auto &[endpoint, track] : _activeVideoTracks) {
if (!track.track->frameSize().isEmpty()) {
hasVideoWithFrames = true;
break;
}
}
}
updateRequestedVideoChannelsDelayed();
_videoStreamActiveUpdates.fire(std::move(endpoint));
_hasVideoWithFrames = hasVideoWithFrames;
_videoStreamActiveUpdates.fire({ endpoint, active });
markTrackShown(endpoint, shown);
}
void GroupCall::markTrackShown(const VideoEndpoint &endpoint, bool shown) {
if ((shown && _shownVideoTracks.emplace(endpoint).second)
|| (!shown && _shownVideoTracks.remove(endpoint))) {
_videoStreamShownUpdates.fire_copy({ endpoint, shown });
}
}
void GroupCall::rejoin() {
@ -1746,7 +1782,11 @@ void GroupCall::ensureOutgoingVideo() {
_cameraCapture->setState(tgcalls::VideoState::Inactive);
}
_isSharingCamera = active;
markEndpointActive({ _joinAs, _cameraEndpoint }, active);
markEndpointActive({
VideoEndpointType::Camera,
_joinAs,
_cameraEndpoint
}, active);
sendSelfUpdate(SendUpdateType::VideoMuted);
applyMeInCallLocally();
}, _lifetime);
@ -1785,7 +1825,11 @@ void GroupCall::ensureOutgoingVideo() {
_screenCapture->setState(tgcalls::VideoState::Inactive);
}
_isSharingScreen = active;
markEndpointActive({ _joinAs, _screenEndpoint }, active);
markEndpointActive({
VideoEndpointType::Screen,
_joinAs,
_screenEndpoint
}, active);
_screenJoinState.nextActionPending = true;
checkNextJoinAction();
}, _lifetime);
@ -2172,22 +2216,23 @@ void GroupCall::fillActiveVideoEndpoints() {
markEndpointActive(std::move(endpoint), true);
}
};
using Type = VideoEndpointType;
for (const auto &participant : participants) {
const auto camera = participant.cameraEndpoint();
if (camera != _cameraEndpoint
&& camera != _screenEndpoint
&& participant.peer != _joinAs) {
feedOne({ participant.peer, camera });
feedOne({ Type::Camera, participant.peer, camera });
}
const auto screen = participant.screenEndpoint();
if (screen != _cameraEndpoint
&& screen != _screenEndpoint
&& participant.peer != _joinAs) {
feedOne({ participant.peer, screen });
feedOne({ Type::Screen, participant.peer, screen });
}
}
feedOne({ _joinAs, cameraSharingEndpoint() });
feedOne({ _joinAs, screenSharingEndpoint() });
feedOne({ Type::Camera, _joinAs, cameraSharingEndpoint() });
feedOne({ Type::Screen, _joinAs, screenSharingEndpoint() });
if (pinned && !pinnedFound) {
_videoEndpointPinned = VideoEndpoint();
}

View file

@ -76,7 +76,13 @@ struct LevelUpdate {
bool me = false;
};
enum class VideoEndpointType {
Camera,
Screen,
};
struct VideoEndpoint {
VideoEndpointType type = VideoEndpointType::Camera;
PeerData *peer = nullptr;
std::string id;
@ -130,6 +136,11 @@ struct VideoPinToggle {
bool pinned = false;
};
struct VideoActiveToggle {
VideoEndpoint endpoint;
bool active = false;
};
struct VideoQualityRequest {
VideoEndpoint endpoint;
Group::VideoQuality quality = Group::VideoQuality();
@ -283,9 +294,13 @@ public:
return _levelUpdates.events();
}
[[nodiscard]] auto videoStreamActiveUpdates() const
-> rpl::producer<VideoEndpoint> {
-> rpl::producer<VideoActiveToggle> {
return _videoStreamActiveUpdates.events();
}
[[nodiscard]] auto videoStreamShownUpdates() const
-> rpl::producer<VideoActiveToggle> {
return _videoStreamShownUpdates.events();
}
void pinVideoEndpoint(VideoEndpoint endpoint);
void requestVideoQuality(
const VideoEndpoint &endpoint,
@ -317,6 +332,10 @@ public:
-> const base::flat_map<VideoEndpoint, VideoTrack> & {
return _activeVideoTracks;
}
[[nodiscard]] auto shownVideoTracks() const
-> const base::flat_set<VideoEndpoint> & {
return _shownVideoTracks;
}
[[nodiscard]] rpl::producer<Group::RejoinEvent> rejoinEvents() const {
return _rejoinEvents.events();
}
@ -491,6 +510,7 @@ private:
void addVideoOutput(const std::string &endpoint, SinkPointer sink);
void markEndpointActive(VideoEndpoint endpoint, bool active);
void markTrackShown(const VideoEndpoint &endpoint, bool shown);
[[nodiscard]] MTPInputGroupCall inputCall() const;
@ -560,10 +580,11 @@ private:
bool _requireARGB32 = true;
rpl::event_stream<LevelUpdate> _levelUpdates;
rpl::event_stream<VideoEndpoint> _videoStreamActiveUpdates;
rpl::event_stream<VideoActiveToggle> _videoStreamActiveUpdates;
rpl::event_stream<VideoActiveToggle> _videoStreamShownUpdates;
base::flat_map<VideoEndpoint, VideoTrack> _activeVideoTracks;
base::flat_set<VideoEndpoint> _shownVideoTracks;
rpl::variable<VideoEndpoint> _videoEndpointPinned;
rpl::variable<bool> _hasVideoWithFrames = false;
base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke;
rpl::event_stream<Group::RejoinEvent> _rejoinEvents;
rpl::event_stream<> _allowedToSpeakNotifications;

View file

@ -171,7 +171,7 @@ private:
// not_null<Row*> row,
// const std::string &endpoint);
bool toggleRowVideo(not_null<PeerListRow*> row);
void showRowMenu(not_null<PeerListRow*> row);
void showRowMenu(not_null<PeerListRow*> row, bool highlightRow);
void toggleVideoEndpointActive(
const VideoEndpoint &endpoint,
@ -180,6 +180,11 @@ private:
void appendInvitedUsers();
void scheduleRaisedHandStatusRemove();
void hideRowsWithVideoExcept(const VideoEndpoint &pinned);
void showAllHiddenRows();
void hideRowWithVideo(const VideoEndpoint &endpoint);
void showRowWithVideo(const VideoEndpoint &endpoint);
const not_null<GroupCall*> _call;
not_null<PeerData*> _peer;
std::string _largeEndpoint;
@ -412,6 +417,27 @@ void Members::Controller::setupListChangeViewers() {
// }
//}, _lifetime);
_call->videoEndpointPinnedValue(
) | rpl::start_with_next([=](const VideoEndpoint &pinned) {
if (pinned) {
hideRowsWithVideoExcept(pinned);
} else {
showAllHiddenRows();
}
}, _lifetime);
_call->videoStreamShownUpdates(
) | rpl::filter([=](const VideoActiveToggle &update) {
const auto &pinned = _call->videoEndpointPinned();
return pinned && (update.endpoint != pinned);
}) | rpl::start_with_next([=](const VideoActiveToggle &update) {
if (update.active) {
hideRowWithVideo(update.endpoint);
} else {
showRowWithVideo(update.endpoint);
}
}, _lifetime);
_call->rejoinEvents(
) | rpl::start_with_next([=](const Group::RejoinEvent &event) {
const auto guard = gsl::finally([&] {
@ -428,6 +454,59 @@ void Members::Controller::setupListChangeViewers() {
}, _lifetime);
}
void Members::Controller::hideRowsWithVideoExcept(
const VideoEndpoint &pinned) {
auto hidden = false;
for (const auto &endpoint : _call->shownVideoTracks()) {
if (endpoint != pinned) {
if (const auto row = findRow(endpoint.peer)) {
delegate()->peerListSetRowHidden(row, true);
hidden = true;
}
}
}
if (hidden) {
delegate()->peerListRefreshRows();
}
}
void Members::Controller::showAllHiddenRows() {
auto shown = false;
for (const auto &endpoint : _call->shownVideoTracks()) {
if (const auto row = findRow(endpoint.peer)) {
delegate()->peerListSetRowHidden(row, false);
shown = true;
}
}
if (shown) {
delegate()->peerListRefreshRows();
}
}
void Members::Controller::hideRowWithVideo(const VideoEndpoint &endpoint) {
if (const auto row = findRow(endpoint.peer)) {
delegate()->peerListSetRowHidden(row, true);
delegate()->peerListRefreshRows();
}
}
void Members::Controller::showRowWithVideo(const VideoEndpoint &endpoint) {
const auto peer = endpoint.peer;
const auto &pinned = _call->videoEndpointPinned();
if (pinned) {
for (const auto &endpoint : _call->shownVideoTracks()) {
if (endpoint != pinned && endpoint.peer == peer) {
// Still hidden with another video.
return;
}
}
}
if (const auto row = findRow(endpoint.peer)) {
delegate()->peerListSetRowHidden(row, false);
delegate()->peerListRefreshRows();
}
}
void Members::Controller::subscribeToChanges(not_null<Data::GroupCall*> real) {
_fullCount = real->fullCountValue();
@ -463,9 +542,8 @@ void Members::Controller::subscribeToChanges(not_null<Data::GroupCall*> real) {
toggleVideoEndpointActive(endpoint, true);
}
_call->videoStreamActiveUpdates(
) | rpl::start_with_next([=](const VideoEndpoint &endpoint) {
const auto active = _call->activeVideoTracks().contains(endpoint);
toggleVideoEndpointActive(endpoint, active);
) | rpl::start_with_next([=](const VideoActiveToggle &update) {
toggleVideoEndpointActive(update.endpoint, update.active);
}, _lifetime);
if (_prepared) {
@ -822,9 +900,8 @@ Main::Session &Members::Controller::session() const {
void Members::Controller::prepare() {
delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled);
//delegate()->peerListSetTitle(std::move(title));
setDescriptionText(tr::lng_contacts_loading(tr::now));
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
setDescription(nullptr);
setSearchNoResults(nullptr);
if (const auto real = _call->lookupReal()) {
prepareRows(real);
@ -1155,7 +1232,7 @@ bool Members::Controller::rowIsNarrow() {
}
void Members::Controller::rowShowContextMenu(not_null<PeerListRow*> row) {
showRowMenu(row);
showRowMenu(row, false);
}
//void Members::Controller::rowPaintNarrowBackground(
@ -1253,12 +1330,14 @@ auto Members::Controller::kickParticipantRequests() const
void Members::Controller::rowClicked(not_null<PeerListRow*> row) {
if (!toggleRowVideo(row)) {
showRowMenu(row);
showRowMenu(row, true);
}
}
void Members::Controller::showRowMenu(not_null<PeerListRow*> row) {
delegate()->peerListShowRowMenu(row, [=](not_null<Ui::PopupMenu*> menu) {
void Members::Controller::showRowMenu(
not_null<PeerListRow*> row,
bool highlightRow) {
const auto cleanup = [=](not_null<Ui::PopupMenu*> menu) {
if (!_menu || _menu.get() != menu) {
return;
}
@ -1269,7 +1348,8 @@ void Members::Controller::showRowMenu(not_null<PeerListRow*> row) {
}
}
_menu = std::move(saved);
});
};
delegate()->peerListShowRowMenu(row, highlightRow, cleanup);
}
bool Members::Controller::toggleRowVideo(not_null<PeerListRow*> row) {
@ -1317,7 +1397,7 @@ bool Members::Controller::toggleRowVideo(not_null<PeerListRow*> row) {
void Members::Controller::rowActionClicked(
not_null<PeerListRow*> row) {
showRowMenu(row);
showRowMenu(row, true);
}
base::unique_qptr<Ui::PopupMenu> Members::Controller::rowContextMenu(
@ -1414,6 +1494,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
result->addAction(
tr::lng_group_call_context_pin_camera(tr::now),
[=] { _call->pinVideoEndpoint(VideoEndpoint{
VideoEndpointType::Camera,
participantPeer,
camera }); });
}
@ -1427,6 +1508,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
result->addAction(
tr::lng_group_call_context_pin_screen(tr::now),
[=] { _call->pinVideoEndpoint(VideoEndpoint{
VideoEndpointType::Screen,
participantPeer,
screen }); });
}
@ -1828,7 +1910,7 @@ QRect Members::getInnerGeometry() const {
0,
-_scroll->scrollTop(),
width(),
_list->y() + _list->height() + add);
_list->y() + _list->height() + _bottomSkip->height() + add);
}
rpl::producer<int> Members::fullCountValue() const {
@ -1837,18 +1919,38 @@ rpl::producer<int> Members::fullCountValue() const {
void Members::setupList() {
_listController->setStyleOverrides(&st::groupCallMembersList);
_topSkip = _layout->add(
object_ptr<Ui::FixedHeightWidget>(
_layout.get(),
st::groupCallMembersTopSkip));
_topSkip->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
QPainter(_topSkip).fillRect(clip, st::groupCallMembersBg);
}, _topSkip->lifetime());
const auto addSkip = [&] {
const auto result = _layout->add(
object_ptr<Ui::FixedHeightWidget>(
_layout.get(),
st::groupCallMembersTopSkip));
result->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
QPainter(result).fillRect(clip, st::groupCallMembersBg);
}, result->lifetime());
return result;
};
_topSkip = addSkip();
_list = _layout->add(
object_ptr<ListWidget>(
_layout.get(),
_listController.get()));
_bottomSkip = addSkip();
using namespace rpl::mappers;
rpl::combine(
_list->heightValue() | rpl::map(_1 > 0),
_addMemberButton.value() | rpl::map(_1 != nullptr)
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool hasList, bool hasAddMembers) {
_topSkip->resize(
_topSkip->width(),
hasList ? st::groupCallMembersTopSkip : 0);
_bottomSkip->resize(
_bottomSkip->width(),
(hasList && !hasAddMembers) ? st::groupCallMembersTopSkip : 0);
}, _list->lifetime());
const auto skip = _layout->add(object_ptr<Ui::RpWidget>(_layout.get()));
_mode.value(
) | rpl::start_with_next([=](PanelMode mode) {
@ -1997,6 +2099,7 @@ void Members::setupFakeRoundCorners() {
const auto bottom = top
+ _topSkip->height()
+ list.height()
+ _bottomSkip->height()
+ addMembers
- bottomleft->height();
topleft->move(left, top);

View file

@ -102,6 +102,7 @@ private:
rpl::event_stream<> _enlargeVideoClicks;
rpl::variable<Ui::RpWidget*> _addMemberButton = nullptr;
RpWidget *_topSkip = nullptr;
RpWidget *_bottomSkip = nullptr;
ListWidget *_list = nullptr;
rpl::event_stream<> _addMemberRequests;

View file

@ -399,7 +399,7 @@ bool MembersRow::paintVideo(
// .outer = QSize(sizew, sizeh) * cIntRetinaFactor(),
//};
//const auto frame = _videoTrackShown->frame(request);
//auto copy = frame; // #TODO calls optimize.
//auto copy = frame; // TODO calls optimize.
//copy.detach();
//if (mode == PanelMode::Default) {
// Images::prepareCircle(copy);

View file

@ -254,7 +254,7 @@ private:
//std::unique_ptr<Webrtc::VideoTrack> _videoTrack;
//Webrtc::VideoTrack *_videoTrackShown = nullptr;
//std::string _videoTrackEndpoint;
//rpl::lifetime _videoTrackLifetime; // #TODO calls move to unique_ptr.
//rpl::lifetime _videoTrackLifetime; // TODO calls move to unique_ptr.
Ui::Animations::Simple _speakingAnimation; // For gray-red/green icon.
Ui::Animations::Simple _mutedAnimation; // For gray/red icon.
Ui::Animations::Simple _activeAnimation; // For icon cross animation.

View file

@ -977,8 +977,14 @@ void Panel::setupMembers() {
_startsWhen.destroy();
_members.create(widget(), _call, mode());
setupVideo(_viewport.get());
setupVideo(_members->viewport());
_viewport->mouseInsideValue(
) | rpl::start_with_next([=](bool inside) {
toggleWideControls(inside);
}, _viewport->lifetime());
_members->show();
refreshControlsBackground();
@ -1135,9 +1141,10 @@ void Panel::setupVideo(not_null<Viewport*> viewport) {
setupTile(endpoint, track);
}
_call->videoStreamActiveUpdates(
) | rpl::start_with_next([=](const VideoEndpoint &endpoint) {
if (_call->activeVideoTracks().contains(endpoint)) {
) | rpl::start_with_next([=](const VideoActiveToggle &update) {
if (update.active) {
// Add async (=> the participant row is definitely in Members).
const auto endpoint = update.endpoint;
crl::on_main(viewport->widget(), [=] {
const auto &tracks = _call->activeVideoTracks();
const auto i = tracks.find(endpoint);
@ -1147,7 +1154,7 @@ void Panel::setupVideo(not_null<Viewport*> viewport) {
});
} else {
// Remove sync.
viewport->remove(endpoint);
viewport->remove(update.endpoint);
}
}, viewport->lifetime());
@ -1168,11 +1175,6 @@ void Panel::setupVideo(not_null<Viewport*> viewport) {
) | rpl::start_with_next([=](const VideoQualityRequest &request) {
_call->requestVideoQuality(request.endpoint, request.quality);
}, viewport->lifetime());
viewport->mouseInsideValue(
) | rpl::start_with_next([=](bool inside) {
toggleWideControls(inside);
}, viewport->lifetime());
}
void Panel::toggleWideControls(bool shown) {

View file

@ -26,8 +26,11 @@ constexpr auto kScaleForBlurTextureIndex = 3;
constexpr auto kFirstBlurPassTextureIndex = 4;
constexpr auto kBlurTextureSizeFactor = 1.7;
constexpr auto kBlurOpacity = 0.7;
constexpr auto kMinCameraVisiblePart = 0.75;
ShaderPart FragmentBlurTexture(bool vertical, char prefix = 'v') {
[[nodiscard]] ShaderPart FragmentBlurTexture(
bool vertical,
char prefix = 'v') {
const auto offsets = (vertical ? QString("0, 1") : QString("1, 0"));
const auto name = prefix + QString("_texcoord");
return {
@ -94,6 +97,16 @@ vec4 background() {
};
}
[[nodiscard]] bool UseExpandForCamera(QSize original, QSize viewport) {
const auto big = original.scaled(
viewport,
Qt::KeepAspectRatioByExpanding);
// If we cut out no more than 0.25 of the original, let's use expanding.
return (big.width() * kMinCameraVisiblePart <= viewport.width())
&& (big.height() * kMinCameraVisiblePart <= viewport.height());
}
[[nodiscard]] QSize NonEmpty(QSize size) {
return QSize(std::max(size.width(), 1), std::max(size.height(), 1));
}
@ -440,12 +453,14 @@ void Viewport::RendererGL::paintTile(
const auto unscaled = Media::View::FlipSizeByRotation(
data.yuv420->size,
data.rotation);
const auto tileSize = geometry.size();
const auto swap = (((data.rotation / 90) % 2) == 1);
const auto expand = !_owner->wide()/* && !tile->screencast()*/;
auto texCoords = CountTexCoords(unscaled, geometry.size(), expand, swap);
const auto expand = !tile->screencast()
&& (!_owner->wide() || UseExpandForCamera(unscaled, tileSize));
auto texCoords = CountTexCoords(unscaled, tileSize, expand, swap);
auto blurTexCoords = expand
? texCoords
: CountTexCoords(unscaled, geometry.size(), true);
: CountTexCoords(unscaled, tileSize, true);
const auto rect = transformRect(geometry);
auto toBlurTexCoords = std::array<std::array<GLfloat, 2>, 4> { {
{ { 0.f, 1.f } },

View file

@ -46,6 +46,10 @@ int Viewport::VideoTile::pinSlide() const {
_pinShownAnimation.value(_pinShown ? 1. : 0.));
}
bool Viewport::VideoTile::screencast() const {
return (_endpoint.type == VideoEndpointType::Screen);
}
void Viewport::VideoTile::setGeometry(QRect geometry) {
_geometry = geometry;
updatePinnedGeometry();

View file

@ -53,6 +53,7 @@ public:
return _trackSize.value();
}
[[nodiscard]] bool screencast() const;
void setGeometry(QRect geometry);
void togglePinShown(bool shown);
bool updateRequestedQuality(VideoQuality quality);