mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement nice expandable story caption viewer.
This commit is contained in:
parent
8cc90c3373
commit
5aa6102903
11 changed files with 259 additions and 137 deletions
|
@ -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";
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue