3#include "linear_layout.hpp"
4#include "qml_material/model/item_source.hpp"
10class ViewportController {
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(); }
24 m_layout.reset(0, 0, 0);
27 Result apply(ItemSnapshotPtr next,
double width,
double estimate,
double spacing,
29 if (! next || ! std::isfinite(width) || width < 0 || ! std::isfinite(offset))
30 return {
false, offset };
32 if (! layout.reset(
int(next->rows.size()), estimate, spacing))
return {
false, offset };
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;
42 screenOffset = m_layout.offset(index) - offset;
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);
51 QHash<QString, Measurement> cache;
52 const bool reuse = m_snapshot && width == m_width &&
53 ((m_snapshot->stableKeys && next->stableKeys) || next == m_snapshot);
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)
61 if (! layout.setExtent(i, found->height))
return {
false, offset };
62 cache.insert(row.key, *found);
65 double corrected = offset;
66 if (next->rows.isEmpty())
68 else if (! anchor.isEmpty())
69 corrected = layout.offset(next->indexOfKey(anchor)) - screenOffset;
70 m_snapshot = std::move(next);
72 m_estimate = estimate;
73 m_layout = std::move(layout);
74 m_cache = std::move(cache);
75 return {
true, corrected };
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 };
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 };
97 return {
true, offset + m_layout.offset(anchor) - oldStart };
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))
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 +
119 ItemSnapshotPtr m_snapshot;
120 LinearLayout m_layout;
121 QHash<QString, Measurement> m_cache;
122 double m_width { 0 };
123 double m_estimate { 0 };