Add Back button to large video tiles.

This commit is contained in:
John Preston 2021-05-30 21:08:54 +04:00
parent 090d7d7112
commit 8bde53cd0f
12 changed files with 220 additions and 44 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

View file

@ -1202,6 +1202,7 @@ GroupCallVideoTile {
pinPosition: point; pinPosition: point;
pinPadding: margins; pinPadding: margins;
pinTextPosition: point; pinTextPosition: point;
back: icon;
iconPosition: point; iconPosition: point;
} }
@ -1219,6 +1220,7 @@ groupCallVideoTile: GroupCallVideoTile {
pinPosition: point(18px, 18px); pinPosition: point(18px, 18px);
pinPadding: margins(6px, 2px, 12px, 1px); pinPadding: margins(6px, 2px, 12px, 1px);
pinTextPosition: point(1px, 3px); pinTextPosition: point(1px, 3px);
back: icon {{ "calls/video_back", groupCallVideoTextFg }};
iconPosition: point(10px, 5px); iconPosition: point(10px, 5px);
} }

View file

@ -1187,8 +1187,19 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
}); });
if (const auto real = _call->lookupReal()) { if (const auto real = _call->lookupReal()) {
auto oneFound = false;
auto hasTwoOrMore = false;
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
if (_call->shownVideoTracks().contains(endpoint)) {
if (oneFound) {
hasTwoOrMore = true;
break;
}
oneFound = true;
}
}
const auto participant = real->participantByPeer(participantPeer); const auto participant = real->participantByPeer(participantPeer);
if (participant) { if (participant && hasTwoOrMore) {
const auto &large = _call->videoEndpointLarge(); const auto &large = _call->videoEndpointLarge();
const auto pinned = _call->videoEndpointPinned(); const auto pinned = _call->videoEndpointPinned();
const auto &camera = computeCameraEndpoint(participant); const auto &camera = computeCameraEndpoint(participant);

View file

@ -134,10 +134,10 @@ void Viewport::setMode(PanelMode mode, not_null<QWidget*> parent) {
} }
if (!wide()) { if (!wide()) {
for (const auto &tile : _tiles) { for (const auto &tile : _tiles) {
tile->togglePinShown(false); tile->toggleTopControlsShown(false);
} }
} else if (_selected.tile) { } else if (_selected.tile) {
_selected.tile->togglePinShown(true); _selected.tile->toggleTopControlsShown(true);
} }
} }
@ -154,7 +154,9 @@ void Viewport::handleMouseRelease(QPoint position, Qt::MouseButton button) {
if (pressed == _selected) { if (pressed == _selected) {
if (button == Qt::RightButton) { if (button == Qt::RightButton) {
tile->row()->showContextMenu(); tile->row()->showContextMenu();
} else if (!wide()) { } else if (!wide()
|| (_hasTwoOrMore && !_large)
|| pressed.element == Selection::Element::BackButton) {
_clicks.fire_copy(tile->endpoint()); _clicks.fire_copy(tile->endpoint());
} else if (pressed.element == Selection::Element::PinButton) { } else if (pressed.element == Selection::Element::PinButton) {
_pinToggles.fire(!tile->pinned()); _pinToggles.fire(!tile->pinned());
@ -177,10 +179,14 @@ void Viewport::updateSelected(QPoint position) {
if (geometry.contains(position)) { if (geometry.contains(position)) {
const auto pin = wide() const auto pin = wide()
&& tile->pinOuter().contains(position - geometry.topLeft()); && tile->pinOuter().contains(position - geometry.topLeft());
const auto back = wide()
&& tile->backOuter().contains(position - geometry.topLeft());
setSelected({ setSelected({
.tile = tile.get(), .tile = tile.get(),
.element = (pin .element = (pin
? Selection::Element::PinButton ? Selection::Element::PinButton
: back
? Selection::Element::BackButton
: Selection::Element::Tile), : Selection::Element::Tile),
}); });
return; return;
@ -238,6 +244,7 @@ void Viewport::showLarge(const VideoEndpoint &endpoint) {
const auto large = (i != end(_tiles)) ? i->get() : nullptr; const auto large = (i != end(_tiles)) ? i->get() : nullptr;
if (_large != large) { if (_large != large) {
_large = large; _large = large;
updateTopControlsVisibility();
updateTilesGeometry(); updateTilesGeometry();
} }
} }
@ -263,12 +270,40 @@ void Viewport::updateTilesGeometry(int outerWidth) {
if (wide()) { if (wide()) {
updateTilesGeometryWide(outerWidth, outerHeight); updateTilesGeometryWide(outerWidth, outerHeight);
refreshHasTwoOrMore();
_fullHeight = 0; _fullHeight = 0;
} else { } else {
updateTilesGeometryNarrow(outerWidth); updateTilesGeometryNarrow(outerWidth);
} }
} }
void Viewport::refreshHasTwoOrMore() {
auto hasTwoOrMore = false;
auto oneFound = false;
for (const auto &tile : _tiles) {
if (!tile->trackSize().isEmpty()) {
if (oneFound) {
hasTwoOrMore = true;
break;
}
oneFound = true;
}
}
if (_hasTwoOrMore == hasTwoOrMore) {
return;
}
_hasTwoOrMore = hasTwoOrMore;
updateCursor();
updateTopControlsVisibility();
}
void Viewport::updateTopControlsVisibility() {
if (_selected.tile) {
_selected.tile->toggleTopControlsShown(
_hasTwoOrMore && wide() && _large && _large == _selected.tile);
}
}
void Viewport::updateTilesGeometryWide(int outerWidth, int outerHeight) { void Viewport::updateTilesGeometryWide(int outerWidth, int outerHeight) {
if (!outerHeight) { if (!outerHeight) {
return; return;
@ -485,14 +520,19 @@ void Viewport::setSelected(Selection value) {
return; return;
} }
if (_selected.tile) { if (_selected.tile) {
_selected.tile->togglePinShown(false); _selected.tile->toggleTopControlsShown(false);
} }
_selected = value; _selected = value;
if (_selected.tile && wide()) { updateTopControlsVisibility();
_selected.tile->togglePinShown(true); updateCursor();
} }
void Viewport::updateCursor() {
const auto pointer = _selected.tile const auto pointer = _selected.tile
&& (!wide() || _selected.element == Selection::Element::PinButton); && (!wide()
|| (_hasTwoOrMore && !_large)
|| _selected.element == Selection::Element::PinButton
|| _selected.element == Selection::Element::BackButton);
widget()->setCursor(pointer ? style::cur_pointer : style::cur_default); widget()->setCursor(pointer ? style::cur_pointer : style::cur_default);
} }

View file

@ -97,6 +97,7 @@ private:
None, None,
Tile, Tile,
PinButton, PinButton,
BackButton,
}; };
VideoTile *tile = nullptr; VideoTile *tile = nullptr;
Element element = Element::None; Element element = Element::None;
@ -111,11 +112,14 @@ private:
void setup(); void setup();
[[nodiscard]] bool wide() const; [[nodiscard]] bool wide() const;
void updateCursor();
void updateTilesGeometry(); void updateTilesGeometry();
void updateTilesGeometry(int outerWidth); void updateTilesGeometry(int outerWidth);
void updateTilesGeometryWide(int outerWidth, int outerHeight); void updateTilesGeometryWide(int outerWidth, int outerHeight);
void updateTilesGeometryNarrow(int outerWidth); void updateTilesGeometryNarrow(int outerWidth);
void setTileGeometry(not_null<VideoTile*> tile, QRect geometry); void setTileGeometry(not_null<VideoTile*> tile, QRect geometry);
void refreshHasTwoOrMore();
void updateTopControlsVisibility();
void setSelected(Selection value); void setSelected(Selection value);
void setPressed(Selection value); void setPressed(Selection value);
@ -135,6 +139,7 @@ private:
const std::unique_ptr<Ui::RpWidgetWrap> _content; const std::unique_ptr<Ui::RpWidgetWrap> _content;
std::vector<std::unique_ptr<VideoTile>> _tiles; std::vector<std::unique_ptr<VideoTile>> _tiles;
rpl::variable<int> _fullHeight = 0; rpl::variable<int> _fullHeight = 0;
bool _hasTwoOrMore = false;
int _scrollTop = 0; int _scrollTop = 0;
QImage _shadow; QImage _shadow;
rpl::event_stream<VideoEndpoint> _clicks; rpl::event_stream<VideoEndpoint> _clicks;

View file

@ -262,7 +262,7 @@ void Viewport::RendererGL::init(
_frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); _frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
_frameBuffer->create(); _frameBuffer->create();
_frameBuffer->bind(); _frameBuffer->bind();
constexpr auto kQuads = 6; constexpr auto kQuads = 7;
constexpr auto kQuadVertices = kQuads * 4; constexpr auto kQuadVertices = kQuads * 4;
constexpr auto kQuadValues = kQuadVertices * 4; constexpr auto kQuadValues = kQuadVertices * 4;
constexpr auto kValues = kQuadValues + 8; // Blur texture coordinates. constexpr auto kValues = kQuadValues + 8; // Blur texture coordinates.
@ -486,6 +486,13 @@ void Viewport::RendererGL::paintTile(
geometry); geometry);
const auto pinRect = transformRect(pin.geometry); const auto pinRect = transformRect(pin.geometry);
// Back.
const auto back = _buttons.texturedRect(
tile->backInner().translated(x, y),
_back,
geometry);
const auto backRect = transformRect(back.geometry);
// Mute. // Mute.
const auto &icon = st::groupCallVideoCrossLine.icon; const auto &icon = st::groupCallVideoCrossLine.icon;
const auto iconLeft = x + width - st.iconPosition.x() - icon.width(); const auto iconLeft = x + width - st.iconPosition.x() - icon.width();
@ -573,6 +580,19 @@ void Viewport::RendererGL::paintTile(
pinRect.left(), pinRect.bottom(), pinRect.left(), pinRect.bottom(),
pin.texture.left(), pin.texture.top(), pin.texture.left(), pin.texture.top(),
// Back button.
backRect.left(), backRect.top(),
back.texture.left(), back.texture.bottom(),
backRect.right(), backRect.top(),
back.texture.right(), back.texture.bottom(),
backRect.right(), backRect.bottom(),
back.texture.right(), back.texture.top(),
backRect.left(), backRect.bottom(),
back.texture.left(), back.texture.top(),
// Mute icon. // Mute icon.
muteRect.left(), muteRect.top(), muteRect.left(), muteRect.top(),
mute.texture.left(), mute.texture.bottom(), mute.texture.left(), mute.texture.bottom(),
@ -683,6 +703,7 @@ void Viewport::RendererGL::paintTile(
if (pinVisible) { if (pinVisible) {
FillTexturedRectangle(f, &*_imageProgram, 14); FillTexturedRectangle(f, &*_imageProgram, 14);
FillTexturedRectangle(f, &*_imageProgram, 18);
} }
if (nameShift == fullNameShift) { if (nameShift == fullNameShift) {
@ -691,13 +712,13 @@ void Viewport::RendererGL::paintTile(
// Mute. // Mute.
if (!muteRect.empty()) { if (!muteRect.empty()) {
FillTexturedRectangle(f, &*_imageProgram, 18); FillTexturedRectangle(f, &*_imageProgram, 22);
} }
// Name. // Name.
if (!nameRect.empty()) { if (!nameRect.empty()) {
_names.bind(f); _names.bind(f);
FillTexturedRectangle(f, &*_imageProgram, 22); FillTexturedRectangle(f, &*_imageProgram, 26);
} }
} }
@ -905,15 +926,20 @@ void Viewport::RendererGL::ensureButtonsImage() {
const auto factor = cIntRetinaFactor(); const auto factor = cIntRetinaFactor();
const auto pinOnSize = VideoTile::PinInnerSize(true); const auto pinOnSize = VideoTile::PinInnerSize(true);
const auto pinOffSize = VideoTile::PinInnerSize(false); const auto pinOffSize = VideoTile::PinInnerSize(false);
const auto backSize = VideoTile::BackInnerSize();
const auto muteSize = st::groupCallVideoCrossLine.icon.size(); const auto muteSize = st::groupCallVideoCrossLine.icon.size();
const auto fullSize = QSize( const auto fullSize = QSize(
std::max({ std::max({
pinOnSize.width(), pinOnSize.width(),
pinOffSize.width(), pinOffSize.width(),
backSize.width(),
2 * muteSize.width(), 2 * muteSize.width(),
}), }),
pinOnSize.height() + pinOffSize.height() + muteSize.height()); (pinOnSize.height()
+ pinOffSize.height()
+ backSize.height()
+ muteSize.height()));
const auto imageSize = fullSize * factor; const auto imageSize = fullSize * factor;
auto image = _buttons.takeImage(); auto image = _buttons.takeImage();
if (image.size() != imageSize) { if (image.size() != imageSize) {
@ -947,7 +973,19 @@ void Viewport::RendererGL::ensureButtonsImage() {
&_pinBackground, &_pinBackground,
&_pinIcon); &_pinIcon);
const auto muteTop = pinOnSize.height() + pinOffSize.height(); _back = QRect(
QPoint(0, pinOnSize.height() + pinOffSize.height()) * factor,
backSize * factor);
VideoTile::PaintBackButton(
p,
0,
pinOnSize.height() + pinOffSize.height(),
fullSize.width(),
&_pinBackground);
const auto muteTop = pinOnSize.height()
+ pinOffSize.height()
+ backSize.height();
_muteOn = QRect(QPoint(0, muteTop) * factor, muteSize * factor); _muteOn = QRect(QPoint(0, muteTop) * factor, muteSize * factor);
_muteIcon.paint(p, { 0, muteTop }, 1.); _muteIcon.paint(p, { 0, muteTop }, 1.);

View file

@ -120,6 +120,7 @@ private:
Ui::GL::Image _buttons; Ui::GL::Image _buttons;
QRect _pinOn; QRect _pinOn;
QRect _pinOff; QRect _pinOff;
QRect _back;
QRect _muteOn; QRect _muteOn;
QRect _muteOff; QRect _muteOff;

View file

@ -122,18 +122,28 @@ void Viewport::Renderer::paintTileControls(
p.setClipRect(x, y, width, height); p.setClipRect(x, y, width, height);
const auto guard = gsl::finally([&] { p.setClipping(false); }); const auto guard = gsl::finally([&] { p.setClipping(false); });
// Pin.
const auto wide = _owner->wide(); const auto wide = _owner->wide();
if (wide) { if (wide) {
const auto inner = tile->pinInner(); // Pin.
const auto pinInner = tile->pinInner();
VideoTile::PaintPinButton( VideoTile::PaintPinButton(
p, p,
tile->pinned(), tile->pinned(),
x + inner.x(), x + pinInner.x(),
y + inner.y(), y + pinInner.y(),
_owner->widget()->width(), _owner->widget()->width(),
&_pinBackground, &_pinBackground,
&_pinIcon); &_pinIcon);
// Back.
const auto backInner = tile->backInner();
VideoTile::PaintBackButton(
p,
x + backInner.x(),
y + backInner.y(),
_owner->widget()->width(),
&_pinBackground);
} }
const auto shown = _owner->_controlsShownRatio; const auto shown = _owner->_controlsShownRatio;

View file

@ -31,19 +31,27 @@ Viewport::VideoTile::VideoTile(
setup(std::move(pinned)); setup(std::move(pinned));
} }
QRect Viewport::VideoTile::pinInner() const {
return _pinInner.translated(0, -pinSlide());
}
QRect Viewport::VideoTile::pinOuter() const { QRect Viewport::VideoTile::pinOuter() const {
return _pinOuter; return _pinOuter;
} }
int Viewport::VideoTile::pinSlide() const { QRect Viewport::VideoTile::pinInner() const {
return _pinInner.translated(0, -topControlsSlide());
}
QRect Viewport::VideoTile::backOuter() const {
return _backOuter;
}
QRect Viewport::VideoTile::backInner() const {
return _backInner.translated(0, -topControlsSlide());
}
int Viewport::VideoTile::topControlsSlide() const {
return anim::interpolate( return anim::interpolate(
st::groupCallVideoTile.pinPosition.y() + _pinInner.height(), st::groupCallVideoTile.pinPosition.y() + _pinInner.height(),
0, 0,
_pinShownAnimation.value(_pinShown ? 1. : 0.)); _topControlsShownAnimation.value(_topControlsShown ? 1. : 0.));
} }
bool Viewport::VideoTile::screencast() const { bool Viewport::VideoTile::screencast() const {
@ -52,15 +60,15 @@ bool Viewport::VideoTile::screencast() const {
void Viewport::VideoTile::setGeometry(QRect geometry) { void Viewport::VideoTile::setGeometry(QRect geometry) {
_geometry = geometry; _geometry = geometry;
updatePinnedGeometry(); updateTopControlsGeometry();
} }
void Viewport::VideoTile::togglePinShown(bool shown) { void Viewport::VideoTile::toggleTopControlsShown(bool shown) {
if (_pinShown == shown) { if (_topControlsShown == shown) {
return; return;
} }
_pinShown = shown; _topControlsShown = shown;
_pinShownAnimation.start( _topControlsShownAnimation.start(
_update, _update,
shown ? 0. : 1., shown ? 0. : 1.,
shown ? 1. : 0., shown ? 1. : 0.,
@ -128,19 +136,68 @@ void Viewport::VideoTile::PaintPinButton(
} }
void Viewport::VideoTile::updatePinnedGeometry() { QSize Viewport::VideoTile::BackInnerSize() {
const auto &st = st::groupCallVideoTile; const auto &st = st::groupCallVideoTile;
const auto buttonSize = PinInnerSize(_pinned); const auto &icon = st::groupCallVideoTile.back;
const auto fullWidth = st.pinPosition.x() * 2 + buttonSize.width(); const auto innerWidth = icon.width()
const auto fullHeight = st.pinPosition.y() * 2 + buttonSize.height(); + st.pinTextPosition.x()
_pinInner = QRect(QPoint(), buttonSize).translated( + st::semiboldFont->width(tr::lng_create_group_back(tr::now));
_geometry.width() - st.pinPosition.x() - buttonSize.width(), 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();
return { buttonWidth, buttonHeight };
}
void Viewport::VideoTile::PaintBackButton(
Painter &p,
int x,
int y,
int outerWidth,
not_null<Ui::RoundRect*> background) {
const auto &st = st::groupCallVideoTile;
const auto rect = QRect(QPoint(x, y), BackInnerSize());
background->paint(p, rect);
st.back.paint(
p,
rect.marginsRemoved(st.pinPadding).topLeft(),
outerWidth);
p.setPen(st::groupCallVideoTextFg);
p.setFont(st::semiboldFont);
p.drawTextLeft(
(x
+ st.pinPadding.left()
+ st::groupCallVideoTile.pin.icon.width()
+ st.pinTextPosition.x()),
(y
+ st.pinPadding.top()
+ st.pinTextPosition.y()),
outerWidth,
tr::lng_create_group_back(tr::now));
}
void Viewport::VideoTile::updateTopControlsGeometry() {
const auto &st = st::groupCallVideoTile;
const auto pinSize = PinInnerSize(_pinned);
const auto pinWidth = st.pinPosition.x() * 2 + pinSize.width();
const auto pinHeight = st.pinPosition.y() * 2 + pinSize.height();
_pinInner = QRect(QPoint(), pinSize).translated(
_geometry.width() - st.pinPosition.x() - pinSize.width(),
st.pinPosition.y()); st.pinPosition.y());
_pinOuter = QRect( _pinOuter = QRect(
_geometry.width() - fullWidth, _geometry.width() - pinWidth,
0, 0,
fullWidth, pinWidth,
fullHeight); pinHeight);
const auto backSize = BackInnerSize();
const auto backWidth = st.pinPosition.x() * 2 + backSize.width();
const auto backHeight = st.pinPosition.y() * 2 + backSize.height();
_backInner = QRect(QPoint(), backSize).translated(st.pinPosition);
_backOuter = QRect(0, 0, backWidth, backHeight);
} }
void Viewport::VideoTile::setup(rpl::producer<bool> pinned) { void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
@ -150,7 +207,7 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
return (_pinned != pinned); return (_pinned != pinned);
}) | rpl::start_with_next([=](bool pinned) { }) | rpl::start_with_next([=](bool pinned) {
_pinned = pinned; _pinned = pinned;
updatePinnedGeometry(); updateTopControlsGeometry();
_update(); _update();
}, _lifetime); }, _lifetime);

View file

@ -43,6 +43,8 @@ public:
} }
[[nodiscard]] QRect pinOuter() const; [[nodiscard]] QRect pinOuter() const;
[[nodiscard]] QRect pinInner() const; [[nodiscard]] QRect pinInner() const;
[[nodiscard]] QRect backOuter() const;
[[nodiscard]] QRect backInner() const;
[[nodiscard]] const VideoEndpoint &endpoint() const { [[nodiscard]] const VideoEndpoint &endpoint() const {
return _endpoint; return _endpoint;
} }
@ -55,7 +57,7 @@ public:
[[nodiscard]] bool screencast() const; [[nodiscard]] bool screencast() const;
void setGeometry(QRect geometry); void setGeometry(QRect geometry);
void togglePinShown(bool shown); void toggleTopControlsShown(bool shown);
bool updateRequestedQuality(VideoQuality quality); bool updateRequestedQuality(VideoQuality quality);
[[nodiscard]] rpl::lifetime &lifetime() { [[nodiscard]] rpl::lifetime &lifetime() {
@ -72,10 +74,18 @@ public:
not_null<Ui::RoundRect*> background, not_null<Ui::RoundRect*> background,
not_null<Ui::CrossLineAnimation*> icon); not_null<Ui::CrossLineAnimation*> icon);
[[nodiscard]] static QSize BackInnerSize();
static void PaintBackButton(
Painter &p,
int x,
int y,
int outerWidth,
not_null<Ui::RoundRect*> background);
private: private:
void setup(rpl::producer<bool> pinned); void setup(rpl::producer<bool> pinned);
[[nodiscard]] int pinSlide() const; [[nodiscard]] int topControlsSlide() const;
void updatePinnedGeometry(); void updateTopControlsGeometry();
const VideoEndpoint _endpoint; const VideoEndpoint _endpoint;
const Fn<void()> _update; const Fn<void()> _update;
@ -85,8 +95,10 @@ private:
rpl::variable<QSize> _trackSize; rpl::variable<QSize> _trackSize;
QRect _pinOuter; QRect _pinOuter;
QRect _pinInner; QRect _pinInner;
Ui::Animations::Simple _pinShownAnimation; QRect _backOuter;
bool _pinShown = false; QRect _backInner;
Ui::Animations::Simple _topControlsShownAnimation;
bool _topControlsShown = false;
bool _pinned = false; bool _pinned = false;
std::optional<VideoQuality> _quality; std::optional<VideoQuality> _quality;