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