lexgui 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -106,10 +106,31 @@ class CodeEditor {
106
106
 
107
107
  constructor( area, options = {} ) {
108
108
 
109
+ // var a = [];
110
+
111
+ // var map = {};
112
+
113
+ // for( var i = 0; i < 1000000; ++i )
114
+ // map[i] = 1;
115
+
116
+ // const start = performance.now();
117
+
118
+ // for( var i = 0; i < 3000; ++i ) {
119
+ // const b = map[Math.floor( Math.random() * 1000000 )];
120
+ // }
121
+
122
+ // console.log( performance.now() - start );
123
+
124
+
125
+ // debugger;
126
+
127
+
109
128
  window.editor = this;
110
129
 
111
130
  CodeEditor.__instances.push( this );
112
131
 
132
+ var that = this;
133
+
113
134
  this.base_area = area;
114
135
  this.area = new LX.Area( { className: "lexcodeeditor", height: "auto", no_append: true } );
115
136
 
@@ -146,6 +167,9 @@ class CodeEditor {
146
167
  this.root.addEventListener( 'click', this.processMouse.bind(this) );
147
168
  this.root.addEventListener( 'contextmenu', this.processMouse.bind(this) );
148
169
 
170
+ // Take into account the scrollbar..
171
+ this.tabs.area.root.classList.add( 'codetabsarea' );
172
+
149
173
  // Cursors and selection
150
174
 
151
175
  this.cursors = document.createElement('div');
@@ -174,6 +198,96 @@ class CodeEditor {
174
198
  this.cursors.appendChild(cursor);
175
199
  }
176
200
 
201
+ // Add custom vertical scroll bar
202
+ {
203
+ var scrollbar = document.createElement('div');
204
+ scrollbar.className = "lexcodescrollbar";
205
+ this.scrollbar = scrollbar;
206
+ area.attach(this.scrollbar);
207
+
208
+ var scrollbarThumb = document.createElement('div');
209
+ this.scrollbarThumb = scrollbarThumb;
210
+ this.scrollbarThumb._top = 0;
211
+ scrollbar.appendChild(scrollbarThumb);
212
+
213
+ this.scrollbarThumb.addEventListener("mousedown", inner_mousedown);
214
+
215
+ var last_pos = 0;
216
+
217
+ function inner_mousedown(e)
218
+ {
219
+ var doc = that.root.ownerDocument;
220
+ doc.addEventListener("mousemove",inner_mousemove);
221
+ doc.addEventListener("mouseup",inner_mouseup);
222
+ last_pos = e.y;
223
+ e.stopPropagation();
224
+ e.preventDefault();
225
+ }
226
+
227
+ function inner_mousemove(e)
228
+ {
229
+ var dt = (last_pos - e.y);
230
+
231
+ that.applyVerticalScrollFromScrollBar( that.scrollbarThumb._top - dt )
232
+
233
+ last_pos = e.y;
234
+ e.stopPropagation();
235
+ e.preventDefault();
236
+ }
237
+
238
+ function inner_mouseup(e)
239
+ {
240
+ var doc = that.root.ownerDocument;
241
+ doc.removeEventListener("mousemove", inner_mousemove);
242
+ doc.removeEventListener("mouseup", inner_mouseup);
243
+ }
244
+ }
245
+
246
+ // Add custom horizontal scroll bar
247
+ {
248
+ var hScrollbar = document.createElement('div');
249
+ hScrollbar.className = "lexcodescrollbar horizontal";
250
+ this.hScrollbar = hScrollbar;
251
+ area.attach(this.hScrollbar);
252
+
253
+ var hScrollbarThumb = document.createElement('div');
254
+ this.hScrollbarThumb = hScrollbarThumb;
255
+ this.hScrollbarThumb._left = 0;
256
+ hScrollbar.appendChild(hScrollbarThumb);
257
+
258
+ this.hScrollbarThumb.addEventListener("mousedown", inner_mousedown);
259
+
260
+ var last_pos = 0;
261
+
262
+ function inner_mousedown(e)
263
+ {
264
+ var doc = that.root.ownerDocument;
265
+ doc.addEventListener("mousemove",inner_mousemove);
266
+ doc.addEventListener("mouseup",inner_mouseup);
267
+ last_pos = e.x;
268
+ e.stopPropagation();
269
+ e.preventDefault();
270
+ }
271
+
272
+ function inner_mousemove(e)
273
+ {
274
+ var dt = (last_pos - e.x);
275
+
276
+ that.applyHorizontalScrollFromScrollBar( that.hScrollbarThumb._left - dt )
277
+
278
+ last_pos = e.x;
279
+ e.stopPropagation();
280
+ e.preventDefault();
281
+ }
282
+
283
+ function inner_mouseup(e)
284
+ {
285
+ var doc = that.root.ownerDocument;
286
+ doc.removeEventListener("mousemove", inner_mousemove);
287
+ doc.removeEventListener("mouseup", inner_mouseup);
288
+ }
289
+ }
290
+
177
291
  // Add autocomplete box
178
292
  {
179
293
  var box = document.createElement('div');
@@ -241,11 +355,13 @@ class CodeEditor {
241
355
  'ArrowRight', 'ArrowLeft', 'Delete', 'Home',
242
356
  'End', 'Tab', 'Escape'
243
357
  ];
358
+
244
359
  this.keywords = {
245
360
  'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
246
361
  'arguments', 'extends', 'instanceof'],
247
362
  'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
248
363
  'NULL', 'unsigned'],
364
+ 'JSON': ['true', 'false'],
249
365
  'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
250
366
  'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
251
367
  'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
@@ -281,7 +397,7 @@ class CodeEditor {
281
397
  };
282
398
  this.statementsAndDeclarations = {
283
399
  'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
284
- 'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'glm'],
400
+ 'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'glm', 'spdlog'],
285
401
  'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
286
402
  'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
287
403
  'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
@@ -290,7 +406,7 @@ class CodeEditor {
290
406
  };
291
407
  this.symbols = {
292
408
  'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
293
- 'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::'],
409
+ 'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
294
410
  'JSON': ['[', ']', '{', '}', '(', ')'],
295
411
  'GLSL': ['[', ']', '{', '}', '(', ')'],
296
412
  'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
@@ -299,6 +415,15 @@ class CodeEditor {
299
415
  'Batch': ['[', ']', '(', ')', '%'],
300
416
  };
301
417
 
418
+ // Convert reserved word arrays to maps so we can search tokens faster
419
+
420
+ for( let lang in this.keywords ) this.keywords[lang] = this.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
421
+ for( let lang in this.utils ) this.utils[lang] = this.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
422
+ for( let lang in this.types ) this.types[lang] = this.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
423
+ for( let lang in this.builtin ) this.builtin[lang] = this.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
424
+ for( let lang in this.statementsAndDeclarations ) this.statementsAndDeclarations[lang] = this.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
425
+ for( let lang in this.symbols ) this.symbols[lang] = this.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
426
+
302
427
  // Action keys
303
428
 
304
429
  this.action('Escape', false, ( ln, cursor, e ) => {
@@ -382,7 +507,7 @@ class CodeEditor {
382
507
  this.resetCursorPos( CodeEditor.CURSOR_LEFT );
383
508
  if(idx > 0) this.cursorToString(cursor, prestring);
384
509
  this._refreshCodeInfo(cursor.line, cursor.position);
385
- this.code.scrollLeft = 0;
510
+ this.setScrollLeft( 0 );
386
511
 
387
512
  if( e.shiftKey && !e.cancelShift )
388
513
  {
@@ -411,8 +536,8 @@ class CodeEditor {
411
536
  this.resetCursorPos( CodeEditor.CURSOR_LEFT );
412
537
  this.cursorToString( cursor, this.code.lines[ln] );
413
538
 
414
- const last_char = ((this.code.scrollLeft + this.code.clientWidth) / this.charWidth)|0;
415
- this.code.scrollLeft = cursor.position >= last_char ? (cursor.position - last_char) * this.charWidth : 0;
539
+ const last_char = (this.code.clientWidth / this.charWidth)|0;
540
+ this.setScrollLeft( cursor.position >= last_char ? (cursor.position - last_char) * this.charWidth : 0 );
416
541
  });
417
542
 
418
543
  this.action('Enter', true, ( ln, cursor, e ) => {
@@ -531,7 +656,7 @@ class CodeEditor {
531
656
 
532
657
  if(e.metaKey) { // Apple devices (Command)
533
658
  e.preventDefault();
534
- this.actions[ 'Home' ].callback( ln, cursor );
659
+ this.actions[ 'Home' ].callback( ln, cursor, e );
535
660
  }
536
661
  else if(e.ctrlKey) {
537
662
  // Get next word
@@ -581,8 +706,7 @@ class CodeEditor {
581
706
  }
582
707
 
583
708
  this.lineUp( cursor );
584
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
585
- this.cursorToPosition( cursor, this.code.lines[cursor.line].length );
709
+ this.actions[ 'End' ].callback( cursor.line, cursor, e );
586
710
 
587
711
  if( e.shiftKey ) {
588
712
  this.selection.toX = cursor.position;
@@ -771,12 +895,13 @@ class CodeEditor {
771
895
  loadFile( file ) {
772
896
 
773
897
  const inner_add_tab = ( text, name, title ) => {
774
- this.addTab(name, true, title);
775
- text = text.replaceAll('\r', '');
776
- this.code.lines = text.split('\n');
777
- this._refreshCodeInfo();
778
- this._changeLanguageFromExtension( LX.getExtension(name) );
779
- this.processLines();
898
+ const existing = this.addTab(name, true, title);
899
+ if( !existing )
900
+ {
901
+ text = text.replaceAll('\r', '');
902
+ this.code.lines = text.split('\n');
903
+ this._changeLanguageFromExtension( LX.getExtension(name) );
904
+ }
780
905
  };
781
906
 
782
907
  if(file.constructor == String)
@@ -797,7 +922,6 @@ class CodeEditor {
797
922
  inner_add_tab( text, file.name );
798
923
  };
799
924
  }
800
-
801
925
  }
802
926
 
803
927
  _addUndoStep( cursor ) {
@@ -901,7 +1025,7 @@ class CodeEditor {
901
1025
  if(this.openedTabs[name])
902
1026
  {
903
1027
  this.tabs.select( this.code.tabName );
904
- return;
1028
+ return true;
905
1029
  }
906
1030
 
907
1031
  // Create code content
@@ -913,37 +1037,36 @@ class CodeEditor {
913
1037
  code.undoSteps = [];
914
1038
  code.tabName = name;
915
1039
  code.title = title ?? name;
916
- code.tokens = {};
1040
+ code.tokens = {};
1041
+ code.customScroll = new LX.vec2;
917
1042
 
918
1043
  code.addEventListener('dragenter', function(e) {
919
1044
  e.preventDefault();
920
- this.classList.add('dragging');
1045
+ this.parentElement.classList.add('dragging');
921
1046
  });
922
1047
  code.addEventListener('dragleave', function(e) {
923
1048
  e.preventDefault();
924
- this.classList.remove('dragging');
1049
+ this.parentElement.remove('dragging');
925
1050
  });
926
1051
  code.addEventListener('drop', (e) => {
927
1052
  e.preventDefault();
928
- code.classList.remove('dragging');
1053
+ code.parentElement.classList.remove('dragging');
929
1054
  for( let i = 0; i < e.dataTransfer.files.length; ++i )
930
1055
  this.loadFile( e.dataTransfer.files[i] );
931
1056
  });
1057
+ code.addEventListener('wheel', (e) => {
932
1058
 
933
- code.addEventListener('scroll', (e) => {
934
- this.gutter.scrollTop = code.scrollTop;
935
- this.gutter.scrollLeft = code.scrollLeft;
1059
+ // Get scroll data
936
1060
 
937
- // Update cursor
938
- var cursor = this.cursors.children[0];
939
- cursor.style.top = (cursor._top - code.scrollTop) + "px";
940
- cursor.style.left = "calc( " + (cursor._left - code.scrollLeft) + "px + " + this.xPadding + ")";
1061
+ const dX = (e.deltaY > 0.0 ? 1.0 : -1.0) * 20.0 * ( e.shiftKey ? 1.0 : 0.0 );
1062
+ const dY = (e.deltaY > 0.0 ? 1.0 : -1.0) * 40.0 * ( e.shiftKey ? 0.0 : 1.0 );
941
1063
 
942
- // Update selection
943
- for( let s of this.selections.childNodes ) {
944
- s.style.top = (s._top - code.scrollTop) + "px";
945
- s.style.left = "calc( " + (s._left - code.scrollLeft) + "px + " + this.xPadding + ")";
946
- }
1064
+ var new_scroll = code.customScroll.add( new LX.vec2( dX, dY ), new LX.vec2() );
1065
+
1066
+ // Update state
1067
+
1068
+ if( new_scroll.x != this.getScrollLeft()) this.setScrollLeft( new_scroll.x );
1069
+ if( new_scroll.y != this.getScrollTop()) this.setScrollTop( new_scroll.y );
947
1070
  });
948
1071
 
949
1072
  this.openedTabs[name] = code;
@@ -965,13 +1088,14 @@ class CodeEditor {
965
1088
  this._refreshCodeInfo(cursor.line, cursor.position);
966
1089
 
967
1090
  // Restore scroll
968
- this.gutter.scrollTop = this.code.scrollTop;
969
- this.gutter.scrollLeft = this.code.scrollLeft;
1091
+ this.gutter.scrollLeft = this.getScrollLeft();
1092
+ this.gutter.scrollTop = this.getScrollTop();
970
1093
  }});
971
1094
 
972
1095
  this.endSelection();
973
1096
 
974
- if(selected){
1097
+ if( selected )
1098
+ {
975
1099
  this.code = code;
976
1100
  this.resetCursorPos(CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP);
977
1101
  this.processLines();
@@ -1009,7 +1133,7 @@ class CodeEditor {
1009
1133
 
1010
1134
  var cursor = this.cursors.children[0];
1011
1135
  var code_rect = this.code.getBoundingClientRect();
1012
- var mouse_pos = [(e.clientX - code_rect.x) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
1136
+ var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
1013
1137
 
1014
1138
  // Discard out of lines click...
1015
1139
  if( e.type != 'contextmenu' )
@@ -1020,9 +1144,6 @@ class CodeEditor {
1020
1144
 
1021
1145
  if( e.type == 'mousedown' )
1022
1146
  {
1023
- if( mouse_pos[0] > this.code.scrollWidth || mouse_pos[1] > this.code.scrollHeight )
1024
- return; // Scrollbar click
1025
-
1026
1147
  // Left click only...
1027
1148
  if( e.button === 2 )
1028
1149
  {
@@ -1113,7 +1234,7 @@ class CodeEditor {
1113
1234
  processClick(e, skip_refresh = false) {
1114
1235
 
1115
1236
  var code_rect = this.code.getBoundingClientRect();
1116
- var position = [(e.clientX - code_rect.x) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
1237
+ var position = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
1117
1238
  var ln = (position[1] / this.lineHeight)|0;
1118
1239
 
1119
1240
  if(this.code.lines[ln] == undefined) return;
@@ -1125,7 +1246,6 @@ class CodeEditor {
1125
1246
 
1126
1247
  var ch = (position[0] / this.charWidth)|0;
1127
1248
  var string = this.code.lines[ln].slice(0, ch);
1128
- // this.cursorToString(cursor, string);
1129
1249
  this.cursorToPosition(cursor, string.length);
1130
1250
 
1131
1251
  this.hideAutoCompleteBox();
@@ -1516,19 +1636,37 @@ class CodeEditor {
1516
1636
  }
1517
1637
  }
1518
1638
 
1519
- processLines( from ) {
1639
+ processLines( from__legacy ) {
1520
1640
 
1521
- // const start = performance.now();
1641
+ const start = performance.now();
1642
+
1643
+ var gutter_html = "";
1644
+ var code_html = "";
1645
+
1646
+ this.resizeScrollBars();
1522
1647
 
1523
- this.gutter.innerHTML = "";
1524
1648
  this.code.innerHTML = "";
1649
+ this.gutter.innerHTML = "";
1525
1650
 
1526
- for( let i = 0; i < this.code.lines.length; ++i )
1651
+ // Get info about lines in viewport
1652
+ const margin = 20;
1653
+ const firstLineInViewport = (this.getScrollTop() / this.lineHeight)|0;
1654
+ const totalLinesInViewport = ((this.code.parentElement.offsetHeight - 36) / this.lineHeight)|0;
1655
+ const viewportRange = new LX.vec2(
1656
+ Math.max( firstLineInViewport - margin, 0 ),
1657
+ Math.min( firstLineInViewport + totalLinesInViewport + margin, this.code.lines.length )
1658
+ );
1659
+
1660
+ for( let i = viewportRange.x; i < viewportRange.y; ++i )
1527
1661
  {
1528
- this.processLine( i, true );
1662
+ gutter_html += "<span>" + (i + 1) + "</span>";
1663
+ code_html += this.processLine( i, true );
1529
1664
  }
1530
1665
 
1531
- // console.log( performance.now() - start );
1666
+ this.code.innerHTML = code_html;
1667
+ this.gutter.innerHTML = gutter_html;
1668
+
1669
+ console.log( "Num lines processed: " + (viewportRange.y - viewportRange.x), performance.now() - start );
1532
1670
  }
1533
1671
 
1534
1672
  processLine( linenum, force ) {
@@ -1538,31 +1676,27 @@ class CodeEditor {
1538
1676
  // It's allowed to process only 1 line to optimize
1539
1677
  let linestring = this.code.lines[ linenum ];
1540
1678
 
1541
- var pre = document.createElement('pre');
1542
- pre.dataset['linenum'] = linenum;
1543
-
1544
- // Line gutter
1545
- var linenumspan = document.createElement('span');
1546
- linenumspan.innerHTML = (linenum + 1);
1547
-
1548
- if( force )
1679
+ if( !force )
1549
1680
  {
1550
- this.gutter.appendChild(linenumspan);
1551
- this.code.appendChild( pre );
1552
- } else
1553
- {
1554
- deleteElement( this.gutter.childNodes[ linenum ] );
1681
+ var pre = document.createElement('pre');
1682
+
1683
+ // Single code line
1555
1684
  deleteElement( this.code.childNodes[ linenum ] );
1556
- this.gutter.insertChildAtIndex( linenumspan, linenum );
1557
1685
  this.code.insertChildAtIndex( pre, linenum );
1686
+
1687
+ // Gutter
1688
+ deleteElement( this.gutter.childNodes[ linenum ] );
1689
+ var linenumspan = document.createElement('span');
1690
+ linenumspan.innerHTML = (linenum + 1);
1691
+ this.gutter.insertChildAtIndex( linenumspan, linenum );
1558
1692
  }
1559
1693
 
1560
- var linespan = document.createElement('span');
1561
- pre.appendChild(linespan);
1562
-
1563
1694
  const tokensToEvaluate = this._getTokensFromString( linestring );
1564
1695
 
1565
- let line_inner_html = "";
1696
+ if( !tokensToEvaluate.length )
1697
+ return "<pre></pre>";
1698
+
1699
+ var line_inner_html = "";
1566
1700
 
1567
1701
  // Process all tokens
1568
1702
  for( var i = 0; i < tokensToEvaluate.length; ++i )
@@ -1576,12 +1710,12 @@ class CodeEditor {
1576
1710
 
1577
1711
  it = i + 1;
1578
1712
  let next = tokensToEvaluate[it];
1579
- while( next == ' ' || next == '"') {
1713
+ while( next == ' ' || next == '"' ) {
1580
1714
  it++;
1581
1715
  next = tokensToEvaluate[it];
1582
1716
  }
1583
1717
 
1584
- let token = tokensToEvaluate[i];
1718
+ const token = tokensToEvaluate[i];
1585
1719
 
1586
1720
  if( this.languages[ this.highlight ].blockComments ?? true )
1587
1721
  {
@@ -1594,7 +1728,16 @@ class CodeEditor {
1594
1728
  line_inner_html += this.evaluateToken(token, prev, next);
1595
1729
  }
1596
1730
 
1597
- linespan.innerHTML = line_inner_html;
1731
+ // Single line update
1732
+ if( !force )
1733
+ {
1734
+ this.code.childNodes[ linenum ].innerHTML = line_inner_html;
1735
+ }
1736
+ // Update all lines at once
1737
+ else
1738
+ {
1739
+ return "<pre>" + line_inner_html + "</pre>";
1740
+ }
1598
1741
  }
1599
1742
 
1600
1743
  _processTokens( tokens, offset = 0 ) {
@@ -1624,145 +1767,168 @@ class CodeEditor {
1624
1767
 
1625
1768
  // Check if line comment
1626
1769
  const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
1627
- const is_comment = linestring.split(singleLineCommentToken);
1628
- linestring = ( is_comment.length > 1 ) ? is_comment[0] : linestring;
1770
+ const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
1771
+ const has_comment = linestring.split(singleLineCommentToken);
1772
+ linestring = ( has_comment.length > 1 ) ? has_comment[0] : linestring;
1629
1773
 
1630
- const tokens = linestring.split(' ').join('¬ ¬').split('¬'); // trick to split without losing spaces
1774
+ // const tokens = linestring.split(' ').join('¬ ¬').split('¬'); // trick to split without losing spaces
1775
+
1631
1776
  let tokensToEvaluate = []; // store in a temp array so we know prev and next tokens...
1632
1777
 
1633
- for( let t of tokens )
1634
- {
1635
- if( !t.length || (skipNonWords && ( t.includes('"') || t.length < 3 )) )
1636
- continue;
1637
-
1638
- if( t == ' ' ) {
1639
- tokensToEvaluate.push( t );
1640
- continue;
1641
- }
1778
+ const pushToken = function( t ) {
1779
+ if( (skipNonWords && ( t.includes('"') || t.length < 3 )) )
1780
+ return;
1781
+ tokensToEvaluate.push( t );
1782
+ };
1642
1783
 
1643
- let iter = t.matchAll(/(::|[\[\](){}<>.,;:"'])/g);
1644
- let subtokens = iter.next();
1645
- if( subtokens.value )
1784
+ let iter = linestring.matchAll(/(::|[\[\](){}<>.,;:*"'%@ ])/g);
1785
+ let subtokens = iter.next();
1786
+ if( subtokens.value )
1787
+ {
1788
+ let idx = 0;
1789
+ while( subtokens.value != undefined )
1646
1790
  {
1647
- let idx = 0;
1648
- while( subtokens.value != undefined )
1649
- {
1650
- const _pt = t.substring(idx, subtokens.value.index);
1651
- if( _pt.length ) tokensToEvaluate.push( _pt );
1652
- tokensToEvaluate.push( subtokens.value[0] );
1653
- idx = subtokens.value.index + subtokens.value[0].length;
1654
- subtokens = iter.next();
1655
- if(!subtokens.value) {
1656
- const _at = t.substring(idx);
1657
- if( _at.length ) tokensToEvaluate.push( _at );
1658
- }
1791
+ const _pt = linestring.substring(idx, subtokens.value.index);
1792
+ if( _pt.length ) pushToken( _pt );
1793
+ pushToken( subtokens.value[0] );
1794
+ idx = subtokens.value.index + subtokens.value[0].length;
1795
+ subtokens = iter.next();
1796
+ if(!subtokens.value) {
1797
+ const _at = linestring.substring(idx);
1798
+ if( _at.length ) pushToken( _at );
1659
1799
  }
1660
1800
  }
1661
- else tokensToEvaluate.push( t );
1662
1801
  }
1802
+ else tokensToEvaluate.push( linestring );
1803
+
1804
+ // if( usesBlockComments )
1805
+ // {
1806
+ // var block = false;
1807
+
1808
+ // if( t.includes('/*') )
1809
+ // {
1810
+ // const idx = t.indexOf( '/*' );
1811
+ // tokensToEvaluate.push( t.substring( 0, idx ), '/*', t.substring( idx + 2 ) );
1812
+ // block |= true;
1813
+ // }
1814
+ // else if( t.includes('*/') )
1815
+ // {
1816
+ // const idx = t.indexOf( '*/' );
1817
+ // tokensToEvaluate.push( t.substring( 0, idx ), '*/', t.substring( idx + 2 ) );
1818
+ // block |= true;
1819
+ // }
1820
+
1821
+ // if( block ) continue;
1822
+ // }
1663
1823
 
1664
- if( is_comment.length > 1 && !skipNonWords )
1665
- tokensToEvaluate.push( singleLineCommentToken + is_comment[1] );
1824
+ if( has_comment.length > 1 && !skipNonWords )
1825
+ pushToken( singleLineCommentToken + has_comment[1] );
1826
+
1827
+ // console.log( tokensToEvaluate );
1666
1828
 
1667
1829
  return this._processTokens( tokensToEvaluate );
1668
1830
  }
1669
1831
 
1670
1832
  _mustHightlightWord( token, kindArray ) {
1671
1833
 
1672
- return kindArray[this.highlight] && kindArray[this.highlight].indexOf(token) > -1;
1834
+ return kindArray[this.highlight] && kindArray[this.highlight][token] != undefined;
1673
1835
  }
1674
1836
 
1675
1837
  evaluateToken( token, prev, next ) {
1676
1838
 
1677
- let stringEnded = false;
1678
- let highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
1679
- let inner_html = "", token_classname = "";
1680
-
1681
- const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
1682
- const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
1683
- const customStringKeys = Object.assign( {}, this.stringKeys );
1684
-
1685
- if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
1686
- {
1687
- customStringKeys['@<'] = '>';
1688
- }
1689
-
1690
- // Manage strings
1691
- if( this._buildingString != undefined )
1692
- {
1693
- const idx = Object.values(customStringKeys).indexOf( token );
1694
- stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
1695
- }
1696
- else if( customStringKeys[ '@' + token ] )
1697
- {
1698
- // Start new string
1699
- this._buildingString = token;
1700
- }
1701
-
1702
1839
  if(token == ' ')
1703
1840
  {
1704
- inner_html += token;
1841
+ return token;
1705
1842
  }
1706
1843
  else
1707
1844
  {
1845
+ let stringEnded = false;
1846
+ let highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
1847
+
1848
+ const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
1849
+ const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
1850
+ const customStringKeys = Object.assign( {}, this.stringKeys );
1851
+
1852
+ if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
1853
+ {
1854
+ customStringKeys['@<'] = '>';
1855
+ }
1856
+
1857
+ // Manage strings
1858
+ if( this._buildingString != undefined )
1859
+ {
1860
+ const idx = Object.values(customStringKeys).indexOf( token );
1861
+ stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
1862
+ }
1863
+ else if( customStringKeys[ '@' + token ] )
1864
+ {
1865
+ // Start new string
1866
+ this._buildingString = token;
1867
+ }
1868
+
1869
+ let token_classname = "";
1870
+
1708
1871
  if( this._buildingBlockComment != undefined )
1709
- token_classname += "cm-com";
1872
+ token_classname = "cm-com";
1710
1873
 
1711
1874
  else if( this._buildingString != undefined )
1712
- token_classname += "cm-str";
1875
+ token_classname = "cm-str";
1713
1876
 
1714
1877
  else if( this._mustHightlightWord( token, this.keywords ) )
1715
- token_classname += "cm-kwd";
1878
+ token_classname = "cm-kwd";
1716
1879
 
1717
1880
  else if( this._mustHightlightWord( token, this.builtin ) )
1718
- token_classname += "cm-bln";
1881
+ token_classname = "cm-bln";
1719
1882
 
1720
1883
  else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
1721
- token_classname += "cm-std";
1884
+ token_classname = "cm-std";
1722
1885
 
1723
1886
  else if( this._mustHightlightWord( token, this.symbols ) )
1724
- token_classname += "cm-sym";
1887
+ token_classname = "cm-sym";
1725
1888
 
1726
1889
  else if( token.substr(0, 2) == singleLineCommentToken )
1727
- token_classname += "cm-com";
1890
+ token_classname = "cm-com";
1728
1891
 
1729
1892
  else if( usesBlockComments && token.substr(0, 2) == '/*' )
1730
- token_classname += "cm-com";
1893
+ token_classname = "cm-com";
1731
1894
 
1732
1895
  else if( usesBlockComments && token.substr(token.length - 2) == '*/' )
1733
- token_classname += "cm-com";
1896
+ token_classname = "cm-com";
1734
1897
 
1735
- else if( this.isNumber(token) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
1736
- token_classname += "cm-dec";
1898
+ else if( this.isNumber(token) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
1899
+ token_classname = "cm-dec";
1737
1900
 
1738
1901
  else if( this.isCSSClass(token, prev, next) )
1739
- token_classname += "cm-kwd";
1902
+ token_classname = "cm-kwd";
1740
1903
 
1741
1904
  else if ( this.isType(token, prev, next) )
1742
- token_classname += "cm-typ";
1905
+ token_classname = "cm-typ";
1743
1906
 
1744
- else if ( token[0] != '@' && next == '(' )
1745
- token_classname += "cm-mtd";
1907
+ else if ( highlight == 'batch' && (token == '@' || prev == ':' || prev == '@') )
1908
+ token_classname = "cm-kwd";
1746
1909
 
1747
1910
  else if ( highlight == 'cpp' && token.includes('#') ) // C++ preprocessor
1748
- token_classname += "cm-ppc";
1911
+ token_classname = "cm-ppc";
1912
+
1913
+ else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
1914
+ token_classname = "cm-typ";
1749
1915
 
1750
- else if ( highlight == 'cpp' && prev != 'WDWD:' && next == '::' ) // C++ Class
1751
- token_classname += "cm-typ";
1916
+ else if ( highlight == 'cpp' && (next == '::' || prev == '::' && next != '(' )) // C++ Class
1917
+ token_classname = "cm-typ";
1752
1918
 
1753
1919
  else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
1754
- token_classname += "cm-str";
1920
+ token_classname = "cm-str";
1755
1921
 
1756
1922
  else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
1757
- token_classname += "cm-typ";
1923
+ token_classname = "cm-typ";
1758
1924
 
1759
- token_classname += " " + highlight;
1760
- inner_html += "<span class=' " + token_classname + "'>" + token + "</span>";
1761
- }
1925
+ else if ( token[0] != '@' && next == '(' )
1926
+ token_classname = "cm-mtd";
1762
1927
 
1763
- if(stringEnded) delete this._buildingString;
1928
+ this._buildingString = stringEnded ? undefined : this._buildingString;
1764
1929
 
1765
- return inner_html;
1930
+ return "<span class='" + highlight + " " + token_classname + "'>" + token + "</span>";
1931
+ }
1766
1932
  }
1767
1933
 
1768
1934
  isCSSClass( token, prev, next ) {
@@ -1798,7 +1964,7 @@ class CodeEditor {
1798
1964
  }
1799
1965
  else if ( this.highlight == 'WGSL' )
1800
1966
  {
1801
- const is_kwd = (this.keywords[this.highlight] && this.keywords[this.highlight].indexOf(token) == -1);
1967
+ const is_kwd = !this._mustHightlightWord( token, this.keywords );
1802
1968
  return (prev == 'struct' && next == '{') ||
1803
1969
  ( is_kwd &&
1804
1970
  ( prev == ':' && next == ')' || prev == ':' && next == ',' || prev == '>' && next == '{'
@@ -1947,9 +2113,9 @@ class CodeEditor {
1947
2113
  // Add horizontal scroll
1948
2114
 
1949
2115
  doAsync(() => {
1950
- var last_char = ((this.code.scrollLeft + this.code.clientWidth) / this.charWidth)|0;
2116
+ var last_char = ((this.code.clientWidth) / this.charWidth)|0;
1951
2117
  if( cursor.position >= last_char )
1952
- this.code.scrollLeft += this.charWidth;
2118
+ this.setScrollLeft( this.getScrollLeft() + this.charWidth );
1953
2119
  });
1954
2120
  }
1955
2121
 
@@ -1965,10 +2131,12 @@ class CodeEditor {
1965
2131
  this.restartBlink();
1966
2132
  this._refreshCodeInfo( cursor.line, cursor.position );
1967
2133
 
2134
+ // Add horizontal scroll
2135
+
1968
2136
  doAsync(() => {
1969
- var first_char = (this.code.scrollLeft / this.charWidth)|0;
2137
+ var first_char = (this.getScrollLeft() / this.charWidth)|0;
1970
2138
  if( (cursor.position - 1) < first_char )
1971
- this.code.scrollLeft -= this.charWidth;
2139
+ this.setScrollLeft( this.getScrollLeft() - this.charWidth );
1972
2140
  });
1973
2141
  }
1974
2142
 
@@ -1986,9 +2154,9 @@ class CodeEditor {
1986
2154
  this._refreshCodeInfo( cursor.line, cursor.position );
1987
2155
 
1988
2156
  doAsync(() => {
1989
- var first_line = (this.code.scrollTop / this.lineHeight)|0;
2157
+ var first_line = (this.getScrollTop() / this.lineHeight)|0;
1990
2158
  if( (cursor.line - 1) < first_line )
1991
- this.code.scrollTop -= this.lineHeight;
2159
+ this.setScrollTop( this.getScrollTop() - this.lineHeight );
1992
2160
  });
1993
2161
  }
1994
2162
 
@@ -2005,9 +2173,9 @@ class CodeEditor {
2005
2173
  this._refreshCodeInfo( cursor.line, cursor.position );
2006
2174
 
2007
2175
  doAsync(() => {
2008
- var last_line = ((this.code.scrollTop + this.code.offsetHeight) / this.lineHeight)|0;
2176
+ var last_line = ((this.code.parentElement.offsetHeight - 32) / this.lineHeight)|0;
2009
2177
  if( cursor.line >= last_line )
2010
- this.code.scrollTop += this.lineHeight;
2178
+ this.setScrollTop( this.getScrollTop() + this.lineHeight );
2011
2179
  });
2012
2180
  }
2013
2181
 
@@ -2094,13 +2262,148 @@ class CodeEditor {
2094
2262
  getScrollLeft() {
2095
2263
 
2096
2264
  if(!this.code) return 0;
2097
- return this.code.scrollLeft;
2265
+ return this.code.customScroll.x;
2098
2266
  }
2099
2267
 
2100
2268
  getScrollTop() {
2101
2269
 
2102
2270
  if(!this.code) return 0;
2103
- return this.code.scrollTop;
2271
+ return this.code.customScroll.y;
2272
+ }
2273
+
2274
+ setScrollLeft( value, keepScrollBar ) {
2275
+
2276
+ if(!this.code) return;
2277
+
2278
+ const realClientWidth = (this.code.clientWidth - this.code.customScroll.x);
2279
+ const maxWidth = Math.max( this.code.scrollWidth - realClientWidth, 0 );
2280
+
2281
+ value = LX.UTILS.clamp( value, 0, maxWidth );
2282
+
2283
+ this.code.style.marginLeft = (-value) + "px";
2284
+
2285
+ if( !keepScrollBar )
2286
+ {
2287
+ const scrollWidth = this.hScrollbarThumb.parentElement.offsetWidth;
2288
+ const scrollBarWidth = this.hScrollbarThumb.offsetWidth;
2289
+ this.setScrollBarValue( ( scrollWidth - scrollBarWidth ) * ( value / maxWidth ), 'horizontal' );
2290
+ }
2291
+
2292
+ // Update cursor
2293
+ var cursor = this.cursors.children[0];
2294
+ cursor.style.left = "calc( " + (cursor._left - value) + "px + " + this.xPadding + ")";
2295
+
2296
+ // Update selection
2297
+ for( let s of this.selections.childNodes ) {
2298
+ s.style.left = "calc( " + (s._left - value) + "px + " + this.xPadding + ")";
2299
+ }
2300
+
2301
+ this.code.customScroll.x = value;
2302
+ }
2303
+
2304
+ setScrollTop( value, keepScrollBar ) {
2305
+
2306
+ if(!this.code) return;
2307
+
2308
+ const realClientHeight = this.code.parentElement.offsetHeight - 36;
2309
+ const maxHeight = Math.max( this.code.scrollHeight - realClientHeight, 0 );
2310
+
2311
+ value = LX.UTILS.clamp( value, 0, maxHeight );
2312
+
2313
+ this.gutter.scrollTop = value;
2314
+
2315
+ this.code.style.marginTop = (-value) + "px";
2316
+
2317
+ if( !keepScrollBar )
2318
+ {
2319
+ const scrollHeight = this.scrollbarThumb.parentElement.offsetHeight;
2320
+ const scrollBarHeight = this.scrollbarThumb.offsetHeight;
2321
+ // this.setScrollBarValue( ( scrollHeight - scrollBarHeight ) * ( value / maxHeight ) )
2322
+ const firstLineInViewport = (this.getScrollTop() / this.lineHeight)|0;
2323
+ this.setScrollBarValue( ( scrollHeight - scrollBarHeight ) * ( firstLineInViewport / this.code.lines.length ) )
2324
+ }
2325
+
2326
+ // Update cursor
2327
+ var cursor = this.cursors.children[0];
2328
+ cursor.style.top = (cursor._top - value) + "px";
2329
+
2330
+ // Update selection
2331
+ for( let s of this.selections.childNodes ) {
2332
+ s.style.top = (s._top - value) + "px";
2333
+ }
2334
+
2335
+ this.code.customScroll.y = value;
2336
+ }
2337
+
2338
+ resizeScrollBars() {
2339
+
2340
+ const numViewportLines = Math.floor( (this.code.parentElement.offsetHeight - 36) / this.lineHeight );
2341
+
2342
+ if( numViewportLines > this.code.lines.length )
2343
+ {
2344
+ this.scrollbar.classList.add( 'scrollbar-unused' );
2345
+ this.tabs.area.root.classList.remove( 'with-vscrollbar' );
2346
+ }
2347
+ else
2348
+ {
2349
+ this.scrollbar.classList.remove( 'scrollbar-unused' );
2350
+ this.tabs.area.root.classList.add( 'with-vscrollbar' );
2351
+ this.scrollbarThumb.size = (numViewportLines / this.code.lines.length);
2352
+ this.scrollbarThumb.style.height = (this.scrollbarThumb.size * 100.0) + "%";
2353
+ }
2354
+
2355
+ const numViewportChars = Math.floor( this.code.clientWidth / this.charWidth );
2356
+ const line_lengths = this.code.lines.map( value => value.length );
2357
+ const maxLineLength = Math.max(...line_lengths);
2358
+
2359
+ if( numViewportChars > maxLineLength )
2360
+ {
2361
+ this.hScrollbar.classList.add( 'scrollbar-unused' );
2362
+ this.tabs.area.root.classList.remove( 'with-hscrollbar' );
2363
+ }
2364
+ else
2365
+ {
2366
+ this.hScrollbar.classList.remove( 'scrollbar-unused' );
2367
+ this.tabs.area.root.classList.add( 'with-hscrollbar' );
2368
+ this.hScrollbarThumb.size = (numViewportChars / maxLineLength);
2369
+ this.hScrollbarThumb.style.width = (this.hScrollbarThumb.size * 100.0) + "%";
2370
+ }
2371
+ }
2372
+
2373
+ setScrollBarValue( value, type = 'vertical' ) {
2374
+
2375
+ if( type == 'vertical' )
2376
+ {
2377
+ const scrollHeight = this.scrollbarThumb.parentElement.offsetHeight;
2378
+ const scrollBarHeight = this.scrollbarThumb.offsetHeight;
2379
+
2380
+ value = LX.UTILS.clamp( value, 0, ( scrollHeight - scrollBarHeight ) );
2381
+
2382
+ this.scrollbarThumb._top = value;
2383
+ this.scrollbarThumb.style.top = this.scrollbarThumb._top + "px";
2384
+ }
2385
+ else
2386
+ {
2387
+ const scrollWidth = this.hScrollbarThumb.parentElement.offsetWidth;
2388
+ const scrollBarWidth = this.hScrollbarThumb.offsetWidth;
2389
+
2390
+ value = LX.UTILS.clamp( value, 0, ( scrollWidth - scrollBarWidth ) );
2391
+
2392
+ this.hScrollbarThumb._left = value;
2393
+ this.hScrollbarThumb.style.left = this.hScrollbarThumb._left + "px";
2394
+ }
2395
+ }
2396
+
2397
+ applyHorizontalScrollFromScrollBar( value ) {
2398
+
2399
+ this.setScrollBarValue( value, 'horizontal');
2400
+ this.setScrollLeft( value / this.hScrollbarThumb.size, true );
2401
+ }
2402
+
2403
+ applyVerticalScrollFromScrollBar( value ) {
2404
+
2405
+ this.setScrollBarValue( value );
2406
+ this.setScrollTop( value / this.scrollbarThumb.size, true );
2104
2407
  }
2105
2408
 
2106
2409
  getCharAtPos( cursor, offset = 0 ) {
@@ -2155,6 +2458,7 @@ class CodeEditor {
2155
2458
  }
2156
2459
 
2157
2460
  runScript( code ) {
2461
+
2158
2462
  var script = document.createElement('script');
2159
2463
  script.type = 'module';
2160
2464
  script.innerHTML = code;
@@ -2211,15 +2515,15 @@ class CodeEditor {
2211
2515
 
2212
2516
  // Add language special keys...
2213
2517
  suggestions = suggestions.concat(
2214
- this.builtin[ this.highlight ] ?? [],
2215
- this.keywords[ this.highlight ] ?? [],
2216
- this.statementsAndDeclarations[ this.highlight ] ?? [],
2217
- this.types[ this.highlight ] ?? [],
2218
- this.utils[ this.highlight ] ?? []
2518
+ Object.keys( this.builtin[ this.highlight ] ) ?? [],
2519
+ Object.keys( this.keywords[ this.highlight ] ) ?? [],
2520
+ Object.keys( this.statementsAndDeclarations[ this.highlight ] ) ?? [],
2521
+ Object.keys( this.types[ this.highlight ] ) ?? [],
2522
+ Object.keys( this.utils[ this.highlight ] ) ?? []
2219
2523
  );
2220
2524
 
2221
2525
  // Add words in current tab plus remove current word
2222
- suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
2526
+ // suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
2223
2527
 
2224
2528
  // Remove 1/2 char words and duplicates...
2225
2529
  suggestions = suggestions.filter( (value, index) => value.length > 2 && suggestions.indexOf(value) === index );
@@ -2279,9 +2583,8 @@ class CodeEditor {
2279
2583
  // Show box
2280
2584
  this.autocomplete.classList.toggle('show', true);
2281
2585
  this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
2282
- this.autocomplete.style.left = (cursor._left + 36) + "px";
2283
- this.autocomplete.style.top = (cursor._top + 48) + "px";
2284
-
2586
+ this.autocomplete.style.left = (cursor._left + 36 - this.getScrollLeft()) + "px";
2587
+ this.autocomplete.style.top = (cursor._top + 48 - this.getScrollTop()) + "px";
2285
2588
 
2286
2589
  this.isAutoCompleteActive = true;
2287
2590
  }
package/build/lexgui.css CHANGED
@@ -2709,7 +2709,7 @@ ul.lexassetscontent {
2709
2709
  .lexcodeeditor {
2710
2710
  outline: none;
2711
2711
  overflow: hidden;
2712
- width: calc(100% );
2712
+ width: calc(100%);
2713
2713
  -webkit-user-select: none; /* Safari 3.1+ */
2714
2714
  -moz-user-select: none; /* Firefox 2+ */
2715
2715
  -ms-user-select: none; /* IE 10+ */
@@ -2730,9 +2730,9 @@ ul.lexassetscontent {
2730
2730
 
2731
2731
  .lexcodegutter {
2732
2732
  width: 32px;
2733
- height: calc(100% - 69px); /* 6px of the vertical scrollbar */
2733
+ height: calc(100% - 70px);
2734
2734
  background-color: var(--global-branch-darker);
2735
- margin-top: 25px;
2735
+ margin-top: 24px;
2736
2736
  text-align: center;
2737
2737
  overflow: hidden;
2738
2738
  -webkit-user-select: none; /* Safari 3.1+ */
@@ -2753,19 +2753,29 @@ ul.lexassetscontent {
2753
2753
  line-height: 22px;
2754
2754
  }
2755
2755
 
2756
- .lexcodeeditor .code {
2757
- height: calc(100% - 30px);
2758
- cursor: text;
2759
- font-size: 16px;
2760
- overflow: scroll;
2756
+ .lexcodeeditor .codetabsarea {
2761
2757
  background-color: var(--global-branch-darker);
2762
- padding-right: 6px;
2763
2758
  }
2764
2759
 
2765
- .lexcodeeditor .code.dragging {
2760
+ .lexcodeeditor .codetabsarea.with-vscrollbar {
2761
+ width: calc( 100% - 10px ) !important;
2762
+ }
2763
+
2764
+ .lexcodeeditor .codetabsarea.with-hscrollbar {
2765
+ height: calc( 100% - 40px ) !important;
2766
+ }
2767
+
2768
+ .lexcodeeditor .codetabsarea.dragging {
2766
2769
  background-color: var(--global-color-secondary);
2767
2770
  }
2768
2771
 
2772
+ .lexcodeeditor .code {
2773
+ cursor: text;
2774
+ font-size: 16px;
2775
+ overflow: hidden;
2776
+ padding-right: 6px;
2777
+ }
2778
+
2769
2779
  .codechar {
2770
2780
  font-size: 16px;
2771
2781
  font-family: 'Inconsolata', monospace;
@@ -2797,6 +2807,7 @@ ul.lexassetscontent {
2797
2807
  -webkit-tap-highlight-color: transparent;
2798
2808
  z-index: 0 !important;
2799
2809
  pointer-events: none;
2810
+ height: 16px;
2800
2811
  }
2801
2812
 
2802
2813
  .lexcodeeditor span {
@@ -2852,6 +2863,56 @@ ul.lexassetscontent {
2852
2863
  height: 18px;
2853
2864
  }
2854
2865
 
2866
+ .lexcodescrollbar {
2867
+ position: absolute;
2868
+ background-color: var(--global-branch-darker);
2869
+ filter: brightness(0.9);
2870
+ box-sizing: border-box;
2871
+ margin: 0;
2872
+ padding: 0;
2873
+ margin-top: 26px;
2874
+ z-index: 1 !important;
2875
+ right: 0px;
2876
+ width: 10px;
2877
+ height: calc(100% - 62px);
2878
+ }
2879
+
2880
+ .lexcodescrollbar.horizontal {
2881
+ position: absolute;
2882
+ background-color: var(--global-branch-darker);
2883
+ filter: brightness(0.9);
2884
+ box-sizing: border-box;
2885
+ margin: 0;
2886
+ padding: 0;
2887
+ z-index: 1 !important;
2888
+ bottom: 36px;
2889
+ right: 10px;
2890
+ width: calc( 100% - 40px );
2891
+ height: 10px;
2892
+ }
2893
+
2894
+ .lexcodescrollbar.scrollbar-unused {
2895
+ display: none;
2896
+ }
2897
+
2898
+ .lexcodescrollbar div { /* thumb */
2899
+ background-color: #bbbbbb57 !important;
2900
+ box-sizing: border-box;
2901
+ margin: 0;
2902
+ padding: 0;
2903
+ border-right: none;
2904
+ position: absolute;
2905
+ z-index: 0 !important;
2906
+ top: 0px;
2907
+ width: 10px;
2908
+ height: 10px;
2909
+ transition: linear 0.1s background-color;
2910
+ }
2911
+
2912
+ .lexcodescrollbar div:hover { /* thumb */
2913
+ background-color: #bbbbbb8c !important;
2914
+ }
2915
+
2855
2916
  .lexcodeeditor .lexcodeselection {
2856
2917
  -webkit-text-size-adjust: 100%;
2857
2918
  font-family: monospace;
@@ -3010,7 +3071,7 @@ ul.lexassetscontent {
3010
3071
  .cm-std.batch { color: #cf6dcf; } /* statements & declarations */
3011
3072
  .cm-bln.batch { color: inherit; } /* builtin */
3012
3073
  .cm-dec.batch { color: #b1ce9b; } /* decimal */
3013
- .cm-sym.batch { color: #e6dc20; } /* symbol */
3074
+ .cm-sym.batch { color: #dfd85e; } /* symbol */
3014
3075
  .cm-mtd.batch { color: inherit } /* method */
3015
3076
 
3016
3077
  /* plain color */
package/build/lexgui.js CHANGED
@@ -12,7 +12,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
12
12
  */
13
13
 
14
14
  var LX = global.LX = {
15
- version: "0.1.15",
15
+ version: "0.1.16",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -5046,50 +5046,45 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5046
5046
  if(!name) {
5047
5047
  throw("Set Widget Name!");
5048
5048
  }
5049
-
5049
+
5050
5050
  let widget = this.create_widget(name, Widget.FILE, options);
5051
5051
  let element = widget.domEl;
5052
-
5052
+
5053
5053
  let local = options.local ?? true;
5054
5054
  let type = options.type ?? 'text';
5055
5055
  let read = options.read ?? true;
5056
-
5056
+
5057
5057
  // Create hidden input
5058
5058
  let input = document.createElement('input');
5059
5059
  input.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " - 10%)";
5060
5060
  input.type = 'file';
5061
5061
  if(options.placeholder)
5062
5062
  input.placeholder = options.placeholder;
5063
-
5063
+
5064
5064
  input.addEventListener('change', function(e) {
5065
5065
  const files = e.target.files;
5066
- if(!files.length) return;
5067
- if(read) {
5066
+ if( !files.length ) return;
5067
+ if(read)
5068
+ {
5068
5069
  const reader = new FileReader();
5069
-
5070
- if(type === 'text') {
5071
- reader.readAsText(files[0]);
5072
- }else if(type === 'buffer') {
5073
- reader.readAsArrayBuffer(files[0])
5074
- }else if(type === 'bin') {
5075
- reader.readAsBinaryString(files[0])
5076
- }else if(type === 'url') {
5077
- reader.readAsDataURL(files[0])
5078
- }
5079
-
5080
- reader.onload = (e) => { callback.call(this, e.target.result) } ;
5070
+
5071
+ if(type === 'text') reader.readAsText(files[0]);
5072
+ else if(type === 'buffer') reader.readAsArrayBuffer(files[0]);
5073
+ else if(type === 'bin') reader.readAsBinaryString(files[0]);
5074
+ else if(type === 'url') reader.readAsDataURL(files[0]);
5075
+
5076
+ reader.onload = (e) => { callback.call( this, e.target.result, files[0] ) } ;
5081
5077
  }
5082
- else
5083
- callback(files[0]);
5078
+ else callback(files[0]);
5084
5079
 
5085
5080
  });
5086
-
5081
+
5087
5082
  element.appendChild(input);
5088
-
5083
+
5089
5084
  this.queue( element );
5090
5085
 
5091
- if(local) {
5092
-
5086
+ if( local )
5087
+ {
5093
5088
  this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-gear'></a>", () => {
5094
5089
 
5095
5090
  new Dialog("Load Settings", p => {
@@ -5099,9 +5094,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5099
5094
 
5100
5095
  }, { className: "micro", skipInlineCount: true });
5101
5096
  }
5102
-
5097
+
5103
5098
  this.clearQueue();
5104
-
5099
+
5105
5100
  return widget;
5106
5101
  }
5107
5102
 
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.15",
11
+ version: "0.1.16",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -5059,24 +5059,19 @@ class Panel {
5059
5059
 
5060
5060
  input.addEventListener('change', function(e) {
5061
5061
  const files = e.target.files;
5062
- if(!files.length) return;
5063
- if(read) {
5062
+ if( !files.length ) return;
5063
+ if(read)
5064
+ {
5064
5065
  const reader = new FileReader();
5065
5066
 
5066
- if(type === 'text') {
5067
- reader.readAsText(files[0]);
5068
- }else if(type === 'buffer') {
5069
- reader.readAsArrayBuffer(files[0])
5070
- }else if(type === 'bin') {
5071
- reader.readAsBinaryString(files[0])
5072
- }else if(type === 'url') {
5073
- reader.readAsDataURL(files[0])
5074
- }
5067
+ if(type === 'text') reader.readAsText(files[0]);
5068
+ else if(type === 'buffer') reader.readAsArrayBuffer(files[0]);
5069
+ else if(type === 'bin') reader.readAsBinaryString(files[0]);
5070
+ else if(type === 'url') reader.readAsDataURL(files[0]);
5075
5071
 
5076
- reader.onload = (e) => { callback.call(this, e.target.result) } ;
5072
+ reader.onload = (e) => { callback.call( this, e.target.result, files[0] ) } ;
5077
5073
  }
5078
- else
5079
- callback(files[0]);
5074
+ else callback(files[0]);
5080
5075
 
5081
5076
  });
5082
5077
 
@@ -5084,8 +5079,8 @@ class Panel {
5084
5079
 
5085
5080
  this.queue( element );
5086
5081
 
5087
- if(local) {
5088
-
5082
+ if( local )
5083
+ {
5089
5084
  this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-gear'></a>", () => {
5090
5085
 
5091
5086
  new Dialog("Load Settings", p => {
@@ -51,8 +51,16 @@
51
51
  // editor.loadFile( "../data/style.css" );
52
52
  // editor.loadFile( "../data/script.js" );
53
53
  // editor.loadFile( "../data/engine.cpp" );
54
- editor.loadFile( "../localhost.bat" );
54
+ editor.loadFile( "../demo.js" );
55
55
 
56
+ // var button = document.createElement('button');
57
+ // button.value = "CLICK ME";
58
+ // button.style.width = "100px";
59
+ // button.style.height = "100px";
60
+ // button.style.position = "absolute";
61
+ // button.onclick = editor.processLines.bind(editor);
62
+ // document.body.appendChild( button );
63
+
56
64
  var ctx = canvas.getContext("2d");
57
65
  ctx.fillStyle = "#b7a9b1";
58
66
  ctx.font = "48px Monospace";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",