mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Highlight reply quote in original message.
This commit is contained in:
parent
8615a25cd1
commit
d1c310de00
29 changed files with 342 additions and 166 deletions
|
@ -579,11 +579,6 @@ bool InnerWidget::elementUnderCursor(
|
|||
return (Element::Hovered() == view);
|
||||
}
|
||||
|
||||
float64 InnerWidget::elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
bool InnerWidget::elementInSelectionMode() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -93,8 +93,6 @@ public:
|
|||
HistoryView::Context elementContext() override;
|
||||
bool elementUnderCursor(
|
||||
not_null<const HistoryView::Element*> view) override;
|
||||
[[nodiscard]] float64 elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const override;
|
||||
bool elementInSelectionMode() override;
|
||||
bool elementIntersectsRange(
|
||||
not_null<const HistoryView::Element*> view,
|
||||
|
|
|
@ -202,10 +202,6 @@ public:
|
|||
not_null<const Element*> view) override {
|
||||
return (Element::Moused() == view);
|
||||
}
|
||||
[[nodiscard]] float64 elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const override {
|
||||
return _widget ? _widget->elementHighlightOpacity(item) : 0.;
|
||||
}
|
||||
bool elementInSelectionMode() override {
|
||||
return _widget ? _widget->inSelectionMode() : false;
|
||||
}
|
||||
|
@ -1060,6 +1056,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
auto clip = e->rect();
|
||||
|
||||
auto context = preparePaintContext(clip);
|
||||
context.highlightPathCache = &_highlightPathCache;
|
||||
_pathGradient->startFrame(
|
||||
0,
|
||||
width(),
|
||||
|
@ -1143,7 +1140,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
} else if (item->isUnreadMention()
|
||||
&& !item->isUnreadMedia()) {
|
||||
readContents.insert(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
_widget->enqueueMessageHighlight(view, {});
|
||||
}
|
||||
}
|
||||
session().data().reactions().poll(item, context.now);
|
||||
|
@ -1185,6 +1182,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
view,
|
||||
selfromy - mtop,
|
||||
seltoy - mtop);
|
||||
context.highlight = _widget->itemHighlight(view->data());
|
||||
view->draw(p, context);
|
||||
processPainted(view, top, height);
|
||||
|
||||
|
@ -1219,9 +1217,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
const auto &sendingAnimation = _controller->sendingAnimation();
|
||||
while (top < drawToY) {
|
||||
const auto height = view->height();
|
||||
const auto item = view->data();
|
||||
if ((context.clip.y() < height)
|
||||
&& (hdrawtop < top + height)
|
||||
&& !sendingAnimation.hasAnimatedMessage(view->data())) {
|
||||
&& !sendingAnimation.hasAnimatedMessage(item)) {
|
||||
context.reactionInfo
|
||||
= _reactionsManager->currentReactionPaintInfo();
|
||||
context.outbg = view->hasOutLayout();
|
||||
|
@ -1229,6 +1228,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
view,
|
||||
selfromy - htop,
|
||||
seltoy - htop);
|
||||
context.highlight = _widget->itemHighlight(item);
|
||||
view->draw(p, context);
|
||||
processPainted(view, top, height);
|
||||
}
|
||||
|
@ -2410,6 +2410,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
_menu->addAction(text, [=] {
|
||||
if (canSendReply) {
|
||||
_widget->replyToMessage({ itemId, quote });
|
||||
if (!quote.empty()) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
} else {
|
||||
HistoryView::Controls::ShowReplyToChatBox(
|
||||
controller->uiShow(),
|
||||
|
@ -3474,11 +3477,6 @@ void HistoryInner::elementStartStickerLoop(
|
|||
_animatedStickersPlayed.emplace(view->data());
|
||||
}
|
||||
|
||||
float64 HistoryInner::elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
return _widget->highlightOpacity(item);
|
||||
}
|
||||
|
||||
void HistoryInner::elementShowPollResults(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId context) {
|
||||
|
|
|
@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/scroll_area.h"
|
||||
#include "history/view/history_view_top_bar_widget.h"
|
||||
|
||||
#include <QtGui/QPainterPath>
|
||||
|
||||
struct ClickContext;
|
||||
struct ClickHandlerContext;
|
||||
|
||||
|
@ -136,8 +138,6 @@ public:
|
|||
int from,
|
||||
int till) const;
|
||||
void elementStartStickerLoop(not_null<const Element*> view);
|
||||
[[nodiscard]] float64 elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
void elementShowPollResults(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId context);
|
||||
|
@ -462,6 +462,7 @@ private:
|
|||
std::optional<Ui::ReportReason> _chooseForReportReason;
|
||||
|
||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||
QPainterPath _highlightPathCache;
|
||||
bool _isChatWide = false;
|
||||
|
||||
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
|
||||
|
|
|
@ -630,7 +630,10 @@ void HistoryMessageReply::setLinkFrom(
|
|||
}
|
||||
};
|
||||
_link = resolvedMessage
|
||||
? JumpToMessageClickHandler(resolvedMessage.get(), holder->fullId())
|
||||
? JumpToMessageClickHandler(
|
||||
resolvedMessage.get(),
|
||||
holder->fullId(),
|
||||
_fields.quote)
|
||||
: resolvedStory
|
||||
? JumpToStoryClickHandler(resolvedStory.get())
|
||||
: (external && !_fields.messageId)
|
||||
|
|
|
@ -259,17 +259,20 @@ bool IsItemScheduledUntilOnline(not_null<const HistoryItem*> item) {
|
|||
|
||||
ClickHandlerPtr JumpToMessageClickHandler(
|
||||
not_null<HistoryItem*> item,
|
||||
FullMsgId returnToId) {
|
||||
FullMsgId returnToId,
|
||||
TextWithEntities highlightPart) {
|
||||
return JumpToMessageClickHandler(
|
||||
item->history()->peer,
|
||||
item->id,
|
||||
returnToId);
|
||||
returnToId,
|
||||
std::move(highlightPart));
|
||||
}
|
||||
|
||||
ClickHandlerPtr JumpToMessageClickHandler(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId msgId,
|
||||
FullMsgId returnToId) {
|
||||
FullMsgId returnToId,
|
||||
TextWithEntities highlightPart) {
|
||||
return std::make_shared<LambdaClickHandler>([=] {
|
||||
const auto separate = Core::App().separateWindowForPeer(peer);
|
||||
const auto controller = separate
|
||||
|
@ -279,6 +282,7 @@ ClickHandlerPtr JumpToMessageClickHandler(
|
|||
auto params = Window::SectionShow{
|
||||
Window::SectionShow::Way::Forward
|
||||
};
|
||||
params.highlightPart = highlightPart;
|
||||
params.origin = Window::SectionShow::OriginMessage{
|
||||
returnToId
|
||||
};
|
||||
|
|
|
@ -120,10 +120,12 @@ struct SendingErrorRequest {
|
|||
[[nodiscard]] ClickHandlerPtr JumpToMessageClickHandler(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId msgId,
|
||||
FullMsgId returnToId = FullMsgId());
|
||||
FullMsgId returnToId = FullMsgId(),
|
||||
TextWithEntities highlightPart = {});
|
||||
[[nodiscard]] ClickHandlerPtr JumpToMessageClickHandler(
|
||||
not_null<HistoryItem*> item,
|
||||
FullMsgId returnToId = FullMsgId());
|
||||
FullMsgId returnToId = FullMsgId(),
|
||||
TextWithEntities highlightPart = {});
|
||||
[[nodiscard]] ClickHandlerPtr JumpToStoryClickHandler(
|
||||
not_null<Data::Story*> story);
|
||||
ClickHandlerPtr JumpToStoryClickHandler(
|
||||
|
|
|
@ -10,12 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_session.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
constexpr auto kAnimationFirstPart = st::activeFadeInDuration
|
||||
/ float64(st::activeFadeInDuration + st::activeFadeOutDuration);
|
||||
|
||||
ElementHighlighter::ElementHighlighter(
|
||||
not_null<Data::Session*> data,
|
||||
ViewForItem viewForItem,
|
||||
|
@ -26,14 +24,15 @@ ElementHighlighter::ElementHighlighter(
|
|||
, _animation(*this) {
|
||||
}
|
||||
|
||||
void ElementHighlighter::enqueue(not_null<Element*> view) {
|
||||
void ElementHighlighter::enqueue(
|
||||
not_null<Element*> view,
|
||||
TextSelection part) {
|
||||
const auto item = view->data();
|
||||
const auto fullId = item->fullId();
|
||||
const auto data = Highlight{ item->fullId(), part };
|
||||
if (_queue.empty() && !_animation.animating()) {
|
||||
highlight(fullId);
|
||||
} else if (_highlightedMessageId != fullId
|
||||
&& !base::contains(_queue, fullId)) {
|
||||
_queue.push_back(fullId);
|
||||
highlight(data.itemId, data.part);
|
||||
} else if (_highlighted != data && !base::contains(_queue, data)) {
|
||||
_queue.push_back(data);
|
||||
checkNextHighlight();
|
||||
}
|
||||
}
|
||||
|
@ -42,47 +41,46 @@ void ElementHighlighter::checkNextHighlight() {
|
|||
if (_animation.animating()) {
|
||||
return;
|
||||
}
|
||||
const auto nextHighlight = [&] {
|
||||
const auto next = [&] {
|
||||
while (!_queue.empty()) {
|
||||
const auto fullId = _queue.front();
|
||||
const auto highlight = _queue.front();
|
||||
_queue.pop_front();
|
||||
if (const auto item = _data->message(fullId)) {
|
||||
if (const auto item = _data->message(highlight.itemId)) {
|
||||
if (_viewForItem(item)) {
|
||||
return fullId;
|
||||
return highlight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FullMsgId();
|
||||
return Highlight();
|
||||
}();
|
||||
if (!nextHighlight) {
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
highlight(nextHighlight);
|
||||
highlight(next.itemId, next.part);
|
||||
}
|
||||
|
||||
float64 ElementHighlighter::progress(
|
||||
Ui::ChatPaintHighlight ElementHighlighter::state(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
if (item->fullId() == _highlightedMessageId) {
|
||||
const auto progress = _animation.progress();
|
||||
return std::min(progress / kAnimationFirstPart, 1.)
|
||||
- ((progress - kAnimationFirstPart) / (1. - kAnimationFirstPart));
|
||||
if (item->fullId() == _highlighted.itemId) {
|
||||
auto result = _animation.state();
|
||||
result.range = _highlighted.part;
|
||||
return result;
|
||||
}
|
||||
return 0.;
|
||||
return {};
|
||||
}
|
||||
|
||||
void ElementHighlighter::highlight(FullMsgId itemId) {
|
||||
void ElementHighlighter::highlight(FullMsgId itemId, TextSelection part) {
|
||||
if (const auto item = _data->message(itemId)) {
|
||||
if (const auto view = _viewForItem(item)) {
|
||||
if (_highlightedMessageId
|
||||
&& _highlightedMessageId != itemId) {
|
||||
if (const auto was = _data->message(_highlightedMessageId)) {
|
||||
if (_highlighted && _highlighted.itemId != itemId) {
|
||||
if (const auto was = _data->message(_highlighted.itemId)) {
|
||||
if (const auto view = _viewForItem(was)) {
|
||||
repaintHighlightedItem(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
_highlightedMessageId = itemId;
|
||||
_animation.start();
|
||||
_highlighted = { itemId, part };
|
||||
_animation.start(!part.empty());
|
||||
|
||||
repaintHighlightedItem(view);
|
||||
}
|
||||
|
@ -105,7 +103,7 @@ void ElementHighlighter::repaintHighlightedItem(
|
|||
}
|
||||
|
||||
void ElementHighlighter::updateMessage() {
|
||||
if (const auto item = _data->message(_highlightedMessageId)) {
|
||||
if (const auto item = _data->message(_highlighted.itemId)) {
|
||||
if (const auto view = _viewForItem(item)) {
|
||||
repaintHighlightedItem(view);
|
||||
}
|
||||
|
@ -114,7 +112,7 @@ void ElementHighlighter::updateMessage() {
|
|||
|
||||
void ElementHighlighter::clear() {
|
||||
_animation.cancel();
|
||||
_highlightedMessageId = FullMsgId();
|
||||
_highlighted = {};
|
||||
_lastHighlightedMessageId = FullMsgId();
|
||||
_queue.clear();
|
||||
}
|
||||
|
@ -125,60 +123,117 @@ ElementHighlighter::AnimationManager::AnimationManager(
|
|||
}
|
||||
|
||||
bool ElementHighlighter::AnimationManager::animating() const {
|
||||
if (anim::Disabled()) {
|
||||
return (_timer && _timer->isActive());
|
||||
} else {
|
||||
if (_timer && _timer->isActive()) {
|
||||
return true;
|
||||
} else if (!anim::Disabled()) {
|
||||
return _simple.animating();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float64 ElementHighlighter::AnimationManager::progress() const {
|
||||
Ui::ChatPaintHighlight ElementHighlighter::AnimationManager::state() const {
|
||||
if (anim::Disabled()) {
|
||||
return (_timer && _timer->isActive()) ? kAnimationFirstPart : 0.;
|
||||
} else {
|
||||
return _simple.value(0.);
|
||||
return {
|
||||
.opacity = !_timer ? 0. : 1.,
|
||||
.collapsion = !_timer ? 0. : _fadingOut ? 1. : 0.,
|
||||
};
|
||||
}
|
||||
return {
|
||||
.opacity = ((!_fadingOut && _collapsing)
|
||||
? 1.
|
||||
: _simple.value(_fadingOut ? 0. : 1.)),
|
||||
.collapsion = ((!_withTextPart || !_collapsing)
|
||||
? 0.
|
||||
: _fadingOut
|
||||
? 1.
|
||||
: _simple.value(1.)),
|
||||
};
|
||||
}
|
||||
|
||||
MsgId ElementHighlighter::latestSingleHighlightedMsgId() const {
|
||||
return _highlightedMessageId
|
||||
? _highlightedMessageId.msg
|
||||
return _highlighted.itemId
|
||||
? _highlighted.itemId.msg
|
||||
: _lastHighlightedMessageId.msg;
|
||||
}
|
||||
|
||||
void ElementHighlighter::AnimationManager::start() {
|
||||
void ElementHighlighter::AnimationManager::start(bool withTextPart) {
|
||||
_withTextPart = withTextPart;
|
||||
const auto finish = [=] {
|
||||
cancel();
|
||||
_parent._lastHighlightedMessageId = base::take(
|
||||
_parent._highlightedMessageId);
|
||||
_parent._highlighted.itemId);
|
||||
_parent.checkNextHighlight();
|
||||
};
|
||||
cancel();
|
||||
if (anim::Disabled()) {
|
||||
_timer.emplace([=] {
|
||||
_parent.updateMessage();
|
||||
finish();
|
||||
if (_withTextPart && !_fadingOut) {
|
||||
_fadingOut = true;
|
||||
_timer->callOnce(st::activeFadeOutDuration);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
_timer->callOnce(st::activeFadeOutDuration);
|
||||
_timer->callOnce(_withTextPart
|
||||
? st::activeFadeInDuration
|
||||
: st::activeFadeOutDuration);
|
||||
_parent.updateMessage();
|
||||
} else {
|
||||
const auto to = 1.;
|
||||
_simple.start(
|
||||
[=](float64 value) {
|
||||
_parent.updateMessage();
|
||||
if (value == to) {
|
||||
finish();
|
||||
if (value == 1.) {
|
||||
if (_withTextPart) {
|
||||
_timer.emplace([=] {
|
||||
_parent.updateMessage();
|
||||
if (_collapsing) {
|
||||
_fadingOut = true;
|
||||
} else {
|
||||
_collapsing = true;
|
||||
}
|
||||
_simple.start([=](float64 value) {
|
||||
_parent.updateMessage();
|
||||
if (_fadingOut && value == 0.) {
|
||||
finish();
|
||||
} else if (!_fadingOut && value == 1.) {
|
||||
_timer->callOnce(
|
||||
st::activeFadeOutDuration);
|
||||
}
|
||||
},
|
||||
_fadingOut ? 1. : 0.,
|
||||
_fadingOut ? 0. : 1.,
|
||||
(_fadingOut
|
||||
? st::activeFadeInDuration
|
||||
: st::fadeWrapDuration));
|
||||
});
|
||||
_timer->callOnce(st::activeFadeInDuration);
|
||||
} else {
|
||||
_fadingOut = true;
|
||||
_simple.start([=](float64 value) {
|
||||
_parent.updateMessage();
|
||||
if (value == 0.) {
|
||||
finish();
|
||||
}
|
||||
},
|
||||
1.,
|
||||
0.,
|
||||
st::activeFadeOutDuration);
|
||||
}
|
||||
}
|
||||
},
|
||||
0.,
|
||||
to,
|
||||
st::activeFadeInDuration + st::activeFadeOutDuration);
|
||||
1.,
|
||||
st::activeFadeInDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void ElementHighlighter::AnimationManager::cancel() {
|
||||
_simple.stop();
|
||||
_timer.reset();
|
||||
_fadingOut = false;
|
||||
_collapsed = false;
|
||||
_collapsing = false;
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -16,6 +16,10 @@ namespace Data {
|
|||
class Session;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
struct ChatPaintHighlight;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class Element;
|
||||
|
@ -29,40 +33,56 @@ public:
|
|||
ViewForItem viewForItem,
|
||||
RepaintView repaintView);
|
||||
|
||||
void enqueue(not_null<Element*> view);
|
||||
void highlight(FullMsgId itemId);
|
||||
void enqueue(not_null<Element*> view, TextSelection part);
|
||||
void highlight(FullMsgId itemId, TextSelection part);
|
||||
void clear();
|
||||
|
||||
[[nodiscard]] float64 progress(not_null<const HistoryItem*> item) const;
|
||||
[[nodiscard]] Ui::ChatPaintHighlight state(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
[[nodiscard]] MsgId latestSingleHighlightedMsgId() const;
|
||||
|
||||
private:
|
||||
void checkNextHighlight();
|
||||
void repaintHighlightedItem(not_null<const Element*> view);
|
||||
void updateMessage();
|
||||
|
||||
class AnimationManager final {
|
||||
public:
|
||||
AnimationManager(ElementHighlighter &parent);
|
||||
[[nodiscard]] bool animating() const;
|
||||
[[nodiscard]] float64 progress() const;
|
||||
void start();
|
||||
[[nodiscard]] Ui::ChatPaintHighlight state() const;
|
||||
void start(bool withTextPart);
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
ElementHighlighter &_parent;
|
||||
Ui::Animations::Simple _simple;
|
||||
std::optional<base::Timer> _timer;
|
||||
bool _withTextPart = false;
|
||||
bool _collapsing = false;
|
||||
bool _collapsed = false;
|
||||
bool _fadingOut = false;
|
||||
|
||||
};
|
||||
|
||||
struct Highlight {
|
||||
FullMsgId itemId;
|
||||
TextSelection part;
|
||||
|
||||
explicit operator bool() const {
|
||||
return itemId.operator bool();
|
||||
}
|
||||
friend inline auto operator<=>(Highlight, Highlight) = default;
|
||||
friend inline bool operator==(Highlight, Highlight) = default;
|
||||
};
|
||||
|
||||
void checkNextHighlight();
|
||||
void repaintHighlightedItem(not_null<const Element*> view);
|
||||
void updateMessage();
|
||||
|
||||
const not_null<Data::Session*> _data;
|
||||
const ViewForItem _viewForItem;
|
||||
const RepaintView _repaintView;
|
||||
|
||||
FullMsgId _highlightedMessageId;
|
||||
Highlight _highlighted;
|
||||
FullMsgId _lastHighlightedMessageId;
|
||||
std::deque<FullMsgId> _queue;
|
||||
std::deque<Highlight> _queue;
|
||||
|
||||
AnimationManager _animation;
|
||||
|
||||
|
|
|
@ -1072,7 +1072,7 @@ void HistoryWidget::initTabbedSelector() {
|
|||
if (!data.recipientOverride) {
|
||||
return true;
|
||||
} else if (data.recipientOverride != _peer) {
|
||||
showHistory(data.recipientOverride->id, ShowAtTheEndMsgId);
|
||||
showHistory(data.recipientOverride->id, ShowAtTheEndMsgId, {});
|
||||
}
|
||||
return (data.recipientOverride == _peer);
|
||||
}) | rpl::start_with_next([=](ChatHelpers::InlineChosen data) {
|
||||
|
@ -1267,13 +1267,14 @@ void HistoryWidget::scrollToAnimationCallback(
|
|||
}
|
||||
|
||||
void HistoryWidget::enqueueMessageHighlight(
|
||||
not_null<HistoryView::Element*> view) {
|
||||
_highlighter.enqueue(view);
|
||||
not_null<HistoryView::Element*> view,
|
||||
TextSelection part) {
|
||||
_highlighter.enqueue(view, part);
|
||||
}
|
||||
|
||||
float64 HistoryWidget::highlightOpacity(
|
||||
Ui::ChatPaintHighlight HistoryWidget::itemHighlight(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
return _highlighter.progress(item);
|
||||
return _highlighter.state(item);
|
||||
}
|
||||
|
||||
int HistoryWidget::itemTopForHighlight(
|
||||
|
@ -1971,9 +1972,10 @@ bool HistoryWidget::insideJumpToEndInsteadOfToUnread() const {
|
|||
void HistoryWidget::showHistory(
|
||||
const PeerId &peerId,
|
||||
MsgId showAtMsgId,
|
||||
bool reload) {
|
||||
const TextWithEntities &highlightPart) {
|
||||
_pinnedClickedId = FullMsgId();
|
||||
_minPinnedId = std::nullopt;
|
||||
_showAtMsgHighlightPart = {};
|
||||
|
||||
const auto wasDialogsEntryState = computeDialogsEntryState();
|
||||
const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
|
||||
|
@ -1985,7 +1987,7 @@ void HistoryWidget::showHistory(
|
|||
controller()->sendingAnimation().clear();
|
||||
_topToast.hide(anim::type::instant);
|
||||
if (_history) {
|
||||
if (_peer->id == peerId && !reload) {
|
||||
if (_peer->id == peerId) {
|
||||
updateForwarding();
|
||||
|
||||
if (showAtMsgId == ShowAtUnreadMsgId
|
||||
|
@ -2021,10 +2023,10 @@ void HistoryWidget::showHistory(
|
|||
).arg(_history->inboxReadTillId().bare
|
||||
).arg(Logs::b(_history->loadedAtBottom())
|
||||
).arg(showAtMsgId.bare));
|
||||
delayedShowAt(showAtMsgId);
|
||||
delayedShowAt(showAtMsgId, highlightPart);
|
||||
} else if (_showAtMsgId != showAtMsgId) {
|
||||
clearAllLoadRequests();
|
||||
setMsgId(showAtMsgId);
|
||||
setMsgId(showAtMsgId, highlightPart);
|
||||
firstLoadMessages();
|
||||
doneShow();
|
||||
}
|
||||
|
@ -2044,7 +2046,7 @@ void HistoryWidget::showHistory(
|
|||
_cornerButtons.skipReplyReturn(skipId);
|
||||
}
|
||||
|
||||
setMsgId(showAtMsgId);
|
||||
setMsgId(showAtMsgId, highlightPart);
|
||||
if (_historyInited) {
|
||||
DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
|
||||
"Showing instant at %4."
|
||||
|
@ -2147,6 +2149,7 @@ void HistoryWidget::showHistory(
|
|||
clearInlineBot();
|
||||
|
||||
_showAtMsgId = showAtMsgId;
|
||||
_showAtMsgHighlightPart = highlightPart;
|
||||
_historyInited = false;
|
||||
_contactStatus = nullptr;
|
||||
|
||||
|
@ -3301,7 +3304,7 @@ void HistoryWidget::messagesReceived(
|
|||
}
|
||||
|
||||
_delayedShowAtRequest = 0;
|
||||
setMsgId(_delayedShowAtMsgId);
|
||||
setMsgId(_delayedShowAtMsgId, _delayedShowAtMsgHighlightPart);
|
||||
historyLoaded();
|
||||
}
|
||||
if (session().supportMode()) {
|
||||
|
@ -3523,9 +3526,16 @@ void HistoryWidget::loadMessagesDown() {
|
|||
});
|
||||
}
|
||||
|
||||
void HistoryWidget::delayedShowAt(MsgId showAtMsgId) {
|
||||
if (!_history
|
||||
|| (_delayedShowAtRequest && _delayedShowAtMsgId == showAtMsgId)) {
|
||||
void HistoryWidget::delayedShowAt(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart) {
|
||||
if (!_history) {
|
||||
return;
|
||||
}
|
||||
if (_delayedShowAtMsgHighlightPart != highlightPart) {
|
||||
_delayedShowAtMsgHighlightPart = highlightPart;
|
||||
}
|
||||
if (_delayedShowAtRequest && _delayedShowAtMsgId == showAtMsgId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4102,7 +4112,12 @@ PeerData *HistoryWidget::peer() const {
|
|||
}
|
||||
|
||||
// Sometimes _showAtMsgId is set directly.
|
||||
void HistoryWidget::setMsgId(MsgId showAtMsgId) {
|
||||
void HistoryWidget::setMsgId(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart) {
|
||||
if (_showAtMsgHighlightPart != highlightPart) {
|
||||
_showAtMsgHighlightPart = highlightPart;
|
||||
}
|
||||
if (_showAtMsgId != showAtMsgId) {
|
||||
_showAtMsgId = showAtMsgId;
|
||||
if (_history) {
|
||||
|
@ -4223,11 +4238,11 @@ void HistoryWidget::cornerButtonsShowAtPosition(
|
|||
).arg(_history->peer->name()
|
||||
).arg(_history->inboxReadTillId().bare
|
||||
).arg(Logs::b(_history->loadedAtBottom())));
|
||||
showHistory(_peer->id, ShowAtUnreadMsgId);
|
||||
showHistory(_peer->id, ShowAtUnreadMsgId, {});
|
||||
} else if (_peer && position.fullId.peer == _peer->id) {
|
||||
showHistory(_peer->id, position.fullId.msg);
|
||||
showHistory(_peer->id, position.fullId.msg, {});
|
||||
} else if (_migrated && position.fullId.peer == _migrated->peer->id) {
|
||||
showHistory(_peer->id, -position.fullId.msg);
|
||||
showHistory(_peer->id, -position.fullId.msg, {});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5678,14 +5693,17 @@ int HistoryWidget::countInitialScrollTop() {
|
|||
const auto item = getItemFromHistoryOrMigrated(_showAtMsgId);
|
||||
const auto itemTop = _list->itemTop(item);
|
||||
if (itemTop < 0) {
|
||||
setMsgId(0);
|
||||
setMsgId(ShowAtUnreadMsgId);
|
||||
controller()->showToast(tr::lng_message_not_found(tr::now));
|
||||
return countInitialScrollTop();
|
||||
} else {
|
||||
const auto view = item->mainView();
|
||||
Assert(view != nullptr);
|
||||
|
||||
enqueueMessageHighlight(view);
|
||||
enqueueMessageHighlight(
|
||||
view,
|
||||
view->selectionFromQuote(
|
||||
base::take(_showAtMsgHighlightPart)));
|
||||
const auto result = itemTopForHighlight(view);
|
||||
createUnreadBarIfBelowVisibleArea(result);
|
||||
return result;
|
||||
|
@ -6377,7 +6395,8 @@ void HistoryWidget::handlePeerMigration() {
|
|||
if (_peer != channel) {
|
||||
showHistory(
|
||||
channel->id,
|
||||
(_showAtMsgId > 0) ? (-_showAtMsgId) : _showAtMsgId);
|
||||
(_showAtMsgId > 0) ? (-_showAtMsgId) : _showAtMsgId,
|
||||
{});
|
||||
channel->session().api().chatParticipants().requestCountDelayed(
|
||||
channel);
|
||||
} else {
|
||||
|
@ -6473,7 +6492,7 @@ bool HistoryWidget::showSlowmodeError() {
|
|||
if (const auto item = _history->latestSendingMessage()) {
|
||||
if (const auto view = item->mainView()) {
|
||||
animatedScrollToItem(item->id);
|
||||
enqueueMessageHighlight(view);
|
||||
enqueueMessageHighlight(view, {});
|
||||
}
|
||||
return tr::lng_slowmode_no_many(tr::now);
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ class SpoilerAnimation;
|
|||
enum class ReportReason;
|
||||
class ChooseThemeController;
|
||||
class ContinuousScroll;
|
||||
struct ChatPaintHighlight;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
|
@ -146,7 +147,9 @@ public:
|
|||
void loadMessages();
|
||||
void loadMessagesDown();
|
||||
void firstLoadMessages();
|
||||
void delayedShowAt(MsgId showAtMsgId);
|
||||
void delayedShowAt(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart);
|
||||
|
||||
bool updateReplaceMediaButton();
|
||||
void updateFieldPlaceholder();
|
||||
|
@ -160,7 +163,9 @@ public:
|
|||
|
||||
History *history() const;
|
||||
PeerData *peer() const;
|
||||
void setMsgId(MsgId showAtMsgId);
|
||||
void setMsgId(
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart = {});
|
||||
MsgId msgId() const;
|
||||
|
||||
bool hasTopBarShadow() const {
|
||||
|
@ -177,8 +182,10 @@ public:
|
|||
|
||||
bool touchScroll(const QPoint &delta);
|
||||
|
||||
void enqueueMessageHighlight(not_null<HistoryView::Element*> view);
|
||||
[[nodiscard]] float64 highlightOpacity(
|
||||
void enqueueMessageHighlight(
|
||||
not_null<HistoryView::Element*> view,
|
||||
TextSelection part);
|
||||
[[nodiscard]] Ui::ChatPaintHighlight itemHighlight(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
|
||||
MessageIdsList getSelectedItems() const;
|
||||
|
@ -218,7 +225,10 @@ public:
|
|||
void fastShowAtEnd(not_null<History*> history);
|
||||
bool applyDraft(
|
||||
FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
|
||||
void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false);
|
||||
void showHistory(
|
||||
const PeerId &peer,
|
||||
MsgId showAtMsgId,
|
||||
const TextWithEntities &highlightPart);
|
||||
void setChooseReportMessagesDetails(
|
||||
Ui::ReportReason reason,
|
||||
Fn<void(MessageIdsList)> callback);
|
||||
|
@ -684,12 +694,14 @@ private:
|
|||
bool _canSendMessages = false;
|
||||
bool _canSendTexts = false;
|
||||
MsgId _showAtMsgId = ShowAtUnreadMsgId;
|
||||
TextWithEntities _showAtMsgHighlightPart;
|
||||
|
||||
int _firstLoadRequest = 0; // Not real mtpRequestId.
|
||||
int _preloadRequest = 0; // Not real mtpRequestId.
|
||||
int _preloadDownRequest = 0; // Not real mtpRequestId.
|
||||
|
||||
MsgId _delayedShowAtMsgId = -1;
|
||||
TextWithEntities _delayedShowAtMsgHighlightPart;
|
||||
int _delayedShowAtRequest = 0; // Not real mtpRequestId.
|
||||
|
||||
History *_supportPreloadHistory = nullptr;
|
||||
|
|
|
@ -111,11 +111,6 @@ bool DefaultElementDelegate::elementUnderCursor(
|
|||
return false;
|
||||
}
|
||||
|
||||
float64 DefaultElementDelegate::elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
bool DefaultElementDelegate::elementInSelectionMode() {
|
||||
return false;
|
||||
}
|
||||
|
@ -605,18 +600,13 @@ void Element::paintCustomHighlight(
|
|||
int y,
|
||||
int height,
|
||||
not_null<const HistoryItem*> item) const {
|
||||
const auto opacity = delegate()->elementHighlightOpacity(item);
|
||||
const auto opacity = context.highlight.opacity;
|
||||
if (opacity == 0.) {
|
||||
return;
|
||||
}
|
||||
const auto o = p.opacity();
|
||||
p.setOpacity(o * opacity);
|
||||
p.fillRect(
|
||||
0,
|
||||
y,
|
||||
width(),
|
||||
height,
|
||||
context.st->msgSelectOverlay());
|
||||
p.fillRect(0, y, width(), height, context.st->msgSelectOverlay());
|
||||
p.setOpacity(o);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,8 +69,6 @@ class ElementDelegate {
|
|||
public:
|
||||
virtual Context elementContext() = 0;
|
||||
virtual bool elementUnderCursor(not_null<const Element*> view) = 0;
|
||||
[[nodiscard]] virtual float64 elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const = 0;
|
||||
virtual bool elementInSelectionMode() = 0;
|
||||
virtual bool elementIntersectsRange(
|
||||
not_null<const Element*> view,
|
||||
|
@ -120,8 +118,6 @@ public:
|
|||
class DefaultElementDelegate : public ElementDelegate {
|
||||
public:
|
||||
bool elementUnderCursor(not_null<const Element*> view) override;
|
||||
[[nodiscard]] float64 elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const override;
|
||||
bool elementInSelectionMode() override;
|
||||
bool elementIntersectsRange(
|
||||
not_null<const Element*> view,
|
||||
|
|
|
@ -707,8 +707,15 @@ bool ListWidget::isBelowPosition(Data::MessagePosition position) const {
|
|||
return _items.front()->data()->position() > position;
|
||||
}
|
||||
|
||||
void ListWidget::highlightMessage(FullMsgId itemId) {
|
||||
_highlighter.highlight(itemId);
|
||||
void ListWidget::highlightMessage(
|
||||
FullMsgId itemId,
|
||||
const TextWithEntities &highlightPart) {
|
||||
const auto view = !highlightPart.empty()
|
||||
? viewForItem(itemId)
|
||||
: nullptr;
|
||||
_highlighter.highlight(
|
||||
itemId,
|
||||
view ? view->selectionFromQuote(highlightPart) : TextSelection());
|
||||
}
|
||||
|
||||
void ListWidget::showAroundPosition(
|
||||
|
@ -741,12 +748,12 @@ bool ListWidget::jumpToBottomInsteadOfUnread() const {
|
|||
|
||||
void ListWidget::showAtPosition(
|
||||
Data::MessagePosition position,
|
||||
anim::type animated,
|
||||
const Window::SectionShow ¶ms,
|
||||
Fn<void(bool found)> done) {
|
||||
const auto showAtUnread = (position == Data::UnreadMessagePosition);
|
||||
|
||||
if (showAtUnread && jumpToBottomInsteadOfUnread()) {
|
||||
showAtPosition(Data::MaxMessagePosition, animated, std::move(done));
|
||||
showAtPosition(Data::MaxMessagePosition, params, std::move(done));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -766,24 +773,24 @@ void ListWidget::showAtPosition(
|
|||
_bar = {};
|
||||
}
|
||||
checkUnreadBarCreation();
|
||||
return showAtPositionNow(position, animated, done);
|
||||
return showAtPositionNow(position, params, done);
|
||||
});
|
||||
} else if (!showAtPositionNow(position, animated, done)) {
|
||||
} else if (!showAtPositionNow(position, params, done)) {
|
||||
showAroundPosition(position, [=] {
|
||||
return showAtPositionNow(position, animated, done);
|
||||
return showAtPositionNow(position, params, done);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool ListWidget::showAtPositionNow(
|
||||
Data::MessagePosition position,
|
||||
anim::type animated,
|
||||
const Window::SectionShow ¶ms,
|
||||
Fn<void(bool found)> done) {
|
||||
if (const auto scrollTop = scrollTopForPosition(position)) {
|
||||
computeScrollTo(*scrollTop, position, animated);
|
||||
computeScrollTo(*scrollTop, position, params.animated);
|
||||
if (position != Data::MaxMessagePosition
|
||||
&& position != Data::UnreadMessagePosition) {
|
||||
highlightMessage(position.fullId);
|
||||
highlightMessage(position.fullId, params.highlightPart);
|
||||
}
|
||||
if (done) {
|
||||
const auto found = !position.fullId.peer
|
||||
|
@ -1655,11 +1662,6 @@ bool ListWidget::elementUnderCursor(
|
|||
return (_overElement == view);
|
||||
}
|
||||
|
||||
float64 ListWidget::elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
return _highlighter.progress(item);
|
||||
}
|
||||
|
||||
bool ListWidget::elementInSelectionMode() {
|
||||
return inSelectionMode();
|
||||
}
|
||||
|
@ -2108,6 +2110,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
= _reactionsManager->currentReactionPaintInfo();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(view);
|
||||
context.highlight = _highlighter.state(item);
|
||||
view->draw(p, context);
|
||||
}
|
||||
if (_translateTracker) {
|
||||
|
@ -2142,7 +2145,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
} else if (item->isUnreadMention()
|
||||
&& !item->isUnreadMedia()) {
|
||||
readContents.insert(item);
|
||||
_highlighter.enqueue(view);
|
||||
_highlighter.enqueue(view, {});
|
||||
}
|
||||
}
|
||||
session->data().reactions().poll(item, context.now);
|
||||
|
|
|
@ -48,6 +48,10 @@ struct ChosenReaction;
|
|||
struct ButtonParameters;
|
||||
} // namespace HistoryView::Reactions
|
||||
|
||||
namespace Window {
|
||||
struct SectionShow;
|
||||
} // namespace Window
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
struct TextState;
|
||||
|
@ -227,11 +231,13 @@ public:
|
|||
[[nodiscard]] bool animatedScrolling() const;
|
||||
bool isAbovePosition(Data::MessagePosition position) const;
|
||||
bool isBelowPosition(Data::MessagePosition position) const;
|
||||
void highlightMessage(FullMsgId itemId);
|
||||
void highlightMessage(
|
||||
FullMsgId itemId,
|
||||
const TextWithEntities &highlightPart);
|
||||
|
||||
void showAtPosition(
|
||||
Data::MessagePosition position,
|
||||
anim::type animated = anim::type::normal,
|
||||
const Window::SectionShow ¶ms,
|
||||
Fn<void(bool found)> done = nullptr);
|
||||
void refreshViewer();
|
||||
|
||||
|
@ -292,8 +298,6 @@ public:
|
|||
// ElementDelegate interface.
|
||||
Context elementContext() override;
|
||||
bool elementUnderCursor(not_null<const Element*> view) override;
|
||||
[[nodiscard]] float64 elementHighlightOpacity(
|
||||
not_null<const HistoryItem*> item) const override;
|
||||
bool elementInSelectionMode() override;
|
||||
bool elementIntersectsRange(
|
||||
not_null<const Element*> view,
|
||||
|
@ -430,7 +434,7 @@ private:
|
|||
Fn<bool()> overrideInitialScroll);
|
||||
bool showAtPositionNow(
|
||||
Data::MessagePosition position,
|
||||
anim::type animated,
|
||||
const Window::SectionShow ¶ms,
|
||||
Fn<void(bool found)> done);
|
||||
|
||||
Ui::ChatPaintContext preparePaintContext(const QRect &clip) const;
|
||||
|
|
|
@ -1018,6 +1018,10 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
p.translate(-reactionsPosition);
|
||||
}
|
||||
|
||||
if (context.highlightPathCache) {
|
||||
context.highlightInterpolateTo = g;
|
||||
context.highlightPathCache->clear();
|
||||
}
|
||||
if (bubble) {
|
||||
if (displayFromName()
|
||||
&& item->displayFrom()
|
||||
|
@ -1110,6 +1114,7 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
- (_bottomInfo.height() - st::msgDateFont->height));
|
||||
}
|
||||
auto textSelection = context.selection;
|
||||
auto highlightRange = context.highlight.range;
|
||||
const auto mediaHeight = mediaDisplayed ? media->height() : 0;
|
||||
const auto paintMedia = [&](int top) {
|
||||
if (!mediaDisplayed) {
|
||||
|
@ -1130,6 +1135,10 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
context.reactionInfo->effectOffset -= add;
|
||||
}
|
||||
}
|
||||
if (context.highlightPathCache
|
||||
&& !context.highlightPathCache->isEmpty()) {
|
||||
context.highlightPathCache->translate(mediaPosition);
|
||||
}
|
||||
p.translate(-mediaPosition);
|
||||
};
|
||||
if (mediaDisplayed && _invertMedia) {
|
||||
|
@ -1141,8 +1150,12 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
+ mediaHeight
|
||||
+ (mediaOnBottom ? 0 : st::mediaInBubbleSkip));
|
||||
textSelection = media->skipSelection(textSelection);
|
||||
highlightRange = media->skipSelection(highlightRange);
|
||||
}
|
||||
paintText(p, trect, context.withSelection(textSelection));
|
||||
auto copy = context;
|
||||
copy.selection = textSelection;
|
||||
copy.highlight.range = highlightRange;
|
||||
paintText(p, trect, copy);
|
||||
if (mediaDisplayed && !_invertMedia) {
|
||||
paintMedia(trect.y() + trect.height() - mediaHeight);
|
||||
if (context.reactionInfo && !displayInfo && !_reactions) {
|
||||
|
@ -1224,6 +1237,20 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
|
||||
p.restoreTextPalette();
|
||||
|
||||
if (context.highlightPathCache
|
||||
&& !context.highlightPathCache->isEmpty()) {
|
||||
const auto alpha = int(0.25
|
||||
* context.highlight.collapsion
|
||||
* context.highlight.opacity
|
||||
* 255);
|
||||
if (alpha > 0) {
|
||||
context.highlightPathCache->setFillRule(Qt::WindingFill);
|
||||
auto color = context.messageStyle()->textPalette.linkFg->c;
|
||||
color.setAlpha(alpha);
|
||||
p.fillPath(*context.highlightPathCache, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (roll) {
|
||||
p.restore();
|
||||
}
|
||||
|
@ -1651,6 +1678,7 @@ void Message::paintText(
|
|||
width());
|
||||
trect.setY(trect.y() + botTop->height);
|
||||
}
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
|
@ -1663,6 +1691,7 @@ void Message::paintText(
|
|||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = context.selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2732,10 +2761,13 @@ TextWithEntities Message::selectedQuote(
|
|||
|
||||
TextSelection Message::selectionFromQuote(
|
||||
const TextWithEntities "e) const {
|
||||
if (quote.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto item = data();
|
||||
const auto &translated = item->translatedText();
|
||||
const auto &original = item->originalText();
|
||||
if (&translated != &original || quote.empty()) {
|
||||
if (&translated != &original) {
|
||||
return {};
|
||||
} else if (hasVisibleText()) {
|
||||
const auto media = this->media();
|
||||
|
|
|
@ -262,7 +262,7 @@ void PinnedWidget::showAtPosition(
|
|||
FullMsgId originId) {
|
||||
_inner->showAtPosition(
|
||||
position,
|
||||
anim::type::normal,
|
||||
{},
|
||||
_cornerButtons.doneJumpFrom(position.fullId, originId));
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,7 @@ void PinnedWidget::restoreState(not_null<PinnedMemento*> memento) {
|
|||
? FullMsgId(_history->peer->id, highlight)
|
||||
: FullMsgId(_migratedPeer->id, -highlight)),
|
||||
.date = TimeId(0),
|
||||
}, anim::type::instant);
|
||||
}, { Window::SectionShow::Way::Forward, anim::type::instant });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1859,16 +1859,22 @@ void RepliesWidget::finishSending() {
|
|||
refreshTopBarActiveChat();
|
||||
}
|
||||
|
||||
void RepliesWidget::showAtPosition(
|
||||
Data::MessagePosition position,
|
||||
FullMsgId originItemId) {
|
||||
showAtPosition(position, originItemId, {});
|
||||
}
|
||||
|
||||
void RepliesWidget::showAtPosition(
|
||||
Data::MessagePosition position,
|
||||
FullMsgId originItemId,
|
||||
anim::type animated) {
|
||||
const Window::SectionShow ¶ms) {
|
||||
_lastShownAt = position.fullId;
|
||||
controller()->setActiveChatEntry(activeChat());
|
||||
const auto ignore = (position.fullId.msg == _rootId);
|
||||
_inner->showAtPosition(
|
||||
position,
|
||||
animated,
|
||||
params,
|
||||
_cornerButtons.doneJumpFrom(position.fullId, originItemId, ignore));
|
||||
}
|
||||
|
||||
|
@ -2030,7 +2036,7 @@ bool RepliesWidget::showMessage(
|
|||
const auto originItemId = (_cornerButtons.replyReturn() != originMessage)
|
||||
? originMessage->fullId()
|
||||
: FullMsgId();
|
||||
showAtPosition(message->position(), originItemId);
|
||||
showAtPosition(message->position(), originItemId, params);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2136,7 +2142,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
|
|||
showAtPosition(Data::MessagePosition{
|
||||
.fullId = FullMsgId(_history->peer->id, highlight),
|
||||
.date = TimeId(0),
|
||||
}, {}, anim::type::instant);
|
||||
}, {}, { Window::SectionShow::Way::Forward, anim::type::instant });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,8 +208,11 @@ private:
|
|||
void showAtEnd();
|
||||
void showAtPosition(
|
||||
Data::MessagePosition position,
|
||||
FullMsgId originItemId = {},
|
||||
anim::type animated = anim::type::normal);
|
||||
FullMsgId originItemId = {});
|
||||
void showAtPosition(
|
||||
Data::MessagePosition position,
|
||||
FullMsgId originItemId,
|
||||
const Window::SectionShow ¶ms);
|
||||
void finishSending();
|
||||
|
||||
void setupComposeControls();
|
||||
|
|
|
@ -858,7 +858,7 @@ void ScheduledWidget::showAtPosition(
|
|||
FullMsgId originId) {
|
||||
_inner->showAtPosition(
|
||||
position,
|
||||
anim::type::normal,
|
||||
{},
|
||||
_cornerButtons.doneJumpFrom(position.fullId, originId));
|
||||
}
|
||||
|
||||
|
|
|
@ -744,6 +744,7 @@ void Document::draw(
|
|||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
p.setPen(stm->historyTextFg);
|
||||
_parent->prepareCustomEmojiPaint(p, context, captioned->caption);
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
captioned->caption.draw(p, {
|
||||
.position = { st::msgPadding.left(), captiontop },
|
||||
.availableWidth = captionw,
|
||||
|
@ -756,6 +757,7 @@ void Document::draw(
|
|||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,6 +229,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
|
|||
if (!_caption.isEmpty()) {
|
||||
p.setPen(stm->historyTextFg);
|
||||
_parent->prepareCustomEmojiPaint(p, context, _caption);
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(
|
||||
st::msgPadding.left(),
|
||||
|
@ -243,6 +244,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
|
|||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = context.selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
});
|
||||
} else if (!inWebPage) {
|
||||
auto fullRight = paintx + paintw;
|
||||
|
|
|
@ -716,6 +716,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
_parent->width());
|
||||
top += botTop->height;
|
||||
}
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(st::msgPadding.left(), top),
|
||||
.availableWidth = captionw,
|
||||
|
@ -728,6 +729,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = context.selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
});
|
||||
} else if (!inWebPage && !skipDrawingSurrounding) {
|
||||
auto fullRight = paintx + usex + usew;
|
||||
|
|
|
@ -325,12 +325,13 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
|
|||
: IsGroupItemSelection(selection, i)
|
||||
? FullSelection
|
||||
: TextSelection());
|
||||
const auto highlightOpacity = IsGroupItemSelection(
|
||||
context.highlight.range,
|
||||
i
|
||||
) ? context.highlight.opacity : 0.;
|
||||
if (textSelection) {
|
||||
selection = part.content->skipSelection(selection);
|
||||
}
|
||||
const auto highlightOpacity = (_mode == Mode::Grid)
|
||||
? _parent->delegate()->elementHighlightOpacity(part.item)
|
||||
: 0.;
|
||||
if (!part.cache.isNull()) {
|
||||
wasCache = true;
|
||||
}
|
||||
|
@ -361,6 +362,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
|
|||
const auto stm = context.messageStyle();
|
||||
p.setPen(stm->historyTextFg);
|
||||
_parent->prepareCustomEmojiPaint(p, context, _caption);
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(
|
||||
st::msgPadding.left(),
|
||||
|
@ -375,6 +377,7 @@ void GroupedMedia::draw(Painter &p, const PaintContext &context) const {
|
|||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = context.selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
});
|
||||
} else if (_parent->media() == this) {
|
||||
auto fullRight = width();
|
||||
|
|
|
@ -401,6 +401,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
_parent->width());
|
||||
top += botTop->height;
|
||||
}
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(st::msgPadding.left(), top),
|
||||
.availableWidth = captionw,
|
||||
|
@ -413,6 +414,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.selection = context.selection,
|
||||
.highlight = highlightRequest ? &*highlightRequest : nullptr,
|
||||
});
|
||||
} else if (!inWebPage) {
|
||||
auto fullRight = paintx + paintw;
|
||||
|
|
|
@ -1411,7 +1411,7 @@ void MainWidget::showHistory(
|
|||
&& way != Way::Forward) {
|
||||
clearBotStartToken(_history->peer());
|
||||
}
|
||||
_history->showHistory(peerId, showAtMsgId);
|
||||
_history->showHistory(peerId, showAtMsgId, params.highlightPart);
|
||||
if (alreadyThatPeer && params.reapplyLocalDraft) {
|
||||
_history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
|
||||
}
|
||||
|
@ -1772,7 +1772,7 @@ void MainWidget::showNewSection(
|
|||
} else {
|
||||
_mainSection = std::move(newMainSection);
|
||||
_history->finishAnimating();
|
||||
_history->showHistory(0, 0);
|
||||
_history->showHistory(0, 0, {});
|
||||
|
||||
if (const auto entry = _mainSection->activeChat(); entry.key) {
|
||||
_controller->setActiveChatEntry(entry);
|
||||
|
|
|
@ -142,6 +142,12 @@ struct BackgroundEmojiData {
|
|||
uint8 colorIndexPlusOne);
|
||||
};
|
||||
|
||||
struct ChatPaintHighlight {
|
||||
float64 opacity = 0.;
|
||||
float64 collapsion = 0.;
|
||||
TextSelection range;
|
||||
};
|
||||
|
||||
struct ChatPaintContext {
|
||||
not_null<const ChatStyle*> st;
|
||||
const BubblePattern *bubblesPattern = nullptr;
|
||||
|
@ -149,11 +155,15 @@ struct ChatPaintContext {
|
|||
QRect viewport;
|
||||
QRect clip;
|
||||
TextSelection selection;
|
||||
ChatPaintHighlight highlight;
|
||||
QPainterPath *highlightPathCache = nullptr;
|
||||
mutable QRect highlightInterpolateTo;
|
||||
crl::time now = 0;
|
||||
|
||||
void translate(int x, int y) {
|
||||
viewport.translate(x, y);
|
||||
clip.translate(x, y);
|
||||
highlightInterpolateTo.translate(x, y);
|
||||
}
|
||||
void translate(QPoint point) {
|
||||
translate(point.x(), point.y());
|
||||
|
@ -181,6 +191,19 @@ struct ChatPaintContext {
|
|||
result.selection = selection;
|
||||
return result;
|
||||
}
|
||||
[[nodiscard]] auto computeHighlightCache() const
|
||||
-> std::optional<Ui::Text::HighlightInfoRequest> {
|
||||
if (highlight.range.empty() || highlight.collapsion <= 0.) {
|
||||
return {};
|
||||
}
|
||||
return Ui::Text::HighlightInfoRequest{
|
||||
.range = highlight.range,
|
||||
.interpolateTo = highlightInterpolateTo,
|
||||
.interpolateProgress = (1. - highlight.collapsion),
|
||||
.outPath = highlightPathCache,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// This is supported only in unwrapped media for now.
|
||||
enum class SkipDrawingParts {
|
||||
|
|
|
@ -166,6 +166,7 @@ struct SectionShow {
|
|||
return copy;
|
||||
}
|
||||
|
||||
TextWithEntities highlightPart;
|
||||
Way way = Way::Forward;
|
||||
anim::type animated = anim::type::normal;
|
||||
anim::activation activation = anim::activation::normal;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit dc8313f6ae6c87572512424818b9e24e7ef2383e
|
||||
Subproject commit 6dc93b53a1c4d237dea0a458d2b02c4171529f18
|
Loading…
Add table
Reference in a new issue