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

View file

@ -1187,8 +1187,19 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
});
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);
if (participant) {
if (participant && hasTwoOrMore) {
const auto &large = _call->videoEndpointLarge();
const auto pinned = _call->videoEndpointPinned();
const auto &camera = computeCameraEndpoint(participant);

View file

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

View file

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

View file

@ -262,7 +262,7 @@ void Viewport::RendererGL::init(
_frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
_frameBuffer->create();
_frameBuffer->bind();
constexpr auto kQuads = 6;
constexpr auto kQuads = 7;
constexpr auto kQuadVertices = kQuads * 4;
constexpr auto kQuadValues = kQuadVertices * 4;
constexpr auto kValues = kQuadValues + 8; // Blur texture coordinates.
@ -486,6 +486,13 @@ void Viewport::RendererGL::paintTile(
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.
const auto &icon = st::groupCallVideoCrossLine.icon;
const auto iconLeft = x + width - st.iconPosition.x() - icon.width();
@ -573,6 +580,19 @@ void Viewport::RendererGL::paintTile(
pinRect.left(), pinRect.bottom(),
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.
muteRect.left(), muteRect.top(),
mute.texture.left(), mute.texture.bottom(),
@ -683,6 +703,7 @@ void Viewport::RendererGL::paintTile(
if (pinVisible) {
FillTexturedRectangle(f, &*_imageProgram, 14);
FillTexturedRectangle(f, &*_imageProgram, 18);
}
if (nameShift == fullNameShift) {
@ -691,13 +712,13 @@ void Viewport::RendererGL::paintTile(
// Mute.
if (!muteRect.empty()) {
FillTexturedRectangle(f, &*_imageProgram, 18);
FillTexturedRectangle(f, &*_imageProgram, 22);
}
// Name.
if (!nameRect.empty()) {
_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 pinOnSize = VideoTile::PinInnerSize(true);
const auto pinOffSize = VideoTile::PinInnerSize(false);
const auto backSize = VideoTile::BackInnerSize();
const auto muteSize = st::groupCallVideoCrossLine.icon.size();
const auto fullSize = QSize(
std::max({
pinOnSize.width(),
pinOffSize.width(),
backSize.width(),
2 * muteSize.width(),
}),
pinOnSize.height() + pinOffSize.height() + muteSize.height());
(pinOnSize.height()
+ pinOffSize.height()
+ backSize.height()
+ muteSize.height()));
const auto imageSize = fullSize * factor;
auto image = _buttons.takeImage();
if (image.size() != imageSize) {
@ -947,7 +973,19 @@ void Viewport::RendererGL::ensureButtonsImage() {
&_pinBackground,
&_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);
_muteIcon.paint(p, { 0, muteTop }, 1.);

View file

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

View file

@ -122,18 +122,28 @@ void Viewport::Renderer::paintTileControls(
p.setClipRect(x, y, width, height);
const auto guard = gsl::finally([&] { p.setClipping(false); });
// Pin.
const auto wide = _owner->wide();
if (wide) {
const auto inner = tile->pinInner();
// Pin.
const auto pinInner = tile->pinInner();
VideoTile::PaintPinButton(
p,
tile->pinned(),
x + inner.x(),
y + inner.y(),
x + pinInner.x(),
y + pinInner.y(),
_owner->widget()->width(),
&_pinBackground,
&_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;

View file

@ -31,19 +31,27 @@ Viewport::VideoTile::VideoTile(
setup(std::move(pinned));
}
QRect Viewport::VideoTile::pinInner() const {
return _pinInner.translated(0, -pinSlide());
}
QRect Viewport::VideoTile::pinOuter() const {
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(
st::groupCallVideoTile.pinPosition.y() + _pinInner.height(),
0,
_pinShownAnimation.value(_pinShown ? 1. : 0.));
_topControlsShownAnimation.value(_topControlsShown ? 1. : 0.));
}
bool Viewport::VideoTile::screencast() const {
@ -52,15 +60,15 @@ bool Viewport::VideoTile::screencast() const {
void Viewport::VideoTile::setGeometry(QRect geometry) {
_geometry = geometry;
updatePinnedGeometry();
updateTopControlsGeometry();
}
void Viewport::VideoTile::togglePinShown(bool shown) {
if (_pinShown == shown) {
void Viewport::VideoTile::toggleTopControlsShown(bool shown) {
if (_topControlsShown == shown) {
return;
}
_pinShown = shown;
_pinShownAnimation.start(
_topControlsShown = shown;
_topControlsShownAnimation.start(
_update,
shown ? 0. : 1.,
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 buttonSize = PinInnerSize(_pinned);
const auto fullWidth = st.pinPosition.x() * 2 + buttonSize.width();
const auto fullHeight = st.pinPosition.y() * 2 + buttonSize.height();
_pinInner = QRect(QPoint(), buttonSize).translated(
_geometry.width() - st.pinPosition.x() - buttonSize.width(),
const auto &icon = st::groupCallVideoTile.back;
const auto innerWidth = icon.width()
+ st.pinTextPosition.x()
+ st::semiboldFont->width(tr::lng_create_group_back(tr::now));
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());
_pinOuter = QRect(
_geometry.width() - fullWidth,
_geometry.width() - pinWidth,
0,
fullWidth,
fullHeight);
pinWidth,
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) {
@ -150,7 +207,7 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
return (_pinned != pinned);
}) | rpl::start_with_next([=](bool pinned) {
_pinned = pinned;
updatePinnedGeometry();
updateTopControlsGeometry();
_update();
}, _lifetime);

View file

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