/* 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 */ #pragma once #include "base/timer.h" #include "ui/rp_widget.h" #include "ui/effects/animations.h" #include "ui/dragging_scroll_manager.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/scroll_area.h" #include "history/view/history_view_top_bar_widget.h" namespace Data { struct Group; class CloudImageView; } // namespace Data namespace HistoryView { class ElementDelegate; class EmojiInteractions; struct TextState; struct StateRequest; enum class CursorState : char; enum class PointState : char; class EmptyPainter; class Element; } // namespace HistoryView namespace HistoryView::Reactions { class Manager; struct ButtonParameters; } // namespace HistoryView::Reactions namespace Window { class SessionController; } // namespace Window namespace Ui { class ChatTheme; class ChatStyle; class PopupMenu; enum class ReportReason; struct ChatPaintContext; class PathShiftGradient; } // namespace Ui namespace Dialogs::Ui { class VideoUserpic; } // namespace Dialogs::Ui class HistoryInner; class HistoryMainElementDelegate; class HistoryMainElementDelegateMixin { public: void setCurrent(HistoryInner *widget) { _widget = widget; } virtual not_null delegate() = 0; virtual ~HistoryMainElementDelegateMixin(); private: friend class HistoryMainElementDelegate; HistoryMainElementDelegateMixin(); HistoryInner *_widget = nullptr; }; class HistoryWidget; class HistoryInner : public Ui::RpWidget , public Ui::AbstractTooltipShower { public: using Element = HistoryView::Element; HistoryInner( not_null historyWidget, not_null scroll, not_null controller, not_null history); ~HistoryInner(); [[nodiscard]] Main::Session &session() const; [[nodiscard]] not_null theme() const { return _theme.get(); } Ui::ChatPaintContext preparePaintContext(const QRect &clip) const; void messagesReceived(PeerData *peer, const QVector &messages); void messagesReceivedDown(PeerData *peer, const QVector &messages); [[nodiscard]] TextForMimeData getSelectedText() const; void touchScrollUpdated(const QPoint &screenPos); void setItemsRevealHeight(int revealHeight); void changeItemsRevealHeight(int revealHeight); void checkHistoryActivation(); void recountHistoryGeometry(); void updateSize(); void repaintItem(const HistoryItem *item); void repaintItem(const Element *view); [[nodiscard]] bool canCopySelected() const; [[nodiscard]] bool canDeleteSelected() const; [[nodiscard]] auto getSelectionState() const -> HistoryView::TopBarWidget::SelectedState; void clearSelected(bool onlyTextSelection = false); [[nodiscard]] MessageIdsList getSelectedItems() const; [[nodiscard]] bool hasSelectedItems() const; [[nodiscard]] bool inSelectionMode() const; [[nodiscard]] bool elementIntersectsRange( not_null view, int from, int till) const; void elementStartStickerLoop(not_null view); [[nodiscard]] crl::time elementHighlightTime( not_null item); void elementShowPollResults( not_null poll, FullMsgId context); void elementOpenPhoto( not_null photo, FullMsgId context); void elementOpenDocument( not_null document, FullMsgId context, bool showInMediaView = false); void elementCancelUpload(const FullMsgId &context); void elementShowTooltip( const TextWithEntities &text, Fn hiddenCallback); bool elementIsGifPaused(); void elementSendBotCommand( const QString &command, const FullMsgId &context); void elementHandleViaClick(not_null bot); bool elementIsChatWide(); not_null elementPathShiftGradient(); void elementReplyTo(const FullMsgId &to); void elementStartInteraction(not_null view); void elementStartPremium( not_null view, Element *replacing); void elementCancelPremium(not_null view); void elementShowSpoilerAnimation(); void updateBotInfo(bool recount = true); bool wasSelectedText() const; // updates history->scrollTopItem/scrollTopOffset void visibleAreaUpdated(int top, int bottom); int historyHeight() const; int historyScrollTop() const; int migratedTop() const; int historyTop() const; int historyDrawTop() const; void setChooseReportReason(Ui::ReportReason reason); void clearChooseReportReason(); // -1 if should not be visible, -2 if bad history() int itemTop(const HistoryItem *item) const; int itemTop(const Element *view) const; // Returns (view, offset-from-top). [[nodiscard]] std::pair findViewForPinnedTracking( int top) const; void notifyIsBotChanged(); void notifyMigrateUpdated(); // Ui::AbstractTooltipShower interface. QString tooltipText() const override; QPoint tooltipPos() const override; bool tooltipWindowActive() const override; void onParentGeometryChanged(); [[nodiscard]] static auto DelegateMixin() -> std::unique_ptr; protected: bool focusNextPrevChild(bool next) override; bool eventHook(QEvent *e) override; // calls touchEvent when necessary void touchEvent(QTouchEvent *e); void paintEvent(QPaintEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *e) override; void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; void resizeEvent(QResizeEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override; private: void onTouchSelect(); void onTouchScrollTimer(); class BotAbout; using VideoUserpic = Dialogs::Ui::VideoUserpic; using SelectedItems = std::map>; enum class MouseAction { None, PrepareDrag, Dragging, PrepareSelect, Selecting, }; enum class SelectAction { Select, Deselect, Invert, }; enum class EnumItemsDirection { TopToBottom, BottomToTop, }; using CursorState = HistoryView::CursorState; using PointState = HistoryView::PointState; using TextState = HistoryView::TextState; using StateRequest = HistoryView::StateRequest; // This function finds all history items that are displayed and calls template method // for each found message (in given direction) in the passed history with passed top offset. // // Method has "bool (*Method)(not_null view, int itemtop, int itembottom)" signature // if it returns false the enumeration stops immidiately. template void enumerateItemsInHistory(History *history, int historytop, Method method); template void enumerateItems(Method method) { constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom); if (TopToBottom && _migrated) { enumerateItemsInHistory(_migrated, migratedTop(), method); } enumerateItemsInHistory(_history, historyTop(), method); if (!TopToBottom && _migrated) { enumerateItemsInHistory(_migrated, migratedTop(), method); } } // This function finds all userpics on the left that are displayed and calls template method // for each found userpic (from the top to the bottom) using enumerateItems() method. // // Method has "bool (*Method)(not_null view, int userpicTop)" signature // if it returns false the enumeration stops immidiately. template void enumerateUserpics(Method method); // This function finds all date elements that are displayed and calls template method // for each found date element (from the bottom to the top) using enumerateItems() method. // // Method has "bool (*Method)(not_null view, int itemtop, int dateTop)" signature // if it returns false the enumeration stops immidiately. template void enumerateDates(Method method); void scrollDateCheck(); void scrollDateHideByTimer(); bool canHaveFromUserpics() const; void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button); void mouseActionUpdate(); void mouseActionUpdate(const QPoint &screenPos); void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button); void mouseActionCancel(); std::unique_ptr prepareDrag(); void performDrag(); void paintEmpty( Painter &p, not_null st, int width, int height); QPoint mapPointToItem(QPoint p, const Element *view) const; QPoint mapPointToItem(QPoint p, const HistoryItem *item) const; void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); void cancelContextDownload(not_null document); void openContextGif(FullMsgId itemId); void saveContextGif(FullMsgId itemId); void copyContextText(FullMsgId itemId); void showContextInFolder(not_null document); void savePhotoToFile(not_null photo); void saveDocumentToFile( FullMsgId contextId, not_null document); void copyContextImage(not_null photo, FullMsgId itemId); void showStickerPackInfo(not_null document); void itemRemoved(not_null item); void viewRemoved(not_null view); void touchResetSpeed(); void touchUpdateSpeed(); void touchDeaccelerate(int32 elapsed); void adjustCurrent(int32 y) const; void adjustCurrent(int32 y, History *history) const; Element *prevItem(Element *item); Element *nextItem(Element *item); void updateDragSelection(Element *dragSelFrom, Element *dragSelTo, bool dragSelecting); TextSelection itemRenderSelection( not_null view, int selfromy, int seltoy) const; TextSelection computeRenderSelection( not_null selected, not_null view) const; void toggleScrollDateShown(); void repaintScrollDateCallback(); bool displayScrollDate() const; void scrollDateHide(); void keepScrollDateForNow(); void applyDragSelection(); void applyDragSelection(not_null toItems) const; void addSelectionRange( not_null toItems, not_null history, int fromblock, int fromitem, int toblock, int toitem) const; bool isSelected( not_null toItems, not_null item) const; bool isSelectedGroup( not_null toItems, not_null group) const; bool isSelectedAsGroup( not_null toItems, not_null item) const; bool goodForSelection( not_null toItems, not_null item, int &totalCount) const; void addToSelection( not_null toItems, not_null item) const; void removeFromSelection( not_null toItems, not_null item) const; void changeSelection( not_null toItems, not_null item, SelectAction action) const; void changeSelectionAsGroup( not_null toItems, not_null item, SelectAction action) const; void forwardItem(FullMsgId itemId); void forwardAsGroup(FullMsgId itemId); void deleteItem(not_null item); void deleteItem(FullMsgId itemId); void deleteAsGroup(FullMsgId itemId); void reportItem(FullMsgId itemId); void reportAsGroup(FullMsgId itemId); void blockSenderItem(FullMsgId itemId); void blockSenderAsGroup(FullMsgId itemId); void copySelectedText(); [[nodiscard]] auto reactionButtonParameters( not_null view, QPoint position, const HistoryView::TextState &reactionState) const -> HistoryView::Reactions::ButtonParameters; void toggleFavoriteReaction(not_null view) const; void setupSharingDisallowed(); [[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const; bool showCopyRestriction(HistoryItem *item = nullptr); [[nodiscard]] bool hasCopyRestrictionForSelected() const; bool showCopyRestrictionForSelected(); [[nodiscard]] bool hasSelectRestriction() const; VideoUserpic *validateVideoUserpic(not_null peer); // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const; const not_null _widget; const not_null _scroll; const not_null _controller; const not_null _peer; const not_null _history; const not_null _elementDelegate; const std::unique_ptr _emojiInteractions; std::shared_ptr _theme; History *_migrated = nullptr; HistoryView::ElementDelegate *_migratedElementDelegate = nullptr; int _contentWidth = 0; int _historyPaddingTop = 0; int _revealHeight = 0; // Save visible area coords for painting / pressing userpics. int _visibleAreaTop = 0; int _visibleAreaBottom = 0; // With migrated history we perhaps do not need to display // the first _history message date (just skip it by height). int _historySkipHeight = 0; std::unique_ptr _botAbout; std::unique_ptr _emptyPainter; mutable History *_curHistory = nullptr; mutable int _curBlock = 0; mutable int _curItem = 0; style::cursor _cursor = style::cur_default; SelectedItems _selected; std::optional _chooseForReportReason; const std::unique_ptr _pathGradient; bool _isChatWide = false; base::flat_set> _animatedStickersPlayed; base::flat_map< not_null, std::shared_ptr> _userpics, _userpicsCache; base::flat_map< MsgId, std::shared_ptr> _sponsoredUserpics; base::flat_map< not_null, std::unique_ptr> _videoUserpics; std::unique_ptr _reactionsManager; MouseAction _mouseAction = MouseAction::None; TextSelectType _mouseSelectType = TextSelectType::Letters; QPoint _dragStartPosition; QPoint _mousePosition; HistoryItem *_mouseActionItem = nullptr; HistoryItem *_dragStateItem = nullptr; CursorState _mouseCursorState = CursorState(); uint16 _mouseTextSymbol = 0; bool _pressWasInactive = false; bool _recountedAfterPendingResizedItems = false; QPoint _trippleClickPoint; base::Timer _trippleClickTimer; Element *_dragSelFrom = nullptr; Element *_dragSelTo = nullptr; bool _dragSelecting = false; bool _wasSelectedText = false; // was some text selected in current drag action // scroll by touch support (at least Windows Surface tablets) bool _touchScroll = false; bool _touchSelect = false; bool _touchInProgress = false; QPoint _touchStart, _touchPrevPos, _touchPos; base::Timer _touchSelectTimer; Ui::DraggingScrollManager _selectScroll; rpl::variable _sharingDisallowed = false; Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual; bool _touchPrevPosValid = false; bool _touchWaitingAcceleration = false; QPoint _touchSpeed; crl::time _touchSpeedTime = 0; crl::time _touchAccelerationTime = 0; crl::time _touchTime = 0; base::Timer _touchScrollTimer; Ui::Animations::Simple _spoilerOpacity; // _menu must be destroyed before _whoReactedMenuLifetime. rpl::lifetime _whoReactedMenuLifetime; base::unique_qptr _menu; bool _scrollDateShown = false; Ui::Animations::Simple _scrollDateOpacity; SingleQueuedInvokation _scrollDateCheck; base::Timer _scrollDateHideTimer; Element *_scrollDateLastItem = nullptr; int _scrollDateLastItemTop = 0; ClickHandlerPtr _scrollDateLink; };