1 let get_index (text_display : TextDisplay 2 x : u32 3 y : u32) = 4 let grid_x = x as i32 - text_display.margin.left as i32 5 let character = if grid_x < 0 6 then 0 7 else grid_x.as<f32> / text_display.font.advance |> as<u32> 8 9 let line = if y < text_display.margin.up then 10 0 11 else 12 let grid_y = y - text_display.margin.up 13 grid_y / text_display.font.line_height 14 15 TextPos line character 16 17 local set_cursor_position (text_box : MultilineTextBox 18 prev_pos : TextPos 19 position : TextPos 20 is_mouse_move : bool 21 is_move : bool) = 22 if text_box.os_window.is_shift_pressed && is_move || is_mouse_move then 23 if prev_pos <> position then 24 let max_position = text_box.display_text.max 25 let prev_selection = text_box.selection 26 let left = position.min max_position |> min prev_pos 27 let right = position.min max_position |> max prev_pos 28 let (min, max) = case prev_selection of 29 None -> (left, right) 30 Some (prev_min, prev_max) -> 31 if position > prev_pos then 32 if prev_pos == prev_max 33 then (prev_min, right) 34 else (prev_max.min right, prev_max.max right) 35 else 36 if prev_pos == prev_min 37 then (left, prev_max) 38 else (left.min prev_min, left.max prev_min) 39 40 let selection = if min == max then None else Some (min, max) 41 text_box.selection = selection 42 else 43 text_box.selection = None 44 45 text_box.cursor_pos = position 46 update_offset 47 48 type MultilineTextBox 49 local coerce_pos (text_pos : TextPos) = 50 let line_count = display_text.lines.size 51 let line_index = text_pos.line.min (line_count - 1) 52 let line = display_text[line_index] 53 let char_index = text_pos.char.min (line.length - 1) 54 55 TextPos line_index char_index 56 57 let move_cursor (text_box : MultilineTextBox 58 x : u32 59 y : u32 60 is_mouse_move : bool) = 61 if not text_box.text_display.has_text then 62 return 63 64 let position = text_box.coerce_pos (get_index text_box.text_display x y) 65 set_cursor_position text_box text_box.cursor_pos position 66 =is_mouse_move 67 is_move = true 68 69 let char_offset (text_box : MultilineTextBox) (position : TextPos) = 70 if text_box.text_display.has_text then 71 let bounds = text_box.text_display.bounds position 72 (bounds.left, bounds.up) 73 else 74 let margin = text_box.text_margin 75 (margin.left, margin.up) 76 77 let get_cursor_position (text_box : MultilineTextBox) (position : TextPos) = 78 let (char_offset_left, char_offset_up) = char_offset text_box position 79 let offset = text_box.offset 80 let char_height = text_box.font.height 81 let y = char_height as f32 * 1.1 |> round + char_offset_up as f32 82 Vector3 83 x = char_offset_left as f32 + offset as f32 84 =y 85 z = Control.offset_z * 2 86 87 let create_cursor (text_box : MultilineTextBox) = 88 let canvas = text_box.canvas 89 let cursor = QuadControl 90 is_visible = text_box.is_focused 91 92 let advance = text_box.font.advance 93 cursor.size = if advance == 0 then 94 Size.new 95 else 96 let width = advance.round.as<u32> 97 let height = width / 4 |> max 3 98 Size width height 99 100 text_box.cursor_pos@atom.bind { position -> 101 cursor.position = get_cursor_position text_box position } 102 |> text_box.push_token 103 104 text_box.offset_controls.add cursor 105 canvas.items.add cursor 106 107 cursor 108 109 type MultilineTextBox 110 val cursor = create_cursor self 111 112 canvas.subscribe { _, event -> case event of 113 is MouseEvent/Button -> 114 if event.action.is_press then 115 move_cursor 116 text_box = self 117 x = event.x as i32 - offset |> as<u32> 118 y = event.y 119 is_mouse_move = false 120 121 is MouseEvent/Move -> 122 let relative_x = event.x as i32 - event.begin_x as i32 123 + event.begin_relative_x as i32 124 let relative_y = event.y as i32 - event.begin_y as i32 125 + event.begin_relative_y as i32 126 let global_x = canvas.global_position.x as i32 127 let global_y = canvas.global_position.y as i32 128 let global_offset_x = event.begin_x as i32 129 - event.begin_relative_x as i32 - global_x 130 let global_offset_y = event.begin_y as i32 131 - event.begin_relative_y as i32 - global_y 132 move_cursor 133 text_box = self 134 x = relative_x as i32 + global_offset_x |> max 0 - offset |> as<u32> 135 y = relative_y as i32 + global_offset_y |> max 0 as u32 136 is_mouse_move = true 137 138 else -> () } 139 140 canvas.is_clickable = true 141 is_receive_mouse_move = true 142