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 u32 / text_display.advance 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.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 70 font : Font 71 position : TextPos) = 72 if text_box.text_display.has_text then 73 let bounds = text_box.text_display.bounds position 74 (bounds.left, bounds.up) 75 else 76 let margin = font.text_margin 77 (margin.left, margin.up) 78 79 let create_cursor (text_box : MultilineTextBox) = 80 let canvas = text_box.canvas 81 let cursor = QuadControl 82 is_visible = text_box.is_focused 83 84 let size_token = text_box.text_display.advance@atom.bind { advance -> 85 cursor.size = if advance == 0 then 86 Size.new 87 else 88 let width = advance 89 let height = width / 4 |> max 3 90 Size width height } 91 92 let index_token = text_box.cursor_pos@atom.bind { i -> 93 let font = text_box.font 94 let (char_offset_left, char_offset_up) = char_offset text_box font i 95 let offset = text_box.offset 96 let line_height = font.line_height 97 let y = line_height as f32 * 0.7 |> round + char_offset_up as f32 98 cursor.position = Vector3 99 x = char_offset_left as f32 + offset as f32 100 =y 101 z = Control.offset_z * 2 } 102 103 text_box.offset_controls.add cursor 104 canvas.items.add cursor 105 106 text_box.push_token index_token 107 push_token size_token 108 cursor 109 110 type MultilineTextBox 111 val cursor = create_cursor self 112 113 canvas.subscribe { _, event -> case event of 114 is MouseEvent/Button -> 115 if event.action.is_press then 116 move_cursor 117 text_box = self 118 x = event.x as i32 - offset |> as<u32> 119 y = event.y 120 is_mouse_move = false 121 122 is MouseEvent/Move -> 123 let relative_x = event.x as i32 - event.begin_x as i32 124 + event.begin_relative_x as i32 125 let relative_y = event.y as i32 - event.begin_y as i32 126 + event.begin_relative_y as i32 127 let global_x = canvas.global_position.x as i32 128 let global_y = canvas.global_position.y as i32 129 let global_offset_x = event.begin_x as i32 130 - event.begin_relative_x as i32 - global_x 131 let global_offset_y = event.begin_y as i32 132 - event.begin_relative_y as i32 - global_y 133 move_cursor 134 text_box = self 135 x = relative_x as i32 + global_offset_x |> max 0 - offset |> as<u32> 136 y = relative_y as i32 + global_offset_y |> max 0 as u32 137 is_mouse_move = true 138 139 else -> () } 140 141 canvas.is_clickable = true 142 is_receive_mouse_move = true 143