terminal_scrollbar.rs

 1use std::{
 2    any::Any,
 3    cell::{Cell, RefCell},
 4    rc::Rc,
 5};
 6
 7use gpui::{Bounds, Point, size};
 8use terminal::Terminal;
 9use ui::{ContentSize, Pixels, ScrollableHandle, px};
10
11#[derive(Debug)]
12struct ScrollHandleState {
13    line_height: Pixels,
14    total_lines: usize,
15    viewport_lines: usize,
16    display_offset: usize,
17}
18
19impl ScrollHandleState {
20    fn new(terminal: &Terminal) -> Self {
21        Self {
22            line_height: terminal.last_content().terminal_bounds.line_height,
23            total_lines: terminal.total_lines(),
24            viewport_lines: terminal.viewport_lines(),
25            display_offset: terminal.last_content().display_offset,
26        }
27    }
28}
29
30#[derive(Debug, Clone)]
31pub struct TerminalScrollHandle {
32    state: Rc<RefCell<ScrollHandleState>>,
33    pub future_display_offset: Rc<Cell<Option<usize>>>,
34}
35
36impl TerminalScrollHandle {
37    pub fn new(terminal: &Terminal) -> Self {
38        Self {
39            state: Rc::new(RefCell::new(ScrollHandleState::new(terminal))),
40            future_display_offset: Rc::new(Cell::new(None)),
41        }
42    }
43
44    pub fn update(&self, terminal: &Terminal) {
45        *self.state.borrow_mut() = ScrollHandleState::new(terminal);
46    }
47}
48
49impl ScrollableHandle for TerminalScrollHandle {
50    fn content_size(&self) -> Option<ContentSize> {
51        let state = self.state.borrow();
52        Some(ContentSize {
53            size: size(px(0.), px(state.total_lines as f32 * state.line_height.0)),
54            scroll_adjustment: Some(Point::new(px(0.), px(0.))),
55        })
56    }
57
58    fn offset(&self) -> Point<Pixels> {
59        let state = self.state.borrow();
60        let scroll_offset = state.total_lines - state.viewport_lines - state.display_offset;
61        Point::new(
62            px(0.),
63            -px(scroll_offset as f32 * self.state.borrow().line_height.0),
64        )
65    }
66
67    fn set_offset(&self, point: Point<Pixels>) {
68        let state = self.state.borrow();
69        let offset_delta = (point.y.0 / state.line_height.0).round() as i32;
70
71        let max_offset = state.total_lines - state.viewport_lines;
72        let display_offset = (max_offset as i32 + offset_delta).clamp(0, max_offset as i32);
73
74        self.future_display_offset
75            .set(Some(display_offset as usize));
76    }
77
78    fn viewport(&self) -> Bounds<Pixels> {
79        let state = self.state.borrow();
80        Bounds::new(
81            Point::new(px(0.), px(0.)),
82            size(
83                px(0.),
84                px(state.viewport_lines as f32 * state.line_height.0),
85            ),
86        )
87    }
88
89    fn as_any(&self) -> &dyn Any {
90        self
91    }
92}