vtcode_tui/core_tui/session/
impl_events.rs1use super::*;
2
3impl Session {
4 pub fn handle_event(
5 &mut self,
6 event: CrosstermEvent,
7 events: &UnboundedSender<InlineEvent>,
8 callback: Option<&(dyn Fn(&InlineEvent) + Send + Sync + 'static)>,
9 ) {
10 match event {
11 CrosstermEvent::Key(key) => {
12 if matches!(key.kind, KeyEventKind::Press)
15 && let Some(outbound) = events::process_key(self, key)
16 {
17 self.emit_inline_event(&outbound, events, callback);
18 }
19 }
20 CrosstermEvent::Mouse(mouse_event) => match mouse_event.kind {
21 MouseEventKind::ScrollDown => {
22 if self.history_picker_state.active {
24 self.history_picker_state.move_down();
25 self.mark_dirty();
26 } else {
27 self.scroll_line_down();
28 self.mark_dirty();
29 }
30 }
31 MouseEventKind::ScrollUp => {
32 if self.history_picker_state.active {
34 self.history_picker_state.move_up();
35 self.mark_dirty();
36 } else {
37 self.scroll_line_up();
38 self.mark_dirty();
39 }
40 }
41 MouseEventKind::Down(crossterm::event::MouseButton::Left) => {
42 self.mouse_selection
44 .start_selection(mouse_event.column, mouse_event.row);
45 self.mark_dirty();
46 if !self.handle_input_click(mouse_event) {
47 self.handle_transcript_click(mouse_event);
48 }
49 }
50 MouseEventKind::Drag(crossterm::event::MouseButton::Left) => {
51 self.mouse_selection
52 .update_selection(mouse_event.column, mouse_event.row);
53 self.mark_dirty();
54 }
55 MouseEventKind::Up(crossterm::event::MouseButton::Left) => {
56 self.mouse_selection
57 .finish_selection(mouse_event.column, mouse_event.row);
58 self.mark_dirty();
59 }
60 _ => {}
61 },
62 CrosstermEvent::Paste(content) => {
63 events::handle_paste(self, &content);
64 }
65 CrosstermEvent::Resize(_, rows) => {
66 self.apply_view_rows(rows);
67 self.mark_dirty();
68 }
69 CrosstermEvent::FocusGained => {
70 }
72 CrosstermEvent::FocusLost => {
73 }
75 }
76 }
77
78 pub(crate) fn handle_transcript_click(&mut self, mouse_event: MouseEvent) -> bool {
79 if !matches!(
80 mouse_event.kind,
81 MouseEventKind::Down(crossterm::event::MouseButton::Left)
82 ) {
83 return false;
84 }
85
86 let Some(area) = self.transcript_area else {
87 return false;
88 };
89
90 if mouse_event.row < area.y
91 || mouse_event.row >= area.y.saturating_add(area.height)
92 || mouse_event.column < area.x
93 || mouse_event.column >= area.x.saturating_add(area.width)
94 {
95 return false;
96 }
97
98 if self.transcript_width == 0 || self.transcript_rows == 0 {
99 return false;
100 }
101
102 let row_in_view = (mouse_event.row - area.y) as usize;
103 if row_in_view >= self.transcript_rows as usize {
104 return false;
105 }
106
107 let viewport_rows = self.transcript_rows.max(1) as usize;
108 let padding = usize::from(ui::INLINE_TRANSCRIPT_BOTTOM_PADDING);
109 let effective_padding = padding.min(viewport_rows.saturating_sub(1));
110 let total_rows = self.total_transcript_rows(self.transcript_width) + effective_padding;
111 let (top_offset, _clamped_total_rows) =
112 self.prepare_transcript_scroll(total_rows, viewport_rows);
113 let view_top = top_offset.min(self.scroll_manager.max_offset());
114 self.transcript_view_top = view_top;
115
116 let clicked_row = view_top.saturating_add(row_in_view);
117 let expanded = self.expand_collapsed_paste_at_row(self.transcript_width, clicked_row);
118 if expanded {
119 self.mark_dirty();
120 }
121 expanded
122 }
123
124 pub(crate) fn handle_input_click(&mut self, mouse_event: MouseEvent) -> bool {
125 if !matches!(
126 mouse_event.kind,
127 MouseEventKind::Down(crossterm::event::MouseButton::Left)
128 ) {
129 return false;
130 }
131
132 let Some(area) = self.input_area else {
133 return false;
134 };
135
136 if mouse_event.row < area.y
137 || mouse_event.row >= area.y.saturating_add(area.height)
138 || mouse_event.column < area.x
139 || mouse_event.column >= area.x.saturating_add(area.width)
140 {
141 return false;
142 }
143
144 let cursor_at_end = self.input_manager.cursor() == self.input_manager.content().len();
145 if !self.input_compact_mode || !cursor_at_end {
146 return false;
147 }
148
149 if self.input_compact_placeholder().is_none() {
150 return false;
151 }
152
153 self.input_compact_mode = false;
154 self.mark_dirty();
155 true
156 }
157}