lexgui 0.6.11 → 0.7.0

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.
Files changed (46) hide show
  1. package/README.md +14 -9
  2. package/build/{components → extensions}/audio.js +11 -11
  3. package/build/{components → extensions}/codeeditor.js +109 -74
  4. package/build/{components → extensions}/docmaker.js +10 -3
  5. package/build/{components → extensions}/imui.js +19 -12
  6. package/build/{components → extensions}/nodegraph.js +1 -1
  7. package/build/{components → extensions}/timeline.js +150 -94
  8. package/build/{components → extensions}/videoeditor.js +1 -1
  9. package/build/lexgui-docs.css +9 -9
  10. package/build/lexgui.css +489 -223
  11. package/build/lexgui.js +1771 -777
  12. package/build/lexgui.min.css +2 -2
  13. package/build/lexgui.min.js +1 -1
  14. package/build/lexgui.module.js +1803 -809
  15. package/build/lexgui.module.min.js +1 -1
  16. package/changelog.md +90 -21
  17. package/demo.js +52 -32
  18. package/examples/{all_widgets.html → all-components.html} +22 -4
  19. package/examples/{area_tabs.html → area-tabs.html} +3 -3
  20. package/examples/{asset_view.html → asset-view.html} +3 -3
  21. package/examples/{code_editor.html → code-editor.html} +4 -4
  22. package/examples/dialogs.html +3 -3
  23. package/examples/editor.html +27 -18
  24. package/examples/{immediate_ui.html → immediate-ui.html} +3 -3
  25. package/examples/index.html +8 -8
  26. package/examples/{node_graph.html → node-graph.html} +3 -3
  27. package/examples/previews/all-components.png +0 -0
  28. package/examples/previews/area-tabs.png +0 -0
  29. package/examples/previews/asset-view.png +0 -0
  30. package/examples/previews/code-editor.png +0 -0
  31. package/examples/previews/dialogs.png +0 -0
  32. package/examples/previews/editor.png +0 -0
  33. package/examples/previews/node-graph.png +0 -0
  34. package/examples/previews/side-bar.png +0 -0
  35. package/examples/previews/timeline.png +0 -0
  36. package/examples/{side_bar.html → side-bar.html} +3 -3
  37. package/examples/timeline.html +5 -5
  38. package/examples/{video_editor.html → video-editor.html} +3 -3
  39. package/examples/{video_editor2.html → video-editor2.html} +3 -3
  40. package/package.json +2 -2
  41. package/examples/previews/all_widgets.png +0 -0
  42. package/examples/previews/area_tabs.png +0 -0
  43. package/examples/previews/asset_view.png +0 -0
  44. package/examples/previews/code_editor.png +0 -0
  45. package/examples/previews/node_graph.png +0 -0
  46. package/examples/previews/side_bar.png +0 -0
@@ -4,7 +4,7 @@ if(!LX) {
4
4
  throw("lexgui.js missing!");
5
5
  }
6
6
 
7
- LX.components.push( 'Timeline' );
7
+ LX.extensions.push( 'Timeline' );
8
8
 
