lexgui 0.7.9 → 0.7.11

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.
package/changelog.md CHANGED
@@ -2,7 +2,54 @@
2
2
 
3
3
  ## dev
4
4
 
5
- ## 0.7.9 (master)
5
+ ## 0.7.11 (master)
6
+
7
+ Fixed `Menubar.getSubItem`.
8
+
9
+ Timeline:
10
+ - Adjust scroll if left panel size changes.
11
+ - Keyframe selection now uses the correct keyframeSize values.
12
+ - Added `onTrackTreeEvent`.
13
+ - On curve tracks, `Alt` key now saves state before modifying keyframes.
14
+ - Fixed tracks resize on refresh.
15
+
16
+ NodeTree Component:
17
+ - Item actions are now standard buttons instead of icons.
18
+ - Fix select when filter is present.
19
+ - Allow null id to unselect all.
20
+
21
+ TreeEvent:
22
+ - Added event attribute to store mouse/keyboard/system.
23
+ - Homogenised calls to TreeEvent so node, value and event are consistent.
24
+
25
+ ## 0.7.10
26
+
27
+ Removed Fit-Tabs thumb CSS transition on `Tabs.add`.
28
+ Added Popover documentation.
29
+ Fixed `LX.codeSnippet` linesAdded/Removed options.
30
+ Fixed `Area.extend()` and `Area.reduce()` transitions.
31
+
32
+ AssetView:
33
+ - Added support for video elements.
34
+ - Fixed support for images using url with params.
35
+ - Added more supported image extensions.
36
+ - Updated documentation.
37
+
38
+ CodeEditor:
39
+ - Added support to `options.callback` to call when the editor is completely loaded.
40
+ - Docs updated.
41
+
42
+ Timeline:
43
+ - Added `Timeline.setKeyframeSize` and `Timeline.setTrackHeight`.
44
+ - Curve keyframes are visually clamped to the tracks' boundaries.
45
+ - Fixed `Add Keyframe` option in context menu.
46
+
47
+ VideoEditor:
48
+ - Minor functions refactor.
49
+ - Speed and loop buttons added to video controls.
50
+ - Fixed resize controls area bug.
51
+
52
+ ## 0.7.9
6
53
 
7
54
  Allow wheel/middle click in menubar buttons.
8
55
  Pass RadioGroup name as argument to radio option callback.
@@ -26,7 +26,7 @@
26
26
  let area = await LX.init();
27
27
 
28
28
  // split main area
29
- var [leftArea, rightArea] = area.split({ sizes: ["75%", "25%"] });
29
+ var [ leftArea, rightArea ] = area.split({ sizes: ["75%", "25%"], minimizable: true });
30
30
 
31
31
  // Get new content area to fill it
32
32
  const leftTabs = leftArea.addTabs();
@@ -56,7 +56,33 @@
56
56
  {
57
57
  id: "dog.png",
58
58
  type: "image",
59
- path: "https://pngfre.com/wp-content/uploads/1653714420512-1-904x1024.png"
59
+ src: "https://pngfre.com/wp-content/uploads/1653714420512-1-904x1024.png"
60
+ },
61
+ {
62
+ id: "greenland_grid_velo.bmp",
63
+ type: "image",
64
+ src: "https://people.math.sc.edu/Burkardt/data/bmp/greenland_grid_velo.bmp"
65
+ },
66
+ {
67
+ id: "Fox Monochrome.avif",
68
+ type: "image",
69
+ src: "https://raw.githubusercontent.com/link-u/avif-sample-images/refs/heads/master/fox.profile0.10bpc.yuv420.monochrome.avif"
70
+ },
71
+ {
72
+ id: "Avatar",
73
+ type: "Avatar",
74
+ preview: "https://models.readyplayer.me/66e30a18eca8fb70dcadde68.png?background=68,68,245"
75
+ },
76
+ {
77
+ id: "video-test",
78
+ type: "video",
79
+ preview: "https://godotengine.org/assets/press/logo_vertical_color_dark.png",
80
+ src: "https://catsl.eelvex.net/static/vid/teacher-video-Ψ.mp4"
81
+ },
82
+ {
83
+ id: "nyan-pyportal.gif",
84
+ type: "image",
85
+ src: "../data/nyan-pyportal.gif"
60
86
  },
61
87
  {
62
88
  id: "sigml_test.sigml",
@@ -635,13 +635,13 @@
635
635
  switch (event.type) {
636
636
  case LX.TreeEvent.NODE_SELECTED:
637
637
  if (event.multiple)
638
- console.log("Selected: ", event.node);
638
+ console.log("Selected: ", event.value);
639
639
  else
640
640
  console.log(event.node.id + " selected");
641
641
  break;
642
642
  case LX.TreeEvent.NODE_DELETED:
643
643
  if (event.multiple)
644
- console.log("Deleted: ", event.node);
644
+ console.log("Deleted: ", event.value);
645
645
  else
646
646
  console.log(event.node.id + " deleted");
647
647
  break;
@@ -48,15 +48,17 @@
48
48
  const types = { KEYFRAMES: 0, CLIPS: 1, CURVES: 2};
49
49
  let position = [250, 350];
50
50
  let trackName = "";
51
+ let kfTimeline = null;
51
52
 
52
53
  let panel = new LX.Panel();
53
54
  panel = rightArea.addPanel(panel);
54
- fillPanel( panel );
55
-
55
+
56
56
  let activeTimeline = null
57
57
  createKeyframeTimeline( bottomTabs );
58
58
  createClipsTimeline( bottomTabs );
59
59
  createCurvesTimeline( bottomTabs );
60
+
61
+ fillPanel( panel );
60
62
 
61
63
  LX.doAsync( activeTimeline.resize.bind( activeTimeline ) );
62
64
 
@@ -144,8 +146,17 @@
144
146
  track.edited[keyframe] = true;
145
147
  }
146
148
  }, { min: 0, max: 1024 });
147
- panel.branch_open = true;
148
149
  panel.merge();
150
+
151
+ panel.addRange("Keyframe Size", kfTimeline.keyframeSize / kfTimeline.trackHeight, (v,e)=>{
152
+ kfTimeline.keyframeSize = v * kfTimeline.trackHeight;
153
+ }, { step: 0.001, min: 0, max: 1 });
154
+
155
+ panel.addNumber("Track Height", kfTimeline.trackHeight, (v,e)=>{
156
+ let ratio = kfTimeline.keyframeSize / kfTimeline.trackHeight;
157
+ kfTimeline.setTrackHeight(v);
158
+ kfTimeline.setKeyframeSize( kfTimeline.trackHeight * ratio, kfTimeline.trackHeight * ratio + 6 );
159
+ }, { step: 1, min: parseFloat(getComputedStyle(document.documentElement).fontSize) * 0.25 });
149
160
  }
150
161
 
151
162
 
@@ -206,7 +217,7 @@
206
217
  keyframesPanel.onresize = () => {
207
218
  kfTimeline.resize();
208
219
  }
209
- let kfTimeline = new LX.KeyFramesTimeline('Keyframes Timeline', {
220
+ kfTimeline = new LX.KeyFramesTimeline('Keyframes Timeline', {
210
221
  disableNewTracks: true,
211
222
  onCreateBeforeTopBar: (panel) => {
212
223
  panel.addSelect("Animation", ["Anim 1", "Anim 2", "Anim 3"], "Anim 1", ()=> {})
@@ -224,25 +235,24 @@
224
235
  kfTimeline.show();
225
236
 
226
237
 
227
- kfTimeline.onSelectKeyFrame = (data, currentSelection) => {
228
- if(data.multipleSelection) {
238
+ kfTimeline.onSelectKeyFrame = (currentSelection) => {
239
+ if(kfTimeline.lastKeyFramesSelected.length > 1) {
229
240
  rightArea.hide();
230
241
  return;
231
242
  }
232
243
  rightArea.show();
233
- trackName = data.track.id ;
234
- if( data.track.groupId != "Font" || data.track.id != "position") {
244
+ const track = kfTimeline.animationClip.tracks[currentSelection[0]];
245
+ trackName = track.id ;
246
+ if( track.groupId != "Font" || track.id != "position") {
235
247
  return
236
248
  }
237
- const id = data.track.trackIdx;
238
- let values = kfTimeline.animationClip.tracks[id].values;
239
- const dim = kfTimeline.animationClip.tracks[id].dim;
249
+ const idx = track.trackIdx;
250
+ let values = track.values;
251
+ const dim = track.dim;
240
252
 
241
- //currentSelection == [track.name, track.idx, frameIdx, track.clipIdx, track.times[frameIdx]]
242
253
  const keyFrameIndex = currentSelection[1];
243
254
  position = values.slice(keyFrameIndex * dim, keyFrameIndex * dim + dim);
244
255
  fillPanel(panel);
245
- //kfTimeline.animationClip.tracks[currentSelection[0][0]];
246
256
  }
247
257
 
248
258
  kfTimeline.onSetTime = ( time ) => {
@@ -21,6 +21,155 @@
21
21
 
22
22
  import { LX } from 'lexgui';
23
23
  import { VideoEditor } from 'lexgui/extensions/videoeditor.js';
24
+
25
+ class Window {
26
+ constructor( timeline, options = {} ) {
27
+ this.timeline = timeline;
28
+ this.timeline.padding = 0;
29
+
30
+ this.start = options.start != undefined ? options.start : 0.0; // sec
31
+ this.end = options.end || 3.0; // sec
32
+ this.center = options.center != undefined ? options.center : this.start
33
+
34
+ this.canvas = this.timeline.canvas;
35
+ this.windowHeight = options.windowHeight || this.canvas.height - 15;
36
+ this.handleWidth = options.handleWidth || 6;
37
+
38
+ this.allowDragging = options.dragging ?? false;
39
+ this.dragging = false;
40
+ this.dragOffset = 0;
41
+ this.resizingLeft = false;
42
+ this.resizingRight = false;
43
+ }
44
+
45
+ draw() {
46
+ const ctx = this.canvas.getContext('2d');
47
+ let startX = Math.max( this.timeline.startX, this.timeline.timeToX(this.start) );
48
+ let endX = Math.min( this.timeline.endX, this.timeline.timeToX(this.end) );
49
+ const width = endX - startX;
50
+
51
+ // Window background
52
+ ctx.fillStyle = "rgba(200, 200, 255, 0.15)";
53
+ ctx.roundRect(startX , this.canvas.height / 2 - this.windowHeight / 2, width, this.windowHeight, 5);
54
+ ctx.fill();
55
+ // Border
56
+ ctx.strokeStyle = "rgba(200, 200, 255, 0.5)";
57
+ ctx.lineWidth = 1;
58
+ ctx.beginPath();
59
+ ctx.roundRect(startX, this.canvas.height / 2 - this.windowHeight / 2, width, this.windowHeight, 5);
60
+ ctx.stroke();
61
+
62
+ // Handlers
63
+ const offsetW = 2;
64
+ const offsetH = 8;
65
+ if( startX > this.timeline.startX ) {
66
+ ctx.fillStyle = "whitesmoke"//"#579aff";
67
+ ctx.fillRect(startX, this.canvas.height / 2 - this.windowHeight / 2, this.handleWidth, this.windowHeight);
68
+ ctx.fillStyle = "#579aff";
69
+ ctx.fillRect(startX + this.handleWidth/2 - offsetW/2, this.canvas.height / 2 - this.windowHeight / 2 + offsetH / 2 , offsetW, this.windowHeight - offsetH);
70
+ }
71
+ if( endX < this.timeline.endX ) {
72
+ ctx.fillStyle = "whitesmoke"//"#579aff";
73
+ ctx.fillRect(endX - this.handleWidth, this.canvas.height / 2 - this.windowHeight / 2, this.handleWidth, this.windowHeight);
74
+ ctx.fillStyle = "#579aff";
75
+ ctx.fillRect(endX - this.handleWidth/2 - offsetW/2, this.canvas.height / 2 - this.windowHeight / 2 + offsetH / 2, offsetW, this.windowHeight - offsetH);
76
+ }
77
+ }
78
+
79
+ onMouse( e ) {
80
+ switch(e.type) {
81
+ case "mousedown":
82
+ this.onMouseDown( e );
83
+ break;
84
+ case "mousemove":
85
+ this.onMouseMove( e );
86
+ break;
87
+ case "mouseup":
88
+ this.onMouseUp( e );
89
+ break;
90
+ }
91
+ }
92
+
93
+ onMouseDown( e ) {
94
+ const x = e.offsetX;
95
+ const startX = this.timeline.timeToX(this.start);
96
+ const endX = this.timeline.timeToX(this.end);
97
+
98
+ if( x >= startX && x <= startX + this.handleWidth ) {
99
+ this.resizingLeft = true;
100
+ e.cancelBubble = true;
101
+ }
102
+ else if( x >= endX - this.handleWidth && x <= endX ) {
103
+ this.resizingRight = true;
104
+ e.cancelBubble = true;
105
+ }
106
+ else if( this.allowDragging && x >= startX && x <= endX ) {
107
+ this.dragging = true;
108
+ this.dragOffset = x - startX;
109
+ e.cancelBubble = true;
110
+ }
111
+ }
112
+
113
+ onMouseMove( e ) {
114
+ const x = e.offsetX;
115
+ let startX = this.timeline.timeToX(this.start);
116
+ let endX = this.timeline.timeToX(this.end);
117
+ const centerX = this.timeline.timeToX(this.center);
118
+ if( this.resizingLeft ) {
119
+ startX = Math.min( centerX, Math.max( this.timeline.startX, x ) );//Math.max(startX - 0.1, (x - this.timeline.padding) / (this.canvas.width - 2 * this.timeline.padding) * this.timeline.endX);
120
+ this.start = this.timeline.xToTime( startX );
121
+ }
122
+ else if( this.resizingRight ) {
123
+ endX = Math.max( centerX, Math.min( this.timeline.endX, x ) ); //Math.min(endX + 0.1, (x - this.timeline.padding) / (this.canvas.width - 2 * this.timeline.padding) * this.timeline.endX);
124
+ this.end = this.timeline.xToTime( endX );
125
+ }
126
+ else if( this.dragging ) {
127
+ const time = this.timeline.xToTime( x - this.dragOffset );
128
+ this.moveWindow( time )
129
+ }
130
+ else {
131
+ const x = e.offsetX;
132
+ const startX = this.timeline.timeToX(this.start);
133
+ const endX = this.timeline.timeToX(this.end);
134
+
135
+ if( x >= startX && x <= startX + this.handleWidth ) {
136
+ this.canvas.style.cursor = "col-resize";
137
+ e.cancelBubble = true;
138
+ }
139
+ else if( x >= endX - this.handleWidth && x <= endX ) {
140
+ this.canvas.style.cursor = "col-resize";
141
+ e.cancelBubble = true;
142
+ }
143
+ return;
144
+ }
145
+ e.cancelBubble = true;
146
+ this.timeline._draw();
147
+ }
148
+
149
+ onMouseUp( e ) {
150
+ this.dragging = false;
151
+ this.resizingLeft = false;
152
+ this.resizingRight = false;
153
+ }
154
+
155
+ moveWindow( time ) {
156
+
157
+ const leftOffset = this.start - this.center;
158
+ const rightOffset = this.end - this.center;
159
+
160
+ // const startX = this.timeline.timeToX( time + leftOffset );
161
+ // const endX = this.timeline.timeToX( rightOffset );
162
+
163
+ // const windowLength = this.end - this.start;
164
+
165
+ // newStart = Math.max(0, Math.min(this.timeline.endX - windowLength, startX));
166
+ this.start = time + leftOffset;
167
+ this.end = time + rightOffset;
168
+ this.center = time;
169
+ }
170
+
171
+ }
172
+
24
173
  // init library and get main area
25
174
  let area = await LX.init();
26
175
 
@@ -33,9 +182,9 @@
33
182
  video.src = "../data/video.mp4";
34
183
  videoArea.attach(video);
35
184
 
36
- const videoEditor = new LX.VideoEditor(leftArea, { videoArea, video, crop: true })
37
- videoEditor._loadVideo();
38
-
185
+ const videoEditor = new LX.VideoEditor(leftArea, { videoArea, video, crop: true, loop: true })
186
+ videoEditor.loadVideo();
187
+
39
188
  const canvas = document.createElement('canvas');
40
189
  canvas.width = video.clientWidth;
41
190
  canvas.height = video.clientHeight;
@@ -30,7 +30,7 @@
30
30
  // Split main area
31
31
  let [leftArea, rightArea] = area.split({ sizes: ["75%", "25%"], minimizable: true });
32
32
  let [topArea, bottomArea] = leftArea.split({ sizes: ["80%", null], minimizable: false, resize: false, type: "vertical" });
33
- let [controlsArea, buttonsArea] = bottomArea.split({ sizes: ["50%", null], minimizable: false, resize: false, type: "vertical" });
33
+ let [controlsArea, buttonsArea] = bottomArea.split({ sizes: ["50%", null], minimizable: false, type: "vertical" });
34
34
  area.extend();
35
35
 
36
36
  /* Create video area with a menubar */
@@ -41,7 +41,8 @@
41
41
  video.src = "../data/video.mp4";
42
42
  videoArea.attach(video);
43
43
  const videoEditor = new LX.VideoEditor(topArea, { videoArea, video, controlsArea })
44
- videoEditor._loadVideo();
44
+ videoEditor.loadVideo();
45
+
45
46
  /* Add canvas above video */
46
47
  const canvas = document.createElement('canvas');
47
48
  canvas.width = video.clientWidth;
@@ -72,7 +73,7 @@
72
73
  icon: "Info",
73
74
  name: "Properties",
74
75
  callback: (v, e) => {
75
- if (area.split_extended) {
76
+ if (area.splitExtended) {
76
77
  area.reduce();
77
78
  }
78
79
  else {
@@ -92,8 +93,7 @@
92
93
  /* Create right panel */
93
94
  let panel = new LX.Panel({ id: "Properties" });
94
95
  panel = rightArea.addPanel({ id: "Properties" });
95
- createBlendShapesInspector({ "Name 1": 0, "Name 2": 0, "Name 3": 0.5, "Name 4": 0, "Name 5": 1, },
96
- { inspector: panel });
96
+ createBlendShapesInspector({ "Name 1": 0, "Name 2": 0, "Name 3": 0.5, "Name 4": 0, "Name 5": 1, }, { inspector: panel });
97
97
 
98
98
  /* Functions */
99
99
  function createBlendShapesInspector(bsNames, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.7.9",
3
+ "version": "0.7.11",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",