lexgui 0.1.23 → 0.1.24

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.
@@ -333,7 +333,7 @@ class CodeEditor {
333
333
 
334
334
  // Add main cursor
335
335
  {
336
- this._addCursor( 0, 0, true );
336
+ this._addCursor( 0, 0, true, true );
337
337
 
338
338
  Object.defineProperty( this, 'line', {
339
339
  get: (v) => { return this._getCurrentCursor().line }
@@ -739,14 +739,18 @@ class CodeEditor {
739
739
  if(idx > 0) this.cursorToString( cursor, prestring );
740
740
  this.setScrollLeft( 0 );
741
741
 
742
+ // Merge cursors
743
+ this.mergeCursors( ln );
744
+
742
745
  if( e.shiftKey && !e.cancelShift )
743
746
  {
744
747
  // Get last selection range
745
748
  if( this.selection )
746
- lastX += this.selection.chars;
749
+ lastX += this.selection.chars;
747
750
 
748
751
  if( !this.selection )
749
752
  this.startSelection( cursor );
753
+
750
754
  var string = this.code.lines[ ln ].substring( idx, lastX );
751
755
  if( this.selection.sameLine() )
752
756
  this.selection.selectInline( idx, cursor.line, this.measureString( string ) );
@@ -781,6 +785,9 @@ class CodeEditor {
781
785
 
782
786
  const last_char = ( this.code.clientWidth / this.charWidth )|0;
783
787
  this.setScrollLeft( cursor.position >= last_char ? ( cursor.position - last_char ) * this.charWidth : 0 );
788
+
789
+ // Merge cursors
790
+ this.mergeCursors( ln );
784
791
  });
785
792
 
786
793
  this.action( 'Enter', true, ( ln, cursor, e ) => {
@@ -806,6 +813,7 @@ class CodeEditor {
806
813
  this.code.lines.splice( cursor.line + 1, 0, "" );
807
814
  this.code.lines[cursor.line + 1] = this.code.lines[ ln ].substr( cursor.position ); // new line (below)
808
815
  this.code.lines[ ln ] = this.code.lines[ ln ].substr( 0, cursor.position ); // line above
816
+
809
817
  this.lineDown( cursor, true );
810
818
 
811
819
  // Check indentation
@@ -864,24 +872,20 @@ class CodeEditor {
864
872
  if( e.shiftKey ) {
865
873
  if( !this.selection )
866
874
  this.startSelection( cursor );
875
+ } else {
876
+ this.endSelection();
877
+ }
867
878
 
868
- this.lineDown( cursor );
869
-
870
- var letter = this.getCharAtPos( cursor );
871
- if( !letter ) {
872
- this.cursorToPosition( cursor, Math.max(this.code.lines[ cursor.line ].length - 1, 0) );
873
- }
879
+ const canGoDown = this.lineDown( cursor );
880
+ const letter = this.getCharAtPos( cursor );
881
+
882
+ // Go to end of line if out of range
883
+ if( !letter || !canGoDown ) {
884
+ this.actions[ 'End' ].callback( cursor.line, cursor, e );
885
+ }
874
886
 
887
+ if( e.shiftKey ) {
875
888
  this.processSelection( e );
876
- } else {
877
-
878
- if( this.code.lines[ ln + 1 ] == undefined )
879
- return;
880
- this.endSelection();
881
- this.lineDown( cursor );
882
- // Go to end of line if out of line
883
- var letter = this.getCharAtPos( cursor );
884
- if( !letter ) this.actions['End'].callback(cursor.line, cursor, e);
885
889
  }
886
890
  }
887
891
  // Move down autocomplete selection
@@ -1212,25 +1216,36 @@ class CodeEditor {
1212
1216
  }
1213
1217
  }
1214
1218
 
1215
- _addCursor( line = 0, position = 0, isMain = false ) {
1219
+ _addCursor( line = 0, position = 0, force, isMain = false ) {
1220
+
1221
+ // If cursor in that position exists, remove it instead..
1222
+ const exists = Array.from( this.cursors.children ).find( v => v.position == position && v.line == line );
1223
+ if( exists && !force )
1224
+ {
1225
+ if( !exists.isMain )
1226
+ exists.remove();
1227
+
1228
+ return;
1229
+ }
1216
1230
 
1217
1231
  let cursor = document.createElement( 'div' );
1218
1232
  cursor.className = "cursor";
1219
1233
  cursor.innerHTML = " ";
1220
- cursor.isMainCursor = isMain;
1234
+ cursor.isMain = isMain;
1221
1235
  cursor._left = position * this.charWidth;
1222
1236
  cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
1223
1237
  cursor._top = line * this.lineHeight;
1224
1238
  cursor.style.top = cursor._top + "px";
1225
1239
  cursor._position = position;
1226
1240
  cursor._line = line;
1227
- cursor.print = (function() { console.log( this, this._line, this._position ) }).bind( cursor );
1241
+ cursor.print = (function() { console.log( this._line, this._position ) }).bind( cursor );
1242
+ cursor.isLast = (function() { return this.cursors.lastChild == cursor; }).bind( this );
1228
1243
 
1229
1244
  Object.defineProperty( cursor, 'line', {
1230
1245
  get: (v) => { return cursor._line },
1231
1246
  set: (v) => {
1232
1247
  cursor._line = v;
1233
- if( cursor.isMainCursor ) this._setActiveLine( v );
1248
+ if( cursor.isMain ) this._setActiveLine( v );
1234
1249
  }
1235
1250
  } );
1236
1251
 
@@ -1238,7 +1253,7 @@ class CodeEditor {
1238
1253
  get: (v) => { return cursor._position },
1239
1254
  set: (v) => {
1240
1255
  cursor._position = v;
1241
- if( cursor.isMainCursor ) this._updateDataInfoPanel( "@cursor-pos", "Col " + v );
1256
+ if( cursor.isMain ) this._updateDataInfoPanel( "@cursor-pos", "Col " + v );
1242
1257
  }
1243
1258
  } );
1244
1259
 
@@ -1273,6 +1288,10 @@ class CodeEditor {
1273
1288
 
1274
1289
  _addUndoStep( cursor, force, deleteRedo = true ) {
1275
1290
 
1291
+ // Only the mainc cursor stores undo steps
1292
+ if( !cursor.isMain )
1293
+ return;
1294
+
1276
1295
  const d = new Date();
1277
1296
  const current = d.getTime();
1278
1297
 
@@ -1281,7 +1300,7 @@ class CodeEditor {
1281
1300
  if( !this._lastTime ) {
1282
1301
  this._lastTime = current;
1283
1302
  } else {
1284
- if( ( current - this._lastTime ) > 3000 ){
1303
+ if( ( current - this._lastTime ) > 2000 ){
1285
1304
  this._lastTime = null;
1286
1305
  } else {
1287
1306
  // If time not enough, reset timer
@@ -1299,22 +1318,78 @@ class CodeEditor {
1299
1318
 
1300
1319
  this.code.undoSteps.push( {
1301
1320
  lines: LX.deepCopy( this.code.lines ),
1302
- cursor: this.saveCursor( cursor ),
1303
- line: cursor.line,
1304
- position: cursor.position
1321
+ cursors: this.saveCursors()
1305
1322
  } );
1306
1323
  }
1307
1324
 
1325
+ _doUndo( cursor ) {
1326
+
1327
+ if( !this.code.undoSteps.length )
1328
+ return;
1329
+
1330
+ this._addRedoStep( cursor );
1331
+
1332
+ // Extract info from the last code state
1333
+ const step = this.code.undoSteps.pop();
1334
+
1335
+ // Set old state lines
1336
+ this.code.lines = step.lines;
1337
+ this.processLines();
1338
+
1339
+ this._removeSecondaryCursors();
1340
+
1341
+ for( let i = 0; i < step.cursors.length; ++i )
1342
+ {
1343
+ var currentCursor = this.cursors.children[ i ];
1344
+
1345
+ // Generate new if needed
1346
+ if( !currentCursor )
1347
+ currentCursor = this._addCursor();
1348
+
1349
+ this.restoreCursor( currentCursor, step.cursors[ i ] );
1350
+ }
1351
+ }
1352
+
1308
1353
  _addRedoStep( cursor ) {
1309
1354
 
1355
+ // Only the mainc cursor stores redo steps
1356
+ if( !cursor.isMain )
1357
+ return;
1358
+
1310
1359
  this.code.redoSteps.push( {
1311
1360
  lines: LX.deepCopy( this.code.lines ),
1312
- cursor: this.saveCursor( cursor ),
1313
- line: cursor.line,
1314
- position: cursor.position
1361
+ cursors: this.saveCursors()
1315
1362
  } );
1316
1363
  }
1317
1364
 
1365
+ _doRedo( cursor ) {
1366
+
1367
+ if( !this.code.redoSteps.length )
1368
+ return;
1369
+
1370
+ this._addUndoStep( cursor, true, false);
1371
+
1372
+ // Extract info from the next saved code state
1373
+ const step = this.code.redoSteps.pop();
1374
+
1375
+ // Set old state lines
1376
+ this.code.lines = step.lines;
1377
+ this.processLines();
1378
+
1379
+ this._removeSecondaryCursors();
1380
+
1381
+ for( let i = 0; i < step.cursors.length; ++i )
1382
+ {
1383
+ var currentCursor = this.cursors.children[ i ];
1384
+
1385
+ // Generate new if needed
1386
+ if( !currentCursor )
1387
+ currentCursor = this._addCursor();
1388
+
1389
+ this.restoreCursor( currentCursor, step.cursors[ i ] );
1390
+ }
1391
+ }
1392
+
1318
1393
  _changeLanguage( lang ) {
1319
1394
 
1320
1395
  this.code.language = lang;
@@ -1610,7 +1685,7 @@ class CodeEditor {
1610
1685
 
1611
1686
  processMouse( e ) {
1612
1687
 
1613
- if( !e.target.classList.contains('code') ) return;
1688
+ if( !e.target.classList.contains('code') && !e.target.classList.contains('codetabsarea') ) return;
1614
1689
  if( !this.code ) return;
1615
1690
 
1616
1691
  var cursor = this._getCurrentCursor();
@@ -1618,11 +1693,8 @@ class CodeEditor {
1618
1693
  var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
1619
1694
 
1620
1695
  // Discard out of lines click...
1621
- if( e.type != 'contextmenu' )
1622
- {
1623
- var ln = (mouse_pos[1] / this.lineHeight)|0;
1624
- if(this.code.lines[ ln ] == undefined) return;
1625
- }
1696
+ var ln = ( mouse_pos[ 1 ] / this.lineHeight ) | 0;
1697
+ if( ln < 0 ) return;
1626
1698
 
1627
1699
  if( e.type == 'mousedown' )
1628
1700
  {
@@ -1730,11 +1802,12 @@ class CodeEditor {
1730
1802
  var position = [( e.clientX - code_rect.x ) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
1731
1803
  var ln = (position[ 1 ] / this.lineHeight)|0;
1732
1804
 
1733
- if( this.code.lines[ ln ] == undefined )
1734
- return;
1805
+ // Check out of range line
1806
+ const outOfRange = ln > this.code.lines.length - 1;
1807
+ ln = Math.min( ln, this.code.lines.length - 1 );
1735
1808
 
1736
1809
  var ch = ( ( position[ 0 ] - parseInt( this.xPadding ) + 3) / this.charWidth )|0;
1737
- var string = this.code.lines[ ln ].slice( 0, ch );
1810
+ var string = outOfRange ? this.code.lines[ ln ] : this.code.lines[ ln ].slice( 0, ch );
1738
1811
 
1739
1812
  // Move main cursor there...
1740
1813
  if( !e.altKey )
@@ -1911,10 +1984,29 @@ class CodeEditor {
1911
1984
  }
1912
1985
  }
1913
1986
 
1914
- async processKey( event ) {
1987
+ async processKey( e ) {
1915
1988
 
1916
1989
  const numCursors = this.cursors.childElementCount;
1917
1990
 
1991
+ if( !this.code || e.srcElement.constructor != HTMLDivElement )
1992
+ return;
1993
+
1994
+ const key = e.key ?? e.detail.key;
1995
+ const target = e.detail.targetCursor;
1996
+
1997
+ if( target !== undefined )
1998
+ {
1999
+ this.processKeyAtTargetCursor( e, key, target );
2000
+ return;
2001
+ }
2002
+
2003
+ // By cursor keys
2004
+
2005
+ this._lastProcessedCursorIndex = null;
2006
+
2007
+ var lastProcessedCursor = null;
2008
+ var cursorOffset = new LX.vec2( 0, 0 );
2009
+
1918
2010
  for( var i = 0; i < numCursors; i++ )
1919
2011
  {
1920
2012
  let cursor = this.cursors.children[ i ];
@@ -1923,16 +2015,83 @@ class CodeEditor {
1923
2015
  if( !cursor )
1924
2016
  break;
1925
2017
 
1926
- this.processKeyAtCursor( event, cursor );
2018
+ // Arrows don't modify code lines.. And only add offset if in the same line
2019
+ if( lastProcessedCursor && lastProcessedCursor.line == cursor.line && !key.includes( 'Arrow' ) )
2020
+ {
2021
+ cursor.position += cursorOffset.x;
2022
+ cursor.line += cursorOffset.y;
2023
+
2024
+ this.relocateCursors();
2025
+ }
2026
+
2027
+ lastProcessedCursor = this.saveCursor( cursor );
2028
+ this._lastProcessedCursorIndex = i;
2029
+
2030
+ this._processKeyAtCursor( e, key, cursor );
2031
+
2032
+ cursorOffset.x += ( cursor.position - lastProcessedCursor.position );
2033
+ cursorOffset.y += ( cursor.line - lastProcessedCursor.line );
1927
2034
  }
2035
+
2036
+ // Global keys
2037
+
2038
+ this._processGlobalKeys( e, key );
2039
+
2040
+ // Clear tmp
2041
+
2042
+ delete this._lastProcessedCursorIndex;
1928
2043
  }
1929
2044
 
1930
- async processKeyAtCursor( e, cursor ) {
2045
+ async processKeyAtTargetCursor( e, key, targetIdx ) {
1931
2046
 
1932
- if( !this.code || e.srcElement.constructor != HTMLDivElement )
2047
+ let cursor = this.cursors.children[ targetIdx ];
2048
+
2049
+ // We could delete secondary cursor while iterating..
2050
+ if( !cursor )
1933
2051
  return;
1934
2052
 
1935
- var key = e.key ?? e.detail.key;
2053
+ this._processKeyAtCursor( e, key, cursor );
2054
+ this._processGlobalKeys( e, key );
2055
+ }
2056
+
2057
+ async _processGlobalKeys( e, key ) {
2058
+
2059
+ let cursor = this._getCurrentCursor();
2060
+
2061
+ if( e.ctrlKey || e.metaKey )
2062
+ {
2063
+ e.preventDefault();
2064
+
2065
+ switch( key.toLowerCase() ) {
2066
+ case 'a': // select all
2067
+ this.selectAll();
2068
+ break;
2069
+ case 'f': // find/search
2070
+ this.showSearchBox();
2071
+ break;
2072
+ case 'g': // find line
2073
+ this.showSearchLineBox();
2074
+ break;
2075
+ case 's': // save
2076
+ this.onsave( this.getText() );
2077
+ break;
2078
+ case 'y': // redo
2079
+ this._doRedo( cursor );
2080
+ break;
2081
+ case 'z': // undo
2082
+ this._doUndo( cursor );
2083
+ break;
2084
+ case '+': // increase size
2085
+ this._increaseFontSize();
2086
+ break;
2087
+ case '-': // decrease size
2088
+ this._decreaseFontSize();
2089
+ break;
2090
+ }
2091
+ }
2092
+ }
2093
+
2094
+ async _processKeyAtCursor( e, key, cursor ) {
1936
2095
 
1937
2096
  const skip_undo = e.detail.skip_undo ?? false;
1938
2097
 
@@ -1945,32 +2104,20 @@ class CodeEditor {
1945
2104
 
1946
2105
  // Check combinations
1947
2106
 
2107
+ const isLastCursor = cursor.isLast();
2108
+
1948
2109
  if( e.ctrlKey || e.metaKey )
1949
2110
  {
1950
2111
  switch( key.toLowerCase() ) {
1951
- case 'a': // select all
1952
- e.preventDefault();
1953
- this.selectAll( cursor );
1954
- break;
1955
2112
  case 'c': // copy
2113
+ // TODO: COPY TEXT FROM EVERY CURSOR
1956
2114
  this._copyContent( cursor );
1957
2115
  return;
1958
2116
  case 'd': // duplicate line
1959
2117
  e.preventDefault();
2118
+ // TODO: UPDATE NEXT CURSOR ON MODIFY STATE
1960
2119
  this._duplicateLine( lidx, cursor );
1961
2120
  return;
1962
- case 'f': // find/search
1963
- e.preventDefault();
1964
- this.showSearchBox();
1965
- return;
1966
- case 'g': // find line
1967
- e.preventDefault();
1968
- this.showSearchLineBox();
1969
- return;
1970
- case 's': // save
1971
- e.preventDefault();
1972
- this.onsave( this.getText() );
1973
- return;
1974
2121
  case 'v': // paste
1975
2122
  this._pasteContent( cursor );
1976
2123
  return;
@@ -1978,39 +2125,13 @@ class CodeEditor {
1978
2125
  this._cutContent( cursor );
1979
2126
  this.hideAutoCompleteBox();
1980
2127
  return;
1981
- case 'y': // redo
1982
- if( !this.code.redoSteps.length )
1983
- return;
1984
- this._addUndoStep( cursor, true, false);
1985
- const redo_step = this.code.redoSteps.pop();
1986
- this.code.lines = redo_step.lines;
1987
- this.processLines();
1988
- this.restoreCursor( cursor, redo_step.cursor );
1989
- return;
1990
- case 'z': // undo
1991
- if( !this.code.undoSteps.length )
1992
- return;
1993
- this._addRedoStep( cursor );
1994
- const undo_step = this.code.undoSteps.pop();
1995
- this.code.lines = undo_step.lines;
1996
- this.processLines();
1997
- this.restoreCursor( cursor, undo_step.cursor );
1998
- return;
1999
- case '+': // increase size
2000
- e.preventDefault();
2001
- this._increaseFontSize();
2002
- return;
2003
- case '-': // decrease size
2004
- e.preventDefault();
2005
- this._decreaseFontSize();
2006
- return;
2007
2128
  case 'arrowdown': // add cursor below only for the main cursor..
2008
- if( cursor.isMainCursor && this.code.lines[ lidx + 1 ] != undefined )
2129
+ if( isLastCursor && this.code.lines[ lidx + 1 ] != undefined )
2009
2130
  {
2010
- var new_cursor = this._addCursor( cursor.line, cursor.position );
2131
+ var new_cursor = this._addCursor( cursor.line, cursor.position, true );
2011
2132
  this.lineDown( new_cursor );
2012
- return;
2013
2133
  }
2134
+ return;
2014
2135
  }
2015
2136
  }
2016
2137
 
@@ -2136,6 +2257,9 @@ class CodeEditor {
2136
2257
  async _pasteContent( cursor ) {
2137
2258
 
2138
2259
  let text = await navigator.clipboard.readText();
2260
+
2261
+ this._addUndoStep( cursor, true );
2262
+
2139
2263
  this.appendText( text, cursor );
2140
2264
  }
2141
2265
 
@@ -2152,18 +2276,18 @@ class CodeEditor {
2152
2276
  if( this.selection ) this.selection.invertIfNecessary();
2153
2277
 
2154
2278
  const separator = "_NEWLINE_";
2155
- let code = this.code.lines.join(separator);
2279
+ let code = this.code.lines.join( separator );
2156
2280
 
2157
2281
  // Get linear start index
2158
2282
  let index = 0;
2159
2283
 
2160
- for(let i = 0; i <= this.selection.fromY; i++)
2161
- index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length);
2284
+ for( let i = 0; i <= this.selection.fromY; i++ )
2285
+ index += ( i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length );
2162
2286
 
2163
2287
  index += this.selection.fromY * separator.length;
2164
- const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
2165
- const text = code.substr(index, num_chars);
2166
- const lines = text.split(separator);
2288
+ const num_chars = this.selection.chars + ( this.selection.toY - this.selection.fromY ) * separator.length;
2289
+ const text = code.substr( index, num_chars );
2290
+ const lines = text.split( separator );
2167
2291
  text_to_copy = lines.join('\n');
2168
2292
  }
2169
2293
 
@@ -2254,8 +2378,6 @@ class CodeEditor {
2254
2378
 
2255
2379
  processLines( mode ) {
2256
2380
 
2257
- const start = performance.now();
2258
-
2259
2381
  var code_html = "";
2260
2382
 
2261
2383
  // Reset all lines content
@@ -2804,6 +2926,7 @@ class CodeEditor {
2804
2926
  return false;
2805
2927
 
2806
2928
  cursor.line++;
2929
+
2807
2930
  this.cursorToBottom( cursor, resetLeft );
2808
2931
 
2809
2932
  return true;
@@ -2876,16 +2999,25 @@ class CodeEditor {
2876
2999
  delete this._lastSelectionKeyDir;
2877
3000
  }
2878
3001
 
2879
- selectAll( cursor ) {
3002
+ selectAll() {
3003
+
3004
+ // Use main cursor
3005
+ this._removeSecondaryCursors();
2880
3006
 
3007
+ var cursor = this._getCurrentCursor();
2881
3008
  this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
3009
+
2882
3010
  this.startSelection( cursor );
3011
+
2883
3012
  const nlines = this.code.lines.length - 1;
2884
3013
  this.selection.toX = this.code.lines[ nlines ].length;
2885
3014
  this.selection.toY = nlines;
3015
+
2886
3016
  this.cursorToPosition( cursor, this.selection.toX );
2887
3017
  this.cursorToLine( cursor, this.selection.toY );
3018
+
2888
3019
  this.processSelection( null, true );
3020
+
2889
3021
  this.hideAutoCompleteBox();
2890
3022
  }
2891
3023
 
@@ -2988,22 +3120,51 @@ class CodeEditor {
2988
3120
 
2989
3121
  saveCursor( cursor, state = {} ) {
2990
3122
 
2991
- state.top = cursor._top;
2992
- state.left = cursor._left;
2993
- state.line = cursor.line;
2994
3123
  state.position = cursor.position;
2995
-
3124
+ state.line = cursor.line;
2996
3125
  return state;
2997
3126
  }
2998
3127
 
3128
+ saveCursors() {
3129
+
3130
+ var cursors = [];
3131
+
3132
+ for( let cursor of this.cursors.children )
3133
+ {
3134
+ cursors.push( this.saveCursor( cursor ) );
3135
+ }
3136
+
3137
+ return cursors;
3138
+ }
3139
+
3140
+ relocateCursors() {
3141
+
3142
+ for( let cursor of this.cursors.children )
3143
+ {
3144
+ cursor._left = cursor.position * this.charWidth;
3145
+ cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
3146
+ cursor._top = cursor.line * this.lineHeight;
3147
+ cursor.style.top = "calc(" + cursor._top + "px)";
3148
+ }
3149
+ }
3150
+
3151
+ mergeCursors( line ) {
3152
+
3153
+ console.assert( line >= 0 );
3154
+ const cursorsInLine = Array.from( this.cursors.children ).filter( v => v.line == line );
3155
+
3156
+ while( cursorsInLine.length > 1 )
3157
+ cursorsInLine.pop().remove();
3158
+ }
3159
+
2999
3160
  restoreCursor( cursor, state ) {
3000
3161
 
3001
- cursor.line = state.line ?? 0;
3002
3162
  cursor.position = state.position ?? 0;
3163
+ cursor.line = state.line ?? 0;
3003
3164
 
3004
- cursor._left = state.left ?? 0;
3165
+ cursor._left = cursor.position * this.charWidth;
3005
3166
  cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
3006
- cursor._top = state.top ?? 0;
3167
+ cursor._top = cursor.line * this.lineHeight;
3007
3168
  cursor.style.top = "calc(" + cursor._top + "px)";
3008
3169
  }
3009
3170
 
@@ -3038,7 +3199,8 @@ class CodeEditor {
3038
3199
  for( var i = 0; i < n; ++i ) {
3039
3200
  this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
3040
3201
  skip_undo: true,
3041
- key: ' '
3202
+ key: ' ',
3203
+ targetCursor: this._lastProcessedCursorIndex
3042
3204
  }}));
3043
3205
  }
3044
3206
  }
@@ -3664,7 +3826,13 @@ class CodeEditor {
3664
3826
  r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
3665
3827
  this.lineHeight = row_pixels;
3666
3828
 
3667
- this.processLines(); // ... it's necessary?
3829
+ // Relocate cursors
3830
+
3831
+ this.relocateCursors();
3832
+
3833
+ // Resize the code area
3834
+
3835
+ this.processLines();
3668
3836
  }
3669
3837
 
3670
3838
  _decreaseFontSize() {
@@ -3684,7 +3852,13 @@ class CodeEditor {
3684
3852
  r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
3685
3853
  this.lineHeight = row_pixels;
3686
3854
 
3687
- this.processLines(); // ... it's necessary?
3855
+ // Relocate cursors
3856
+
3857
+ this.relocateCursors();
3858
+
3859
+ // Resize the code area
3860
+
3861
+ this.processLines();
3688
3862
  }
3689
3863
 
3690
3864
  _clearTmpVariables() {
package/build/lexgui.css CHANGED
@@ -5,6 +5,7 @@
5
5
  --global-font: 'Noto Sans', sans-serif; /*'Rubik', sans-serif; */
6
6
  --global-selected: #3e57e4;
7
7
  --global-selected-light: #7b8ae2;
8
+ --global-selected-dark: #344bccb7;
8
9
  --global-color-primary: #232323;
9
10
  --global-color-secondary: #343434;
10
11
  --global-color-terciary: #444;
@@ -1786,10 +1787,10 @@ meter::-webkit-meter-even-less-good-value {
1786
1787
  z-index: 102;
1787
1788
  position: absolute;
1788
1789
  padding-right: 20px;
1789
- border-bottom-left-radius: 6px;
1790
- border-bottom-right-radius: 6px;
1791
- box-shadow: 0 0 6px black !important;
1790
+ border-radius: 4px;
1791
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.63) !important;
1792
1792
  background-color: var(--global-blur-background);
1793
+ border: 1px solid #91909036;
1793
1794
  }
1794
1795
 
1795
1796
  .lexcontextmenubox:before {
@@ -1802,13 +1803,14 @@ meter::-webkit-meter-even-less-good-value {
1802
1803
  -webkit-backdrop-filter: blur(10px);
1803
1804
  backdrop-filter: blur(10px);
1804
1805
  z-index:-1;
1806
+ border-radius: 4px;
1805
1807
  }
1806
1808
 
1807
1809
  .lexcontextmenubox .lexcontextmenuentry {
1808
1810
  width: 100%;
1809
1811
  color: #c0c4cbe3;
1810
1812
  padding: 3px;
1811
- padding-left: 8px;
1813
+ padding-left: 10px;
1812
1814
  padding-right: 10px;
1813
1815
  padding-bottom: 4px;
1814
1816
  cursor: pointer;
@@ -1816,7 +1818,16 @@ meter::-webkit-meter-even-less-good-value {
1816
1818
  -moz-user-select: none;
1817
1819
  -ms-user-select: none;
1818
1820
  user-select: none;
1819
- border-left: 2px solid var(--global-selected-light);
1821
+ }
1822
+
1823
+ .lexcontextmenubox .lexcontextmenuentry:first-child {
1824
+ border-top-left-radius: 4px;
1825
+ border-top-right-radius: 4px;
1826
+ }
1827
+
1828
+ .lexcontextmenubox .lexcontextmenuentry:last-child {
1829
+ border-bottom-left-radius: 4px;
1830
+ border-bottom-right-radius: 4px;
1820
1831
  }
1821
1832
 
1822
1833
  .lexcontextmenubox .lexcontextmenuentry.cmtitle {
@@ -1848,17 +1859,17 @@ meter::-webkit-meter-even-less-good-value {
1848
1859
  float: right;
1849
1860
  margin-right: 0px;
1850
1861
  margin-top: 10px;
1851
- font-size: 11px;
1862
+ font-size: 10px;
1852
1863
  }
1853
1864
 
1854
1865
  /* submenu specified arrow */
1855
1866
  .lexcontextmenubox .lexcontextmenuentry a.fa-xs {
1856
1867
  float: right;
1857
- margin-top: -7px;
1868
+ margin-top: -8px;
1858
1869
  }
1859
1870
 
1860
1871
  .lexcontextmenubox .lexcontextmenuentry:hover:not(.cmtitle) {
1861
- background-color: var(--global-selected);
1872
+ background-color: var(--global-selected-dark);
1862
1873
  color: #f5f5f5;
1863
1874
  }
1864
1875
 
@@ -2950,7 +2961,8 @@ pre .line-gutter {
2950
2961
  height: var(--code-editor-row-height);
2951
2962
  position: absolute;
2952
2963
  border-left: 3px solid #fff !important;
2953
- z-index: 0 !important;
2964
+ z-index: 1 !important;
2965
+ opacity: 0.6;
2954
2966
  left: 0px;
2955
2967
  top: 0px;
2956
2968
  }
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.23",
15
+ version: "0.1.24",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.23",
11
+ version: "0.1.24",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
package/changelog.md CHANGED
@@ -1,38 +1,55 @@
1
1
  # lexgui.js changelog
2
2
 
3
+ ## 0.1.24
4
+
5
+ Code Editor:
6
+ - Improved single and multiple cursor usability.
7
+ - Cursor can be added or removed using "Alt+LeftClick".
8
+ - Fixed clicks outside the code area.
9
+ - Minor bug fixes.
10
+
3
11
  ## 0.1.23
4
12
 
5
- Begin integration of multiple cursors ("Ctrl+ArrowDown").
6
- Code tabs have new VS Code-alike style.
7
- Improved CSS highlighting.
8
- Add Undo Steps to some actions that were missing.
9
- When using Ctrl+G, the selected line is now highlighted.
10
- General bug fixes.
13
+ Code Editor:
14
+ - Begin integration of multiple cursors ("Ctrl+ArrowDown").
15
+ - Code tabs have new VS Code-alike style.
16
+ - Improved CSS highlighting.
17
+ - Add Undo Steps to some actions that were missing.
18
+ - When using Ctrl+G, the selected line is now highlighted.
19
+
20
+ Minor fixes.
11
21
 
12
22
  ## 0.1.22
13
23
 
14
- Added REDO using "Ctrl+Y".
15
- Added FontSize customization. "Ctrl+PLUS", "Ctrl+MINUS" or "Ctrl+Wheel".
16
- Added "Ctrl+G" to scroll to specific line.
17
- General bug fixes.
24
+ Code Editor:
25
+ - Added REDO using "Ctrl+Y".
26
+ - Added FontSize customization. "Ctrl+PLUS", "Ctrl+MINUS" or "Ctrl+Wheel".
27
+ - Added "Ctrl+G" to scroll to specific line.
28
+
29
+ Minor fixes.
18
30
 
19
31
  ## 0.1.21
20
32
 
21
- Added "Ctrl+F" to find text in code tabs.
22
- "Shift+Backspace" deletes word at current position.
23
- Added "Markdown" syntax highlighting.
24
- Improved hightlighting of tag languages (HTML, Markdown, XML).
33
+ Code Editor:
34
+ - Added "Ctrl+F" to find text in code tabs.
35
+ - "Shift+Backspace" deletes word at current position.
36
+ - Added "Markdown" syntax highlighting.
37
+ - Improved hightlighting of tag languages (HTML, Markdown, XML).
25
38
 
26
39
  ## 0.1.20
27
40
 
28
- Active line is now hightlighted.
29
- Using CommitMono font (https://commitmono.com/) as the font for the Code Editor.
30
- Added "Rust" syntax highlighting.
31
- Improved all code selections (and some bugs fixed).
32
- Block comments are now working again (with bugs probably).
41
+ Code Editor:
42
+ - Active line is now hightlighted.
43
+ - Using CommitMono font (https://commitmono.com/) as the font for the Code Editor.
44
+ - Added "Rust" syntax highlighting.
45
+ - Improved all code selections (and some bugs fixed).
46
+ - Block comments are now working again (with bugs probably).
47
+
33
48
  Minor fixes.
34
49
 
35
50
  ## 0.1.19
36
51
 
37
- Add file explorer to Code Editor component.
52
+ Code Editor:
53
+ - Add file explorer to Code Editor component.
54
+
38
55
  Minor fixes.
@@ -55,7 +55,7 @@
55
55
  file_explorer: file_explorer
56
56
  });
57
57
 
58
- editor.loadFile( "../demo.js" );
58
+ editor.loadFile( "../data/js_sample.js" );
59
59
  // editor.loadFile( "../data/json_sample.json" );
60
60
  // editor.loadFile( "../data/css_sample.css" );
61
61
  // editor.loadFile( "../data/cpp_sample.cpp" );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",