mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Merge tag 'v6.7.5' into dev
This commit is contained in:
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
||||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||
constexpr auto AppFile = "AyuGram"_cs;
|
||||
constexpr auto AppVersion = 6007004;
|
||||
constexpr auto AppVersionStr = "6.7.4";
|
||||
constexpr auto AppVersion = 6007005;
|
||||
constexpr auto AppVersionStr = "6.7.5";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -2065,6 +2065,16 @@ rpl::producer<not_null<ViewElement*>> Session::viewResizeRequest() const {
|
||||
return _viewResizeRequest.events();
|
||||
}
|
||||
|
||||
void Session::notifyViewHeightAdjusted(
|
||||
not_null<ViewElement*> view,
|
||||
int delta) {
|
||||
_viewHeightAdjusted.fire({ view, delta });
|
||||
}
|
||||
|
||||
rpl::producer<Session::ViewHeightAdjusted> Session::viewHeightAdjusted() const {
|
||||
return _viewHeightAdjusted.events();
|
||||
}
|
||||
|
||||
void Session::requestItemShowHighlight(not_null<HistoryItem*> item) {
|
||||
_itemShowHighlightRequest.fire_copy(item);
|
||||
}
|
||||
@@ -2107,6 +2117,14 @@ void Session::requestItemTextRefresh(not_null<HistoryItem*> item) {
|
||||
}
|
||||
}
|
||||
|
||||
void Session::requestItemTextRefreshStreaming(
|
||||
not_null<HistoryItem*> item) {
|
||||
enumerateItemViews(item, [&](not_null<ViewElement*> view) {
|
||||
view->itemTextUpdatedStreaming();
|
||||
});
|
||||
requestItemResize(item);
|
||||
}
|
||||
|
||||
void Session::registerRestricted(
|
||||
not_null<const HistoryItem*> item,
|
||||
const QString &reason) {
|
||||
|
||||
@@ -395,11 +395,18 @@ public:
|
||||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemResizeRequest() const;
|
||||
void requestViewResize(not_null<ViewElement*> view);
|
||||
[[nodiscard]] rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
|
||||
struct ViewHeightAdjusted {
|
||||
not_null<ViewElement*> view;
|
||||
int delta = 0;
|
||||
};
|
||||
void notifyViewHeightAdjusted(not_null<ViewElement*> view, int delta);
|
||||
[[nodiscard]] rpl::producer<ViewHeightAdjusted> viewHeightAdjusted() const;
|
||||
void requestItemShowHighlight(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> itemShowHighlightRequest() const;
|
||||
void requestItemViewRefresh(not_null<const HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemViewRefreshRequest() const;
|
||||
void requestItemTextRefresh(not_null<HistoryItem*> item);
|
||||
void requestItemTextRefreshStreaming(not_null<HistoryItem*> item);
|
||||
void requestUnreadReactionsAnimation(not_null<HistoryItem*> item);
|
||||
void notifyHistoryUnloaded(not_null<const History*> history);
|
||||
[[nodiscard]] rpl::producer<not_null<const History*>> historyUnloaded() const;
|
||||
@@ -1137,6 +1144,7 @@ private:
|
||||
rpl::event_stream<RequestViewRepaint> _viewRepaintRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
|
||||
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
|
||||
rpl::event_stream<ViewHeightAdjusted> _viewHeightAdjusted;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemShowHighlightRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemViewRefreshRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemTextRefreshRequest;
|
||||
|
||||
@@ -365,6 +365,8 @@ enum class MessageFlag : uint64 {
|
||||
CanBeSummarized = (1ULL << 58),
|
||||
HasUnreadPollVote = (1ULL << 59),
|
||||
|
||||
TextAppearing = (1ULL << 60),
|
||||
|
||||
AyuNoForwards = (1ULL << 63),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
|
||||
@@ -1494,6 +1494,38 @@ void History::applyServiceChanges(
|
||||
});
|
||||
}
|
||||
|
||||
void History::viewHeightAdjusted(not_null<Element*> view, int delta) {
|
||||
if (view->data()->mainView() == view) {
|
||||
mainViewHeightAdjusted(view, delta);
|
||||
}
|
||||
owner().notifyViewHeightAdjusted(view, delta);
|
||||
}
|
||||
|
||||
void History::mainViewHeightAdjusted(not_null<Element*> view, int delta) {
|
||||
const auto viewInBlock = view->indexInBlock();
|
||||
if (viewInBlock < 0 || _width <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto block = view->block();
|
||||
for (auto i = viewInBlock + 1, count = int(block->messages.size())
|
||||
; i != count
|
||||
; ++i) {
|
||||
const auto view = block->messages[i].get();
|
||||
view->setY(view->y() + delta);
|
||||
}
|
||||
block->resizeGetHeight(
|
||||
_width,
|
||||
HistoryBlock::ResizeRequest::ResizePending);
|
||||
const auto blockInHistory = block->indexInHistory();
|
||||
for (auto i = blockInHistory + 1, count = int(blocks.size())
|
||||
; i != count
|
||||
; ++i) {
|
||||
const auto block = blocks[i].get();
|
||||
block->setY(block->y() + delta);
|
||||
}
|
||||
_height += delta;
|
||||
}
|
||||
|
||||
void History::mainViewRemoved(
|
||||
not_null<HistoryBlock*> block,
|
||||
not_null<HistoryView::Element*> view) {
|
||||
|
||||
@@ -420,6 +420,7 @@ public:
|
||||
PeerId dataPeerId,
|
||||
const MTPmessages_Messages &data);
|
||||
|
||||
void viewHeightAdjusted(not_null<Element*> view, int delta);
|
||||
void forgetScrollState() {
|
||||
scrollTopItem = nullptr;
|
||||
}
|
||||
@@ -558,6 +559,7 @@ private:
|
||||
void mainViewRemoved(
|
||||
not_null<HistoryBlock*> block,
|
||||
not_null<Element*> view);
|
||||
void mainViewHeightAdjusted(not_null<Element*> view, int delta);
|
||||
|
||||
TimeId adjustedChatListTimeId() const override;
|
||||
void changedChatListPinHook() override;
|
||||
|
||||
@@ -4113,7 +4113,8 @@ FullReplyTo HistoryItem::replyTo() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
void HistoryItem::setText(const TextWithEntities &textWithEntities) {
|
||||
void HistoryItem::detectTextLinks(
|
||||
const TextWithEntities &textWithEntities) {
|
||||
auto text = textWithEntities;
|
||||
const auto &settings = AyuSettings::getInstance();
|
||||
if (settings.filterZalgo()) {
|
||||
@@ -4133,6 +4134,10 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::setText(const TextWithEntities &textWithEntities) {
|
||||
detectTextLinks(textWithEntities);
|
||||
setTextValue((_media && _media->consumeMessageText(text))
|
||||
? TextWithEntities()
|
||||
: std::move(text));
|
||||
@@ -4151,6 +4156,13 @@ void HistoryItem::setTextValue(TextWithEntities text, bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::setTextStreaming(TextWithEntities text) {
|
||||
detectTextLinks(text);
|
||||
_text = std::move(text);
|
||||
RemoveComponents(HistoryMessageTranslation::Bit());
|
||||
history()->owner().requestItemTextRefreshStreaming(this);
|
||||
}
|
||||
|
||||
bool HistoryItem::inHighlightProcess() const {
|
||||
return _flags & MessageFlag::InHighlightProcess;
|
||||
}
|
||||
|
||||
@@ -345,6 +345,9 @@ public:
|
||||
[[nodiscard]] bool canBeSummarized() const {
|
||||
return _flags & MessageFlag::CanBeSummarized;
|
||||
}
|
||||
[[nodiscard]] bool isTextAppearing() const {
|
||||
return _flags & MessageFlag::TextAppearing;
|
||||
}
|
||||
[[nodiscard]] bool hasRealFromId() const;
|
||||
[[nodiscard]] bool isPostHidingAuthor() const;
|
||||
[[nodiscard]] bool isPostShowingAuthor() const;
|
||||
@@ -528,6 +531,7 @@ public:
|
||||
}
|
||||
[[nodiscard]] bool computeDropForwardedInfo() const;
|
||||
void setText(const TextWithEntities &textWithEntities);
|
||||
void setTextStreaming(TextWithEntities text);
|
||||
|
||||
[[nodiscard]] MsgId replyToId() const;
|
||||
[[nodiscard]] FullMsgId replyToFullId() const;
|
||||
@@ -642,6 +646,7 @@ private:
|
||||
[[nodiscard]] bool generateLocalEntitiesByReply() const;
|
||||
[[nodiscard]] TextWithEntities withLocalEntities(
|
||||
const TextWithEntities &textWithEntities) const;
|
||||
void detectTextLinks(const TextWithEntities &textWithEntities);
|
||||
void setTextValue(TextWithEntities text, bool force = false);
|
||||
[[nodiscard]] bool isTooOldForEdit(TimeId now) const;
|
||||
[[nodiscard]] bool isLegacyMessage() const {
|
||||
|
||||
@@ -56,7 +56,9 @@ void HistoryStreamedDrafts::apply(
|
||||
_drafts.emplace(rootId, Draft{
|
||||
.message = _history->addNewLocalMessage({
|
||||
.id = _history->owner().nextLocalMessageId(),
|
||||
.flags = MessageFlag::Local | MessageFlag::HasReplyInfo,
|
||||
.flags = (MessageFlag::Local
|
||||
| MessageFlag::HasReplyInfo
|
||||
| MessageFlag::TextAppearing),
|
||||
.from = fromId,
|
||||
.replyTo = {
|
||||
.messageId = replyToId,
|
||||
@@ -80,7 +82,7 @@ bool HistoryStreamedDrafts::update(
|
||||
if (i == end(_drafts) || i->second.randomId != randomId) {
|
||||
return false;
|
||||
}
|
||||
i->second.message->setText(text);
|
||||
i->second.message->setTextStreaming(text);
|
||||
i->second.updated = crl::now();
|
||||
return true;
|
||||
}
|
||||
@@ -94,6 +96,13 @@ void HistoryStreamedDrafts::clear(MsgId rootId) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryStreamedDrafts::hasFor(not_null<HistoryItem*> item) const {
|
||||
const auto rootId = item->topicRootId();
|
||||
const auto i = _drafts.find(rootId);
|
||||
return (i != end(_drafts))
|
||||
&& (i->second.message->from() == item->from());
|
||||
}
|
||||
|
||||
void HistoryStreamedDrafts::applyItemAdded(not_null<HistoryItem*> item) {
|
||||
const auto rootId = item->topicRootId();
|
||||
const auto i = _drafts.find(rootId);
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
TimeId when,
|
||||
const MTPDsendMessageTextDraftAction &data);
|
||||
|
||||
[[nodiscard]] bool hasFor(not_null<HistoryItem*> item) const;
|
||||
void applyItemAdded(not_null<HistoryItem*> item);
|
||||
void applyItemRemoved(not_null<HistoryItem*> item);
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_drag_area.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_streamed_drafts.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "history/view/controls/history_view_characters_limit.h"
|
||||
@@ -665,6 +666,15 @@ HistoryWidget::HistoryWidget(
|
||||
updateHistoryGeometry();
|
||||
}
|
||||
}, lifetime());
|
||||
session().data().viewHeightAdjusted(
|
||||
) | rpl::on_next([=](Data::Session::ViewHeightAdjusted data) {
|
||||
const auto item = data.view->data();
|
||||
const auto history = item->history();
|
||||
if (item->mainView() == data.view
|
||||
&& (history == _history || history == _migrated)) {
|
||||
updateHistoryGeometry();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
session().data().itemShowHighlightRequest(
|
||||
) | rpl::on_next([=](not_null<HistoryItem*> item) {
|
||||
@@ -4013,7 +4023,9 @@ void HistoryWidget::newItemAdded(not_null<HistoryItem*> item) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
_itemRevealPending.emplace(item);
|
||||
if (!item->history()->streamedDrafts().hasFor(item)) {
|
||||
_itemRevealPending.emplace(item);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {
|
||||
|
||||
@@ -99,7 +99,8 @@ private:
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) override;
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
@@ -648,7 +649,8 @@ void Item::listMarkContentsRead(
|
||||
}
|
||||
|
||||
MessagesBarData Item::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) {
|
||||
if (elements.empty()) {
|
||||
return {};
|
||||
} else if (!_replies && !_sublist && !_history->unreadCount()) {
|
||||
|
||||
@@ -3132,7 +3132,8 @@ void ChatWidget::listMarkContentsRead(
|
||||
}
|
||||
|
||||
MessagesBarData ChatWidget::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) {
|
||||
if ((!_sublist && !_replies) || elements.empty()) {
|
||||
return {};
|
||||
}
|
||||
@@ -3143,7 +3144,15 @@ MessagesBarData ChatWidget::listMessagesBar(
|
||||
for (auto i = 0, count = int(elements.size()); i != count; ++i) {
|
||||
const auto item = elements[i]->data();
|
||||
if (item->isRegular() && item->id > till) {
|
||||
if (item->out() || (_replies && !item->replyToId())) {
|
||||
if (markLastAsRead
|
||||
|| item->out()
|
||||
|| (_replies && !item->replyToId())) {
|
||||
if (markLastAsRead) {
|
||||
if (item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
session().api().markContentsRead(item);
|
||||
}
|
||||
item->markClientSideAsRead();
|
||||
}
|
||||
if (_replies) {
|
||||
_replies->readTill(item);
|
||||
} else {
|
||||
|
||||
@@ -163,7 +163,8 @@ public:
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) override;
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
|
||||
@@ -2672,6 +2672,12 @@ void Element::itemTextUpdated() {
|
||||
}
|
||||
}
|
||||
|
||||
void Element::itemTextUpdatedStreaming() {
|
||||
clearSpecialOnlyEmoji();
|
||||
_text = Ui::Text::String(st::msgMinWidth);
|
||||
invalidateTextSizeCache();
|
||||
}
|
||||
|
||||
void Element::blockquoteExpandChanged() {
|
||||
invalidateTextSizeCache();
|
||||
history()->owner().requestViewResize(this);
|
||||
|
||||
@@ -626,6 +626,7 @@ public:
|
||||
|
||||
virtual void itemDataChanged();
|
||||
void itemTextUpdated();
|
||||
void itemTextUpdatedStreaming();
|
||||
void blockquoteExpandChanged();
|
||||
|
||||
virtual void unloadHeavyPart();
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/history_item_text.h"
|
||||
#include "history/history_streamed_drafts.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/reactions/history_view_reactions.h"
|
||||
@@ -467,6 +468,12 @@ ListWidget::ListWidget(
|
||||
resizeItem(view);
|
||||
}
|
||||
}, lifetime());
|
||||
_session->data().viewHeightAdjusted(
|
||||
) | rpl::on_next([this](Data::Session::ViewHeightAdjusted data) {
|
||||
if (data.view->delegate() == this) {
|
||||
viewHeightAdjusted(data.view);
|
||||
}
|
||||
}, lifetime());
|
||||
_session->data().itemViewRefreshRequest(
|
||||
) | rpl::on_next([this](auto item) {
|
||||
if (const auto view = viewForItem(item)) {
|
||||
@@ -645,6 +652,10 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) {
|
||||
|
||||
saveScrollState();
|
||||
|
||||
const auto scrolledTillEnd = _itemsKnownTillEnd
|
||||
&& (_visibleBottom == height())
|
||||
&& (_visibleBottom > _visibleTop);
|
||||
|
||||
const auto addedToEndFrom = (old.skippedAfter == 0
|
||||
&& (_slice.skippedAfter == 0)
|
||||
&& !old.ids.empty())
|
||||
@@ -682,7 +693,10 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) {
|
||||
_translateTracker->addBunchFrom(_items);
|
||||
}
|
||||
for (auto e = end(_items), i = e - addedToEndCount; i != e; ++i) {
|
||||
_itemRevealPending.emplace(*i);
|
||||
const auto item = (*i)->data();
|
||||
if (!item->history()->streamedDrafts().hasFor(item)) {
|
||||
_itemRevealPending.emplace(*i);
|
||||
}
|
||||
}
|
||||
updateAroundPositionFromNearest(nearestIndex);
|
||||
|
||||
@@ -703,7 +717,8 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) {
|
||||
}
|
||||
_viewsCapacity.clear();
|
||||
|
||||
checkUnreadBarCreation();
|
||||
const auto markLastAsRead = (scrolledTillEnd && markingMessagesRead());
|
||||
checkUnreadBarCreation(markLastAsRead);
|
||||
restoreScrollState();
|
||||
if (!_itemsRevealHeight) {
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
@@ -984,18 +999,21 @@ void ListWidget::computeScrollTo(
|
||||
scrollTo(wanted, position, scrollDelta, type);
|
||||
}
|
||||
|
||||
void ListWidget::checkUnreadBarCreation() {
|
||||
if (!_bar.element) {
|
||||
if (auto data = _delegate->listMessagesBar(_items); data.bar.element) {
|
||||
_bar = std::move(data.bar);
|
||||
_barText = std::move(data.text);
|
||||
if (!_bar.hidden) {
|
||||
_bar.element->createUnreadBar(_barText.value());
|
||||
const auto i = ranges::find(_items, not_null{ _bar.element });
|
||||
Assert(i != end(_items));
|
||||
refreshAttachmentsAtIndex(i - begin(_items));
|
||||
}
|
||||
}
|
||||
void ListWidget::checkUnreadBarCreation(bool markLastAsRead) {
|
||||
if (_bar.element) {
|
||||
return;
|
||||
}
|
||||
auto data = _delegate->listMessagesBar(_items, markLastAsRead);
|
||||
if (!data.bar.element) {
|
||||
return;
|
||||
}
|
||||
_bar = std::move(data.bar);
|
||||
_barText = std::move(data.text);
|
||||
if (!_bar.hidden) {
|
||||
_bar.element->createUnreadBar(_barText.value());
|
||||
const auto i = ranges::find(_items, not_null{ _bar.element });
|
||||
Assert(i != end(_items));
|
||||
refreshAttachmentsAtIndex(i - begin(_items));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4360,6 +4378,30 @@ void ListWidget::refreshAttachmentsFromTill(int from, int till) {
|
||||
}
|
||||
}
|
||||
|
||||
void ListWidget::viewHeightAdjusted(not_null<Element*> view) {
|
||||
const auto i = ranges::find(_items, view);
|
||||
if (i == end(_items)) {
|
||||
return;
|
||||
}
|
||||
auto next = i + 1;
|
||||
const auto was = (next != end(_items))
|
||||
? (*next)->y()
|
||||
: _itemsHeight;
|
||||
const auto now = view->y() + view->height();
|
||||
const auto delta = now - was;
|
||||
for (; next != end(_items); ++next) {
|
||||
(*next)->setY((*next)->y() + delta);
|
||||
}
|
||||
_itemsHeight += delta;
|
||||
_itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom)
|
||||
? (_minHeight - _itemsHeight - st::historyPaddingBottom)
|
||||
: 0;
|
||||
resize(width(), _itemsTop + _itemsHeight + st::historyPaddingBottom);
|
||||
restoreScrollPosition();
|
||||
updateVisibleTopItem();
|
||||
update();
|
||||
}
|
||||
|
||||
void ListWidget::refreshItem(not_null<const Element*> view) {
|
||||
const auto i = ranges::find(_items, view);
|
||||
const auto index = i - begin(_items);
|
||||
|
||||
@@ -126,7 +126,8 @@ public:
|
||||
virtual void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) = 0;
|
||||
virtual MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) = 0;
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) = 0;
|
||||
virtual void listContentRefreshed() = 0;
|
||||
virtual void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
@@ -596,6 +597,7 @@ private:
|
||||
void repaintItem(const Element *view, QRect rect);
|
||||
void resizeItem(not_null<Element*> view);
|
||||
void refreshItem(not_null<const Element*> view);
|
||||
void viewHeightAdjusted(not_null<Element*> view);
|
||||
void showItemHighlight(not_null<HistoryItem*> item);
|
||||
void itemRemoved(not_null<const HistoryItem*> item);
|
||||
QPoint mapPointToItem(QPoint point, const Element *view) const;
|
||||
@@ -716,7 +718,7 @@ private:
|
||||
TextSelection computeRenderSelection(
|
||||
not_null<const SelectedMap*> selected,
|
||||
not_null<const Element*> view) const;
|
||||
void checkUnreadBarCreation();
|
||||
void checkUnreadBarCreation(bool markLastAsRead = false);
|
||||
void applyUpdatedScrollState();
|
||||
void scrollToAnimationCallback(FullMsgId attachToId, int relativeTo);
|
||||
void startItemRevealAnimations();
|
||||
|
||||
@@ -74,6 +74,8 @@ constexpr auto kSummarizeThreshold = 512;
|
||||
constexpr auto kPlayStatusLimit = 12;
|
||||
constexpr auto kMaxNiceToReadLines = 6;
|
||||
const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
|
||||
constexpr auto kFullLineAppearDuration = crl::time(300);
|
||||
constexpr auto kLineHeightAppearDuration = crl::time(100);
|
||||
|
||||
struct SecondRightAction {
|
||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||
@@ -668,6 +670,11 @@ QSize Message::performCountOptimalSize() {
|
||||
} else {
|
||||
RemoveComponents(Factcheck::Bit());
|
||||
}
|
||||
if (item->isTextAppearing()) {
|
||||
AddComponents(TextAppearing::Bit());
|
||||
} else {
|
||||
RemoveComponents(TextAppearing::Bit());
|
||||
}
|
||||
refreshRightBadge();
|
||||
|
||||
const auto markup = item->inlineReplyMarkup();
|
||||
@@ -901,6 +908,9 @@ QSize Message::performCountOptimalSize() {
|
||||
minHeight -= text().minHeight();
|
||||
minHeight += textHeightFor(bubbleTextWidth(maxWidth));
|
||||
}
|
||||
if (const auto appearing = Get<TextAppearing>()) {
|
||||
appearing->geometryValid = false;
|
||||
}
|
||||
return QSize(maxWidth, minHeight);
|
||||
}
|
||||
|
||||
@@ -2260,6 +2270,37 @@ void Message::paintText(
|
||||
.outPath = &ripplePath,
|
||||
};
|
||||
|
||||
const auto appearing = Get<TextAppearing>();
|
||||
if (appearing
|
||||
&& (appearing->shownLines > 0)
|
||||
&& (appearing->shownLines <= int(appearing->lines.size()))) {
|
||||
const auto lastLineIndex = appearing->shownLines - 1;
|
||||
p.save();
|
||||
auto clipPath = QPainterPath();
|
||||
const auto prevBottom = (lastLineIndex > 0)
|
||||
? appearing->lines[lastLineIndex - 1].top
|
||||
: 0;
|
||||
if (prevBottom > 0) {
|
||||
clipPath.addRect(
|
||||
trect.x(),
|
||||
trect.y(),
|
||||
trect.width(),
|
||||
prevBottom);
|
||||
}
|
||||
const auto lastLineHeight = appearing->lines[lastLineIndex].top
|
||||
- prevBottom;
|
||||
const auto &lastLine = appearing->lines[lastLineIndex];
|
||||
const auto clipLeft = lastLine.rtl
|
||||
? (trect.x() + trect.width() - appearing->revealedLineWidth)
|
||||
: trect.x();
|
||||
clipPath.addRect(
|
||||
clipLeft,
|
||||
trect.y() + prevBottom,
|
||||
appearing->revealedLineWidth,
|
||||
lastLineHeight);
|
||||
p.setClipPath(clipPath);
|
||||
}
|
||||
|
||||
auto highlightRequest = context.computeHighlightCache();
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
@@ -2290,6 +2331,10 @@ void Message::paintText(
|
||||
} else if (needRippleMask) {
|
||||
_linkRipple = nullptr;
|
||||
}
|
||||
if (appearing && appearing->shownLines > 0
|
||||
&& appearing->shownLines <= int(appearing->lines.size())) {
|
||||
p.restore();
|
||||
}
|
||||
}
|
||||
|
||||
PointState Message::pointState(QPoint point) const {
|
||||
@@ -4958,6 +5003,19 @@ QRect Message::countGeometry() const {
|
||||
contentWidth = mediaWidth;
|
||||
}
|
||||
}
|
||||
if (const auto appearing = Get<TextAppearing>()) {
|
||||
if (appearing->shownWidth > 0) {
|
||||
const auto animatedWidth = st::msgPadding.left()
|
||||
+ appearing->shownWidth
|
||||
+ st::msgPadding.right();
|
||||
const auto minWidth = _bottomInfo.optimalSize().width()
|
||||
+ st::msgPadding.left()
|
||||
+ st::msgPadding.right();
|
||||
accumulate_min(contentWidth, std::max(
|
||||
animatedWidth,
|
||||
minWidth));
|
||||
}
|
||||
}
|
||||
if (contentWidth < availableWidth
|
||||
&& delegate()->elementChatMode() != ElementChatMode::Wide) {
|
||||
if (outbg) {
|
||||
@@ -5117,7 +5175,8 @@ int Message::resizeContentGetHeight(int newWidth) {
|
||||
_reactions->resizeGetHeight(textWidth);
|
||||
}
|
||||
|
||||
if (contentWidth == maxWidth()) {
|
||||
const auto appearing = Get<TextAppearing>();
|
||||
if (contentWidth == maxWidth() && !appearing) {
|
||||
if (mediaDisplayed) {
|
||||
if (check) {
|
||||
newHeight += check->resizeGetHeight(contentWidth) + st::mediaInBubbleSkip;
|
||||
@@ -5141,7 +5200,31 @@ int Message::resizeContentGetHeight(int newWidth) {
|
||||
if (botTop) {
|
||||
newHeight += botTop->height;
|
||||
}
|
||||
newHeight += textHeightFor(textWidth);
|
||||
const auto fullTextHeight = textHeightFor(textWidth);
|
||||
if (appearing) {
|
||||
if (!appearing->geometryValid
|
||||
|| appearing->textWidth != textWidth) {
|
||||
appearing->textWidth = textWidth;
|
||||
appearing->lines = text().countLinesGeometry(
|
||||
textWidth);
|
||||
appearing->geometryValid = true;
|
||||
if (appearing->shownHeight <= 0) {
|
||||
appearing->shownHeight = appearing->lines.empty()
|
||||
? text().lineHeight()
|
||||
: appearing->lines.front().top;
|
||||
appearing->shownLines = 1;
|
||||
appearing->revealedLineWidth = 0;
|
||||
appearing->shownWidth = 0;
|
||||
startTextAppearingWidthAnimation();
|
||||
} else if (!appearing->heightStarted
|
||||
&& !appearing->widthAnimation.animating()) {
|
||||
startTextAppearingHeightAnimation();
|
||||
}
|
||||
}
|
||||
newHeight += appearing->shownHeight;
|
||||
} else {
|
||||
newHeight += fullTextHeight;
|
||||
}
|
||||
}
|
||||
if (!mediaOnBottom && (!_viewButton || !reactionsInBubble)) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
@@ -5255,6 +5338,119 @@ void Message::invalidateTextDependentCache() {
|
||||
_bubbleTextualWidthCache = 0;
|
||||
}
|
||||
|
||||
void Message::startTextAppearingWidthAnimation() {
|
||||
const auto appearing = Get<TextAppearing>();
|
||||
if (!appearing || appearing->lines.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto lineIndex = appearing->shownLines - 1;
|
||||
if (lineIndex < 0
|
||||
|| lineIndex >= int(appearing->lines.size())) {
|
||||
return;
|
||||
}
|
||||
appearing->heightStarted = false;
|
||||
appearing->revealedLineWidth = 0;
|
||||
const auto lineWidth = appearing->lines[lineIndex].width;
|
||||
const auto duration = std::max(
|
||||
kFullLineAppearDuration * lineWidth / st::msgMaxWidth,
|
||||
crl::time(10));
|
||||
appearing->widthDuration = duration;
|
||||
appearing->widthAnimation.start(
|
||||
[=] { textAppearingTick(); },
|
||||
0.,
|
||||
1.,
|
||||
duration,
|
||||
anim::linear);
|
||||
}
|
||||
|
||||
void Message::startTextAppearingHeightAnimation() {
|
||||
const auto appearing = Get<TextAppearing>();
|
||||
if (!appearing
|
||||
|| appearing->heightStarted
|
||||
|| (appearing->shownLines >= int(appearing->lines.size()))) {
|
||||
return;
|
||||
}
|
||||
appearing->heightStarted = true;
|
||||
const auto oldHeight = appearing->shownHeight;
|
||||
const auto newTargetHeight = appearing->lines[appearing->shownLines].top;
|
||||
appearing->heightAnimation.start(
|
||||
[=] { textAppearingHeightTick(); },
|
||||
double(oldHeight),
|
||||
double(newTargetHeight),
|
||||
kLineHeightAppearDuration,
|
||||
anim::easeOutCubic);
|
||||
}
|
||||
|
||||
void Message::textAppearingTick() {
|
||||
auto appearing = Get<TextAppearing>();
|
||||
if (!appearing) {
|
||||
return;
|
||||
}
|
||||
const auto lineIndex = appearing->shownLines - 1;
|
||||
if (lineIndex < 0
|
||||
|| lineIndex >= int(appearing->lines.size())) {
|
||||
return;
|
||||
}
|
||||
const auto progress = appearing->widthAnimation.value(1.);
|
||||
appearing->revealedLineWidth = int(
|
||||
appearing->lines[lineIndex].width * progress);
|
||||
appearing->shownWidth = std::max(
|
||||
appearing->shownWidth,
|
||||
appearing->revealedLineWidth);
|
||||
|
||||
const auto hasMoreLines =
|
||||
(appearing->shownLines < int(appearing->lines.size()));
|
||||
const auto remaining = crl::time(
|
||||
appearing->widthDuration * (1. - progress));
|
||||
if (hasMoreLines
|
||||
&& (remaining <= kLineHeightAppearDuration)
|
||||
&& !appearing->heightStarted) {
|
||||
startTextAppearingHeightAnimation();
|
||||
}
|
||||
|
||||
if (!appearing->widthAnimation.animating()) {
|
||||
tryAdvanceTextAppearing();
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void Message::textAppearingHeightTick() {
|
||||
auto appearing = Get<TextAppearing>();
|
||||
if (!appearing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto targetHeight = appearing->lines[appearing->shownLines].top;
|
||||
const auto oldShownHeight = appearing->shownHeight;
|
||||
appearing->shownHeight = int(base::SafeRound(
|
||||
appearing->heightAnimation.value(targetHeight)));
|
||||
const auto delta = appearing->shownHeight - oldShownHeight;
|
||||
if (delta) {
|
||||
adjustHeight(delta);
|
||||
history()->viewHeightAdjusted(this, delta);
|
||||
}
|
||||
|
||||
if (!appearing->heightAnimation.animating()) {
|
||||
tryAdvanceTextAppearing();
|
||||
}
|
||||
}
|
||||
|
||||
void Message::tryAdvanceTextAppearing() {
|
||||
auto appearing = Get<TextAppearing>();
|
||||
if (!appearing) {
|
||||
return;
|
||||
}
|
||||
if (appearing->widthAnimation.animating()
|
||||
|| appearing->heightAnimation.animating()) {
|
||||
return;
|
||||
}
|
||||
if (appearing->shownLines < int(appearing->lines.size())) {
|
||||
appearing->shownLines++;
|
||||
startTextAppearingWidthAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
bool Message::needInfoDisplay() const {
|
||||
const auto media = this->media();
|
||||
const auto mediaDisplayed = media ? media->isDisplayed() : false;
|
||||
|
||||
@@ -83,6 +83,20 @@ struct RightBadge : RuntimeComponent<RightBadge, Element> {
|
||||
mutable QPoint lastPoint;
|
||||
};
|
||||
|
||||
struct TextAppearing : RuntimeComponent<TextAppearing, Element> {
|
||||
std::vector<Ui::Text::LineLayoutInfo> lines;
|
||||
int textWidth = 0;
|
||||
int shownLines = 0;
|
||||
int revealedLineWidth = 0;
|
||||
int shownWidth = 0;
|
||||
int shownHeight = 0;
|
||||
crl::time widthDuration = 0;
|
||||
Ui::Animations::Simple widthAnimation;
|
||||
Ui::Animations::Simple heightAnimation;
|
||||
bool geometryValid = false;
|
||||
bool heightStarted = false;
|
||||
};
|
||||
|
||||
struct BottomRippleMask {
|
||||
QImage image;
|
||||
int shift = 0;
|
||||
@@ -373,6 +387,11 @@ private:
|
||||
[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
|
||||
void psaTooltipToggled(bool shown) const;
|
||||
void invalidateTextDependentCache() override;
|
||||
void startTextAppearingWidthAnimation();
|
||||
void startTextAppearingHeightAnimation();
|
||||
void textAppearingTick();
|
||||
void textAppearingHeightTick();
|
||||
void tryAdvanceTextAppearing();
|
||||
|
||||
void refreshRightBadge();
|
||||
[[nodiscard]] int rightBadgeWidth() const;
|
||||
|
||||
@@ -54,6 +54,9 @@ protected:
|
||||
_width = size.width();
|
||||
_height = size.height();
|
||||
}
|
||||
void adjustHeight(int delta) {
|
||||
_height += delta;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual QSize countOptimalSize() = 0;
|
||||
|
||||
@@ -608,7 +608,8 @@ void PinnedWidget::listMarkContentsRead(
|
||||
}
|
||||
|
||||
MessagesBarData PinnedWidget::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,8 @@ public:
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) override;
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
|
||||
@@ -1472,7 +1472,8 @@ void ScheduledWidget::listMarkContentsRead(
|
||||
}
|
||||
|
||||
MessagesBarData ScheduledWidget::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@ public:
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) override;
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
|
||||
@@ -106,8 +106,8 @@ private:
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
HistoryView::MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<HistoryView::Element*>> &elements)
|
||||
override;
|
||||
const std::vector<not_null<HistoryView::Element*>> &elements,
|
||||
bool markLastAsRead) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
@@ -522,7 +522,8 @@ void ListWidget::Inner::listMarkContentsRead(
|
||||
}
|
||||
|
||||
HistoryView::MessagesBarData ListWidget::Inner::listMessagesBar(
|
||||
const std::vector<not_null<HistoryView::Element*>> &elements) {
|
||||
const std::vector<not_null<HistoryView::Element*>> &elements,
|
||||
bool markLastAsRead) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,8 @@ private:
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) override;
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
@@ -911,7 +912,8 @@ void ShortcutMessages::listMarkContentsRead(
|
||||
}
|
||||
|
||||
MessagesBarData ShortcutMessages::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
const std::vector<not_null<Element*>> &elements,
|
||||
bool markLastAsRead) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user