Refactored material ui elements

This commit is contained in:
Lucas Dower 2022-11-12 23:23:00 +00:00
parent 47f0042d9e
commit a34734265e
No known key found for this signature in database
GPG Key ID: B3EE6B8499593605
4 changed files with 199 additions and 73 deletions

View File

@ -12,6 +12,7 @@ import { MaterialMap, MaterialType, SolidMaterial, TexturedMaterial } from './me
import { Renderer } from './renderer';
import { StatusHandler, StatusMessage } from './status';
import { TextureFiltering } from './texture';
import { SolidMaterialUIElement, TextureMaterialUIElement } from './ui/elements/material';
import { OutputStyle } from './ui/elements/output';
import { UI } from './ui/layout';
import { UIMessageBuilder, UITreeBuilder } from './ui/misc';
@ -227,7 +228,7 @@ export class AppContext {
this._ui.disableAll();
}
private _onMaterialTypeSwitched(materialName: string) {
public onMaterialTypeSwitched(materialName: string) {
const oldMaterial = this._materialMap[materialName];
if (oldMaterial.type == MaterialType.textured) {
@ -253,7 +254,7 @@ export class AppContext {
});
}
private _onMaterialTextureReplace(materialName: string, newTexturePath: string) {
public onMaterialTextureReplace(materialName: string, newTexturePath: string) {
const oldMaterial = this._materialMap[materialName];
ASSERT(oldMaterial.type === MaterialType.textured);
@ -271,7 +272,7 @@ export class AppContext {
});
}
private _onMaterialColourChanged(materialName: string, newColour: RGBA) {
public onMaterialColourChanged(materialName: string, newColour: RGBA) {
ASSERT(this._materialMap[materialName].type === MaterialType.solid);
const oldMaterial = this._materialMap[materialName] as TexturedMaterial;
this._materialMap[materialName] = {
@ -300,74 +301,16 @@ export class AppContext {
const subTree = UITreeBuilder.create(material.edited ? `<i>'${materialName}'*</i>` : `'${materialName}'`);
if (material.type === MaterialType.solid) {
const colourId = getRandomID();
subTree.addChild({ text: `Colour: <input class="colour-swatch" type="color" id="${colourId}" value="${RGBAUtil.toHexString(material.colour)}">`, warning: false }, () => {
const tmp = document.getElementById(colourId) as HTMLInputElement;
if (tmp) {
tmp.addEventListener('change', () => {
const newColour = RGBAUtil.fromHexString(tmp.value);
this._onMaterialColourChanged(materialName, newColour);
});
}
});
const uiElement = new SolidMaterialUIElement(materialName, this, material);
if (material.canBeTextured) {
// Add option to switch to texture material
const switchId = getRandomID();
subTree.addChild({ text: `<a id="${switchId}">[Switch to Texture]</a>`, warning: false }, () => {
const tmp = document.getElementById(switchId) as HTMLLinkElement;
tmp.onclick = () => {
this._onMaterialTypeSwitched(materialName);
};
});
}
subTree.addChild({ html: uiElement.buildHTML(), warning: uiElement.hasWarning()}, () => {
uiElement.registerEvents();
});
} else {
const parsedPath = path.parse(material.path);
const dirId = getRandomID();
const replaceId = getRandomID();
const switchId = getRandomID();
const uiElement = new TextureMaterialUIElement(materialName, this, material);
const isMissingTexture = parsedPath.base === 'debug.png';
if (!isMissingTexture) {
subTree.addChild({ text: `Texture: <a id="${dirId}">${parsedPath.base}</a>`, warning: false }, () => {
const tmp = document.getElementById(dirId) as HTMLLinkElement;
if (tmp) {
tmp.onclick = () => {
FileUtil.openDir(material.path);
};
}
});
} else {
subTree.addChild({ text: `Texture: Missing`, warning: true });
}
// Add option to replace texture
const text = isMissingTexture ? `<b><a id="${replaceId}">[Find Texture]</a></b>` : `<a id="${replaceId}">[Replace Texture]</a>`;
subTree.addChild({ text: text, warning: false }, () => {
const tmp = document.getElementById(replaceId) as HTMLLinkElement;
if (tmp) {
tmp.onclick = () => {
const files = remote.dialog.showOpenDialogSync({
title: 'Load',
buttonLabel: 'Load',
filters: [{
name: 'Images',
extensions: ['png', 'jpeg', 'jpg'],
}],
});
if (files && files[0]) {
this._onMaterialTextureReplace(materialName, files[0]);
}
};
}
});
// Add option to switch to colour material
subTree.addChild({ text: `<a id="${switchId}">[Switch to Colour]</a>`, warning: false }, () => {
const tmp = document.getElementById(switchId) as HTMLLinkElement;
tmp.onclick = () => {
this._onMaterialTypeSwitched(materialName);
};
subTree.addChild({ html: uiElement.buildHTML(), warning: uiElement.hasWarning()}, () => {
uiElement.registerEvents();
});
}

157
src/ui/elements/material.ts Normal file
View File

@ -0,0 +1,157 @@
import { remote } from 'electron';
import path from 'path';
import { AppContext } from '../../app_context';
import { RGBAUtil } from '../../colour';
import { SolidMaterial, TexturedMaterial } from '../../mesh';
import { getRandomID } from '../../util';
import { FileUtil } from '../../util/file_util';
export abstract class MaterialUIElement {
protected readonly _materialName: string;
protected readonly _appContext: AppContext;
private _actions: { text: string, onClick: () => void, id: string }[];
public constructor(materialName: string, appContext: AppContext) {
this._materialName = materialName;
this._appContext = appContext;
this._actions = [];
}
public hasWarning() {
return false;
}
public buildHTML(): string {
let html = this.buildChildHTML();
this._actions.forEach((action) => {
html += `<br><a id="${action.id}">[${action.text}]</a>`;
});
return html;
}
public registerEvents() {
this._actions.forEach((action) => {
const element = document.getElementById(action.id);
if (element !== null) {
element.addEventListener('click', () => {
action.onClick();
});
}
});
}
public addAction(text: string, onClick: () => void) {
this._actions.push({ text: text, onClick: onClick, id: getRandomID() });
}
protected abstract buildChildHTML(): string
}
export class TextureMaterialUIElement extends MaterialUIElement {
private _material: TexturedMaterial;
private _imageId: string;
public constructor(materialName: string, appContext: AppContext, material: TexturedMaterial) {
super(materialName, appContext);
this._material = material;
this._imageId = getRandomID();
const parsedPath = path.parse(material.path);
const isMissingTexture = parsedPath.base === 'debug.png';
super.addAction(isMissingTexture ? 'Find texture' : 'Replace texture', () => {
const files = remote.dialog.showOpenDialogSync({
title: 'Load',
buttonLabel: 'Load',
filters: [{
name: 'Images',
extensions: ['png', 'jpeg', 'jpg'],
}],
});
if (files && files[0]) {
this._appContext.onMaterialTextureReplace(materialName, files[0]);
}
});
super.addAction('Switch to colour', () => {
this._appContext.onMaterialTypeSwitched(materialName);
});
}
private _isMissingTexture() {
const parsedPath = path.parse(this._material.path);
const isMissingTexture = parsedPath.base === 'debug.png';
return isMissingTexture;
}
public hasWarning(): boolean {
return this._isMissingTexture();
}
protected buildChildHTML(): string {
return `<img id="${this._imageId}" class="texture-preview" src="${this._material.path}" width="75%" loading="lazy"></img>`;
}
public registerEvents(): void {
super.registerEvents();
const element = document.getElementById(this._imageId) as HTMLLinkElement;
if (element) {
if (!this._isMissingTexture()) {
element.addEventListener('mouseover', () => {
element.classList.add('texture-hover');
});
element.addEventListener('mouseleave', () => {
element.classList.remove('texture-hover');
});
element.addEventListener('click', () => {
FileUtil.openDir(this._material.path);
});
} else {
element.classList.add('texture-preview-missing');
}
}
}
}
export class SolidMaterialUIElement extends MaterialUIElement {
private _material: SolidMaterial;
private _colourId: string;
public constructor(materialName: string, appContext: AppContext, material: SolidMaterial) {
super(materialName, appContext);
this._material = material;
this._colourId = getRandomID();
if (material.canBeTextured) {
super.addAction('Switch to texture', () => {
this._appContext.onMaterialTypeSwitched(materialName);
});
}
}
protected buildChildHTML(): string {
return `<input class="colour-swatch" type="color" id="${this._colourId}" value="${RGBAUtil.toHexString(this._material.colour)}">`;
}
public registerEvents(): void {
super.registerEvents();
const colourElement = document.getElementById(this._colourId) as (HTMLInputElement | null);
if (colourElement !== null) {
colourElement.addEventListener('change', () => {
const newColour = RGBAUtil.fromHexString(colourElement.value);
this._appContext.onMaterialColourChanged(this._materialName, newColour);
});
colourElement.addEventListener('mouseenter', () => {
colourElement.classList.add('colour-swatch-hover');
});
colourElement.addEventListener('mouseleave', () => {
colourElement.classList.remove('colour-swatch-hover');
});
}
}
}

View File

@ -11,7 +11,7 @@ interface IUIOutputElement {
export class UITreeBuilder implements IUIOutputElement {
private _rootLabel: string;
private _children: Array<{ text: string, warning: boolean } | UITreeBuilder>;
private _children: Array<{ html: string, warning: boolean } | UITreeBuilder>;
private _postBuildDelegates: Array<() => void>;
private _warning: boolean;
@ -50,7 +50,7 @@ export class UITreeBuilder implements IUIOutputElement {
return false;
}
public addChild(child: { text: string, warning: boolean } | UITreeBuilder, postBuildDelegate?: () => void) {
public addChild(child: { html: string, warning: boolean } | UITreeBuilder, postBuildDelegate?: () => void) {
this._children.push(child);
if (postBuildDelegate !== undefined) {
this._postBuildDelegates.push(postBuildDelegate);
@ -73,7 +73,7 @@ export class UITreeBuilder implements IUIOutputElement {
if (child instanceof UITreeBuilder) {
childrenHTML += child.buildHTML();
} else {
childrenHTML += child.warning ? `<p style="margin:0px; color:orange;">${child.text}</p>` : child.text;
childrenHTML += child.warning ? `<p style="margin:0px; color:orange;">${child.html}</p>` : child.html;
}
childrenHTML += '<li>';
});

View File

@ -660,14 +660,40 @@ a:hover {
}
.colour-swatch {
border: none;
border: 1px solid #8C8C8C80;
border-radius: 5px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: none;
width: 75%;
margin: 0px;
padding: 0px;
}
.colour-swatch-hover {
border-color: var(--text-standard) !important;
}
.colour-swatch::-webkit-color-swatch-wrapper {
border: none;
margin: 0px;
padding: 0px;
}
.colour-swatch::-webkit-color-swatch {
border: none;
border-radius: 15px;
margin: 0px;
padding: 0px;
border-radius: 5px;
}
.texture-preview {
border: 1px solid #8C8C8C80;
border-radius: 5px;
}
.texture-preview-missing {
border-color: orange !important;
}
.texture-hover {
border-color: var(--text-standard) !important;
}