1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2pub struct ListState {
3 pub selected: usize,
4 pub offset: usize,
5 pub visible_rows: usize,
6}
7
8impl ListState {
9 pub const fn new(selected: usize, offset: usize, visible_rows: usize) -> Self {
10 Self {
11 selected,
12 offset,
13 visible_rows,
14 }
15 }
16
17 pub fn set_selected(&mut self, selected: usize, len: usize) -> bool {
18 let next = selected.min(len.saturating_sub(1));
19 let changed = next != self.selected;
20 self.selected = next;
21 self.keep_selected_visible();
22 changed
23 }
24
25 pub fn next(&mut self, len: usize) -> bool {
26 self.bump(len, 1)
27 }
28
29 pub fn previous(&mut self, len: usize) -> bool {
30 self.bump(len, -1)
31 }
32
33 pub fn bump(&mut self, len: usize, delta: i8) -> bool {
34 if len == 0 {
35 return false;
36 }
37 let next = if delta >= 0 {
38 (self.selected + 1) % len
39 } else if self.selected == 0 {
40 len - 1
41 } else {
42 self.selected - 1
43 };
44 self.set_selected(next, len)
45 }
46
47 pub fn keep_selected_visible(&mut self) {
48 let rows = self.visible_rows.max(1);
49 if self.selected < self.offset {
50 self.offset = self.selected;
51 } else if self.selected >= self.offset.saturating_add(rows) {
52 self.offset = self.selected.saturating_add(1).saturating_sub(rows);
53 }
54 }
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub struct TabsState {
59 pub selected: usize,
60}
61
62impl TabsState {
63 pub const fn new(selected: usize) -> Self {
64 Self { selected }
65 }
66
67 pub fn set_selected(&mut self, selected: usize, len: usize) -> bool {
68 let next = selected.min(len.saturating_sub(1));
69 let changed = next != self.selected;
70 self.selected = next;
71 changed
72 }
73
74 pub fn next(&mut self, len: usize) -> bool {
75 self.bump(len, 1)
76 }
77
78 pub fn previous(&mut self, len: usize) -> bool {
79 self.bump(len, -1)
80 }
81
82 pub fn bump(&mut self, len: usize, delta: i8) -> bool {
83 if len == 0 {
84 return false;
85 }
86 let next = if delta >= 0 {
87 (self.selected + 1) % len
88 } else if self.selected == 0 {
89 len - 1
90 } else {
91 self.selected - 1
92 };
93 self.set_selected(next, len)
94 }
95}
96
97#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
98pub struct ScrollState {
99 pub offset_y: i32,
100 pub content_h: u32,
101}
102
103impl ScrollState {
104 pub const fn new(offset_y: i32, content_h: u32) -> Self {
105 Self {
106 offset_y,
107 content_h,
108 }
109 }
110
111 pub fn set_offset(&mut self, offset_y: i32) -> bool {
112 let next = offset_y.clamp(0, self.content_h as i32);
113 let changed = next != self.offset_y;
114 self.offset_y = next;
115 changed
116 }
117
118 pub fn scroll_by(&mut self, delta_y: i32) -> bool {
119 self.set_offset(self.offset_y.saturating_add(delta_y))
120 }
121}
122
123#[derive(Clone, Copy, Debug, PartialEq)]
124pub struct SliderState {
125 pub value: f32,
126 pub min: f32,
127 pub max: f32,
128}
129
130#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
131pub struct FeedTimelineState {
132 pub selected: usize,
133 pub offset: usize,
134 pub visible_rows: usize,
135 pub expanded: bool,
136}
137
138impl FeedTimelineState {
139 pub const fn new(selected: usize, offset: usize, visible_rows: usize, expanded: bool) -> Self {
140 Self {
141 selected,
142 offset,
143 visible_rows,
144 expanded,
145 }
146 }
147
148 pub fn set_selected(&mut self, selected: usize, len: usize) -> bool {
149 let next = selected.min(len.saturating_sub(1));
150 let changed = next != self.selected;
151 self.selected = next;
152 self.keep_selected_visible();
153 changed
154 }
155
156 pub fn bump(&mut self, len: usize, delta: i8) -> bool {
157 if len == 0 {
158 return false;
159 }
160 let next = if delta >= 0 {
161 (self.selected + 1) % len
162 } else if self.selected == 0 {
163 len - 1
164 } else {
165 self.selected - 1
166 };
167 self.set_selected(next, len)
168 }
169
170 pub fn set_expanded(&mut self, expanded: bool) -> bool {
171 let changed = self.expanded != expanded;
172 self.expanded = expanded;
173 changed
174 }
175
176 pub fn keep_selected_visible(&mut self) {
177 let rows = self.visible_rows.max(1);
178 if self.selected < self.offset {
179 self.offset = self.selected;
180 } else if self.selected >= self.offset.saturating_add(rows) {
181 self.offset = self.selected.saturating_add(1).saturating_sub(rows);
182 }
183 }
184}
185
186impl SliderState {
187 pub const fn new(value: f32, min: f32, max: f32) -> Self {
188 Self { value, min, max }
189 }
190
191 pub fn set_value(&mut self, value: f32) -> bool {
192 let next = value.clamp(self.min.min(self.max), self.min.max(self.max));
193 let changed = (next - self.value).abs() > f32::EPSILON;
194 self.value = next;
195 changed
196 }
197
198 pub fn step_by(&mut self, direction: f32) -> bool {
199 let step = ((self.max - self.min).abs() / 20.0).max(0.01);
200 self.set_value(self.value + step * direction)
201 }
202}