Przeglądaj źródła

Merge pull request #4596 from RaananW/navbar-plugin-system

Viewer template plugin system
Raanan Weber 7 lat temu
rodzic
commit
c48ea86c5f

+ 1 - 19
Viewer/assets/templates/default/navbar.html

@@ -152,16 +152,6 @@
         content: "\EF4E";
     }
 
-    .hd-icon:after {
-        font-size: 16px;
-        content: "\F765";
-    }
-
-    .sd-icon:after {
-        font-size: 16px;
-        content: "\F766";
-    }
-
     viewer.in-vr .vr-icon:after {
         font-size: 16px;
         content: "\E7F4";
@@ -503,15 +493,7 @@
     </div>
     {{/unless}}
     <div class="default-control">
-        {{#unless hideHd}}
-        <button class="hd-button hd-button" title="{{text.hdButton}}">
-            {{#if hdEnabled}}
-            <span class="icon sd-icon"></span>
-            {{else}}
-            <span class="icon hd-icon"></span>
-            {{/if}}
-        </button>
-        {{/unless}} {{#unless hideVr}}
+        {{#unless hideVr}}
         <button class="vr vr-button" title="{{text.vrButton}}">
             <span class="icon vr-icon"></span>
         </button>

+ 38 - 0
Viewer/src/templating/plugins/hdButtonPlugin.ts

@@ -0,0 +1,38 @@
+import { AbstractViewerNavbarButton } from "../viewerTemplatePlugin";
+import { DefaultViewer } from "../../viewer/defaultViewer";
+import { EventCallback, Template } from "../templateManager";
+
+export class HDButtonPlugin extends AbstractViewerNavbarButton {
+
+    protected _buttonClass = "hd-button";
+
+    constructor(private _viewer: DefaultViewer) {
+        super();
+    }
+
+    onEvent(event: EventCallback): void {
+        let button = event.template.parent.querySelector(".hd-button");
+        if (button) {
+            button.classList.contains("hd-toggled") ? button.classList.remove("hd-toggled") : button.classList.add("hd-toggled");
+        }
+        this._viewer.toggleHD();
+    }
+
+    protected _htmlTemplate: string = `
+{{#unless hideHd}}
+<style>
+.hd-icon:after {
+    font-size: 16px;
+    content: "\\F765";
+}
+
+.hd-toggled span.hd-icon:after {
+    content: "\\F766";
+}
+</style>
+<button class="hd-button" title="{{text.hdButton}}">
+     <span class="icon hd-icon"></span>
+ </button>
+ {{/unless}}
+`;
+}

+ 9 - 0
Viewer/src/templating/templateManager.ts

@@ -272,6 +272,10 @@ export class Template {
      */
     public onEventTriggered: Observable<EventCallback>;
 
+    public onParamsUpdated: Observable<Template>;
+
+    public onHTMLRendered: Observable<Template>;
+
     /**
      * is the template loaded?
      */
@@ -309,6 +313,8 @@ export class Template {
         this.onAppended = new Observable<Template>();
         this.onStateChange = new Observable<Template>();
         this.onEventTriggered = new Observable<EventCallback>();
+        this.onParamsUpdated = new Observable<Template>();
+        this.onHTMLRendered = new Observable<Template>();
 
         this.loadRequests = [];
 
@@ -432,6 +438,9 @@ export class Template {
         } else {
             this.parent.insertAdjacentHTML("beforeend", this._rawHtml);
         }
+
+        this.onHTMLRendered.notifyObservers(this);
+
         // appended only one frame after.
         setTimeout(() => {
             this._registerEvents();

+ 70 - 0
Viewer/src/templating/viewerTemplatePlugin.ts

@@ -0,0 +1,70 @@
+import { EventCallback, Template } from "./templateManager";
+import * as Handlebars from 'handlebars/dist/handlebars';
+
+export interface IViewerTemplatePlugin {
+
+    readonly templateName: string;
+    readonly eventsToAttach?: Array<string>;
+
+    interactionPredicate(event: EventCallback): boolean;
+    onEvent?(event: EventCallback): void;
+    addHTMLTemplate?(template: Template): void;
+}
+
+export abstract class AbstractViewerNavbarButton implements IViewerTemplatePlugin {
+
+    public readonly templateName: string = "navBar";
+    public readonly eventsToAttach: Array<string> = ['pointerdown'];
+    protected _prepend: boolean = true;
+    protected abstract _buttonClass: string;
+    protected abstract _htmlTemplate: string;
+
+    interactionPredicate(event: EventCallback): boolean {
+        let pointerDown = <PointerEvent>event.event;
+        if (pointerDown.button !== 0) return false;
+        var element = (<HTMLElement>event.event.target);
+
+        if (!element) {
+            return false;
+        }
+
+        let elementClasses = element.classList;
+
+        for (let i = 0; i < elementClasses.length; ++i) {
+            let className = elementClasses[i];
+            if (className.indexOf(this._buttonClass) !== -1) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+    abstract onEvent(event: EventCallback): void;
+
+    public addHTMLTemplate(template: Template): void {
+        let element = this._generateHTMLElement(template);
+        let container = template.parent.querySelector("div.default-control");
+        if (container) {
+            if (this._prepend) {
+                container.insertBefore(element, container.firstChild);
+            } else {
+                container.appendChild(element);
+            }
+        }
+    }
+
+    protected _generateHTMLElement(template: Template): Element | DocumentFragment {
+        let compiledTemplate = Handlebars.compile(this._htmlTemplate, { noEscape: (template.configuration.params && !!template.configuration.params.noEscape) });
+        let config = template.configuration.params || {};
+        let rawHtml = compiledTemplate(config);
+        let fragment: Element | DocumentFragment;
+        try {
+            fragment = document.createRange().createContextualFragment(rawHtml);
+        } catch (e) {
+            let test = document.createElement(this._buttonClass);
+            test.innerHTML = rawHtml;
+            fragment = test;
+        }
+        return fragment;
+    }
+}

+ 40 - 35
Viewer/src/viewer/defaultViewer.ts

@@ -1,13 +1,13 @@
 
 
 import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration';
-import { Template, EventCallback, TemplateManager } from '../templating/templateManager';
+import { Template, EventCallback } from '../templating/templateManager';
 import { AbstractViewer } from './viewer';
-import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight, FilesInput } from 'babylonjs';
-import { CameraBehavior } from '../interfaces';
+import { SpotLight, Vector3, FilesInput } from 'babylonjs';
 import { ViewerModel } from '../model/viewerModel';
-import { extendClassWithConfig } from '../helper';
 import { IModelAnimation, AnimationState } from '../model/modelAnimation';
+import { IViewerTemplatePlugin } from '../templating/viewerTemplatePlugin';
+import { HDButtonPlugin } from '../templating/plugins/hdButtonPlugin';
 
 /**
  * The Default viewer is the default implementation of the AbstractViewer.
@@ -32,11 +32,40 @@ export class DefaultViewer extends AbstractViewer {
 
         this.onEngineInitObservable.add(() => {
             this.sceneManager.onLightsConfiguredObservable.add((data) => {
-                this._configureLights(data.newConfiguration, data.model!);
+                this._configureLights();
             })
         });
     }
 
+    private _registeredPlugins: Array<IViewerTemplatePlugin> = [];
+
+    public registerTemplatePlugin(plugin: IViewerTemplatePlugin) {
+        //validate
+        if (!plugin.templateName) {
+            throw new Error("No template name provided");
+        }
+        this._registeredPlugins.push(plugin);
+        let template = this.templateManager.getTemplate(plugin.templateName);
+        if (!template) {
+            throw new Error(`Template ${plugin.templateName} not found`);
+        }
+        if (plugin.addHTMLTemplate) {
+            template.onHTMLRendered.add((tmpl) => {
+                plugin.addHTMLTemplate && plugin.addHTMLTemplate(tmpl);
+            });
+        }
+
+        if (plugin.eventsToAttach) {
+            plugin.eventsToAttach.forEach(eventName => {
+                plugin.onEvent && this.templateManager.eventManager.registerCallback(plugin.templateName, (event) => {
+                    if (plugin.onEvent && plugin.interactionPredicate(event)) {
+                        plugin.onEvent(event);
+                    }
+                }, eventName);
+            });
+        }
+    }
+
     /**
      * This will be executed when the templates initialize.
      */
@@ -77,10 +106,6 @@ export class DefaultViewer extends AbstractViewer {
         return super._onTemplatesLoaded();
     }
 
-    private _dropped(evt: EventCallback) {
-
-    }
-
     private _initNavbar() {
         let navbar = this.templateManager.getTemplate('navBar');
         if (navbar) {
@@ -100,7 +125,7 @@ export class DefaultViewer extends AbstractViewer {
                 this._currentAnimation.goToFrame(gotoFrame);
             }, "input");
 
-            this.templateManager.eventManager.registerCallback("navBar", (e) => {
+            this.templateManager.eventManager.registerCallback("navBar", () => {
                 if (this._resumePlay) {
                     this._togglePlayPause(true);
                 }
@@ -112,6 +137,8 @@ export class DefaultViewer extends AbstractViewer {
                     hideHdButton: true
                 });
             }
+
+            this.registerTemplatePlugin(new HDButtonPlugin(this));
         }
     }
 
@@ -134,7 +161,7 @@ export class DefaultViewer extends AbstractViewer {
 
         let elementClasses = element.classList;
 
-        let elementName = ""; 0
+        let elementName = "";
 
         for (let i = 0; i < elementClasses.length; ++i) {
             let className = elementClasses[i];
@@ -180,9 +207,6 @@ export class DefaultViewer extends AbstractViewer {
             case "fullscreen-button":
                 this.toggleFullscreen();
                 break;
-            case "hd-button":
-                this.toggleHD();
-                break;
             case "vr-button":
                 this.toggleVR();
                 break;
@@ -293,24 +317,6 @@ export class DefaultViewer extends AbstractViewer {
         this._updateAnimationSpeed("1.0", paramsObject);
     }
 
-    public toggleHD() {
-        super.toggleHD();
-
-        // update UI element
-        let navbar = this.templateManager.getTemplate('navBar');
-        if (!navbar) return;
-
-        if (navbar.configuration.params) {
-            navbar.configuration.params.hdEnabled = this._hdToggled;
-        }
-
-        let span = navbar.parent.querySelector("button.hd-button span");
-        if (span) {
-            span.classList.remove(this._hdToggled ? "hd-icon" : "sd-icon");
-            span.classList.add(!this._hdToggled ? "hd-icon" : "sd-icon")
-        }
-    }
-
     public toggleVR() {
         super.toggleVR();
 
@@ -589,10 +595,9 @@ export class DefaultViewer extends AbstractViewer {
      * @param lightsConfiguration the light configuration to use
      * @param model the model that will be used to configure the lights (if the lights are model-dependant)
      */
-    private _configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean | number } = {}, model?: ViewerModel) {
+    private _configureLights() {
         // labs feature - flashlight
         if (this.configuration.lab && this.configuration.lab.flashlight) {
-            let pointerPosition = Vector3.Zero();
             let lightTarget;
             let angle = 0.5;
             let exponent = Math.PI / 2;
@@ -617,7 +622,7 @@ export class DefaultViewer extends AbstractViewer {
 
             }
             this.sceneManager.scene.constantlyUpdateMeshUnderPointer = true;
-            this.sceneManager.scene.onPointerObservable.add((eventData, eventState) => {
+            this.sceneManager.scene.onPointerObservable.add((eventData) => {
                 if (eventData.type === 4 && eventData.pickInfo) {
                     lightTarget = (eventData.pickInfo.pickedPoint);
                 } else {

Plik diff jest za duży
+ 3 - 7
Viewer/src/viewer/viewer.ts


+ 1 - 0
dist/preview release/what's new.md

@@ -78,6 +78,7 @@
 - A new HD-Toggler button allows setting a better hardware scaling rate ([RaananW](https://github.com/RaananW))
 - An initial support for WebVR is implemented ([RaananW](https://github.com/RaananW))
 - It is now possible to choose the element that goes fullscreen in the default viewer ([RaananW](https://github.com/RaananW))
+- The default viewer has a plugin system with which new buttons can be added externally ([RaananW](https://github.com/RaananW))
 
 ### Documentation