~aleteoryx/muditaos

ref: d07a0c1e13bb00dfc0510a79a1707aa78d7d40a4 muditaos/module-gui/gui/widgets/ListView.hpp -rw-r--r-- 6.1 KiB
d07a0c1e — Marcin Smoczyński Merge branch 'stable' 5 years 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
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <list>
#include "Rect.hpp"
#include "BoxLayout.hpp"
#include "Span.hpp"
#include "ListItemProvider.hpp"
#include "Style.hpp"

namespace gui
{
    class ListItemProvider;

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

    struct ListViewScrollUpdateData
    {
        const unsigned int startIndex;
        const unsigned int listPageSize;
        const unsigned int elementsCount;
        const unsigned int elementMinimalHeight;
        const style::listview::Direction direction;
        const style::listview::Boundaries boundaries;
        const int topMargin;
    };

    class ListViewScroll : public Rect
    {
      private:
        unsigned int storedStartIndex = 0;
        unsigned int currentPage      = style::listview::nPos;
        unsigned int pagesCount       = 0;

        void updateProportional(const ListViewScrollUpdateData &data);
        void updateFixed(const ListViewScrollUpdateData &data);
        void updatePreRendered(const ListViewScrollUpdateData &data);

      public:
        style::listview::ScrollBarType type = style::listview::ScrollBarType::None;

        ListViewScroll(Item *parent,
                       unsigned int x,
                       unsigned int y,
                       unsigned int w,
                       unsigned int h,
                       style::listview::ScrollBarType type);

        bool shouldShowScroll(unsigned int listPageSize, unsigned int elementsCount);
        void updateStartConditions(const unsigned int storedStartIndex,
                                   const unsigned int currentPage,
                                   const unsigned int pagesCount);
        void update(const ListViewScrollUpdateData &data);
    };

    class ListView : public Rect
    {
      protected:
        unsigned int startIndex                    = 0;
        unsigned int storedFocusIndex              = style::listview::nPos;
        unsigned int elementsCount                 = 0;
        std::shared_ptr<ListItemProvider> provider = nullptr;
        VBox *body                                 = nullptr;
        ListViewScroll *scroll                     = nullptr;
        std::list<rebuildRequest> rebuildRequests;
        rebuildRequest lastRebuildRequest = {style::listview::RebuildType::Full, 0};

        unsigned int currentPageSize = 0;
        bool pageLoaded              = true;
        bool focusOnLastItem         = false;
        int scrollTopMargin          = style::margins::big;

        style::listview::Boundaries boundaries   = style::listview::Boundaries::Fixed;
        style::listview::Direction direction     = style::listview::Direction::Bottom;
        style::listview::Orientation orientation = style::listview::Orientation::TopBottom;

        void clearItems();
        virtual void addItemsOnPage();

        bool requestCompleteData   = false;
        bool requestFullListRender = false;
        bool renderFullList();
        void checkFullRenderRequirement();

        void prepareFullRebuild();
        void prepareOnOffsetRebuild(unsigned int dataOffset);
        void prepareInPlaceRebuild();
        void prepareOnPageElementRebuild(unsigned int dataOffset);

        void setFocus();
        void refresh();
        void resizeWithScroll();
        void recalculateStartIndex();
        void fillFirstPage();
        void setStartIndex();
        void recalculateOnBoxRequestedResize();
        void setFocusOnElement(unsigned int elementNumber);
        [[nodiscard]] unsigned int getFocusItemIndex();
        /// Default empty list to inform that there is no elements - callback should be override in applications
        void onElementsCountChanged();
        unsigned int calculateMaxItemsOnPage();
        unsigned int calculateLimit(style::listview::Direction value = style::listview::Direction::Bottom);
        [[nodiscard]] Order getOrderFromDirection() const noexcept;
        [[nodiscard]] Order getOppositeOrderFromDirection() const noexcept;
        virtual bool requestNextPage();
        virtual bool requestPreviousPage();
        void setup(style::listview::RebuildType rebuildType, unsigned int dataOffset = 0);

      public:
        ListView();
        ListView(Item *parent,
                 unsigned int x,
                 unsigned int y,
                 unsigned int w,
                 unsigned int h,
                 std::shared_ptr<ListItemProvider> prov,
                 style::listview::ScrollBarType scrollType = style::listview::ScrollBarType::Proportional);
        ~ListView();

        void setElementsCount(unsigned int count);
        void setProvider(std::shared_ptr<ListItemProvider> provider);

        void rebuildList(style::listview::RebuildType rebuildType = style::listview::RebuildType::Full,
                         unsigned int 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;

        void clear();
        std::shared_ptr<ListItemProvider> getProvider();
        void setOrientation(style::listview::Orientation value);
        void setBoundaries(style::listview::Boundaries value);
        void setScrollTopMargin(int value);
        void setAlignment(const Alignment &value) override;
        void onProviderDataUpdate();

        [[nodiscard]] bool isEmpty() const noexcept;
        std::function<void()> emptyListCallback;
        std::function<void()> notEmptyListCallback;

        // virtual methods from Item
        bool onInput(const InputEvent &inputEvent) override;
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
        auto handleRequestResize(const Item *, unsigned short request_w, unsigned short request_h) -> Size override;
    };

} /* namespace gui */