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