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