~aleteoryx/muditaos

muditaos/module-gui/gui/widgets/ListViewEngine.hpp -rw-r--r-- 8.6 KiB
a405cad6Aleteoryx trim readme 6 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

#pragma once

#include <ListItemProvider.hpp>
#include <BoxLayout.hpp>

namespace gui
{
    namespace listview
    {
        inline constexpr auto nPos = std::numeric_limits<unsigned>::max();

        /// Possible List scrolling directions
        enum class Direction
        {
            Top,
            Bottom
        };

        /// Possible List rebuild types
        enum class RebuildType
        {
            Full,          ///< Full rebuild - resets lists to all initial conditions and request data from beginning.
            InPlace,       ///< InPlace rebuild - stores currently focused part of list and rebuild from that part.
            OnPageElement, ///< OnPageElement rebuild - focus on provided element index and calculated page.
            OnOffset       ///< OnOffset rebuild - resets lists to all initial conditions and request data from provided
                           ///< offset.
        };

        /// Possible List ScrollBar types
        enum class ScrollBarType
        {
            None,         ///< None - list without scroll bar (but with scrolling).
            Proportional, ///< Proportional - scroll bar size calculated based on elements count in model and currently
                          ///< displayed number of elements. Use with large unequal heights lists elements.
            Fixed,        ///< Fixed - scroll bar size calculated based on fixed equal elements sizes in list.
                          ///< Use when all elements have equal heights.
            PreRendered   ///< PreRendered - scroll bar size calculated based on pre rendered pages on whole list. Use
                          ///< when elements are not equal heights but there are few of them as its renders whole
                          ///< context and can be time consuming.
        };

        /// Possible page fill types
        enum class FetchType
        {
            Filled, ///< Filled - list always fetch object from provider as long it has space.
            Fixed   ///< Fixed - lists fetches objects from provider accordinly to fixed pages sizes.
        };

        enum class Orientation
        {
            TopBottom,
            BottomTop
        };

    } // namespace listview

    struct ListViewScrollSetupData
    {
        const unsigned storedStartIndex;
        const unsigned currentPage;
        const unsigned pagesCount;
    };

    struct ListViewScrollUpdateData
    {
        const unsigned startIndex;
        const unsigned listPageSize;
        const unsigned elementsCount;
        const unsigned elementMinimalSpaceRequired;
        const listview::Direction direction;
        const Boundaries boundaries;
    };

    class ListItemProvider;

    using rebuildRequest = std::pair<listview::RebuildType, unsigned>;

    class ListViewEngine
    {
      protected:
        explicit ListViewEngine(std::shared_ptr<ListItemProvider> prov);
        virtual ~ListViewEngine();

        /// First requested index from provider
        unsigned startIndex = 0;
        void setStartIndex();
        /// Recalculating startIndex if list internal conditions (i.e. size has changed).
        void recalculateStartIndex();
        /// Calculate max items on page based on provided minimal item in axis size from provider.
        /// That method is used when full list render can be omitted.
        unsigned calculateMaxItemsOnPage();
        /// Calculates request elements count to full next request page with small excess
        unsigned calculateLimit(listview::Direction value = listview::Direction::Bottom);
        /// Recalculate startIndex on body resize requests
        void recalculateOnBoxRequestedResize();

        /// Request next page based on calculated offset and limit
        virtual bool requestNextPage();
        /// Request previous based on calculated offset and limit
        virtual bool requestPreviousPage();
        /// Request data to fillFirst list page - used in some conditions (i.e items are various item in axis sizes)
        void fillFirstPage();

        /// Stored index of focused element before new data request
        unsigned storedFocusIndex = listview::nPos;
        [[nodiscard]] unsigned getFocusItemIndex();

        /// Total provider elements count
        unsigned elementsCount = listview::nPos;
        void setElementsCount(unsigned count);
        void onElementsCountChanged(unsigned count);
        bool shouldCallEmptyListCallbacks = false;
        void checkEmptyListCallbacks();

        /// Data model provider
        std::shared_ptr<ListItemProvider> provider = nullptr;

        /// Count of elements displayed on current page
        unsigned currentPageSize = 0;
        /// Flag indicating that page has been loaded
        bool pageLoaded = true;
        /// Handle focusing method
        virtual void setFocus();
        /// Flag indicating that focus after rebuild has to be on last displayed element
        bool focusOnLastItem = false;

        /// Layout element to display received data
        BoxLayout *body = nullptr;

        /// Scroll handling callbacks
        std::function<void(const ListViewScrollUpdateData &data)> updateScrollCallback;
        std::function<void(const ListViewScrollSetupData &data)> setupScrollCallback;
        std::function<void()> resizeScrollCallback;

        /// Main loop method
        void refresh();
        /// Adding elements from provider to page
        virtual void addItemsOnPage();

        /// Pending rebuild requests
        std::list<rebuildRequest> rebuildRequests;
        /// Stored last executed rebuildRequest
        rebuildRequest lastRebuildRequest = {listview::RebuildType::Full, 0};
        /// Setup method for list rebuild request
        void setup(listview::RebuildType rebuildType, unsigned dataOffset = 0);
        void prepareFullRebuild();
        void prepareOnOffsetRebuild(unsigned dataOffset);
        void prepareInPlaceRebuild();
        void prepareOnPageElementRebuild(unsigned dataOffset);

        /// List boundaries types
        Boundaries boundaries = Boundaries::Fixed;
        /// List current movement direction
        listview::Direction direction = listview::Direction::Bottom;
        /// List orientation (from which end element will start to load)
        listview::Orientation orientation = listview::Orientation::TopBottom;
        /// List fill type
        listview::FetchType fetchType = listview::FetchType::Filled;

        /// Flag to indicate full data request completed
        bool requestCompleteData = false;
        /// Flag to indicate full list render (with all provider data) completed
        bool requestFullListRender = false;
        /// Full list render method
        bool renderFullList();
        /// Checking requirements for full list render
        std::function<void()> checkFullRenderRequirementCallback;

        /// Methods to translate direction to request order
        [[nodiscard]] Order getOrderFromDirection() const noexcept;
        [[nodiscard]] Order getOppositeOrderFromDirection() const noexcept;

      public:
        /// set data model provider method
        void setProvider(std::shared_ptr<ListItemProvider> provider);

        /// send list rebuild request
        void rebuildList(listview::RebuildType rebuildType = listview::RebuildType::Full,
                         unsigned dataOffset               = 0,
                         bool forceRebuild                 = false);
        /// In case of elements count change there can be a need to resend request in case of having one async query for
        /// count and records.
        void reSendLastRebuildRequest();
        /// Callback to be called on rebuild preparation - in example to on demand clear provider data.
        std::function<void()> prepareRebuildCallback;

        virtual void reset();
        virtual void clear();
        void onClose();

        std::shared_ptr<ListItemProvider> getProvider();
        void setOrientation(listview::Orientation value);
        void setBoundaries(Boundaries value);
        void onProviderDataUpdate();

        /// Empty list handling callbacks
        [[nodiscard]] bool isEmpty() const noexcept;
        std::function<void()> emptyListCallback;
        std::function<void()> notEmptyListCallback;

        /// Update the number of items above the current page
        void updateCountOfElementsAboveCurrentPage();
        /// Callback on update the number of items above the current page
        std::function<void(const unsigned elementsAboveOfCurrentPageCount)> onElementsAboveOfCurrentPageChangeCallback;
    };

} /* namespace gui */