on master help sign in
ui: menu: Adjust scroll via required_size area caching.
ddcf515Blaž Hrastnik
helix-term/src/ui/menu.rs+28−17
@@ -8,7 +8,6 @@ use tui::{
8};
8};
9
9
10use std::borrow::Cow;
10use std::borrow::Cow;
11−use std::cell::Cell;
12
11
13use helix_core::Position;
12use helix_core::Position;
14use helix_view::Editor;
13use helix_view::Editor;
@@ -24,7 +23,8 @@ pub struct Menu<T> {
24 format_fn: Box<dyn Fn(&T) -> Cow<str>>,
23 format_fn: Box<dyn Fn(&T) -> Cow<str>>,
25 callback_fn: Box<dyn Fn(&mut Editor, Option<&T>, MenuEvent)>,
24 callback_fn: Box<dyn Fn(&mut Editor, Option<&T>, MenuEvent)>,
26
25
27− scroll: Cell<usize>,
26+ scroll: usize,
27+ size: (u16, u16),
28}
28}
29
29
30impl<T> Menu<T> {
30impl<T> Menu<T> {
@@ -40,7 +40,8 @@ impl<T> Menu<T> {
40 cursor: None,
40 cursor: None,
41 format_fn: Box::new(format_fn),
41 format_fn: Box::new(format_fn),
42 callback_fn: Box::new(callback_fn),
42 callback_fn: Box::new(callback_fn),
43− scroll: Cell::new(0),
43+ scroll: 0,
44+ size: (0, 0),
44 }
45 }
45 }
46 }
46
47
@@ -48,11 +49,28 @@ impl<T> Menu<T> {
48 // TODO: wrap around to end
49 // TODO: wrap around to end
49 let pos = self.cursor.map(|i| i.saturating_sub(1)).unwrap_or(0) % self.options.len();
50 let pos = self.cursor.map(|i| i.saturating_sub(1)).unwrap_or(0) % self.options.len();
50 self.cursor = Some(pos);
51 self.cursor = Some(pos);
52+ self.adjust_scroll();
51 }
53 }
52
54
53 pub fn move_down(&mut self) {
55 pub fn move_down(&mut self) {
54 let pos = self.cursor.map(|i| i + 1).unwrap_or(0) % self.options.len();
56 let pos = self.cursor.map(|i| i + 1).unwrap_or(0) % self.options.len();
55 self.cursor = Some(pos);
57 self.cursor = Some(pos);
58+ self.adjust_scroll();
59+ }
60+
61+ fn adjust_scroll(&mut self) {
62+ let win_height = self.size.1 as usize;
63+ if let Some(cursor) = self.cursor {
64+ let mut scroll = self.scroll;
65+ if cursor > (win_height + scroll).saturating_sub(1) {
66+ // scroll down
67+ scroll += cursor - (win_height + scroll).saturating_sub(1)
68+ } else if cursor < scroll {
69+ // scroll up
70+ scroll = cursor
71+ }
72+ self.scroll = scroll;
73+ }
56 }
74 }
57
75
58 pub fn selection(&self) -> Option<&T> {
76 pub fn selection(&self) -> Option<&T> {
@@ -156,30 +174,23 @@ impl<T> Component for Menu<T> {
156 let height = std::cmp::min(self.options.len(), MAX);
174 let height = std::cmp::min(self.options.len(), MAX);
157 let height = std::cmp::min(height, viewport.1 as usize);
175 let height = std::cmp::min(height, viewport.1 as usize);
158
176
159− Some((width as u16, height as u16))
177+ self.size = (width as u16, height as u16);
178+
179+ // adjust scroll offsets if size changed
180+ self.adjust_scroll();
181+
182+ Some(self.size)
160 }
183 }
161
184
162 fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
185 fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
163 let style = Style::default().fg(Color::Rgb(164, 160, 232)); // lavender
186 let style = Style::default().fg(Color::Rgb(164, 160, 232)); // lavender
164 let selected = Style::default().fg(Color::Rgb(255, 255, 255));
187 let selected = Style::default().fg(Color::Rgb(255, 255, 255));
165
188
166− // TODO: instead of a cell, all these numbers should be precomputed in handle_event + init
167− let mut scroll = self.scroll.get();
189+ let scroll = self.scroll;
168 let len = self.options.len();
190 let len = self.options.len();
169
191
170 let win_height = area.height as usize;
192 let win_height = area.height as usize;
171
193
172− if let Some(cursor) = self.cursor {
173− if cursor > (win_height + scroll).saturating_sub(1) {
174− // scroll down
175− scroll += cursor - (win_height + scroll).saturating_sub(1)
176− } else if cursor < scroll {
177− // scroll up
178− scroll = cursor
179− }
180− self.scroll.set(scroll);
181− }
182−
183 fn div_ceil(a: usize, b: usize) -> usize {
194 fn div_ceil(a: usize, b: usize) -> usize {
184 (a + b - 1) / a
195 (a + b - 1) / a
185 }
196 }