diff --git a/demo/client/css/pathfinder.css b/demo/client/css/pathfinder.css index 7411127d..ea2f4ba8 100644 --- a/demo/client/css/pathfinder.css +++ b/demo/client/css/pathfinder.css @@ -1,12 +1,33 @@ -.pf-bottom-control { - position: fixed; - bottom: 1em; -} #pf-load-font-button-label, #pf-load-svg-button-label { left: 1em; margin: 0; } +#pf-settings { + user-select: none; + -moz-user-select: none; + opacity: 1.0; + transition: opacity 300ms, transform 300ms; + transform: translateY(0); +} +#pf-settings-button:not(:hover) { + background: rgba(255, 255, 255, 0.75); +} +#pf-file-select { + position: absolute; + visibility: hidden; +} +#pf-settings.pf-invisible { + opacity: 0.0; + transform: translateY(1em); +} +button > svg { + width: 1.25em; + display: block; +} +button > svg path { + fill: currentColor; +} #pf-rendering-options-group { right: 1em; } @@ -16,12 +37,32 @@ touch-action: none; } #pf-fps-label { - position: absolute; - right: 1em; - margin-top: 1em; color: white; background: rgba(0, 0, 0, 0.75); } #pf-svg { visibility: hidden; } + +.pf-arrow-box:after, .pf-arrow-box:before { + top: 100%; + left: 22px; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.pf-arrow-box:after { + border-color: rgba(0, 0, 0, 0); + border-top-color: white; + border-width: 14px; + margin-left: -14px; +} +.pf-arrow-box:before { + border-color: rgba(0, 0, 0, 0); + border-top-color: rgba(0, 0, 0, 0.125); + border-width: 15px; + margin-left: -15px; +} \ No newline at end of file diff --git a/demo/client/html/3d-demo.html b/demo/client/html/3d-demo.html index 4b6f3d83..330c27b9 100644 --- a/demo/client/html/3d-demo.html +++ b/demo/client/html/3d-demo.html @@ -8,15 +8,36 @@ ${require('./include/navbar.html')} -
0 ms
-
- +
+
+
+
+ +

Settings

+
+
+ + +
+
+
+
+
+ +
+
0 ms
diff --git a/demo/client/html/include/header.html b/demo/client/html/include/header.html index e757f451..8dee8b16 100644 --- a/demo/client/html/include/header.html +++ b/demo/client/html/include/header.html @@ -1,5 +1,6 @@ + diff --git a/demo/client/html/svg-demo.html b/demo/client/html/svg-demo.html index cf0869f2..0df1bddb 100644 --- a/demo/client/html/svg-demo.html +++ b/demo/client/html/svg-demo.html @@ -8,20 +8,45 @@ ${require('./include/navbar.html')} -
0 ms
- -
- +
+
+
+
+ +

Settings

+
+
+ + + +
+
+ + +
+
+
+
+
+ +
+
0 ms
diff --git a/demo/client/html/text-demo.html b/demo/client/html/text-demo.html index 9d4c1d04..b900d372 100644 --- a/demo/client/html/text-demo.html +++ b/demo/client/html/text-demo.html @@ -8,19 +8,46 @@ ${require('./include/navbar.html')} -
0 ms
- -
- +
+
+
+
+ +

Settings

