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