/
Text_Editor.cs
3833 lines (3366 loc) · 206 KB
/
Text_Editor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//All c# keywords by type
//https://www.w3schools.in/csharp-tutorial/keywords/
//https://www.w3schools.in/csharp-tutorial/operators-in-c/
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using Rect_Extension;
public class Text_Editor : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
string note = @"using System.Collections.Generic;
string sss; sss.Split().
List<string> lll; lll.ToArray().
Dictionary<string, int> ddd; ddd.
Notice
Что должно работать и на что обращать внимание
Навигация
---------
Влево/Вправо - может глюкнуть при переходе на предыдущую/следущую строчку, особенно при выделении (т.е. с зажатым шифтом)
так же может 'потерять' скроллинг (разсинхронизироватся со скроллбаром)
Вверх/Вниз - может глюкнуть при переходе с длинной строчки на короткую, когда позиция курсора должна менятся
PgUp/PgDown - может 'потерять' скроллинг (разсинхронизироватся со скроллбаром)
Home/End - тут вроде работает
Ctrl + Home/End - проверить как оно работает при выделении (ctrl + shift + Home/End)
Ctrl + Влево/Вправо - не уверен насчёт алгоритма...
Ctrl + Вверх/Вниз - может 'потерять' скроллинг
Scrolling
---------
Курсор никогда не должен залезать за границы вьюпорта. Если это происходит - оно автоскроллится. По хорошему.
Но возможно, в какой то мало продуманной ситуации (shift + ctrl + home или shift + pageDn какой ни будь), оно сломается.
Тупе
---------
Должны печататся все символы, цифры, буквы и пробел
Так же должны работать delete/backspace
Кнопка ентер должна правильно разрывать строку, и перемещать всё вниз
Табуляция заменяется тремя пробелами
При использовании этих кнопок и активном выделении - выделение должно стиратся
Tab / Shift+Tab - ident, должна работать даже при нескольких выделенных линяях
Key repeat
----------
Кнопки которые ТУПЕ и кнопки навигации должны повторятся с частотой в 0.15 sec по истечении 0.5 sec
Остальные кнопки повторятся не должны, но это особо и не проверишь. А если и повторяются, то, в принципе, пофиг...
Insert mode
-----------
Не уверен что должно происходить в этом режиме во время вставки/ентера/делита. О многом мог просто забыть :(.
Так же, при изменении текста с активным выделением не совсем понятно...
Copy / Cut / Paste
------------------
Должно работать.
Обратить внимание на мультистрочное копирование, вырезание и вставку. Реально может проглючить или в принципе не правильно работать
Выделение
---------
Ctrl + A - выделить всё. Тут не должно быть проблем
Shift + стрелки - проверить переходы с линии на линию
Shift + click - вроде работает как надо
Выделение мышой - тоже проверить переходы с линии на линию
Double click - select word
Triple click - select full row
У меня не получилось сделать универсально, и в итоге там аж шесть методов выделения
С лева на право и с права на лево, в каждом из этих двух по три вариации:
- выделение в пределах одной строки
- двух строк
- трёх и более строк
Все их не плохо бы проверить, на удаление выделенных фрагментов разными способами
(ctrl+x, backspace, delete или просто начать печатать буквы)
Scrolling
---------
Колесо мыши - вертикальный скроллинг
Alt + колесо - горизонтальный
Ctrl + колесо - change font size (optional)
Undo
----
Ctrl + z - точно глючит, но я не смог найти в какой момент, что бы стабильно воспроизводить.
Вроде бы, когда нажатием одной кнопки одновременно происходят несколько вещей
(backspace на первом символе строчки, delete при нескольких выделенных линяях и т.д.)
Хорошо бы найти ситуацию, в которой глючит.
//Test variables
var vvv = new Vector3();
Quaternion qqq = new Quaternion();
";
//Public parameters
public Font font;
public int font_size = 14;
public int tab_spaces = 3;
public float cursor_width = 3f;
public float cursor_blink_rate = 0.4f;
float cursor_blink_timer = 0f;
//Focus
public Activate_Scheme_Info Activate_Settings = new Activate_Scheme_Info();
bool mouse_is_over_me = false;
bool mouse_is_over_my_child = false;
[System.Serializable]
public class Activate_Scheme_Info {
public bool IsActive = true;
public Activate_Enum Activate = Activate_Enum.OnClick;
public DeActivate_Enum DeActivate = DeActivate_Enum.OnClickOutside;
public bool Hide_Cursor_When_Inactive = true;
public GameObject Activation_Border = null;
public enum Activate_Enum { OnClick, OnMouseOver, Never };
public enum DeActivate_Enum { OnClickOutside, OnMouseOut, Never };
}
//Modes
bool insert_mode = false;
enum nav_enum { Left, Right, Up, Down, PgUp, PgDn, Home, End, Ctrl_Home, Ctrl_End, Word_Left, Word_Right, Row_Up, Row_Down, Mouse_Scroll, Mouse_ScrollH, Submit }
//References
GameObject cursor; //Cursor
GameObject text_template = null; //Text object template
Transform txt_cnt = null; //Text container Transform
RectTransform txt_cnt_rt = null; //Text container RectTransform
//Internals text tracking
int current_row = 0;
int current_chr = 0;
int current_chr_previous = 0;
float lineHeight = 0f;
List<Text> text_rows = new List<Text>(); //Text components per row
List<List<float>> text_char_w = new List<List<float>>(); //Char width per row > per char
List<float> text_rows_w = new List<float>(); //Row width in pixel
float canvas_scale = 1f;
int scaled_font_size = 0;
Vector3 old_scale = Vector2.zero;
Vector2 old_size = Vector2.zero;
//Navigation
public bool allow_fontsize_change = true;
public Vector2Int fontsize_change_limit = new Vector2Int(6, 100);
//Key repeating
public float repeat_start = 0.7f;
public float repeat_ratio = 0.2f;
float repeat_start_timer = -1f;
float repeat_ratio_timer = -1f;
KeyCode repeat_key = KeyCode.None;
//Selection
bool ready_to_select = false;
int[] selection_start = new int[]{0,0};
int[] selection_end = new int[]{0,0};
GameObject selector; //Selector template
Dictionary<int, RectTransform> selector_per_row = new Dictionary<int, RectTransform>(); //Selectors per row
List<GameObject> selector_highlighters = new List<GameObject>();
//Undo
public int undo_limit = 50;
List< List<Undo_struct> > Undo_Buffer = new List< List<Undo_struct> >();
float undo_last_set_time = -9999f;
struct Undo_struct {
public undo_op operation;
public string txt;
public int line_ind;
public Vector2Int cursor_pos;
}
enum undo_op { modify, add, remove };
//DblClick
float dbl_click_max_delay = 0.25f;
float dbl_click_max_offset = 2f;
click_info_struct click_info = new click_info_struct();
struct click_info_struct {
public float last_time;
public Vector2 last_pos;
public bool dbl_clicked;
//public int click_count;
}
//Scroll bars
float cur_scroll_H = 0f;
float cur_scroll_V = 0f;
public Scrollbar ScrollH = null; //Horizontal scroll-bar
public Scrollbar ScrollV = null; //Vertical scroll-bar
//Typing keys
public string Auto_Close_Pairs = "[];();{};'';\"\"";
Dictionary<string, string> Auto_Close_Pairs_Dict = new Dictionary<string, string>();
Dictionary<KeyCode, char> keyinfo = new Dictionary<KeyCode, char>(); //All typing keys codes and characters
Dictionary<KeyCode, char> keyUpper = new Dictionary<KeyCode, char>(); //Uppercase char for keys
Dictionary<KeyCode, char> keyinfo_ru = new Dictionary<KeyCode, char>(); //All typing keys codes and characters
Dictionary<KeyCode, char> keyUpper_ru = new Dictionary<KeyCode, char>(); //Uppercase char for keys
//Colors
public Color_Scheme_Info Color_Scheme = new Color_Scheme_Info();
[System.Serializable]
public class Color_Scheme_Info {
public Color background = new Color32(30, 30, 30, 255);
public Color cursor = new Color32(255, 255, 255, 255);
public Color scrollbar_back = new Color32(30, 30, 30, 255);
public Color scrollbar_front = new Color32(66, 66, 66, 255);
public Color scrollbar_front_highlight = new Color32(88, 88, 88, 255);
public Color scrollbar_front_pressed = new Color32(109, 109, 109, 255);
public Color selection_color = new Color32(58, 61, 65, 255);
public Color highlight_default_color = new Color32(200, 32, 32, 180);
public Color highlight_breakpoint_color = new Color32(32, 32, 200, 180);
public Color t_std = new Color32(156, 220, 254, 255);
public Color t_sym = Color.white;
public Color t_quote = new Color32(255, 165, 0, 255);
public Color t_comment = new Color32(0, 128, 0, 255);
public Color t_keyword = new Color32(197, 134, 192, 255);
public Color t_modif = new Color32(86, 156, 214, 255);
public Color t_types = new Color32(78, 201, 176, 255);
public Color t_const = new Color32(220, 220, 170, 255);
public Color t_other = new Color32(209, 105, 105, 255);
public Color t_refl = new Color32(206, 145, 120, 255);
public Color Inactive_Tint = Color.white;
}
//Syntax highlighting
public bool Syntax_Highlight_Enable = true;
string charset_alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789";
string[] word_types = new string[]{ "class", "const", "new", "void", "bool", "byte", "char", "decimal", "double", "float", "int", "long", "object", "short", "string", "uint", "ulong", "ushort" };
string[] word_const = new string[]{ "false", "true", "null", "this" };
string[] word_modif = new string[]{ "override", "private", "protected", "public", "readonly", "sealed", "static", "switch", "throw", "try" };
string[] word_key = new string[]{ "break", "case", "catch", "continue", "do", "else", "finally", "for", "foreach", "goto", "if", "return", "while" };
string[] word_refl = new string[]{ "sizeof", "typeof" };
string[] word_other = new string[]{ "using", "enum", "struct" };
enum color_groups { none, alphanumeric, NON_alphanumeric, Quote_Dbl, space, comment };
List<Text> colored_text = new List<Text>();
List<KeyValuePair<Text, Text>> multiline_comments = new List<KeyValuePair<Text, Text>>();
Dictionary<Text, int> multiline_comments_rows = new Dictionary<Text, int>();
ConcurrentQueue<int> multiline_comments_recolor = new ConcurrentQueue<int>();
//Code suggestion
bool suggestion_mouse_enabled = true;
public Suggestion_Info Intellisense_Settings = new Suggestion_Info();
Vector2Int current_suggestion = new Vector2Int(-1, -1);
List<string> current_suggestion_signatures = new List<string>();
classes_dict intellisense_vars = new classes_dict();
List<classes_dict> intellisense_usings = new List<classes_dict>();
classes_dict classes_dictionary = new classes_dict();
Dictionary<string, string> builtin_types = new Dictionary<string, string>();
Regex paranthesis = new Regex(@"\(.*?\)", RegexOptions.Compiled);
Regex brackets = new Regex(@"\[.*?\]", RegexOptions.Compiled);
Regex brackets_content = new Regex(@"\[.+?\]", RegexOptions.Compiled);
Regex var_var = new Regex(@"var\s+(\w+)\s*=(\s*new)?\s*(([\w\.""](\s*<.*>)?(\s*\(.*?\))?((\[[\w|,]*\])*({.*})?)?)+)\s*;", RegexOptions.Compiled);
Regex var_common = new Regex(@"\s*(public[\s]*)?(static[\s]*)?([\w\.]+(\s*<.+>)?(\s*\[,*\])*\s+[\w]+)\s*(=?.*?);", RegexOptions.Compiled);
Regex var_inline = new Regex(@"(foreach|catch|using)\((.+)\)", RegexOptions.Compiled);
Regex method_declaration = new Regex(@"\s*(public[\s]*)?(static[\s]*)?([\w]+)(\s*<.+>)?(\s*\[,*\])*\s+([\w]+)\s*(\(.*\))", RegexOptions.Compiled);
Regex using_regex = new Regex("using\\s+([\\w\\.]+);", RegexOptions.Compiled);
BackgroundWorker check_var_thread = new BackgroundWorker();
ConcurrentQueue<changed_rows_info> changed_rows = new ConcurrentQueue<changed_rows_info>();
ConcurrentDictionary<string, variable_info[]> vars_rows_assoc = new ConcurrentDictionary<string, variable_info[]>();
ConcurrentDictionary<Text, string[]> vars_rows_assoc_rev = new ConcurrentDictionary<Text, string[]>();
ConcurrentDictionary<classes_dict, Text[]> using_rows_assoc = new ConcurrentDictionary<classes_dict, Text[]>();
ConcurrentDictionary<Text, classes_dict[]> using_rows_assoc_rev = new ConcurrentDictionary<Text, classes_dict[]>();
class classes_dict
{
public Dictionary<string, classes_dict> dict = new Dictionary<string, classes_dict>(StringComparer.OrdinalIgnoreCase);
public Type_Enum type = Type_Enum.T_NotSet;
public string full_name = "";
public string return_type = "";
public string return_type_full = "";
public enum Type_Enum { N_Namespace, T_NotSet, T_class, T_Interface, T_struct, T_enum, T_Delegate, P_Property, P_Indexer, M_Method, M_ExtMethod, F_Field, F_CONST, F_READONLY, E_Event, Enum_Value };
public string method_signature = "";
public bool is_static = false;
public List<string> derived_from = new List<string>();
public List<classes_dict> derived_from_cl = new List<classes_dict>();
public List<KeyValuePair<string, classes_dict>> T2 = new List<KeyValuePair<string, classes_dict>>();
public classes_dict Make_Copy() {
classes_dict new_cldt = new classes_dict();
new_cldt.dict = this.dict;
new_cldt.type = this.type;
new_cldt.full_name = this.full_name;
new_cldt.return_type = this.return_type;
new_cldt.return_type_full = this.return_type_full;
new_cldt.method_signature = this.method_signature;
new_cldt.is_static = this.is_static;
new_cldt.derived_from = this.derived_from;
new_cldt.derived_from_cl = this.derived_from_cl;
new_cldt.T2 = this.T2.ToList();
return new_cldt;
}
}
class changed_rows_info {
public changed_rows_info(Text r, string txt) { associated_row = r; text = txt; }
public changed_rows_info(Text r, string txt, op_type o) { associated_row = r; text = txt; op = o; }
public string text;
public Text associated_row;
public op_type op = op_type.changed;
public enum op_type { changed, deleted, created };
}
class variable_info {
//public variable_info(Text r, classes_dict d) { row = r; classes_dict = d; }
public variable_info(Text r, classes_dict d, int[] c, bool inl) { row = r; classes_dict = d; context = c; is_inline = inl; }
public Text row;
public classes_dict classes_dict;
public int[] context = new int[]{};
public bool is_inline = false;
}
[TextArea(6,10)] public string exclude_namespaces = "";
[System.Serializable]
public struct Suggestion_Info {
public Sprite icon_not_set;
public Sprite icon_namespace;
public Sprite icon_class;
public Sprite icon_interface;
public Sprite icon_struct;
public Sprite icon_enum;
public Sprite icon_enum_value;
public Sprite icon_method;
public Sprite icon_method_extension;
public Sprite icon_property;
public Sprite icon_delegate;
public Sprite icon_field;
public Sprite icon_field_readonly;
public Sprite icon_field_const;
public Sprite icon_event;
public RectTransform suggestion_rt; //Suggestion panel
public GameObject suggestion_item; //Suggestion item_template
public Scrollbar suggestion_scroll;
public Text method_signature_text;
public bool get_assemblies_from_file;
public bool get_assemblies_from_reflection;
public bool write_assemblies_to_file;
public bool write_namespaces_to_file;
public int max_suggestion_count;
public string add_using;
}
//Events
public EventHandler OnTextChanged;
public UnityEvent On_Intellisense_BeginInit = null;
public UnityEvent On_Intellisense_EndInit = null;
ConcurrentQueue<UnityEvent> Event_Queue = new ConcurrentQueue<UnityEvent>();
//Code
Dictionary<Text, GameObject> breakpoints = new Dictionary<Text, GameObject>();
//TODO: This is windows only solution, and that's bad :(
[System.Runtime.InteropServices.DllImport("USER32.dll")] public static extern short GetKeyState(int nVirtKey);
//68748313 - ru
//https://stackoverflow.com/questions/14701095/how-to-get-keyboard-layout-name-from-a-keyboard-layout-identifier
//[System.Runtime.InteropServices.DllImport("User32.dll")] public static extern IntPtr GetKeyboardLayout(int idThread);
[System.Runtime.InteropServices.DllImport("user32.dll")] private static extern long GetKeyboardLayoutName(System.Text.StringBuilder pwszKLID);
// Start is called before the first frame update
void Awake()
{
//Objects
txt_cnt = transform.GetChild(0);
txt_cnt_rt = txt_cnt.GetComponent<RectTransform>();
//Fill typing codes and chars
string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
foreach (char c in letters) keyinfo.Add( (KeyCode)System.Enum.Parse(typeof(KeyCode), c.ToString()), c.ToString().ToLower()[0] );
string numbers = "1234567890";
foreach (char c in numbers) {
keyinfo.Add( (KeyCode)System.Enum.Parse(typeof(KeyCode), "Alpha" + c.ToString()), c );
keyinfo.Add( (KeyCode)System.Enum.Parse(typeof(KeyCode), "Keypad" + c.ToString()), c );
}
keyinfo.Add(KeyCode.BackQuote, '`');
keyinfo.Add(KeyCode.Minus, '-'); keyinfo.Add(KeyCode.Equals, '='); keyinfo.Add(KeyCode.Backslash, '\\');
keyinfo.Add(KeyCode.LeftBracket, '['); keyinfo.Add(KeyCode.RightBracket, ']');
keyinfo.Add(KeyCode.Semicolon, ';'); keyinfo.Add(KeyCode.Quote, '\'');
keyinfo.Add(KeyCode.Comma, ','); keyinfo.Add(KeyCode.Period, '.'); keyinfo.Add(KeyCode.Slash, '/');
keyinfo.Add(KeyCode.Space, ' ');
keyinfo.Add(KeyCode.Tab, '\t');
keyinfo.Add(KeyCode.KeypadDivide, '/'); keyinfo.Add(KeyCode.KeypadMultiply, '*');
keyinfo.Add(KeyCode.KeypadMinus, '-'); keyinfo.Add(KeyCode.KeypadPlus, '+'); keyinfo.Add(KeyCode.KeypadPeriod, '.');
string numbersU = ")!@#$%^&*(";
for (int i = 0; i < 10; i++) keyUpper.Add( (KeyCode)System.Enum.Parse(typeof(KeyCode), "Alpha" + i.ToString()), numbersU[i] );
keyUpper.Add(KeyCode.BackQuote, '~');
keyUpper.Add(KeyCode.Minus, '_'); keyUpper.Add(KeyCode.Equals, '+'); keyUpper.Add(KeyCode.Backslash, '|');
keyUpper.Add(KeyCode.LeftBracket, '{'); keyUpper.Add(KeyCode.RightBracket, '}');
keyUpper.Add(KeyCode.Semicolon, ':'); keyUpper.Add(KeyCode.Quote, '"');
keyUpper.Add(KeyCode.Comma, '<'); keyUpper.Add(KeyCode.Period, '>'); keyUpper.Add(KeyCode.Slash, '?');
//cyrillic
keyinfo_ru = new Dictionary<KeyCode, char>(keyinfo);
keyUpper_ru = new Dictionary<KeyCode, char>(keyUpper);
string letters_ru = "ФИСВУАПРШОЛДЬТЩЗЙКЫЕГМЦЧНЯ";
for (int c = 0; c < letters.Length; c++) keyinfo_ru[(KeyCode)System.Enum.Parse(typeof(KeyCode), letters[c].ToString())] = letters_ru[c].ToString().ToLower()[0];
keyinfo_ru[KeyCode.Quote] = 'э'; keyinfo_ru[KeyCode.BackQuote] = 'ё'; keyinfo_ru[KeyCode.Semicolon] = 'ж';
keyinfo_ru[KeyCode.Comma] = 'б'; keyinfo_ru[KeyCode.Period] = 'ю';
keyinfo_ru[KeyCode.LeftBracket] = 'х'; keyinfo_ru[KeyCode.RightBracket] = 'ъ';
keyinfo_ru[KeyCode.Slash] = '.';
string numbersU_ru = ")!\"№;%:?*(";
for (int i = 0; i < 10; i++) keyUpper_ru[(KeyCode)System.Enum.Parse(typeof(KeyCode), "Alpha" + i.ToString())] = numbersU_ru[i];
keyUpper_ru[KeyCode.Quote] = 'Э'; keyUpper_ru[KeyCode.BackQuote] = 'Ё'; keyUpper_ru[KeyCode.Semicolon] = 'Ж';
keyUpper_ru[KeyCode.Comma] = 'Б'; keyUpper_ru[KeyCode.Period] = 'Ю';
keyUpper_ru[KeyCode.LeftBracket] = 'Х'; keyUpper_ru[KeyCode.RightBracket] = 'Ъ';
keyUpper_ru[KeyCode.Slash] = ','; keyUpper_ru[KeyCode.Backslash] = '/';
//Create cursor
cursor = new GameObject("cursor");
cursor.transform.SetParent(txt_cnt);
RawImage img = cursor.AddComponent<RawImage>();
Texture2D tex = new Texture2D(1, 1);
tex.SetPixel(0, 0, Color.black); tex.Apply();
img.texture = tex;
RectTransform rt = cursor.GetComponent<RectTransform>();
rt.localScale = new Vector3(1f, 1f, 1f);
rt.localRotation = Quaternion.identity;
rt.pivot = new Vector2(0f, 1f);
rt.anchorMin = new Vector2(0f, 1f);
rt.anchorMax = new Vector2(0f, 1f);
rt.sizeDelta = new Vector2(cursor_width, 10f);
rt.anchoredPosition3D = Vector3.zero;
//Create selector
GameObject selector_cnt = new GameObject("Selectors_container");
selector_cnt.transform.SetParent(txt_cnt);
selector_cnt.transform.SetSiblingIndex(0);
selector_cnt.transform.localRotation = Quaternion.identity;
rt = selector_cnt.AddComponent<RectTransform>();
rt.anchoredPosition3D = Vector3.zero;
rt.anchorMin = new Vector2(0f, 0f);
rt.anchorMax = new Vector2(1f, 1f);
rt.pivot = new Vector2(0.5f, 0.5f);
rt.offsetMin = new Vector2(0f, 0f);
rt.offsetMax = new Vector2(0f, 0f);
rt.localScale = new Vector3(1f, 1f, 1f);
selector = Instantiate(cursor, selector_cnt.transform);
selector.name = "Selector";
Texture2D tex2 = new Texture2D(1, 1);
tex2.SetPixel(0, 0, new Color32(128, 128, 255, 200) ); tex2.Apply();
selector.GetComponent<RawImage>().texture = tex2;
selector.SetActive(false);
//Create text
text_template = new GameObject("TextRow_Template");
text_template.transform.SetParent(txt_cnt);
Text t = text_template.AddComponent<Text>();
t.font = font;
t.fontSize = font_size;
t.color = Color.black;
t.horizontalOverflow = HorizontalWrapMode.Overflow;
t.raycastTarget = false;
rt = text_template.GetComponent<RectTransform>();
rt.pivot = new Vector2(0f, 0.5f);
rt.anchorMin = new Vector2(0f, 0f);
rt.anchorMax = new Vector2(1f, 1f);
rt.sizeDelta = new Vector2(0f, 0f);
rt.anchoredPosition3D = Vector3.zero;
rt.localScale = new Vector3(1f, 1f, 1f);
rt.localRotation = Quaternion.identity; //This is needed, if text_editor is used in world space canvas
//Adjust cursor size to line height
Canvas cnv = GetComponentInParent<Canvas>();
var extents = t.cachedTextGenerator.rectExtents.size * 0.5f;
lineHeight = t.cachedTextGeneratorForLayout.GetPreferredHeight("A", t.GetGenerationSettings(extents)) / cnv.scaleFactor;
cursor.GetComponent<RectTransform>().sizeDelta = new Vector2(cursor_width, lineHeight);
//Canvas scaling handling
canvas_scale = cnv.transform.localScale.x;
scaled_font_size = Mathf.FloorToInt(font_size * canvas_scale);
text_template.SetActive(false);
//Scrollbars events
if (ScrollH != null) ScrollH.onValueChanged.AddListener((v)=> On_Scroll_H(v) );
if (ScrollV != null) ScrollV.onValueChanged.AddListener((v)=> On_Scroll_V(v) );
Color_Scheme_Apply();
Create_New_Row();
//To get character width call .font.GetCharacterInfo(chr, out ci, text_rows[current_row].fontSize);
//But it will be set to 0 if canvas is not updated for this character
//ForceUpdateCanvases is needed because a character needs to be rendered before we can get width
//Here we render all possible characters and update the canvas once, instead of doing it each time the key is pressed
//BUT - it's not working :(
// string str = "";
// foreach (var kv in keyinfo) str += kv.Value;
// foreach (var kv in keyUpper) str += kv.Value;
// text_rows[0].text = str;
// Canvas.ForceUpdateCanvases();
// text_rows[0].font.RequestCharactersInTexture(str, text_rows[0].fontSize, text_rows[0].fontStyle);
// text_rows[0].text = "";
Update_AutoclosePairs();
// foreach (string s in Auto_Close_Pairs.Split(new char[]{';'}, StringSplitOptions.RemoveEmptyEntries) ) {
// Auto_Close_Pairs_Dict.Add(s[0].ToString(), s[1].ToString());
// }
//Intellisense_Init();
Close_Intellisense();
if (Intellisense_Settings.suggestion_item != null) Intellisense_Settings.suggestion_item.SetActive(false);
if (Intellisense_Settings.method_signature_text != null) Intellisense_Settings.method_signature_text.enabled = false;
Intellisense_AddUsing("FromSettings");
builtin_types.Add("bool", "System.Boolean"); builtin_types.Add("byte", "System.Byte"); builtin_types.Add("sbyte", "System.SByte");
builtin_types.Add("char", "System.Char"); builtin_types.Add("decimal", "System.Decimal"); builtin_types.Add("double ", "System.Double");
builtin_types.Add("float", "System.Single"); builtin_types.Add("int", "System.Int32"); builtin_types.Add("uint", "System.UInt32");
builtin_types.Add("long", "System.Int64"); builtin_types.Add("ulong", "System.UInt64"); builtin_types.Add("object", "System.Object");
builtin_types.Add("short", "System.Int16"); builtin_types.Add("ushort", "System.UInt16"); builtin_types.Add("string", "System.String");
var keys = builtin_types.Keys.ToArray(); foreach (var k in keys) builtin_types.Add(k.Trim()+"[]", builtin_types[k].Trim()+"[]");
if (Intellisense_Settings.max_suggestion_count <= 0) Intellisense_Settings.max_suggestion_count = 20;
text = note.Replace("\r", "");
current_row = 0;
Cursor_Update_Position();
//Hack to use without Engine script in scene
if (GameObject.Find("Compiler") == null && GameObject.Find("Cube_Center") == null) {
Engine.Hotkeys.Add(Engine.Key.Set_Breakpoint, new KeyCode[][]{ new KeyCode[]{KeyCode.LeftAlt, KeyCode.B} });
Engine.Hotkeys.Add(Engine.Key.Intellisense_Up, new KeyCode[][]{ new KeyCode[]{KeyCode.LeftAlt, KeyCode.UpArrow} });
Engine.Hotkeys.Add(Engine.Key.Intellisense_Down, new KeyCode[][]{ new KeyCode[]{KeyCode.LeftAlt, KeyCode.DownArrow} });
Engine.Hotkeys.Add(Engine.Key.Intellisense_Submit, new KeyCode[][]{ new KeyCode[]{KeyCode.Tab}, new KeyCode[]{KeyCode.LeftAlt, KeyCode.Return}, new KeyCode[]{KeyCode.LeftAlt, KeyCode.KeypadEnter}});
Engine.Hotkeys.Add(Engine.Key.Intellisense_Enable, new KeyCode[][]{ new KeyCode[]{KeyCode.LeftAlt, KeyCode.LeftControl, KeyCode.I}});
}
//Add child elements script to handle mouseOver/mouseOut of overlayed objects
Text_Editor_Child_Element tece;
if (ScrollH != null) { tece = ScrollH.gameObject.AddComponent<Text_Editor_Child_Element>(); tece.te = this; }
if (ScrollV != null) { tece = ScrollV.gameObject.AddComponent<Text_Editor_Child_Element>(); tece.te = this; }
if (Intellisense_Settings.suggestion_rt != null) {
tece = Intellisense_Settings.suggestion_rt.gameObject.AddComponent<Text_Editor_Child_Element>(); tece.te = this;
}
if (Intellisense_Settings.suggestion_item != null) {
var tesi = Intellisense_Settings.suggestion_item.gameObject.AddComponent<Text_Editor_Suggestion_Item>(); tesi.te = this;
}
check_var_thread.DoWork += Intellisense_var_check_bg; check_var_thread.RunWorkerAsync();
if (Activate_Settings.Activate != Activate_Scheme_Info.Activate_Enum.OnMouseOver)
{ if (Activate_Settings.IsActive) Activate(); else DeActivate(); }
else
{ DeActivate(); }
}
// Update is called once per frame
void Update()
{
//TEST
//if(Input.GetKeyDown(KeyCode.Z)) { Scroll_To_Line(50); return; }
UnityEvent ue;
while (Event_Queue.TryDequeue(out ue)) {
//Intellisense_begin_init and Intellisense_end_init events, queued from background thread
if (ue != null) ue.Invoke();
}
if (Engine.check_key(Engine.Key.Set_Breakpoint)) { SetBreakpoint(); }
if (Engine.check_key(Engine.Key.Intellisense_Enable)) { enable_intellisense = !enable_intellisense; }
//Recalculate text width on canvas size change (when game window is resized)
if (transform.hasChanged) {
transform.hasChanged = false;
var cur_scale = GetComponent<RectTransform>().lossyScale;
if (cur_scale != old_scale) { old_scale = cur_scale; Recalculate_text_width(); }
}
//TODO: When vertical scroll down is possible, clicking at the bottom-most line, will invoke selection, because of cursor adjust which will reinvoke scroll adjust
//TODO: Intellisense: for (int iii;) { } iii.--- --> if all block is on the same row, iii still be suggested
//
//CAN'T REPRODUCE: If ctrl + X longest line and HScroll > 0, hscroll size is set to full, but scrolling still on the right
//QUESTIONABLE: Selection of carriage-return symbol (last symbol on the line) for copying/pasting/deleting
//
//DONE: Tab button handle
//DONE: Clicks on the end of the line not detected, if the line is bigger then one screen
//DONE: Caps lock status (caps lock should only uppercase letters, not symbols)
//DONE: Backspace/Delete (including first/last char handling and another line altering)
//DONE: Selection (mouse and shift+arrows/home/end)
//DONE: Repeat by keyCode, not by typed char (for repeating arrows, baclspaces e.t.c.)
//DONE: Vertical scroll
//DONE: PgUp/PgDwn
//DONE: Ctrl + Home/End
//DONE: Insert mode
//DONE: BUG: type line, press shift+home, delete, shift+end = error
//DONE: Typing should erase selection entirely
//DONE: Enter should erase selection
//DONE: Copy/Cut/Paste
//DONE: Backspace and delete should erase selection entirely
//DONE: Arrow keys should change line when pressed at beginning/end of line
//DONE: ctrl + X multiple lines does not delete empty lines
//DONE: ctrl + A = select all
//DONE: BUG: shift+up - up arrow still be repeated even when released
//DONE: BUG: type short line and 2nd longer line. At start of line 0 select to bottom (shift+down), then press 'END' multiple times (while still holding shift). SOMETIMES it throws an error (it's related to shift+up/down repeating bug, but if down & end pressed in same frame current_row check should be done anyway)
//DONE: Ctrl + up/down = VScroll per row
//DONE: Ctrl + Arrows for word navigation
//DONE: Go to the end of 2nd line (should be longer or equal then first one), press shift+up = selector shows at 0,0
//DONE: Go to the end of 2nd line (should be longer or equal then first one), press shift+up, select some leters to the left (shift-left), now while still holding shift press 'END' - selection is not deselected
//DONE: Go to the end of 2nd line, press shift+up, select some leters to the left. Now press right while holding shift to deselect. Last letter on this line can not be deselected.
//DONE: Select a line to the bottom (shift+down) - first letter on this line can not be deselected
//DONE: When select multiple rows, first/last charcter on the last/first selected row can not be deselected
//DONE: V-Scroll size не всегда расширяется (и сужается?)
//DONE: Mouse V-scroll
//DONE: Click below viewport height (when scrolled to bottom a little bit) not detected
//DONE: ctrl + Z = undo
//DONE: Intellisense: when typing '1234A' should not give suggestion
//DONE: Syntax higlight: higlighting 'if' in 'modification'
//DONE: Scrollbar slider size not calculated correctly?
//DONE: Mouse VScroll navigation should allow cursor to go out of viewport
//DONE: Intellisense: suggestion mouse scroll
//DONE: Intellisense: suggestion mouse click
//DONE: Alt + mouse scroll to scroll horizontally
//DONE: Ctrl + up/down = VScroll per row navigation should not stick cursor and allow cursor to go out of viewport
//DONE: When requested cursor x_pos is grater then line_width, need to store requested x_pos to apply it when navigating to another line
//DONE: Ctrl + mouse scroll to change font size
//DONE: Triple click to select full row
Cursor_Blink();
bool read_only = Control_UI.isInPauseState() || Control_UI.isPlaying();
//Activation / Deactivation
#region "Activation / Deactivation OnMouseClick"
if (Input.GetKeyDown(KeyCode.Mouse0)) {
//Debug.Log("Mouse over me = " + mouse_is_over_me);
if (Activate_Settings.IsActive) {
if (!mouse_is_over_me && Activate_Settings.DeActivate == Activate_Scheme_Info.DeActivate_Enum.OnClickOutside) DeActivate();
} else {
if (mouse_is_over_me && Activate_Settings.Activate == Activate_Scheme_Info.Activate_Enum.OnClick) Activate();
}
}
if (!Activate_Settings.IsActive) return;
#endregion
#region "Key repeat handler"
if (repeat_start_timer >= 0) repeat_start_timer += Time.unscaledDeltaTime;
if (repeat_ratio_timer >= 0) repeat_ratio_timer += Time.unscaledDeltaTime;
//Reset repeat timer on KeyUp
if (repeat_start_timer >= 0) {
if ((int)repeat_key < 1000) { if (!Input.GetKey(repeat_key)) repeat_start_timer = -1f; }
else if ((int)repeat_key == 10001) { if (!Engine.check_key(Engine.Key.Intellisense_Up, false)) repeat_start_timer = -1f; }
else if ((int)repeat_key == 10002) { if (!Engine.check_key(Engine.Key.Intellisense_Down, false)) { repeat_start_timer = -1f; } }
}
//Repeating character
KeyCode repeat = KeyCode.None;
if (repeat_start_timer >= repeat_start && repeat_ratio_timer >= repeat_ratio) {
repeat_ratio_timer = 0f;
repeat = repeat_key;
}
#endregion
//If suggestion is active and suggestion key is hit - submit suggestion
#region "Intellisense navigation"
if (Intellisense_Settings.suggestion_rt != null && Intellisense_Settings.suggestion_rt.gameObject.activeSelf) {
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0) suggestion_mouse_enabled = true;
if (Engine.check_key(Engine.Key.Intellisense_Submit) && !read_only) { Intellisense_Navigation(nav_enum.Submit); return; }
if (repeat == (KeyCode)10001 || Engine.check_key(Engine.Key.Intellisense_Up)) { Intellisense_Navigation(nav_enum.Up); suggestion_mouse_enabled = false; }
if (repeat == (KeyCode)10002 || Engine.check_key(Engine.Key.Intellisense_Down)) { Intellisense_Navigation(nav_enum.Down); suggestion_mouse_enabled = false; }
}
#endregion
bool alt_Pressed = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
bool ctrl_Pressed = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
bool shift_pressed = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
//Handle letter/number/symbols keys
#region "Handle letter/number/symbols keys"
string type = "";
bool isCapsLockOn = (GetKeyState(0x14) & 1) > 0;
bool pair_autoclosed = false;
if (!ctrl_Pressed && !alt_Pressed) {
var key_arr = keyinfo;
var key_arr_U = keyUpper;
//Language
var lang = new System.Text.StringBuilder(9);
GetKeyboardLayoutName(lang);
if (lang.ToString() == "00000419") { key_arr = keyinfo_ru; key_arr_U = keyUpper_ru; }
foreach (var ki in key_arr) {
if (Input.GetKeyDown(ki.Key) || repeat == ki.Key) {
type = ki.Value.ToString();
if (shift_pressed) {
if (key_arr_U.ContainsKey(ki.Key)) type = key_arr_U[ki.Key].ToString(); else type = type.ToUpper();
}
//Caps lock should only uppercase letters, and not affect symbols
if (isCapsLockOn) {
if(!shift_pressed) type = type.ToUpper(); else type = type.ToLower();
}
Character_Repeat_Handler("SET", ki.Key);
//Autoclose pairs
if (Auto_Close_Pairs_Dict.ContainsValue(type) && Selection_Get_Text() == "") {
string t = text_rows[current_row].text;
if (t.Length > current_chr && t[current_chr] == type[0]) { current_chr++; Cursor_Update_Position(); return; }
}
if (Auto_Close_Pairs_Dict.ContainsKey(type) && Selection_Get_Text() == "" ) {
type += Auto_Close_Pairs_Dict[type]; pair_autoclosed = true;
}
break;
}
}
}
#endregion
//Clipboard copy/cut/paste
#region "Clipboard copy/cut/paste"
if (Input.GetKeyDown(KeyCode.C) && ctrl_Pressed) {
GUIUtility.systemCopyBuffer = Selection_Get_Text();
}
if (Input.GetKeyDown(KeyCode.X) && ctrl_Pressed) {
if (read_only) { Selection_Get_Text(); Hud.ShowImportantMessage("Can't edit while script is running."); }
else { GUIUtility.systemCopyBuffer = Selection_Get_Text(true); }
}
if (Input.GetKeyDown(KeyCode.V) && ctrl_Pressed && !read_only) {
type = GUIUtility.systemCopyBuffer.Replace("\r\n", "\n");
}
#endregion
//Enter
#region "Enter"
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || repeat == KeyCode.Return || repeat == KeyCode.KeypadEnter) {
if (read_only) { Hud.ShowImportantMessage("Can't edit while script is running."); return; }
if (!alt_Pressed) {
string sel = Selection_Get_Text(true);
if (current_chr < text_rows[current_row].text.Length) {
//If enter is pressed in the middle of the row - split row and move right part to the next row
Row_Split(current_row, current_chr);
Syntax_Highlight ( current_row );
Syntax_Highlight ( current_row + 1 );
} else {
Create_New_Row(current_row + 1);
}
if (sel == "") {
//If text was selected, changed_rows is added in Selection_Get_Text()
changed_rows.Enqueue( new changed_rows_info(text_rows[current_row], text_rows[current_row].text) );
changed_rows.Enqueue( new changed_rows_info(text_rows[current_row+1], text_rows[current_row+1].text, changed_rows_info.op_type.created) );
}
current_row++; current_chr = 0;
Cursor_Update_Position(); Selection_Clear(); Cursor_Blink(true); Update_Scroll_V_Size(); Update_Scroll_H_Size();
Close_Intellisense();
if (Input.GetKeyDown(KeyCode.Return)) Character_Repeat_Handler("SET", KeyCode.Return); else Character_Repeat_Handler("SET", KeyCode.KeypadEnter);
} else {
Intellisense_Navigation(nav_enum.Submit);
}
}
#endregion
//Backspace
#region "Backspace"
if (Input.GetKeyDown(KeyCode.Backspace) || repeat == KeyCode.Backspace) {
if (read_only) { Hud.ShowImportantMessage("Can't edit while script is running."); return; }
if (Selection_Get_Text(true).Length == 0) {
if (current_chr > 0) {
Undo_Add( undo_op.modify, current_row );
current_chr--;
text_rows[current_row].text = text_rows[current_row].text.Remove(current_chr, 1);
float w = text_char_w[current_row][current_chr];
text_char_w[current_row].RemoveAt(current_chr);
text_rows_w[current_row] -= w;
CheckBreakpoint(current_row, undo_op.modify);
} else if (current_row > 0) {
current_row--;
Undo_Add( undo_op.modify, current_row );
current_chr = text_char_w[current_row].Count();
text_rows[current_row].text += text_rows[current_row+1].text;
text_char_w[current_row].AddRange(text_char_w[current_row+1]);
text_rows_w[current_row] += text_rows_w[current_row+1];
Row_Erase(current_row + 1);
}
Syntax_Highlight ( current_row );
changed_rows.Enqueue( new changed_rows_info(text_rows[current_row], text_rows[current_row].text) );
}
Cursor_Update_Position(); Selection_Clear(); Cursor_Blink(true); Update_Scroll_V_Size(); Update_Scroll_H_Size();
Close_Intellisense();
Character_Repeat_Handler("SET", KeyCode.Backspace);
}
#endregion
//Delete
#region "Delete"
if (Input.GetKeyDown(KeyCode.Delete) || repeat == KeyCode.Delete) {
if (read_only) { Hud.ShowImportantMessage("Can't edit while script is running."); return; }
if (Selection_Get_Text(true).Length == 0) {
if (current_chr < text_char_w[current_row].Count()) {
//Deleting a character
Undo_Add( undo_op.modify, current_row );
text_rows[current_row].text = text_rows[current_row].text.Remove(current_chr, 1);
float w = text_char_w[current_row][current_chr];
text_char_w[current_row].RemoveAt(current_chr);
text_rows_w[current_row] -= w;
CheckBreakpoint(current_row, undo_op.modify);
} else if (current_row < text_rows_w.Count()-1) {
//This is a last character - Deleting a row
Undo_Add( undo_op.modify, current_row );
Undo_Add( undo_op.modify, current_row + 1 );
text_rows[current_row].text += text_rows[current_row+1].text;
text_char_w[current_row].AddRange(text_char_w[current_row+1]);
text_rows_w[current_row] += text_rows_w[current_row+1];
//We are deleting the next row, with transfering its text to the current row
// so, if we had a breakpoint at that row - move it one row up
if (text_rows[current_row].text.Trim() != "") {
if (breakpoints.ContainsKey(text_rows[current_row+1]) && !breakpoints.ContainsKey(text_rows[current_row])) {
SetBreakpoint();
}
}
Row_Erase(current_row + 1);
}
Syntax_Highlight ( current_row );
changed_rows.Enqueue( new changed_rows_info(text_rows[current_row], text_rows[current_row].text) );
}
Cursor_Update_Position(); Selection_Clear(); Cursor_Blink(true); Update_Scroll_V_Size(); Update_Scroll_H_Size();
Close_Intellisense();
Character_Repeat_Handler("SET", KeyCode.Delete);
}
#endregion
//Undo
#region "Undo"
if ((Input.GetKeyDown(KeyCode.Z) && ctrl_Pressed)) {
if (read_only) { Hud.ShowImportantMessage("Can't edit while script is running."); return; }
Undo_Restore();
Cursor_Update_Position(); Selection_Clear(); Cursor_Blink(true); Update_Scroll_V_Size(); Update_Scroll_H_Size();
Close_Intellisense();
if (OnTextChanged != null) OnTextChanged(this, new EventArgs());
}
#endregion
//Arrows/Home/End navigation and Mouse scroll
#region "Arrows/Home/End navigation and Mouse scroll"
if (Input.GetKeyDown(KeyCode.LeftArrow) || repeat == KeyCode.LeftArrow) { Navigate(nav_enum.Left); }
if (Input.GetKeyDown(KeyCode.RightArrow) || repeat == KeyCode.RightArrow) { Navigate(nav_enum.Right); }
if (Input.GetKeyDown(KeyCode.UpArrow) || repeat == KeyCode.UpArrow) { if (!alt_Pressed) Navigate(nav_enum.Up); }
if (Input.GetKeyDown(KeyCode.DownArrow) || repeat == KeyCode.DownArrow) { if (!alt_Pressed) Navigate(nav_enum.Down); }
if (Input.GetKeyDown(KeyCode.PageUp) || repeat == KeyCode.PageUp) { Navigate(nav_enum.PgUp); }
if (Input.GetKeyDown(KeyCode.PageDown) || repeat == KeyCode.PageDown) { Navigate(nav_enum.PgDn); }
if (Input.GetKeyDown(KeyCode.Home)) { Navigate(nav_enum.Home); }
if (Input.GetKeyDown(KeyCode.End)) { Navigate(nav_enum.End); }
if (Input.GetKeyDown(KeyCode.Home) && ctrl_Pressed) { Navigate(nav_enum.Ctrl_Home); }
if (Input.GetKeyDown(KeyCode.End) && ctrl_Pressed) { Navigate(nav_enum.Ctrl_End); }
if (Input.mouseScrollDelta.y != 0f) {
if (ctrl_Pressed && allow_fontsize_change) {
font_size += Mathf.RoundToInt(Input.mouseScrollDelta.y);
if (font_size < fontsize_change_limit.x) font_size = fontsize_change_limit.x;
if (font_size > fontsize_change_limit.y) font_size = fontsize_change_limit.y;
Update_Text_Size();
}
else if (!alt_Pressed) { Navigate(nav_enum.Mouse_Scroll); }
else { Navigate(nav_enum.Mouse_ScrollH); }
}
#endregion
//Select all
if (ctrl_Pressed && Input.GetKeyDown(KeyCode.A)) {
current_row = text_rows_w.Count-1;
current_chr = text_rows[current_row].text.Length;
selection_start = new int[] {0,0};
selection_end = new int[]{ current_row, current_chr };
Cursor_Update_Position();
Selection_Draw(selection_start, selection_end);
Close_Intellisense();
}
//Mode
if (Input.GetKeyDown(KeyCode.Insert)) {
insert_mode = !insert_mode;
Cursor_Update_Position(); Cursor_Blink(true);
}
//Mouse click
#region "Mouse positioning and selectioning"
if (Input.GetKeyDown(KeyCode.Mouse0) && !mouse_is_over_my_child) {
int[] coord = Char_Index_Under_Mouse();
if (coord[0] >= 0 && coord[1] >= 0) {
current_row = coord[0];
current_chr = coord[1];
Cursor_Update_Position(); Cursor_Blink(true);
ready_to_select = true;
if (!shift_pressed) selection_start = coord;
else { selection_end = coord; Selection_Draw(selection_start, selection_end); }
Close_Intellisense();
//Dbl_click
if (Time.time <= click_info.last_time + dbl_click_max_delay) {
if (Vector2.Distance(Input.mousePosition, click_info.last_pos) <= dbl_click_max_offset) {
ready_to_select = false; //Deactivate mouse selection
int start = 0; int end = 0;
if (!click_info.dbl_clicked) {
//Dblclick
start = Navigate_find_word_boundary(current_chr, current_row, -1);
end = Navigate_find_word_boundary(current_chr, current_row, 1);
//Debug.Log("Word boundary of " + current_chr + " is " + start + "x" + end);
} else {
//Tripleclick
end = text_rows[current_row].text.Length;
}
click_info.dbl_clicked = true;
current_chr = end;
selection_start = new int[]{current_row, start};
selection_end = new int[]{current_row, end};
Cursor_Update_Position(); Cursor_Blink(true);
Selection_Draw(selection_start, selection_end);
}
} else {
click_info.dbl_clicked = false;
}
click_info.last_time = Time.time;
click_info.last_pos = Input.mousePosition;
}
}
if (Input.GetKeyUp(KeyCode.Mouse0)) {
ready_to_select = false;
}
if (ready_to_select) {
int[] coord = Char_Index_Under_Mouse();
if (coord[0] >= 0 && coord[1] >= 0) {
current_row = coord[0];
current_chr = coord[1];
Cursor_Update_Position(); Cursor_Blink(true);
selection_end = coord;
Selection_Draw(selection_start, selection_end);
}
}
#endregion
//Tab with multiline selection or shift+tab
if (Input.GetKeyDown(KeyCode.Tab) && (selection_start[0] != selection_end[0] || shift_pressed)) {
if (read_only) { Hud.ShowImportantMessage("Can't edit while script is running."); return; }
int tab_length = 3;
var cur_position = new int[]{current_row, current_chr};
int[] s = selection_start; int[] e = selection_end;
bool dont_select_full_rows = false;
Selection_Clear();
if (s[0] > e[0]) { int[] tmp = e; e = s; s = tmp; }
if (!shift_pressed) {
for (int i = s[0]; i <= e[0]; i++) {
current_row = i; current_chr = 0; type_text("\t", true);
}
current_row = cur_position[0];
current_chr = cur_position[1] + tab_length;
} else {
int count = 0;
for (int i = s[0]; i <= e[0]; i++) {
Undo_Add( undo_op.modify, current_row );
count = text_rows[i].text.TakeWhile(Char.IsWhiteSpace).Count();
if (count > tab_length) count = tab_length;
text_rows[i].text = text_rows[i].text.Remove(0, count);
float w = text_char_w[i].Skip(current_chr).Take(count).Sum();
text_char_w[i].RemoveRange(0, count);
text_rows_w[i] -= w;
Syntax_Highlight ( i );
if (i == current_row) {
if (current_chr >= count) current_chr -= count; else current_chr = 0;
}
}
//If we had only one row selected - try to restore selection
if (s[0] == e[0]) {
if (s[1] >= count) { s[1] -= count; } else { s[1] = 0; }
if (e[1] >= count) { e[1] -= count; } else { e[1] = 0; }
selection_start = s; selection_end = e; dont_select_full_rows = true;
}
}
if (!dont_select_full_rows) {
selection_start = new int[]{s[0], 0};
selection_end = new int[]{e[0], text_char_w[e[0]].Count};
}