Line data Source code
1 : #ifndef NEWSLIST_H
2 : #define NEWSLIST_H
3 :
4 : #include <QList>
5 : #include <functional>
6 :
7 : #include "../FangObject.h"
8 : #include "NewsPosition.h"
9 :
10 : class NewsItem;
11 :
12 : /*!
13 : \brief Represents a slot in the news list that may or may not have a loaded NewsItem.
14 :
15 : The position (timestamp + ID) is always present for ordering purposes.
16 : The item pointer may be nullptr if the item has been unloaded to save memory.
17 : */
18 : struct NewsSlot {
19 : NewsPosition position; // Always present (timestamp + ID)
20 : NewsItem* item; // nullptr when unloaded
21 :
22 : NewsSlot(NewsItem* newsItem);
23 :
24 647 : inline bool isLoaded() const { return item != nullptr; }
25 89057 : inline qint64 id() const { return position.id(); }
26 : };
27 :
28 : /*!
29 : \brief Callback type for reloading unloaded news items from the database.
30 : Takes a list of news IDs and returns the corresponding NewsItem objects.
31 : */
32 : using ReloadCallback = std::function<QList<NewsItem*>(const QList<qint64>&)>;
33 :
34 : /*!
35 : \brief The NewsList class contains a custom list type that internally is also stored as a set.
36 :
37 : The list maintains ALL loaded items in memory, with a "display window" that tracks
38 : which items are currently visible to the UI. This allows paging through previously
39 : loaded items without re-fetching from the database.
40 :
41 : Public methods (size, first, last, at, indexOf) operate on the display window.
42 : Internal methods (fullSize, fullAt, etc.) operate on the complete list.
43 : */
44 : class NewsList : public FangObject
45 : {
46 : public:
47 : explicit NewsList(FangObject *parent = nullptr);
48 : virtual ~NewsList();
49 :
50 : /*!
51 : \brief Delete everything in the list and clear it.
52 : */
53 : void clear();
54 :
55 : //////////////////////////////////////////
56 : // Display Window (reflects the web views)
57 : //////////////////////////////////////////
58 :
59 : qsizetype size() const;
60 : bool isEmpty() const;
61 : NewsItem* first() const;
62 : NewsItem* last() const;
63 : NewsItem* at(qsizetype i) const;
64 : bool contains(NewsItem* value) const;
65 : qsizetype indexOf(const NewsItem* value, qsizetype from = 0) const;
66 :
67 : ///////////////////////////////////////
68 : // Full List Methods (all loaded items)
69 : ///////////////////////////////////////
70 :
71 : /*!
72 : \brief Returns the total number of items loaded (including those outside display window).
73 : */
74 : qsizetype fullSize() const;
75 :
76 : /*!
77 : \brief Returns item at index in the full list.
78 : */
79 : NewsItem* fullAt(qsizetype i) const;
80 :
81 : /*!
82 : \brief Checks if an item with the given ID exists anywhere in the full list.
83 : */
84 : bool containsID(qint64 id) const;
85 :
86 : /*!
87 : \brief Returns the index in the full list for a given ID, or -1 if not found.
88 : */
89 : qsizetype fullIndexForItemID(qint64 id) const;
90 :
91 : ////////////////////////////
92 : // Display Window Navigation
93 : ////////////////////////////
94 :
95 : /*!
96 : \brief Returns true if there are loaded items before the display window.
97 : */
98 : bool canPageUp() const;
99 :
100 : /*!
101 : \brief Returns true if there are loaded items after the display window.
102 : */
103 : bool canPageDown() const;
104 :
105 : /*!
106 : \brief Expands the display window to include N more items at the start.
107 : \return The actual number of items added to the window.
108 : */
109 : qsizetype pageUp(qsizetype count);
110 :
111 : /*!
112 : \brief Expands the display window to include N more items at the end.
113 : \return The actual number of items added to the window.
114 : */
115 : qsizetype pageDown(qsizetype count);
116 :
117 : /*!
118 : \brief Gets the display window start index in the full list.
119 : */
120 12 : inline qsizetype getDisplayStart() const { return displayStart; }
121 :
122 : /*!
123 : \brief Gets the display window end index (exclusive) in the full list.
124 : */
125 23 : inline qsizetype getDisplayEnd() const { return displayEnd; }
126 :
127 : ///////////////////////
128 : // Modification Methods
129 : ///////////////////////
130 :
131 : void append(NewsItem* value);
132 : void prepend(NewsItem* value);
133 :
134 : /*!
135 : \brief Shrinks the display window from the start or end.
136 :
137 : Items are NOT deleted, they remain in the full list for potential paging.
138 : \param fromStart True to shrink from start, false to shrink from end.
139 : \param numberToRemove Number of items to remove from the display window.
140 : */
141 : void shrinkDisplayWindow(bool fromStart, qsizetype numberToRemove);
142 :
143 : /*!
144 : \brief Shrinks the display window. Items remain in memory for paging.
145 : */
146 : void removeAndDelete(bool fromStart, qsizetype numberToRemove);
147 :
148 : /*!
149 : \brief If loaded, returns the news item for a given ID (searches display window only).
150 : Linear time complexity.
151 : */
152 : NewsItem* newsItemForID(const qint64 id) const;
153 :
154 : /*!
155 : \brief Returns the news item for a given ID (searches FULL list, not just display window).
156 : Linear time complexity.
157 : */
158 : NewsItem* fullNewsItemForID(const qint64 id) const;
159 :
160 : /*!
161 : \brief Returns the index within the display window for a given ID, or -1 if not found.
162 : Linear time complexity.
163 : */
164 : qsizetype indexForItemID(const qint64 id) const;
165 :
166 : ////////////////////////////
167 : // Reload Callback
168 : ////////////////////////////
169 :
170 : /*!
171 : \brief Sets the callback function for reloading unloaded items from the database.
172 : \param callback Function that takes a list of IDs and returns NewsItem objects.
173 : */
174 : void setReloadCallback(ReloadCallback callback);
175 :
176 : ////////////////////////////
177 : // Slot Access
178 : ////////////////////////////
179 :
180 : /*!
181 : \brief Returns the NewsPosition for an item at the given full index.
182 : Works for both loaded and unloaded items since positions are always present.
183 : \param i Index in the full list (not display window)
184 : \return The position, or invalid NewsPosition if index is out of bounds
185 : */
186 : NewsPosition positionAt(qsizetype i) const;
187 :
188 : /*!
189 : \brief Returns the count of currently loaded items.
190 : */
191 : qsizetype loadedCount() const;
192 :
193 : // Iterator class for iterating over NewsItem* in the display window
194 : class Iterator {
195 : public:
196 : using iterator_category = std::forward_iterator_tag;
197 : using value_type = NewsItem*;
198 : using difference_type = qsizetype;
199 : using pointer = NewsItem**;
200 : using reference = NewsItem*&;
201 :
202 8 : Iterator(QList<NewsSlot>* slotList, qsizetype index) : slotList(slotList), index(index) {}
203 :
204 12 : NewsItem* operator*() const { return slotList->at(index).item; }
205 12 : Iterator& operator++() { ++index; return *this; }
206 : Iterator operator++(int) { Iterator tmp = *this; ++index; return tmp; }
207 : bool operator==(const Iterator& other) const { return index == other.index; }
208 16 : bool operator!=(const Iterator& other) const { return index != other.index; }
209 :
210 : private:
211 : QList<NewsSlot>* slotList;
212 : qsizetype index;
213 : };
214 :
215 : // For list iteration (iterates display window only).
216 4 : inline Iterator begin() { return Iterator(&slotList, displayStart); }
217 4 : inline Iterator end() { return Iterator(&slotList, displayEnd); }
218 :
219 : private:
220 : // Ordered list of ALL news slots (items may be loaded or unloaded).
221 : QList<NewsSlot> slotList;
222 :
223 : // Set of LOADED news items for fast "contains" check.
224 : QSet<NewsItem*> loadedSet;
225 :
226 : // Callback for reloading items from database.
227 : ReloadCallback reloadCallback;
228 :
229 : // Display window: [displayStart, displayEnd) - indices into `slotList`
230 : qsizetype displayStart = 0;
231 : qsizetype displayEnd = 0;
232 :
233 : ////////////////////////////
234 : // Memory Management
235 : ////////////////////////////
236 :
237 : /*!
238 : \brief Checks if loaded item count exceeds threshold and unloads if needed.
239 : Called after append/prepend operations.
240 : */
241 : void checkMemoryBounds();
242 :
243 : /*!
244 : \brief Unloads items furthest from the display window.
245 : \param count Number of items to unload.
246 : */
247 : void unloadDistantItems(qsizetype count);
248 :
249 : /*!
250 : \brief Populates slotList with reloaded NewsItem objects.
251 : \param items The reloaded items (matched by ID to existing slotList).
252 : */
253 : void populateSlots(const QList<NewsItem*>& items);
254 :
255 : /*!
256 : \brief Verifies all items in the display window are loaded.
257 : Logs errors for any unloaded items found.
258 : \return true if all items are loaded, false otherwise.
259 : */
260 : bool verifyDisplayWindowIntegrity() const;
261 : };
262 :
263 : #endif // NEWSLIST_H
|