+
+
+ + + +
+
+ + +
+
+
+
+
+ +
+
0 ms
diff --git a/demo/client/package.json b/demo/client/package.json index 4bf7934d..60e09b79 100644 --- a/demo/client/package.json +++ b/demo/client/package.json @@ -22,10 +22,12 @@ "html-loader": "^0.5.1", "jquery": "^3.2.1", "lodash": "^4.17.4", + "octicons": "^6.0.1", "opentype.js": "^0.7.3", "parse-color": "^1.0.0", "path-data-polyfill.js": "^1.0.2", "popper.js": "^1.12.5", + "svg-inline-loader": "^0.8.0", "ts-loader": "^2.3.2", "typescript": "^2.4.2", "webpack": "^3.4.1" diff --git a/demo/client/src/app-controller.ts b/demo/client/src/app-controller.ts index 242e8b70..0540eeb4 100644 --- a/demo/client/src/app-controller.ts +++ b/demo/client/src/app-controller.ts @@ -10,7 +10,7 @@ import {AntialiasingStrategyName} from "./aa-strategy"; import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader'; -import {expectNotNull, unwrapUndef} from './utils'; +import { expectNotNull, unwrapUndef, unwrapNull } from './utils'; import {PathfinderView} from "./view"; export default abstract class AppController { @@ -19,6 +19,25 @@ export default abstract class AppController { start() { const canvas = document.getElementById('pf-canvas') as HTMLCanvasElement; + this.settingsCard = document.getElementById('pf-settings') as HTMLElement; + this.settingsButton = document.getElementById('pf-settings-button') as HTMLButtonElement; + this.settingsCloseButton = document.getElementById('pf-settings-close-button') as + HTMLButtonElement; + this.settingsButton.addEventListener('click', () => { + this.settingsCard.classList.toggle('pf-invisible'); + }, false); + this.settingsCloseButton.addEventListener('click', () => { + this.settingsCard.classList.add('pf-invisible'); + }, false); + + this.filePickerElement = document.getElementById('pf-file-select') as HTMLInputElement; + this.filePickerElement.addEventListener('change', () => this.loadFile(), false); + + this.selectFileElement = document.getElementById('pf-select-file') as HTMLSelectElement; + this.selectFileElement.addEventListener('click', () => { + this.fileSelectionChanged(); + }, false); + const shaderLoader = new ShaderLoader; shaderLoader.load(); @@ -33,14 +52,14 @@ export default abstract class AppController { private updateAALevel() { const selectedOption = this.aaLevelSelect.selectedOptions[0]; - const aaType = unwrapUndef(selectedOption.dataset.pfType) as - AntialiasingStrategyName; - const aaLevel = parseInt(unwrapUndef(selectedOption.dataset.pfLevel)); + const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value)); + const aaType = aaValues[1] as AntialiasingStrategyName; + const aaLevel = aaValues[2] === "" ? 1 : parseInt(aaValues[2]); this.view.then(view => view.setAntialiasingOptions(aaType, aaLevel)); } protected loadFile() { - const file = expectNotNull(this.loadFileButton.files, "No file selected!")[0]; + const file = expectNotNull(this.filePickerElement.files, "No file selected!")[0]; const reader = new FileReader; reader.addEventListener('loadend', () => { this.fileData = reader.result; @@ -49,18 +68,41 @@ export default abstract class AppController { reader.readAsArrayBuffer(file); } + protected fileSelectionChanged() { + const selectedOption = this.selectFileElement.selectedOptions[0] as HTMLOptionElement; + + if (selectedOption.value === 'load-custom') { + this.filePickerElement.click(); + + const oldSelectedIndex = this.selectFileElement.selectedIndex; + const newOption = document.createElement('option'); + newOption.id = 'pf-custom-option-placeholder'; + newOption.appendChild(document.createTextNode("Custom")); + this.selectFileElement.insertBefore(newOption, selectedOption); + this.selectFileElement.selectedIndex = oldSelectedIndex; + return; + } + + const placeholder = document.getElementById('pf-custom-option-placeholder'); + if (placeholder != null) + this.selectFileElement.removeChild(placeholder); + } + protected abstract fileLoaded(): void; protected abstract createView(canvas: HTMLCanvasElement, commonShaderSource: string, - shaderSources: ShaderMap): - View; + shaderSources: ShaderMap): View; view: Promise; protected fileData: ArrayBuffer; protected canvas: HTMLCanvasElement; - protected loadFileButton: HTMLInputElement; + protected selectFileElement: HTMLSelectElement; + protected filePickerElement: HTMLInputElement; private aaLevelSelect: HTMLSelectElement; + private settingsCard: HTMLElement; + private settingsButton: HTMLButtonElement; + private settingsCloseButton: HTMLButtonElement; } diff --git a/demo/client/src/svg-demo.ts b/demo/client/src/svg-demo.ts index db4e589f..86fb5df8 100644 --- a/demo/client/src/svg-demo.ts +++ b/demo/client/src/svg-demo.ts @@ -59,9 +59,6 @@ class SVGDemoController extends AppController { this.svg = document.getElementById('pf-svg') as Element as SVGSVGElement; this.pathElements = []; - - this.loadFileButton = document.getElementById('pf-load-svg-button') as HTMLInputElement; - this.loadFileButton.addEventListener('change', () => this.loadFile(), false); } protected fileLoaded() { diff --git a/demo/client/src/text-demo.ts b/demo/client/src/text-demo.ts index fe28d80a..d3d0d171 100644 --- a/demo/client/src/text-demo.ts +++ b/demo/client/src/text-demo.ts @@ -119,9 +119,6 @@ class TextDemoController extends AppController { this.fontSize = INITIAL_FONT_SIZE; this.fpsLabel = unwrapNull(document.getElementById('pf-fps-label')); - - this.loadFileButton = document.getElementById('pf-load-font-button') as HTMLInputElement; - this.loadFileButton.addEventListener('change', () => this.loadFile(), false); } protected createView(canvas: HTMLCanvasElement, diff --git a/demo/client/webpack.config.js b/demo/client/webpack.config.js index 87b486ee..a1c5b254 100644 --- a/demo/client/webpack.config.js +++ b/demo/client/webpack.config.js @@ -12,6 +12,10 @@ module.exports = { use: 'ts-loader', exclude: /node_modules/, }, + { + test: /\.svg?$/, + use: 'svg-inline-loader', + }, { test: /html\/[a-zA-Z0-9_-]+\.html$/, use: [ diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index 0109e511..aafb04a9 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -42,11 +42,13 @@ static STATIC_TEXT_DEMO_PATH: &'static str = "../client/text-demo.html"; static STATIC_SVG_DEMO_PATH: &'static str = "../client/svg-demo.html"; static STATIC_3D_DEMO_PATH: &'static str = "../client/3d-demo.html"; static STATIC_CSS_BOOTSTRAP_PATH: &'static str = "../client/node_modules/bootstrap/dist/css"; +static STATIC_CSS_OCTICONS_PATH: &'static str = "../client/node_modules/octicons/build"; static STATIC_CSS_PATHFINDER_PATH: &'static str = "../client/css/pathfinder.css"; static STATIC_JS_BOOTSTRAP_PATH: &'static str = "../client/node_modules/bootstrap/dist/js"; static STATIC_JS_JQUERY_PATH: &'static str = "../client/node_modules/jquery/dist"; static STATIC_JS_POPPER_JS_PATH: &'static str = "../client/node_modules/popper.js/dist/umd"; static STATIC_JS_PATHFINDER_PATH: &'static str = "../client"; +static STATIC_SVG_OCTICONS_PATH: &'static str = "../client/node_modules/octicons/build/svg"; static STATIC_GLSL_PATH: &'static str = "../../shaders"; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -489,6 +491,10 @@ fn static_3d_demo() -> io::Result { fn static_css_bootstrap(file: PathBuf) -> Option { NamedFile::open(Path::new(STATIC_CSS_BOOTSTRAP_PATH).join(file)).ok() } +#[get("/css/octicons/")] +fn static_css_octicons(file: PathBuf) -> Option { + NamedFile::open(Path::new(STATIC_CSS_OCTICONS_PATH).join(file)).ok() +} #[get("/css/pathfinder.css")] fn static_css_pathfinder_css() -> io::Result { NamedFile::open(STATIC_CSS_PATHFINDER_PATH) @@ -509,6 +515,10 @@ fn static_js_popper_js(file: PathBuf) -> Option { fn static_js_pathfinder(file: PathBuf) -> Option { NamedFile::open(Path::new(STATIC_JS_PATHFINDER_PATH).join(file)).ok() } +#[get("/svg/octicons/")] +fn static_svg_octicons(file: PathBuf) -> Option { + NamedFile::open(Path::new(STATIC_SVG_OCTICONS_PATH).join(file)).ok() +} #[get("/glsl/")] fn static_glsl(file: PathBuf) -> Option { Shader::open(Path::new(STATIC_GLSL_PATH).join(file)).ok() @@ -540,11 +550,13 @@ fn main() { static_svg_demo, static_3d_demo, static_css_bootstrap, + static_css_octicons, static_css_pathfinder_css, static_js_bootstrap, static_js_jquery, static_js_popper_js, static_js_pathfinder, + static_svg_octicons, static_glsl, ]).launch(); }