Allow HistoryView::ListWidget without SessionController.

This commit is contained in:
John Preston 2024-05-02 19:39:19 +04:00
parent e5132e3fe8
commit ef2aa05197
14 changed files with 312 additions and 125 deletions

View file

@ -90,6 +90,95 @@ constexpr auto kClearUserpicsAfter = 50;
} // namespace } // namespace
const crl::time ListWidget::kItemRevealDuration = crl::time(150);
WindowListDelegate::WindowListDelegate(
not_null<Window::SessionController*> window)
: _window(window) {
}
not_null<Window::SessionController*> WindowListDelegate::listWindow() {
return _window;
}
not_null<const Ui::ChatStyle*> WindowListDelegate::listChatStyle() {
return _window->chatStyle();
}
rpl::producer<bool> WindowListDelegate::listChatWideValue() {
return _window->adaptive().chatWideValue();
}
auto WindowListDelegate::listMakeReactionsManager(
QWidget *wheelEventsTarget,
Fn<void(QRect)> update)
-> std::unique_ptr<Reactions::Manager> {
return std::make_unique<Reactions::Manager>(
wheelEventsTarget,
std::move(update),
_window->cachedReactionIconFactory().createMethod());
}
void WindowListDelegate::listVisibleAreaUpdated() {
_window->floatPlayerAreaUpdated();
}
std::shared_ptr<Ui::Show> WindowListDelegate::listUiShow() {
return _window->uiShow();
}
void WindowListDelegate::listShowPollResults(
not_null<PollData*> poll,
FullMsgId context) {
_window->showPollResults(poll, context);
}
void WindowListDelegate::listCancelUploadLayer(not_null<HistoryItem*> item) {
_window->cancelUploadLayer(item);
}
bool WindowListDelegate::listAnimationsPaused() {
return _window->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
}
auto WindowListDelegate::listSendingAnimation()
-> Ui::MessageSendingAnimationController * {
return &_window->sendingAnimation();
}
Ui::ChatPaintContext WindowListDelegate::listPreparePaintContext(
Ui::ChatPaintContextArgs &&args) {
return _window->preparePaintContext(std::move(args));
}
bool WindowListDelegate::listMarkingContentRead() {
return _window->widget()->markingAsRead();
}
bool WindowListDelegate::listIgnorePaintEvent(QWidget *w, QPaintEvent *e) {
return _window->contentOverlapped(w, e);
}
bool WindowListDelegate::listShowReactPremiumError(
not_null<HistoryItem*> item,
const Data::ReactionId &id) {
return Window::ShowReactPremiumError(_window, item, id);
}
void WindowListDelegate::listWindowSetInnerFocus() {
_window->widget()->setInnerFocus();
}
bool WindowListDelegate::listAllowsDragForward() {
return _window->adaptive().isOneColumn();
}
void WindowListDelegate::listLaunchDrag(
std::unique_ptr<QMimeData> data,
Fn<void()> finished) {
_window->widget()->launchDrag(std::move(data), std::move(finished));
}
ListWidget::MouseState::MouseState() : pointState(PointState::Outside) { ListWidget::MouseState::MouseState() : pointState(PointState::Outside) {
} }
@ -104,8 +193,6 @@ ListWidget::MouseState::MouseState(
, pointState(pointState) { , pointState(pointState) {
} }
const crl::time ListWidget::kItemRevealDuration = crl::time(150);
template <ListWidget::EnumItemsDirection direction, typename Method> template <ListWidget::EnumItemsDirection direction, typename Method>
void ListWidget::enumerateItems(Method method) { void ListWidget::enumerateItems(Method method) {
constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom); constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom);
@ -283,31 +370,29 @@ void ListWidget::enumerateDates(Method method) {
ListWidget::ListWidget( ListWidget::ListWidget(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Main::Session*> session,
not_null<ListDelegate*> delegate) not_null<ListDelegate*> delegate)
: RpWidget(parent) : RpWidget(parent)
, _delegate(delegate) , _delegate(delegate)
, _controller(controller) , _session(session)
, _emojiInteractions(std::make_unique<EmojiInteractions>( , _emojiInteractions(std::make_unique<EmojiInteractions>(
&controller->session(), session,
[=](not_null<const Element*> view) { return itemTop(view); })) [=](not_null<const Element*> view) { return itemTop(view); }))
, _context(_delegate->listContext()) , _context(_delegate->listContext())
, _itemAverageHeight(itemMinimalHeight()) , _itemAverageHeight(itemMinimalHeight())
, _pathGradient( , _pathGradient(
MakePathShiftGradient( MakePathShiftGradient(
controller->chatStyle(), _delegate->listChatStyle(),
[=] { update(); })) [=] { update(); }))
, _reactionsManager( , _reactionsManager(_delegate->listMakeReactionsManager(
std::make_unique<Reactions::Manager>( this,
this, [=](QRect updated) { update(updated); }))
[=](QRect updated) { update(updated); },
controller->cachedReactionIconFactory().createMethod()))
, _translateTracker(MaybeTranslateTracker(_delegate->listTranslateHistory())) , _translateTracker(MaybeTranslateTracker(_delegate->listTranslateHistory()))
, _scrollDateCheck([this] { scrollDateCheck(); }) , _scrollDateCheck([this] { scrollDateCheck(); })
, _applyUpdatedScrollState([this] { applyUpdatedScrollState(); }) , _applyUpdatedScrollState([this] { applyUpdatedScrollState(); })
, _selectEnabled(_delegate->listAllowsMultiSelect()) , _selectEnabled(_delegate->listAllowsMultiSelect())
, _highlighter( , _highlighter(
&session().data(), &_session->data(),
[=](const HistoryItem *item) { return viewForItem(item); }, [=](const HistoryItem *item) { return viewForItem(item); },
[=](const Element *view) { repaintItem(view); }) [=](const Element *view) { repaintItem(view); })
, _touchSelectTimer([=] { onTouchSelect(); }) , _touchSelectTimer([=] { onTouchSelect(); })
@ -315,25 +400,25 @@ ListWidget::ListWidget(
setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_AcceptTouchEvents);
setMouseTracking(true); setMouseTracking(true);
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); _scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
session().data().viewRepaintRequest( _session->data().viewRepaintRequest(
) | rpl::start_with_next([this](auto view) { ) | rpl::start_with_next([this](auto view) {
if (view->delegate() == this) { if (view->delegate() == this) {
repaintItem(view); repaintItem(view);
} }
}, lifetime()); }, lifetime());
session().data().viewResizeRequest( _session->data().viewResizeRequest(
) | rpl::start_with_next([this](auto view) { ) | rpl::start_with_next([this](auto view) {
if (view->delegate() == this) { if (view->delegate() == this) {
resizeItem(view); resizeItem(view);
} }
}, lifetime()); }, lifetime());
session().data().itemViewRefreshRequest( _session->data().itemViewRefreshRequest(
) | rpl::start_with_next([this](auto item) { ) | rpl::start_with_next([this](auto item) {
if (const auto view = viewForItem(item)) { if (const auto view = viewForItem(item)) {
refreshItem(view); refreshItem(view);
} }
}, lifetime()); }, lifetime());
session().data().viewLayoutChanged( _session->data().viewLayoutChanged(
) | rpl::start_with_next([this](auto view) { ) | rpl::start_with_next([this](auto view) {
if (view->delegate() == this) { if (view->delegate() == this) {
if (view->isUnderCursor()) { if (view->isUnderCursor()) {
@ -341,37 +426,37 @@ ListWidget::ListWidget(
} }
} }
}, lifetime()); }, lifetime());
session().data().itemDataChanges( _session->data().itemDataChanges(
) | rpl::start_with_next([=](not_null<HistoryItem*> item) { ) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
if (const auto view = viewForItem(item)) { if (const auto view = viewForItem(item)) {
view->itemDataChanged(); view->itemDataChanged();
} }
}, lifetime()); }, lifetime());
session().downloaderTaskFinished( _session->downloaderTaskFinished(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
update(); update();
}, lifetime()); }, lifetime());
session().data().peerDecorationsUpdated( _session->data().peerDecorationsUpdated(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
update(); update();
}, lifetime()); }, lifetime());
session().data().itemRemoved( _session->data().itemRemoved(
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) { ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
itemRemoved(item); itemRemoved(item);
}, lifetime()); }, lifetime());
using MessageUpdateFlag = Data::MessageUpdate::Flag; using MessageUpdateFlag = Data::MessageUpdate::Flag;
session().changes().realtimeMessageUpdates( _session->changes().realtimeMessageUpdates(
MessageUpdateFlag::NewUnreadReaction MessageUpdateFlag::NewUnreadReaction
) | rpl::start_with_next([=](const Data::MessageUpdate &update) { ) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
maybeMarkReactionsRead(update.item); maybeMarkReactionsRead(update.item);
}, lifetime()); }, lifetime());
if (const auto history = _delegate->listTranslateHistory()) { if (const auto history = _delegate->listTranslateHistory()) {
session().changes().historyUpdates( _session->changes().historyUpdates(
history, history,
Data::HistoryUpdate::Flag::TranslatedTo Data::HistoryUpdate::Flag::TranslatedTo
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -379,7 +464,7 @@ ListWidget::ListWidget(
}, lifetime()); }, lifetime());
} }
session().data().itemVisibilityQueries( _session->data().itemVisibilityQueries(
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
const Data::Session::ItemVisibilityQuery &query) { const Data::Session::ItemVisibilityQuery &query) {
if (const auto view = viewForItem(query.item)) { if (const auto view = viewForItem(query.item)) {
@ -392,25 +477,27 @@ ListWidget::ListWidget(
} }
}, lifetime()); }, lifetime());
_reactionsManager->chosen( if (_reactionsManager) {
) | rpl::start_with_next([=](ChosenReaction reaction) { _reactionsManager->chosen(
_reactionsManager->updateButton({}); ) | rpl::start_with_next([=](ChosenReaction reaction) {
reactionChosen(reaction);
}, lifetime());
Reactions::SetupManagerList(
_reactionsManager.get(),
_reactionsItem.value());
Core::App().settings().cornerReactionValue(
) | rpl::start_with_next([=](bool value) {
_useCornerReaction = value;
if (!value) {
_reactionsManager->updateButton({}); _reactionsManager->updateButton({});
} reactionChosen(reaction);
}, lifetime()); }, lifetime());
controller->adaptive().chatWideValue( Reactions::SetupManagerList(
_reactionsManager.get(),
_reactionsItem.value());
Core::App().settings().cornerReactionValue(
) | rpl::start_with_next([=](bool value) {
_useCornerReaction = value;
if (!value) {
_reactionsManager->updateButton({});
}
}, lifetime());
}
_delegate->listChatWideValue(
) | rpl::start_with_next([=](bool wide) { ) | rpl::start_with_next([=](bool wide) {
_isChatWide = wide; _isChatWide = wide;
}, lifetime()); }, lifetime());
@ -427,11 +514,11 @@ ListWidget::ListWidget(
} }
Main::Session &ListWidget::session() const { Main::Session &ListWidget::session() const {
return _controller->session(); return *_session;
} }
not_null<Window::SessionController*> ListWidget::controller() const { not_null<Window::SessionController*> ListWidget::controller() const {
return _controller; return _delegate->listWindow();
} }
not_null<ListDelegate*> ListWidget::delegate() const { not_null<ListDelegate*> ListWidget::delegate() const {
@ -1006,7 +1093,7 @@ void ListWidget::visibleTopBottomUpdated(
} else { } else {
scrollDateHideByTimer(); scrollDateHideByTimer();
} }
_controller->floatPlayerAreaUpdated(); _delegate->listVisibleAreaUpdated();
session().data().itemVisibilitiesUpdated(); session().data().itemVisibilitiesUpdated();
_applyUpdatedScrollState.call(); _applyUpdatedScrollState.call();
@ -1439,7 +1526,7 @@ bool ListWidget::showCopyRestriction(HistoryItem *item) {
if (type == CopyRestrictionType::None) { if (type == CopyRestrictionType::None) {
return false; return false;
} }
_controller->showToast((type == CopyRestrictionType::Channel) _delegate->listUiShow()->showToast((type == CopyRestrictionType::Channel)
? tr::lng_error_nocopy_channel(tr::now) ? tr::lng_error_nocopy_channel(tr::now)
: tr::lng_error_nocopy_group(tr::now)); : tr::lng_error_nocopy_group(tr::now));
return true; return true;
@ -1450,7 +1537,7 @@ bool ListWidget::showCopyMediaRestriction(not_null<HistoryItem*> item) {
if (type == CopyRestrictionType::None) { if (type == CopyRestrictionType::None) {
return false; return false;
} }
_controller->showToast((type == CopyRestrictionType::Channel) _delegate->listUiShow()->showToast((type == CopyRestrictionType::Channel)
? tr::lng_error_nocopy_channel(tr::now) ? tr::lng_error_nocopy_channel(tr::now)
: tr::lng_error_nocopy_group(tr::now)); : tr::lng_error_nocopy_group(tr::now));
return true; return true;
@ -1676,7 +1763,7 @@ void ListWidget::elementStartStickerLoop(not_null<const Element*> view) {
void ListWidget::elementShowPollResults( void ListWidget::elementShowPollResults(
not_null<PollData*> poll, not_null<PollData*> poll,
FullMsgId context) { FullMsgId context) {
_controller->showPollResults(poll, context); _delegate->listShowPollResults(poll, context);
} }
void ListWidget::elementOpenPhoto( void ListWidget::elementOpenPhoto(
@ -1694,7 +1781,7 @@ void ListWidget::elementOpenDocument(
void ListWidget::elementCancelUpload(const FullMsgId &context) { void ListWidget::elementCancelUpload(const FullMsgId &context) {
if (const auto item = session().data().message(context)) { if (const auto item = session().data().message(context)) {
_controller->cancelUploadLayer(item); _delegate->listCancelUploadLayer(item);
} }
} }
@ -1706,7 +1793,7 @@ void ListWidget::elementShowTooltip(
} }
bool ListWidget::elementAnimationsPaused() { bool ListWidget::elementAnimationsPaused() {
return _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any); return _delegate->listAnimationsPaused();
} }
bool ListWidget::elementHideReply(not_null<const Element*> view) { bool ListWidget::elementHideReply(not_null<const Element*> view) {
@ -1838,8 +1925,8 @@ void ListWidget::startItemRevealAnimations() {
void ListWidget::startMessageSendingAnimation( void ListWidget::startMessageSendingAnimation(
not_null<HistoryItem*> item) { not_null<HistoryItem*> item) {
auto &sendingAnimation = controller()->sendingAnimation(); const auto sendingAnimation = _delegate->listSendingAnimation();
if (!sendingAnimation.checkExpectedType(item)) { if (!sendingAnimation || !sendingAnimation->checkExpectedType(item)) {
return; return;
} }
@ -1855,7 +1942,7 @@ void ListWidget::startMessageSendingAnimation(
return mapToGlobal(QPoint(0, itemTop(view) - additional)); return mapToGlobal(QPoint(0, itemTop(view) - additional));
}); });
sendingAnimation.startAnimation({ sendingAnimation->startAnimation({
.globalEndTopLeft = std::move(globalEndTopLeft), .globalEndTopLeft = std::move(globalEndTopLeft),
.view = [=] { return viewForItem(item); }, .view = [=] { return viewForItem(item); },
.paintContext = [=] { return preparePaintContext({}); }, .paintContext = [=] { return preparePaintContext({}); },
@ -2010,7 +2097,7 @@ TextSelection ListWidget::itemRenderSelection(
Ui::ChatPaintContext ListWidget::preparePaintContext( Ui::ChatPaintContext ListWidget::preparePaintContext(
const QRect &clip) const { const QRect &clip) const {
return controller()->preparePaintContext({ return _delegate->listPreparePaintContext({
.theme = _delegate->listChatTheme(), .theme = _delegate->listChatTheme(),
.clip = clip, .clip = clip,
.visibleAreaPositionGlobal = mapToGlobal(QPoint(0, _visibleTop)), .visibleAreaPositionGlobal = mapToGlobal(QPoint(0, _visibleTop)),
@ -2022,7 +2109,7 @@ Ui::ChatPaintContext ListWidget::preparePaintContext(
bool ListWidget::markingContentsRead() const { bool ListWidget::markingContentsRead() const {
return _showFinished return _showFinished
&& !_refreshingViewer && !_refreshingViewer
&& controller()->widget()->markingAsRead(); && _delegate->listMarkingContentRead();
} }
bool ListWidget::markingMessagesRead() const { bool ListWidget::markingMessagesRead() const {
@ -2053,12 +2140,9 @@ void ListWidget::checkActivation() {
} }
void ListWidget::paintEvent(QPaintEvent *e) { void ListWidget::paintEvent(QPaintEvent *e) {
if ((_context != Context::ShortcutMessages) if (_delegate->listIgnorePaintEvent(this, e)) {
&& _controller->contentOverlapped(this, e)) {
return; return;
} } else if (_translateTracker) {
if (_translateTracker) {
_translateTracker->startBunch(); _translateTracker->startBunch();
} }
auto readTill = (HistoryItem*)nullptr; auto readTill = (HistoryItem*)nullptr;
@ -2100,20 +2184,25 @@ void ListWidget::paintEvent(QPaintEvent *e) {
_delegate->listPaintEmpty(p, context); _delegate->listPaintEmpty(p, context);
return; return;
} }
_reactionsManager->startEffectsCollection(); if (_reactionsManager) {
_reactionsManager->startEffectsCollection();
}
const auto session = &controller()->session(); const auto session = &this->session();
auto top = itemTop(from->get()); auto top = itemTop(from->get());
context = context.translated(0, -top); context = context.translated(0, -top);
p.translate(0, top); p.translate(0, top);
const auto &sendingAnimation = _controller->sendingAnimation(); const auto sendingAnimation = _delegate->listSendingAnimation();
for (auto i = from; i != to; ++i) { for (auto i = from; i != to; ++i) {
const auto view = *i; const auto view = *i;
const auto item = view->data(); const auto item = view->data();
const auto height = view->height(); const auto height = view->height();
if (!sendingAnimation.hasAnimatedMessage(item)) { if (!sendingAnimation
context.reactionInfo || !sendingAnimation->hasAnimatedMessage(item)) {
= _reactionsManager->currentReactionPaintInfo(); if (_reactionsManager) {
context.reactionInfo
= _reactionsManager->currentReactionPaintInfo();
}
context.outbg = view->hasOutLayout(); context.outbg = view->hasOutLayout();
context.selection = itemRenderSelection(view); context.selection = itemRenderSelection(view);
context.highlight = _highlighter.state(item); context.highlight = _highlighter.state(item);
@ -2125,17 +2214,19 @@ void ListWidget::paintEvent(QPaintEvent *e) {
const auto isSponsored = item->isSponsored(); const auto isSponsored = item->isSponsored();
const auto isUnread = _delegate->listElementShownUnread(view) const auto isUnread = _delegate->listElementShownUnread(view)
&& item->isRegular(); && item->isRegular();
const auto withReaction = item->hasUnreadReaction(); const auto withReaction = context.reactionInfo
&& item->hasUnreadReaction();
const auto yShown = [&](int y) { const auto yShown = [&](int y) {
return (_visibleBottom >= y && _visibleTop <= y); return (_visibleBottom >= y && _visibleTop <= y);
}; };
const auto markShown = isSponsored const auto markShown = (_context != Context::ChatPreview)
? view->markSponsoredViewed(_visibleBottom - top) && (isSponsored
: withReaction ? view->markSponsoredViewed(_visibleBottom - top)
? yShown(top + context.reactionInfo->position.y()) : withReaction
: isUnread ? yShown(top + context.reactionInfo->position.y())
? yShown(top + height) : isUnread
: yShown(top + height / 2); ? yShown(top + height)
: yShown(top + height / 2));
if (markShown) { if (markShown) {
if (isSponsored) { if (isSponsored) {
session->sponsoredMessages().view(item->fullId()); session->sponsoredMessages().view(item->fullId());
@ -2157,9 +2248,11 @@ void ListWidget::paintEvent(QPaintEvent *e) {
if (item->hasExtendedMediaPreview()) { if (item->hasExtendedMediaPreview()) {
session->api().views().pollExtendedMedia(item); session->api().views().pollExtendedMedia(item);
} }
_reactionsManager->recordCurrentReactionEffect( if (_reactionsManager) {
item->fullId(), _reactionsManager->recordCurrentReactionEffect(
QPoint(0, top)); item->fullId(),
QPoint(0, top));
}
top += height; top += height;
context.translate(0, -height); context.translate(0, -height);
p.translate(0, height); p.translate(0, height);
@ -2170,7 +2263,9 @@ void ListWidget::paintEvent(QPaintEvent *e) {
paintUserpics(p, context, clip); paintUserpics(p, context, clip);
paintDates(p, context, clip); paintDates(p, context, clip);
_reactionsManager->paint(p, context); if (_reactionsManager) {
_reactionsManager->paint(p, context);
}
_emojiInteractions->paint(p); _emojiInteractions->paint(p);
} }
@ -2181,7 +2276,7 @@ void ListWidget::paintUserpics(
if (_context == Context::ShortcutMessages) { if (_context == Context::ShortcutMessages) {
return; return;
} }
const auto session = &controller()->session(); const auto session = &this->session();
enumerateUserpics([&](not_null<Element*> view, int userpicTop) { enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
// stop the enumeration if the userpic is below the painted rect // stop the enumeration if the userpic is below the painted rect
if (userpicTop >= clip.top() + clip.height()) { if (userpicTop >= clip.top() + clip.height()) {
@ -2530,7 +2625,7 @@ void ListWidget::toggleFavoriteReaction(not_null<Element*> view) const {
Data::LookupPossibleReactions(item).recent, Data::LookupPossibleReactions(item).recent,
favorite, favorite,
&Data::Reaction::id) &Data::Reaction::id)
|| Window::ShowReactPremiumError(_controller, item, favorite)) { || _delegate->listShowReactPremiumError(item, favorite)) {
return; return;
} else if (!ranges::contains(item->chosenReactions(), favorite)) { } else if (!ranges::contains(item->chosenReactions(), favorite)) {
if (const auto top = itemTop(view); top >= 0) { if (const auto top = itemTop(view); top >= 0) {
@ -2599,6 +2694,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (link if (link
&& !link->property( && !link->property(
kSendReactionEmojiProperty).value<Data::ReactionId>().empty() kSendReactionEmojiProperty).value<Data::ReactionId>().empty()
&& _reactionsManager
&& _reactionsManager->showContextMenu( && _reactionsManager->showContextMenu(
this, this,
e, e,
@ -2624,13 +2720,13 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
this, this,
overItem, overItem,
clickedReaction, clickedReaction,
_controller, controller(),
_whoReactedMenuLifetime); _whoReactedMenuLifetime);
e->accept(); e->accept();
return; return;
} }
auto request = ContextMenuRequest(_controller); auto request = ContextMenuRequest(controller());
request.link = link; request.link = link;
request.view = _overElement; request.view = _overElement;
@ -2666,12 +2762,12 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto attached = reactItem const auto attached = reactItem
? AttachSelectorToMenu( ? AttachSelectorToMenu(
_menu.get(), _menu.get(),
_controller, controller(),
desiredPosition, desiredPosition,
reactItem, reactItem,
[=](ChosenReaction reaction) { reactionChosen(reaction); }, [=](ChosenReaction reaction) { reactionChosen(reaction); },
ItemReactionsAbout(reactItem), ItemReactionsAbout(reactItem),
_controller->cachedReactionIconFactory().createMethod()) controller()->cachedReactionIconFactory().createMethod())
: AttachSelectorResult::Skipped; : AttachSelectorResult::Skipped;
if (attached == AttachSelectorResult::Failed) { if (attached == AttachSelectorResult::Failed) {
_menu = nullptr; _menu = nullptr;
@ -2688,10 +2784,7 @@ void ListWidget::reactionChosen(ChosenReaction reaction) {
const auto item = session().data().message(reaction.context); const auto item = session().data().message(reaction.context);
if (!item) { if (!item) {
return; return;
} else if (Window::ShowReactPremiumError( } else if (_delegate->listShowReactPremiumError(item, reaction.id)) {
_controller,
item,
reaction.id)) {
if (_menu) { if (_menu) {
_menu->hideMenu(); _menu->hideMenu();
} }
@ -2945,7 +3038,9 @@ void ListWidget::enterEventHook(QEnterEvent *e) {
} }
void ListWidget::leaveEventHook(QEvent *e) { void ListWidget::leaveEventHook(QEvent *e) {
_reactionsManager->updateButton({ .cursorLeft = true }); if (_reactionsManager) {
_reactionsManager->updateButton({ .cursorLeft = true });
}
if (const auto view = _overElement) { if (const auto view = _overElement) {
if (_overState.pointState != PointState::Outside) { if (_overState.pointState != PointState::Outside) {
repaintItem(view); repaintItem(view);
@ -3160,9 +3255,9 @@ void ListWidget::mouseActionStart(
const auto pressElement = _overElement; const auto pressElement = _overElement;
_mouseAction = MouseAction::None; _mouseAction = MouseAction::None;
_pressWasInactive = Ui::WasInactivePress(_controller->widget()); _pressWasInactive = Ui::WasInactivePress(window());
if (_pressWasInactive) { if (_pressWasInactive) {
Ui::MarkInactivePress(_controller->widget(), false); Ui::MarkInactivePress(window(), false);
} }
if (ClickHandler::getPressed()) { if (ClickHandler::getPressed()) {
@ -3334,7 +3429,7 @@ void ListWidget::mouseActionFinish(
} else if (_selectedTextItem && !_pressWasInactive) { } else if (_selectedTextItem && !_pressWasInactive) {
if (_selectedTextRange.from == _selectedTextRange.to) { if (_selectedTextRange.from == _selectedTextRange.to) {
clearTextSelection(); clearTextSelection();
_controller->widget()->setInnerFocus(); _delegate->listWindowSetInnerFocus();
} }
} }
} }
@ -3362,7 +3457,7 @@ ClickHandlerContext ListWidget::prepareClickHandlerContext(FullMsgId id) {
? (ElementDelegate*)weak ? (ElementDelegate*)weak
: nullptr; : nullptr;
}, },
.sessionWindow = base::make_weak(_controller), .sessionWindow = base::make_weak(controller()),
}; };
} }
@ -3372,7 +3467,9 @@ void ListWidget::mouseActionUpdate() {
std::clamp(mousePosition.x(), 0, width()), std::clamp(mousePosition.x(), 0, width()),
std::clamp(mousePosition.y(), _visibleTop, _visibleBottom)); std::clamp(mousePosition.y(), _visibleTop, _visibleBottom));
const auto reactionState = _reactionsManager->buttonTextState(point); const auto reactionState = _reactionsManager
? _reactionsManager->buttonTextState(point)
: TextState();
const auto reactionItem = session().data().message(reactionState.itemId); const auto reactionItem = session().data().message(reactionState.itemId);
const auto reactionView = viewForItem(reactionItem); const auto reactionView = viewForItem(reactionItem);
const auto view = reactionView const auto view = reactionView
@ -3392,12 +3489,14 @@ void ListWidget::mouseActionUpdate() {
_overElement = view; _overElement = view;
repaintItem(_overElement); repaintItem(_overElement);
} }
_reactionsManager->updateButton(view if (_reactionsManager) {
? reactionButtonParameters( _reactionsManager->updateButton(view
view, ? reactionButtonParameters(
itemPoint, view,
reactionState) itemPoint,
: Reactions::ButtonParameters()); reactionState)
: Reactions::ButtonParameters());
}
if (viewChanged && view) { if (viewChanged && view) {
_reactionsItem = item; _reactionsItem = item;
} }
@ -3624,7 +3723,7 @@ std::unique_ptr<QMimeData> ListWidget::prepareDrag() {
if (!urls.isEmpty()) { if (!urls.isEmpty()) {
mimeData->setUrls(urls); mimeData->setUrls(urls);
} }
if (uponSelected && !_controller->adaptive().isOneColumn()) { if (uponSelected && !_delegate->listAllowsDragForward()) {
const auto canForwardAll = [&] { const auto canForwardAll = [&] {
for (const auto &[itemId, data] : _selected) { for (const auto &[itemId, data] : _selected) {
if (!data.canForward) { if (!data.canForward) {
@ -3689,8 +3788,10 @@ std::unique_ptr<QMimeData> ListWidget::prepareDrag() {
void ListWidget::performDrag() { void ListWidget::performDrag() {
if (auto mimeData = prepareDrag()) { if (auto mimeData = prepareDrag()) {
// This call enters event loop and can destroy any QObject. // This call enters event loop and can destroy any QObject.
_reactionsManager->updateButton({}); if (_reactionsManager) {
_controller->widget()->launchDrag( _reactionsManager->updateButton({});
}
_delegate->listLaunchDrag(
std::move(mimeData), std::move(mimeData),
crl::guard(this, [=] { mouseActionUpdate(QCursor::pos()); })); crl::guard(this, [=] { mouseActionUpdate(QCursor::pos()); }));
} }
@ -3708,7 +3809,10 @@ void ListWidget::repaintItem(const Element *view) {
const auto range = view->verticalRepaintRange(); const auto range = view->verticalRepaintRange();
update(0, top + range.top, width(), range.height); update(0, top + range.top, width(), range.height);
const auto id = view->data()->fullId(); const auto id = view->data()->fullId();
if (const auto area = _reactionsManager->lookupEffectArea(id)) { const auto area = _reactionsManager
? _reactionsManager->lookupEffectArea(id)
: std::nullopt;
if (area) {
update(*area); update(*area);
} }
} }
@ -3866,7 +3970,9 @@ void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
viewReplaced(view, nullptr); viewReplaced(view, nullptr);
_views.erase(i); _views.erase(i);
_reactionsManager->remove(item->fullId()); if (_reactionsManager) {
_reactionsManager->remove(item->fullId());
}
updateItemsGeometry(); updateItemsGeometry();
} }
@ -3992,7 +4098,7 @@ void ConfirmDeleteSelectedItems(not_null<ListWidget*> widget) {
} }
} }
auto box = Box<DeleteMessagesBox>( auto box = Box<DeleteMessagesBox>(
&widget->controller()->session(), &widget->session(),
widget->getSelectedIds()); widget->getSelectedIds());
box->setDeleteConfirmedCallback(crl::guard(widget, [=] { box->setDeleteConfirmedCallback(crl::guard(widget, [=] {
widget->cancelSelection(); widget->cancelSelection();

View file

@ -25,11 +25,14 @@ class Session;
} // namespace Main } // namespace Main
namespace Ui { namespace Ui {
class Show;
class PopupMenu; class PopupMenu;
class ChatTheme; class ChatTheme;
struct ChatPaintContext; struct ChatPaintContext;
struct ChatPaintContextArgs;
enum class TouchScrollState; enum class TouchScrollState;
struct PeerUserpicView; struct PeerUserpicView;
class MessageSendingAnimationController;
} // namespace Ui } // namespace Ui
namespace Window { namespace Window {
@ -40,6 +43,7 @@ namespace Data {
struct Group; struct Group;
struct Reaction; struct Reaction;
struct AllowedReactions; struct AllowedReactions;
struct ReactionId;
} // namespace Data } // namespace Data
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
@ -154,6 +158,71 @@ public:
virtual History *listTranslateHistory() = 0; virtual History *listTranslateHistory() = 0;
virtual void listAddTranslatedItems( virtual void listAddTranslatedItems(
not_null<TranslateTracker*> tracker) = 0; not_null<TranslateTracker*> tracker) = 0;
// Methods that use Window::SessionController by default.
virtual not_null<Window::SessionController*> listWindow() = 0;
virtual not_null<const Ui::ChatStyle*> listChatStyle() = 0;
virtual rpl::producer<bool> listChatWideValue() = 0;
virtual std::unique_ptr<Reactions::Manager> listMakeReactionsManager(
QWidget *wheelEventsTarget,
Fn<void(QRect)> update) = 0;
virtual void listVisibleAreaUpdated() = 0;
virtual std::shared_ptr<Ui::Show> listUiShow() = 0;
virtual void listShowPollResults(
not_null<PollData*> poll,
FullMsgId context) = 0;
virtual void listCancelUploadLayer(not_null<HistoryItem*> item) = 0;
virtual bool listAnimationsPaused() = 0;
virtual auto listSendingAnimation()
-> Ui::MessageSendingAnimationController* = 0;
virtual Ui::ChatPaintContext listPreparePaintContext(
Ui::ChatPaintContextArgs &&args) = 0;
virtual bool listMarkingContentRead() = 0;
virtual bool listIgnorePaintEvent(QWidget *w, QPaintEvent *e) = 0;
virtual bool listShowReactPremiumError(
not_null<HistoryItem*> item,
const Data::ReactionId &id) = 0;
virtual void listWindowSetInnerFocus() = 0;
virtual bool listAllowsDragForward() = 0;
virtual void listLaunchDrag(
std::unique_ptr<QMimeData> data,
Fn<void()> finished) = 0;
};
class WindowListDelegate : public ListDelegate {
public:
explicit WindowListDelegate(not_null<Window::SessionController*> window);
not_null<Window::SessionController*> listWindow() override;
not_null<const Ui::ChatStyle*> listChatStyle() override;
rpl::producer<bool> listChatWideValue() override;
std::unique_ptr<Reactions::Manager> listMakeReactionsManager(
QWidget *wheelEventsTarget,
Fn<void(QRect)> update) override;
void listVisibleAreaUpdated() override;
std::shared_ptr<Ui::Show> listUiShow() override;
void listShowPollResults(
not_null<PollData*> poll,
FullMsgId context) override;
void listCancelUploadLayer(not_null<HistoryItem*> item) override;
bool listAnimationsPaused() override;
Ui::MessageSendingAnimationController *listSendingAnimation() override;
Ui::ChatPaintContext listPreparePaintContext(
Ui::ChatPaintContextArgs &&args) override;
bool listMarkingContentRead() override;
bool listIgnorePaintEvent(QWidget *w, QPaintEvent *e) override;
bool listShowReactPremiumError(
not_null<HistoryItem*> item,
const Data::ReactionId &id) override;
void listWindowSetInnerFocus() override;
bool listAllowsDragForward() override;
void listLaunchDrag(
std::unique_ptr<QMimeData> data,
Fn<void()> finished) override;
private:
const not_null<Window::SessionController*> _window;
}; };
struct SelectionData { struct SelectionData {
@ -211,7 +280,7 @@ class ListWidget final
public: public:
ListWidget( ListWidget(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Main::Session*> session,
not_null<ListDelegate*> delegate); not_null<ListDelegate*> delegate);
static const crl::time kItemRevealDuration; static const crl::time kItemRevealDuration;
@ -648,7 +717,7 @@ private:
static constexpr auto kMinimalIdsLimit = 24; static constexpr auto kMinimalIdsLimit = 24;
const not_null<ListDelegate*> _delegate; const not_null<ListDelegate*> _delegate;
const not_null<Window::SessionController*> _controller; const not_null<Main::Session*> _session;
const std::unique_ptr<EmojiInteractions> _emojiInteractions; const std::unique_ptr<EmojiInteractions> _emojiInteractions;
const Context _context; const Context _context;

View file

@ -94,6 +94,7 @@ PinnedWidget::PinnedWidget(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<Data::Thread*> thread) not_null<Data::Thread*> thread)
: Window::SectionWidget(parent, controller, thread->peer()) : Window::SectionWidget(parent, controller, thread->peer())
, WindowListDelegate(controller)
, _thread(thread->migrateToOrMe()) , _thread(thread->migrateToOrMe())
, _history(thread->owningHistory()) , _history(thread->owningHistory())
, _migratedPeer(thread->asHistory() , _migratedPeer(thread->asHistory()
@ -161,7 +162,7 @@ PinnedWidget::PinnedWidget(
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>( _inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
this, this,
controller, &controller->session(),
static_cast<ListDelegate*>(this))); static_cast<ListDelegate*>(this)));
_scroll->move(0, _topBar->height()); _scroll->move(0, _topBar->height());
_scroll->show(); _scroll->show();

View file

@ -36,7 +36,7 @@ class TranslateBar;
class PinnedWidget final class PinnedWidget final
: public Window::SectionWidget : public Window::SectionWidget
, private ListDelegate , private WindowListDelegate
, private CornerButtonsDelegate { , private CornerButtonsDelegate {
public: public:
PinnedWidget( PinnedWidget(

View file

@ -205,6 +205,7 @@ RepliesWidget::RepliesWidget(
not_null<History*> history, not_null<History*> history,
MsgId rootId) MsgId rootId)
: Window::SectionWidget(parent, controller, history->peer) : Window::SectionWidget(parent, controller, history->peer)
, WindowListDelegate(controller)
, _history(history) , _history(history)
, _rootId(rootId) , _rootId(rootId)
, _root(lookupRoot()) , _root(lookupRoot())
@ -299,7 +300,7 @@ RepliesWidget::RepliesWidget(
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>( _inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
this, this,
controller, &controller->session(),
static_cast<ListDelegate*>(this))); static_cast<ListDelegate*>(this)));
_scroll->move(0, _topBar->height()); _scroll->move(0, _topBar->height());
_scroll->show(); _scroll->show();

View file

@ -71,7 +71,7 @@ class TranslateBar;
class RepliesWidget final class RepliesWidget final
: public Window::SectionWidget : public Window::SectionWidget
, private ListDelegate , private WindowListDelegate
, private CornerButtonsDelegate { , private CornerButtonsDelegate {
public: public:
RepliesWidget( RepliesWidget(

View file

@ -97,6 +97,7 @@ ScheduledWidget::ScheduledWidget(
not_null<History*> history, not_null<History*> history,
const Data::ForumTopic *forumTopic) const Data::ForumTopic *forumTopic)
: Window::SectionWidget(parent, controller, history->peer) : Window::SectionWidget(parent, controller, history->peer)
, WindowListDelegate(controller)
, _history(history) , _history(history)
, _forumTopic(forumTopic) , _forumTopic(forumTopic)
, _scroll( , _scroll(
@ -167,7 +168,7 @@ ScheduledWidget::ScheduledWidget(
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>( _inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
this, this,
controller, &controller->session(),
static_cast<ListDelegate*>(this))); static_cast<ListDelegate*>(this)));
_scroll->move(0, _topBar->height()); _scroll->move(0, _topBar->height());
_scroll->show(); _scroll->show();

View file

@ -52,7 +52,7 @@ class StickerToast;
class ScheduledWidget final class ScheduledWidget final
: public Window::SectionWidget : public Window::SectionWidget
, private ListDelegate , private WindowListDelegate
, private CornerButtonsDelegate { , private CornerButtonsDelegate {
public: public:
ScheduledWidget( ScheduledWidget(

View file

@ -67,6 +67,7 @@ SublistWidget::SublistWidget(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<Data::SavedSublist*> sublist) not_null<Data::SavedSublist*> sublist)
: Window::SectionWidget(parent, controller, sublist->peer()) : Window::SectionWidget(parent, controller, sublist->peer())
, WindowListDelegate(controller)
, _sublist(sublist) , _sublist(sublist)
, _history(sublist->owner().history(sublist->session().user())) , _history(sublist->owner().history(sublist->session().user()))
, _topBar(this, controller) , _topBar(this, controller)
@ -133,7 +134,7 @@ SublistWidget::SublistWidget(
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>( _inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
this, this,
controller, &controller->session(),
static_cast<ListDelegate*>(this))); static_cast<ListDelegate*>(this)));
_scroll->move(0, _topBar->height()); _scroll->move(0, _topBar->height());
_scroll->show(); _scroll->show();

View file

@ -37,7 +37,7 @@ class ComposeSearch;
class SublistWidget final class SublistWidget final
: public Window::SectionWidget : public Window::SectionWidget
, private ListDelegate , private WindowListDelegate
, private CornerButtonsDelegate { , private CornerButtonsDelegate {
public: public:
SublistWidget( SublistWidget(

View file

@ -72,7 +72,7 @@ using namespace HistoryView;
class ShortcutMessages class ShortcutMessages
: public AbstractSection : public AbstractSection
, private ListDelegate , private WindowListDelegate
, private CornerButtonsDelegate { , private CornerButtonsDelegate {
public: public:
ShortcutMessages( ShortcutMessages(
@ -164,6 +164,7 @@ private:
History *listTranslateHistory() override; History *listTranslateHistory() override;
void listAddTranslatedItems( void listAddTranslatedItems(
not_null<TranslateTracker*> tracker) override; not_null<TranslateTracker*> tracker) override;
bool listIgnorePaintEvent(QWidget *w, QPaintEvent *e) override;
// CornerButtonsDelegate delegate. // CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition( void cornerButtonsShowAtPosition(
@ -330,6 +331,7 @@ ShortcutMessages::ShortcutMessages(
rpl::producer<Container> containerValue, rpl::producer<Container> containerValue,
BusinessShortcutId shortcutId) BusinessShortcutId shortcutId)
: AbstractSection(parent) : AbstractSection(parent)
, WindowListDelegate(controller)
, _controller(controller) , _controller(controller)
, _session(&controller->session()) , _session(&controller->session())
, _scroll(scroll) , _scroll(scroll)
@ -370,7 +372,7 @@ ShortcutMessages::ShortcutMessages(
_inner = Ui::CreateChild<ListWidget>( _inner = Ui::CreateChild<ListWidget>(
this, this,
controller, &controller->session(),
static_cast<ListDelegate*>(this)); static_cast<ListDelegate*>(this));
_inner->overrideIsChatWide(false); _inner->overrideIsChatWide(false);
@ -1053,6 +1055,10 @@ void ShortcutMessages::listAddTranslatedItems(
not_null<TranslateTracker*> tracker) { not_null<TranslateTracker*> tracker) {
} }
bool ShortcutMessages::listIgnorePaintEvent(QWidget *w, QPaintEvent *e) {
return false;
}
void ShortcutMessages::cornerButtonsShowAtPosition( void ShortcutMessages::cornerButtonsShowAtPosition(
Data::MessagePosition position) { Data::MessagePosition position) {
showAtPosition(position); showAtPosition(position);

View file

@ -223,6 +223,14 @@ struct ChatPaintContext {
}; };
struct ChatPaintContextArgs {
not_null<ChatTheme*> theme;
QRect clip;
QPoint visibleAreaPositionGlobal;
int visibleAreaTop = 0;
int visibleAreaWidth = 0;
};
[[nodiscard]] int HistoryServiceMsgRadius(); [[nodiscard]] int HistoryServiceMsgRadius();
[[nodiscard]] int HistoryServiceMsgInvertedRadius(); [[nodiscard]] int HistoryServiceMsgInvertedRadius();
[[nodiscard]] int HistoryServiceMsgInvertedShrink(); [[nodiscard]] int HistoryServiceMsgInvertedShrink();

View file

@ -2951,7 +2951,7 @@ void SessionController::openPeerStories(
} }
HistoryView::PaintContext SessionController::preparePaintContext( HistoryView::PaintContext SessionController::preparePaintContext(
PaintContextArgs &&args) { Ui::ChatPaintContextArgs &&args) {
const auto visibleAreaTopLocal = content()->mapFromGlobal( const auto visibleAreaTopLocal = content()->mapFromGlobal(
args.visibleAreaPositionGlobal).y(); args.visibleAreaPositionGlobal).y();
const auto viewport = QRect( const auto viewport = QRect(

View file

@ -64,6 +64,7 @@ struct ChatThemeBackground;
struct ChatThemeBackgroundData; struct ChatThemeBackgroundData;
class MessageSendingAnimationController; class MessageSendingAnimationController;
struct BoostCounters; struct BoostCounters;
struct ChatPaintContextArgs;
} // namespace Ui } // namespace Ui
namespace Data { namespace Data {
@ -586,15 +587,8 @@ public:
PeerId peerId, PeerId peerId,
std::optional<Data::StorySourcesList> list = std::nullopt); std::optional<Data::StorySourcesList> list = std::nullopt);
struct PaintContextArgs {
not_null<Ui::ChatTheme*> theme;
QRect clip;
QPoint visibleAreaPositionGlobal;
int visibleAreaTop = 0;
int visibleAreaWidth = 0;
};
[[nodiscard]] Ui::ChatPaintContext preparePaintContext( [[nodiscard]] Ui::ChatPaintContext preparePaintContext(
PaintContextArgs &&args); Ui::ChatPaintContextArgs &&args);
[[nodiscard]] not_null<const Ui::ChatStyle*> chatStyle() const { [[nodiscard]] not_null<const Ui::ChatStyle*> chatStyle() const {
return _chatStyle.get(); return _chatStyle.get();
} }