Implement nice expandable story caption viewer.

This commit is contained in:
John Preston 2023-07-25 17:37:56 +04:00
parent 8cc90c3373
commit 5aa6102903
11 changed files with 259 additions and 137 deletions

View file

@ -3846,6 +3846,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_click_to_view" = "Click here to view updates from {users}."; "lng_stories_click_to_view" = "Click here to view updates from {users}.";
"lng_stories_click_to_view_and_one" = "{accumulated}, {user}"; "lng_stories_click_to_view_and_one" = "{accumulated}, {user}";
"lng_stories_click_to_view_and_last" = "{accumulated} and {user}"; "lng_stories_click_to_view_and_last" = "{accumulated} and {user}";
"lng_stories_show_more" = "Show more";
"lng_stories_my_title" = "Saved Stories"; "lng_stories_my_title" = "Saved Stories";
"lng_stories_archive_button" = "Stories Archive"; "lng_stories_archive_button" = "Stories Archive";

View file

@ -147,6 +147,7 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
my.show->showBox(std::move(box)); my.show->showBox(std::move(box));
} else if (use) { } else if (use) {
use->show(std::move(box)); use->show(std::move(box));
use->activate();
} }
} else { } else {
open(); open();

View file

@ -7,79 +7,168 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "media/stories/media_stories_caption_full_view.h" #include "media/stories/media_stories_caption_full_view.h"
#include "base/event_filter.h"
#include "core/ui_integration.h" #include "core/ui_integration.h"
#include "ui/widgets/scroll_area.h" #include "chat_helpers/compose/compose_show.h"
#include "media/stories/media_stories_controller.h"
#include "media/stories/media_stories_view.h"
#include "ui/widgets/elastic_scroll.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/click_handler.h"
#include "styles/style_media_view.h" #include "styles/style_media_view.h"
namespace Media::Stories { namespace Media::Stories {
CaptionFullView::CaptionFullView( CaptionFullView::CaptionFullView(not_null<Controller*> controller)
not_null<Ui::RpWidget*> parent, : _controller(controller)
not_null<Main::Session*> session, , _scroll(std::make_unique<Ui::ElasticScroll>(controller->wrap()))
const TextWithEntities &text, , _wrap(_scroll->setOwnedWidget(
Fn<void()> close)
: RpWidget(parent)
, _scroll(std::make_unique<Ui::ScrollArea>((RpWidget*)this))
, _text(_scroll->setOwnedWidget(
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>( object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
_scroll.get(), _scroll.get(),
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull), object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
st::mediaviewCaptionPadding))->entity()) st::mediaviewCaptionPadding)))
, _close(std::move(close)) , _text(_wrap->entity()) {
, _background(st::storiesRadius, st::mediaviewCaptionBg) { _text->setMarkedText(controller->captionText(), Core::MarkedTextContext{
_text->setMarkedText(text, Core::MarkedTextContext{ .session = &controller->uiShow()->session(),
.session = session,
.customEmojiRepaint = [=] { _text->update(); }, .customEmojiRepaint = [=] { _text->update(); },
}); });
parent->sizeValue() | rpl::start_with_next([=](QSize size) { startAnimation();
setGeometry(QRect(QPoint(), size)); _controller->layoutValue(
}, lifetime()); ) | rpl::start_with_next([=](const Layout &layout) {
if (_outer != layout.content) {
const auto skip = layout.header.y()
+ layout.header.height()
- layout.content.y();
_outer = layout.content.marginsRemoved({ 0, skip, 0, 0 });
updateGeometry();
}
}, _scroll->lifetime());
show(); const auto filter = [=](not_null<QEvent*> e) {
setFocus(); const auto mouse = [&] {
return static_cast<QMouseEvent*>(e.get());
};
const auto type = e->type();
if (type == QEvent::MouseButtonPress
&& mouse()->button() == Qt::LeftButton
&& !ClickHandler::getActive()) {
_down = true;
} else if (type == QEvent::MouseButtonRelease && _down) {
_down = false;
if (!ClickHandler::getPressed()) {
close();
}
} else if (type == QEvent::KeyPress
&& static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape) {
close();
return base::EventFilterResult::Cancel;
}
return base::EventFilterResult::Continue;
};
base::install_event_filter(_text.get(), filter);
base::install_event_filter(_wrap.get(), filter);
using Type = Ui::ElasticScroll::OverscrollType;
rpl::combine(
_scroll->positionValue(),
_scroll->movementValue()
) | rpl::filter([=] {
return !_closing;
}) | rpl::start_with_next([=](
Ui::ElasticScrollPosition position,
Ui::ElasticScrollMovement movement) {
const auto overscrollTop = std::max(-position.overscroll, 0);
using Phase = Ui::ElasticScrollMovement;
if (movement == Phase::Progress) {
if (overscrollTop > 0) {
_pulling = true;
} else {
_pulling = false;
}
} else if (_pulling
&& (movement == Phase::Momentum
|| movement == Phase::Returning)) {
_pulling = false;
if (overscrollTop > st::storiesCaptionPullThreshold) {
_closingTopAdded = overscrollTop;
_scroll->setOverscrollTypes(Type::None, Type::Real);
close();
updateGeometry();
}
}
}, _scroll->lifetime());
_scroll->show();
_scroll->setOverscrollBg(QColor(0, 0, 0, 0));
_scroll->setOverscrollTypes(Type::Real, Type::Real);
_text->show();
_text->setFocus();
} }
CaptionFullView::~CaptionFullView() = default; CaptionFullView::~CaptionFullView() = default;
void CaptionFullView::paintEvent(QPaintEvent *e) { bool CaptionFullView::closing() const {
auto p = QPainter(this); return _closing;
_background.paint(p, _scroll->geometry());
_background.paint(p, _scroll->geometry());
} }
void CaptionFullView::resizeEvent(QResizeEvent *e) { bool CaptionFullView::focused() const {
const auto wanted = _text->naturalWidth(); return Ui::InFocusChain(_scroll.get());
}
void CaptionFullView::close() {
if (_closing) {
return;
}
_closing = true;
_controller->captionClosing();
startAnimation();
}
void CaptionFullView::updateGeometry() {
if (_outer.isEmpty()) {
return;
}
const auto lineHeight = st::mediaviewCaptionStyle.font->height;
const auto padding = st::mediaviewCaptionPadding; const auto padding = st::mediaviewCaptionPadding;
const auto margin = st::mediaviewCaptionMargin * 2; _text->resizeToWidth(_outer.width() - padding.left() - padding.right());
const auto available = (rect() - padding).width() const auto add = padding.top() + padding.bottom();
- (margin.width() * 2); const auto maxShownHeight = lineHeight * kMaxShownCaptionLines;
const auto use = std::min(wanted, available); const auto shownHeight = (_text->height() > maxShownHeight)
_text->resizeToWidth(use); ? (lineHeight * kCollapsedCaptionLines)
const auto fullw = use + padding.left() + padding.right(); : _text->height();
const auto fullh = std::min( const auto collapsedHeight = shownHeight + add;
_text->height() + padding.top() + padding.bottom(), const auto addedToBottom = lineHeight;
height() - (margin.height() * 2)); const auto expandedHeight = _text->height() + add + addedToBottom;
const auto left = (width() - fullw) / 2; const auto fullHeight = std::min(expandedHeight, _outer.height());
const auto top = (height() - fullh) / 2; const auto shown = _animation.value(_closing ? 0. : 1.);
_scroll->setGeometry(left, top, fullw, fullh); const auto height = (_closing || _animation.animating())
} ? anim::interpolate(collapsedHeight, fullHeight, shown)
: _outer.height();
void CaptionFullView::keyPressEvent(QKeyEvent *e) { const auto added = anim::interpolate(0, _closingTopAdded, shown);
if (e->key() == Qt::Key_Escape) { const auto bottomPadding = anim::interpolate(0, addedToBottom, shown);
if (const auto onstack = _close) { const auto use = padding + ((_closing || _animation.animating())
onstack(); ? QMargins(0, 0, 0, bottomPadding)
} : QMargins(0, height - fullHeight, 0, bottomPadding));
_wrap->setPadding(use);
_scroll->setGeometry(
_outer.x(),
added + _outer.y() + _outer.height() - height,
_outer.width(),
std::max(height - added, 0));
if (_closing && !_animation.animating()) {
_controller->captionClosed();
} }
} }
void CaptionFullView::mousePressEvent(QMouseEvent *e) { void CaptionFullView::startAnimation() {
if (e->button() == Qt::LeftButton) { _animation.start(
if (const auto onstack = _close) { [=] { updateGeometry(); },
onstack(); _closing ? 1. : 0.,
} _closing ? 0. : 1.,
} st::fadeWrapDuration,
anim::sineInOut);
} }
} // namespace Media::Stories } // namespace Media::Stories