9
9
  /**
10
10
  * @class Timeline
@@ -103,8 +103,8 @@ class Timeline {
103
103
  this.selectedItems = []; // [trackInfo, "groupId"], contains the visible items (tracks or groups) of the timeline
104
104
  this.leftPanel = left.addPanel( { className: 'lextimelinepanel', width: "100%", height: "100%" } );
105
105
  this.trackTreesPanel = null;
106
- this.trackTreesWidget = null;
107
- this.lastTrackTreesWidgetOffset = 0; // this.trackTreesWidget.innerTree.domEl.offsetTop - canvas.offsetTop. Check draw()
106
+ this.trackTreesComponent = null;
107
+ this.lastTrackTreesComponentOffset = 0; // this.trackTreesComponent.innerTree.domEl.offsetTop - canvas.offsetTop. Check draw()
108
108
  this.updateLeftPanel();
109
109
 
110
110
  if(this.uniqueID != '') {
@@ -196,6 +196,7 @@ class Timeline {
196
196
  signal: "@on_set_time_" + this.uniqueID,
197
197
  step: 0.01, min: 0, precision: 3,
198
198
  skipSlider: true,
199
+ skipReset: true,
199
200
  nameWidth: "auto"
200
201
  });
201
202
 
@@ -205,6 +206,7 @@ class Timeline {
205
206
  units: "s",
206
207
  step: 0.01, min: 0,
207
208
  signal: "@on_set_duration_" + this.uniqueID,
209
+ skipReset: true,
208
210
  nameWidth: "auto"
209
211
  });
210
212
 
@@ -265,8 +267,8 @@ class Timeline {
265
267
  const panel = this.leftPanel;
266
268
 
267
269
  panel.sameLine( 2 );
268
- let titleWidget = panel.addTitle( "Tracks", { style: { background: "none"}, className: "fg-secondary text-lg px-4"} );
269
- let title = titleWidget.root;
270
+ let titleComponent = panel.addTitle( "Tracks", { style: { background: "none"}, className: "fg-secondary text-lg px-4"} );
271
+ let title = titleComponent.root;
270
272
 
271
273
  if( !this.disableNewTracks )
272
274
  {
@@ -291,7 +293,7 @@ class Timeline {
291
293
  {
292
294
  treeTracks = this.generateSelectedItemsTreeData();
293
295
  }
294
- this.trackTreesWidget = p.addTree(null, treeTracks, {filter: false, rename: false, draggable: false, onevent: (e) => {
296
+ this.trackTreesComponent = p.addTree(null, treeTracks, {filter: false, rename: false, draggable: false, onevent: (e) => {
295
297
  switch(e.type) {
296
298
  case LX.TreeEvent.NODE_SELECTED:
297
299
  if (e.node.trackData){
@@ -308,8 +310,8 @@ class Timeline {
308
310
  }
309
311
  }});
310
312
  // setting a name in the addTree function adds an undesired node
311
- this.trackTreesWidget.name = "tracksTrees";
312
- p.widgets[this.trackTreesWidget.name] = this.trackTreesWidget;
313
+ this.trackTreesComponent.name = "tracksTrees";
314
+ p.components[this.trackTreesComponent.name] = this.trackTreesComponent;
313
315
 
314
316
  this.trackTreesPanel = p;
315
317
  panel.attach( p.root );
@@ -374,8 +376,8 @@ class Timeline {
374
376
  return [];
375
377
  }
376
378
 
377
- const startY = minY - this.lastTrackTreesWidgetOffset + this.currentScrollInPixels;
378
- const endY = maxY - this.lastTrackTreesWidgetOffset + this.currentScrollInPixels;
379
+ const startY = minY - this.lastTrackTreesComponentOffset + this.currentScrollInPixels;
380
+ const endY = maxY - this.lastTrackTreesComponentOffset + this.currentScrollInPixels;
379
381
 
380
382
  const startIdx = Math.max( 0, Math.floor( startY / this.trackHeight ) );
381
383
  const endIdx = Math.min( elements.length-1, Math.floor( endY / this.trackHeight ) ) + 1;
@@ -400,13 +402,9 @@ class Timeline {
400
402
  */
