Display full caption if it doesn't fit.

This commit is contained in:
John Preston 2023-05-12 21:10:00 +04:00
parent 0331955ce7
commit a745c9ff75
10 changed files with 246 additions and 15 deletions

View File

@ -962,6 +962,8 @@ PRIVATE
media/player/media_player_volume_controller.h
media/player/media_player_widget.cpp
media/player/media_player_widget.h
media/stories/media_stories_caption_full_view.cpp
media/stories/media_stories_caption_full_view.h
media/stories/media_stories_controller.cpp
media/stories/media_stories_controller.h
media/stories/media_stories_delegate.cpp

View File

@ -0,0 +1,81 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_caption_full_view.h"
#include "core/ui_integration.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/labels.h"
#include "styles/style_media_view.h"
namespace Media::Stories {
CaptionFullView::CaptionFullView(
not_null<Ui::RpWidget*> parent,
not_null<Main::Session*> session,
const TextWithEntities &text,
Fn<void()> close)
: RpWidget(parent)
, _scroll(std::make_unique<Ui::ScrollArea>((RpWidget*)this))
, _text(_scroll->setOwnedWidget(
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
_scroll.get(),
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
st::mediaviewCaptionPadding))->entity())
, _close(std::move(close))
, _background(st::storiesRadius, st::mediaviewCaptionBg) {
_text->setMarkedText(text, Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { _text->update(); },
});
parent->sizeValue() | rpl::start_with_next([=](QSize size) {
setGeometry(QRect(QPoint(), size));
}, lifetime());
show();
setFocus();
}
CaptionFullView::~CaptionFullView() = default;
void CaptionFullView::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
_background.paint(p, _scroll->geometry());
_background.paint(p, _scroll->geometry());
}
void CaptionFullView::resizeEvent(QResizeEvent *e) {
const auto wanted = _text->naturalWidth();
const auto padding = st::mediaviewCaptionPadding;
const auto margin = st::mediaviewCaptionMargin * 2;
const auto available = (rect() - padding).width()
- (margin.width() * 2);
const auto use = std::min(wanted, available);
_text->resizeToWidth(use);
const auto fullw = use + padding.left() + padding.right();
const auto fullh = std::min(
_text->height() + padding.top() + padding.bottom(),
height() - (margin.height() * 2));
const auto left = (width() - fullw) / 2;
const auto top = (height() - fullh) / 2;
_scroll->setGeometry(left, top, fullw, fullh);
}
void CaptionFullView::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
_close();
}
}
void CaptionFullView::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
_close();
}
}
} // namespace Media::Stories

View File

@ -0,0 +1,46 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/rp_widget.h"
#include "ui/round_rect.h"
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class FlatLabel;
class ScrollArea;
} // namespace Ui
namespace Media::Stories {
class CaptionFullView final : private Ui::RpWidget {
public:
CaptionFullView(
not_null<Ui::RpWidget*> parent,
not_null<Main::Session*> session,
const TextWithEntities &text,
Fn<void()> close);
~CaptionFullView();
private:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
std::unique_ptr<Ui::ScrollArea> _scroll;
const not_null<Ui::FlatLabel*> _text;
Fn<void()> _close;
Ui::RoundRect _background;
};
} // namespace Media::Stories

View File

