lexgui 0.1.31 → 0.1.33

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
@@ -1,5 +1,23 @@
1
1
  # lexgui.js changelog
2
2
 
3
+ ## 0.1.33
4
+
5
+ - New VideoEditor UI component.
6
+ - Improvements Timeline.
7
+ - Allow to select multiple options in AssetViewer.
8
+ - Minor bug fixes.
9
+
10
+ ## 0.1.32
11
+
12
+ Timeline:
13
+ - Allow addition of widgets in top bar.
14
+ - General improvements.
15
+ - Docs started.
16
+
17
+ New VideoEditor component (still wip).
18
+ Added skipCallback for `Widget.set()`.
19
+ Minor bug fixes.
20
+
3
21
  ## 0.1.31
4
22
 
5
23
  GraphEditor:
@@ -50,7 +50,8 @@
50
50
  'Dialogs',
51
51
  // 'Immediate UI',
52
52
  'Node Graph',
53
- 'Side Bar'
53
+ 'Side Bar',
54
+ "Timeline"
54
55
  ];
55
56
 
56
57
  let panel = leftArea.addPanel({ className: "lexexamplespanel" });
@@ -0,0 +1,294 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
5
+ <title>LexGUI Timelines Demo</title>
6
+ <link rel="stylesheet" href="../build/lexgui.css">
7
+ <link rel="icon" href="../images/icon.png">
8
+ <script type="importmap">
9
+ {
10
+ "imports": {
11
+ "lexgui": "../build/lexgui.module.js",
12
+ "lexgui/components/": "../build/components/"
13
+ }
14
+ }
15
+ </script>
16
+ </head>
17
+ <body></body>
18
+ <script type="module">
19
+
20
+ import { LX } from 'lexgui';
21
+ import { Timeline, KeyFramesTimeline, ClipsTimeline, CurvesTimeline, CurvesKeyFramesTimeline } from 'lexgui/components/timeline.js';
22
+
23
+ // init library and get main area
24
+ let area = LX.init();
25
+
26
+ // split main area
27
+ var [leftArea, rightArea] = area.split({sizes:["75%","25%"]});
28
+ // split left area
29
+ var [upArea, bottomArea] = leftArea.split({ type: 'vertical', sizes:["50%", null], minimizable: true });
30
+
31
+ // add canvas to left upper part
32
+ var canvas = document.createElement('canvas');
33
+ canvas.id = "mycanvas";
34
+ canvas.width = leftArea.root.clientWidth;
35
+ canvas.height = leftArea.root.clientHeight;
36
+ canvas.style.width = "100%";
37
+ canvas.style.height = "100%";
38
+
39
+ upArea.attach(canvas);
40
+
41
+ // add on resize event to control canvas size
42
+ leftArea.onresize = function( bounding ) {
43
+ canvas.width = bounding.width;
44
+ canvas.height = bounding.height;
45
+ };
46
+
47
+ const bottomTabs = bottomArea.addTabs();
48
+ const types = { KEYFRAMES: 0, CLIPS: 1, CURVES: 2};
49
+ let position = [250, 350];
50
+ let trackName = "";
51
+
52
+ let panel = new LX.Panel();
53
+ panel = rightArea.addPanel(panel);
54
+ fillPanel( panel );
55
+
56
+ let activeTimeline = null
57
+ createKyeframeTimeline( bottomTabs );
58
+ createClipsTimeline( bottomTabs );
59
+ createCurvesTimeline( bottomTabs );
60
+
61
+ let mode = types.KEYFRAMES;
62
+
63
+ bottomArea.onresize = (size) => {
64
+ activeTimeline.resize(size);
65
+ }
66
+
67
+ function loop(dt) {
68
+
69
+ if (!lastTime) {
70
+ lastTime = dt;
71
+ }
72
+
73
+ let elapsed = dt - lastTime;
74
+ lastTime = dt;
75
+
76
+ if(activeTimeline) {
77
+ activeTimeline.draw();
78
+ if(activeTimeline.playing) {
79
+
80
+ activeTimeline.currentTime += elapsed* 0.001;
81
+ if(activeTimeline.onSetTime) {
82
+ activeTimeline.onSetTime(activeTimeline.currentTime);
83
+ }
84
+ if(activeTimeline.currentTime >= activeTimeline.animationClip.duration) {
85
+ activeTimeline.currentTime = 0;
86
+ if(!activeTimeline.loop) {
87
+ activeTimeline.changeState();
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ if(mode == types.KEYFRAMES) {
94
+ let ctx = canvas.getContext("2d");
95
+ ctx.fillStyle = LX.getThemeColor("global-selected-light");
96
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
97
+ ctx.font = panel.getValue('Font Size') + "px Monospace";
98
+ ctx.fillStyle = panel.getValue('Font Color');
99
+
100
+ // Get values from panel widgets (e.g. position value)
101
+ const text = panel.getValue('Text');
102
+ const pos2D = panel.getValue('2D Position');
103
+ ctx.fillText(text, pos2D[0], pos2D[1]);
104
+ }
105
+ requestAnimationFrame(loop);
106
+ }
107
+
108
+ let lastTime = 0;
109
+ requestAnimationFrame(loop);
110
+
111
+ // **** **** **** **** **** **** **** **** **** **** **** ****
112
+
113
+ function fillPanel( panel ) {
114
+ panel.clear();
115
+ panel.branch("Canvas", {icon: "fa-solid fa-palette", filter: true});
116
+ panel.addColor("Background", LX.getThemeColor("global-selected-light"));
117
+ panel.addText("Track", trackName, null)
118
+ panel.addText("Text", "Lexgui.js @evallsg", null, {placeholder: "e.g. ColorPicker", icon: "fa fa-font"});
119
+ panel.addColor("Font Color", [0.1, 0.1, 0.8], (value, event) => {
120
+ console.log("Font Color: ", value);
121
+ });
122
+ panel.addNumber("Font Size", 36, (value, event) => {
123
+ console.log(value);
124
+ }, { min: 1, max: 48, step: 1});
125
+ panel.addVector2("2D Position", position, (value, event) => {
126
+
127
+ activeTimeline.updateSelectedKeyframe(value);
128
+ }, { min: 0, max: 1024 });
129
+ panel.branch_open = true;
130
+ panel.merge();
131
+ }
132
+
133
+
134
+ function fillClipPanel( panel, clip ) {
135
+
136
+ panel.clear();
137
+
138
+ if(!clip) {
139
+ return;
140
+ }
141
+
142
+ panel.branch(clip.name || "Clip", {icon: "fa-solid fa-clip", filter: true});
143
+ panel.addNumber("Start", clip.start, (v) => {
144
+ const diff = v - clip.start;
145
+ clip.start = v;
146
+ clip.fadein += diff;
147
+ clip.fadeout += diff;
148
+ LX.emit("@on_start_change_fadein", clip.fadein);
149
+ LX.emit("@on_start_change_fadeout", clip.fadeout);
150
+ }, {step: 0.01});
151
+ if(clip.fadein != undefined) {
152
+ panel.addNumber("Fade-In", clip.fadein, (v) => {
153
+ clip.fadein = v;
154
+ }, {min: clip.start, max: clip.fadeout, step: 0.01, signal: "@on_start_change_fadein"});
155
+ }
156
+ if(clip.fadeout != undefined) {
157
+ panel.addNumber("Fade-Out", clip.fadeout, (v) => {
158
+ clip.fadeout = v;
159
+ }, {min: clip.fadein, max: clip.start + clip.duration, step: 0.01, signal: "@on_start_change_fadeout"});
160
+ }
161
+ panel.addNumber("Duration", clip.duration, (v) => {
162
+ clip.duration = v;
163
+ }, {step: 0.01});
164
+
165
+ panel.merge();
166
+ }
167
+
168
+ function createKyeframeTimeline( bottomTabs ) {
169
+
170
+ let keyframesPanel = new LX.Panel();
171
+ bottomTabs.add( "Keyframes", keyframesPanel, {onSelect: () => {
172
+ activeTimeline = kfTimeline;
173
+ kfTimeline.show();
174
+ fillPanel(panel);
175
+ mode = types.KEYFRAMES;
176
+ }} );
177
+ keyframesPanel.onresize = () => {
178
+ kfTimeline.resize();
179
+ }
180
+ let kfTimeline = new LX.KeyFramesTimeline('', {
181
+ disableNewTracks: true,
182
+ onBeforeCreateTopBar: (panel) => {
183
+ panel.addDropdown("Animation", ["Anim 1", "Anim 2", "Anim 3"], "Anim 1", ()=> {})
184
+ },
185
+ onAfterCreateTopBar: (panel) => {
186
+ panel.addButton("", "autoKeyEnable", null, { icon: 'fa fa-wand-magic-sparkles', name: 'autoKeyEnabled', width: "40px" });
187
+ panel.addButton("", "unselectAll", (value, event) => { kfTimeline.unSelectAllKeyFrames();}, { icon: 'fa-regular fa-rectangle-xmark', name: 'unselectAll', callback: (value, event) => { kfTimeline.unSelectAllKeyFrames();}, width: "40px"});
188
+ }
189
+ });
190
+
191
+ keyframesPanel.attach(kfTimeline.root);
192
+ kfTimeline.setSelectedItems(["Font", "Item 2", "Item 3"]);
193
+ kfTimeline.setAnimationClip({tracks: [{name: "Font.position", values: [250, 350, 250, 250, 250, 350], times: [0, 0.1, 0.2]}, {name: "Item 1.scale", values: [0,1,0, 0.5], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 2", values: [0,1,0,1], times: [0.1]}, {name: "Item 3.position", values: [0,1,0,0,0,0], times: [0.1, 0.2, 0.3]}, {name: "Item 3.scale", values: [0,1,0], times: [ 0.1, 0.2, 0.3]}], duration: 1});
194
+ activeTimeline = kfTimeline;
195
+ kfTimeline.show();
196
+
197
+
198
+ kfTimeline.onSelectKeyFrame = (data, currentSelection, keyFrameIndex) => {
199
+ if(data.multipleSelection) {
200
+ rightArea.hide();
201
+ return;
202
+ }
203
+ rightArea.show();
204
+ trackName = data.track.name;
205
+ if( trackName != "Font" || data.track.type != "position") {
206
+ return
207
+ }
208
+ const id = data.track.clipIdx;
209
+ let values = kfTimeline.animationClip.tracks[id].values;
210
+ const dim = kfTimeline.animationClip.tracks[id].dim;
211
+
212
+ position = values.slice(keyFrameIndex * dim, keyFrameIndex * dim + dim);
213
+ fillPanel(panel);
214
+ //kfTimeline.animationClip.tracks[currentSelection[0][0]];
215
+ }
216
+
217
+ kfTimeline.onSetTime = ( time ) => {
218
+ rightArea.show();
219
+ const frametime = kfTimeline.getNearestKeyFrame( kfTimeline.animationClip.tracks[0], time);
220
+ let keyframe = kfTimeline.animationClip.tracks[0].times.indexOf(frametime);
221
+ let values = kfTimeline.animationClip.tracks[0].values;
222
+ let valueX = values[keyframe * 2];
223
+ let valueY = values[keyframe * 2 + 1];
224
+ if(keyframe != undefined) {
225
+ let t = Math.abs(frametime - time);
226
+ let sign = frametime - time > 0 ? -1 : 1;
227
+ const nearestFrame = keyframe * 2 + sign;
228
+ if (nearestFrame <= values.length - 2 && nearestFrame >= 0) {
229
+ valueX = valueX * (1 - t) + values[keyframe * 2 + sign] * t;
230
+ valueY = valueY * (1 - t) + values[keyframe * 2 + sign + 1] * t;
231
+ }
232
+ }
233
+ position = [valueX, valueY];
234
+ fillPanel( panel );
235
+ }
236
+ }
237
+
238
+ function createClipsTimeline( bottomTabs ) {
239
+
240
+ let clipsPanel = new LX.Panel();
241
+ bottomTabs.add( "Clips", clipsPanel, {onSelect: () => {
242
+ activeTimeline = clipsTimeline;
243
+ clipsTimeline.show();
244
+ panel.clear();
245
+ mode = types.CLIPS;
246
+ }});
247
+
248
+ let clipsTimeline = new LX.ClipsTimeline("clips-timeline");
249
+ clipsPanel.attach(clipsTimeline.root);
250
+
251
+ let clip = {id:"Clip 1", start:0, duration:1, type:""};
252
+ clipsTimeline.addClip(clip);
253
+ clip = {id:"Clip 2", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
254
+ clipsTimeline.addClip(clip);
255
+ clip = {id:"Clip 3", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
256
+ clipsTimeline.addClip(clip);
257
+ clip = {id:"Clip 4", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
258
+ clipsTimeline.addClip(clip);
259
+ clip = {id:"Clip 5", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
260
+ clipsTimeline.addClip(clip);
261
+
262
+ // clipsTimeline.setAnimationClip({tracks: [{clips: [clip]}], duration: 2});
263
+ clipsTimeline.selectedItems = ["Clip 1"];
264
+ clipsTimeline.show();
265
+
266
+ clipsTimeline.onSelectClip = (clip) => {
267
+ rightArea.show();
268
+ fillClipPanel(panel, clip);
269
+ }
270
+
271
+ clipsTimeline.draw(0);
272
+ }
273
+
274
+ function createCurvesTimeline( bottomTabs ) {
275
+
276
+ let curvesPanel = new LX.Panel();
277
+ bottomTabs.add( "Curves", curvesPanel, {onSelect: () => {
278
+ activeTimeline = curvesTimeline;
279
+ curvesTimeline.show();
280
+ panel.clear();
281
+ mode = types.CLIPS;
282
+
283
+ }});
284
+
285
+ let curvesTimeline = new LX.CurvesKeyFramesTimeline("curves-timeline", { range: [-1,1]});
286
+ curvesPanel.attach(curvesTimeline.root);
287
+ curvesTimeline.setSelectedItems(["Item 1", "Item 2", "Item 3"]);
288
+ curvesTimeline.setAnimationClip({tracks: [{name: "Item 1.position", values: [0,1,0,-1], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 1.scale", values: [0,1,0, 0.5], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 2", values: [0,1,0,1], times: [0.1, 0.2, 0.3, 0.8]}, {name: "Item 3.position", values: [0,0,1], times: [0]}, {name: "Item 3.scale", values: [0,1,0], times: [0, 0.1, 0.3]}], duration: 1});
289
+ curvesTimeline.show();
290
+ curvesTimeline.draw(0);
291
+ }
292
+
293
+ </script>
294
+ </html>
@@ -0,0 +1,52 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
5
+ <title>LexGUI Video Editor Demo</title>
6
+ <link rel="stylesheet" href="../build/lexgui.css">
7
+ <link rel="icon" href="../images/icon.png">
8
+ <script type="importmap">
9
+ {
10
+ "imports": {
11
+ "lexgui": "../build/lexgui.module.js",
12
+ "lexgui/components/": "../build/components/"
13
+ }
14
+ }
15
+ </script>
16
+ </head>
17
+ <body></body>
18
+ <script type="module">
19
+
20
+ import { LX } from 'lexgui';
21
+ import { VideoEditor } from 'lexgui/components/videoeditor.js';
22
+ // init library and get main area
23
+ let area = LX.init();
24
+
25
+ // split main area
26
+ let [leftArea, rightArea] = area.split({sizes:["75%","25%"], minimizable: true});
27
+ area.extend();
28
+
29
+ const videoArea = new LX.Area({id:'video-area'});
30
+ const video = document.createElement('video');
31
+ video.src = "../data/video.mp4";
32
+ videoArea.attach(video);
33
+
34
+ const videoEditor = new LX.VideoEditor(leftArea, {videoArea, video})
35
+
36
+ const canvas = document.createElement('canvas');
37
+ canvas.width = video.clientWidth;
38
+ canvas.height = video.clientHeight;
39
+ const ctx = canvas.getContext("2d");
40
+ ctx.fillStyle = LX.getThemeColor("global-selected-light");
41
+ ctx.font = "40px Arial";
42
+ ctx.fillText("I'm a canvas above the video!", canvas.width/2, 100);
43
+
44
+ canvas.style.position = "absolute";
45
+ canvas.style.left = "0";
46
+ videoArea.attach(canvas);
47
+
48
+ let panel = new LX.Panel();
49
+ panel = rightArea.addPanel(panel);
50
+
51
+ </script>
52
+ </html>
@@ -0,0 +1,118 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
5
+ <title>LexGUI Video Editor Demo</title>
6
+ <link rel="stylesheet" href="../build/lexgui.css">
7
+ <link rel="icon" href="../images/icon.png">
8
+ <script type="importmap">
9
+ {
10
+ "imports": {
11
+ "lexgui": "../build/lexgui.module.js",
12
+ "lexgui/components/": "../build/components/"
13
+ }
14
+ }
15
+ </script>
16
+ </head>
17
+ <body></body>
18
+ <script type="module">
19
+
20
+ import { LX } from 'lexgui';
21
+ import { VideoEditor } from 'lexgui/components/videoeditor.js';
22
+
23
+ // Init library and get main area
24
+ let area = LX.init();
25
+ const menubar = area.addMenubar( m => {
26
+ m.setButtonImage("Page", "../data/godot_pixelart.png", null, {float: "left"});
27
+ });
28
+
29
+ // Split main area
30
+ let [leftArea, rightArea] = area.split({sizes:["75%","25%"], minimizable: true});
31
+ let [topArea, bottomArea] = leftArea.split({sizes:["90%", null], minimizable: false, resize: false, type: "vertical"});
32
+ area.extend();
33
+
34
+ /* Create video area with a menubar */
35
+ const videoArea = new LX.Area({id:'video-area'});
36
+
37
+ /* Add video editor with the video into the area*/
38
+ const video = document.createElement('video');
39
+ video.src = "../data/video.mp4";
40
+ videoArea.attach(video);
41
+ const videoEditor = new LX.VideoEditor(topArea, {videoArea, video})
42
+
43
+ /* Add canvas above video */
44
+ const canvas = document.createElement('canvas');
45
+ canvas.width = video.clientWidth;
46
+ canvas.height = video.clientHeight;
47
+ const ctx = canvas.getContext("2d");
48
+ ctx.fillStyle = LX.getThemeColor("global-selected-light");
49
+ ctx.font = "40px Arial";
50
+ ctx.fillText("I'm a canvas above the video!", canvas.width/2, 100);
51
+
52
+ canvas.style.position = "absolute";
53
+ canvas.style.left = "0";
54
+ videoArea.attach(canvas);
55
+ leftArea.onresize = (size) => {
56
+
57
+ canvas.width = video.clientWidth;
58
+ canvas.height = video.clientHeight;
59
+ const ctx = canvas.getContext("2d");
60
+ ctx.fillStyle = LX.getThemeColor("global-selected-light");
61
+ ctx.font = "40px Arial";
62
+ ctx.fillText("I'm a canvas above the video!", canvas.width/2, 100);
63
+ //videoEditor.timebar.resize(size)
64
+ }
65
+
66
+ /* Add show/hide right panel button*/
67
+ videoArea.addOverlayButtons([{
68
+ selectable: true,
69
+ selected: true,
70
+ icon: "fa-solid fa-info",
71
+ name: "Properties",
72
+ callback: (v, e) => {
73
+ if(area.split_extended) {
74
+ area.reduce();
75
+ }
76
+ else {
77
+ area.extend();
78
+ }
79
+ }
80
+ }], {float: 'tvr'});
81
+
82
+ let p = bottomArea.addPanel({width: "100%", height: "100%", style: {display: "flex", "flex-direction": "row", "justify-content": "center", "align-content": "flex-start", "flex-wrap": "wrap"}});
83
+ p.addButton(null, "Trim", (v) => {
84
+ console.log(videoEditor.getTrimedTimes())
85
+
86
+ videoArea.sections[1].root.resize(["20%", "20%"])
87
+ }, {width: "100px"})
88
+ p.addButton(null, null, (v) => {}, {width: "40px", icon: "fa-solid fa-rotate-left"})
89
+
90
+ /* Create right panel */
91
+ let panel = new LX.Panel({id:"Properties"});
92
+ panel = rightArea.addPanel({id:"Properties"});
93
+ createBlendShapesInspector({"Name 1": 0, "Name 2": 0, "Name 3": 0.5, "Name 4": 0, "Name 5": 1,},
94
+ {inspector: panel});
95
+
96
+ /* Functions */
97
+ function createBlendShapesInspector(bsNames, options = {}) {
98
+
99
+ let inspector = options.inspector || new LX.Panel({id:"blendshapes-inspector"});
100
+
101
+ if(options.clear) {
102
+ inspector.clear();
103
+ }
104
+
105
+ if(inspector.root.id) {
106
+ inspector.addTitle(inspector.root.id);
107
+ }
108
+
109
+ for(let name in bsNames) {
110
+ inspector.addProgress(name, bsNames[name], {min: 0, max: 1, low: 0.3, optimum: 1, high: 0.6, editable: options.editable, showNumber: options.showNumber,
111
+ callback: (v,e) => {},
112
+ signal: "@on_change_au_" + name});
113
+ }
114
+
115
+ return inspector;
116
+ }
117
+ </script>
118
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",