1 /* 2 This file is part of Telegram Desktop, 3 the official desktop application for the Telegram messaging service. 4 5 For license and copyright information please follow this link: 6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL 7 */ 8 #pragma once 9 10 #include "ui/rp_widget.h" 11 #include "info/media/info_media_widget.h" 12 #include "data/data_shared_media.h" 13 #include "overview/overview_layout_delegate.h" 14 15 class DeleteMessagesBox; 16 17 namespace Main { 18 class Session; 19 } // namespace Main 20 21 namespace HistoryView { 22 struct TextState; 23 struct StateRequest; 24 enum class CursorState : char; 25 enum class PointState : char; 26 } // namespace HistoryView 27 28 namespace Ui { 29 class PopupMenu; 30 } // namespace Ui 31 32 namespace Overview { 33 namespace Layout { 34 class ItemBase; 35 } // namespace Layout 36 } // namespace Overview 37 38 namespace Window { 39 class SessionController; 40 } // namespace Window 41 42 namespace Info { 43 44 class AbstractController; 45 46 namespace Media { 47 48 using BaseLayout = Overview::Layout::ItemBase; 49 using UniversalMsgId = MsgId; 50 51 class ListWidget final 52 : public Ui::RpWidget 53 , public Overview::Layout::Delegate { 54 public: 55 ListWidget( 56 QWidget *parent, 57 not_null<AbstractController*> controller); 58 ~ListWidget(); 59 60 Main::Session &session() const; 61 62 void restart(); 63 64 rpl::producer<int> scrollToRequests() const; 65 rpl::producer<SelectedItems> selectedListValue() const; cancelSelection()66 void cancelSelection() { 67 clearSelected(); 68 } 69 70 QRect getCurrentSongGeometry(); checkForHide()71 rpl::producer<> checkForHide() const { 72 return _checkForHide.events(); 73 } 74 bool preventAutoHide() const; 75 76 void saveState(not_null<Memento*> memento); 77 void restoreState(not_null<Memento*> memento); 78 79 // Overview::Layout::Delegate 80 void registerHeavyItem(not_null<const BaseLayout*> item) override; 81 void unregisterHeavyItem(not_null<const BaseLayout*> item) override; 82 void repaintItem(not_null<const BaseLayout*> item) override; 83 bool itemVisible(not_null<const BaseLayout*> item) override; 84 85 void openPhoto(not_null<PhotoData*> photo, FullMsgId id) override; 86 void openDocument( 87 not_null<DocumentData*> document, 88 FullMsgId id, 89 bool showInMediaView = false) override; 90 91 private: 92 struct Context; 93 struct DateBadge; 94 class Section; 95 using CursorState = HistoryView::CursorState; 96 using TextState = HistoryView::TextState; 97 using StateRequest = HistoryView::StateRequest; 98 enum class MouseAction { 99 None, 100 PrepareDrag, 101 Dragging, 102 PrepareSelect, 103 Selecting, 104 }; 105 struct CachedItem { 106 CachedItem(std::unique_ptr<BaseLayout> item); 107 CachedItem(CachedItem &&other); 108 CachedItem &operator=(CachedItem &&other); 109 ~CachedItem(); 110 111 std::unique_ptr<BaseLayout> item; 112 bool stale = false; 113 }; 114 struct FoundItem { 115 not_null<BaseLayout*> layout; 116 QRect geometry; 117 bool exact = false; 118 }; 119 struct SelectionData { SelectionDataSelectionData120 explicit SelectionData(TextSelection text) : text(text) { 121 } 122 123 TextSelection text; 124 bool canDelete = false; 125 bool canForward = false; 126 }; 127 using SelectedMap = base::flat_map< 128 UniversalMsgId, 129 SelectionData, 130 std::less<>>; 131 enum class DragSelectAction { 132 None, 133 Selecting, 134 Deselecting, 135 }; 136 struct MouseState { 137 UniversalMsgId itemId = 0; 138 QSize size; 139 QPoint cursor; 140 bool inside = false; 141 142 inline bool operator==(const MouseState &other) const { 143 return (itemId == other.itemId) 144 && (cursor == other.cursor); 145 } 146 inline bool operator!=(const MouseState &other) const { 147 return !(*this == other); 148 } 149 150 }; 151 enum class ContextMenuSource { 152 Mouse, 153 Touch, 154 Other, 155 }; 156 struct ScrollTopState { 157 UniversalMsgId item = 0; 158 int shift = 0; 159 }; 160 161 int resizeGetHeight(int newWidth) override; 162 void visibleTopBottomUpdated( 163 int visibleTop, 164 int visibleBottom) override; 165 166 void paintEvent(QPaintEvent *e) override; 167 void mouseMoveEvent(QMouseEvent *e) override; 168 void mousePressEvent(QMouseEvent *e) override; 169 void mouseReleaseEvent(QMouseEvent *e) override; 170 void mouseDoubleClickEvent(QMouseEvent *e) override; 171 void contextMenuEvent(QContextMenuEvent *e) override; 172 void enterEventHook(QEnterEvent *e) override; 173 void leaveEventHook(QEvent *e) override; 174 175 void start(); 176 int recountHeight(); 177 void refreshHeight(); 178 179 QMargins padding() const; 180 bool isMyItem(not_null<const HistoryItem*> item) const; 181 bool isItemLayout( 182 not_null<const HistoryItem*> item, 183 BaseLayout *layout) const; 184 bool isPossiblyMyId(FullMsgId fullId) const; 185 void repaintItem(const HistoryItem *item); 186 void repaintItem(UniversalMsgId msgId); 187 void repaintItem(const BaseLayout *item); 188 void repaintItem(QRect itemGeometry); 189 void itemRemoved(not_null<const HistoryItem*> item); 190 void itemLayoutChanged(not_null<const HistoryItem*> item); 191 192 void refreshViewer(); 193 void invalidatePaletteCache(); 194 void refreshRows(); 195 SparseIdsMergedSlice::Key sliceKey( 196 UniversalMsgId universalId) const; 197 BaseLayout *getLayout(UniversalMsgId universalId); 198 BaseLayout *getExistingLayout(UniversalMsgId universalId) const; 199 std::unique_ptr<BaseLayout> createLayout( 200 UniversalMsgId universalId, 201 Type type); 202 203 SelectedItems collectSelectedItems() const; 204 MessageIdsList collectSelectedIds() const; 205 void pushSelectedItems(); 206 FullMsgId computeFullId(UniversalMsgId universalId) const; 207 bool hasSelected() const; 208 bool isSelectedItem( 209 const SelectedMap::const_iterator &i) const; 210 void removeItemSelection( 211 const SelectedMap::const_iterator &i); 212 bool hasSelectedText() const; 213 bool hasSelectedItems() const; 214 void clearSelected(); 215 void forwardSelected(); 216 void forwardItem(UniversalMsgId universalId); 217 void forwardItems(MessageIdsList &&items); 218 void deleteSelected(); 219 void deleteItem(UniversalMsgId universalId); 220 DeleteMessagesBox *deleteItems(MessageIdsList &&items); 221 void applyItemSelection( 222 UniversalMsgId universalId, 223 TextSelection selection); 224 void toggleItemSelection( 225 UniversalMsgId universalId); 226 SelectedMap::iterator itemUnderPressSelection(); 227 SelectedMap::const_iterator itemUnderPressSelection() const; 228 bool isItemUnderPressSelected() const; 229 bool requiredToStartDragging(not_null<BaseLayout*> layout) const; 230 bool isPressInSelectedText(TextState state) const; 231 void applyDragSelection(); 232 void applyDragSelection(SelectedMap &applyTo) const; 233 bool changeItemSelection( 234 SelectedMap &selected, 235 UniversalMsgId universalId, 236 TextSelection selection) const; 237 238 static bool IsAfter( 239 const MouseState &a, 240 const MouseState &b); 241 static bool SkipSelectFromItem(const MouseState &state); 242 static bool SkipSelectTillItem(const MouseState &state); 243 244 void markLayoutsStale(); 245 void clearStaleLayouts(); 246 std::vector<Section>::iterator findSectionByItem( 247 UniversalMsgId universalId); 248 std::vector<Section>::iterator findSectionAfterTop(int top); 249 std::vector<Section>::const_iterator findSectionAfterTop( 250 int top) const; 251 std::vector<Section>::const_iterator findSectionAfterBottom( 252 std::vector<Section>::const_iterator from, 253 int bottom) const; 254 FoundItem findItemByPoint(QPoint point) const; 255 std::optional<FoundItem> findItemById(UniversalMsgId universalId); 256 FoundItem findItemDetails(not_null<BaseLayout*> item); 257 FoundItem foundItemInSection( 258 const FoundItem &item, 259 const Section §ion) const; 260 261 ScrollTopState countScrollState() const; 262 void saveScrollState(); 263 void restoreScrollState(); 264 265 QPoint clampMousePosition(QPoint position) const; 266 void mouseActionStart( 267 const QPoint &globalPosition, 268 Qt::MouseButton button); 269 void mouseActionUpdate(const QPoint &globalPosition); 270 void mouseActionUpdate(); 271 void mouseActionFinish( 272 const QPoint &globalPosition, 273 Qt::MouseButton button); 274 void mouseActionCancel(); 275 void performDrag(); 276 style::cursor computeMouseCursor() const; 277 void showContextMenu( 278 QContextMenuEvent *e, 279 ContextMenuSource source); 280 281 void updateDragSelection(); 282 void clearDragSelection(); 283 284 void updateDateBadgeFor(int top); 285 void scrollDateCheck(); 286 void scrollDateHide(); 287 void toggleScrollDateShown(); 288 289 void trySwitchToWordSelection(); 290 void switchToWordSelection(); 291 void validateTrippleClickStartTime(); 292 void checkMoveToOtherViewer(); 293 void clearHeavyItems(); 294 295 void setActionBoxWeak(QPointer<Ui::RpWidget> box); 296 297 const not_null<AbstractController*> _controller; 298 const not_null<PeerData*> _peer; 299 PeerData * const _migrated = nullptr; 300 const Type _type = Type::Photo; 301 302 static constexpr auto kMinimalIdsLimit = 16; 303 static constexpr auto kDefaultAroundId = (ServerMaxMsgId - 1); 304 UniversalMsgId _universalAroundId = kDefaultAroundId; 305 int _idsLimit = kMinimalIdsLimit; 306 SparseIdsMergedSlice _slice; 307 308 std::unordered_map<UniversalMsgId, CachedItem> _layouts; 309 base::flat_set<not_null<const BaseLayout*>> _heavyLayouts; 310 bool _heavyLayoutsInvalidated = false; 311 std::vector<Section> _sections; 312 313 int _visibleTop = 0; 314 int _visibleBottom = 0; 315 ScrollTopState _scrollTopState; 316 rpl::event_stream<int> _scrollToRequests; 317 318 MouseAction _mouseAction = MouseAction::None; 319 TextSelectType _mouseSelectType = TextSelectType::Letters; 320 QPoint _mousePosition; 321 MouseState _overState; 322 MouseState _pressState; 323 BaseLayout *_overLayout = nullptr; 324 UniversalMsgId _contextUniversalId = 0; 325 CursorState _mouseCursorState = CursorState(); 326 uint16 _mouseTextSymbol = 0; 327 bool _pressWasInactive = false; 328 SelectedMap _selected; 329 SelectedMap _dragSelected; 330 rpl::event_stream<SelectedItems> _selectedListStream; 331 style::cursor _cursor = style::cur_default; 332 DragSelectAction _dragSelectAction = DragSelectAction::None; 333 bool _wasSelectedText = false; // was some text selected in current drag action 334 335 const std::unique_ptr<DateBadge> _dateBadge; 336 337 base::unique_qptr<Ui::PopupMenu> _contextMenu; 338 rpl::event_stream<> _checkForHide; 339 QPointer<Ui::RpWidget> _actionBoxWeak; 340 rpl::lifetime _actionBoxWeakLifetime; 341 342 QPoint _trippleClickPoint; 343 crl::time _trippleClickStartTime = 0; 344 345 rpl::lifetime _viewerLifetime; 346 347 }; 348 349 } // namespace Media 350 } // namespace Info 351