mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-12-11 20:15:30 +01:00
Refactored material ui elements
This commit is contained in:
parent
47f0042d9e
commit
a34734265e
@ -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
157
src/ui/elements/material.ts
Normal 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');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>';
|
||||
});
|
||||
|
||||
30
styles.css
30
styles.css
@ -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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user