@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 17.6 kB
JavaScript
import*as Common from"../../core/common/common.js";import*as Host from"../../core/host/host.js";import*as i18n from"../../core/i18n/i18n.js";import*as Platform from"../../core/platform/platform.js";import*as SDK from"../../core/sdk/sdk.js";import*as UI from"../../ui/legacy/legacy.js";import{AnimationGroupPreviewUI}from"./AnimationGroupPreviewUI.js";import animationTimelineStyles from"./animationTimeline.css.js";import{AnimationModel,Events}from"./AnimationModel.js";import{AnimationScreenshotPopover}from"./AnimationScreenshotPopover.js";import{AnimationUI}from"./AnimationUI.js";const UIStrings={selectAnEffectAboveToInspectAnd:"Select an effect above to inspect and modify.",clearAll:"Clear all",pauseAll:"Pause all",playbackRates:"Playback rates",playbackRatePlaceholder:"{PH1}%",pause:"Pause",setSpeedToS:"Set speed to {PH1}",animationPreviews:"Animation previews",waitingForAnimations:"Waiting for animations...",replayTimeline:"Replay timeline",resumeAll:"Resume all",playTimeline:"Play timeline",pauseTimeline:"Pause timeline",animationPreviewS:"Animation Preview {PH1}"},str_=i18n.i18n.registerUIStrings("panels/animation/AnimationTimeline.ts",UIStrings),i18nString=i18n.i18n.getLocalizedString.bind(void 0,str_),nodeUIsByNode=new WeakMap,playbackRates=new WeakMap;let animationTimelineInstance;export class AnimationTimeline extends UI.Widget.VBox{#e;#t;#i;#n;#s;#r;#o;#a;#l;#h;#c;#d;#u;#m;#p;#b;#g;#f;#v;#y;#S;#T;#w;#A;#P;#I;#M;#C;#k;#U;#B;#R;constructor(){super(!0),this.element.classList.add("animations-timeline"),this.#e=this.contentElement.createChild("div","grid-overflow-wrapper"),this.#t=UI.UIUtils.createSVGChild(this.#e,"svg","animation-timeline-grid"),this.#i=1,this.#n=!1,this.createHeader(),this.#s=this.contentElement.createChild("div","animation-timeline-rows");this.contentElement.createChild("div","animation-timeline-rows-hint").textContent=i18nString(UIStrings.selectAnEffectAboveToInspectAnd),this.#m=100,this.#p=this.#m,this.#b=150,this.#g=new Map,this.#f=[],this.#v=[],this.#y=new Map,this.#S=new Map,SDK.TargetManager.TargetManager.instance().addModelListener(SDK.DOMModel.DOMModel,SDK.DOMModel.Events.NodeRemoved,this.nodeRemoved,this,{scoped:!0}),SDK.TargetManager.TargetManager.instance().observeModels(AnimationModel,this,{scoped:!0}),UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode,this.nodeChanged,this)}static instance(e){return animationTimelineInstance&&!e?.forceNew||(animationTimelineInstance=new AnimationTimeline),animationTimelineInstance}get previewMap(){return this.#y}get uiAnimations(){return this.#f}get groupBuffer(){return this.#v}wasShown(){for(const e of SDK.TargetManager.TargetManager.instance().models(AnimationModel,{scoped:!0}))this.addEventListeners(e);this.registerCSSFiles([animationTimelineStyles])}willHide(){for(const e of SDK.TargetManager.TargetManager.instance().models(AnimationModel,{scoped:!0}))this.removeEventListeners(e);this.#h&&this.#h.hidePopover()}modelAdded(e){this.isShowing()&&this.addEventListeners(e)}modelRemoved(e){this.removeEventListeners(e)}addEventListeners(e){e.ensureEnabled(),e.addEventListener(Events.AnimationGroupStarted,this.animationGroupStarted,this),e.addEventListener(Events.ModelReset,this.reset,this)}removeEventListeners(e){e.removeEventListener(Events.AnimationGroupStarted,this.animationGroupStarted,this),e.removeEventListener(Events.ModelReset,this.reset,this)}nodeChanged(){for(const e of this.#g.values())e.nodeChanged()}createScrubber(){return this.#a=document.createElement("div"),this.#a.classList.add("animation-scrubber"),this.#a.classList.add("hidden"),this.#T=this.#a.createChild("div","animation-scrubber-line"),this.#T.createChild("div","animation-scrubber-head"),this.#a.createChild("div","animation-time-overlay"),this.#a}createHeader(){const e=this.contentElement.createChild("div","animation-timeline-toolbar-container"),t=new UI.Toolbar.Toolbar("animation-timeline-toolbar",e);this.#c=new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clearAll),"clear"),this.#c.addEventListener(UI.Toolbar.ToolbarButton.Events.Click,(()=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.AnimationGroupsCleared),this.reset()})),t.appendToolbarItem(this.#c),t.appendSeparator(),this.#w=new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.pauseAll),"pause","resume"),this.#w.addEventListener(UI.Toolbar.ToolbarButton.Events.Click,(()=>{this.togglePauseAll()})),t.appendToolbarItem(this.#w);const i=e.createChild("div","animation-playback-rate-control");i.addEventListener("keydown",this.handlePlaybackRateControlKeyDown.bind(this)),UI.ARIAUtils.markAsListBox(i),UI.ARIAUtils.setLabel(i,i18nString(UIStrings.playbackRates)),this.#r=[];for(const e of GlobalPlaybackRates){const t=i.createChild("button","animation-playback-rate-button");t.textContent=e?i18nString(UIStrings.playbackRatePlaceholder,{PH1:100*e}):i18nString(UIStrings.pause),playbackRates.set(t,e),t.addEventListener("click",this.setPlaybackRate.bind(this,e)),UI.ARIAUtils.markAsOption(t),UI.Tooltip.Tooltip.install(t,i18nString(UIStrings.setSpeedToS,{PH1:t.textContent})),t.tabIndex=-1,this.#r.push(t)}this.updatePlaybackControls(),this.#o=this.contentElement.createChild("div","animation-timeline-buffer"),UI.ARIAUtils.markAsListBox(this.#o),UI.ARIAUtils.setLabel(this.#o,i18nString(UIStrings.animationPreviews)),this.#h=new UI.PopoverHelper.PopoverHelper(this.#o,this.getPopoverRequest.bind(this)),this.#h.setDisableOnClick(!0),this.#h.setTimeout(0);this.contentElement.createChild("div","animation-timeline-buffer-hint").textContent=i18nString(UIStrings.waitingForAnimations);const n=this.contentElement.createChild("div","animation-timeline-header"),s=n.createChild("div","animation-controls");this.#l=s.createChild("div","animation-timeline-current-time monospace");const r=new UI.Toolbar.Toolbar("animation-controls-toolbar",s);this.#A=new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.replayTimeline),"replay"),this.#P="replay-outline",this.#A.setToggled(!0),this.#A.addEventListener(UI.Toolbar.ToolbarButton.Events.Click,this.controlButtonToggle.bind(this)),r.appendToolbarItem(this.#A);const o=n.createChild("div","animation-grid-header");return UI.UIUtils.installDragHandle(o,this.repositionScrubber.bind(this),this.scrubberDragMove.bind(this),this.scrubberDragEnd.bind(this),"text"),this.#e.appendChild(this.createScrubber()),this.#T&&UI.UIUtils.installDragHandle(this.#T,this.scrubberDragStart.bind(this),this.scrubberDragMove.bind(this),this.scrubberDragEnd.bind(this),"col-resize"),this.#l.textContent="",n}handlePlaybackRateControlKeyDown(e){switch(e.key){case"ArrowLeft":case"ArrowUp":this.focusNextPlaybackRateButton(e.target,!0);break;case"ArrowRight":case"ArrowDown":this.focusNextPlaybackRateButton(e.target)}}focusNextPlaybackRateButton(e,t){const i=e,n=this.#r.indexOf(i),s=t?n-1:n+1;if(s<0||s>=this.#r.length)return;const r=this.#r[s];r.tabIndex=0,r.focus(),e&&(e.tabIndex=-1)}getPopoverRequest(e){const t=e.target;return t&&t.isDescendant(this.#o)?{box:t.boxInWindow(),show:e=>{let i;for(const[e,n]of this.#y)n.element!==t&&n.element!==t.parentElement||(i=e);if(console.assert(void 0!==i),!i)return Promise.resolve(!1);const n=i.screenshots();if(!n.length)return Promise.resolve(!1);let s;const r=new Promise((e=>{s=e}));return n[0].complete?o(n):n[0].onload=o.bind(null,n),r;function o(t){new AnimationScreenshotPopover(t).show(e.contentElement),s(!0)}},hide:void 0}:null}togglePauseAll(){this.#n=!this.#n,Host.userMetrics.actionTaken(this.#n?Host.UserMetrics.Action.AnimationsPaused:Host.UserMetrics.Action.AnimationsResumed),this.#w&&this.#w.setToggled(this.#n),this.setPlaybackRate(this.#i),this.#w&&this.#w.setTitle(this.#n?i18nString(UIStrings.resumeAll):i18nString(UIStrings.pauseAll))}setPlaybackRate(e){e!==this.#i&&Host.userMetrics.animationPlaybackRateChanged(.1===e?2:.25===e?1:1===e?0:3),this.#i=e;for(const e of SDK.TargetManager.TargetManager.instance().models(AnimationModel,{scoped:!0}))e.setPlaybackRate(this.#n?0:this.#i);Host.userMetrics.actionTaken(Host.UserMetrics.Action.AnimationsPlaybackRateChanged),this.#k&&(this.#k.playbackRate=this.effectivePlaybackRate()),this.updatePlaybackControls()}updatePlaybackControls(){for(const e of this.#r){const t=this.#i===playbackRates.get(e);e.classList.toggle("selected",t),e.tabIndex=t?0:-1}}controlButtonToggle(){"play-outline"===this.#P?this.togglePause(!1):"replay-outline"===this.#P?(Host.userMetrics.actionTaken(Host.UserMetrics.Action.AnimationGroupReplayed),this.replay()):this.togglePause(!0)}updateControlButton(){this.#A&&(this.#A.setEnabled(Boolean(this.#d)),this.#d&&this.#d.paused()?(this.#P="play-outline",this.#A.setToggled(!0),this.#A.setTitle(i18nString(UIStrings.playTimeline)),this.#A.setGlyph("play")):!this.#k||!this.#k.currentTime||"number"!=typeof this.#k.currentTime||this.#k.currentTime>=this.duration()?(this.#P="replay-outline",this.#A.setToggled(!0),this.#A.setTitle(i18nString(UIStrings.replayTimeline)),this.#A.setGlyph("replay")):(this.#P="pause-outline",this.#A.setToggled(!1),this.#A.setTitle(i18nString(UIStrings.pauseTimeline)),this.#A.setGlyph("pause")))}effectivePlaybackRate(){return this.#n||this.#d&&this.#d.paused()?0:this.#i}togglePause(e){if(this.#d){this.#d.togglePause(e);const t=this.#y.get(this.#d);t&&t.element.classList.toggle("paused",e)}this.#k&&(this.#k.playbackRate=this.effectivePlaybackRate()),this.updateControlButton()}replay(){this.#d&&(this.#d.seekTo(0),this.animateTime(0),this.updateControlButton())}duration(){return this.#p}setDuration(e){this.#p=e,this.scheduleRedraw()}clearTimeline(){this.#f=[],this.#g.clear(),this.#S.clear(),this.#s.removeChildren(),this.#p=this.#m,this.#a.classList.add("hidden"),this.#d=null,this.#k&&this.#k.cancel(),this.#k=void 0,this.#l.textContent="",this.updateControlButton()}reset(){this.clearTimeline(),this.setPlaybackRate(this.#i);for(const e of this.#v)e.release();this.#v=[],this.#y.clear(),this.#o.removeChildren(),this.#h.hidePopover(),this.renderGrid()}animationGroupStarted({data:e}){this.addAnimationGroup(e)}addAnimationGroup(e){const t=this.#y.get(e);if(t)return void(this.#d===e?this.syncScrubber():t.replay());this.#v.sort((function(e,t){return e.startTime()===t.startTime()?0:e.startTime()>t.startTime()?1:-1}));const i=[],n=this.width()/50;for(;this.#v.length>n;){const e=this.#v.splice(this.#v[0]===this.#d?1:0,1);i.push(e[0])}for(const e of i){const t=this.#y.get(e);t&&(t.element.remove(),this.#y.delete(e),e.release())}const s=new AnimationGroupPreviewUI(e);if(this.#v.push(e),this.#y.set(e,s),this.#o.appendChild(s.element),s.removeButton().addEventListener("click",this.removeAnimationGroup.bind(this,e)),s.element.addEventListener("click",this.selectAnimationGroup.bind(this,e)),s.element.addEventListener("keydown",this.handleAnimationGroupKeyDown.bind(this,e)),UI.ARIAUtils.setLabel(s.element,i18nString(UIStrings.animationPreviewS,{PH1:this.#v.indexOf(e)+1})),UI.ARIAUtils.markAsOption(s.element),1===this.#y.size){const e=this.#y.get(this.#v[0]);e&&(e.element.tabIndex=0)}}handleAnimationGroupKeyDown(e,t){switch(t.key){case" ":case"Enter":this.selectAnimationGroup(e);break;case"Backspace":case"Delete":this.removeAnimationGroup(e,t);break;case"ArrowLeft":case"ArrowUp":this.focusNextGroup(e,t.target,!0);break;case"ArrowRight":case"ArrowDown":this.focusNextGroup(e,t.target)}}focusNextGroup(e,t,i){const n=this.#v.indexOf(e),s=i?n-1:n+1;if(s<0||s>=this.#v.length)return;const r=this.#y.get(this.#v[s]);r&&(r.element.tabIndex=0,r.element.focus()),t&&(t.tabIndex=-1)}removeAnimationGroup(e,t){const i=this.#v.indexOf(e);Platform.ArrayUtilities.removeElement(this.#v,e);const n=this.#y.get(e);n&&n.element.remove(),this.#y.delete(e),e.release(),t.consume(!0),this.#d===e&&(this.clearTimeline(),this.renderGrid());if(0===this.#v.length)return void this.#c.element.focus();const s=i>=this.#v.length?this.#y.get(this.#v[this.#v.length-1]):this.#y.get(this.#v[i]);s&&(s.element.tabIndex=0,s.element.focus())}selectAnimationGroup(e){if(this.#d===e)return this.togglePause(!1),void this.replay();this.clearTimeline(),this.#d=e,this.#y.forEach((function(e,t){e.element.classList.toggle("selected",this.#d===t)}),this),this.setDuration(Math.max(500,e.finiteDuration()+100));for(const t of e.animations())this.addAnimation(t);this.scheduleRedraw(),this.#a.classList.remove("hidden"),this.togglePause(!1),this.replay()}addAnimation(e){let t=this.#g.get(e.source().backendNodeId());t||(t=new NodeUI(e.source()),this.#s.appendChild(t.element),this.#g.set(e.source().backendNodeId(),t));const i=t.createNewRow(),n=new AnimationUI(e,this,i);e.source().deferredNode().resolve(function(e){n.setNode(e),e&&t&&(t.nodeResolved(e),nodeUIsByNode.set(e,t))}.bind(this)),this.#f.push(n),this.#S.set(e.id(),e)}nodeRemoved(e){const{node:t}=e.data,i=nodeUIsByNode.get(t);i&&i.nodeRemoved()}renderGrid(){const e=(this.width()+10).toString(),t=((this.#C||0)+30).toString();let i;this.#e.style.width=e+"px",this.#e.style.height=t.toString()+"px",this.#t.setAttribute("width",e),this.#t.setAttribute("height",t.toString()),this.#t.setAttribute("shape-rendering","crispEdges"),this.#t.removeChildren();for(let e=0;e<this.duration();e+=250){const t=UI.UIUtils.createSVGChild(this.#t,"rect","animation-timeline-grid-line");t.setAttribute("x",(e*this.pixelMsRatio()+10).toString()),t.setAttribute("y","23"),t.setAttribute("height","100%"),t.setAttribute("width","1")}for(let e=0;e<this.duration();e+=250){const t=e*this.pixelMsRatio();if(void 0===i||t-i>50){i=t;const n=UI.UIUtils.createSVGChild(this.#t,"text","animation-timeline-grid-label");n.textContent=i18n.TimeUtilities.millisToString(e),n.setAttribute("x",(t+10).toString()),n.setAttribute("y","16")}}}scheduleRedraw(){this.#u=[];for(const e of this.#f)this.#u.push(e);this.#I||(this.#I=!0,this.renderGrid(),this.#s.window().requestAnimationFrame(this.render.bind(this)))}render(e){for(;this.#u.length&&(!e||window.performance.now()-e<50);){const e=this.#u.shift();e&&e.redraw()}this.#u.length?this.#s.window().requestAnimationFrame(this.render.bind(this)):this.#I=void 0}onResize(){this.#M=Math.max(0,this.#s.offsetWidth-this.#b)||0,this.#C=this.#s.offsetHeight,this.scheduleRedraw(),this.#k&&this.syncScrubber(),this.#U=void 0}width(){return this.#M||0}resizeWindow(e){let t=!1;const i=e.source().duration()*Math.min(2,e.source().iterations()),n=e.source().delay()+i+e.source().endDelay();return n>this.#p&&(t=!0,this.#p=n+200),t}syncScrubber(){this.#d&&this.#d.currentTimePromise().then(this.animateTime.bind(this)).then(this.updateControlButton.bind(this))}animateTime(e){this.#k&&this.#k.cancel(),this.#k=this.#a.animate([{transform:"translateX(0px)"},{transform:"translateX("+this.width()+"px)"}],{duration:this.duration(),fill:"forwards"}),this.#k.playbackRate=this.effectivePlaybackRate(),this.#k.onfinish=this.updateControlButton.bind(this),this.#k.currentTime=e,this.element.window().requestAnimationFrame(this.updateScrubber.bind(this))}pixelMsRatio(){return this.width()/this.duration()||0}updateScrubber(e){this.#k&&(this.#l.textContent=i18n.TimeUtilities.millisToString(this.#x()),"pending"===this.#k.playState.toString()||"running"===this.#k.playState?this.element.window().requestAnimationFrame(this.updateScrubber.bind(this)):"finished"===this.#k.playState&&(this.#l.textContent=""))}repositionScrubber(e){if(!this.#d)return!1;this.#U||(this.#U=this.#t.getBoundingClientRect().left+10);const{x:t}=e,i=Math.max(0,t-this.#U)/this.pixelMsRatio();return this.#d.seekTo(i),this.togglePause(!0),this.animateTime(i),this.#B=i,this.#R=t,!0}scrubberDragStart(e){if(!this.#k||!this.#d)return!1;this.#B="number"==typeof this.#k.currentTime?this.#k.currentTime:null,this.#a.classList.remove("animation-timeline-end"),this.#k.pause();const{x:t}=e;return this.#R=t,this.togglePause(!0),!0}scrubberDragMove(e){const{x:t}=e,i=t-(this.#R||0),n=Math.max(0,Math.min((this.#B||0)+i/this.pixelMsRatio(),this.duration()));this.#k&&(this.#k.currentTime=n),this.#l.textContent=i18n.TimeUtilities.millisToString(Math.round(n)),this.#d&&this.#d.seekTo(n)}#x(){return"number"==typeof this.#k?.currentTime?this.#k.currentTime:0}scrubberDragEnd(e){if(this.#k){const e=Math.max(0,this.#x());this.#k.play(),this.#k.currentTime=e}Host.userMetrics.actionTaken(Host.UserMetrics.Action.AnimationGroupScrubbed),this.#l.window().requestAnimationFrame(this.updateScrubber.bind(this))}}export const GlobalPlaybackRates=[1,.25,.1];export class NodeUI{element;#G;#E;#L;constructor(e){this.element=document.createElement("div"),this.element.classList.add("animation-node-row"),this.#G=this.element.createChild("div","animation-node-description"),this.#E=this.element.createChild("div","animation-node-timeline"),UI.ARIAUtils.markAsApplication(this.#E)}nodeResolved(e){e?(this.#L=e,this.nodeChanged(),Common.Linkifier.Linkifier.linkify(e).then((e=>{e.addEventListener("click",(()=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.AnimatedNodeDescriptionClicked)})),this.#G.appendChild(e)})),e.ownerDocument||this.nodeRemoved()):UI.UIUtils.createTextChild(this.#G,"<node>")}createNewRow(){return this.#E.createChild("div","animation-timeline-row")}nodeRemoved(){this.element.classList.add("animation-node-removed"),this.#L=null}nodeChanged(){let e=!1;this.#L&&(e=this.#L===UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode)),this.element.classList.toggle("animation-node-selected",e)}}export class StepTimingFunction{steps;stepAtPosition;constructor(e,t){this.steps=e,this.stepAtPosition=t}static parse(e){let t=e.match(/^steps\((\d+), (start|middle)\)$/);return t?new StepTimingFunction(parseInt(t[1],10),t[2]):(t=e.match(/^steps\((\d+)\)$/),t?new StepTimingFunction(parseInt(t[1],10),"end"):null)}}