1 let edit_keys : Set<Key> = array@ 2 Key/Z Key/X Key/V Key/Tab Key/Enter Key/NumPadEnter Key/Delete Key/Backspace 3 4 let remove_range (text_box : MultilineTextBox 5 start : TextPos 6 end : TextPos 7 cursor_pos : TextPos) = 8 text_box.is_changed = true 9 let display_text = text_box.display_text 10 let item = HistoryItem/RemoveStr 11 position = start 12 =cursor_pos 13 s = display_text.range start end 14 15 text_box.history.add item 16 display_text.remove_range start end 17 text_box.update_colors start.line 18 if start.line <> end.line && display_text.lines.size > start.line + 1 then 19 display_text.lines[start.line + 1].commented_in = 20 display_text.lines[start.line].commented_out 21 22 text_box.update_colors (start.line + 1) 23 24 let get_first_character_position (text : Text) (position : TextPos) = 25 let line = text.lines[position.line] 26 for i = 0 until line.length do 27 let c = line[i].char 28 if c <> ' ' && c <> '\n' then 29 return TextPos position.line i 30 31 TextPos position.line 0 32 33 let get_line_end_position (text : Text) (position : TextPos) = 34 let line = text.lines[position.line] 35 TextPos position.line (line.length - 1) 36 37 let insert (text_box : MultilineTextBox) (s : String) = 38 assert s.is_not_empty 39 text_box.is_changed = true 40 41 let display_text = text_box.display_text 42 let cursor_pos = text_box.cursor_pos 43 let last_pos : TextPos @late 44 case text_box.selection of 45 None -> 46 last_pos = display_text.insert cursor_pos s 47 let item = HistoryItem/AddStr 48 from = cursor_pos 49 to = last_pos 50 51 text_box.history.add item 52 cursor_pos = last_pos 53 54 Some (start, end) -> 55 let remove_item = HistoryItem/RemoveStr 56 position = start 57 =cursor_pos 58 s = display_text.range start end 59 60 text_box.history.add remove_item 61 display_text.remove_range start end 62 last_pos = display_text.insert start s 63 let add_item = HistoryItem/AddStr 64 from = start 65 to = last_pos 66 67 text_box.history.add add_item 68 selection = None 69 cursor_pos = last_pos 70 71 text_box.update_colors cursor_pos.line (last_pos.line + 1) 72 73 type MultilineTextBox 74 def TextEditControl.process_key (self 75 key : Key 76 action : KeyAction 77 mods : KeyModifiers) = 78 assert self.is_focused 79 if self.is_readonly && edit_keys.contains key then return 80 81 let font = self.font 82 let display_text = self.display_text 83 let scroller = self.scroller 84 let scroll_to (y : u32) = 85 scroller/move_to scroller None -y.as<i32> 86 87 let remove_at (position : TextPos) (cursor_pos : TextPos) = 88 self.is_changed = true 89 let item = HistoryItem/Remove 90 =position 91 =cursor_pos 92 c = display_text[position] 93 94 self.history.add item 95 display_text.remove position 96 self.update_colors position.line 97 98 if action <> KeyAction/Press && action <> KeyAction/Repeat then 99 return 100 101 case key of 102 Key/Backspace -> 103 let selection = self.selection 104 if self.cursor_pos <> TextPos@zero || selection.is_some 105 then 106 case selection of 107 None -> 108 let cursor_pos = self.cursor_pos 109 let remove_pos = display_text.prev cursor_pos 110 if cursor_pos.char == 0 then 111 assert display_text[remove_pos] == '\n' 112 113 remove_at remove_pos cursor_pos 114 self.cursor_pos = remove_pos 115 116 Some (l, r) -> 117 remove_range self l r self.cursor_pos 118 self.selection = None 119 cursor_pos = l 120 121 self.update_offset 122 123 Key/Left 124 | Key/Right 125 | Key/Up 126 | Key/Down 127 | Key/Home 128 | Key/End 129 | Key/PageUp 130 | Key/PageDown -> 131 let prev_cursor_pos = self.cursor_pos 132 let mut cursor_pos = prev_cursor_pos 133 case key of 134 Key/Left -> 135 if self.cursor_pos <> TextPos@zero then 136 cursor_pos = display_text.prev self.cursor_pos 137 self.show cursor_pos 138 139 Key/Right -> 140 if self.cursor_pos < display_text.max then 141 cursor_pos = display_text.next self.cursor_pos 142 self.show cursor_pos 143 144 Key/Up | Key/Down -> 145 let is_up = key == Key/Up 146 if not is_up || cursor_pos.line > 0 then 147 let position = if is_up 148 then cursor_pos.up 149 else cursor_pos.down 150 151 cursor_pos = self.coerce_pos position 152 153 if cursor_pos <> prev_cursor_pos then 154 let (up, down) = text_position_to_y self.text_display 155 cursor_pos 156 let line_height = font.line_height 157 if scroller.visible_y.down - line_height < down then 158 let y = down + line_height 159 + scroller.visible_y.up 160 - scroller.visible_y.down 161 162 scroll_to y 163 164 else if scroller.visible_y.up + line_height > up then 165 let y = if up > line_height 166 then up - line_height 167 else 0 168 169 scroll_to y 170 171 Key/Home -> 172 cursor_pos = get_first_character_position display_text 173 prev_cursor_pos 174 Key/End -> 175 cursor_pos = get_line_end_position display_text 176 prev_cursor_pos 177 Key/PageUp -> 178 let text_display = self.text_display 179 let (visible_up, visible_down) = scroller.visible_y 180 let n = visible_down - visible_up |> as<f32> 181 / font.line_height as f32 |> round as u32 182 183 let line = if cursor_pos.line > n 184 then cursor_pos.line - n 185 else 0 186 187 cursor_pos = self.coerce_pos (TextPos line cursor_pos.char) 188 189 let y = if visible_up > visible_down - visible_up then 190 let y = visible_up + visible_up - visible_down 191 192 (y - text_display.margin.up) / font.line_height 193 * font.line_height 194 else 195 0 196 197 scroll_to y 198 199 Key/PageDown -> 200 let text_display = self.text_display 201 let (visible_up, visible_down) = scroller.visible_y 202 let n = visible_down - visible_up 203 |> as<f32> / font.line_height as f32 204 |> round as u32 205 206 let position = TextPos 207 line = cursor_pos.line + n 208 char = cursor_pos.char 209 210 cursor_pos = self.coerce_pos position 211 let y = (visible_down - text_display.margin.up) 212 / font.line_height * font.line_height 213 214 scroll_to y 215 216 else -> fail 217 218 set_cursor_position self prev_cursor_pos cursor_pos 219 is_mouse_move = false 220 is_move = true 221 222 Key/Delete -> 223 let selection = self.selection 224 if display_text.is_not_empty 225 && (self.cursor_pos < display_text.max || selection.is_some) 226 then 227 let cursor_pos = self.cursor_pos 228 case selection of 229 None -> remove_at cursor_pos cursor_pos 230 Some (l, r) -> 231 remove_range self l r cursor_pos 232 self.selection = None 233 cursor_pos = l 234 235 self.update_offset 236 237 Key/Enter | Key/NumPadEnter -> 238 let cursor_pos = self.cursor_pos 239 let first_character = get_first_character_position display_text 240 cursor_pos 241 let indent = 242 if first_character.char == 0 243 && display_text.lines[cursor_pos.line].is_blank 244 then cursor_pos.char 245 else first_character.char.min cursor_pos.char 246 247 let sb = StringBuilder.new 248 sb.append '\n' 249 for _ = 0 until indent do 250 sb.append ' ' 251 252 insert self sb.as_string 253 self.show self.cursor_pos 254 255 Key/Tab -> insert self " " 256 Key/C if mods.contains KeyModifiers/Control -> 257 let window = self.os_window 258 if self.selection ? Some (start, end) then 259 let selected_text = display_text.range start end 260 window.set_clipboard selected_text 261 262 Key/X if mods.contains KeyModifiers/Control -> 263 let window = self.os_window 264 if self.selection ? Some (start, end) then 265 let selected_text = display_text.range start end 266 window.set_clipboard selected_text 267 remove_range self start end self.cursor_pos 268 self.selection = None 269 cursor_pos = start 270 271 Key/V if mods.contains KeyModifiers/Control -> 272 let window = self.os_window 273 let s = window.get_clipboard 274 if s.is_not_empty then 275 insert self s 276 self.show self.cursor_pos 277 278 Key/Z if mods.contains KeyModifiers/Control -> 279 let history = self.history 280 if history.is_not_empty then 281 if self.selection.is_some then 282 self.clear_selection 283 284 let item = history.last 285 case item of 286 is HistoryItem/Add -> 287 display_text.remove item.position 288 self.cursor_pos = item.position 289 update_colors item.position.line 290 291 is HistoryItem/AddStr -> 292 display_text.remove_range item.from item.to 293 self.cursor_pos = item.from 294 update_colors item.from.line 295 296 if item.from.line <> item.to.line then 297 self.update_colors (item.from.line + 1) 298 299 is HistoryItem/Remove -> 300 display_text.insert item.position item.c 301 self.cursor_pos = item.cursor_pos 302 update_colors item.position.line 303 304 is HistoryItem/RemoveStr -> 305 let last_pos = display_text.insert item.position item.s 306 self.cursor_pos = item.cursor_pos 307 update_colors item.position.line (last_pos.line + 1) 308 selection = (item.position, last_pos) 309 310 history.remove_at (history.size - 1) 311 self.is_changed = true 312 313 else -> () 314 315 def TextEditControl.process_char (c : Char) = 316 assert is_focused 317 if self.is_readonly then return 318 319 is_changed = true 320 if selection ? Some (l, r) then 321 remove_range self l r cursor_pos 322 cursor_pos = l 323 324 let item = HistoryItem/Add 325 position = cursor_pos 326 327 history.add item 328 display_text.insert cursor_pos c 329 set_cursor_position self cursor_pos (display_text.next cursor_pos) 330 is_mouse_move = false 331 is_move = false 332 333 update_colors cursor_pos.line 334