Show root as pinned when not visible as message.

This commit is contained in:
John Preston 2020-09-15 21:19:06 +03:00
parent 22dc7601f5
commit 7f928a92ea
5 changed files with 112 additions and 52 deletions

View file

@ -29,6 +29,7 @@ struct RepliesList::Viewer {
MsgId around = 0;
int limitBefore = 0;
int limitAfter = 0;
base::has_weak_ptr guard;
};
RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
@ -80,12 +81,12 @@ rpl::producer<MessagesSlice> RepliesList::sourceFromServer(
) | rpl::filter([=](const MessageUpdate &update) {
return applyUpdate(viewer, update);
}) | rpl::start_with_next([=] {
crl::on_main(this, push);
crl::on_main(&viewer->guard, push);
}, lifetime);
_partLoaded.events(
) | rpl::start_with_next([=] {
crl::on_main(this, push);
crl::on_main(&viewer->guard, push);
}, lifetime);
push();

View file

@ -606,6 +606,13 @@ void ListWidget::updateAroundPositionFromRows() {
}
}
Element *ListWidget::viewByPosition(Data::MessagePosition position) const {
const auto index = findNearestItem(position);
return (index < 0 || _items[index]->data()->position() != position)
? nullptr
: _items[index].get();
}
int ListWidget::findNearestItem(Data::MessagePosition position) const {
if (_items.empty()) {
return -1;

View file

@ -162,6 +162,7 @@ public:
void restoreState(not_null<ListMemento*> memento);
std::optional<int> scrollTopForPosition(
Data::MessagePosition position) const;
Element *viewByPosition(Data::MessagePosition position) const;
std::optional<int> scrollTopForView(not_null<Element*> view) const;
enum class AnimatedScroll {
Full,

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/send_context_menu.h" // SendMenu::Type.
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/layers/generic_box.h"
#include "ui/text_options.h"
#include "ui/toast/toast.h"
@ -129,18 +130,21 @@ RepliesWidget::RepliesWidget(
this,
controller,
ComposeControls::Mode::Normal))
, _rootView(this, object_ptr<Ui::RpWidget>(this))
, _rootShadow(this)
, _scrollDown(_scroll, st::historyToDown)
, _readRequestTimer([=] { sendReadTillRequest(); }) {
setupRoot();
setupRootView();
_rootHeight = st::historyReplyHeight;
_topBar->setActiveChat(_history, TopBarWidget::Section::Replies);
_topBar->move(0, 0);
_topBar->resizeToWidth(width());
_topBar->show();
_rootView->move(0, _topBar->height());
_topBar->sendNowSelectionRequest(
) | rpl::start_with_next([=] {
confirmSendNowSelected();
@ -167,7 +171,7 @@ RepliesWidget::RepliesWidget(
this,
controller,
static_cast<ListDelegate*>(this)));
_scroll->move(0, _topBar->height() + _rootHeight);
_scroll->move(0, _topBar->height());
_scroll->show();
connect(_scroll, &Ui::ScrollArea::scrolled, [=] { onScroll(); });
@ -195,6 +199,7 @@ RepliesWidget::RepliesWidget(
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
if (update.item == _root) {
_root = nullptr;
updatePinnedVisibility();
}
if (update.item == _commentsRoot) {
_commentsRoot = nullptr;
@ -245,12 +250,68 @@ void RepliesWidget::setupRoot() {
_areComments = computeAreComments();
setupCommentsRoot();
}
updatePinnedVisibility();
refreshRootView();
});
_history->session().api().requestMessageData(channel, _rootId, done);
}
}
void RepliesWidget::setupRootView() {
const auto raw = _rootView->entity();
raw->resize(raw->width(), st::historyReplyHeight);
raw->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
auto p = Painter(_rootView->entity());
p.fillRect(clip, st::historyPinnedBg);
auto top = st::msgReplyPadding.top();
QRect rbar(myrtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), top + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height()));
p.fillRect(rbar, st::msgInReplyBarColor);
int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
if (!_rootTitle.isEmpty()) {
const auto media = _root ? _root->media() : nullptr;
if (media && media->hasReplyPreview()) {
if (const auto image = media->replyPreview()) {
QRect to(left, top, st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
p.drawPixmap(to.x(), to.y(), image->pixSingle(image->width() / cIntRetinaFactor(), image->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small));
}
left += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
}
p.setPen(st::historyReplyNameFg);
p.setFont(st::msgServiceNameFont);
const auto poll = media ? media->poll() : nullptr;
const auto pinnedHeader = !poll
? tr::lng_pinned_message(tr::now)
: poll->quiz()
? tr::lng_pinned_quiz(tr::now)
: tr::lng_pinned_poll(tr::now);
_rootTitle.drawElided(p, left, top, width() - left - st::msgReplyPadding.right());
p.setPen(st::historyComposeAreaFg);
p.setTextPalette(st::historyComposeAreaPalette);
_rootMessage.drawElided(p, left, top + st::msgServiceNameFont->height, width() - left - st::msgReplyPadding.right());
p.restoreTextPalette();
} else {
p.setFont(st::msgDateFont);
p.setPen(st::historyComposeAreaFgService);
p.drawText(left, top + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(tr::lng_profile_loading(tr::now), width() - left - st::msgReplyPadding.right()));
}
}, raw->lifetime());
_rootView->geometryValue(
) | rpl::start_with_next([=](QRect rect) {
_rootShadow->moveToLeft(
_rootShadow->x(),
rect.y() + rect.height());
}, _rootView->lifetime());
_rootShadow->showOn(_rootView->shownValue());
_rootView->hide(anim::type::instant);
}
void RepliesWidget::setupCommentsRoot() {
Expects(_root != nullptr);
@ -1105,7 +1166,7 @@ void RepliesWidget::updateAdaptiveLayout() {
_topBar->height());
_rootShadow->moveToLeft(
Adaptive::OneColumn() ? 0 : st::lineWidth,
_topBar->height() + _rootHeight);
_rootShadow->y());
}
not_null<History*> RepliesWidget::history() const {
@ -1210,6 +1271,12 @@ void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
const auto setReplies = [&](std::shared_ptr<Data::RepliesList> replies) {
_replies = std::move(replies);
_replies->fullCount(
) | rpl::take(1) | rpl::start_with_next([=] {
_loaded = true;
updatePinnedVisibility();
}, lifetime());
_topBar->setCustomTitle(tr::lng_manage_discussion_group(tr::now));
};
if (auto replies = memento->getReplies()) {
@ -1238,10 +1305,11 @@ void RepliesWidget::updateControlsGeometry() {
_topBar->resizeToWidth(contentWidth);
_topBarShadow->resize(contentWidth, st::lineWidth);
_rootShadow->resize(contentWidth, st::lineWidth);
_rootView->resizeToWidth(contentWidth);
const auto bottom = height();
const auto controlsHeight = _composeControls->heightCurrent();
const auto scrollHeight = bottom - _topBar->height() - _rootHeight - controlsHeight;
const auto scrollHeight = bottom - _topBar->height() - controlsHeight;
const auto scrollSize = QSize(contentWidth, scrollHeight);
if (_scroll->size() != scrollSize) {
_skipScrollEvent = true;
@ -1268,52 +1336,10 @@ void RepliesWidget::paintEvent(QPaintEvent *e) {
return;
}
const auto aboveHeight = _topBar->height() + _rootHeight;
const auto aboveHeight = _topBar->height();
const auto bg = e->rect().intersected(
QRect(0, aboveHeight, width(), height() - aboveHeight));
SectionWidget::PaintBackground(controller(), this, bg);
auto p = Painter(this);
paintRoot(p);
}
void RepliesWidget::paintRoot(Painter &p) {
auto top = _topBar->bottomNoMargins();
p.fillRect(myrtlrect(0, top, width(), st::historyReplyHeight), st::historyPinnedBg);
top += st::msgReplyPadding.top();
QRect rbar(myrtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), top + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height()));
p.fillRect(rbar, st::msgInReplyBarColor);
int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
if (!_rootTitle.isEmpty()) {
const auto media = _root ? _root->media() : nullptr;
if (media && media->hasReplyPreview()) {
if (const auto image = media->replyPreview()) {
QRect to(left, top, st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
p.drawPixmap(to.x(), to.y(), image->pixSingle(image->width() / cIntRetinaFactor(), image->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small));
}
left += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
}
p.setPen(st::historyReplyNameFg);
p.setFont(st::msgServiceNameFont);
const auto poll = media ? media->poll() : nullptr;
const auto pinnedHeader = !poll
? tr::lng_pinned_message(tr::now)
: poll->quiz()
? tr::lng_pinned_quiz(tr::now)
: tr::lng_pinned_poll(tr::now);
_rootTitle.drawElided(p, left, top, width() - left - st::msgReplyPadding.right());
p.setPen(st::historyComposeAreaFg);
p.setTextPalette(st::historyComposeAreaPalette);
_rootMessage.drawElided(p, left, top + st::msgServiceNameFont->height, width() - left - st::msgReplyPadding.right());
p.restoreTextPalette();
} else {
p.setFont(st::msgDateFont);
p.setPen(st::historyComposeAreaFgService);
p.drawText(left, top + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(tr::lng_profile_loading(tr::now), width() - left - st::msgReplyPadding.right()));
}
}
void RepliesWidget::onScroll() {
@ -1329,9 +1355,28 @@ void RepliesWidget::updateInnerVisibleArea() {
}
const auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
updatePinnedVisibility();
updateScrollDownVisibility();
}
void RepliesWidget::updatePinnedVisibility() {
if (!_loaded) {
return;
} else if (!_root) {
setPinnedVisibility(true);
return;
}
const auto view = _inner->viewByPosition(_root->position());
setPinnedVisibility(!view
|| (view->y() + view->height() <= _scroll->scrollTop()));
}
void RepliesWidget::setPinnedVisibility(bool shown) {
if (!animating()) {
_rootView->toggle(shown, anim::type::normal);
}
}
void RepliesWidget::showAnimatedHook(
const Window::SectionSlideParams &params) {
_topBar->setAnimatingMode(true);
@ -1349,6 +1394,7 @@ void RepliesWidget::showFinishedHook() {
// the section animation is finished,
// because after that the method showChildren() is called.
setupDragArea();
updatePinnedVisibility();
}
bool RepliesWidget::floatPlayerHandleWheelEvent(QEvent *e) {

View file

@ -35,6 +35,8 @@ class ScrollArea;
class PlainShadow;
class FlatButton;
class HistoryDownButton;
template <typename Widget>
class SlideWrap;
} // namespace Ui
namespace Profile {
@ -151,6 +153,7 @@ private:
void setupComposeControls();
void setupRoot();
void setupRootView();
void setupCommentsRoot();
void refreshRootView();
void setupDragArea();
@ -162,11 +165,13 @@ private:
void scrollDownAnimationFinish();
void updateScrollDownVisibility();
void updateScrollDownPosition();
void updatePinnedVisibility();
void confirmSendNowSelected();
void confirmDeleteSelected();
void confirmForwardSelected();
void clearSelected();
void setPinnedVisibility(bool shown);
void send();
void send(Api::SendOptions options);
@ -226,8 +231,6 @@ private:
not_null<UserData*> bot,
Api::SendOptions options);
void paintRoot(Painter &p);
const not_null<History*> _history;
const MsgId _rootId = 0;
HistoryItem *_root = nullptr;
@ -243,8 +246,8 @@ private:
Ui::Text::String _rootTitle;
Ui::Text::String _rootMessage;
object_ptr<Ui::SlideWrap<Ui::RpWidget>> _rootView;
object_ptr<Ui::PlainShadow> _rootShadow;
int _rootHeight = 0;
std::vector<MsgId> _replyReturns;
HistoryItem *_replyReturn = nullptr;
@ -260,6 +263,8 @@ private:
bool _readRequestPending = false;
mtpRequestId _readRequestId = 0;
bool _loaded = false;
};