@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/power_save_blocker.h"
#include "chat_helpers/compose/compose_show.h"
#include "data/data_stories.h"
#include "data/data_user.h"
#include "media/stories/media_stories_caption_full_view.h"
#include "media/stories/media_stories_delegate.h"
#include "media/stories/media_stories_header.h"
#include "media/stories/media_stories_sibling.h"
@ -115,6 +117,9 @@ Controller::Controller(not_null<Delegate*> delegate)
_replyArea->focusedValue(
) | rpl::start_with_next([=](bool focused) {
if (focused) {
_captionFullView = nullptr;
}
_contentFaded = focused;
_contentFadeAnimation.start(
[=] { _delegate->storiesRepaint(); },
@ -292,6 +297,18 @@ TextWithEntities Controller::captionText() const {
return _captionText;
}
void Controller::showFullCaption() {
if (_captionText.empty()) {
return;
}
togglePaused(true);
_captionFullView = std::make_unique<CaptionFullView>(
wrap(),
&_delegate->storiesShow()->session(),
_captionText,
[=] { togglePaused(false); });
}
std::shared_ptr<ChatHelpers::Show> Controller::uiShow() const {
return _delegate->storiesShow();
}
@ -334,10 +351,15 @@ void Controller::show(
}
_shown = id;
_captionText = item.caption;
_captionFullView = nullptr;
_header->show({ .user = list.user, .date = item.date });
_slider->show({ .index = _index, .total = list.total });
_replyArea->show({ .user = list.user });
if (_contentFaded) {
togglePaused(true);
}
}
void Controller::showSiblings(
@ -447,6 +469,9 @@ bool Controller::paused() const {
}
void Controller::togglePaused(bool paused) {
if (!paused) {
_captionFullView = nullptr;
}
if (_photoPlayback) {
_photoPlayback->togglePaused(paused);
} else {

View File

@ -41,6 +41,7 @@ class Delegate;
struct SiblingView;
enum class SiblingType;
struct ContentLayout;
class CaptionFullView;
enum class HeaderLayout {
Normal,
@ -78,6 +79,7 @@ public:
[[nodiscard]] rpl::producer<Layout> layoutValue() const;
[[nodiscard]] ContentLayout contentLayout() const;
[[nodiscard]] TextWithEntities captionText() const;
void showFullCaption();
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
[[nodiscard]] auto stickerOrEmojiChosen() const
@ -130,6 +132,7 @@ private:
const std::unique_ptr<Slider> _slider;
const std::unique_ptr<ReplyArea> _replyArea;
std::unique_ptr<PhotoPlayback> _photoPlayback;
std::unique_ptr<CaptionFullView> _captionFullView;
Ui::Animations::Simple _contentFadeAnimation;
bool _contentFaded = false;

View File

@ -83,6 +83,10 @@ TextWithEntities View::captionText() const {
return _controller->captionText();
}
void View::showFullCaption() {
_controller->showFullCaption();
}
rpl::lifetime &View::lifetime() {
return _controller->lifetime();
}

View File

@ -63,6 +63,7 @@ public:
[[nodiscard]] ContentLayout contentLayout() const;
[[nodiscard]] SiblingView sibling(SiblingType type) const;
[[nodiscard]] TextWithEntities captionText() const;
void showFullCaption();
void updatePlayback(const Player::TrackState &state);

View File

@ -448,3 +448,8 @@ storiesAttach: IconButton(defaultIconButton) {
}
}
storiesSideSkip: 145px;
storiesCaptionFull: FlatLabel(defaultFlatLabel) {
style: mediaviewCaptionStyle;
textFg: mediaviewCaptionFg;
minWidth: 360px;
}

View File

@ -1217,14 +1217,32 @@ void OverlayWidget::refreshCaptionGeometry() {
- st::mediaviewCaptionPadding.left()
- st::mediaviewCaptionPadding.right()),
_caption.maxWidth());
const auto maxHeight = (_stories ? (_h / 3) : (height() / 4))
const auto maxExpandedOuterHeight = (_stories
? (_h - st::storiesShadowTop.height())
: height());
const auto maxCollapsedOuterHeight = !_stories
? (height() / 4)
: (_h / 3);
const auto maxExpandedHeight = maxExpandedOuterHeight
- st::mediaviewCaptionPadding.top()
- st::mediaviewCaptionPadding.bottom()
- (_stories ? 0 : (2 * st::mediaviewCaptionMargin.height()));
- st::mediaviewCaptionPadding.bottom();
const auto maxCollapsedHeight = maxCollapsedOuterHeight
- st::mediaviewCaptionPadding.top()
- st::mediaviewCaptionPadding.bottom();
const auto lineHeight = st::mediaviewCaptionStyle.font->height;
const auto wantedHeight = _caption.countHeight(captionWidth);
const auto maxHeight = _captionExpanded
? maxExpandedHeight
: maxCollapsedHeight;
const auto captionHeight = std::min(
_caption.countHeight(captionWidth),
wantedHeight,
(maxHeight / lineHeight) * lineHeight);
_captionFitsIfExpanded = _stories
&& (wantedHeight <= maxExpandedHeight);
_captionShownFull = (wantedHeight <= maxCollapsedHeight);
if (_captionShownFull) {
_captionExpanded = false;
}
_captionRect = QRect(
(width() - captionWidth) / 2,
(captionBottom
@ -3000,6 +3018,7 @@ void OverlayWidget::show(OpenRequest request) {
_streamingStartPaused = false;
displayDocument(
document,
anim::activation::normal,
request.cloudTheme()
? *request.cloudTheme()
: Data::CloudTheme(),
@ -3014,9 +3033,11 @@ void OverlayWidget::show(OpenRequest request) {
}
}
void OverlayWidget::displayPhoto(not_null<PhotoData*> photo) {
void OverlayWidget::displayPhoto(
not_null<PhotoData*> photo,
anim::activation activation) {
if (photo->isNull()) {
displayDocument(nullptr);
displayDocument(nullptr, activation);
return;
}
_touchbarDisplay.fire(TouchBarItemType::Photo);
@ -3055,7 +3076,7 @@ void OverlayWidget::displayPhoto(not_null<PhotoData*> photo) {
}
contentSizeChanged();
refreshFromLabel();
displayFinished();
displayFinished(activation);
}
void OverlayWidget::destroyThemePreview() {
@ -3071,15 +3092,16 @@ void OverlayWidget::redisplayContent() {
if (isHidden() || !_session) {
return;
} else if (_photo) {
displayPhoto(_photo);
displayPhoto(_photo, anim::activation::background);
} else {
displayDocument(_document);
displayDocument(_document, anim::activation::background);
}
}
// Empty messages shown as docs: doc can be nullptr.
void OverlayWidget::displayDocument(
DocumentData *doc,
anim::activation activation,
const Data::CloudTheme &cloud,
const StartStreaming &startStreaming) {
_fullScreenVideo = false;
@ -3209,7 +3231,7 @@ void OverlayWidget::displayDocument(
if (_showAsPip && _streamed && _streamed->controls) {
switchToPip();
} else {
displayFinished();
displayFinished(activation);
}
}
@ -3236,7 +3258,8 @@ void OverlayWidget::updateThemePreviewGeometry() {
}
}
void OverlayWidget::displayFinished() {
void OverlayWidget::displayFinished(anim::activation activation) {
_captionExpanded = _captionFitsIfExpanded = _captionShownFull = false;
updateControls();
if (isHidden()) {
_helper->beforeShow(_fullscreen);
@ -3247,6 +3270,8 @@ void OverlayWidget::displayFinished() {
//setAttribute(Qt::WA_DontShowOnScreen, false);
//Ui::Platform::UpdateOverlayed(_window);
showAndActivate();
} else if (activation == anim::activation::background) {
return;
} else if (isMinimized()) {
_helper->beforeShow(_fullscreen);
showAndActivate();
@ -4015,10 +4040,11 @@ void OverlayWidget::storiesJumpTo(Data::FullStoryId id) {
clearStreaming();
_streamingStartPaused = false;
const auto &data = j->media.data;
const auto activation = anim::activation::background;
if (const auto photo = std::get_if<not_null<PhotoData*>>(&data)) {
displayPhoto(*photo);
displayPhoto(*photo, activation);
} else {
displayDocument(v::get<not_null<DocumentData*>>(data));
displayDocument(v::get<not_null<DocumentData*>>(data), activation);
}
}
@ -5302,6 +5328,9 @@ void OverlayWidget::updateOver(QPoint pos) {
} else if (_captionRect.contains(pos)) {
auto textState = _caption.getState(pos - _captionRect.topLeft(), _captionRect.width());
lnk = textState.link;
if (_stories && !_captionShownFull && !lnk) {
lnk = ensureCaptionExpandLink();
}
lnkhost = this;
} else if (_groupThumbs && _groupThumbsRect.contains(pos)) {
const auto point = pos - QPoint(_groupThumbsLeft, _groupThumbsTop);
@ -5373,6 +5402,28 @@ void OverlayWidget::updateOver(QPoint pos) {
}
}
ClickHandlerPtr OverlayWidget::ensureCaptionExpandLink() {
if (!_captionExpandLink) {
const auto toggle = crl::guard(_widget, [=] {
if (!_stories) {
return;
} else if (_captionExpanded) {
_captionExpanded = false;
refreshCaptionGeometry();
update();
} else if (_captionFitsIfExpanded) {
_captionExpanded = true;
refreshCaptionGeometry();
update();
} else {
_stories->showFullCaption();
}
});
_captionExpandLink = std::make_shared<LambdaClickHandler>(toggle);
}
return _captionExpandLink;
}
void OverlayWidget::handleMouseRelease(
QPoint position,
Qt::MouseButton button) {

View File

@ -23,6 +23,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History;
namespace anim {
enum class activation : uchar;
} // namespace anim
namespace Data {
class PhotoMedia;
class DocumentMedia;
@ -148,6 +152,7 @@ private:
OverMore,
OverIcon,
OverVideo,
OverCaption,
};
struct Entity {
std::variant<
@ -356,12 +361,15 @@ private:
void resizeContentByScreenSize();
void recountSkipTop();
void displayPhoto(not_null<PhotoData*> photo);
void displayPhoto(
not_null<PhotoData*> photo,
anim::activation activation = anim::activation::normal);
void displayDocument(
DocumentData *document,
anim::activation activation = anim::activation::normal,
const Data::CloudTheme &cloud = Data::CloudTheme(),
const StartStreaming &startStreaming = StartStreaming());
void displayFinished();
void displayFinished(anim::activation activation);
void redisplayContent();
void findCurrent();
@ -496,6 +504,7 @@ private:
[[nodiscard]] bool topShadowOnTheRight() const;
void applyHideWindowWorkaround();
[[nodiscard]] ClickHandlerPtr ensureCaptionExpandLink();
Window::SessionController *findWindow(bool switchTo = true) const;
@ -558,6 +567,10 @@ private:
int _groupThumbsTop = 0;
Ui::Text::String _caption;
QRect _captionRect;
ClickHandlerPtr _captionExpandLink;
bool _captionShownFull = false;
bool _captionFitsIfExpanded = false;
bool _captionExpanded = false;
int _width = 0;
int _height = 0;