lexgui 0.1.24 → 0.1.25

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.
@@ -26,7 +26,8 @@ function sliceChars( str, idx, n = 1 ) {
26
26
  }
27
27
 
28
28
  function firstNonspaceIndex( str ) {
29
- return str.search(/\S|$/);
29
+ const index = str.search(/\S|$/)
30
+ return index < str.length ? index : -1;
30
31
  }
31
32
 
32
33
  function indexOfFrom( str, reg, from, reverse ) {
@@ -118,7 +119,7 @@ class CodeSelection {
118
119
  this.editor.selections.appendChild(domEl);
119
120
 
120
121
  // Hide active line background
121
- this.editor.code.childNodes.forEach( e => e.classList.remove( 'active-line' ) );
122
+ this.editor._hideActiveLine();
122
123
  }
123
124
  };
124
125
 
@@ -201,8 +202,8 @@ class CodeEditor {
201
202
  static WORD_TYPE_METHOD = 0;
202
203
  static WORD_TYPE_CLASS = 1;
203
204
 
204
- static CODE_MAX_FONT_SIZE = 22;
205
205
  static CODE_MIN_FONT_SIZE = 9;
206
+ static CODE_MAX_FONT_SIZE = 22;
206
207
 
207
208
  /**
208
209
  * @param {*} options
@@ -493,7 +494,8 @@ class CodeEditor {
493
494
  this.state = {
494
495
  focused: false,
495
496
  selectingText: false,
496
- activeLine: null
497
+ activeLine: null,
498
+ keyChain: null
497
499
  }
498
500
 
499
501
  // Code
@@ -550,84 +552,16 @@ class CodeEditor {
550
552
  'End', 'Tab', 'Escape'
551
553
  ];
552
554
 
553
- this.keywords = {
554
- 'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
555
- 'arguments', 'extends', 'instanceof'],
556
- 'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
557
- 'NULL', 'unsigned', 'namespace'],
558
- 'JSON': ['true', 'false'],
559
- 'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
560
- 'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
561
- 'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
562
- 'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
563
- 'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
564
- 'texture_storage_2d_array', 'texture_storage_3d'],
565
- 'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
566
- 'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
567
- 'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
568
- 'Batch': ['set', 'SET', 'echo', 'ECHO', 'off', 'OFF', 'del', 'DEL', 'defined', 'DEFINED', 'setlocal', 'SETLOCAL', 'enabledelayedexpansion', 'ENABLEDELAYEDEXPANSION', 'driverquery',
569
- 'DRIVERQUERY', 'print', 'PRINT'],
570
- 'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
571
- 'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
572
- };
573
- this.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
574
- 'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
575
- 'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
576
- 'alert'],
577
- 'WGSL': ['textureSample'],
578
- 'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
579
- 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
580
- 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
581
- 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
582
- };
583
- this.types = {
584
- 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
585
- 'Rust': ['u128'],
586
- 'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
587
- 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
588
- 'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
589
- 'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
590
- 'C++': ['uint8_t', 'uint16_t', 'uint32_t']
591
- };
592
- this.builtin = {
593
- 'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
594
- 'CSS': ['*', '!important'],
595
- 'C++': ['vector', 'list', 'map'],
596
- 'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
597
- 'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
598
- };
599
- this.statementsAndDeclarations = {
600
- 'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
601
- 'CSS': ['@', 'import'],
602
- 'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog'],
603
- 'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
604
- 'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
605
- 'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
606
- 'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
607
- 'global', 'pass'],
608
- 'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT']
609
- };
610
- this.symbols = {
611
- 'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
612
- 'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
613
- 'JSON': ['[', ']', '{', '}', '(', ')'],
614
- 'GLSL': ['[', ']', '{', '}', '(', ')'],
615
- 'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
616
- 'CSS': ['{', '}', '(', ')', '*'],
617
- 'Rust': ['<', '>', '[', ']', '(', ')', '='],
618
- 'Python': ['<', '>', '[', ']', '(', ')', '='],
619
- 'Batch': ['[', ']', '(', ')', '%'],
620
- 'HTML': ['<', '>', '/']
621
- };
555
+ this._blockCommentCache = [];
622
556
 
623
557
  // Convert reserved word arrays to maps so we can search tokens faster
624
558
 
625
- for( let lang in this.keywords ) this.keywords[lang] = this.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
626
- for( let lang in this.utils ) this.utils[lang] = this.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
627
- for( let lang in this.types ) this.types[lang] = this.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
628
- for( let lang in this.builtin ) this.builtin[lang] = this.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
629
- for( let lang in this.statementsAndDeclarations ) this.statementsAndDeclarations[lang] = this.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
630
- for( let lang in this.symbols ) this.symbols[lang] = this.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
559
+ for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = CodeEditor.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
560
+ for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = CodeEditor.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
561
+ for( let lang in CodeEditor.types ) CodeEditor.types[lang] = CodeEditor.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
562
+ for( let lang in CodeEditor.builtin ) CodeEditor.builtin[lang] = CodeEditor.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
563
+ for( let lang in CodeEditor.statementsAndDeclarations ) CodeEditor.statementsAndDeclarations[lang] = CodeEditor.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
564
+ for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = CodeEditor.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
631
565
 
632
566
  // Action keys
633
567
 
@@ -881,7 +815,7 @@ class CodeEditor {
881
815
 
882
816
  // Go to end of line if out of range
883
817
  if( !letter || !canGoDown ) {
884
- this.actions[ 'End' ].callback( cursor.line, cursor, e );
818
+ this.cursorToPosition( cursor, Math.max(this.code.lines[ cursor.line ].length, 0) );
885
819
  }
886
820
 
887
821
  if( e.shiftKey ) {
@@ -1227,7 +1161,7 @@ class CodeEditor {
1227
1161
 
1228
1162
  return;
1229
1163
  }
1230
-
1164
+
1231
1165
  let cursor = document.createElement( 'div' );
1232
1166
  cursor.className = "cursor";
1233
1167
  cursor.innerHTML = "&nbsp;";
@@ -1348,6 +1282,8 @@ class CodeEditor {
1348
1282
 
1349
1283
  this.restoreCursor( currentCursor, step.cursors[ i ] );
1350
1284
  }
1285
+
1286
+ this._hideActiveLine();
1351
1287
  }
1352
1288
 
1353
1289
  _addRedoStep( cursor ) {
@@ -1837,8 +1773,7 @@ class CodeEditor {
1837
1773
  if( !this.selection )
1838
1774
  this.startSelection( cursor );
1839
1775
 
1840
- // Hide active line background
1841
- this.code.childNodes.forEach( e => e.classList.remove( 'active-line' ) );
1776
+ this._hideActiveLine();
1842
1777
 
1843
1778
  // Update selection
1844
1779
  if( !keep_range )
@@ -1994,14 +1929,21 @@ class CodeEditor {
1994
1929
  const key = e.key ?? e.detail.key;
1995
1930
  const target = e.detail.targetCursor;
1996
1931
 
1932
+ // Global keys
1933
+
1934
+ if( this._processGlobalKeys( e, key ) ) {
1935
+ // Stop propagation..
1936
+ return;
1937
+ }
1938
+
1939
+ // By cursor keys
1940
+
1997
1941
  if( target !== undefined )
1998
1942
  {
1999
1943
  this.processKeyAtTargetCursor( e, key, target );
2000
1944
  return;
2001
1945
  }
2002
1946
 
2003
- // By cursor keys
2004
-
2005
1947
  this._lastProcessedCursorIndex = null;
2006
1948
 
2007
1949
  var lastProcessedCursor = null;
@@ -2033,10 +1975,6 @@ class CodeEditor {
2033
1975
  cursorOffset.y += ( cursor.line - lastProcessedCursor.line );
2034
1976
  }
2035
1977
 
2036
- // Global keys
2037
-
2038
- this._processGlobalKeys( e, key );
2039
-
2040
1978
  // Clear tmp
2041
1979
 
2042
1980
  delete this._lastProcessedCursorIndex;
@@ -2054,7 +1992,7 @@ class CodeEditor {
2054
1992
  this._processGlobalKeys( e, key );
2055
1993
  }
2056
1994
 
2057
- async _processGlobalKeys( e, key ) {
1995
+ _processGlobalKeys( e, key ) {
2058
1996
 
2059
1997
  let cursor = this._getCurrentCursor();
2060
1998
 
@@ -2065,30 +2003,48 @@ class CodeEditor {
2065
2003
  switch( key.toLowerCase() ) {
2066
2004
  case 'a': // select all
2067
2005
  this.selectAll();
2068
- break;
2006
+ return true;
2007
+ case 'c': // k+c, comment line
2008
+ if( this.state.keyChain == 'k' ) {
2009
+ this._commentLines();
2010
+ return true;
2011
+ }
2012
+ return false;
2069
2013
  case 'f': // find/search
2070
2014
  this.showSearchBox();
2071
- break;
2015
+ return true;
2072
2016
  case 'g': // find line
2073
2017
  this.showSearchLineBox();
2074
- break;
2018
+ return true;
2019
+ case 'k': // shortcut chain
2020
+ this.state.keyChain = 'k';
2021
+ return true;
2075
2022
  case 's': // save
2076
2023
  this.onsave( this.getText() );
2077
- break;
2024
+ return true;
2025
+ case 'u': // k+u, uncomment line
2026
+ if( this.state.keyChain == 'k' ) {
2027
+ this._uncommentLines();
2028
+ return true;
2029
+ }
2030
+ return false;
2078
2031
  case 'y': // redo
2079
2032
  this._doRedo( cursor );
2080
- break;
2033
+ return true;
2081
2034
  case 'z': // undo
2082
2035
  this._doUndo( cursor );
2083
- break;
2036
+ return true;
2084
2037
  case '+': // increase size
2085
2038
  this._increaseFontSize();
2086
- break;
2039
+ return true;
2087
2040
  case '-': // decrease size
2088
2041
  this._decreaseFontSize();
2089
- break;
2042
+ return true;
2090
2043
  }
2091
2044
  }
2045
+
2046
+ this.state.keyChain = null;
2047
+ return false;
2092
2048
  }
2093
2049
 
2094
2050
  async _processKeyAtCursor( e, key, cursor ) {
@@ -2345,6 +2301,114 @@ class CodeEditor {
2345
2301
  this.hideAutoCompleteBox();
2346
2302
  }
2347
2303
 
2304
+ _commentLines() {
2305
+
2306
+ this.state.keyChain = null;
2307
+
2308
+ if( this.selection )
2309
+ {
2310
+ var cursor = this._getCurrentCursor();
2311
+ this._addUndoStep( cursor, true );
2312
+
2313
+ const selectedLines = this.code.lines.slice( this.selection.fromY, this.selection.toY );
2314
+ const minIdx = Math.min(...selectedLines.map( v => {
2315
+ var idx = firstNonspaceIndex( v );
2316
+ return idx < 0 ? 1e10 : idx;
2317
+ } ));
2318
+
2319
+ for( var i = this.selection.fromY; i <= this.selection.toY; ++i )
2320
+ {
2321
+ this._commentLine( cursor, i, minIdx );
2322
+ }
2323
+ }
2324
+ else
2325
+ {
2326
+ for( let cursor of this.cursors.children )
2327
+ {
2328
+ this._addUndoStep( cursor, true );
2329
+ this._commentLine( cursor, cursor.line );
2330
+ }
2331
+ }
2332
+
2333
+ this.processLines();
2334
+ this._hideActiveLine();
2335
+ }
2336
+
2337
+ _commentLine( cursor, line, minNonspaceIdx ) {
2338
+
2339
+ const lang = this.languages[ this.highlight ];
2340
+
2341
+ if( !( lang.singleLineComments ?? true ))
2342
+ return;
2343
+
2344
+ const token = ( lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken ) + ' ';
2345
+ const string = this.code.lines[ line ];
2346
+
2347
+ let idx = firstNonspaceIndex( string );
2348
+ if( idx > -1 )
2349
+ {
2350
+ // Update idx using min of the selected lines (if necessary..)
2351
+ idx = minNonspaceIdx ?? idx;
2352
+
2353
+ this.code.lines[ line ] = [
2354
+ string.substring( 0, idx ),
2355
+ token,
2356
+ string.substring( idx )
2357
+ ].join( '' );
2358
+
2359
+ this.cursorToString( cursor, token );
2360
+ }
2361
+ }
2362
+
2363
+ _uncommentLines() {
2364
+
2365
+ this.state.keyChain = null;
2366
+
2367
+ if( this.selection )
2368
+ {
2369
+ var cursor = this._getCurrentCursor();
2370
+ this._addUndoStep( cursor, true );
2371
+
2372
+ for( var i = this.selection.fromY; i <= this.selection.toY; ++i )
2373
+ {
2374
+ this._uncommentLine( cursor, i );
2375
+ }
2376
+ }
2377
+ else
2378
+ {
2379
+ for( let cursor of this.cursors.children )
2380
+ {
2381
+ this._addUndoStep( cursor, true );
2382
+ this._uncommentLine( cursor, cursor.line );
2383
+ }
2384
+ }
2385
+
2386
+ this.processLines();
2387
+ this._hideActiveLine();
2388
+ }
2389
+
2390
+ _uncommentLine( cursor, line ) {
2391
+
2392
+ const lang = this.languages[ this.highlight ];
2393
+
2394
+ if( !( lang.singleLineComments ?? true ))
2395
+ return;
2396
+
2397
+ const token = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2398
+ const string = this.code.lines[ line ];
2399
+
2400
+ if( string.includes( token ) )
2401
+ {
2402
+ this.code.lines[ line ] = string.replace( token + ' ', '' );
2403
+
2404
+ // try deleting token + space, and then if not, delete only the token
2405
+ if( string.length == this.code.lines[ line ].length )
2406
+ this.code.lines[ line ] = string.replace( token, '' );
2407
+
2408
+ this.cursorToString( cursor, 'X'.repeat( Math.abs( string.length - this.code.lines[ line ].length ) ), true );
2409
+ }
2410
+ }
2411
+
2348
2412
  action( key, deleteSelection, fn ) {
2349
2413
 
2350
2414
  this.actions[ key ] = {
@@ -2379,6 +2443,7 @@ class CodeEditor {
2379
2443
  processLines( mode ) {
2380
2444
 
2381
2445
  var code_html = "";
2446
+ this._blockCommentCache.length = 0;
2382
2447
 
2383
2448
  // Reset all lines content
2384
2449
  this.code.innerHTML = "";
@@ -2423,6 +2488,13 @@ class CodeEditor {
2423
2488
 
2424
2489
  processLine( linenum, force ) {
2425
2490
 
2491
+ // Check if we are in block comment sections..
2492
+ if( !force && this._inBlockCommentSection( linenum ) )
2493
+ {
2494
+ this.processLines();
2495
+ return;
2496
+ }
2497
+
2426
2498
  const lang = this.languages[ this.highlight ];
2427
2499
  const local_line_num = this.toLocalLine( linenum );
2428
2500
  const gutter_line = "<span class='line-gutter'>" + (linenum + 1) + "</span>";
@@ -2431,6 +2503,7 @@ class CodeEditor {
2431
2503
  if( !force ) // Single line update
2432
2504
  {
2433
2505
  this.code.childNodes[ local_line_num ].innerHTML = gutter_line + html;
2506
+ this._setActiveLine( linenum );
2434
2507
  this._clearTmpVariables();
2435
2508
  }
2436
2509
  else // Update all lines at once
@@ -2457,6 +2530,9 @@ class CodeEditor {
2457
2530
  return UPDATE_LINE( linestring );
2458
2531
  }
2459
2532
 
2533
+ this._currentLineNumber = linenum;
2534
+ this._currentLineString = linestring;
2535
+
2460
2536
  const tokensToEvaluate = this._getTokensFromLine( linestring );
2461
2537
 
2462
2538
  if( !tokensToEvaluate.length )
@@ -2487,7 +2563,7 @@ class CodeEditor {
2487
2563
  {
2488
2564
  const blockCommentsToken = ( lang.blockCommentsTokens ?? this.defaultBlockCommentTokens )[ 0 ];
2489
2565
  if( token.substr( 0, blockCommentsToken.length ) == blockCommentsToken )
2490
- this._buildingBlockComment = true;
2566
+ this._buildingBlockComment = linenum;
2491
2567
  }
2492
2568
 
2493
2569
  line_inner_html += this._evaluateToken( {
@@ -2525,19 +2601,12 @@ class CodeEditor {
2525
2601
  var matches = (linestring.substring( 0, idx ).match( re ) || []);
2526
2602
  return (matches.length % 2) !== 0;
2527
2603
  } );
2528
- err |= stringKeys.some( function(v) {
2529
- var re = new RegExp( v, "g" );
2530
- var matches = (linestring.substring( idx ).match( re ) || []);
2531
- return (matches.length % 2) !== 0;
2532
- } );
2533
2604
  return err ? undefined : idx;
2534
2605
  }
2535
2606
  }
2536
2607
 
2537
2608
  _getTokensFromLine( linestring, skipNonWords ) {
2538
2609
 
2539
- this._currentLineString = linestring;
2540
-
2541
2610
  // Check if line comment
2542
2611
  const ogLine = linestring;
2543
2612
  const hasCommentIdx = this._lineHasComment( linestring );
@@ -2594,12 +2663,12 @@ class CodeEditor {
2594
2663
 
2595
2664
  if( this.highlight == 'C++' || this.highlight == 'CSS' )
2596
2665
  {
2597
- var idx = tokens.slice( offset ).findIndex( ( value, index ) => this.isNumber( value ) );
2666
+ var idx = tokens.slice( offset ).findIndex( ( value, index ) => this._isNumber( value ) );
2598
2667
  if( idx > -1 )
2599
2668
  {
2600
2669
  idx += offset; // Add offset to compute within the whole array of tokens
2601
2670
  let data = tokens[ idx ] + tokens[ ++idx ];
2602
- while( this.isNumber( data ) )
2671
+ while( this._isNumber( data ) )
2603
2672
  {
2604
2673
  tokens[ idx - 1 ] += tokens[ idx ];
2605
2674
  tokens.splice( idx, 1 );
@@ -2647,7 +2716,7 @@ class CodeEditor {
2647
2716
  // Manage strings
2648
2717
  this._stringEnded = false;
2649
2718
 
2650
- if( usePreviousTokenToCheckString || ( !this._buildingBlockComment && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
2719
+ if( usePreviousTokenToCheckString || ( this._buildingBlockComment === undefined && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
2651
2720
  {
2652
2721
  const checkIfStringEnded = t => {
2653
2722
  const idx = Object.values( customStringKeys ).indexOf( t );
@@ -2696,22 +2765,22 @@ class CodeEditor {
2696
2765
  else if( this._buildingString != undefined )
2697
2766
  discardToken = this._appendStringToken( token );
2698
2767
 
2699
- else if( this._mustHightlightWord( token, this.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2768
+ else if( this._mustHightlightWord( token, CodeEditor.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2700
2769
  token_classname = "cm-kwd";
2701
2770
 
2702
- else if( this._mustHightlightWord( token, this.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2771
+ else if( this._mustHightlightWord( token, CodeEditor.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2703
2772
  token_classname = "cm-bln";
2704
2773
 
2705
- else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
2774
+ else if( this._mustHightlightWord( token, CodeEditor.statementsAndDeclarations ) )
2706
2775
  token_classname = "cm-std";
2707
2776
 
2708
- else if( this._mustHightlightWord( token, this.symbols ) )
2777
+ else if( this._mustHightlightWord( token, CodeEditor.symbols ) )
2709
2778
  token_classname = "cm-sym";
2710
2779
 
2711
2780
  else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
2712
2781
  token_classname = "cm-com";
2713
2782
 
2714
- else if( this.isNumber( token ) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
2783
+ else if( this._isNumber( token ) || this._isNumber( token.replace(/[px]|[em]|%/g,'') ) )
2715
2784
  token_classname = "cm-dec";
2716
2785
 
2717
2786
  else if( this._isCSSClass( token, prev, next ) )
@@ -2723,7 +2792,7 @@ class CodeEditor {
2723
2792
  else if ( highlight == 'batch' && ( token == '@' || prev == ':' || prev == '@' ) )
2724
2793
  token_classname = "cm-kwd";
2725
2794
 
2726
- else if ( highlight == 'cpp' && token.includes( '#' ) ) // C++ preprocessor
2795
+ else if ( [ 'cpp', 'wgsl', 'glsl' ].indexOf( highlight ) > -1 && token.includes( '#' ) ) // C++ preprocessor
2727
2796
  token_classname = "cm-ppc";
2728
2797
 
2729
2798
  else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
@@ -2748,9 +2817,10 @@ class CodeEditor {
2748
2817
  token_classname = "cm-mtd";
2749
2818
 
2750
2819
 
2751
- if( usesBlockComments && this._buildingBlockComment
2820
+ if( usesBlockComments && this._buildingBlockComment != undefined
2752
2821
  && token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
2753
2822
  {
2823
+ this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
2754
2824
  delete this._buildingBlockComment;
2755
2825
  }
2756
2826
 
@@ -2799,16 +2869,27 @@ class CodeEditor {
2799
2869
  _enclosedByTokens( token, tokenIndex, tagStart, tagEnd ) {
2800
2870
 
2801
2871
  const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
2802
- const tagStartIndex = indexOfFrom(this._currentLineString, tagStart, tokenStartIndex, true );
2872
+ const tagStartIndex = indexOfFrom( this._currentLineString, tagStart, tokenStartIndex, true );
2803
2873
  if( tagStartIndex < 0 ) // Not found..
2804
2874
  return;
2805
- const tagEndIndex = indexOfFrom(this._currentLineString, tagEnd, tokenStartIndex );
2875
+ const tagEndIndex = indexOfFrom( this._currentLineString, tagEnd, tokenStartIndex );
2806
2876
  if( tagEndIndex < 0 ) // Not found..
2807
2877
  return;
2808
2878
 
2809
2879
  return ( tagStartIndex < tokenStartIndex ) && ( tagEndIndex >= ( tokenStartIndex + token.length ) );
2810
2880
  }
2811
2881
 
2882
+ _inBlockCommentSection( line ) {
2883
+
2884
+ for( var section of this._blockCommentCache )
2885
+ {
2886
+ if( line >= section.x && line <= section.y )
2887
+ return true;
2888
+ }
2889
+
2890
+ return false;
2891
+ }
2892
+
2812
2893
  _isCSSClass( token, prev, next ) {
2813
2894
 
2814
2895
  if( this.highlight != 'CSS' )
@@ -2819,20 +2900,26 @@ class CodeEditor {
2819
2900
  || ( token[ 0 ] == '#' && prev != ':' ) );
2820
2901
  }
2821
2902
 
2822
- isNumber( token ) {
2903
+ _isNumber( token ) {
2823
2904
 
2824
2905
  if(this.highlight == 'C++')
2825
2906
  {
2826
2907
  if( token.lastChar == 'f' )
2827
- return this.isNumber( token.substring(0, token.length - 1) )
2908
+ return this._isNumber( token.substring(0, token.length - 1) )
2828
2909
  else if( token.lastChar == 'u' )
2829
- return !(token.includes('.')) && this.isNumber( token.substring(0, token.length - 1) );
2910
+ return !(token.includes('.')) && this._isNumber( token.substring(0, token.length - 1) );
2911
+ }
2912
+
2913
+ else if(this.highlight == 'WGSL')
2914
+ {
2915
+ if( token.lastChar == 'u' )
2916
+ return !(token.includes('.')) && this._isNumber( token.substring(0, token.length - 1) );
2830
2917
  }
2831
2918
 
2832
2919
  else if(this.highlight == 'CSS')
2833
2920
  {
2834
2921
  if( token.lastChar == '%' )
2835
- return this.isNumber( token.substring(0, token.length - 1) )
2922
+ return this._isNumber( token.substring(0, token.length - 1) )
2836
2923
  }
2837
2924
 
2838
2925
  return token.length && token != ' ' && !Number.isNaN(+token);
@@ -2841,7 +2928,7 @@ class CodeEditor {
2841
2928
  _isType( token, prev, next ) {
2842
2929
 
2843
2930
  // Common case
2844
- if( this._mustHightlightWord( token, this.types ) )
2931
+ if( this._mustHightlightWord( token, CodeEditor.types ) )
2845
2932
  return true;
2846
2933
 
2847
2934
  if( this.highlight == 'JavaScript' )
@@ -2854,11 +2941,12 @@ class CodeEditor {
2854
2941
  }
2855
2942
  else if ( this.highlight == 'WGSL' )
2856
2943
  {
2857
- const is_kwd = !this._mustHightlightWord( token, this.keywords );
2858
- return (prev == 'struct' && next == '{') ||
2859
- ( is_kwd &&
2944
+ const not_kwd = !this._mustHightlightWord( token, CodeEditor.keywords );
2945
+ return (prev == 'struct' && next == '{') ||
2946
+ (not_kwd && prev == ':' && next == ';') ||
2947
+ ( not_kwd &&
2860
2948
  ( prev == ':' && next == ')' || prev == ':' && next == ',' || prev == '>' && next == '{'
2861
- || prev == '<' && next == ',' || prev == '<' && next == '>' || prev == '>' && !next ));
2949
+ || prev == '<' && next == ',' || prev == '<' && next == '>' || prev == '>' && token != ';' && !next ));
2862
2950
  }
2863
2951
  }
2864
2952
 
@@ -3486,11 +3574,11 @@ class CodeEditor {
3486
3574
 
3487
3575
  // Add language special keys...
3488
3576
  suggestions = suggestions.concat(
3489
- Object.keys( this.builtin[ this.highlight ] ?? {} ),
3490
- Object.keys( this.keywords[ this.highlight ] ?? {} ),
3491
- Object.keys( this.statementsAndDeclarations[ this.highlight ] ?? {} ),
3492
- Object.keys( this.types[ this.highlight ] ?? {} ),
3493
- Object.keys( this.utils[ this.highlight ] ?? {} )
3577
+ Object.keys( CodeEditor.builtin[ this.highlight ] ?? {} ),
3578
+ Object.keys( CodeEditor.keywords[ this.highlight ] ?? {} ),
3579
+ Object.keys( CodeEditor.statementsAndDeclarations[ this.highlight ] ?? {} ),
3580
+ Object.keys( CodeEditor.types[ this.highlight ] ?? {} ),
3581
+ Object.keys( CodeEditor.utils[ this.highlight ] ?? {} )
3494
3582
  );
3495
3583
 
3496
3584
  // Add words in current tab plus remove current word
@@ -3512,9 +3600,9 @@ class CodeEditor {
3512
3600
 
3513
3601
  var icon = document.createElement( 'a' );
3514
3602
 
3515
- if( this._mustHightlightWord( s, this.utils ) )
3603
+ if( this._mustHightlightWord( s, CodeEditor.utils ) )
3516
3604
  icon.className = "fa fa-cube";
3517
- else if( this._mustHightlightWord( s, this.types ) )
3605
+ else if( this._mustHightlightWord( s, CodeEditor.types ) )
3518
3606
  icon.className = "fa fa-code";
3519
3607
  else
3520
3608
  icon.className = "fa fa-font";
@@ -3762,7 +3850,7 @@ class CodeEditor {
3762
3850
 
3763
3851
  goToLine( line ) {
3764
3852
 
3765
- if( !this.isNumber( line ) )
3853
+ if( !this._isNumber( line ) )
3766
3854
  return;
3767
3855
 
3768
3856
  this.codeScroller.scrollTo( 0, Math.max( line - 15 ) * this.lineHeight );
@@ -3809,6 +3897,11 @@ class CodeEditor {
3809
3897
  }
3810
3898
  }
3811
3899
 
3900
+ _hideActiveLine() {
3901
+
3902
+ this.code.querySelectorAll( '.active-line' ).forEach( e => e.classList.remove( 'active-line' ) );
3903
+ }
3904
+
3812
3905
  _increaseFontSize() {
3813
3906
 
3814
3907
  // Change font size
@@ -3864,6 +3957,7 @@ class CodeEditor {
3864
3957
  _clearTmpVariables() {
3865
3958
 
3866
3959
  delete this._currentLineString;
3960
+ delete this._currentLineNumber;
3867
3961
  delete this._buildingString;
3868
3962
  delete this._pendingString;
3869
3963
  delete this._buildingBlockComment;
@@ -3871,6 +3965,87 @@ class CodeEditor {
3871
3965
  }
3872
3966
  }
3873
3967
 
3968
+ CodeEditor.keywords = {
3969
+
3970
+ 'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
3971
+ 'arguments', 'extends', 'instanceof'],
3972
+ 'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
3973
+ 'NULL', 'unsigned', 'namespace'],
3974
+ 'JSON': ['true', 'false'],
3975
+ 'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
3976
+ 'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
3977
+ 'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
3978
+ 'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
3979
+ 'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
3980
+ 'texture_storage_2d_array', 'texture_storage_3d', 'vec2u', 'vec3u', 'vec4u'],
3981
+ 'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
3982
+ 'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
3983
+ 'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
3984
+ 'Batch': ['set', 'SET', 'echo', 'ECHO', 'off', 'OFF', 'del', 'DEL', 'defined', 'DEFINED', 'setlocal', 'SETLOCAL', 'enabledelayedexpansion', 'ENABLEDELAYEDEXPANSION', 'driverquery',
3985
+ 'DRIVERQUERY', 'print', 'PRINT'],
3986
+ 'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
3987
+ 'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
3988
+ };
3989
+
3990
+ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
3991
+
3992
+ 'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
3993
+ 'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
3994
+ 'alert'],
3995
+ 'WGSL': ['textureSample'],
3996
+ 'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
3997
+ 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
3998
+ 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
3999
+ 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
4000
+ };
4001
+
4002
+ CodeEditor.types = {
4003
+
4004
+ 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
4005
+ 'Rust': ['u128'],
4006
+ 'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
4007
+ 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
4008
+ 'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
4009
+ 'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
4010
+ 'C++': ['uint8_t', 'uint16_t', 'uint32_t']
4011
+ };
4012
+
4013
+ CodeEditor.builtin = {
4014
+
4015
+ 'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
4016
+ 'CSS': ['*', '!important'],
4017
+ 'C++': ['vector', 'list', 'map'],
4018
+ 'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
4019
+ 'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
4020
+ };
4021
+
4022
+ CodeEditor.statementsAndDeclarations = {
4023
+
4024
+ 'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
4025
+ 'CSS': ['@', 'import'],
4026
+ 'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog'],
4027
+ 'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
4028
+ 'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform', 'function', 'workgroup'],
4029
+ 'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
4030
+ 'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
4031
+ 'global', 'pass'],
4032
+ 'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT']
4033
+ };
4034
+
4035
+ CodeEditor.symbols = {
4036
+
4037
+ 'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
4038
+ 'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
4039
+ 'JSON': ['[', ']', '{', '}', '(', ')'],
4040
+ 'GLSL': ['[', ']', '{', '}', '(', ')'],
4041
+ 'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
4042
+ 'CSS': ['{', '}', '(', ')', '*'],
4043
+ 'Rust': ['<', '>', '[', ']', '(', ')', '='],
4044
+ 'Python': ['<', '>', '[', ']', '(', ')', '='],
4045
+ 'Batch': ['[', ']', '(', ')', '%'],
4046
+ 'HTML': ['<', '>', '/']
4047
+ };
4048
+
3874
4049
  LX.CodeEditor = CodeEditor;
3875
4050
 
3876
4051
  export { CodeEditor };
package/build/lexgui.css CHANGED
@@ -3125,7 +3125,7 @@ pre .line-gutter {
3125
3125
 
3126
3126
  /* Common */
3127
3127
  .cm-str { color: #ca7d59; } /* string */
3128
- .cm-std { color: #cf6dcf; } /* statements & declarations */
3128
+ .cm-std { color: #d181d1; } /* statements & declarations */
3129
3129
  .cm-kwd { color: #2194ce; } /* keyword */
3130
3130
  .cm-com { color: #5cab5a; } /* comment */
3131
3131
 
@@ -3152,16 +3152,16 @@ pre .line-gutter {
3152
3152
  .cm-sym.json { color: #cf6dcf; } /* symbol */
3153
3153
 
3154
3154
  .cm-typ.glsl { color: #36c0b0; } /* type */
3155
- .cm-bln.glsl { color: #cfc159; } /* builtin */
3156
3155
  .cm-dec.glsl { color: #b1ce9b; } /* decimal */
3157
3156
  .cm-sym.glsl { color: #f9cb20; } /* symbol */
3158
3157
  .cm-mtd.glsl { color: #e0cc68; } /* method */
3159
3158
 
3160
3159
  .cm-typ.wgsl { color: #36c0b0; } /* type */
3161
- .cm-bln.wgsl { color: #cfc159; } /* builtin */
3162
3160
  .cm-dec.wgsl { color: #b1ce9b; } /* decimal */
3161
+ .cm-mtd.wgsl { color: #dfe093; } /* method */
3163
3162
  .cm-sym.wgsl { color: #f9cb20; } /* symbol */
3164
- .cm-mtd.wgsl { color: #e0cc68; } /* method */
3163
+ .cm-ppc.wgsl { color: #99caf1; } /* preprocessor */
3164
+
3165
3165
 
3166
3166
  .cm-typ.rust { color: #36c0b0; } /* type */
3167
3167
  .cm-dec.rust { color: #b1ce9b; } /* decimal */
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.24",
15
+ version: "0.1.25",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -2179,6 +2179,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2179
2179
 
2180
2180
  if( this.onSetValue )
2181
2181
  this.onSetValue( value );
2182
+
2183
+ console.warn("Can't set value of " + this.typeName());
2182
2184
  }
2183
2185
 
2184
2186
  oncontextmenu(e) {
@@ -3029,6 +3031,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3029
3031
  let domName = document.createElement('div');
3030
3032
  domName.className = "lexwidgetname";
3031
3033
  domName.innerHTML = name || "";
3034
+ domName.title = options.title ?? domName.innerHTML;
3032
3035
  domName.style.width = options.nameWidth || LX.DEFAULT_NAME_WIDTH;
3033
3036
  element.appendChild(domName);
3034
3037
  element.domName = domName;
@@ -4288,47 +4291,70 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4288
4291
 
4289
4292
  addList( name, values, value, callback, options = {} ) {
4290
4293
 
4291
- let widget = this.create_widget(name, Widget.LIST, options);
4292
- let element = widget.domEl;
4293
-
4294
- // Show list
4295
-
4296
- let list_container = document.createElement('div');
4297
- list_container.className = "lexlist";
4298
- list_container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
4299
-
4300
- for( let i = 0; i < values.length; ++i )
4301
- {
4302
- let icon = null;
4303
- let item_value = values[i];
4304
-
4305
- if( item_value.constructor === Array )
4294
+ let widget = this.create_widget( name, Widget.LIST, options );
4295
+
4296
+ widget.onGetValue = () => {
4297
+ return value;
4298
+ };
4299
+
4300
+ widget.onSetValue = ( newValue ) => {
4301
+ listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
4302
+ const idx = values.indexOf( newValue );
4303
+ if( idx == -1 ) return;
4304
+ listContainer.children[ idx ].classList.toggle( 'selected' );
4305
+ value = newValue;
4306
+ this._trigger( new IEvent( name, newValue ), callback );
4307
+ };
4308
+
4309
+ widget.updateValues = ( newValues ) => {
4310
+
4311
+ values = newValues;
4312
+ listContainer.innerHTML = "";
4313
+
4314
+ for( let i = 0; i < values.length; ++i )
4306
4315
  {
4307
- icon = item_value[1];
4308
- item_value = item_value[0];
4316
+ let icon = null;
4317
+ let itemValue = values[ i ];
4318
+
4319
+ if( itemValue.constructor === Array )
4320
+ {
4321
+ icon = itemValue[ 1 ];
4322
+ itemValue = itemValue[ 0 ];
4323
+ }
4324
+
4325
+ let listElement = document.createElement( 'div' );
4326
+ listElement.className = "lexlistitem" + ( value == itemValue ? " selected" : "" );
4327
+ listElement.innerHTML = "<span>" + itemValue + "</span>" + ( icon ? "<a class='" + icon + "'></a>" : "" );
4328
+
4329
+ listElement.addEventListener( 'click', e => {
4330
+ listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
4331
+ listElement.classList.toggle( 'selected' );
4332
+ value = itemValue;
4333
+ this._trigger( new IEvent( name, itemValue, e ), callback );
4334
+ });
4335
+
4336
+ listContainer.appendChild( listElement );
4309
4337
  }
4310
-
4311
- let list_item = document.createElement('div');
4312
- list_item.className = "lexlistitem" + (value == item_value ? " selected" : "");
4313
- list_item.innerHTML = "<span>" + item_value + "</span>" + (icon ? "<a class='" + icon + "'></a>" : "");
4314
-
4315
- list_item.addEventListener('click', (e) => {
4316
- list_container.querySelectorAll('.lexlistitem').forEach( e => e.classList.remove('selected'));
4317
- list_item.classList.toggle( 'selected' );
4318
- this._trigger( new IEvent(name, item_value, e), callback );
4319
- });
4320
-
4321
- list_container.appendChild(list_item);
4322
- }
4323
-
4338
+ };
4339
+
4340
+ let element = widget.domEl;
4341
+
4342
+ // Show list
4343
+
4344
+ let listContainer = document.createElement( 'div' );
4345
+ listContainer.className = "lexlist";
4346
+ listContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
4347
+
4348
+ widget.updateValues( values );
4349
+
4324
4350
  // Remove branch padding and margins
4325
- if(!widget.name) {
4351
+ if( !widget.name ) {
4326
4352
  element.className += " noname";
4327
- list_container.style.width = "100%";
4353
+ listContainer.style.width = "100%";
4328
4354
  }
4329
-
4330
- element.appendChild(list_container);
4331
-
4355
+
4356
+ element.appendChild( listContainer );
4357
+
4332
4358
  return widget;
4333
4359
  }
4334
4360
 
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.24",
11
+ version: "0.1.25",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -2175,6 +2175,8 @@ class Widget {
2175
2175
 
2176
2176
  if( this.onSetValue )
2177
2177
  this.onSetValue( value );
2178
+
2179
+ console.warn("Can't set value of " + this.typeName());
2178
2180
  }
2179
2181
 
2180
2182
  oncontextmenu(e) {
@@ -3025,6 +3027,7 @@ class Panel {
3025
3027
  let domName = document.createElement('div');
3026
3028
  domName.className = "lexwidgetname";
3027
3029
  domName.innerHTML = name || "";
3030
+ domName.title = options.title ?? domName.innerHTML;
3028
3031
  domName.style.width = options.nameWidth || LX.DEFAULT_NAME_WIDTH;
3029
3032
  element.appendChild(domName);
3030
3033
  element.domName = domName;
@@ -4284,46 +4287,69 @@ class Panel {
4284
4287
 
4285
4288
  addList( name, values, value, callback, options = {} ) {
4286
4289
 
4287
- let widget = this.create_widget(name, Widget.LIST, options);
4288
- let element = widget.domEl;
4290
+ let widget = this.create_widget( name, Widget.LIST, options );
4289
4291
 
4290
- // Show list
4292
+ widget.onGetValue = () => {
4293
+ return value;
4294
+ };
4291
4295
 
4292
- let list_container = document.createElement('div');
4293
- list_container.className = "lexlist";
4294
- list_container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
4296
+ widget.onSetValue = ( newValue ) => {
4297
+ listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
4298
+ const idx = values.indexOf( newValue );
4299
+ if( idx == -1 ) return;
4300
+ listContainer.children[ idx ].classList.toggle( 'selected' );
4301
+ value = newValue;
4302
+ this._trigger( new IEvent( name, newValue ), callback );
4303
+ };
4295
4304
 
4296
- for( let i = 0; i < values.length; ++i )
4297
- {
4298
- let icon = null;
4299
- let item_value = values[i];
4305
+ widget.updateValues = ( newValues ) => {
4300
4306
 
4301
- if( item_value.constructor === Array )
4307
+ values = newValues;
4308
+ listContainer.innerHTML = "";
4309
+
4310
+ for( let i = 0; i < values.length; ++i )
4302
4311
  {
4303
- icon = item_value[1];
4304
- item_value = item_value[0];
4312
+ let icon = null;
4313
+ let itemValue = values[ i ];
4314
+
4315
+ if( itemValue.constructor === Array )
4316
+ {
4317
+ icon = itemValue[ 1 ];
4318
+ itemValue = itemValue[ 0 ];
4319
+ }
4320
+
4321
+ let listElement = document.createElement( 'div' );
4322
+ listElement.className = "lexlistitem" + ( value == itemValue ? " selected" : "" );
4323
+ listElement.innerHTML = "<span>" + itemValue + "</span>" + ( icon ? "<a class='" + icon + "'></a>" : "" );
4324
+
4325
+ listElement.addEventListener( 'click', e => {
4326
+ listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
4327
+ listElement.classList.toggle( 'selected' );
4328
+ value = itemValue;
4329
+ this._trigger( new IEvent( name, itemValue, e ), callback );
4330
+ });
4331
+
4332
+ listContainer.appendChild( listElement );
4305
4333
  }
4334
+ };
4306
4335
 
4307
- let list_item = document.createElement('div');
4308
- list_item.className = "lexlistitem" + (value == item_value ? " selected" : "");
4309
- list_item.innerHTML = "<span>" + item_value + "</span>" + (icon ? "<a class='" + icon + "'></a>" : "");
4336
+ let element = widget.domEl;
4310
4337
 
4311
- list_item.addEventListener('click', (e) => {
4312
- list_container.querySelectorAll('.lexlistitem').forEach( e => e.classList.remove('selected'));
4313
- list_item.classList.toggle( 'selected' );
4314
- this._trigger( new IEvent(name, item_value, e), callback );
4315
- });
4338
+ // Show list
4316
4339
 
4317
- list_container.appendChild(list_item);
4318
- }
4340
+ let listContainer = document.createElement( 'div' );
4341
+ listContainer.className = "lexlist";
4342
+ listContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
4343
+
4344
+ widget.updateValues( values );
4319
4345
 
4320
4346
  // Remove branch padding and margins
4321
- if(!widget.name) {
4347
+ if( !widget.name ) {
4322
4348
  element.className += " noname";
4323
- list_container.style.width = "100%";
4349
+ listContainer.style.width = "100%";
4324
4350
  }
4325
4351
 
4326
- element.appendChild(list_container);
4352
+ element.appendChild( listContainer );
4327
4353
 
4328
4354
  return widget;
4329
4355
  }
package/changelog.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # lexgui.js changelog
2
2
 
3
+ ## 0.1.25
4
+
5
+ Code Editor:
6
+ - Added Ctrl+K + C/U Shortcuts to Comment/Uncomment lines.
7
+ - Improved WGSL highlighting.
8
+ - Minor bug fixes.
9
+
10
+ Added title attribute by default to Widget Name Dom elements.
11
+ Added value getter/setter for LIST widget.
12
+ Added updateValues method for modify list options in LIST widget.
13
+
3
14
  ## 0.1.24
4
15
 
5
16
  Code Editor:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",