From f2e53ea490711524c21f4b582da62e71dc8c8297 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Jul 2025 17:20:47 +0400 Subject: [PATCH] Try scrolling to the task on jump. --- .../SourceFiles/history/history_widget.cpp | 19 ++++-- .../history/view/history_view_element.cpp | 65 +++++++++++++++++++ .../history/view/history_view_element.h | 5 ++ .../history/view/history_view_list_widget.cpp | 19 ++++-- 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 608e1a26fb..9ab254a5e5 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1498,12 +1498,21 @@ int HistoryWidget::itemTopForHighlight( const auto heightLeft = (visibleAreaHeight - viewHeight); if (heightLeft >= 0) { return std::max(itemTop - (heightLeft / 2), 0); - } else if (const auto sel = itemHighlight(item).range - ; !sel.empty() && !IsSubGroupSelection(sel)) { + } else if (const auto highlight = itemHighlight(item) + ; (!highlight.range.empty() || highlight.todoItemId) + && !IsSubGroupSelection(highlight.range)) { + const auto sel = highlight.range; const auto single = st::messageTextStyle.font->height; - const auto begin = HistoryView::FindViewY(view, sel.from) - single; - const auto end = HistoryView::FindViewY(view, sel.to, begin + single) - + 2 * single; + const auto todoy = sel.empty() + ? HistoryView::FindViewTaskY(view, highlight.todoItemId) + : 0; + const auto begin = sel.empty() + ? (todoy - 4 * single) + : HistoryView::FindViewY(view, sel.from) - single; + const auto end = sel.empty() + ? (todoy + 4 * single) + : (HistoryView::FindViewY(view, sel.to, begin + single) + + 2 * single); auto result = itemTop; if (end > visibleAreaHeight) { result = std::max(result, itemTop + end - visibleAreaHeight); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 2f5aa5b566..dd029ad02d 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -46,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_saved_sublist.h" #include "data/data_session.h" +#include "data/data_todo_list.h" #include "data/data_forum.h" #include "data/data_forum_topic.h" #include "data/data_message_reactions.h" @@ -2455,6 +2456,70 @@ int FindViewY(not_null view, uint16 symbol, int yfrom) { return origin.y() + (yfrom + ytill) / 2; } +int FindViewTaskY(not_null view, int taskId, int yfrom) { + auto request = HistoryView::StateRequest(); + request.flags = Ui::Text::StateRequest::Flag::LookupLink; + const auto single = st::messageTextStyle.font->height; + const auto inner = view->innerGeometry(); + const auto origin = inner.topLeft(); + const auto top = 0; + const auto bottom = view->height(); + if (origin.y() < top + || origin.y() + inner.height() > bottom + || inner.height() <= 0) { + return yfrom; + } + const auto media = view->data()->media(); + const auto todolist = media ? media->todolist() : nullptr; + if (!todolist) { + return yfrom; + } + const auto &items = todolist->items; + const auto indexOf = [&](int id) -> int { + return ranges::find(items, id, &TodoListItem::id) - begin(items); + }; + const auto index = indexOf(taskId); + const auto count = int(items.size()); + if (index == count) { + return yfrom; + } + yfrom = std::max(yfrom - origin.y(), 0); + auto ytill = inner.height() - 1; + const auto middle = (yfrom + ytill) / 2; + const auto fory = [&](int y) { + const auto state = view->textState(origin + QPoint(0, y), request); + const auto &link = state.link; + const auto id = link + ? link->property(kTodoListItemIdProperty).toInt() + : -1; + const auto index = (id >= 0) ? indexOf(id) : int(items.size()); + return (index < count) ? index : (y < middle) ? -1 : count; + }; + auto indexfrom = fory(yfrom); + auto indextill = fory(ytill); + if ((yfrom >= ytill) || (indexfrom >= index)) { + return origin.y() + yfrom; + } else if (indextill <= index) { + return origin.y() + ytill; + } + while (ytill - yfrom >= 2 * single) { + const auto middle = (yfrom + ytill) / 2; + const auto found = fory(middle); + if (found == index + || indexfrom > found + || indextill < found) { + return origin.y() + middle; + } else if (found < index) { + yfrom = middle; + indexfrom = found; + } else { + ytill = middle; + indextill = found; + } + } + return origin.y() + (yfrom + ytill) / 2; +} + Window::SessionController *ExtractController(const ClickContext &context) { const auto my = context.other.value(); if (const auto controller = my.sessionWindow.get()) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 95ffc6df67..34bc8679ce 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -747,6 +747,11 @@ private: uint16 symbol, int yfrom = 0); +[[nodiscard]] int FindViewTaskY( + not_null view, + int taskId, + int yfrom = 0); + [[nodiscard]] Window::SessionController *ExtractController( const ClickContext &context); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 31b2a4354f..6077d1a1f6 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -716,12 +716,21 @@ std::optional ListWidget::scrollTopForView( const auto heightLeft = (available - height); if (heightLeft >= 0) { return std::max(top - (heightLeft / 2), 0); - } else if (const auto sel = _highlighter.state(view->data()).range - ; !sel.empty() && !IsSubGroupSelection(sel)) { + } else if (const auto highlight = _highlighter.state(view->data()) + ; (!highlight.range.empty() || highlight.todoItemId) + && !IsSubGroupSelection(highlight.range)) { + const auto sel = highlight.range; const auto single = st::messageTextStyle.font->height; - const auto begin = HistoryView::FindViewY(view, sel.from) - single; - const auto end = HistoryView::FindViewY(view, sel.to, begin + single) - + 2 * single; + const auto todoy = sel.empty() + ? HistoryView::FindViewTaskY(view, highlight.todoItemId) + : 0; + const auto begin = sel.empty() + ? (todoy - 4 * single) + : HistoryView::FindViewY(view, sel.from) - single; + const auto end = sel.empty() + ? (todoy + 4 * single) + : (HistoryView::FindViewY(view, sel.to, begin + single) + + 2 * single); auto result = top; if (end > available) { result = std::max(result, top + end - available);