1use alacritty_terminal::{
2 grid::{Dimensions, GridIterator, Indexed},
3 index::{Column as GridCol, Line as GridLine, Point, Side},
4 selection::{Selection, SelectionRange, SelectionType},
5 sync::FairMutex,
6 term::{
7 cell::{Cell, Flags},
8 SizeInfo,
9 },
10 Term,
11};
12use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine, Input};
13use gpui::{
14 color::Color,
15 elements::*,
16 fonts::{TextStyle, Underline},
17 geometry::{
18 rect::RectF,
19 vector::{vec2f, Vector2F},
20 },
21 json::json,
22 text_layout::{Line, RunStyle},
23 Event, FontCache, KeyDownEvent, MouseRegion, PaintContext, Quad, ScrollWheelEvent,
24 SizeConstraint, TextLayoutCache, WeakModelHandle,
25};
26use itertools::Itertools;
27use ordered_float::OrderedFloat;
28use settings::Settings;
29use theme::TerminalStyle;
30
31use std::{cmp::min, ops::Range, rc::Rc, sync::Arc};
32use std::{fmt::Debug, ops::Sub};
33
34use crate::{
35 color_translation::convert_color, connection::TerminalConnection, ScrollTerminal, ZedListener,
36};
37
38///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
39///Scroll multiplier that is set to 3 by default. This will be removed when I
40///Implement scroll bars.
41const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
42
43///Used to display the grid as passed to Alacritty and the TTY.
44///Useful for debugging inconsistencies between behavior and display
45#[cfg(debug_assertions)]
46const DEBUG_GRID: bool = false;
47
48///The GPUI element that paints the terminal.
49///We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
50pub struct TerminalEl {
51 connection: WeakModelHandle<TerminalConnection>,
52 view_id: usize,
53 modal: bool,
54}
55
56///New type pattern so I don't mix these two up
57struct CellWidth(f32);
58struct LineHeight(f32);
59
60struct LayoutLine {
61 cells: Vec<LayoutCell>,
62 highlighted_range: Option<Range<usize>>,
63}
64
65///New type pattern to ensure that we use adjusted mouse positions throughout the code base, rather than
66struct PaneRelativePos(Vector2F);
67
68///Functionally the constructor for the PaneRelativePos type, mutates the mouse_position
69fn relative_pos(mouse_position: Vector2F, origin: Vector2F) -> PaneRelativePos {
70 PaneRelativePos(mouse_position.sub(origin)) //Avoid the extra allocation by mutating
71}
72
73#[derive(Clone, Debug, Default)]
74struct LayoutCell {
75 point: Point<i32, i32>,
76 text: Line, //NOTE TO SELF THIS IS BAD PERFORMANCE RN!
77 background_color: Color,
78}
79
80impl LayoutCell {
81 fn new(point: Point<i32, i32>, text: Line, background_color: Color) -> LayoutCell {
82 LayoutCell {
83 point,
84 text,
85 background_color,
86 }
87 }
88}
89
90///The information generated during layout that is nescessary for painting
91pub struct LayoutState {
92 layout_lines: Vec<LayoutLine>,
93 line_height: LineHeight,
94 em_width: CellWidth,
95 cursor: Option<Cursor>,
96 background_color: Color,
97 cur_size: SizeInfo,
98 terminal: Arc<FairMutex<Term<ZedListener>>>,
99 selection_color: Color,
100}
101
102impl TerminalEl {
103 pub fn new(
104 view_id: usize,
105 connection: WeakModelHandle<TerminalConnection>,
106 modal: bool,
107 ) -> TerminalEl {
108 TerminalEl {
109 view_id,
110 connection,
111 modal,
112 }
113 }
114}
115
116impl Element for TerminalEl {
117 type LayoutState = LayoutState;
118 type PaintState = ();
119
120 fn layout(
121 &mut self,
122 constraint: gpui::SizeConstraint,
123 cx: &mut gpui::LayoutContext,
124 ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
125 //Settings immutably borrows cx here for the settings and font cache
126 //and we need to modify the cx to resize the terminal. So instead of
127 //storing Settings or the font_cache(), we toss them ASAP and then reborrow later
128 let text_style = make_text_style(cx.font_cache(), cx.global::<Settings>());
129 let line_height = LineHeight(cx.font_cache().line_height(text_style.font_size));
130 let cell_width = CellWidth(
131 cx.font_cache()
132 .em_advance(text_style.font_id, text_style.font_size),
133 );
134 let connection_handle = self.connection.upgrade(cx).unwrap();
135
136 //Tell the view our new size. Requires a mutable borrow of cx and the view
137 let cur_size = make_new_size(constraint, &cell_width, &line_height);
138 //Note that set_size locks and mutates the terminal.
139 connection_handle.update(cx.app, |connection, _| connection.set_size(cur_size));
140
141 let (selection_color, terminal_theme) = {
142 let theme = &(cx.global::<Settings>()).theme;
143 (theme.editor.selection.selection, &theme.terminal)
144 };
145
146 let terminal_mutex = connection_handle.read(cx).term.clone();
147 let term = terminal_mutex.lock();
148 let grid = term.grid();
149 let cursor_point = grid.cursor.point;
150 let cursor_text = grid[cursor_point.line][cursor_point.column].c.to_string();
151
152 let content = term.renderable_content();
153
154 let layout_lines = layout_lines(
155 content.display_iter,
156 &text_style,
157 terminal_theme,
158 cx.text_layout_cache,
159 self.modal,
160 content.selection,
161 );
162
163 let block_text = cx.text_layout_cache.layout_str(
164 &cursor_text,
165 text_style.font_size,
166 &[(
167 cursor_text.len(),
168 RunStyle {
169 font_id: text_style.font_id,
170 color: terminal_theme.colors.background,
171 underline: Default::default(),
172 },
173 )],
174 );
175
176 let cursor = get_cursor_shape(
177 content.cursor.point.line.0 as usize,
178 content.cursor.point.column.0 as usize,
179 content.display_offset,
180 &line_height,
181 &cell_width,
182 cur_size.total_lines(),
183 &block_text,
184 )
185 .map(move |(cursor_position, block_width)| {
186 let block_width = if block_width != 0.0 {
187 block_width
188 } else {
189 cell_width.0
190 };
191
192 Cursor::new(
193 cursor_position,
194 block_width,
195 line_height.0,
196 terminal_theme.colors.cursor,
197 CursorShape::Block,
198 Some(block_text.clone()),
199 )
200 });
201 drop(term);
202
203 let background_color = if self.modal {
204 terminal_theme.colors.modal_background
205 } else {
206 terminal_theme.colors.background
207 };
208
209 (
210 constraint.max,
211 LayoutState {
212 layout_lines,
213 line_height,
214 em_width: cell_width,
215 cursor,
216 cur_size,
217 background_color,
218 terminal: terminal_mutex,
219 selection_color,
220 },
221 )
222 }
223
224 fn paint(
225 &mut self,
226 bounds: gpui::geometry::rect::RectF,
227 visible_bounds: gpui::geometry::rect::RectF,
228 layout: &mut Self::LayoutState,
229 cx: &mut gpui::PaintContext,
230 ) -> Self::PaintState {
231 //Setup element stuff
232 let clip_bounds = Some(visible_bounds);
233
234 cx.paint_layer(clip_bounds, |cx| {
235 let cur_size = layout.cur_size.clone();
236 let origin = bounds.origin() + vec2f(layout.em_width.0, 0.);
237
238 //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
239 attach_mouse_handlers(
240 origin,
241 cur_size,
242 self.view_id,
243 &layout.terminal,
244 visible_bounds,
245 cx,
246 );
247
248 cx.paint_layer(clip_bounds, |cx| {
249 //Start with a background color
250 cx.scene.push_quad(Quad {
251 bounds: RectF::new(bounds.origin(), bounds.size()),
252 background: Some(layout.background_color),
253 border: Default::default(),
254 corner_radius: 0.,
255 });
256
257 //Draw cell backgrounds
258 for layout_line in &layout.layout_lines {
259 for layout_cell in &layout_line.cells {
260 let position = vec2f(
261 origin.x() + layout_cell.point.column as f32 * layout.em_width.0,
262 origin.y() + layout_cell.point.line as f32 * layout.line_height.0,
263 );
264 let size = vec2f(layout.em_width.0, layout.line_height.0);
265
266 cx.scene.push_quad(Quad {
267 bounds: RectF::new(position, size),
268 background: Some(layout_cell.background_color),
269 border: Default::default(),
270 corner_radius: 0.,
271 })
272 }
273 }
274 });
275
276 //Draw Selection
277 cx.paint_layer(clip_bounds, |cx| {
278 let mut highlight_y = None;
279 let highlight_lines = layout
280 .layout_lines
281 .iter()
282 .filter_map(|line| {
283 if let Some(range) = &line.highlighted_range {
284 if let None = highlight_y {
285 highlight_y = Some(
286 origin.y()
287 + line.cells[0].point.line as f32 * layout.line_height.0,
288 );
289 }
290 let start_x = origin.x()
291 + line.cells[range.start].point.column as f32 * layout.em_width.0;
292 let end_x = origin.x()
293 + line.cells[range.end].point.column as f32 * layout.em_width.0
294 + layout.em_width.0;
295
296 return Some(HighlightedRangeLine { start_x, end_x });
297 } else {
298 return None;
299 }
300 })
301 .collect::<Vec<HighlightedRangeLine>>();
302
303 if let Some(y) = highlight_y {
304 let hr = HighlightedRange {
305 start_y: y, //Need to change this
306 line_height: layout.line_height.0,
307 lines: highlight_lines,
308 color: layout.selection_color,
309 //Copied from editor. TODO: move to theme or something
310 corner_radius: 0.15 * layout.line_height.0,
311 };
312 hr.paint(bounds, cx.scene);
313 }
314 });
315
316 cx.paint_layer(clip_bounds, |cx| {
317 for layout_line in &layout.layout_lines {
318 for layout_cell in &layout_line.cells {
319 let point = layout_cell.point;
320
321 //Don't actually know the start_x for a line, until here:
322 let cell_origin = vec2f(
323 origin.x() + point.column as f32 * layout.em_width.0,
324 origin.y() + point.line as f32 * layout.line_height.0,
325 );
326
327 layout_cell.text.paint(
328 cell_origin,
329 visible_bounds,
330 layout.line_height.0,
331 cx,
332 );
333 }
334 }
335 });
336
337 //Draw cursor
338 if let Some(cursor) = &layout.cursor {
339 cx.paint_layer(clip_bounds, |cx| {
340 cursor.paint(origin, cx);
341 })
342 }
343
344 #[cfg(debug_assertions)]
345 if DEBUG_GRID {
346 cx.paint_layer(clip_bounds, |cx| {
347 draw_debug_grid(bounds, layout, cx);
348 })
349 }
350 });
351 }
352
353 fn dispatch_event(
354 &mut self,
355 event: &gpui::Event,
356 _bounds: gpui::geometry::rect::RectF,
357 visible_bounds: gpui::geometry::rect::RectF,
358 layout: &mut Self::LayoutState,
359 _paint: &mut Self::PaintState,
360 cx: &mut gpui::EventContext,
361 ) -> bool {
362 //The problem:
363 //Depending on the terminal mode, we either send an escape sequence
364 //OR update our own data structures.
365 //e.g. scrolling. If we do smooth scrolling, then we need to check if
366 //we own scrolling and then if so, do our scrolling thing.
367 //Ok, so the terminal connection should have APIs for querying it semantically
368 //something like `should_handle_scroll()`. This means we need a handle to the connection.
369 //Actually, this is the only time that this app needs to talk to the outer world.
370 //TODO for scrolling rework: need a way of intercepting Home/End/PageUp etc.
371 //Sometimes going to scroll our own internal buffer, sometimes going to send ESC
372 //
373 //Same goes for key events
374 //Actually, we don't use the terminal at all in dispatch_event code, the view
375 //Handles it all. Check how the editor implements scrolling, is it view-level
376 //or element level?
377
378 //Question: Can we continue dispatching to the view, so it can talk to the connection
379 //Or should we instead add a connection into here?
380
381 match event {
382 Event::ScrollWheel(ScrollWheelEvent {
383 delta, position, ..
384 }) => visible_bounds
385 .contains_point(*position)
386 .then(|| {
387 let vertical_scroll =
388 (delta.y() / layout.line_height.0) * ALACRITTY_SCROLL_MULTIPLIER;
389 cx.dispatch_action(ScrollTerminal(vertical_scroll.round() as i32));
390 })
391 .is_some(),
392 Event::KeyDown(KeyDownEvent {
393 input: Some(input), ..
394 }) => cx
395 .is_parent_view_focused()
396 .then(|| {
397 cx.dispatch_action(Input(input.to_string()));
398 })
399 .is_some(),
400 _ => false,
401 }
402 }
403
404 fn debug(
405 &self,
406 _bounds: gpui::geometry::rect::RectF,
407 _layout: &Self::LayoutState,
408 _paint: &Self::PaintState,
409 _cx: &gpui::DebugContext,
410 ) -> gpui::serde_json::Value {
411 json!({
412 "type": "TerminalElement",
413 })
414 }
415}
416
417pub fn mouse_to_cell_data(
418 pos: Vector2F,
419 origin: Vector2F,
420 cur_size: SizeInfo,
421 display_offset: usize,
422) -> (Point, alacritty_terminal::index::Direction) {
423 let relative_pos = relative_pos(pos, origin);
424 let point = grid_cell(&relative_pos, cur_size, display_offset);
425 let side = cell_side(&relative_pos, cur_size);
426 (point, side)
427}
428
429///Configures a text style from the current settings.
430fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle {
431 TextStyle {
432 color: settings.theme.editor.text_color,
433 font_family_id: settings.buffer_font_family,
434 font_family_name: font_cache.family_name(settings.buffer_font_family).unwrap(),
435 font_id: font_cache
436 .select_font(settings.buffer_font_family, &Default::default())
437 .unwrap(),
438 font_size: settings.buffer_font_size,
439 font_properties: Default::default(),
440 underline: Default::default(),
441 }
442}
443
444///Configures a size info object from the given information.
445fn make_new_size(
446 constraint: SizeConstraint,
447 cell_width: &CellWidth,
448 line_height: &LineHeight,
449) -> SizeInfo {
450 SizeInfo::new(
451 constraint.max.x() - cell_width.0,
452 constraint.max.y(),
453 cell_width.0,
454 line_height.0,
455 0.,
456 0.,
457 false,
458 )
459}
460
461fn layout_lines(
462 grid: GridIterator<Cell>,
463 text_style: &TextStyle,
464 terminal_theme: &TerminalStyle,
465 text_layout_cache: &TextLayoutCache,
466 modal: bool,
467 selection_range: Option<SelectionRange>,
468) -> Vec<LayoutLine> {
469 let lines = grid.group_by(|i| i.point.line);
470 lines
471 .into_iter()
472 .enumerate()
473 .map(|(line_index, (_, line))| {
474 let mut highlighted_range = None;
475 let cells = line
476 .enumerate()
477 .map(|(x_index, indexed_cell)| {
478 if selection_range
479 .map(|range| range.contains(indexed_cell.point))
480 .unwrap_or(false)
481 {
482 let mut range = highlighted_range.take().unwrap_or(x_index..x_index);
483 range.end = range.end.max(x_index);
484 highlighted_range = Some(range);
485 }
486
487 let cell_text = &indexed_cell.c.to_string();
488
489 let cell_style = cell_style(&indexed_cell, terminal_theme, text_style, modal);
490
491 //This is where we might be able to get better performance
492 let layout_cell = text_layout_cache.layout_str(
493 cell_text,
494 text_style.font_size,
495 &[(cell_text.len(), cell_style)],
496 );
497
498 LayoutCell::new(
499 Point::new(line_index as i32, indexed_cell.point.column.0 as i32),
500 layout_cell,
501 convert_color(&indexed_cell.bg, &terminal_theme.colors, modal),
502 )
503 })
504 .collect::<Vec<LayoutCell>>();
505
506 LayoutLine {
507 cells,
508 highlighted_range,
509 }
510 })
511 .collect::<Vec<LayoutLine>>()
512}
513
514// Compute the cursor position and expected block width, may return a zero width if x_for_index returns
515// the same position for sequential indexes. Use em_width instead
516//TODO: This function is messy, too many arguments and too many ifs. Simplify.
517fn get_cursor_shape(
518 line: usize,
519 line_index: usize,
520 display_offset: usize,
521 line_height: &LineHeight,
522 cell_width: &CellWidth,
523 total_lines: usize,
524 text_fragment: &Line,
525) -> Option<(Vector2F, f32)> {
526 let cursor_line = line + display_offset;
527 if cursor_line <= total_lines {
528 let cursor_width = if text_fragment.width() == 0. {
529 cell_width.0
530 } else {
531 text_fragment.width()
532 };
533
534 Some((
535 vec2f(
536 line_index as f32 * cell_width.0,
537 cursor_line as f32 * line_height.0,
538 ),
539 cursor_width,
540 ))
541 } else {
542 None
543 }
544}
545
546///Convert the Alacritty cell styles to GPUI text styles and background color
547fn cell_style(
548 indexed: &Indexed<&Cell>,
549 style: &TerminalStyle,
550 text_style: &TextStyle,
551 modal: bool,
552) -> RunStyle {
553 let flags = indexed.cell.flags;
554 let fg = convert_color(&indexed.cell.fg, &style.colors, modal);
555
556 let underline = flags
557 .contains(Flags::UNDERLINE)
558 .then(|| Underline {
559 color: Some(fg),
560 squiggly: false,
561 thickness: OrderedFloat(1.),
562 })
563 .unwrap_or_default();
564
565 RunStyle {
566 color: fg,
567 font_id: text_style.font_id,
568 underline,
569 }
570}
571
572fn attach_mouse_handlers(
573 origin: Vector2F,
574 cur_size: SizeInfo,
575 view_id: usize,
576 terminal_mutex: &Arc<FairMutex<Term<ZedListener>>>,
577 visible_bounds: RectF,
578 cx: &mut PaintContext,
579) {
580 let click_mutex = terminal_mutex.clone();
581 let drag_mutex = terminal_mutex.clone();
582 let mouse_down_mutex = terminal_mutex.clone();
583
584 cx.scene.push_mouse_region(MouseRegion {
585 view_id,
586 mouse_down: Some(Rc::new(move |pos, _| {
587 let mut term = mouse_down_mutex.lock();
588 let (point, side) = mouse_to_cell_data(
589 pos,
590 origin,
591 cur_size,
592 term.renderable_content().display_offset,
593 );
594 term.selection = Some(Selection::new(SelectionType::Simple, point, side))
595 })),
596 click: Some(Rc::new(move |pos, click_count, cx| {
597 let mut term = click_mutex.lock();
598
599 let (point, side) = mouse_to_cell_data(
600 pos,
601 origin,
602 cur_size,
603 term.renderable_content().display_offset,
604 );
605
606 let selection_type = match click_count {
607 0 => return, //This is a release
608 1 => Some(SelectionType::Simple),
609 2 => Some(SelectionType::Semantic),
610 3 => Some(SelectionType::Lines),
611 _ => None,
612 };
613
614 let selection =
615 selection_type.map(|selection_type| Selection::new(selection_type, point, side));
616
617 term.selection = selection;
618 cx.focus_parent_view();
619 cx.notify();
620 })),
621 bounds: visible_bounds,
622 drag: Some(Rc::new(move |_delta, pos, cx| {
623 let mut term = drag_mutex.lock();
624
625 let (point, side) = mouse_to_cell_data(
626 pos,
627 origin,
628 cur_size,
629 term.renderable_content().display_offset,
630 );
631
632 if let Some(mut selection) = term.selection.take() {
633 selection.update(point, side);
634 term.selection = Some(selection);
635 }
636
637 cx.notify();
638 })),
639 ..Default::default()
640 });
641}
642
643///Copied (with modifications) from alacritty/src/input.rs > Processor::cell_side()
644fn cell_side(pos: &PaneRelativePos, cur_size: SizeInfo) -> Side {
645 let x = pos.0.x() as usize;
646 let cell_x = x.saturating_sub(cur_size.cell_width() as usize) % cur_size.cell_width() as usize;
647 let half_cell_width = (cur_size.cell_width() / 2.0) as usize;
648
649 let additional_padding =
650 (cur_size.width() - cur_size.cell_width() * 2.) % cur_size.cell_width();
651 let end_of_grid = cur_size.width() - cur_size.cell_width() - additional_padding;
652
653 if cell_x > half_cell_width
654 // Edge case when mouse leaves the window.
655 || x as f32 >= end_of_grid
656 {
657 Side::Right
658 } else {
659 Side::Left
660 }
661}
662
663///Copied (with modifications) from alacritty/src/event.rs > Mouse::point()
664///Position is a pane-relative position. That means the top left corner of the mouse
665///Region should be (0,0)
666fn grid_cell(pos: &PaneRelativePos, cur_size: SizeInfo, display_offset: usize) -> Point {
667 let pos = pos.0;
668 let col = pos.x() / cur_size.cell_width(); //TODO: underflow...
669 let col = min(GridCol(col as usize), cur_size.last_column());
670
671 let line = pos.y() / cur_size.cell_height();
672 let line = min(line as i32, cur_size.bottommost_line().0);
673
674 //when clicking, need to ADD to get to the top left cell
675 //e.g. total_lines - viewport_height, THEN subtract display offset
676 //0 -> total_lines - viewport_height - display_offset + mouse_line
677
678 Point::new(GridLine(line - display_offset as i32), col)
679}
680
681///Draws the grid as Alacritty sees it. Useful for checking if there is an inconsistency between
682///Display and conceptual grid.
683#[cfg(debug_assertions)]
684fn draw_debug_grid(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) {
685 let width = layout.cur_size.width();
686 let height = layout.cur_size.height();
687 //Alacritty uses 'as usize', so shall we.
688 for col in 0..(width / layout.em_width.0).round() as usize {
689 cx.scene.push_quad(Quad {
690 bounds: RectF::new(
691 bounds.origin() + vec2f((col + 1) as f32 * layout.em_width.0, 0.),
692 vec2f(1., height),
693 ),
694 background: Some(Color::green()),
695 border: Default::default(),
696 corner_radius: 0.,
697 });
698 }
699 for row in 0..((height / layout.line_height.0) + 1.0).round() as usize {
700 cx.scene.push_quad(Quad {
701 bounds: RectF::new(
702 bounds.origin() + vec2f(layout.em_width.0, row as f32 * layout.line_height.0),
703 vec2f(width, 1.),
704 ),
705 background: Some(Color::green()),
706 border: Default::default(),
707 corner_radius: 0.,
708 });
709 }
710}
711
712mod test {
713
714 #[test]
715 fn test_mouse_to_selection() {
716 let term_width = 100.;
717 let term_height = 200.;
718 let cell_width = 10.;
719 let line_height = 20.;
720 let mouse_pos_x = 100.; //Window relative
721 let mouse_pos_y = 100.; //Window relative
722 let origin_x = 10.;
723 let origin_y = 20.;
724
725 let cur_size = alacritty_terminal::term::SizeInfo::new(
726 term_width,
727 term_height,
728 cell_width,
729 line_height,
730 0.,
731 0.,
732 false,
733 );
734
735 let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
736 let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
737 let (point, _) =
738 crate::terminal_element::mouse_to_cell_data(mouse_pos, origin, cur_size, 0);
739 assert_eq!(
740 point,
741 alacritty_terminal::index::Point::new(
742 alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
743 alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
744 )
745 );
746 }
747
748 #[test]
749 fn test_mouse_to_selection_off_edge() {
750 let term_width = 100.;
751 let term_height = 200.;
752 let cell_width = 10.;
753 let line_height = 20.;
754 let mouse_pos_x = 100.; //Window relative
755 let mouse_pos_y = 100.; //Window relative
756 let origin_x = 10.;
757 let origin_y = 20.;
758
759 let cur_size = alacritty_terminal::term::SizeInfo::new(
760 term_width,
761 term_height,
762 cell_width,
763 line_height,
764 0.,
765 0.,
766 false,
767 );
768
769 let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
770 let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
771 let (point, _) =
772 crate::terminal_element::mouse_to_cell_data(mouse_pos, origin, cur_size, 0);
773 assert_eq!(
774 point,
775 alacritty_terminal::index::Point::new(
776 alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
777 alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
778 )
779 );
780 }
781}