mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support highlighting correct quoted part.
This commit is contained in:
parent
dcc326e17f
commit
3a67e4f1f4
8 changed files with 101 additions and 24 deletions
|
@ -2929,6 +2929,7 @@ FullReplyTo HistoryItem::replyTo() const {
|
||||||
if (const auto id = fields.messageId) {
|
if (const auto id = fields.messageId) {
|
||||||
result.messageId = { replyToPeer, id };
|
result.messageId = { replyToPeer, id };
|
||||||
result.quote = fields.quote;
|
result.quote = fields.quote;
|
||||||
|
result.quoteOffset = fields.quoteOffset;
|
||||||
}
|
}
|
||||||
if (const auto id = fields.storyId) {
|
if (const auto id = fields.storyId) {
|
||||||
result.storyId = { replyToPeer, id };
|
result.storyId = { replyToPeer, id };
|
||||||
|
|
|
@ -6331,7 +6331,12 @@ void HistoryWidget::editDraftOptions() {
|
||||||
|
|
||||||
void HistoryWidget::jumpToReply(FullReplyTo to) {
|
void HistoryWidget::jumpToReply(FullReplyTo to) {
|
||||||
if (const auto item = session().data().message(to.messageId)) {
|
if (const auto item = session().data().message(to.messageId)) {
|
||||||
JumpToMessageClickHandler(item, {}, to.quote)->onClick({});
|
JumpToMessageClickHandler(
|
||||||
|
item,
|
||||||
|
{},
|
||||||
|
to.quote,
|
||||||
|
to.quoteOffset
|
||||||
|
)->onClick({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7158,17 +7163,22 @@ void HistoryWidget::clearFieldText(
|
||||||
|
|
||||||
void HistoryWidget::replyToMessage(FullReplyTo id) {
|
void HistoryWidget::replyToMessage(FullReplyTo id) {
|
||||||
if (const auto item = session().data().message(id.messageId)) {
|
if (const auto item = session().data().message(id.messageId)) {
|
||||||
replyToMessage(item, id.quote);
|
replyToMessage(item, id.quote, id.quoteOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::replyToMessage(
|
void HistoryWidget::replyToMessage(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
TextWithEntities quote) {
|
TextWithEntities quote,
|
||||||
|
int quoteOffset) {
|
||||||
if (isJoinChannel()) {
|
if (isJoinChannel()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_processingReplyTo = { .messageId = item->fullId(), .quote = quote };
|
_processingReplyTo = {
|
||||||
|
.messageId = item->fullId(),
|
||||||
|
.quote = quote,
|
||||||
|
.quoteOffset = quoteOffset,
|
||||||
|
};
|
||||||
_processingReplyItem = item;
|
_processingReplyItem = item;
|
||||||
processReply();
|
processReply();
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,8 @@ public:
|
||||||
void replyToMessage(FullReplyTo id);
|
void replyToMessage(FullReplyTo id);
|
||||||
void replyToMessage(
|
void replyToMessage(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
TextWithEntities quote = {});
|
TextWithEntities quote = {},
|
||||||
|
int quoteOffset = 0);
|
||||||
void editMessage(FullMsgId itemId);
|
void editMessage(FullMsgId itemId);
|
||||||
void editMessage(not_null<HistoryItem*> item);
|
void editMessage(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
|
|
@ -601,7 +601,11 @@ void DraftOptionsBox(
|
||||||
rpl::lifetime resolveLifetime;
|
rpl::lifetime resolveLifetime;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
state->quote = SelectedQuote{ replyItem, draft.reply.quote };
|
state->quote = SelectedQuote{
|
||||||
|
replyItem,
|
||||||
|
draft.reply.quote,
|
||||||
|
draft.reply.quoteOffset,
|
||||||
|
};
|
||||||
state->webpage = draft.webpage;
|
state->webpage = draft.webpage;
|
||||||
state->preview = previewData;
|
state->preview = previewData;
|
||||||
state->shown = previewData ? Section::Link : Section::Reply;
|
state->shown = previewData ? Section::Link : Section::Reply;
|
||||||
|
|
|
@ -1659,22 +1659,71 @@ TextSelection Element::FindSelectionFromQuote(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto &original = quote.item->originalText();
|
const auto &original = quote.item->originalText();
|
||||||
auto result = TextSelection();
|
const auto length = int(original.text.size());
|
||||||
auto offset = 0;
|
const auto qlength = int(quote.text.text.size());
|
||||||
while (true) {
|
const auto checkAt = [&](int offset) {
|
||||||
const auto i = original.text.indexOf(quote.text.text, offset);
|
const auto selection = TextSelection{
|
||||||
if (i < 0) {
|
uint16(offset),
|
||||||
return {};
|
uint16(offset + qlength),
|
||||||
}
|
|
||||||
auto selection = TextSelection{
|
|
||||||
uint16(i),
|
|
||||||
uint16(i + quote.text.text.size()),
|
|
||||||
};
|
};
|
||||||
if (CheckQuoteEntities(quote.text.entities, original, selection)) {
|
return CheckQuoteEntities(quote.text.entities, original, selection)
|
||||||
result = selection;
|
? selection
|
||||||
break;
|
: TextSelection{ uint16(offset + 1), uint16(offset + 1) };
|
||||||
|
};
|
||||||
|
const auto findOneAfter = [&](int offset) {
|
||||||
|
if (offset > length - qlength) {
|
||||||
|
return TextSelection();
|
||||||
}
|
}
|
||||||
offset = i + 1;
|
const auto i = original.text.indexOf(quote.text.text, offset);
|
||||||
|
return (i >= 0) ? checkAt(i) : TextSelection();
|
||||||
|
};
|
||||||
|
const auto findOneBefore = [&](int offset) {
|
||||||
|
if (!offset) {
|
||||||
|
return TextSelection();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
return (i >= 0) ? checkAt(i) : TextSelection();
|
||||||
|
};
|
||||||
|
const auto findAfter = [&](int offset) {
|
||||||
|
while (true) {
|
||||||
|
const auto result = findOneAfter(offset);
|
||||||
|
if (!result.empty() || result == TextSelection()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
offset = result.from;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto findBefore = [&](int offset) {
|
||||||
|
while (true) {
|
||||||
|
const auto result = findOneBefore(offset);
|
||||||
|
if (!result.empty() || result == TextSelection()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
offset = result.from - 2;
|
||||||
|
if (offset < 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto findTwoWays = [&](int offset) {
|
||||||
|
const auto after = findAfter(offset);
|
||||||
|
if (after.empty()) {
|
||||||
|
return findBefore(offset);
|
||||||
|
} else if (after.from == offset) {
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
const auto before = findBefore(offset);
|
||||||
|
return before.empty()
|
||||||
|
? after
|
||||||
|
: (offset - before.from < after.from - offset)
|
||||||
|
? before
|
||||||
|
: after;
|
||||||
|
};
|
||||||
|
auto result = findTwoWays(quote.offset);
|
||||||
|
if (result.empty()) {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
for (const auto &modification : text.modifications()) {
|
for (const auto &modification : text.modifications()) {
|
||||||
if (modification.position >= result.to) {
|
if (modification.position >= result.to) {
|
||||||
|
|
|
@ -797,7 +797,12 @@ void RepliesWidget::setupComposeControls() {
|
||||||
_composeControls->jumpToItemRequests(
|
_composeControls->jumpToItemRequests(
|
||||||
) | rpl::start_with_next([=](FullReplyTo to) {
|
) | rpl::start_with_next([=](FullReplyTo to) {
|
||||||
if (const auto item = session().data().message(to.messageId)) {
|
if (const auto item = session().data().message(to.messageId)) {
|
||||||
JumpToMessageClickHandler(item, {}, to.quote)->onClick({});
|
JumpToMessageClickHandler(
|
||||||
|
item,
|
||||||
|
{},
|
||||||
|
to.quote,
|
||||||
|
to.quoteOffset
|
||||||
|
)->onClick({});
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
|
|
@ -270,6 +270,7 @@ void Reply::setLinkFrom(
|
||||||
const auto quote = fields.manualQuote
|
const auto quote = fields.manualQuote
|
||||||
? fields.quote
|
? fields.quote
|
||||||
: TextWithEntities();
|
: TextWithEntities();
|
||||||
|
const auto quoteOffset = fields.quoteOffset;
|
||||||
const auto returnToId = view->data()->fullId();
|
const auto returnToId = view->data()->fullId();
|
||||||
const auto externalLink = [=](ClickContext context) {
|
const auto externalLink = [=](ClickContext context) {
|
||||||
const auto my = context.other.value<ClickHandlerContext>();
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
@ -292,7 +293,8 @@ void Reply::setLinkFrom(
|
||||||
channel,
|
channel,
|
||||||
messageId,
|
messageId,
|
||||||
returnToId,
|
returnToId,
|
||||||
quote
|
quote,
|
||||||
|
quoteOffset
|
||||||
)->onClick(context);
|
)->onClick(context);
|
||||||
} else {
|
} else {
|
||||||
controller->showPeerInfo(channel);
|
controller->showPeerInfo(channel);
|
||||||
|
@ -313,7 +315,7 @@ void Reply::setLinkFrom(
|
||||||
const auto message = data->resolvedMessage.get();
|
const auto message = data->resolvedMessage.get();
|
||||||
const auto story = data->resolvedStory.get();
|
const auto story = data->resolvedStory.get();
|
||||||
_link = message
|
_link = message
|
||||||
? JumpToMessageClickHandler(message, returnToId, quote)
|
? JumpToMessageClickHandler(message, returnToId, quote, quoteOffset)
|
||||||
: story
|
: story
|
||||||
? JumpToStoryClickHandler(story)
|
? JumpToStoryClickHandler(story)
|
||||||
: (data->external()
|
: (data->external()
|
||||||
|
|
|
@ -281,7 +281,12 @@ void ScheduledWidget::setupComposeControls() {
|
||||||
if (item->isScheduled() && item->history() == _history) {
|
if (item->isScheduled() && item->history() == _history) {
|
||||||
showAtPosition(item->position());
|
showAtPosition(item->position());
|
||||||
} else {
|
} else {
|
||||||
JumpToMessageClickHandler(item, {}, to.quote)->onClick({});
|
JumpToMessageClickHandler(
|
||||||
|
item,
|
||||||
|
{},
|
||||||
|
to.quote,
|
||||||
|
to.quoteOffset
|
||||||
|
)->onClick({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
Loading…
Add table
Reference in a new issue