401
403
  setAnimationClip( animation, needsToProcess = true ) {
402
404
 
403
- if ( this.unSelectAllKeyFrames ){
404
- this.unSelectAllKeyFrames();
405
- }
406
- if ( this.unHoverAll ){
407
- this.unHoverAll();
408
- }
409
- this.unSelectAllTracks();
405
+ this.deselectAllElements();
406
+ this.deselectAllTracks();
407
+
410
408
  this.selectedItems = [];
411
409
 
412
410
  this.clearState();
@@ -499,7 +497,7 @@ class Timeline {
499
497
 
500
498
  // Content
501
499
  const topMargin = this.topMargin;
502
- const treeOffset = this.lastTrackTreesWidgetOffset;
500
+ const treeOffset = this.lastTrackTreesComponentOffset;
503
501
  const line_height = this.trackHeight;
504
502
 
505
503
  //fill track lines
@@ -547,12 +545,12 @@ class Timeline {
547
545
  const w = ctx.canvas.width;
548
546
  const h = ctx.canvas.height;
549
547
 
550
- const scrollableHeight = this.trackTreesWidget.root.scrollHeight;
548
+ const scrollableHeight = this.trackTreesComponent.root.scrollHeight;
551
549
  // tree has gaps of 0.25rem (4px) inbetween entries but not in the beginning nor ending. Move half gap upwards.
552
- const treeOffset = this.lastTrackTreesWidgetOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop -2;
550
+ const treeOffset = this.lastTrackTreesComponentOffset = this.trackTreesComponent.innerTree.domEl.offsetTop - this.canvas.offsetTop -2;
553
551
 
554
552
  if ( this.trackTreesPanel.root.scrollHeight > 0 ){
555
- const ul = this.trackTreesWidget.innerTree.domEl.children[0];
553
+ const ul = this.trackTreesComponent.innerTree.domEl.children[0];
556
554
  this.trackHeight = ul.children.length < 1 ? 25 : ((ul.offsetHeight+4) / ul.children.length);
557
555
  }
558
556
 
@@ -785,7 +783,7 @@ class Timeline {
785
783
  }
786
784
 
787
785
  }
788
- else if( (h-this.topMargin) < this.trackTreesWidget.root.scrollHeight)
786
+ else if( (h-this.topMargin) < this.trackTreesComponent.root.scrollHeight)
789
787
  {
790
788
  this.trackTreesPanel.root.scrollTop += e.deltaY; // wheel deltaY
791
789
  }
@@ -832,11 +830,13 @@ class Timeline {
832
830
  this.movingKeys = false;
833
831
  this.timeBeforeMove = null;
834
832
  this.boxSelection = false; // after mouseup
835
- this.unSelectAllTracks();
833
+ this.deselectAllTracks();
836
834
  }
837
835
 
838
836
 
839
837
  if( e.type == "mousedown") {
838
+ window.getSelection().empty(); // if canvas DOM is selected, dragging does not work properly. Deselect it
839
+
840
840
  // e.preventDefault();
841
841
 
842
842
  this.clickTime = LX.getTime();
@@ -852,7 +852,7 @@ class Timeline {
852
852
  this.grabbingTimeBar = true;
853
853
  this.setTime(time);
854
854
  }
855
- else if( (h-this.topMargin) < this.trackTreesWidget.root.scrollHeight && x > w - 10 ) { // grabbing scroll bar
855
+ else if( (h-this.topMargin) < this.trackTreesComponent.root.scrollHeight && x > w - 10 ) { // grabbing scroll bar
856
856
  this.grabbing = true;
857
857
  this.grabbingScroll = true;
858
858
  }
@@ -972,7 +972,7 @@ class Timeline {
972
972
  setState(state, skipCallback = false) {
973
973
  this.playing = state;
974
974
 
975
- this.header.widgets.playBtn.setState(this.playing, true);
975
+ this.header.components.playBtn.setState(this.playing, true);
976
976
 
977
977
  if(this.onStateChange && !skipCallback) {
978
978
  this.onStateChange(this.playing);
@@ -988,9 +988,9 @@ class Timeline {
988
988
  setLoopMode(loopState, skipCallback = false){
989
989
  this.loop = loopState;
990
990
  if ( this.loop ){
991
- this.header.widgets.loopBtn.root.children[0].classList.add("selected");
991
+ this.header.components.loopBtn.root.children[0].classList.add("selected");
992
992
  }else{
993
- this.header.widgets.loopBtn.root.children[0].classList.remove("selected")
993
+ this.header.components.loopBtn.root.children[0].classList.remove("selected")
994
994
  }
995
995
  if( this.onChangeLoopMode && !skipCallback ){
996
996
  this.onChangeLoopMode( this.loop );
@@ -1003,7 +1003,7 @@ class Timeline {
1003
1003
  * If not a track, trackData will be undefined
1004
1004
  */
1005
1005
  getVisibleItems(){
1006
- return this.trackTreesWidget.innerTree.domEl.children[0].children; // children of 'ul'
1006
+ return this.trackTreesComponent.innerTree.domEl.children[0].children; // children of 'ul'
1007
1007
  }
1008
1008
 
1009
1009
  /**
@@ -1071,7 +1071,7 @@ class Timeline {
1071
1071
  * Only affects render visualisation
1072
1072
  * @method selectTrack
1073
1073
  * @param {int} trackIdx
1074
- * // NOTE: to select a track from outside of the timeline, a this.trackTreesWidget.innerTree.select(item) needs to be called.
1074
+ * // NOTE: to select a track from outside of the timeline, a this.trackTreesComponent.innerTree.select(item) needs to be called.
1075
1075
  */
1076
1076
  selectTrack( trackIdx ) {
1077
1077
 
@@ -1079,7 +1079,7 @@ class Timeline {
1079
1079
  return;
1080
1080
  }
1081
1081
 
1082
- this.unSelectAllTracks();
1082
+ this.deselectAllTracks();
1083
1083
 
1084
1084
  let track = this.animationClip.tracks[ trackIdx ];
1085
1085
  track.isSelected = true;
@@ -1090,7 +1090,7 @@ class Timeline {
1090
1090
  }
1091
1091
 
1092
1092
  // Only affects render visualisation
1093
- unSelectAllTracks() {
1093
+ deselectAllTracks() {
1094
1094
 
1095
1095
  if( !this.animationClip ){
1096
1096
  return;
@@ -1102,7 +1102,7 @@ class Timeline {
1102
1102
  }
1103
1103
  }
1104
1104
 
1105
- unselectAllElements(){
1105
+ deselectAllElements(){
1106
1106
 
1107
1107
  }
1108
1108
 
@@ -1149,7 +1149,7 @@ class Timeline {
1149
1149
 
1150
1150
  if (!toBeShown.length){ return false; }
1151
1151
 
1152
- this.unselectAllElements();
1152
+ this.deselectAllElements();
1153
1153
 
1154
1154
  const combinedState = toBeShown.pop();
1155
1155
  const combinedStateToStore = [];
@@ -1247,7 +1247,7 @@ class Timeline {
1247
1247
 
1248
1248
 
1249
1249
  /**
1250
- * This functions uses the selectedItems and generates the data that will feed the LX.Tree widget.
1250
+ * This functions uses the selectedItems and generates the data that will feed the LX.Tree Component.
1251
1251
  * This function is used by updateLeftPanel. Some timelines might allow grouping of tracks. Such timelines may overwrite this function
1252
1252
  * WARNING: track entries MUST have an attribute of 'trackData' with the track info
1253
1253
  * @returns lexgui tree data as expecte for the creation of a tree
@@ -1555,8 +1555,8 @@ class KeyFramesTimeline extends Timeline {
1555
1555
  }
1556
1556
 
1557
1557
  // OVERRIDE
1558
- unselectAllElements(){
1559
- this.unSelectAllKeyFrames();
1558
+ deselectAllElements(){
1559
+ this.deselectAllKeyFrames();
1560
1560
  this.unHoverAll();
1561
1561
  }
1562
1562
 
@@ -1568,7 +1568,7 @@ class KeyFramesTimeline extends Timeline {
1568
1568
  changeSelectedItems( itemsToAdd = null, itemsToRemove = null, skipCallback = false ) {
1569
1569
 
1570
1570
  // TODO: maybe make this un-functions more general
1571
- this.unSelectAllKeyFrames();
1571
+ this.deselectAllKeyFrames();
1572
1572
  this.unHoverAll();
1573
1573
 
1574
1574
  const tracks = this.animationClip.tracks;
@@ -1717,7 +1717,7 @@ class KeyFramesTimeline extends Timeline {
1717
1717
  const keyFrameIdx = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
1718
1718
  if ( keyFrameIdx > -1 ){
1719
1719
  track.selected[keyFrameIdx] ?
1720
- this.unSelectKeyFrame(track.trackIdx, keyFrameIdx) :
1720
+ this.deselectKeyFrame(track.trackIdx, keyFrameIdx) :
1721
1721
  this.processSelectionKeyFrame( track.trackIdx, keyFrameIdx, true );
1722
1722
  }
1723
1723
  }
@@ -1742,10 +1742,10 @@ class KeyFramesTimeline extends Timeline {
1742
1742
  else if( !this.movingKeys && !discard ){ // if not moving timeline and not adding keyframes through e.shiftkey (just a click)
1743
1743
 
1744
1744
  if ( this.lastKeyFramesSelected.length ){
1745
- if (this.onUnselectKeyFrames){
1746
- this.onUnselectKeyFrames( this.lastKeyFramesSelected );
1745
+ if (this.onDeselectKeyFrames){
1746
+ this.onDeselectKeyFrames( this.lastKeyFramesSelected );
1747
1747
  }
1748
- this.unSelectAllKeyFrames();
1748
+ this.deselectAllKeyFrames();
1749
1749
  }
1750
1750
  if (track){
1751
1751
  const keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
@@ -2129,27 +2129,43 @@ class KeyFramesTimeline extends Timeline {
2129
2129
  //draw lines
2130
2130
  ctx.strokeStyle = "white";
2131
2131
  ctx.beginPath();
2132
- for(let j = 0; j < keyframes.length; ++j){
2133
2132
 
2134
- let time = keyframes[j];
2135
- let keyframePosX = this.timeToX( time );
2136
- let value = values[j];
2137
- value = ((value - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
2133
+ if ( keyframes.length > 1){
2134
+ let startPosX = this.timeToX( keyframes[0] );
2135
+ let startValue = values[0];
2136
+ startValue = ((startValue - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
2137
+ ctx.moveTo( startPosX, startValue );
2138
2138
 
2139
- if( time < startTime ){
2140
- ctx.moveTo( keyframePosX, value );
2141
- continue;
2142
- }
2139
+ for(let j = 1; j < keyframes.length; ++j){
2143
2140
 
2144
- //convert to timeline track range
2145
- ctx.lineTo( keyframePosX, value );
2146
-
2147
- if ( time > endTime ){
2148
- break; //end loop, but print line
2141
+ let time = keyframes[j];
2142
+ let keyframePosX = this.timeToX( time );
2143
+ let value = values[j];
2144
+ value = ((value - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
2145
+
2146
+ if( time < startTime ){
2147
+ ctx.moveTo( keyframePosX, value );
2148
+ continue;
2149
+ }
2150
+
2151
+ if ( time > endTime ){
2152
+ let lastKeyframePosX = this.timeToX( keyframes[j-1] );
2153
+ let dt = keyframePosX - lastKeyframePosX;
2154
+ if ( dt > 0 ){
2155
+ let lastValue = values[j-1];
2156
+ lastValue = ((lastValue - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
2157
+ let f = (this.timeToX( endTime ) - lastKeyframePosX) / dt;
2158
+ ctx.lineTo( lastKeyframePosX + dt * f, lastValue * (1-f) + value * f );
2159
+ }
2160
+ break; //end loop, but print line
2161
+ }
2162
+
2163
+ //convert to timeline track range
2164
+ ctx.lineTo( keyframePosX, value );
2149
2165
  }
2166
+ ctx.stroke();
2150
2167
  }
2151
- ctx.stroke();
2152
-
2168
+
2153
2169
  //draw points
2154
2170
  ctx.fillStyle = Timeline.KEYFRAME_COLOR;
2155
2171
  for(let j = 0; j < keyframes.length; ++j)
@@ -2604,7 +2620,7 @@ class KeyFramesTimeline extends Timeline {
2604
2620
  if ( !this.clipboard.keyframes ){ return false; }
2605
2621
 
2606
2622
  this.unHoverAll();
2607
- this.unSelectAllKeyFrames();
2623
+ this.deselectAllKeyFrames();
2608
2624
 
2609
2625
  let clipboardTracks = this.clipboard.keyframes;
2610
2626
  let globalStart = Infinity;
@@ -2933,16 +2949,16 @@ class KeyFramesTimeline extends Timeline {
2933
2949
  return h;
2934
2950
  }
2935
2951
 
2936
- unSelectAllKeyFrames() {
2952
+ deselectAllKeyFrames() {
2937
2953
 
2938
2954
  for(let [trackIdx, keyIndex] of this.lastKeyFramesSelected) {
2939
2955
  this.animationClip.tracks[trackIdx].selected[keyIndex] = false;
2940
2956
  }
2941
2957
 
2942
- // Something has been unselected
2943
- const unselected = this.lastKeyFramesSelected.length > 0;
2958
+ // Something has been deselected
2959
+ const deselected = this.lastKeyFramesSelected.length > 0;
2944
2960
  this.lastKeyFramesSelected.length = 0;
2945
- return unselected;
2961
+ return deselected;
2946
2962
  }
2947
2963
 
2948
2964
  isKeyFrameSelected( track, index ) {
@@ -2981,7 +2997,7 @@ class KeyFramesTimeline extends Timeline {
2981
2997
  return selection;
2982
2998
  }
2983
2999
 
2984
- unSelectKeyFrame( trackIdx, frameIdx ){
3000
+ deselectKeyFrame( trackIdx, frameIdx ){
2985
3001
  const track = this.animationClip.tracks[trackIdx];
2986
3002
  if( track.locked || !track.active || !track.selected[frameIdx] )
2987
3003
  return false;
@@ -3017,7 +3033,7 @@ class KeyFramesTimeline extends Timeline {
3017
3033
  return;
3018
3034
 
3019
3035
  if(!multipleSelection) {
3020
- this.unSelectAllKeyFrames();
3036
+ this.deselectAllKeyFrames();
3021
3037
  }
3022
3038
 
3023
3039
  this.selectKeyFrame(trackIdx, keyFrameIndex);
@@ -3039,7 +3055,7 @@ class KeyFramesTimeline extends Timeline {
3039
3055
  }
3040
3056
 
3041
3057
  this.unHoverAll();
3042
- this.unSelectAllKeyFrames();
3058
+ this.deselectAllKeyFrames();
3043
3059
 
3044
3060
  this.saveState(track.trackIdx);
3045
3061
 
@@ -3139,9 +3155,30 @@ class ClipsTimeline extends Timeline {
3139
3155
  track.hovered = (new Array(numClips)).fill(false);
3140
3156
  }
3141
3157
 
3158
+ // sanity check. Also done in addClip
3159
+ for( let i = 0; i < track.clips.length; ++i ){
3160
+ track.clips[i].active = track.clips[i].active ?? true;
3161
+ }
3142
3162
  return track;
3143
3163
  }
3144
3164
 
3165
+ // provides an base example of a proper clip
3166
+ instantiateClip(options = {}){
3167
+ return {
3168
+ id: options.id ?? (options.name ?? "clip"),
3169
+
3170
+ start: options.start ?? 0,
3171
+ duration: options.duration ?? 1,
3172
+ fadein: options.fadein ?? undefined,
3173
+ fadeout: options.fadeout ?? undefined,
3174
+
3175
+ clipColor: options.clipColor ?? LX.getThemeColor("global-color-accent"),
3176
+ fadeColor: options.fadeColor ?? null,
3177
+ active: options.active ?? true,
3178
+ trackIdx: -1, // filled by addClip
3179
+ }
3180
+
3181
+ }
3145
3182
  // use default updateleftpanel
3146
3183
  // generateSelectedItemsTreeData(){}
3147
3184
 
@@ -3171,8 +3208,8 @@ class ClipsTimeline extends Timeline {
3171
3208
  }
3172
3209
 
3173
3210
  // OVERRIDE
3174
- unselectAllElements(){
3175
- this.unSelectAllClips();
3211
+ deselectAllElements(){
3212
+ this.deselectAllClips();
3176
3213
  this.unHoverAll();
3177
3214
  }
3178
3215
 
@@ -3183,7 +3220,7 @@ class ClipsTimeline extends Timeline {
3183
3220
  changeSelectedItems( ) {
3184
3221
 
3185
3222
  // TODO: maybe make this un-functions more general
3186
- this.unSelectAllClips();
3223
+ this.deselectAllClips();
3187
3224
  this.unHoverAll();
3188
3225
 
3189
3226
  this.selectedItems = this.animationClip.tracks.slice();
@@ -3214,7 +3251,7 @@ class ClipsTimeline extends Timeline {
3214
3251
  let clipIndex = this.getClipOnTime( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
3215
3252
  if ( clipIndex > -1 ){
3216
3253
  track.selected[clipIndex] ?
3217
- this.unselectClip( track.trackIdx, clipIndex ) :
3254
+ this.deselectClip( track.trackIdx, clipIndex ) :
3218
3255
  this.selectClip( track.trackIdx, clipIndex, false );
3219
3256
  }
3220
3257
  }
@@ -3302,13 +3339,16 @@ class ClipsTimeline extends Timeline {
3302
3339
  this.movingKeys = true;
3303
3340
  }
3304
3341
  else if( !track || track && this.getClipOnTime(track, time, 0.001) == -1) { // clicked on empty space
3305
- this.unSelectAllClips();
3306
- if(this.onSelectClip)
3307
- this.onSelectClip(null);
3342
+ if ( this.lastClipsSelected.length ){
3343
+ this.deselectAllClips();
3344
+ if(this.onSelectClip){
3345
+ this.onSelectClip(null);
3346
+ }
3347
+ }
3308
3348
  }
3309
3349
  else if (track && (this.dragClipMode == "duration" || this.dragClipMode == "fadein" || this.dragClipMode == "fadeout" )) { // clicked while mouse was over fadeIn, fadeOut, duration
3310
3350
  const clipIdx = this.getClipOnTime(track, this.xToTime(localX), 0.001);
3311
- this.selectClip( track.trackIdx, clipIdx ); // select current clip if any (unselect others)
3351
+ this.selectClip( track.trackIdx, clipIdx ); // select current clip if any (deselect others)
3312
3352
  if ( this.lastClipsSelected.length ){
3313
3353
  this.saveState(track.trackIdx);
3314
3354
  }
@@ -3362,7 +3402,7 @@ class ClipsTimeline extends Timeline {
3362
3402
  else if ( this.dragClipMode == "move" && this.lastClipsSelected.length ) { // move clips
3363
3403
  //*********** WARNING: RELIES ON SORTED lastClipsSelected ***********
3364
3404
 
3365
- const treeOffset = this.lastTrackTreesWidgetOffset;
3405
+ const treeOffset = this.lastTrackTreesComponentOffset;
3366
3406
  let newTrackClipsMove = Math.floor( (e.localY - treeOffset) / this.trackHeight );
3367
3407
 
3368
3408
  // move clips vertically
@@ -3621,7 +3661,7 @@ class ClipsTimeline extends Timeline {
3621
3661
 
3622
3662
  if ( track ){
3623
3663
  const clipIdx = this.getClipOnTime(track, this.xToTime(localX), 0.001);
3624
- this.selectClip(track.trackIdx, clipIdx); // unselect and try to select clip in localX, if any
3664
+ this.selectClip(track.trackIdx, clipIdx); // deselect and try to select clip in localX, if any
3625
3665
  }
3626
3666
  }
3627
3667
 
@@ -3737,14 +3777,14 @@ class ClipsTimeline extends Timeline {
3737
3777
  // Overwrite clip color state depending on its state
3738
3778
  ctx.globalAlpha = 1;
3739
3779
  ctx.fillStyle = clip.clipColor || (track.hovered[j] ? Timeline.KEYFRAME_COLOR_HOVERED : (track.selected[j] ? Timeline.TRACK_SELECTED : Timeline.KEYFRAME_COLOR));
3740
- if(!this.active || !track.active) {
3780
+ if(!this.active || !track.active || !clip.active) {
3741
3781
  ctx.fillStyle = Timeline.KEYFRAME_COLOR_INACTIVE;
3742
3782
  }
3743
3783
 
3744
3784
  // Draw clip background
3745
3785
  ctx.roundRect( x, y + offset, w, trackHeight , 5, true);
3746
3786
 
3747
- if(this.active && track.active) {
3787
+ if(this.active && track.active && clip.active) {
3748
3788
 
3749
3789
  ctx.fillStyle = clip.fadeColor ?? "#0004";
3750
3790
 
@@ -3822,6 +3862,8 @@ class ClipsTimeline extends Timeline {
3822
3862
  addClip( clip, trackIdx = -1, offsetTime = 0, searchStartTrackIdx = 0 ) {
3823
3863
  if ( !this.animationClip ){ return -1; }
3824
3864
 
3865
+ this.deselectAllElements(); // TODO: consider adjusting values of hovered and selected instead of deselecting everything
3866
+
3825
3867
  // Update clip information
3826
3868
  let newStart = clip.start + offsetTime;
3827
3869
  if(clip.fadein != undefined)
@@ -3830,6 +3872,9 @@ class ClipsTimeline extends Timeline {
3830
3872
  clip.fadeout += (newStart - clip.start);
3831
3873
  clip.start = newStart;
3832
3874
 
3875
+ // sanity check
3876
+ clip.active = clip.active ?? true;
3877
+
3833
3878
  // find appropriate track
3834
3879
  if ( trackIdx >= this.animationClip.tracks.length ){ // new track ad the end
3835
3880
  trackIdx = this.addNewTrack();
@@ -3856,6 +3901,8 @@ class ClipsTimeline extends Timeline {
3856
3901
  // }
3857
3902
  }
3858
3903
 
3904
+ clip.trackIdx = trackIdx;
3905
+
3859
3906
  const track = this.animationClip.tracks[trackIdx];
3860
3907
 
3861
3908
  // Find new index
@@ -4097,7 +4144,7 @@ class ClipsTimeline extends Timeline {
4097
4144
  }
4098
4145
 
4099
4146
  pasteContent( time = this.currentTime ) {
4100
- this.unSelectAllClips();
4147
+ this.deselectAllClips();
4101
4148
 
4102
4149
  if(!this.clipboard)
4103
4150
  return;
@@ -4155,6 +4202,11 @@ class ClipsTimeline extends Timeline {
4155
4202
  const track = this.animationClip.tracks[trackIdx];
4156
4203
  const clips = this.cloneClips(track.clips, 0, ClipsTimeline.CLONEREASON_HISTORY);
4157
4204
 
4205
+ // sanity check in case cloneClips misses this
4206
+ for( let i = 0; i < clips.length; ++i ){
4207
+ clips[i].trackIdx = trackIdx;
4208
+ }
4209
+
4158
4210
  const undoStep = {
4159
4211
  trackIdx: trackIdx, // already done by saveState
4160
4212
  clips: clips,
@@ -4187,6 +4239,11 @@ class ClipsTimeline extends Timeline {
4187
4239
  track.selected.fill(false);
4188
4240
  track.hovered.fill(false);
4189
4241
 
4242
+ // sanity check. Also done in addClip
4243
+ for( let i = 0; i < track.clips.length; ++i ){
4244
+ track.clips[i].active = track.clips[i].active ?? true;
4245
+ }
4246
+
4190
4247
  return stateToReturn;
4191
4248
  }
4192
4249
 
@@ -4212,20 +4269,20 @@ class ClipsTimeline extends Timeline {
4212
4269
  return -1;
4213
4270
  };
4214
4271
 
4215
- unSelectAllClips() {
4272
+ deselectAllClips() {
4216
4273
 
4217
4274
  for(let [trackIdx, clipIdx] of this.lastClipsSelected) {
4218
4275
  this.animationClip.tracks[trackIdx].selected[clipIdx]= false;
4219
4276
  }
4220
- // Something has been unselected
4221
- const unselected = this.lastClipsSelected.length > 0;
4277
+ // Something has been deselected
4278
+ const deselected = this.lastClipsSelected.length > 0;
4222
4279
  this.lastClipsSelected.length = 0;
4223
- return unselected;
4280
+ return deselected;
4224
4281
  }
4225
4282
 
4226
4283
  selectAll( skipCallback = false) {
4227
4284
 
4228
- this.unSelectAllClips();
4285
+ this.deselectAllClips();
4229
4286
  for(let trackIdx = 0; trackIdx < this.animationClip.tracks.length; trackIdx++) {
4230
4287
  for(let clipIdx = 0; clipIdx < this.animationClip.tracks[trackIdx].clips.length; clipIdx++) {
4231
4288
  this.animationClip.tracks[trackIdx].selected[clipIdx] = true;
@@ -4236,10 +4293,10 @@ class ClipsTimeline extends Timeline {
4236
4293
  this.onSelectClip(null);
4237
4294
  }
4238
4295
 
4239
- selectClip( trackIdx, clipIndex, unselect = true, skipCallback = false ) {
4296
+ selectClip( trackIdx, clipIndex, deselect = true, skipCallback = false ) {
4240
4297
 
4241
- if(unselect){
4242
- this.unSelectAllClips();
4298
+ if(deselect){
4299
+ this.deselectAllClips();
4243
4300
  }
4244
4301
 
4245
4302
  if(clipIndex < 0){
@@ -4270,7 +4327,7 @@ class ClipsTimeline extends Timeline {
4270
4327
  return clipIndex;
4271
4328
  }
4272
4329
 
4273
- unselectClip( trackIdx, clipIndex ){
4330
+ deselectClip( trackIdx, clipIndex ){
4274
4331
 
4275
4332
  if(clipIndex == -1){
4276
4333
  return -1;
@@ -4283,7 +4340,7 @@ class ClipsTimeline extends Timeline {
4283
4340
 
4284
4341
  track.selected[clipIndex] = false;
4285
4342
 
4286
- // unselect
4343
+ // deselect
4287
4344
  for( let i = 0; i < this.lastClipsSelected.length; ++i){
4288
4345
  let t = this.lastClipsSelected[i];
4289
4346
  if ( t[0] == trackIdx && t[1] == clipIndex ){
@@ -4332,11 +4389,10 @@ class ClipsTimeline extends Timeline {
4332
4389
  validateDuration(t) {
4333
4390
  for(let i = 0; i < this.animationClip.tracks.length; i++) {
4334
4391
  const track = this.animationClip.tracks[i];
4335
- const clipsIdxs = this.getClipsInRange( track, t , this.animationClip.duration, 0 );
4336
- if(!clipsIdxs)
4337
- continue;
4338
- const clip = track.clips[clipsIdxs[clipsIdxs.length - 1]];
4339
- t = Math.max(t, clip.start + clip.duration);
4392
+ if ( track.clips.length ){
4393
+ const clip = track.clips[track.clips.length-1]; // assuming they are ordered ascendently
4394
+ t = Math.max(t, clip.start + clip.duration);
4395
+ }
4340
4396
  }
4341
4397
  return t;
4342
4398
  }
@@ -4344,7 +4400,7 @@ class ClipsTimeline extends Timeline {
4344
4400
  setDuration( t, skipCallback = false, updateHeader = true ){
4345
4401
  const oldT = t;
4346
4402
  const newT = this.validateDuration(t);
4347
- super.setDuration( this.validateDuration(t), skipCallback, oldT != newT || updateHeader );
4403
+ super.setDuration( newT, skipCallback, oldT != newT || updateHeader );
4348
4404
  }
4349
4405
  }
4350
4406
 
@@ -4,7 +4,7 @@ if(!LX) {
4
4
  throw("lexgui.js missing!");
5
5
  }
6
6
 
7
- LX.components.push( 'VideoEditor' );
7
+ LX.extensions.push( 'VideoEditor' );
8
8
 
9
9
  /**
10
10
  * @class TimeBar