tdesktop/Telegram/SourceFiles/history/history_view_highlight_mana...

273 lines
6.6 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_view_highlight_manager.h"
#include "data/data_session.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "ui/chat/chat_style.h"
namespace HistoryView {
ElementHighlighter::ElementHighlighter(
not_null<Data::Session*> data,
ViewForItem viewForItem,
RepaintView repaintView)
: _data(data)
, _viewForItem(std::move(viewForItem))
, _repaintView(std::move(repaintView))
, _animation(*this) {
}
void ElementHighlighter::enqueue(
not_null<Element*> view,
const TextWithEntities &part) {
const auto data = computeHighlight(view, part);
if (_queue.empty() && !_animation.animating()) {
highlight(data);
} else if (_highlighted != data && !base::contains(_queue, data)) {
_queue.push_back(data);
checkNextHighlight();
}
}
void ElementHighlighter::highlight(
not_null<Element*> view,
const TextWithEntities &part) {
highlight(computeHighlight(view, part));
}
void ElementHighlighter::checkNextHighlight() {
if (_animation.animating()) {
return;
}
const auto next = [&] {
while (!_queue.empty()) {
const auto highlight = _queue.front();
_queue.pop_front();
if (const auto item = _data->message(highlight.itemId)) {
if (_viewForItem(item)) {
return highlight;
}
}
}
return Highlight();
}();
if (next) {
highlight(next);
}
}
Ui::ChatPaintHighlight ElementHighlighter::state(
not_null<const HistoryItem*> item) const {
if (item->fullId() == _highlighted.itemId) {
auto result = _animation.state();
result.range = _highlighted.part;
return result;
}
return {};
}
ElementHighlighter::Highlight ElementHighlighter::computeHighlight(
not_null<const Element*> view,
const TextWithEntities &part) {
const auto item = view->data();
const auto owner = &item->history()->owner();
if (const auto group = owner->groups().find(item)) {
const auto leader = group->items.front();
const auto leaderId = leader->fullId();
const auto i = ranges::find(group->items, item);
if (i != end(group->items)) {
const auto index = int(i - begin(group->items));
if (part.empty()) {
return { leaderId, AddGroupItemSelection({}, index) };
} else if (const auto leaderView = _viewForItem(leader)) {
return {
leaderId,
leaderView->selectionFromQuote(item, part),
};
}
}
return { leaderId };
} else if (part.empty()) {
return { item->fullId() };
}
return { item->fullId(), view->selectionFromQuote(item, part) };
}
void ElementHighlighter::highlight(Highlight data) {
if (const auto item = _data->message(data.itemId)) {
if (const auto view = _viewForItem(item)) {
if (_highlighted && _highlighted.itemId != data.itemId) {
if (const auto was = _data->message(_highlighted.itemId)) {
if (const auto view = _viewForItem(was)) {
repaintHighlightedItem(view);
}
}
}
_highlighted = data;
_animation.start(!data.part.empty()
&& !IsSubGroupSelection(data.part));
repaintHighlightedItem(view);
}
}
}
void ElementHighlighter::repaintHighlightedItem(
not_null<const Element*> view) {
if (view->isHiddenByGroup()) {
if (const auto group = _data->groups().find(view->data())) {
if (const auto leader = _viewForItem(group->items.front())) {
if (!leader->isHiddenByGroup()) {
_repaintView(leader);
return;
}
}
}
}
_repaintView(view);
}
void ElementHighlighter::updateMessage() {
if (const auto item = _data->message(_highlighted.itemId)) {
if (const auto view = _viewForItem(item)) {
repaintHighlightedItem(view);
}
}
}
void ElementHighlighter::clear() {
_animation.cancel();
_highlighted = {};
_lastHighlightedMessageId = FullMsgId();
_queue.clear();
}
ElementHighlighter::AnimationManager::AnimationManager(
ElementHighlighter &parent)
: _parent(parent) {
}
bool ElementHighlighter::AnimationManager::animating() const {
if (_timer && _timer->isActive()) {
return true;
} else if (!anim::Disabled()) {
return _simple.animating();
}
return false;
}
Ui::ChatPaintHighlight ElementHighlighter::AnimationManager::state() const {
if (anim::Disabled()) {
return {
.opacity = !_timer ? 0. : 1.,
.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 {
return _highlighted.itemId
? _highlighted.itemId.msg
: _lastHighlightedMessageId.msg;
}
void ElementHighlighter::AnimationManager::start(bool withTextPart) {
_withTextPart = withTextPart;
const auto finish = [=] {
cancel();
_parent._lastHighlightedMessageId = base::take(
_parent._highlighted.itemId);
_parent.checkNextHighlight();
};
cancel();
if (anim::Disabled()) {
_timer.emplace([=] {
_parent.updateMessage();
if (_withTextPart && !_fadingOut) {
_fadingOut = true;
_timer->callOnce(st::activeFadeOutDuration);
} else {
finish();
}
});
_timer->callOnce(_withTextPart
? st::activeFadeInDuration
: st::activeFadeOutDuration);
_parent.updateMessage();
} else {
_simple.start(
[=](float64 value) {
_parent.updateMessage();
if (value == 1.) {
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.,
1.,
st::activeFadeInDuration);
}
}
void ElementHighlighter::AnimationManager::cancel() {
_simple.stop();
_timer.reset();
_fadingOut = false;
_collapsed = false;
_collapsing = false;
}
} // namespace HistoryView