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::Moved => {
22 if self.update_transcript_file_link_hover(mouse_event.column, mouse_event.row) {
23 self.mark_dirty();
24 }
25 }
26 MouseEventKind::ScrollDown => {
27 if self.history_picker_state.active {
29 self.history_picker_state.move_down();
30 self.mark_dirty();
31 } else {
32 self.scroll_line_down();
33 self.mark_dirty();
34 }
35 }
36 MouseEventKind::ScrollUp => {
37 if self.history_picker_state.active {
39 self.history_picker_state.move_up();
40 self.mark_dirty();
41 } else {
42 self.scroll_line_up();
43 self.mark_dirty();
44 }
45 }
46 MouseEventKind::Down(crossterm::event::MouseButton::Left) => {
47 if let Some(outbound) = self.transcript_file_link_event(
48 mouse_event.column,
49 mouse_event.row,
50 mouse_event.modifiers,
51 ) {
52 self.mark_dirty();
53 self.emit_inline_event(&outbound, events, callback);
54 return;
55 }
56
57 self.mouse_selection
59 .start_selection(mouse_event.column, mouse_event.row);
60 self.mark_dirty();
61 if !self.handle_input_click(mouse_event) {
62 self.handle_transcript_click(mouse_event);
63 }
64 }
65 MouseEventKind::Drag(crossterm::event::MouseButton::Left) => {
66 self.mouse_selection
67 .update_selection(mouse_event.column, mouse_event.row);
68 self.mark_dirty();
69 }
70 MouseEventKind::Up(crossterm::event::MouseButton::Left) => {
71 self.mouse_selection
72 .finish_selection(mouse_event.column, mouse_event.row);
73 self.mark_dirty();
74 }
75 _ => {}
76 },
77 CrosstermEvent::Paste(content) => {
78 events::handle_paste(self, &content);
79 }
80 CrosstermEvent::Resize(_, rows) => {
81 self.apply_view_rows(rows);
82 self.mark_dirty();
83 }
84 CrosstermEvent::FocusGained => {
85 }
87 CrosstermEvent::FocusLost => {
88 }
90 }
91 }
92
93 pub(crate) fn handle_transcript_click(&mut self, mouse_event: MouseEvent) -> bool {
94 if !matches!(
95 mouse_event.kind,
96 MouseEventKind::Down(crossterm::event::MouseButton::Left)
97 ) {
98 return false;
99 }
100
101 let Some(area) = self.transcript_area else {
102 return false;
103 };
104
105 if mouse_event.row < area.y
106 || mouse_event.row >= area.y.saturating_add(area.height)
107 || mouse_event.column < area.x
108 || mouse_event.column >= area.x.saturating_add(area.width)
109 {
110 return false;
111 }
112
113 if self.transcript_width == 0 || self.transcript_rows == 0 {
114 return false;
115 }
116
117 let row_in_view = (mouse_event.row - area.y) as usize;
118 if row_in_view >= self.transcript_rows as usize {
119 return false;
120 }
121
122 let viewport_rows = self.transcript_rows.max(1) as usize;
123 let padding = usize::from(ui::INLINE_TRANSCRIPT_BOTTOM_PADDING);
124 let effective_padding = padding.min(viewport_rows.saturating_sub(1));
125 let total_rows = self.total_transcript_rows(self.transcript_width) + effective_padding;
126 let (top_offset, _clamped_total_rows) =
127 self.prepare_transcript_scroll(total_rows, viewport_rows);
128 let view_top = top_offset.min(self.scroll_manager.max_offset());
129 self.transcript_view_top = view_top;
130
131 let clicked_row = view_top.saturating_add(row_in_view);
132 let expanded = self.expand_collapsed_paste_at_row(self.transcript_width, clicked_row);
133 if expanded {
134 self.mark_dirty();
135 }
136 expanded
137 }
138
139 pub(crate) fn handle_input_click(&mut self, mouse_event: MouseEvent) -> bool {
140 if !matches!(
141 mouse_event.kind,
142 MouseEventKind::Down(crossterm::event::MouseButton::Left)
143 ) {
144 return false;
145 }
146
147 let Some(area) = self.input_area else {
148 return false;
149 };
150
151 if mouse_event.row < area.y
152 || mouse_event.row >= area.y.saturating_add(area.height)
153 || mouse_event.column < area.x
154 || mouse_event.column >= area.x.saturating_add(area.width)
155 {
156 return false;
157 }
158
159 let cursor_at_end = self.input_manager.cursor() == self.input_manager.content().len();
160 if !self.input_compact_mode || !cursor_at_end {
161 return false;
162 }
163
164 if self.input_compact_placeholder().is_none() {
165 return false;
166 }
167
168 self.input_compact_mode = false;
169 self.mark_dirty();
170 true
171 }
172}