@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 16 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 UI from"../../ui/legacy/legacy.js";import*as EmulationModel from"../../models/emulation/emulation.js";import{DeviceModeToolbar}from"./DeviceModeToolbar.js";import{MediaQueryInspector}from"./MediaQueryInspector.js";import deviceModeViewStyles from"./deviceModeView.css.legacy.js";const UIStrings={doubleclickForFullHeight:"Double-click for full height",mobileS:"Mobile S",mobileM:"Mobile M",mobileL:"Mobile L",tablet:"Tablet",laptop:"Laptop",laptopL:"Laptop L"},str_=i18n.i18n.registerUIStrings("panels/emulation/DeviceModeView.ts",UIStrings),i18nString=i18n.i18n.getLocalizedString.bind(void 0,str_);export class DeviceModeView extends UI.Widget.VBox{wrapperInstance;blockElementToWidth;model;mediaInspector;showMediaInspectorSetting;showRulersSetting;topRuler;leftRuler;presetBlocks;responsivePresetsContainer;screenArea;pageArea;outlineImage;contentClip;contentArea;rightResizerElement;leftResizerElement;bottomResizerElement;bottomRightResizerElement;bottomLeftResizerElement;cachedResizable;mediaInspectorContainer;screenImage;toolbar;slowPositionStart;resizeStart;cachedCssScreenRect;cachedCssVisiblePageRect;cachedOutlineRect;cachedMediaInspectorVisible;cachedShowRulers;cachedScale;handleWidth;handleHeight;constructor(){super(!0),this.blockElementToWidth=new WeakMap,this.setMinimumSize(150,150),this.element.classList.add("device-mode-view"),this.registerRequiredCSS(deviceModeViewStyles),this.model=EmulationModel.DeviceModeModel.DeviceModeModel.instance(),this.model.addEventListener("Updated",this.updateUI,this),this.mediaInspector=new MediaQueryInspector((()=>this.model.appliedDeviceSize().width),this.model.setWidth.bind(this.model),new Common.Throttler.Throttler(0)),this.showMediaInspectorSetting=Common.Settings.Settings.instance().moduleSetting("showMediaQueryInspector"),this.showMediaInspectorSetting.addChangeListener(this.updateUI,this),this.showRulersSetting=Common.Settings.Settings.instance().moduleSetting("emulation.showRulers"),this.showRulersSetting.addChangeListener(this.updateUI,this),this.topRuler=new Ruler(!0,this.model.setWidthAndScaleToFit.bind(this.model)),this.topRuler.element.classList.add("device-mode-ruler-top"),this.leftRuler=new Ruler(!1,this.model.setHeightAndScaleToFit.bind(this.model)),this.leftRuler.element.classList.add("device-mode-ruler-left"),this.createUI(),UI.ZoomManager.ZoomManager.instance().addEventListener("ZoomChanged",this.zoomChanged,this)}createUI(){this.toolbar=new DeviceModeToolbar(this.model,this.showMediaInspectorSetting,this.showRulersSetting),this.contentElement.appendChild(this.toolbar.element()),this.contentClip=this.contentElement.createChild("div","device-mode-content-clip vbox"),this.responsivePresetsContainer=this.contentClip.createChild("div","device-mode-presets-container"),this.populatePresetsContainer(),this.mediaInspectorContainer=this.contentClip.createChild("div","device-mode-media-container"),this.contentArea=this.contentClip.createChild("div","device-mode-content-area"),this.outlineImage=this.contentArea.createChild("img","device-mode-outline-image hidden fill"),this.outlineImage.addEventListener("load",this.onImageLoaded.bind(this,this.outlineImage,!0),!1),this.outlineImage.addEventListener("error",this.onImageLoaded.bind(this,this.outlineImage,!1),!1),this.screenArea=this.contentArea.createChild("div","device-mode-screen-area"),this.screenImage=this.screenArea.createChild("img","device-mode-screen-image hidden"),this.screenImage.addEventListener("load",this.onImageLoaded.bind(this,this.screenImage,!0),!1),this.screenImage.addEventListener("error",this.onImageLoaded.bind(this,this.screenImage,!1),!1),this.bottomRightResizerElement=this.screenArea.createChild("div","device-mode-resizer device-mode-bottom-right-resizer"),this.bottomRightResizerElement.createChild("div",""),this.createResizer(this.bottomRightResizerElement,2,1),this.bottomLeftResizerElement=this.screenArea.createChild("div","device-mode-resizer device-mode-bottom-left-resizer"),this.bottomLeftResizerElement.createChild("div",""),this.createResizer(this.bottomLeftResizerElement,-2,1),this.rightResizerElement=this.screenArea.createChild("div","device-mode-resizer device-mode-right-resizer"),this.rightResizerElement.createChild("div",""),this.createResizer(this.rightResizerElement,2,0),this.leftResizerElement=this.screenArea.createChild("div","device-mode-resizer device-mode-left-resizer"),this.leftResizerElement.createChild("div",""),this.createResizer(this.leftResizerElement,-2,0),this.bottomResizerElement=this.screenArea.createChild("div","device-mode-resizer device-mode-bottom-resizer"),this.bottomResizerElement.createChild("div",""),this.createResizer(this.bottomResizerElement,0,1),this.bottomResizerElement.addEventListener("dblclick",this.model.setHeight.bind(this.model,0),!1),UI.Tooltip.Tooltip.install(this.bottomResizerElement,i18nString(UIStrings.doubleclickForFullHeight)),this.pageArea=this.screenArea.createChild("div","device-mode-page-area"),this.pageArea.createChild("slot")}populatePresetsContainer(){const e=[320,375,425,768,1024,1440,2560],t=[i18nString(UIStrings.mobileS),i18nString(UIStrings.mobileM),i18nString(UIStrings.mobileL),i18nString(UIStrings.tablet),i18nString(UIStrings.laptop),i18nString(UIStrings.laptopL),"4K"];this.presetBlocks=[];const i=this.responsivePresetsContainer.createChild("div","device-mode-presets-container-inner");for(let o=e.length-1;o>=0;--o){const n=i.createChild("div","fill device-mode-preset-bar-outer").createChild("div","device-mode-preset-bar");n.createChild("span").textContent=t[o]+" – "+e[o]+"px",n.addEventListener("click",s.bind(this,e[o]),!1),this.blockElementToWidth.set(n,e[o]),this.presetBlocks.push(n)}function s(e,t){this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive,null,null),this.model.setWidthAndScaleToFit(e),t.consume()}}createResizer(e,t,i){const s=new UI.ResizerWidget.ResizerWidget;s.addElement(e);let o=t?"ew-resize":"ns-resize";return t*i>0&&(o="nwse-resize"),t*i<0&&(o="nesw-resize"),s.setCursor(o),s.addEventListener(UI.ResizerWidget.Events.ResizeStart,this.onResizeStart,this),s.addEventListener(UI.ResizerWidget.Events.ResizeUpdateXY,this.onResizeUpdate.bind(this,t,i)),s.addEventListener(UI.ResizerWidget.Events.ResizeEnd,this.onResizeEnd,this),s}onResizeStart(){this.slowPositionStart=null;const e=this.model.screenRect();this.resizeStart=new UI.Geometry.Size(e.width,e.height)}onResizeUpdate(e,t,i){i.data.shiftKey!==Boolean(this.slowPositionStart)&&(this.slowPositionStart=i.data.shiftKey?{x:i.data.currentX,y:i.data.currentY}:null);let s=i.data.currentX-i.data.startX,o=i.data.currentY-i.data.startY;if(this.slowPositionStart&&(s=(i.data.currentX-this.slowPositionStart.x)/10+this.slowPositionStart.x-i.data.startX,o=(i.data.currentY-this.slowPositionStart.y)/10+this.slowPositionStart.y-i.data.startY),e&&this.resizeStart){const t=s*UI.ZoomManager.ZoomManager.instance().zoomFactor();let i=this.resizeStart.width+t*e;i=Math.round(i/this.model.scale()),i>=EmulationModel.DeviceModeModel.MinDeviceSize&&i<=EmulationModel.DeviceModeModel.MaxDeviceSize&&this.model.setWidth(i)}if(t&&this.resizeStart){const e=o*UI.ZoomManager.ZoomManager.instance().zoomFactor();let i=this.resizeStart.height+e*t;i=Math.round(i/this.model.scale()),i>=EmulationModel.DeviceModeModel.MinDeviceSize&&i<=EmulationModel.DeviceModeModel.MaxDeviceSize&&this.model.setHeight(i)}}exitHingeMode(){this.model&&this.model.exitHingeMode()}onResizeEnd(){delete this.resizeStart,Host.userMetrics.actionTaken(Host.UserMetrics.Action.ResizedViewInResponsiveMode)}updateUI(){function e(e,t){e.style.left=t.left+"px",e.style.top=t.top+"px",e.style.width=t.width+"px",e.style.height=t.height+"px"}if(!this.isShowing())return;const t=UI.ZoomManager.ZoomManager.instance().zoomFactor();let i=!1;const s=this.showRulersSetting.get()&&this.model.type()!==EmulationModel.DeviceModeModel.Type.None;let o=!1,n=!1;const r=this.model.screenRect().scale(1/t);this.cachedCssScreenRect&&r.isEqual(this.cachedCssScreenRect)||(e(this.screenArea,r),n=!0,i=!0,this.cachedCssScreenRect=r);const a=this.model.visiblePageRect().scale(1/t);this.cachedCssVisiblePageRect&&a.isEqual(this.cachedCssVisiblePageRect)||(e(this.pageArea,a),i=!0,this.cachedCssVisiblePageRect=a);const l=this.model.outlineRect();if(l){const s=l.scale(1/t);this.cachedOutlineRect&&s.isEqual(this.cachedOutlineRect)||(e(this.outlineImage,s),i=!0,this.cachedOutlineRect=s)}this.contentClip.classList.toggle("device-mode-outline-visible",Boolean(this.model.outlineImage()));const h=this.model.type()===EmulationModel.DeviceModeModel.Type.Responsive;h!==this.cachedResizable&&(this.rightResizerElement.classList.toggle("hidden",!h),this.leftResizerElement.classList.toggle("hidden",!h),this.bottomResizerElement.classList.toggle("hidden",!h),this.bottomRightResizerElement.classList.toggle("hidden",!h),this.bottomLeftResizerElement.classList.toggle("hidden",!h),this.cachedResizable=h);const d=this.showMediaInspectorSetting.get()&&this.model.type()!==EmulationModel.DeviceModeModel.Type.None;if(d!==this.cachedMediaInspectorVisible&&(d?this.mediaInspector.show(this.mediaInspectorContainer):this.mediaInspector.detach(),o=!0,i=!0,this.cachedMediaInspectorVisible=d),s!==this.cachedShowRulers&&(this.contentClip.classList.toggle("device-mode-rulers-visible",s),s?(this.topRuler.show(this.contentArea),this.leftRuler.show(this.contentArea)):(this.topRuler.detach(),this.leftRuler.detach()),o=!0,i=!0,this.cachedShowRulers=s),this.model.scale()!==this.cachedScale){n=!0,i=!0;for(const e of this.presetBlocks){const t=this.blockElementToWidth.get(e);if(!t)throw new Error("Could not get width for block.");e.style.width=t*this.model.scale()+"px"}this.cachedScale=this.model.scale()}this.toolbar.update(),this.loadImage(this.screenImage,this.model.screenImage()),this.loadImage(this.outlineImage,this.model.outlineImage()),this.mediaInspector.setAxisTransform(this.model.scale()),i&&this.doResize(),n&&(this.topRuler.render(this.model.scale()),this.leftRuler.render(this.model.scale()),this.topRuler.element.positionAt(this.cachedCssScreenRect?this.cachedCssScreenRect.left:0,this.cachedCssScreenRect?this.cachedCssScreenRect.top:0),this.leftRuler.element.positionAt(this.cachedCssScreenRect?this.cachedCssScreenRect.left:0,this.cachedCssScreenRect?this.cachedCssScreenRect.top:0)),o&&this.contentAreaResized()}loadImage(e,t){e.getAttribute("srcset")!==t&&(e.setAttribute("srcset",t),t||e.classList.toggle("hidden",!0))}onImageLoaded(e,t){e.classList.toggle("hidden",!t)}setNonEmulatedAvailableSize(e){if(this.model.type()!==EmulationModel.DeviceModeModel.Type.None)return;const t=UI.ZoomManager.ZoomManager.instance().zoomFactor(),i=e.getBoundingClientRect(),s=new UI.Geometry.Size(Math.max(i.width*t,1),Math.max(i.height*t,1));this.model.setAvailableSize(s,s)}contentAreaResized(){const e=UI.ZoomManager.ZoomManager.instance().zoomFactor(),t=this.contentArea.getBoundingClientRect(),i=new UI.Geometry.Size(Math.max(t.width*e,1),Math.max(t.height*e,1)),s=new UI.Geometry.Size(Math.max((t.width-2*(this.handleWidth||0))*e,1),Math.max((t.height-(this.handleHeight||0))*e,1));this.model.setAvailableSize(i,s)}measureHandles(){const e=this.rightResizerElement.classList.contains("hidden");this.rightResizerElement.classList.toggle("hidden",!1),this.bottomResizerElement.classList.toggle("hidden",!1),this.handleWidth=this.rightResizerElement.offsetWidth,this.handleHeight=this.bottomResizerElement.offsetHeight,this.rightResizerElement.classList.toggle("hidden",e),this.bottomResizerElement.classList.toggle("hidden",e)}zoomChanged(){delete this.handleWidth,delete this.handleHeight,this.isShowing()&&(this.measureHandles(),this.contentAreaResized())}onResize(){this.isShowing()&&this.contentAreaResized()}wasShown(){this.measureHandles(),this.toolbar.restore()}willHide(){this.model.emulate(EmulationModel.DeviceModeModel.Type.None,null,null)}async captureScreenshot(){const e=await this.model.captureScreenshot(!1);if(null===e)return;const t=new Image;t.src="data:image/png;base64,"+e,t.onload=async()=>{const e=t.naturalWidth/this.model.screenRect().width,i=this.model.outlineRect();if(!i)throw new Error("Unable to take screenshot: no outlineRect available.");const s=i.scale(e),o=this.model.screenRect().scale(e),n=this.model.visiblePageRect().scale(e),r=o.left+n.left-s.left,a=o.top+n.top-s.top,l=document.createElement("canvas");l.width=Math.floor(s.width),l.height=Math.min(16384,Math.floor(s.height));const h=l.getContext("2d");if(!h)throw new Error("Could not get 2d context from canvas.");h.imageSmoothingEnabled=!1,this.model.outlineImage()&&await this.paintImage(h,this.model.outlineImage(),s.relativeTo(s)),this.model.screenImage()&&await this.paintImage(h,this.model.screenImage(),o.relativeTo(s)),h.drawImage(t,Math.floor(r),Math.floor(a)),this.saveScreenshot(l)}}async captureFullSizeScreenshot(){const e=await this.model.captureScreenshot(!0);if(null!==e)return this.saveScreenshotBase64(e)}async captureAreaScreenshot(e){const t=await this.model.captureScreenshot(!1,e);if(null!==t)return this.saveScreenshotBase64(t)}saveScreenshotBase64(e){const t=new Image;t.src="data:image/png;base64,"+e,t.onload=()=>{const e=document.createElement("canvas");e.width=t.naturalWidth,e.height=Math.min(16384,Math.floor(t.naturalHeight));const i=e.getContext("2d");if(!i)throw new Error("Could not get 2d context for base64 screenshot.");i.imageSmoothingEnabled=!1,i.drawImage(t,0,0),this.saveScreenshot(e)}}paintImage(e,t,i){return new Promise((s=>{const o=new Image;o.crossOrigin="Anonymous",o.srcset=t,o.onerror=()=>s(),o.onload=()=>{e.drawImage(o,i.left,i.top,i.width,i.height),s()}}))}saveScreenshot(e){const t=this.model.inspectedURL();let i="";if(t){const e=Platform.StringUtilities.removeURLFragment(t);i=Platform.StringUtilities.trimURL(e)}const s=this.model.device();s&&this.model.type()===EmulationModel.DeviceModeModel.Type.Device&&(i+=`(${s.title})`);const o=document.createElement("a");o.download=i+".png",e.toBlob((e=>{null!==e&&(o.href=URL.createObjectURL(e),o.click())}))}}export class Ruler extends UI.Widget.VBox{contentElementInternal;horizontal;scale;count;throttler;applyCallback;renderedScale;renderedZoomFactor;constructor(e,t){super(),this.element.classList.add("device-mode-ruler"),this.contentElementInternal=this.element.createChild("div","device-mode-ruler-content").createChild("div","device-mode-ruler-inner"),this.horizontal=e,this.scale=1,this.count=0,this.throttler=new Common.Throttler.Throttler(0),this.applyCallback=t}render(e){this.scale=e,this.throttler.schedule(this.update.bind(this))}onResize(){this.throttler.schedule(this.update.bind(this))}update(){const e=UI.ZoomManager.ZoomManager.instance().zoomFactor(),t=this.horizontal?this.contentElementInternal.offsetWidth:this.contentElementInternal.offsetHeight;this.scale===this.renderedScale&&e===this.renderedZoomFactor||(this.contentElementInternal.removeChildren(),this.count=0,this.renderedScale=this.scale,this.renderedZoomFactor=e);const i=t*e/this.scale,s=Math.ceil(i/5);let o=1;this.scale<.8&&(o=2),this.scale<.6&&(o=4),this.scale<.4&&(o=8),this.scale<.2&&(o=16),this.scale<.1&&(o=32);for(let e=s;e<this.count;e++)if(!(e%o)){const e=this.contentElementInternal.lastChild;e&&e.remove()}for(let t=this.count;t<s;t++){if(t%o)continue;const i=this.contentElementInternal.createChild("div","device-mode-ruler-marker");if(t&&(this.horizontal?i.style.left=5*t*this.scale/e+"px":i.style.top=5*t*this.scale/e+"px",!(t%20))){const e=i.createChild("div","device-mode-ruler-text");e.textContent=String(5*t),e.addEventListener("click",this.onMarkerClick.bind(this,5*t),!1)}t%10?t%5||i.classList.add("device-mode-ruler-marker-medium"):i.classList.add("device-mode-ruler-marker-large")}return this.count=s,Promise.resolve()}onMarkerClick(e){this.applyCallback.call(null,e)}}