diff --git a/Telegram/SourceFiles/data/data_msg_id.h b/Telegram/SourceFiles/data/data_msg_id.h index 68437b6fd3..02547ef602 100644 --- a/Telegram/SourceFiles/data/data_msg_id.h +++ b/Telegram/SourceFiles/data/data_msg_id.h @@ -172,6 +172,16 @@ inline QDebug operator<<(QDebug debug, const FullMsgId &fullMsgId) { Q_DECLARE_METATYPE(FullMsgId); +struct MessageHighlightId { + TextWithEntities quote; + int quoteOffset = 0; + int todoItemId = 0; + + [[nodiscard]] bool empty() const { + return quote.empty() && !todoItemId; + } +}; + struct FullReplyTo { FullMsgId messageId; TextWithEntities quote; @@ -181,6 +191,9 @@ struct FullReplyTo { int quoteOffset = 0; int todoItemId = 0; + [[nodiscard]] MessageHighlightId highlight() const { + return { quote, quoteOffset, todoItemId }; + } [[nodiscard]] bool replying() const { return messageId || (storyId && storyId.peer); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 74c6f2d297..ffcd9dce43 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -898,10 +898,7 @@ void Widget::chosenRow(const ChosenRow &row) { } else if (const auto topic = row.key.topic()) { auto params = Window::SectionShow( Window::SectionShow::Way::ClearStack); - params.highlightPart.text = _searchState.query; - if (!params.highlightPart.empty()) { - params.highlightPartOffsetHint = kSearchQueryOffsetHint; - } + params.highlight = Window::SearchHighlightId(_searchState.query); if (row.newWindow) { controller()->showInNewWindow( Window::SeparateId(topic), @@ -972,10 +969,7 @@ void Widget::chosenRow(const ChosenRow &row) { ) ? ShowAtUnreadMsgId : row.message.fullId.msg; auto params = Window::SectionShow( Window::SectionShow::Way::ClearStack); - params.highlightPart.text = _searchState.query; - if (!params.highlightPart.empty()) { - params.highlightPartOffsetHint = kSearchQueryOffsetHint; - } + params.highlight = Window::SearchHighlightId(_searchState.query); if (row.newWindow) { controller()->showInNewWindow(peer, showAtMsgId); } else { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 06415a1aef..9ab81e0b75 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -622,10 +622,10 @@ void HistoryInner::setupSwipeReplyAndBack() { : still)->fullId(); _widget->replyToMessage({ .messageId = replyToItemId, - .quote = selected.text, - .quoteOffset = selected.offset, + .quote = selected.highlight.quote, + .quoteOffset = selected.highlight.quoteOffset, }); - if (!selected.text.empty()) { + if (!selected.highlight.quote.empty()) { _widget->clearSelected(); } }; @@ -2712,16 +2712,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { Ui::Text::FixAmpersandInAction); const auto replyToItem = selected.item ? selected.item : item; const auto itemId = replyToItem->fullId(); - const auto quote = selected.text; - const auto quoteOffset = selected.offset; _menu->addAction(std::move(text), [=] { _widget->replyToMessage({ .messageId = itemId, - .quote = quote, - .quoteOffset = quoteOffset, + .quote = selected.highlight.quote, + .quoteOffset = selected.highlight.quoteOffset, .todoItemId = todoListTaskId, }); - if (!quote.empty()) { + if (!selected.highlight.quote.empty()) { _widget->clearSelected(); } }, &st::menuIconReply); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 2200581101..429fffee59 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -909,12 +909,26 @@ void HistoryItem::updateServiceDependent(bool force) { } if (!dependent->lnk) { + auto todoItemId = 0; + if (const auto done = Get()) { + const auto &items = !done->completed.empty() + ? done->completed + : done->incompleted; + if (items.size() == 1) { + todoItemId = items.front(); + } + } else if (const auto append = Get()) { + if (append->list.size() == 1) { + todoItemId = append->list.front().id; + } + } dependent->lnk = JumpToMessageClickHandler( (dependent->peerId ? _history->owner().peer(dependent->peerId) : _history->peer), dependent->msgId, - fullId()); + fullId(), + { .todoItemId = todoItemId }); } auto gotDependencyItem = false; if (!dependent->msg) { diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index cebe57e5c4..57bddb387e 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -713,22 +713,19 @@ bool IsItemScheduledUntilOnline(not_null item) { ClickHandlerPtr JumpToMessageClickHandler( not_null item, FullMsgId returnToId, - TextWithEntities highlightPart, - int highlightPartOffsetHint) { + MessageHighlightId highlight) { return JumpToMessageClickHandler( item->history()->peer, item->id, returnToId, - std::move(highlightPart), - highlightPartOffsetHint); + std::move(highlight)); } ClickHandlerPtr JumpToMessageClickHandler( not_null peer, MsgId msgId, FullMsgId returnToId, - TextWithEntities highlightPart, - int highlightPartOffsetHint) { + MessageHighlightId highlight) { return std::make_shared([=] { const auto separate = Core::App().separateWindowFor(peer); const auto controller = separate @@ -738,8 +735,7 @@ ClickHandlerPtr JumpToMessageClickHandler( auto params = Window::SectionShow{ Window::SectionShow::Way::Forward }; - params.highlightPart = highlightPart; - params.highlightPartOffsetHint = highlightPartOffsetHint; + params.highlight = highlight; params.origin = Window::SectionShow::OriginMessage{ returnToId }; diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index d1c472dd58..f6906136c8 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -229,13 +229,11 @@ private: not_null peer, MsgId msgId, FullMsgId returnToId = FullMsgId(), - TextWithEntities highlightPart = {}, - int highlightPartOffsetHint = 0); + MessageHighlightId highlight = {}); [[nodiscard]] ClickHandlerPtr JumpToMessageClickHandler( not_null item, FullMsgId returnToId = FullMsgId(), - TextWithEntities highlightPart = {}, - int highlightPartOffsetHint = 0); + MessageHighlightId highlight = {}); [[nodiscard]] ClickHandlerPtr JumpToStoryClickHandler( not_null story); ClickHandlerPtr JumpToStoryClickHandler( diff --git a/Telegram/SourceFiles/history/history_view_highlight_manager.cpp b/Telegram/SourceFiles/history/history_view_highlight_manager.cpp index ee07a1571f..5ff3f71a6d 100644 --- a/Telegram/SourceFiles/history/history_view_highlight_manager.cpp +++ b/Telegram/SourceFiles/history/history_view_highlight_manager.cpp @@ -65,6 +65,7 @@ Ui::ChatPaintHighlight ElementHighlighter::state( if (item->fullId() == _highlighted.itemId) { auto result = _animation.state(); result.range = _highlighted.part; + result.todoItemId = _highlighted.todoListId; return result; } return {}; @@ -82,19 +83,27 @@ ElementHighlighter::Highlight ElementHighlighter::computeHighlight( const auto i = ranges::find(group->items, item); if (i != end(group->items)) { const auto index = int(i - begin(group->items)); - if (quote.text.empty()) { + if (quote.highlight.empty()) { return { leaderId, AddGroupItemSelection({}, index) }; } else if (const auto leaderView = _viewForItem(leader)) { - return { leaderId, leaderView->selectionFromQuote(quote) }; + return { + leaderId, + leaderView->selectionFromQuote(quote), + quote.highlight.todoItemId, + }; } } - return { leaderId }; - } else if (quote.text.empty()) { - return { item->fullId() }; + return { leaderId, {}, quote.highlight.todoItemId }; + } else if (quote.highlight.quote.empty()) { + return { item->fullId(), {}, quote.highlight.todoItemId }; } else if (const auto view = _viewForItem(item)) { - return { item->fullId(), view->selectionFromQuote(quote) }; + return { + item->fullId(), + view->selectionFromQuote(quote), + quote.highlight.todoItemId, + }; } - return { item->fullId() }; + return { item->fullId(), {}, quote.highlight.todoItemId }; } void ElementHighlighter::highlight(Highlight data) { @@ -108,7 +117,7 @@ void ElementHighlighter::highlight(Highlight data) { } } _highlighted = data; - _animation.start(!data.part.empty() + _animation.start((!data.part.empty() || data.todoListId) && !IsSubGroupSelection(data.part)); repaintHighlightedItem(view); diff --git a/Telegram/SourceFiles/history/history_view_highlight_manager.h b/Telegram/SourceFiles/history/history_view_highlight_manager.h index 5a3b43ca8e..08ac68e44d 100644 --- a/Telegram/SourceFiles/history/history_view_highlight_manager.h +++ b/Telegram/SourceFiles/history/history_view_highlight_manager.h @@ -65,6 +65,7 @@ private: struct Highlight { FullMsgId itemId; TextSelection part; + int todoListId = 0; explicit operator bool() const { return itemId.operator bool(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2d4a93f1ce..608e1a26fb 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5640,8 +5640,7 @@ void HistoryWidget::switchToSearch(QString query) { const auto item = activation.item; auto params = ::Window::SectionShow( ::Window::SectionShow::Way::ClearStack); - params.highlightPart = { activation.query }; - params.highlightPartOffsetHint = kSearchQueryOffsetHint; + params.highlight = Window::SearchHighlightId(activation.query); controller()->showPeerHistory( item->history()->peer->id, params, @@ -6743,8 +6742,7 @@ int HistoryWidget::countInitialScrollTop() { enqueueMessageHighlight({ item, - base::take(_showAtMsgParams.highlightPart), - base::take(_showAtMsgParams.highlightPartOffsetHint), + base::take(_showAtMsgParams.highlight), }); const auto result = itemTopForHighlight(view); createUnreadBarIfBelowVisibleArea(result); @@ -7501,12 +7499,7 @@ void HistoryWidget::editDraftOptions() { void HistoryWidget::jumpToReply(FullReplyTo to) { if (const auto item = session().data().message(to.messageId)) { - JumpToMessageClickHandler( - item, - {}, - to.quote, - to.quoteOffset - )->onClick({}); + JumpToMessageClickHandler(item, {}, to.highlight())->onClick({}); } } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp index f398211636..4d56475f3b 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp @@ -783,7 +783,7 @@ void DraftOptionsBox( box->setTitle(hasLink ? tr::lng_link_options_header() : hasReply - ? (state->quote.current().text.empty() + ? (state->quote.current().highlight.quote.empty() ? tr::lng_reply_options_header() : tr::lng_reply_options_quote()) : (forwardCount == 1) @@ -807,10 +807,12 @@ void DraftOptionsBox( auto result = draft.reply; if (const auto current = state->quote.current()) { result.messageId = current.item->fullId(); - result.quote = current.text; - result.quoteOffset = current.offset; + result.quote = current.highlight.quote; + result.quoteOffset = current.highlight.quoteOffset; +// result.todoItemId = current.highlight.todoItemId; } else { result.quote = {}; +// result.todoItemId = 0; } return result; }; @@ -1112,7 +1114,7 @@ void DraftOptionsBox( state->quote.value(), state->shown.value() ) | rpl::map([=](const SelectedQuote "e, Section shown) { - return (quote.text.empty() || shown != Section::Reply) + return (quote.highlight.quote.empty() || shown != Section::Reply) ? tr::lng_settings_save() : tr::lng_reply_quote_selected(); }) | rpl::flatten_latest(); diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp index c360b3916d..4f8595266a 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp @@ -119,12 +119,10 @@ rpl::producer RootViewContent( ChatMemento::ChatMemento( ChatViewId id, MsgId highlightId, - const TextWithEntities &highlightPart, - int highlightPartOffsetHint) + MessageHighlightId highlight) : _id(id) -, _highlightPart(highlightPart) -, _highlightPartOffsetHint(highlightPartOffsetHint) -, _highlightId(highlightId) { +, _highlightId(highlightId) +, _highlight(std::move(highlight)) { if (highlightId || _id.sublist) { _list.setAroundPosition({ .fullId = FullMsgId(_id.history->peer->id, highlightId), @@ -876,12 +874,7 @@ void ChatWidget::setupComposeControls() { _composeControls->jumpToItemRequests( ) | rpl::start_with_next([=](FullReplyTo to) { if (const auto item = session().data().message(to.messageId)) { - JumpToMessageClickHandler( - item, - {}, - to.quote, - to.quoteOffset - )->onClick({}); + JumpToMessageClickHandler(item, {}, to.highlight())->onClick({}); } }, lifetime()); @@ -1039,8 +1032,9 @@ void ChatWidget::setupSwipeReplyAndBack() { : still)->fullId(); _inner->replyToMessageRequestNotify({ .messageId = replyToItemId, - .quote = selected.text, - .quoteOffset = selected.offset, + .quote = selected.highlight.quote, + .quoteOffset = selected.highlight.quoteOffset, + .todoItemId = selected.highlight.todoItemId, }); }; return result; @@ -2639,8 +2633,7 @@ void ChatWidget::restoreState(not_null memento) { auto params = Window::SectionShow( Window::SectionShow::Way::Forward, anim::type::instant); - params.highlightPart = memento->highlightPart(); - params.highlightPartOffsetHint = memento->highlightPartOffsetHint(); + params.highlight = memento->highlight(); showAtPosition(Data::MessagePosition{ .fullId = FullMsgId(_peer->id, highlight), .date = TimeId(0), @@ -3443,8 +3436,7 @@ bool ChatWidget::searchInChatEmbedded( const auto item = activation.item; auto params = ::Window::SectionShow( ::Window::SectionShow::Way::ClearStack); - params.highlightPart = { activation.query }; - params.highlightPartOffsetHint = kSearchQueryOffsetHint; + params.highlight = Window::SearchHighlightId(activation.query); controller()->showPeerHistory( item->history()->peer->id, params, diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.h b/Telegram/SourceFiles/history/view/history_view_chat_section.h index 87b4604c04..97030e58e1 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.h +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.h @@ -461,8 +461,7 @@ public: explicit ChatMemento( ChatViewId id, MsgId highlightId = 0, - const TextWithEntities &highlightPart = {}, - int highlightPartOffsetHint = 0); + MessageHighlightId highlight = {}); struct Comments { }; @@ -511,20 +510,16 @@ public: [[nodiscard]] MsgId highlightId() const { return _highlightId; } - [[nodiscard]] const TextWithEntities &highlightPart() const { - return _highlightPart; - } - [[nodiscard]] int highlightPartOffsetHint() const { - return _highlightPartOffsetHint; + [[nodiscard]] const MessageHighlightId &highlight() const { + return _highlight; } private: void setupTopicViewer(); ChatViewId _id; - const TextWithEntities _highlightPart; - const int _highlightPartOffsetHint = 0; const MsgId _highlightId = 0; + const MessageHighlightId _highlight; ListMemento _list; std::shared_ptr _replies; QVector _replyReturns; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 109ccc2fb0..fbdbe38827 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -643,18 +643,18 @@ bool AddReplyToMessageAction( ? request.link->property(kTodoListItemIdProperty).toInt() : 0; const auto "e = request.quote; - auto text = (quote.text.empty() - ? tr::lng_context_reply_msg - : todoListTaskId + auto text = (todoListTaskId ? tr::lng_context_reply_to_task + : quote.highlight.quote.empty() + ? tr::lng_context_reply_msg : tr::lng_context_quote_and_reply)( tr::now, Ui::Text::FixAmpersandInAction); menu->addAction(std::move(text), [=, itemId = item->fullId()] { list->replyToMessageRequestNotify({ .messageId = itemId, - .quote = quote.text, - .quoteOffset = quote.offset, + .quote = quote.highlight.quote, + .quoteOffset = quote.highlight.quoteOffset, .todoItemId = todoListTaskId, }, base::IsCtrlPressed()); }, &st::menuIconReply); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 3c1c1aeff9..2f5aa5b566 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -1341,9 +1341,18 @@ void Element::validateText() { if (const auto done = item->Get()) { if (!done->completed.empty() && !done->incompleted.empty()) { + const auto todoItemId = (done->incompleted.size() == 1) + ? done->incompleted.front() + : 0; setServicePreMessage( item->composeTodoIncompleted(done), - done->lnk); + JumpToMessageClickHandler( + (done->peerId + ? history()->owner().peer(done->peerId) + : history()->peer), + done->msgId, + item->fullId(), + { .todoItemId = todoItemId })); } else { setServicePreMessage({}); } @@ -2190,17 +2199,18 @@ TextSelection Element::FindSelectionFromQuote( const SelectedQuote "e) { Expects(quote.item != nullptr); - if (quote.text.empty()) { + const auto &rich = quote.highlight.quote; + if (rich.empty()) { return {}; } const auto &original = quote.item->originalText(); - if (quote.offset == kSearchQueryOffsetHint) { + if (quote.highlight.quoteOffset == kSearchQueryOffsetHint) { return ApplyModificationsFrom( - FindSearchQueryHighlight(original.text, quote.text.text), + FindSearchQueryHighlight(original.text, rich.text), text); } const auto length = int(original.text.size()); - const auto qlength = int(quote.text.text.size()); + const auto qlength = int(rich.text.size()); const auto checkAt = [&](int offset) { return TextSelection{ uint16(offset), @@ -2211,7 +2221,7 @@ TextSelection Element::FindSelectionFromQuote( if (offset > length - qlength) { return TextSelection(); } - const auto i = original.text.indexOf(quote.text.text, offset); + const auto i = original.text.indexOf(rich.text, offset); return (i >= 0) ? checkAt(i) : TextSelection(); }; const auto findOneBefore = [&](int offset) { @@ -2220,7 +2230,7 @@ TextSelection Element::FindSelectionFromQuote( } const auto end = std::min(offset + qlength - 1, length); const auto from = end - length - 1; - const auto i = original.text.lastIndexOf(quote.text.text, from); + const auto i = original.text.lastIndexOf(rich.text, from); return (i >= 0) ? checkAt(i) : TextSelection(); }; const auto findAfter = [&](int offset) { @@ -2258,7 +2268,7 @@ TextSelection Element::FindSelectionFromQuote( ? before : after; }; - auto result = findTwoWays(quote.offset); + auto result = findTwoWays(quote.highlight.quoteOffset); if (result.empty()) { return {}; } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 6f7a4edea0..95ffc6df67 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -357,12 +357,11 @@ struct TopicButton { struct SelectedQuote { HistoryItem *item = nullptr; - TextWithEntities text; - int offset = 0; + MessageHighlightId highlight; bool overflown = false; explicit operator bool() const { - return item && !text.empty(); + return item && !highlight.quote.empty(); } friend inline bool operator==(SelectedQuote, SelectedQuote) = default; }; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 7a86adb72c..31b2a4354f 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -818,10 +818,9 @@ bool ListWidget::isBelowPosition(Data::MessagePosition position) const { void ListWidget::highlightMessage( FullMsgId itemId, - const TextWithEntities &part, - int partOffsetHint) { + const MessageHighlightId &highlight) { if (const auto view = viewForItem(itemId)) { - _highlighter.highlight({ view->data(), part, partOffsetHint }); + _highlighter.highlight({ view->data(), highlight }); } } @@ -899,11 +898,8 @@ bool ListWidget::showAtPositionNow( } if (position != Data::MaxMessagePosition && position != Data::UnreadMessagePosition) { - const auto hasHighlight = !params.highlightPart.empty(); - highlightMessage( - position.fullId, - params.highlightPart, - params.highlightPartOffsetHint); + const auto hasHighlight = !params.highlight.empty(); + highlightMessage(position.fullId, params.highlight); if (hasHighlight) { // We may want to scroll to a different part of the message. scrollTop = scrollTopForPosition(position); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 8f4a354b8c..f313810626 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -314,8 +314,7 @@ public: bool isBelowPosition(Data::MessagePosition position) const; void highlightMessage( FullMsgId itemId, - const TextWithEntities &part, - int partOffsetHint); + const MessageHighlightId &highlight); void showAtPosition( Data::MessagePosition position, diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 8c80a15e5c..52af74295e 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -3303,7 +3303,7 @@ TextSelection Message::selectionFromQuote( const SelectedQuote "e) const { Expects(quote.item != nullptr); - if (quote.text.empty()) { + if (quote.highlight.quote.empty()) { return {}; } const auto item = quote.item; diff --git a/Telegram/SourceFiles/history/view/history_view_reply.cpp b/Telegram/SourceFiles/history/view/history_view_reply.cpp index 9df096b737..5d18086720 100644 --- a/Telegram/SourceFiles/history/view/history_view_reply.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reply.cpp @@ -384,10 +384,11 @@ void Reply::setLinkFrom( const auto &fields = data->fields(); const auto externalChannelId = peerToChannel(fields.externalPeerId); const auto messageId = fields.messageId; - const auto quote = fields.manualQuote - ? fields.quote - : TextWithEntities(); - const auto quoteOffset = fields.quoteOffset; + const auto highlight = MessageHighlightId{ + .quote = fields.manualQuote ? fields.quote : TextWithEntities(), + .quoteOffset = int(fields.quoteOffset), + .todoItemId = fields.todoItemId, + }; const auto returnToId = view->data()->fullId(); const auto externalLink = [=](ClickContext context) { const auto my = context.other.value(); @@ -410,8 +411,7 @@ void Reply::setLinkFrom( channel, messageId, returnToId, - quote, - quoteOffset + highlight )->onClick(context); } else { controller->showPeerInfo(channel); @@ -432,7 +432,7 @@ void Reply::setLinkFrom( const auto message = data->resolvedMessage.get(); const auto story = data->resolvedStory.get(); _link = message - ? JumpToMessageClickHandler(message, returnToId, quote, quoteOffset) + ? JumpToMessageClickHandler(message, returnToId, highlight) : story ? JumpToStoryClickHandler(story) : (data->external() diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 3842e2eb2e..a6ac266d36 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -430,12 +430,8 @@ void ScheduledWidget::setupComposeControls() { if (item->isScheduled() && item->history() == _history) { showAtPosition(item->position()); } else { - JumpToMessageClickHandler( - item, - {}, - to.quote, - to.quoteOffset - )->onClick({}); + const auto highlight = to.highlight(); + JumpToMessageClickHandler(item, {}, highlight)->onClick({}); } } }, lifetime()); diff --git a/Telegram/SourceFiles/history/view/media/history_view_todo_list.cpp b/Telegram/SourceFiles/history/view/media/history_view_todo_list.cpp index 46bf0ab28d..e468a46eae 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_todo_list.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_todo_list.cpp @@ -482,6 +482,7 @@ void TodoList::draw(Painter &p, const PaintContext &context) const { paintw, width(), context); + appendTaskHighlight(task.id, tshift, height, context); if (was) { heavy = true; } else if (!task.userpic.null()) { @@ -576,6 +577,33 @@ int TodoList::paintTask( return height; } +void TodoList::appendTaskHighlight( + int id, + int top, + int height, + const PaintContext &context) const { + if (context.highlight.todoItemId != id + || context.highlight.collapsion <= 0.) { + return; + } + const auto to = context.highlightInterpolateTo; + const auto toProgress = (1. - context.highlight.collapsion); + if (toProgress >= 1.) { + context.highlightPathCache->addRect(to); + } else if (toProgress <= 0.) { + context.highlightPathCache->addRect(0, top, width(), height); + } else { + const auto lerp = [=](int from, int to) { + return from + (to - from) * toProgress; + }; + context.highlightPathCache->addRect( + lerp(0, to.x()), + lerp(top, to.y()), + lerp(width(), to.width()), + lerp(height, to.height())); + } +} + void TodoList::paintRadio( Painter &p, const Task &task, diff --git a/Telegram/SourceFiles/history/view/media/history_view_todo_list.h b/Telegram/SourceFiles/history/view/media/history_view_todo_list.h index 733c5c2ab0..b17e7853cc 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_todo_list.h +++ b/Telegram/SourceFiles/history/view/media/history_view_todo_list.h @@ -117,6 +117,11 @@ private: int top, int paintw, const PaintContext &context) const; + void appendTaskHighlight( + int id, + int top, + int height, + const PaintContext &context) const; void radialAnimationCallback() const; diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 0d47d16611..8ddf1901bc 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -153,6 +153,7 @@ struct ChatPaintHighlight { float64 opacity = 0.; float64 collapsion = 0.; TextSelection range; + int todoItemId = 0; }; struct ChatPaintContext { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 19c93e4016..e58806b515 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -358,6 +358,14 @@ void DateClickHandler::onClick(ClickContext context) const { } } +MessageHighlightId SearchHighlightId(const QString &query) { + auto result = MessageHighlightId{ .quote = { query } }; + if (!result.quote.empty()) { + result.quoteOffset = kSearchQueryOffsetHint; + } + return result; +} + SessionNavigation::SessionNavigation(not_null session) : _session(session) , _api(&_session->mtp()) { @@ -1146,8 +1154,7 @@ void SessionNavigation::showRepliesForMessage( .repliesRootId = rootId, }, commentId, - params.highlightPart, - params.highlightPartOffsetHint); + params.highlight); memento->setFromTopic(topic); showSection(std::move(memento), params); return; @@ -1269,8 +1276,7 @@ void SessionNavigation::showSublist( .sublist = sublist, }, itemId, - params.highlightPart, - params.highlightPartOffsetHint); + params.highlight); showSection(std::move(memento), params); } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index ab9290d70a..7bde45c28d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -166,8 +166,9 @@ struct SectionShow { return copy; } - TextWithEntities highlightPart; + MessageHighlightId highlight; int highlightPartOffsetHint = 0; + int highlightTodoItemId = 0; std::optional videoTimestamp; Way way = Way::Forward; anim::type animated = anim::type::normal; @@ -182,6 +183,8 @@ struct SectionShow { }; +[[nodiscard]] MessageHighlightId SearchHighlightId(const QString &query); + class SessionController; class SessionNavigation : public base::has_weak_ptr {