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 "data/data_types.h" 11 #include "data/data_peer.h" 12 #include "data/data_drafts.h" 13 #include "dialogs/dialogs_entry.h" 14 #include "dialogs/ui/dialogs_message_view.h" 15 #include "history/view/history_view_send_action.h" 16 #include "base/observer.h" 17 #include "base/timer.h" 18 #include "base/variant.h" 19 #include "base/flat_set.h" 20 #include "base/flags.h" 21 22 class History; 23 class HistoryBlock; 24 class HistoryItem; 25 class HistoryMessage; 26 class HistoryService; 27 struct HistoryMessageMarkupData; 28 29 namespace Main { 30 class Session; 31 } // namespace Main 32 33 namespace Data { 34 struct Draft; 35 class Session; 36 class Folder; 37 class ChatFilter; 38 39 enum class ForwardOptions { 40 PreserveInfo, 41 NoSenderNames, 42 NoNamesAndCaptions, 43 }; 44 45 struct ForwardDraft { 46 MessageIdsList ids; 47 ForwardOptions options = ForwardOptions::PreserveInfo; 48 }; 49 50 struct ResolvedForwardDraft { 51 HistoryItemsList items; 52 ForwardOptions options = ForwardOptions::PreserveInfo; 53 }; 54 55 } // namespace Data 56 57 namespace Dialogs { 58 class Row; 59 class IndexedList; 60 } // namespace Dialogs 61 62 namespace HistoryView { 63 class Element; 64 } // namespace HistoryView 65 66 enum class NewMessageType { 67 Unread, 68 Last, 69 Existing, 70 }; 71 72 enum class UnreadMentionType { 73 New, // when new message is added to history 74 Existing, // when some messages slice was received 75 }; 76 77 class History final : public Dialogs::Entry { 78 public: 79 using Element = HistoryView::Element; 80 81 History(not_null<Data::Session*> owner, PeerId peerId); 82 History(const History &) = delete; 83 History &operator=(const History &) = delete; 84 ~History(); 85 86 ChannelId channelId() const; 87 bool isChannel() const; 88 bool isMegagroup() const; 89 not_null<History*> migrateToOrMe() const; 90 History *migrateFrom() const; 91 MsgRange rangeForDifferenceRequest() const; 92 void checkLocalMessages(); 93 void removeJoinedMessage(); 94 95 96 bool isEmpty() const; 97 bool isDisplayedEmpty() const; 98 Element *findFirstNonEmpty() const; 99 Element *findFirstDisplayed() const; 100 Element *findLastNonEmpty() const; 101 Element *findLastDisplayed() const; 102 bool hasOrphanMediaGroupPart() const; 103 bool removeOrphanMediaGroupPart(); 104 QVector<MsgId> collectMessagesFromUserToDelete( 105 not_null<UserData*> user) const; 106 107 enum class ClearType { 108 Unload, 109 DeleteChat, 110 ClearHistory, 111 }; 112 void clear(ClearType type); 113 void clearUpTill(MsgId availableMinId); 114 115 void applyGroupAdminChanges(const base::flat_set<UserId> &changes); 116 117 template <typename ...Args> makeMessage(Args &&...args)118 not_null<HistoryMessage*> makeMessage(Args &&...args) { 119 return static_cast<HistoryMessage*>( 120 insertItem( 121 std::make_unique<HistoryMessage>( 122 this, 123 std::forward<Args>(args)...)).get()); 124 } 125 126 template <typename ...Args> makeServiceMessage(Args &&...args)127 not_null<HistoryService*> makeServiceMessage(Args &&...args) { 128 return static_cast<HistoryService*>( 129 insertItem( 130 std::make_unique<HistoryService>( 131 this, 132 std::forward<Args>(args)...)).get()); 133 } 134 void destroyMessage(not_null<HistoryItem*> item); 135 136 void unpinAllMessages(); 137 138 not_null<HistoryItem*> addNewMessage( 139 MsgId id, 140 const MTPMessage &msg, 141 MessageFlags localFlags, 142 NewMessageType type); 143 not_null<HistoryItem*> addNewLocalMessage( 144 MsgId id, 145 MessageFlags flags, 146 UserId viaBotId, 147 MsgId replyTo, 148 TimeId date, 149 PeerId from, 150 const QString &postAuthor, 151 const TextWithEntities &text, 152 const MTPMessageMedia &media, 153 HistoryMessageMarkupData &&markup, 154 uint64 groupedId = 0); 155 not_null<HistoryItem*> addNewLocalMessage( 156 MsgId id, 157 MessageFlags flags, 158 TimeId date, 159 PeerId from, 160 const QString &postAuthor, 161 not_null<HistoryItem*> forwardOriginal); 162 not_null<HistoryItem*> addNewLocalMessage( 163 MsgId id, 164 MessageFlags flags, 165 UserId viaBotId, 166 MsgId replyTo, 167 TimeId date, 168 PeerId from, 169 const QString &postAuthor, 170 not_null<DocumentData*> document, 171 const TextWithEntities &caption, 172 HistoryMessageMarkupData &&markup); 173 not_null<HistoryItem*> addNewLocalMessage( 174 MsgId id, 175 MessageFlags flags, 176 UserId viaBotId, 177 MsgId replyTo, 178 TimeId date, 179 PeerId from, 180 const QString &postAuthor, 181 not_null<PhotoData*> photo, 182 const TextWithEntities &caption, 183 HistoryMessageMarkupData &&markup); 184 not_null<HistoryItem*> addNewLocalMessage( 185 MsgId id, 186 MessageFlags flags, 187 UserId viaBotId, 188 MsgId replyTo, 189 TimeId date, 190 PeerId from, 191 const QString &postAuthor, 192 not_null<GameData*> game, 193 HistoryMessageMarkupData &&markup); 194 195 // Used only internally and for channel admin log. 196 not_null<HistoryItem*> createItem( 197 MsgId id, 198 const MTPMessage &message, 199 MessageFlags localFlags, 200 bool detachExistingItem); 201 std::vector<not_null<HistoryItem*>> createItems( 202 const QVector<MTPMessage> &data); 203 204 void addOlderSlice(const QVector<MTPMessage> &slice); 205 void addNewerSlice(const QVector<MTPMessage> &slice); 206 207 void newItemAdded(not_null<HistoryItem*> item); 208 209 void registerClientSideMessage(not_null<HistoryItem*> item); 210 void unregisterClientSideMessage(not_null<HistoryItem*> item); 211 [[nodiscard]] auto clientSideMessages() 212 -> const base::flat_set<not_null<HistoryItem*>> &; 213 [[nodiscard]] HistoryItem *latestSendingMessage() const; 214 215 [[nodiscard]] bool readInboxTillNeedsRequest(MsgId tillId); 216 void applyInboxReadUpdate( 217 FolderId folderId, 218 MsgId upTo, 219 int stillUnread, 220 int32 channelPts = 0); 221 void inboxRead(MsgId upTo, std::optional<int> stillUnread = {}); 222 void inboxRead(not_null<const HistoryItem*> wasRead); 223 void outboxRead(MsgId upTo); 224 void outboxRead(not_null<const HistoryItem*> wasRead); 225 [[nodiscard]] bool isServerSideUnread( 226 not_null<const HistoryItem*> item) const; 227 [[nodiscard]] MsgId loadAroundId() const; 228 [[nodiscard]] MsgId inboxReadTillId() const; 229 [[nodiscard]] MsgId outboxReadTillId() const; 230 231 [[nodiscard]] bool trackUnreadMessages() const; 232 [[nodiscard]] int unreadCount() const; 233 [[nodiscard]] bool unreadCountKnown() const; 234 235 // Some old unread count is known, but we read history till some place. 236 [[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const; 237 238 void setUnreadCount(int newUnreadCount); 239 void setUnreadMark(bool unread); 240 [[nodiscard]] bool unreadMark() const; 241 void setFakeUnreadWhileOpened(bool enabled); 242 [[nodiscard]] bool fakeUnreadWhileOpened() const; 243 [[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0. 244 [[nodiscard]] bool mute() const; 245 bool changeMute(bool newMute); 246 void addUnreadBar(); 247 void destroyUnreadBar(); 248 [[nodiscard]] Element *unreadBar() const; 249 void calculateFirstUnreadMessage(); 250 void unsetFirstUnreadMessage(); 251 [[nodiscard]] Element *firstUnreadMessage() const; 252 void clearNotifications(); 253 void clearIncomingNotifications(); 254 255 [[nodiscard]] bool loadedAtBottom() const; // last message is in the list 256 void setNotLoadedAtBottom(); 257 [[nodiscard]] bool loadedAtTop() const; // nothing was added after loading history back 258 [[nodiscard]] bool isReadyFor(MsgId msgId); // has messages for showing history at msgId 259 void getReadyFor(MsgId msgId); 260 261 [[nodiscard]] HistoryItem *lastMessage() const; 262 [[nodiscard]] HistoryItem *lastServerMessage() const; 263 [[nodiscard]] bool lastMessageKnown() const; 264 [[nodiscard]] bool lastServerMessageKnown() const; 265 void unknownMessageDeleted(MsgId messageId); 266 void applyDialogTopMessage(MsgId topMessageId); 267 void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data); 268 void applyPinnedUpdate(const MTPDupdateDialogPinned &data); 269 void applyDialogFields( 270 Data::Folder *folder, 271 int unreadCount, 272 MsgId maxInboxRead, 273 MsgId maxOutboxRead); 274 void dialogEntryApplied(); 275 276 void cacheTopPromotion( 277 bool promoted, 278 const QString &type, 279 const QString &message); 280 [[nodiscard]] QStringView topPromotionType() const; 281 [[nodiscard]] QString topPromotionMessage() const; 282 [[nodiscard]] bool topPromotionAboutShown() const; 283 void markTopPromotionAboutShown(); 284 285 MsgId minMsgId() const; 286 MsgId maxMsgId() const; 287 MsgId msgIdForRead() const; 288 HistoryItem *lastEditableMessage() const; 289 290 void resizeToWidth(int newWidth); 291 void forceFullResize(); 292 int height() const; 293 294 void itemRemoved(not_null<HistoryItem*> item); 295 void itemVanished(not_null<HistoryItem*> item); 296 297 HistoryItem *currentNotification(); 298 bool hasNotification() const; 299 void skipNotification(); 300 void popNotification(HistoryItem *item); 301 302 bool hasPendingResizedItems() const; 303 void setHasPendingResizedItems(); 304 305 [[nodiscard]] auto sendActionPainter() 306 -> not_null<HistoryView::SendActionPainter*> { 307 return &_sendActionPainter; 308 } 309 310 void clearLastKeyboard(); 311 getUnreadMentionsLoadedCount()312 int getUnreadMentionsLoadedCount() const { 313 return _unreadMentions.size(); 314 } getMinLoadedUnreadMention()315 MsgId getMinLoadedUnreadMention() const { 316 return _unreadMentions.empty() ? 0 : _unreadMentions.front(); 317 } getMaxLoadedUnreadMention()318 MsgId getMaxLoadedUnreadMention() const { 319 return _unreadMentions.empty() ? 0 : _unreadMentions.back(); 320 } 321 int getUnreadMentionsCount(int notLoadedValue = -1) const { 322 return _unreadMentionsCount ? *_unreadMentionsCount : notLoadedValue; 323 } hasUnreadMentions()324 bool hasUnreadMentions() const { 325 return (getUnreadMentionsCount() > 0); 326 } 327 void setUnreadMentionsCount(int count); 328 bool addToUnreadMentions(MsgId msgId, UnreadMentionType type); 329 void eraseFromUnreadMentions(MsgId msgId); 330 void addUnreadMentionsSlice(const MTPmessages_Messages &result); 331 332 Data::Draft *draft(Data::DraftKey key) const; 333 void setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft); 334 void clearDraft(Data::DraftKey key); 335 336 [[nodiscard]] const Data::HistoryDrafts &draftsMap() const; 337 void setDraftsMap(Data::HistoryDrafts &&map); 338 localDraft()339 Data::Draft *localDraft() const { 340 return draft(Data::DraftKey::Local()); 341 } localEditDraft()342 Data::Draft *localEditDraft() const { 343 return draft(Data::DraftKey::LocalEdit()); 344 } cloudDraft()345 Data::Draft *cloudDraft() const { 346 return draft(Data::DraftKey::Cloud()); 347 } setLocalDraft(std::unique_ptr<Data::Draft> && draft)348 void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) { 349 setDraft(Data::DraftKey::Local(), std::move(draft)); 350 } setLocalEditDraft(std::unique_ptr<Data::Draft> && draft)351 void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) { 352 setDraft(Data::DraftKey::LocalEdit(), std::move(draft)); 353 } setCloudDraft(std::unique_ptr<Data::Draft> && draft)354 void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) { 355 setDraft(Data::DraftKey::Cloud(), std::move(draft)); 356 } clearLocalDraft()357 void clearLocalDraft() { 358 clearDraft(Data::DraftKey::Local()); 359 } clearCloudDraft()360 void clearCloudDraft() { 361 clearDraft(Data::DraftKey::Cloud()); 362 } clearLocalEditDraft()363 void clearLocalEditDraft() { 364 clearDraft(Data::DraftKey::LocalEdit()); 365 } 366 void clearDrafts(); 367 Data::Draft *createCloudDraft(const Data::Draft *fromDraft); 368 bool skipCloudDraftUpdate(TimeId date) const; 369 void startSavingCloudDraft(); 370 void finishSavingCloudDraft(TimeId savedAt); 371 void takeLocalDraft(not_null<History*> from); 372 void applyCloudDraft(); 373 void draftSavedToCloud(); 374 forwardDraft()375 [[nodiscard]] const Data::ForwardDraft &forwardDraft() const { 376 return _forwardDraft; 377 } 378 [[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft( 379 const Data::ForwardDraft &draft) const; 380 [[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(); 381 void setForwardDraft(Data::ForwardDraft &&draft); 382 383 History *migrateSibling() const; 384 [[nodiscard]] bool useTopPromotion() const; 385 int fixedOnTopIndex() const override; 386 void updateChatListExistence() override; 387 bool shouldBeInChatList() const override; 388 int chatListUnreadCount() const override; 389 bool chatListUnreadMark() const override; 390 bool chatListMutedBadge() const override; 391 Dialogs::UnreadState chatListUnreadState() const override; 392 HistoryItem *chatListMessage() const override; 393 bool chatListMessageKnown() const override; 394 void requestChatListMessage() override; 395 const QString &chatListName() const override; 396 const QString &chatListNameSortKey() const override; 397 const base::flat_set<QString> &chatListNameWords() const override; 398 const base::flat_set<QChar> &chatListFirstLetters() const override; 399 void loadUserpic() override; 400 void paintUserpic( 401 Painter &p, 402 std::shared_ptr<Data::CloudImageView> &view, 403 int x, 404 int y, 405 int size) const override; 406 407 void refreshChatListNameSortKey(); 408 409 void setFakeChatListMessageFrom(const MTPmessages_Messages &data); 410 void checkChatListMessageRemoved(not_null<HistoryItem*> item); 411 412 void applyChatListGroup( 413 ChannelId channelId, 414 const MTPmessages_Messages &data); 415 forgetScrollState()416 void forgetScrollState() { 417 scrollTopItem = nullptr; 418 } 419 420 // find the correct scrollTopItem and scrollTopOffset using given top 421 // of the displayed window relative to the history start coordinate 422 void countScrollState(int top); 423 424 [[nodiscard]] std::pair<Element*, int> findItemAndOffset(int top) const; 425 426 [[nodiscard]] MsgId nextNonHistoryEntryId(); 427 428 bool folderKnown() const override; 429 Data::Folder *folder() const override; 430 void setFolder( 431 not_null<Data::Folder*> folder, 432 HistoryItem *folderDialogItem = nullptr); 433 void clearFolder(); 434 435 // Interface for Data::Histories. 436 void setInboxReadTill(MsgId upTo); 437 std::optional<int> countStillUnreadLocal(MsgId readTillId) const; 438 439 [[nodiscard]] bool hasPinnedMessages() const; 440 void setHasPinnedMessages(bool has); 441 442 // Still public data. 443 std::deque<std::unique_ptr<HistoryBlock>> blocks; 444 445 not_null<PeerData*> peer; 446 447 // we save the last showAtMsgId to restore the state when switching 448 // between different conversation histories 449 MsgId showAtMsgId = ShowAtUnreadMsgId; 450 451 // we save a pointer of the history item at the top of the displayed window 452 // together with an offset from the window top to the top of this message 453 // resulting scrollTop = top(scrollTopItem) + scrollTopOffset 454 Element *scrollTopItem = nullptr; 455 int scrollTopOffset = 0; 456 457 bool lastKeyboardInited = false; 458 bool lastKeyboardUsed = false; 459 MsgId lastKeyboardId = 0; 460 MsgId lastKeyboardHiddenId = 0; 461 PeerId lastKeyboardFrom = 0; 462 463 mtpRequestId sendRequestId = 0; 464 465 Ui::Text::String cloudDraftTextCache; 466 Dialogs::Ui::MessageView lastItemDialogsView; 467 468 private: 469 friend class HistoryBlock; 470 471 enum class Flag { 472 f_has_pending_resized_items = (1 << 0), 473 }; 474 using Flags = base::flags<Flag>; is_flag_type(Flag)475 friend inline constexpr auto is_flag_type(Flag) { 476 return true; 477 }; 478 479 // when this item is destroyed scrollTopItem just points to the next one 480 // and scrollTopOffset remains the same 481 // if we are at the bottom of the window scrollTopItem == nullptr and 482 // scrollTopOffset is undefined 483 void getNextScrollTopItem(HistoryBlock *block, int32 i); 484 485 // helper method for countScrollState(int top) 486 [[nodiscard]] Element *findScrollTopItem(int top) const; 487 488 // this method just removes a block from the blocks list 489 // when the last item from this block was detached and 490 // calls the required previousItemChanged() 491 void removeBlock(not_null<HistoryBlock*> block); 492 void clearSharedMedia(); 493 494 not_null<HistoryItem*> insertItem(std::unique_ptr<HistoryItem> item); 495 not_null<HistoryItem*> addNewItem( 496 not_null<HistoryItem*> item, 497 bool unread); 498 not_null<HistoryItem*> addNewToBack( 499 not_null<HistoryItem*> item, 500 bool unread); 501 not_null<HistoryItem*> addNewInTheMiddle( 502 not_null<HistoryItem*> item, 503 int blockIndex, 504 int itemIndex); 505 506 // All this methods add a new item to the first or last block 507 // depending on if we are in isBuildingFronBlock() state. 508 // The last block is created on the go if it is needed. 509 510 // Adds the item to the back or front block, depending on 511 // isBuildingFrontBlock(), creating the block if necessary. 512 void addItemToBlock(not_null<HistoryItem*> item); 513 514 // Usually all new items are added to the last block. 515 // Only when we scroll up and add a new slice to the 516 // front we want to create a new front block. 517 void startBuildingFrontBlock(int expectedItemsCount = 1); 518 void finishBuildingFrontBlock(); isBuildingFrontBlock()519 bool isBuildingFrontBlock() const { 520 return _buildingFrontBlock != nullptr; 521 } 522 523 void addCreatedOlderSlice( 524 const std::vector<not_null<HistoryItem*>> &items); 525 526 void checkForLoadedAtTop(not_null<HistoryItem*> added); 527 void mainViewRemoved( 528 not_null<HistoryBlock*> block, 529 not_null<Element*> view); 530 void removeNotification(not_null<HistoryItem*> item); 531 532 TimeId adjustedChatListTimeId() const override; 533 void changedChatListPinHook() override; 534 535 void setOutboxReadTill(MsgId upTo); 536 void readClientSideMessages(); 537 538 void applyMessageChanges( 539 not_null<HistoryItem*> item, 540 const MTPMessage &original); 541 void applyServiceChanges( 542 not_null<HistoryItem*> item, 543 const MTPDmessageService &data); 544 545 // After adding a new history slice check lastMessage / loadedAtBottom. 546 void checkLastMessage(); 547 void setLastMessage(HistoryItem *item); 548 void setLastServerMessage(HistoryItem *item); 549 550 void refreshChatListMessage(); 551 void setChatListMessage(HistoryItem *item); 552 std::optional<HistoryItem*> computeChatListMessageFromLast() const; 553 void setChatListMessageFromLast(); 554 void setChatListMessageUnknown(); 555 void setFakeChatListMessage(); 556 557 // Add all items to the unread mentions if we were not loaded at bottom and now are. 558 void checkAddAllToUnreadMentions(); 559 560 void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items); 561 void addEdgesToSharedMedia(); 562 563 void addItemsToLists(const std::vector<not_null<HistoryItem*>> &items); 564 bool clearUnreadOnClientSide() const; 565 bool skipUnreadUpdate() const; 566 567 HistoryItem *lastAvailableMessage() const; 568 void getNextFirstUnreadMessage(); 569 bool nonEmptyCountMoreThan(int count) const; 570 571 // Creates if necessary a new block for adding item. 572 // Depending on isBuildingFrontBlock() gets front or back block. 573 HistoryBlock *prepareBlockForAddingItem(); 574 575 void viewReplaced(not_null<const Element*> was, Element *now); 576 577 void createLocalDraftFromCloud(); 578 579 HistoryService *insertJoinedMessage(); 580 void insertMessageToBlocks(not_null<HistoryItem*> item); 581 582 void setFolderPointer(Data::Folder *folder); 583 584 Flags _flags = 0; 585 bool _mute = false; 586 int _width = 0; 587 int _height = 0; 588 Element *_unreadBarView = nullptr; 589 Element *_firstUnreadView = nullptr; 590 HistoryService *_joinedMessage = nullptr; 591 bool _loadedAtTop = false; 592 bool _loadedAtBottom = true; 593 594 std::optional<Data::Folder*> _folder; 595 596 std::optional<MsgId> _inboxReadBefore; 597 std::optional<MsgId> _outboxReadBefore; 598 std::optional<int> _unreadCount; 599 std::optional<int> _unreadMentionsCount; 600 base::flat_set<MsgId> _unreadMentions; 601 std::optional<HistoryItem*> _lastMessage; 602 std::optional<HistoryItem*> _lastServerMessage; 603 base::flat_set<not_null<HistoryItem*>> _clientSideMessages; 604 std::unordered_set<std::unique_ptr<HistoryItem>> _messages; 605 606 // This almost always is equal to _lastMessage. The only difference is 607 // for a group that migrated to a supergroup. Then _lastMessage can 608 // be a migrate message, but _chatListMessage should be the one before. 609 std::optional<HistoryItem*> _chatListMessage; 610 611 QString _chatListNameSortKey; 612 613 bool _unreadMark = false; 614 bool _fakeUnreadWhileOpened = false; 615 bool _hasPinnedMessages = false; 616 617 // A pointer to the block that is currently being built. 618 // We hold this pointer so we can destroy it while building 619 // and then create a new one if it is necessary. 620 struct BuildingBlock { 621 int expectedItemsCount = 0; // optimization for block->items.reserve() call 622 HistoryBlock *block = nullptr; 623 }; 624 std::unique_ptr<BuildingBlock> _buildingFrontBlock; 625 626 Data::HistoryDrafts _drafts; 627 TimeId _acceptCloudDraftsAfter = 0; 628 int _savingCloudDraftRequests = 0; 629 Data::ForwardDraft _forwardDraft; 630 631 QString _topPromotedMessage; 632 QString _topPromotedType; 633 634 HistoryView::SendActionPainter _sendActionPainter; 635 636 std::deque<not_null<HistoryItem*>> _notifications; 637 638 }; 639 640 class HistoryBlock { 641 public: 642 using Element = HistoryView::Element; 643 644 HistoryBlock(not_null<History*> history); 645 HistoryBlock(const HistoryBlock &) = delete; 646 HistoryBlock &operator=(const HistoryBlock &) = delete; 647 ~HistoryBlock(); 648 649 std::vector<std::unique_ptr<Element>> messages; 650 651 void remove(not_null<Element*> view); 652 void refreshView(not_null<Element*> view); 653 654 int resizeGetHeight(int newWidth, bool resizeAllItems); y()655 int y() const { 656 return _y; 657 } setY(int y)658 void setY(int y) { 659 _y = y; 660 } height()661 int height() const { 662 return _height; 663 } history()664 not_null<History*> history() const { 665 return _history; 666 } 667 previousBlock()668 HistoryBlock *previousBlock() const { 669 Expects(_indexInHistory >= 0); 670 671 return (_indexInHistory > 0) 672 ? _history->blocks[_indexInHistory - 1].get() 673 : nullptr; 674 } nextBlock()675 HistoryBlock *nextBlock() const { 676 Expects(_indexInHistory >= 0); 677 678 return (_indexInHistory + 1 < _history->blocks.size()) 679 ? _history->blocks[_indexInHistory + 1].get() 680 : nullptr; 681 } setIndexInHistory(int index)682 void setIndexInHistory(int index) { 683 _indexInHistory = index; 684 } indexInHistory()685 int indexInHistory() const { 686 Expects(_indexInHistory >= 0); 687 Expects(_indexInHistory < _history->blocks.size()); 688 Expects(_history->blocks[_indexInHistory].get() == this); 689 690 return _indexInHistory; 691 } 692 693 protected: 694 const not_null<History*> _history; 695 696 int _y = 0; 697 int _height = 0; 698 int _indexInHistory = -1; 699 700 }; 701