View file

@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/effects/animations.h"
#include "ui/round_rect.h"
namespace Main { namespace Main {
class Session; class Session;
@ -16,30 +15,38 @@ class Session;
namespace Ui { namespace Ui {
class FlatLabel; class FlatLabel;
class ScrollArea; class ElasticScroll;
template <typename Widget>
class PaddingWrap;
} // namespace Ui } // namespace Ui
namespace Media::Stories { namespace Media::Stories {
class CaptionFullView final : private Ui::RpWidget { class Controller;
class CaptionFullView final {
public: public:
CaptionFullView( explicit CaptionFullView(not_null<Controller*> controller);
not_null<Ui::RpWidget*> parent,
not_null<Main::Session*> session,
const TextWithEntities &text,
Fn<void()> close);
~CaptionFullView(); ~CaptionFullView();
private: void close();
void paintEvent(QPaintEvent *e) override; [[nodiscard]] bool closing() const;
void resizeEvent(QResizeEvent *e) override; [[nodiscard]] bool focused() const;
void keyPressEvent(QKeyEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
std::unique_ptr<Ui::ScrollArea> _scroll; private:
void updateGeometry();
void startAnimation();
const not_null<Controller*> _controller;
const std::unique_ptr<Ui::ElasticScroll> _scroll;
const not_null<Ui::PaddingWrap<Ui::FlatLabel>*> _wrap;
const not_null<Ui::FlatLabel*> _text; const not_null<Ui::FlatLabel*> _text;
Fn<void()> _close; Ui::Animations::Simple _animation;
Ui::RoundRect _background; QRect _outer;
int _closingTopAdded = 0;
bool _pulling = false;
bool _closing = false;
bool _down = false;
}; };

View file

@ -280,9 +280,6 @@ Controller::Controller(not_null<Delegate*> delegate)
_1 || _2 _1 || _2
) | rpl::distinct_until_changed( ) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool active) { ) | rpl::start_with_next([=](bool active) {
if (active) {
_captionFullView = nullptr;
}
_replyActive = active; _replyActive = active;
updateContentFaded(); updateContentFaded();
}, _lifetime); }, _lifetime);
@ -355,7 +352,8 @@ Controller::~Controller() {
} }
void Controller::updateContentFaded() { void Controller::updateContentFaded() {
const auto faded = _replyActive || _captionFullView || _captionExpanded; const auto faded = _replyActive
|| (_captionFullView && !_captionFullView->closing());
if (_contentFaded == faded) { if (_contentFaded == faded) {
return; return;
} }
@ -584,26 +582,31 @@ TextWithEntities Controller::captionText() const {
return _captionText; return _captionText;
} }
void Controller::setCaptionExpanded(bool expanded) { bool Controller::skipCaption() const {
if (_captionExpanded == expanded) { return _captionFullView != nullptr;
return;
}
_captionExpanded = expanded;
updateContentFaded();
} }
void Controller::showFullCaption() { void Controller::showFullCaption() {
if (_captionText.empty()) { if (_captionText.empty()) {
return; return;
} }
_captionFullView = std::make_unique<CaptionFullView>( _captionFullView = std::make_unique<CaptionFullView>(this);
wrap(),
&_delegate->storiesShow()->session(),
_captionText,
[=] { _captionFullView = nullptr; updateContentFaded(); });
updateContentFaded(); updateContentFaded();
} }
void Controller::captionClosing() {
updateContentFaded();
}
void Controller::captionClosed() {
if (!_captionFullView) {
return;
} else if (_captionFullView->focused()) {
_wrap->setFocus();
}
_captionFullView = nullptr;
}
std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const { std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const {
return _delegate->storiesShow(); return _delegate->storiesShow();
} }
@ -820,9 +823,8 @@ void Controller::show(
_slider->raise(); _slider->raise();
} }
captionClosed();
_captionText = story->caption(); _captionText = story->caption();
_captionFullView = nullptr;
_captionExpanded = false;
_contentFaded = false; _contentFaded = false;
_contentFadeAnimation.stop(); _contentFadeAnimation.stop();
const auto document = story->document(); const auto document = story->document();
@ -972,17 +974,13 @@ void Controller::updatePlayingAllowed() {
&& _windowActive && _windowActive
&& !_paused && !_paused
&& !_replyActive && !_replyActive
&& !_captionFullView && (!_captionFullView || _captionFullView->closing())
&& !_captionExpanded
&& !_layerShown && !_layerShown
&& !_menuShown && !_menuShown
&& !_tooltipShown); && !_tooltipShown);
} }
void Controller::setPlayingAllowed(bool allowed) { void Controller::setPlayingAllowed(bool allowed) {
if (allowed) {
_captionFullView = nullptr;
}
if (_photoPlayback) { if (_photoPlayback) {
_photoPlayback->togglePaused(!allowed); _photoPlayback->togglePaused(!allowed);
} else { } else {
@ -1192,6 +1190,9 @@ void Controller::togglePaused(bool paused) {
void Controller::contentPressed(bool pressed) { void Controller::contentPressed(bool pressed) {
togglePaused(pressed); togglePaused(pressed);
if (_captionFullView) {
_captionFullView->close();
}
if (pressed) { if (pressed) {
_reactions->collapse(); _reactions->collapse();
} }

View file

@ -122,8 +122,10 @@ public:
[[nodiscard]] bool closeByClickAt(QPoint position) const; [[nodiscard]] bool closeByClickAt(QPoint position) const;
[[nodiscard]] Data::FileOrigin fileOrigin() const; [[nodiscard]] Data::FileOrigin fileOrigin() const;
[[nodiscard]] TextWithEntities captionText() const; [[nodiscard]] TextWithEntities captionText() const;
void setCaptionExpanded(bool expanded); [[nodiscard]] bool skipCaption() const;
void showFullCaption(); void showFullCaption();
void captionClosing();
void captionClosed();
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const; [[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
[[nodiscard]] auto stickerOrEmojiChosen() const [[nodiscard]] auto stickerOrEmojiChosen() const
@ -250,7 +252,6 @@ private:
Ui::Animations::Simple _contentFadeAnimation; Ui::Animations::Simple _contentFadeAnimation;
bool _contentFaded = false; bool _contentFaded = false;
bool _captionExpanded = false;
bool _windowActive = false; bool _windowActive = false;
bool _replyFocused = false; bool _replyFocused = false;
bool _replyActive = false; bool _replyActive = false;

View file

@ -123,8 +123,8 @@ TextWithEntities View::captionText() const {
return _controller->captionText(); return _controller->captionText();
} }
void View::setCaptionExpanded(bool expanded) { bool View::skipCaption() const {
_controller->setCaptionExpanded(expanded); return _controller->skipCaption();
} }
void View::showFullCaption() { void View::showFullCaption() {

View file

@ -48,6 +48,9 @@ struct SiblingView {
} }
}; };
inline constexpr auto kCollapsedCaptionLines = 2;
inline constexpr auto kMaxShownCaptionLines = 4;
class View final { class View final {
public: public:
explicit View(not_null<Delegate*> delegate); explicit View(not_null<Delegate*> delegate);
@ -64,7 +67,7 @@ public:
[[nodiscard]] SiblingView sibling(SiblingType type) const; [[nodiscard]] SiblingView sibling(SiblingType type) const;
[[nodiscard]] Data::FileOrigin fileOrigin() const; [[nodiscard]] Data::FileOrigin fileOrigin() const;
[[nodiscard]] TextWithEntities captionText() const; [[nodiscard]] TextWithEntities captionText() const;
void setCaptionExpanded(bool expanded); [[nodiscard]] bool skipCaption() const;
void showFullCaption(); void showFullCaption();
void updatePlayback(const Player::TrackState &state); void updatePlayback(const Player::TrackState &state);

View file

@ -445,7 +445,8 @@ storiesSideSkip: 145px;
storiesCaptionFull: FlatLabel(defaultFlatLabel) { storiesCaptionFull: FlatLabel(defaultFlatLabel) {
style: mediaviewCaptionStyle; style: mediaviewCaptionStyle;
textFg: mediaviewCaptionFg; textFg: mediaviewCaptionFg;
minWidth: 360px; palette: mediaviewTextPalette;
minWidth: 36px;
} }
storiesComposeBg: groupCallMembersBg; storiesComposeBg: groupCallMembersBg;
storiesComposeBgOver: groupCallMembersBgOver; storiesComposeBgOver: groupCallMembersBgOver;
@ -908,3 +909,6 @@ storiesVolumeSlider: MediaSlider {
storiesInfoTooltipLabel: defaultImportantTooltipLabel; storiesInfoTooltipLabel: defaultImportantTooltipLabel;
storiesInfoTooltip: defaultImportantTooltip; storiesInfoTooltip: defaultImportantTooltip;
storiesInfoTooltipMaxWidth: 360px; storiesInfoTooltipMaxWidth: 360px;
storiesCaptionPullThreshold: 50px;
storiesShowMorePadding: margins(6px, 4px, 6px, 4px);
storiesShowMoreFont: semiboldFont;

View file

@ -1383,6 +1383,10 @@ void OverlayWidget::resizeCenteredControls() {
} }
void OverlayWidget::refreshCaptionGeometry() { void OverlayWidget::refreshCaptionGeometry() {
_caption.updateSkipBlock(0, 0);
_captionShowMoreWidth = 0;
_captionSkipBlockWidth = 0;
if (_caption.isEmpty()) { if (_caption.isEmpty()) {
_captionRect = QRect(); _captionRect = QRect();
return; return;
@ -1408,32 +1412,28 @@ void OverlayWidget::refreshCaptionGeometry() {
- st::mediaviewCaptionPadding.left() - st::mediaviewCaptionPadding.left()
- st::mediaviewCaptionPadding.right()), - st::mediaviewCaptionPadding.right()),
_caption.maxWidth()); _caption.maxWidth());
const auto maxExpandedOuterHeight = (_stories
? (_h - st::storiesShadowTop.height())
: _maxUsedHeight);
const auto maxCollapsedOuterHeight = !_stories
? (_maxUsedHeight / 4)
: (_h / 3);
const auto maxExpandedHeight = maxExpandedOuterHeight
- st::mediaviewCaptionPadding.top()
- st::mediaviewCaptionPadding.bottom();
const auto maxCollapsedHeight = maxCollapsedOuterHeight
- st::mediaviewCaptionPadding.top()
- st::mediaviewCaptionPadding.bottom();
const auto lineHeight = st::mediaviewCaptionStyle.font->height; const auto lineHeight = st::mediaviewCaptionStyle.font->height;
const auto wantedHeight = _caption.countHeight(captionWidth); const auto wantedHeight = _caption.countHeight(captionWidth);
const auto maxHeight = _captionExpanded const auto maxHeight = !_stories
? maxExpandedHeight ? (_maxUsedHeight / 4)
: maxCollapsedHeight; : (wantedHeight > lineHeight * Stories::kMaxShownCaptionLines)
? (lineHeight * Stories::kCollapsedCaptionLines)
: wantedHeight;
const auto captionHeight = std::min( const auto captionHeight = std::min(
wantedHeight, wantedHeight,
(maxHeight / lineHeight) * lineHeight); (maxHeight / lineHeight) * lineHeight);
_captionFitsIfExpanded = _stories if (_stories && captionHeight < wantedHeight) {
&& (wantedHeight <= maxExpandedHeight); const auto padding = st::storiesShowMorePadding;
_captionShownFull = (wantedHeight <= maxCollapsedHeight); _captionShowMoreWidth = st::storiesShowMoreFont->width(
if (_captionShownFull && _captionExpanded && _stories) { tr::lng_stories_show_more(tr::now));
_captionExpanded = false; _captionSkipBlockWidth = _captionShowMoreWidth
_stories->setCaptionExpanded(false); + padding.left()
+ padding.right()
- st::mediaviewCaptionPadding.right();
const auto skiph = st::storiesShowMoreFont->height
+ padding.bottom()
- st::mediaviewCaptionPadding.bottom();
_caption.updateSkipBlock(_captionSkipBlockWidth, skiph);
} }
_captionRect = QRect( _captionRect = QRect(
(width() - captionWidth) / 2, (width() - captionWidth) / 2,
@ -3495,7 +3495,6 @@ void OverlayWidget::updateThemePreviewGeometry() {
} }
void OverlayWidget::displayFinished(anim::activation activation) { void OverlayWidget::displayFinished(anim::activation activation) {
_captionExpanded = _captionFitsIfExpanded = _captionShownFull = false;
updateControls(); updateControls();
if (isHidden()) { if (isHidden()) {
_helper->beforeShow(_fullscreen); _helper->beforeShow(_fullscreen);
@ -4502,7 +4501,8 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
if (!_stories) { if (!_stories) {
renderer->paintFooter(footerGeometry(), opacity); renderer->paintFooter(footerGeometry(), opacity);
} }
if (!_caption.isEmpty()) { if (!_caption.isEmpty()
&& (!_stories || !_stories->skipCaption())) {
renderer->paintCaption(captionGeometry(), opacity); renderer->paintCaption(captionGeometry(), opacity);
} }
if (_groupThumbs) { if (_groupThumbs) {
@ -4912,6 +4912,7 @@ void OverlayWidget::paintCaptionContent(
} }
if (inner.intersects(clip)) { if (inner.intersects(clip)) {
p.setPen(st::mediaviewCaptionFg); p.setPen(st::mediaviewCaptionFg);
const auto lineHeight = st::mediaviewCaptionStyle.font->height;
_caption.draw(p, { _caption.draw(p, {
.position = inner.topLeft(), .position = inner.topLeft(),
.availableWidth = inner.width(), .availableWidth = inner.width(),
@ -4919,8 +4920,31 @@ void OverlayWidget::paintCaptionContent(
.spoiler = Ui::Text::DefaultSpoilerCache(), .spoiler = Ui::Text::DefaultSpoilerCache(),
.pausedEmoji = On(PowerSaving::kEmojiChat), .pausedEmoji = On(PowerSaving::kEmojiChat),
.pausedSpoiler = On(PowerSaving::kChatSpoiler), .pausedSpoiler = On(PowerSaving::kChatSpoiler),
.elisionLines = inner.height() / st::mediaviewCaptionStyle.font->height, .elisionLines = inner.height() / lineHeight,
.elisionRemoveFromEnd = _captionSkipBlockWidth,
}); });
if (_captionShowMoreWidth > 0) {
const auto padding = st::storiesShowMorePadding;
const auto showMoreLeft = outer.x()
+ outer.width()
- padding.right()
- _captionShowMoreWidth;
const auto showMoreTop = outer.y()
+ outer.height()
- padding.bottom()
- st::storiesShowMoreFont->height;
const auto underline = _captionExpandLink
&& ClickHandler::showAsActive(_captionExpandLink);
p.setFont(underline
? st::storiesShowMoreFont->underline()
: st::storiesShowMoreFont);
p.drawTextLeft(
showMoreLeft,
showMoreTop,
width(),
tr::lng_stories_show_more(tr::now));
}
} }
} }
@ -5545,9 +5569,13 @@ void OverlayWidget::updateOver(QPoint pos) {
lnk = textState.link; lnk = textState.link;
lnkhost = this; lnkhost = this;
} else if (_captionRect.contains(pos)) { } else if (_captionRect.contains(pos)) {
auto textState = _caption.getState(pos - _captionRect.topLeft(), _captionRect.width()); auto request = Ui::Text::StateRequestElided();
const auto lineHeight = st::mediaviewCaptionStyle.font->height;
request.lines = _captionRect.height() / lineHeight;
request.removeFromEnd = _captionSkipBlockWidth;
auto textState = _caption.getStateElided(pos - _captionRect.topLeft(), _captionRect.width(), request);
lnk = textState.link; lnk = textState.link;
if (_stories && !_captionShownFull && !lnk) { if (_stories && !lnk) {
lnk = ensureCaptionExpandLink(); lnk = ensureCaptionExpandLink();
} }
lnkhost = this; lnkhost = this;
@ -5626,19 +5654,7 @@ void OverlayWidget::updateOver(QPoint pos) {
ClickHandlerPtr OverlayWidget::ensureCaptionExpandLink() { ClickHandlerPtr OverlayWidget::ensureCaptionExpandLink() {
if (!_captionExpandLink) { if (!_captionExpandLink) {
const auto toggle = crl::guard(_widget, [=] { const auto toggle = crl::guard(_widget, [=] {
if (!_stories) { if (_stories) {
return;
} else if (_captionExpanded) {
_captionExpanded = false;
_stories->setCaptionExpanded(false);
refreshCaptionGeometry();
update();
} else if (_captionFitsIfExpanded) {
_captionExpanded = true;
_stories->setCaptionExpanded(true);
refreshCaptionGeometry();
update();
} else {
_stories->showFullCaption(); _stories->showFullCaption();
} }
}); });

View file

@ -587,9 +587,8 @@ private:
Ui::Text::String _caption; Ui::Text::String _caption;
QRect _captionRect; QRect _captionRect;
ClickHandlerPtr _captionExpandLink; ClickHandlerPtr _captionExpandLink;
bool _captionShownFull = false; int _captionShowMoreWidth = 0;
bool _captionFitsIfExpanded = false; int _captionSkipBlockWidth = 0;
bool _captionExpanded = false;
int _topNotchSize = 0; int _topNotchSize = 0;
int _width = 0; int _width = 0;