QmlMaterial 0.1.0
Loading...
Searching...
No Matches
viewport_controller.hpp
1#pragma once
2
3#include "linear_layout.hpp"
4#include "qml_material/model/item_source.hpp"
5#include <optional>
6
7namespace qml_material
8{
9
10class ViewportController {
11public:
12 struct Result {
13 bool accepted;
14 double offset;
15 };
16
17 const LinearLayout& layout() const { return m_layout; }
18 ItemSnapshotPtr snapshot() const { return m_snapshot; }
19 bool estimated() const { return m_cache.size() < m_layout.count(); }
20
21 void clear() {
22 m_snapshot.reset();
23 m_cache.clear();
24 m_layout.reset(0, 0, 0);
25 }
26
27 Result apply(ItemSnapshotPtr next, double width, double estimate, double spacing,
28 double offset) {
29 if (! next || ! std::isfinite(width) || width < 0 || ! std::isfinite(offset))
30 return { false, offset };
31 LinearLayout layout;
32 if (! layout.reset(int(next->rows.size()), estimate, spacing)) return { false, offset };
33
34 QString anchor;
35 double screenOffset = 0;
36 if (m_snapshot && m_snapshot->stableKeys && next->stableKeys) {
37 const int oldIndex = m_layout.indexAt(offset);
38 auto select = [&](int index) {
39 const auto& key = m_snapshot->rows[index].key;
40 if (next->indexOfKey(key) < 0) return false;
41 anchor = key;
42 screenOffset = m_layout.offset(index) - offset;
43 return true;
44 };
45 if (oldIndex >= 0 && ! select(oldIndex)) {
46 for (int i = oldIndex + 1; i < m_layout.count() && anchor.isEmpty(); ++i) select(i);
47 for (int i = oldIndex - 1; i >= 0 && anchor.isEmpty(); --i) select(i);
48 }
49 }
50
51 QHash<QString, Measurement> cache;
52 const bool reuse = m_snapshot && width == m_width &&
53 ((m_snapshot->stableKeys && next->stableKeys) || next == m_snapshot);
54 if (reuse) {
55 for (int i = 0; i < next->rows.size(); ++i) {
56 const auto& row = next->rows[i];
57 const auto found = m_cache.constFind(row.key);
58 if (found == m_cache.cend() || found->value != row.value ||
59 found->type != row.reuseType || found->revision != row.contentRevision)
60 continue;
61 if (! layout.setExtent(i, found->height)) return { false, offset };
62 cache.insert(row.key, *found);
63 }
64 }
65 double corrected = offset;
66 if (next->rows.isEmpty())
67 corrected = 0;
68 else if (! anchor.isEmpty())
69 corrected = layout.offset(next->indexOfKey(anchor)) - screenOffset;
70 m_snapshot = std::move(next);
71 m_width = width;
72 m_estimate = estimate;
73 m_layout = std::move(layout);
74 m_cache = std::move(cache);
75 return { true, corrected };
76 }
77
78 Result measure(int index, double height, double offset) {
79 if (! m_snapshot || index < 0 || index >= m_layout.count() || ! std::isfinite(offset))
80 return { false, offset };
81 const int anchor = m_layout.indexAt(offset);
82 const double oldStart = m_layout.offset(anchor);
83 if (! m_layout.setExtent(index, height)) return { false, offset };
84 const auto& row = m_snapshot->rows[index];
85 m_cache.insert(row.key, { height, row.value, row.reuseType, row.contentRevision });
86 return { true, offset + m_layout.offset(anchor) - oldStart };
87 }
88
89 Result invalidate(const QString& key, double offset) {
90 if (! m_snapshot) return { false, offset };
91 const int index = m_snapshot->indexOfKey(key);
92 if (index < 0 || ! std::isfinite(offset)) return { false, offset };
93 const int anchor = m_layout.indexAt(offset);
94 const double oldStart = m_layout.offset(anchor);
95 if (! m_layout.setExtent(index, m_estimate)) return { false, offset };
96 m_cache.remove(key);
97 return { true, offset + m_layout.offset(anchor) - oldStart };
98 }
99
100 std::optional<double> positionAt(const QString& key, double viewport, double alignment,
101 double extraOffset = 0) const {
102 if (! m_snapshot || ! std::isfinite(viewport) || viewport < 0 ||
103 ! std::isfinite(alignment) || alignment < 0 || alignment > 1 ||
104 ! std::isfinite(extraOffset))
105 return {};
106 const int index = m_snapshot->indexOfKey(key);
107 if (index < 0) return {};
108 return m_layout.offset(index) - (viewport - m_layout.extent(index)) * alignment +
109 extraOffset;
110 }
111
112private:
113 struct Measurement {
114 double height;
115 QVariant value;
116 QString type;
117 quint64 revision;
118 };
119 ItemSnapshotPtr m_snapshot;
120 LinearLayout m_layout;
121 QHash<QString, Measurement> m_cache;
122 double m_width { 0 };
123 double m_estimate { 0 };
124};
125
126} // namespace qml_material