/* 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/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; struct TextState; struct StateRequest; enum class CursorState : char; enum class PointState : char; class EmptyPainter; class Element; } // namespace HistoryView namespace Window { class SessionController; } // namespace Window namespace Ui { class PopupMenu; } // namespace Ui class HistoryWidget; class HistoryInner : public Ui::RpWidget , public Ui::AbstractTooltipShower , private base::Subscriber { // The Q_OBJECT meta info is used for qobject_cast! Q_OBJECT public: using Element = HistoryView::Element; HistoryInner( not_null historyWidget, not_null scroll, not_null controller, not_null history); Main::Session &session() const; void messagesReceived(PeerData *peer, const QVector &messages); void messagesReceivedDown(PeerData *peer, const QVector &messages); TextForMimeData getSelectedText() const; void touchScrollUpdated(const QPoint &screenPos); void checkHistoryActivation(); void recountHistoryGeometry(); void updateSize(); void repaintItem(const HistoryItem *item); void repaintItem(const Element *view); bool canCopySelected() const; bool canDeleteSelected() const; HistoryView::TopBarWidget::SelectedState getSelectionState() const; void clearSelected(bool onlyTextSelection = false); MessageIdsList getSelectedItems() const; void selectItem(not_null item); bool inSelectionMode() const; bool elementIntersectsRange( not_null view, int from, int till) const; void elementStartStickerLoop(not_null view); [[nodiscard]] crl::time elementHighlightTime( not_null view); void elementShowPollResults( not_null poll, FullMsgId context); void elementShowTooltip( const TextWithEntities &text, Fn hiddenCallback); bool elementIsGifPaused(); void elementSendBotCommand( const QString &command, const FullMsgId &context); void elementHandleViaClick(not_null bot); void updateBotInfo(bool recount = true); bool wasSelectedText() const; void setFirstLoading(bool loading); // 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; // -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; // HistoryView::ElementDelegate interface. static not_null ElementDelegate(); ~HistoryInner(); 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(QEvent *e) override; void leaveEventHook(QEvent *e) override; void resizeEvent(QResizeEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override; public slots: void onParentGeometryChanged(); void onTouchSelect(); void onTouchScrollTimer(); private: class BotAbout; 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); ClickHandlerPtr hiddenUserpicLink(FullMsgId id); 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, 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); 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(); // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const; static HistoryInner *Instance; const not_null _widget; const not_null _scroll; const not_null _controller; const not_null _peer; const not_null _history; History *_migrated = nullptr; int _contentWidth = 0; int _historyPaddingTop = 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; bool _firstLoading = false; style::cursor _cursor = style::cur_default; SelectedItems _selected; base::flat_set> _animatedStickersPlayed; base::flat_map< not_null, std::shared_ptr> _userpics, _userpicsCache; 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; QPoint _trippleClickPoint; QTimer _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; QTimer _touchSelectTimer; 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; QTimer _touchScrollTimer; base::unique_qptr _menu; bool _scrollDateShown = false; Ui::Animations::Simple _scrollDateOpacity; SingleQueuedInvokation _scrollDateCheck; base::Timer _scrollDateHideTimer; Element *_scrollDateLastItem = nullptr; int _scrollDateLastItemTop = 0; ClickHandlerPtr _scrollDateLink; };