forked from mirror/ObjToSchematic
Merge pull request #116 from LucasDower/0.8-web-ui
Merge 0.8-web-ui into 0.8-web
This commit is contained in:
commit
af9dcd6ce2
@ -9,6 +9,9 @@ import { MaterialMapManager } from './material-map';
|
||||
import { MaterialType } from './mesh';
|
||||
import { MeshType, Renderer } from './renderer';
|
||||
import { AppConsole, TMessage } from './ui/console';
|
||||
import { ButtonElement } from './ui/elements/button';
|
||||
import { CheckboxElement } from './ui/elements/checkbox';
|
||||
import { PlaceholderElement } from './ui/elements/placeholder_element';
|
||||
import { SolidMaterialElement } from './ui/elements/solid_material_element';
|
||||
import { TexturedMaterialElement } from './ui/elements/textured_material_element';
|
||||
import { UI } from './ui/layout';
|
||||
@ -48,6 +51,7 @@ export class AppContext {
|
||||
this._ui.build();
|
||||
this._ui.registerEvents();
|
||||
this._ui.disable(EAction.Materials);
|
||||
this._updateMaterialsAction();
|
||||
|
||||
this._workerController = new WorkerController();
|
||||
this._workerController.addJob({ id: 'init', payload: { action: 'Init', params: {} } });
|
||||
@ -229,29 +233,37 @@ export class AppContext {
|
||||
this._ui.layoutDull['materials'].elements = {};
|
||||
this._ui.layoutDull['materials'].elementsOrder = [];
|
||||
|
||||
this._materialManager.materials.forEach((material, materialName) => {
|
||||
if (material.type === MaterialType.solid) {
|
||||
this._ui.layoutDull['materials'].elements[`mat_${materialName}`] = new SolidMaterialElement(materialName, material)
|
||||
.setLabel(materialName)
|
||||
.onChangeTypeDelegate(() => {
|
||||
this._materialManager.changeMaterialType(materialName, MaterialType.textured);
|
||||
this._updateMaterialsAction();
|
||||
});
|
||||
} else {
|
||||
this._ui.layoutDull['materials'].elements[`mat_${materialName}`] = new TexturedMaterialElement(materialName, material)
|
||||
.setLabel(materialName)
|
||||
.onChangeTypeDelegate(() => {
|
||||
this._materialManager.changeMaterialType(materialName, MaterialType.solid);
|
||||
this._updateMaterialsAction();
|
||||
})
|
||||
.onChangeTransparencyTypeDelegate((newTransparency) => {
|
||||
this._materialManager.changeTransparencyType(materialName, newTransparency);
|
||||
this._updateMaterialsAction();
|
||||
});
|
||||
}
|
||||
if (this._materialManager.materials.size == 0) {
|
||||
this._ui.layoutDull['materials'].elements[`placeholder_element`] = new PlaceholderElement('No materials loaded');
|
||||
this._ui.layoutDull['materials'].elementsOrder.push(`placeholder_element`);
|
||||
} else {
|
||||
this._materialManager.materials.forEach((material, materialName) => {
|
||||
if (material.type === MaterialType.solid) {
|
||||
this._ui.layoutDull['materials'].elements[`mat_${materialName}`] = new SolidMaterialElement(materialName, material)
|
||||
.setLabel(materialName)
|
||||
.onChangeTypeDelegate(() => {
|
||||
this._materialManager.changeMaterialType(materialName, MaterialType.textured);
|
||||
this._updateMaterialsAction();
|
||||
});
|
||||
} else {
|
||||
this._ui.layoutDull['materials'].elements[`mat_${materialName}`] = new TexturedMaterialElement(materialName, material)
|
||||
.setLabel(materialName)
|
||||
.onChangeTypeDelegate(() => {
|
||||
console.log('on change type');
|
||||
this._materialManager.changeMaterialType(materialName, MaterialType.solid);
|
||||
this._updateMaterialsAction();
|
||||
})
|
||||
.onChangeTransparencyTypeDelegate((newTransparency) => {
|
||||
console.log('on change trans');
|
||||
this._materialManager.changeTransparencyType(materialName, newTransparency);
|
||||
this._updateMaterialsAction();
|
||||
});
|
||||
}
|
||||
|
||||
this._ui.layoutDull['materials'].elementsOrder.push(`mat_${materialName}`);
|
||||
});
|
||||
}
|
||||
|
||||
this._ui.layoutDull['materials'].elementsOrder.push(`mat_${materialName}`);
|
||||
});
|
||||
this._ui.refreshSubcomponents(this._ui.layoutDull['materials']);
|
||||
}
|
||||
|
||||
|
@ -10,17 +10,10 @@ export class AppConfig {
|
||||
|
||||
public readonly RELEASE_MODE = true;
|
||||
public readonly MAJOR_VERSION = 0;
|
||||
public readonly MINOR_VERSION = 7;
|
||||
public readonly HOTFIX_VERSION = 12;
|
||||
public readonly MINOR_VERSION = 8;
|
||||
public readonly HOTFIX_VERSION = 0;
|
||||
public readonly VERSION_TYPE: 'd' | 'a' | 'r' = 'r'; // dev, alpha, or release build
|
||||
public readonly MINECRAFT_VERSION = '1.19.3';
|
||||
public readonly CHANGELOG = [
|
||||
'+ Added customisable block palettes.',
|
||||
'* Added a console log panel for persistent message logging.',
|
||||
'+ Added support for importing/exporting block palettes.',
|
||||
'* Improved UI for checkbox and material-type components.',
|
||||
'- Removed the output component from action groups.',
|
||||
];
|
||||
|
||||
public readonly VOXEL_BUFFER_CHUNK_SIZE = 5_000;
|
||||
public readonly AMBIENT_OCCLUSION_OVERRIDE_CORNER = true;
|
||||
|
@ -1,18 +1,26 @@
|
||||
import { getRandomID } from '../../util';
|
||||
import { UIUtil } from '../../util/ui_util';
|
||||
|
||||
export interface IInterfaceItem {
|
||||
generateHTML: () => string;
|
||||
registerEvents: () => void;
|
||||
finalise: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base UI class from which user interactable DOM elements are built from.
|
||||
* Each `BaseUIElement` can be enabled/disabled.
|
||||
*/
|
||||
export abstract class BaseUIElement<T> {
|
||||
export abstract class BaseUIElement<T> implements IInterfaceItem {
|
||||
private _id: string;
|
||||
private _isEnabled: boolean;
|
||||
private _isHovered: boolean;
|
||||
private _obeyGroupEnables: boolean;
|
||||
|
||||
public constructor() {
|
||||
this._id = getRandomID();
|
||||
this._isEnabled = true;
|
||||
this._isHovered = false;
|
||||
this._obeyGroupEnables = true;
|
||||
}
|
||||
|
||||
@ -31,6 +39,10 @@ export abstract class BaseUIElement<T> {
|
||||
return this._isEnabled;
|
||||
}
|
||||
|
||||
public get disabled() {
|
||||
return !this._isEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this UI element is interactable.
|
||||
*/
|
||||
@ -42,6 +54,14 @@ export abstract class BaseUIElement<T> {
|
||||
this._onEnabledChanged();
|
||||
}
|
||||
|
||||
protected _setHovered(isHovered: boolean) {
|
||||
this._isHovered = isHovered;
|
||||
}
|
||||
|
||||
public get hovered() {
|
||||
return this._isHovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not this element should be enabled when the group
|
||||
* is it apart of becomes enabled. This is useful if an element should
|
||||
@ -69,6 +89,7 @@ export abstract class BaseUIElement<T> {
|
||||
|
||||
public finalise(): void {
|
||||
this._onEnabledChanged();
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,4 +112,10 @@ export abstract class BaseUIElement<T> {
|
||||
* A delegate that is called when the enabled status is changed.
|
||||
*/
|
||||
protected abstract _onEnabledChanged(): void;
|
||||
|
||||
/**
|
||||
* Called after _onEnabledChanged() and _onValueChanged()
|
||||
*/
|
||||
protected _updateStyles(): void {
|
||||
}
|
||||
}
|
||||
|
@ -71,27 +71,39 @@ export class ButtonElement extends BaseUIElement<HTMLDivElement> {
|
||||
|
||||
public override generateHTML() {
|
||||
return `
|
||||
<div class="button" id="${this._getId()}">
|
||||
<div class="button-label">${this._label}</div>
|
||||
<div class="button-progress" id="${this._getProgressBarId()}"></div>
|
||||
<div class="container-button">
|
||||
<div class="struct-prop button" id="${this._getId()}">
|
||||
<div class="button-label">${this._label}</div>
|
||||
<div class="button-progress" id="${this._getProgressBarId()}"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._getElement().addEventListener('click', () => {
|
||||
if (this.getEnabled()) {
|
||||
if (this.enabled) {
|
||||
this._onClick?.();
|
||||
}
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('mouseenter', () => {
|
||||
this._setHovered(true);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('mouseleave', () => {
|
||||
this._setHovered(false);
|
||||
this._updateStyles();
|
||||
});
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
if (this.getEnabled()) {
|
||||
this._getElement().classList.remove('button-disabled');
|
||||
} else {
|
||||
this._getElement().classList.add('button-disabled');
|
||||
}
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,4 +112,12 @@ export class ButtonElement extends BaseUIElement<HTMLDivElement> {
|
||||
private _getProgressBarId() {
|
||||
return this._getId() + '-progress';
|
||||
}
|
||||
|
||||
protected _updateStyles(): void {
|
||||
UIUtil.updateStyles(this._getElement(), {
|
||||
isActive: true,
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this.hovered,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,11 @@ import { ConfigUIElement } from './config_element';
|
||||
export class CheckboxElement extends ConfigUIElement<boolean, HTMLSelectElement> {
|
||||
private _labelChecked: string;
|
||||
private _labelUnchecked: string;
|
||||
private _hovering: boolean;
|
||||
|
||||
public constructor() {
|
||||
super(false);
|
||||
this._labelChecked = 'On';
|
||||
this._labelUnchecked = 'Off';
|
||||
this._hovering = false;
|
||||
}
|
||||
|
||||
public setCheckedText(label: string) {
|
||||
@ -59,15 +57,14 @@ export class CheckboxElement extends ConfigUIElement<boolean, HTMLSelectElement>
|
||||
}
|
||||
|
||||
private _onMouseEnterLeave(isHovering: boolean) {
|
||||
this._hovering = isHovering;
|
||||
|
||||
this._setHovered(isHovering);
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
public override _generateInnerHTML(): string {
|
||||
return `
|
||||
<div class="checkbox" id="${this._getId()}">
|
||||
<svg id="${this._getPipId()}" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<div class="struct-prop container-checkbox" id="${this._getId()}">
|
||||
<svg id="${this._getPipId()}" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M5 12l5 5l10 -10" />
|
||||
</svg>
|
||||
@ -86,7 +83,6 @@ export class CheckboxElement extends ConfigUIElement<boolean, HTMLSelectElement>
|
||||
|
||||
protected override _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
@ -106,36 +102,33 @@ export class CheckboxElement extends ConfigUIElement<boolean, HTMLSelectElement>
|
||||
this._setValue(false);
|
||||
}
|
||||
|
||||
private _updateStyles() {
|
||||
const checkboxElement = UIUtil.getElementById(this._getId());
|
||||
protected _updateStyles() {
|
||||
{
|
||||
const checkboxElement = UIUtil.getElementById(this._getId());
|
||||
UIUtil.updateStyles(checkboxElement, {
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this.hovered,
|
||||
isActive: false,
|
||||
});
|
||||
}
|
||||
const checkboxPipElement = UIUtil.getElementById(this._getPipId());
|
||||
const checkboxTextElement = UIUtil.getElementById(this._getTextId());
|
||||
|
||||
checkboxElement.classList.remove('checkbox-disabled');
|
||||
checkboxElement.classList.remove('checkbox-hover');
|
||||
checkboxPipElement.classList.remove('checkbox-pip-disabled');
|
||||
checkboxPipElement.classList.remove('checkbox-pip-hover');
|
||||
checkboxTextElement.classList.remove('text-dark');
|
||||
checkboxTextElement.classList.remove('text-standard');
|
||||
checkboxTextElement.classList.remove('text-light');
|
||||
checkboxTextElement.classList.remove('checkbox-text-hover');
|
||||
|
||||
checkboxTextElement.innerHTML = this.getValue() ? this._labelChecked : this._labelUnchecked;
|
||||
checkboxPipElement.style.visibility = this.getValue() ? 'visible' : 'hidden';
|
||||
|
||||
if (this.enabled) {
|
||||
if (this._hovering) {
|
||||
checkboxElement.classList.add('checkbox-hover');
|
||||
checkboxPipElement.classList.add('checkbox-pip-hover');
|
||||
if (this.hovered) {
|
||||
checkboxTextElement.classList.add('text-light');
|
||||
checkboxTextElement.classList.add('checkbox-text-hover');
|
||||
} else if (this.getValue()) {
|
||||
checkboxTextElement.classList.add('text-standard');
|
||||
}
|
||||
} else {
|
||||
checkboxElement.classList.add('checkbox-disabled');
|
||||
checkboxTextElement.classList.add('text-dark');
|
||||
checkboxPipElement.classList.add('checkbox-pip-disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
src/ui/elements/colour_element.ts
Normal file
29
src/ui/elements/colour_element.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { RGBA, RGBAUtil } from '../../colour';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
|
||||
export class ColourElement extends ConfigUIElement<RGBA, HTMLInputElement> {
|
||||
public constructor(colour: RGBA) {
|
||||
super(colour);
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML(): string {
|
||||
return `<input class="colour-swatch" type="color" id="${this._getId()}" value="${RGBAUtil.toHexString(this.getValue())}">`;
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._getElement().addEventListener('change', () => {
|
||||
const newColour = RGBAUtil.fromHexString(this._getElement().value);
|
||||
this._setValue(newColour);
|
||||
});
|
||||
}
|
||||
|
||||
protected _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
|
||||
if (this.enabled) {
|
||||
this._getElement().classList.add('enabled');
|
||||
} else {
|
||||
this._getElement().classList.remove('enabled');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
import { ASSERT } from '../../util/error_util';
|
||||
import { UIUtil } from '../../util/ui_util';
|
||||
import { AppIcons } from '../icons';
|
||||
import { HTMLBuilder } from '../misc';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
|
||||
export type ComboBoxItem<T> = {
|
||||
@ -9,12 +11,10 @@ export type ComboBoxItem<T> = {
|
||||
|
||||
export class ComboBoxElement<T> extends ConfigUIElement<T, HTMLSelectElement> {
|
||||
private _items: ComboBoxItem<T>[];
|
||||
private _small: boolean;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
this._items = [];
|
||||
this._small = false;
|
||||
}
|
||||
|
||||
public addItems(items: ComboBoxItem<T>[]) {
|
||||
@ -30,62 +30,83 @@ export class ComboBoxElement<T> extends ConfigUIElement<T, HTMLSelectElement> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public setSmall() {
|
||||
this._small = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._getElement().addEventListener('mouseenter', () => {
|
||||
this._setHovered(true);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('mouseleave', () => {
|
||||
this._setHovered(false);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('change', (e: Event) => {
|
||||
const selectedValue = this._items[this._getElement().selectedIndex].payload;
|
||||
this._setValue(selectedValue);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
public override setDefaultValue(value: T): this {
|
||||
super.setDefaultValue(value);
|
||||
|
||||
const element = this._getElement();
|
||||
|
||||
const newSelectedIndex = this._items.findIndex((item) => item.payload === value);
|
||||
ASSERT(newSelectedIndex !== -1, 'Invalid selected index');
|
||||
element.selectedIndex = newSelectedIndex;
|
||||
|
||||
return this;
|
||||
}
|
||||
*/
|
||||
|
||||
public override _generateInnerHTML() {
|
||||
//ASSERT(this._items.length > 0);
|
||||
const builder = new HTMLBuilder();
|
||||
|
||||
let itemsHTML = '';
|
||||
builder.add('<div style="position: relative; width: 100%;">');
|
||||
builder.add(`<select class="struct-prop" name="${this._getId()}" id="${this._getId()}">`);
|
||||
for (const item of this._items) {
|
||||
itemsHTML += `<option value="${item.payload}" title="${item.tooltip || ''}">${item.displayText}</option>`;
|
||||
builder.add(`<option value="${item.payload}" title="${item.tooltip || ''}">${item.displayText}</option>`);
|
||||
}
|
||||
builder.add('</select>');
|
||||
|
||||
return `
|
||||
<select class="${this._small ? 'height-small' : 'height-normal'}" name="${this._getId()}" id="${this._getId()}">
|
||||
${itemsHTML}
|
||||
</select>
|
||||
`;
|
||||
builder.add(`<div id="${this._getId()}-arrow" class="checkbox-arrow">`);
|
||||
builder.add(AppIcons.ARROW_DOWN);
|
||||
builder.add(`</div>`);
|
||||
builder.add('</div>');
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
protected _onValueChanged(): void {
|
||||
super._onValueChanged();
|
||||
|
||||
console.log('combo changed');
|
||||
}
|
||||
|
||||
protected _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
this._getElement().disabled = !this.getEnabled();
|
||||
this._getElement().disabled = this.disabled;
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
// TODO: Subproperty combo boxes are not updating values when changed!!!
|
||||
protected override _updateStyles(): void {
|
||||
UIUtil.updateStyles(this._getElement(), {
|
||||
isHovered: this.hovered,
|
||||
isEnabled: this.enabled,
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
const arrowElement = UIUtil.getElementById(this._getId() + '-arrow');
|
||||
arrowElement.classList.remove('text-dark');
|
||||
arrowElement.classList.remove('text-standard');
|
||||
arrowElement.classList.remove('text-light');
|
||||
if (this.enabled) {
|
||||
if (this.hovered) {
|
||||
arrowElement.classList.add('text-light');
|
||||
} else {
|
||||
arrowElement.classList.add('text-standard');
|
||||
}
|
||||
} else {
|
||||
arrowElement.classList.add('text-dark');
|
||||
}
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
super.finalise();
|
||||
|
||||
const selectedIndex = this._items.findIndex((item) => item.payload === this.getValue());
|
||||
const element = this._getElement();
|
||||
|
||||
//ASSERT(selectedIndex !== -1, 'Invalid selected index');
|
||||
element.selectedIndex = selectedIndex;
|
||||
|
||||
this._updateStyles();
|
||||
}
|
||||
}
|
||||
|
@ -72,10 +72,12 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
public override finalise(): void {
|
||||
super.finalise();
|
||||
|
||||
/*
|
||||
this._onValueChanged();
|
||||
this._onValueChangedListeners.forEach((listener) => {
|
||||
listener(this._value!);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
public override generateHTML() {
|
||||
@ -99,10 +101,12 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
protected override _onEnabledChanged() {
|
||||
const label = document.getElementById(this._getLabelId()) as (HTMLDivElement | null);
|
||||
|
||||
if (this.getEnabled()) {
|
||||
label?.classList.remove('text-disabled');
|
||||
if (this.enabled) {
|
||||
label?.classList.remove('text-dark');
|
||||
label?.classList.add('text-standard');
|
||||
} else {
|
||||
label?.classList.add('text-disabled');
|
||||
label?.classList.add('text-dark');
|
||||
label?.classList.remove('text-standard');
|
||||
}
|
||||
|
||||
this._onEnabledChangedListeners.forEach((listener) => {
|
||||
@ -125,7 +129,8 @@ export abstract class ConfigUIElement<T, F> extends BaseUIElement<F> {
|
||||
/**
|
||||
* A delegate that is called when the value of this element changes.
|
||||
*/
|
||||
protected abstract _onValueChanged(): void;
|
||||
protected _onValueChanged(): void {
|
||||
}
|
||||
|
||||
protected _getLabelId() {
|
||||
return this._getId() + '_label';
|
||||
|
@ -4,29 +4,17 @@ import { ASSERT } from '../../util/error_util';
|
||||
import { UIUtil } from '../../util/ui_util';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
|
||||
export class FileInputElement extends ConfigUIElement<Promise<string>, HTMLDivElement> {
|
||||
private _fileExtensions: string[];
|
||||
export class ObjFileInputElement extends ConfigUIElement<Promise<string>, HTMLDivElement> {
|
||||
private _loadedFilePath: string;
|
||||
private _hovering: boolean;
|
||||
|
||||
public constructor() {
|
||||
super(Promise.resolve(''));
|
||||
this._fileExtensions = [];
|
||||
this._loadedFilePath = '';
|
||||
this._hovering = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the allow list of file extensions that can be uploaded.
|
||||
*/
|
||||
public setFileExtensions(extensions: string[]) {
|
||||
this._fileExtensions = extensions;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML() {
|
||||
return `
|
||||
<div class="input-file" id="${this._getId()}">
|
||||
<div class="input-file struct-prop" id="${this._getId()}">
|
||||
<input type="file" accept=".obj" style="display: none;" id="${this._getId()}-input">
|
||||
${this._loadedFilePath}
|
||||
</div>
|
||||
@ -35,13 +23,13 @@ export class FileInputElement extends ConfigUIElement<Promise<string>, HTMLDivEl
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._getElement().addEventListener('mouseenter', () => {
|
||||
this._hovering = true;
|
||||
this._updateStyle();
|
||||
this._setHovered(true);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('mouseleave', () => {
|
||||
this._hovering = false;
|
||||
this._updateStyle();
|
||||
this._setHovered(false);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
const inputElement = UIUtil.getElementById(this._getId() + '-input') as HTMLInputElement;
|
||||
@ -57,43 +45,24 @@ export class FileInputElement extends ConfigUIElement<Promise<string>, HTMLDivEl
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('click', () => {
|
||||
if (!this.getEnabled()) {
|
||||
return;
|
||||
if (this.enabled) {
|
||||
inputElement.click();
|
||||
}
|
||||
|
||||
inputElement.click();
|
||||
});
|
||||
|
||||
this._getElement().addEventListener('mousemove', () => {
|
||||
this._updateStyle();
|
||||
});
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
super._onEnabledChanged();
|
||||
|
||||
if (this.getEnabled()) {
|
||||
this._getElement().classList.remove('input-file-disabled');
|
||||
} else {
|
||||
this._getElement().classList.add('input-file-disabled');
|
||||
}
|
||||
|
||||
protected _onValueChanged(): void {
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
protected override _updateStyles() {
|
||||
const parsedPath = path.parse(this._loadedFilePath);
|
||||
this._getElement().innerHTML = parsedPath.name + parsedPath.ext;
|
||||
}
|
||||
|
||||
private _updateStyle() {
|
||||
this._getElement().classList.remove('input-file-disabled');
|
||||
this._getElement().classList.remove('input-file-hover');
|
||||
|
||||
if (this.getEnabled()) {
|
||||
if (this._hovering) {
|
||||
this._getElement().classList.add('input-file-hover');
|
||||
}
|
||||
} else {
|
||||
this._getElement().classList.add('input-file-disabled');
|
||||
}
|
||||
UIUtil.updateStyles(this._getElement(), {
|
||||
isHovered: this.hovered,
|
||||
isEnabled: this.enabled,
|
||||
isActive: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { ConfigUIElement } from './config_element';
|
||||
export abstract class FullConfigUIElement<T, F> extends ConfigUIElement<T, F> {
|
||||
public override generateHTML() {
|
||||
return `
|
||||
<div class="property full-width-property" style="flex-direction: column; align-items: start;">
|
||||
<div class="property" style="flex-direction: column; align-items: start;">
|
||||
<div class="prop-key-container" id="${this._getLabelId()}">
|
||||
${this._label}
|
||||
</div>
|
||||
|
@ -43,38 +43,27 @@ export class HeaderUIElement extends BaseUIElement<HTMLDivElement> {
|
||||
|
||||
public override generateHTML(): string {
|
||||
return `
|
||||
<div class="property">
|
||||
<div class="col-container header-cols">
|
||||
<div class="col-container">
|
||||
<div class="col-item">
|
||||
<img class="logo" alt="Logo" src="${IMAGE_LOGO}">
|
||||
</div>
|
||||
<div class="col-item">
|
||||
<div class="row-container">
|
||||
<div class="row-item title">
|
||||
ObjToSchematic
|
||||
</div>
|
||||
<div class="row-item subtitle">
|
||||
v${AppConfig.Get.MAJOR_VERSION}.${AppConfig.Get.MINOR_VERSION}.${AppConfig.Get.HOTFIX_VERSION}${AppConfig.Get.VERSION_TYPE} • Minecraft ${AppConfig.Get.MINECRAFT_VERSION}
|
||||
</div>
|
||||
<div class="col-container header-cols">
|
||||
<div class="col-container">
|
||||
<div class="col-item">
|
||||
<img class="logo" alt="Logo" src="${IMAGE_LOGO}">
|
||||
</div>
|
||||
<div class="col-item">
|
||||
<div class="row-container">
|
||||
<div class="row-item title">
|
||||
ObjToSchematic
|
||||
</div>
|
||||
<div class="row-item subtitle">
|
||||
v${AppConfig.Get.MAJOR_VERSION}.${AppConfig.Get.MINOR_VERSION}.${AppConfig.Get.HOTFIX_VERSION}${AppConfig.Get.VERSION_TYPE} • Minecraft ${AppConfig.Get.MINECRAFT_VERSION}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-container">
|
||||
<div class="col-item">
|
||||
${this._githubButton.generateHTML()}
|
||||
</div>
|
||||
<div class="col-item">
|
||||
${this._bugButton.generateHTML()}
|
||||
</div>
|
||||
<div class="col-item">
|
||||
${this._discordButton.generateHTML()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property changelog">
|
||||
${AppConfig.Get.CHANGELOG.join('<br>')}
|
||||
<div class="toolbar-group">
|
||||
${this._githubButton.generateHTML()}
|
||||
${this._bugButton.generateHTML()}
|
||||
${this._discordButton.generateHTML()}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -86,6 +75,9 @@ export class HeaderUIElement extends BaseUIElement<HTMLDivElement> {
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
this._githubButton.finalise();
|
||||
this._bugButton.finalise();
|
||||
this._discordButton.finalise();
|
||||
// const updateElement = UIUtil.getElementById('update-checker') as HTMLDivElement;
|
||||
// updateElement.style.animation = 'pulse-opacity 1.5s infinite';
|
||||
// updateElement.innerHTML = '<i style="animation: pulse-opacity 1.5s infinite;">Checking for updates...</i>';
|
||||
|
@ -15,7 +15,6 @@ export class ImageElement extends ConfigUIElement<Promise<TImageRawWrap>, HTMLIm
|
||||
super(Promise.resolve(param ?? { raw: '', filetype: 'png' }));
|
||||
|
||||
this._switchElement = new ToolbarItemElement({ id: 'sw', iconSVG: AppIcons.UPLOAD })
|
||||
.setSmall()
|
||||
.setLabel('Choose')
|
||||
.onClick(() => {
|
||||
const inputElement = UIUtil.getElementById(this._getId() + '-input') as HTMLInputElement;
|
||||
@ -30,15 +29,17 @@ export class ImageElement extends ConfigUIElement<Promise<TImageRawWrap>, HTMLIm
|
||||
<div class="row-container">
|
||||
<div class="row-item">
|
||||
<img id="${this._imageId}" alt="Texture Preview" class="texture-preview" loading="lazy"></img>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="col-container">
|
||||
<div class="col-item">
|
||||
<input type="file" accept="images/png" style="display: none;" id="${this._getId()}-input">
|
||||
${this._switchElement.generateHTML()}
|
||||
<div id="${this._imageId}-placeholder" class="texture-preview-placeholder">
|
||||
<div class="row-container" style="align-items: center;">
|
||||
<div class="row-item">${AppIcons.IMAGE_MISSING}</div>
|
||||
<div class="row-item">No image loaded</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<input type="file" accept="images/png" style="display: none;" id="${this._getId()}-input">
|
||||
${this._switchElement.generateHTML()}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -70,10 +71,15 @@ export class ImageElement extends ConfigUIElement<Promise<TImageRawWrap>, HTMLIm
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
|
||||
this._switchElement.setEnabled(this.enabled);
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
const inputElement = UIUtil.getElementById(this._imageId) as HTMLImageElement;
|
||||
const placeholderElement = UIUtil.getElementById(this._imageId + '-placeholder');
|
||||
|
||||
this.getValue()
|
||||
.then((res) => {
|
||||
if (res.raw === '') {
|
||||
@ -82,11 +88,13 @@ export class ImageElement extends ConfigUIElement<Promise<TImageRawWrap>, HTMLIm
|
||||
this._switchElement.setActive(false);
|
||||
inputElement.src = res.raw;
|
||||
inputElement.style.display = 'unset';
|
||||
placeholderElement.style.display = 'none';
|
||||
})
|
||||
.catch((err) => {
|
||||
this._switchElement.setActive(true);
|
||||
inputElement.src = '';
|
||||
inputElement.style.display = 'none';
|
||||
placeholderElement.style.display = 'flex';
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,5 +102,6 @@ export class ImageElement extends ConfigUIElement<Promise<TImageRawWrap>, HTMLIm
|
||||
super.finalise();
|
||||
|
||||
this._onValueChanged();
|
||||
this._onEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
@ -4,48 +4,58 @@ import { ConfigUIElement } from './config_element';
|
||||
import { ToolbarItemElement } from './toolbar_item';
|
||||
|
||||
export class MaterialTypeElement extends ConfigUIElement<MaterialType, HTMLDivElement> {
|
||||
private _switchElement: ToolbarItemElement;
|
||||
private _solidButton: ToolbarItemElement;
|
||||
private _texturedButton: ToolbarItemElement;
|
||||
private _material: SolidMaterial | TexturedMaterial;
|
||||
|
||||
public constructor(material: SolidMaterial | TexturedMaterial) {
|
||||
super(material.type);
|
||||
this._material = material;
|
||||
this._switchElement = new ToolbarItemElement({ id: 'sw2', iconSVG: AppIcons.SWITCH })
|
||||
.setSmall()
|
||||
.setLabel('Switch')
|
||||
|
||||
this._solidButton = new ToolbarItemElement({ id: 'sw1', iconSVG: AppIcons.COLOUR_SWATCH })
|
||||
.setLabel('Solid')
|
||||
.onClick(() => {
|
||||
this._onClickChangeTypeDelegate?.();
|
||||
if (this._material.type === MaterialType.textured) {
|
||||
this._onClickChangeTypeDelegate?.();
|
||||
}
|
||||
});
|
||||
|
||||
this._texturedButton = new ToolbarItemElement({ id: 'sw2', iconSVG: AppIcons.IMAGE })
|
||||
.setLabel('Textured')
|
||||
.onClick(() => {
|
||||
if (this._material.type === MaterialType.solid) {
|
||||
this._onClickChangeTypeDelegate?.();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override _generateInnerHTML() {
|
||||
const material = this.getValue();
|
||||
|
||||
return `
|
||||
<div class="row-container">
|
||||
<div class="row-item">
|
||||
${material === MaterialType.solid ? 'Solid' : 'Textured'}
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="col-container">
|
||||
<div class="col-item">
|
||||
${this._switchElement.generateHTML()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-group" style="width: 100%;">
|
||||
${this._solidButton.generateHTML()}
|
||||
${this._texturedButton.generateHTML()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
this._switchElement.setActive(this._material.type === MaterialType.solid && this._material.canBeTextured);
|
||||
this._solidButton.finalise();
|
||||
this._texturedButton.finalise();
|
||||
|
||||
this._solidButton.setActive(this._material.type === MaterialType.solid);
|
||||
this._texturedButton.setActive(this._material.type === MaterialType.textured);
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._switchElement.registerEvents();
|
||||
this._solidButton.registerEvents();
|
||||
this._texturedButton.registerEvents();
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
|
||||
this._solidButton.setEnabled(this.enabled);
|
||||
this._texturedButton.setEnabled(this.enabled);
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
|
@ -8,10 +8,11 @@ import { AppConsole } from '../console';
|
||||
import { AppIcons } from '../icons';
|
||||
import { ButtonElement } from './button';
|
||||
import { CheckboxElement } from './checkbox';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
import { FullConfigUIElement } from './full_config_element';
|
||||
import { ToolbarItemElement } from './toolbar_item';
|
||||
|
||||
export class PaletteElement extends FullConfigUIElement<Palette, HTMLDivElement> {
|
||||
export class PaletteElement extends ConfigUIElement<Palette, HTMLDivElement> {
|
||||
private _checkboxes: { block: string, element: CheckboxElement }[];
|
||||
private _palette: Palette;
|
||||
private _selectAll: ToolbarItemElement;
|
||||
@ -133,15 +134,9 @@ export class PaletteElement extends FullConfigUIElement<Palette, HTMLDivElement>
|
||||
checkboxesHTML += '</div>';
|
||||
});
|
||||
|
||||
/*
|
||||
<select>
|
||||
<option value="All">All</option>
|
||||
</select>
|
||||
*/
|
||||
|
||||
return `
|
||||
<div class="row-container" style="width: 100%; gap: 5px;">
|
||||
<input type="text" style="width: 100%;" placeholder="Search..." id="${this._getId() + '-search'}"></input>
|
||||
<input class="struct-prop" type="text" style="width: 100%; text-align: start;" placeholder="Search..." id="${this._getId() + '-search'}"></input>
|
||||
<div class="col-container header-cols" style="padding-top: 0px;">
|
||||
<div class="col-container">
|
||||
<div class="col-item">
|
||||
@ -186,6 +181,7 @@ export class PaletteElement extends FullConfigUIElement<Palette, HTMLDivElement>
|
||||
this._exportTo.setEnabled(this.enabled);
|
||||
|
||||
this._onCountSelectedChanged();
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
@ -193,6 +189,14 @@ export class PaletteElement extends FullConfigUIElement<Palette, HTMLDivElement>
|
||||
searchElement.addEventListener('keyup', () => {
|
||||
this._onSearchBoxChanged(searchElement.value);
|
||||
});
|
||||
searchElement.addEventListener('mouseenter', () => {
|
||||
this._setHovered(true);
|
||||
this._updateStyles();
|
||||
});
|
||||
searchElement.addEventListener('mouseleave', () => {
|
||||
this._setHovered(false);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
this._checkboxes.forEach((checkbox) => {
|
||||
checkbox.element.registerEvents();
|
||||
@ -218,7 +222,14 @@ export class PaletteElement extends FullConfigUIElement<Palette, HTMLDivElement>
|
||||
checkbox.element.finalise();
|
||||
});
|
||||
|
||||
this._selectAll.finalise();
|
||||
this._deselectAll.finalise();
|
||||
this._importFrom.finalise();
|
||||
this._exportTo.finalise();
|
||||
|
||||
this._onCountSelectedChanged();
|
||||
|
||||
this._updateStyles();
|
||||
//this._selectAll.finalise();
|
||||
//this._deselectAll.finalise();
|
||||
//this._importFrom.finalise();
|
||||
@ -235,4 +246,12 @@ export class PaletteElement extends FullConfigUIElement<Palette, HTMLDivElement>
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override _updateStyles(): void {
|
||||
UIUtil.updateStyles(UIUtil.getElementById(this._getId() + '-search'), {
|
||||
isActive: false,
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this.hovered,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
30
src/ui/elements/placeholder_element.ts
Normal file
30
src/ui/elements/placeholder_element.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { ConfigUIElement } from './config_element';
|
||||
|
||||
export class PlaceholderElement extends ConfigUIElement<undefined, HTMLDivElement> {
|
||||
private _placeholderText: string;
|
||||
|
||||
public constructor(placeholderText: string) {
|
||||
super(undefined);
|
||||
|
||||
this._placeholderText = placeholderText;
|
||||
}
|
||||
|
||||
public override generateHTML(): string {
|
||||
return `
|
||||
<div class="property" style="justify-content: center;">
|
||||
${this._generateInnerHTML()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML(): string {
|
||||
return `
|
||||
<div class="text-dark">
|
||||
${this._placeholderText}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
}
|
||||
}
|
@ -17,9 +17,8 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
private _decimals: number;
|
||||
private _step: number;
|
||||
private _dragging: boolean;
|
||||
private _hovering: boolean;
|
||||
private _internalValue: number;
|
||||
private _small: boolean;
|
||||
private _valueHovered: boolean;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
@ -29,8 +28,7 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
this._step = 0.1;
|
||||
this._internalValue = 0.5;
|
||||
this._dragging = false;
|
||||
this._hovering = false;
|
||||
this._small = false;
|
||||
this._valueHovered = false;
|
||||
}
|
||||
|
||||
public override setDefaultValue(value: number) {
|
||||
@ -39,11 +37,6 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public setSmall() {
|
||||
this._small = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum value the slider can be set to.
|
||||
*/
|
||||
@ -82,19 +75,13 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
const elementValue = UIUtil.getElementById(this._getSliderValueId()) as HTMLInputElement;
|
||||
|
||||
element.onmouseenter = () => {
|
||||
this._hovering = true;
|
||||
if (this.getEnabled()) {
|
||||
element.classList.add('new-slider-hover');
|
||||
elementBar.classList.add('new-slider-bar-hover');
|
||||
}
|
||||
this._setHovered(true);
|
||||
this._updateStyles();
|
||||
};
|
||||
|
||||
element.onmouseleave = () => {
|
||||
this._hovering = false;
|
||||
if (!this._dragging) {
|
||||
element.classList.remove('new-slider-hover');
|
||||
elementBar.classList.remove('new-slider-bar-hover');
|
||||
}
|
||||
this._setHovered(false);
|
||||
this._updateStyles();
|
||||
};
|
||||
|
||||
element.onmousedown = () => {
|
||||
@ -111,10 +98,6 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
if (this._dragging) {
|
||||
this._onDragSlider(e);
|
||||
}
|
||||
if (!this._hovering) {
|
||||
element.classList.remove('new-slider-hover');
|
||||
elementBar.classList.remove('new-slider-bar-hover');
|
||||
}
|
||||
this._dragging = false;
|
||||
});
|
||||
|
||||
@ -128,15 +111,25 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
elementValue.addEventListener('change', () => {
|
||||
this._onTypedValue();
|
||||
});
|
||||
|
||||
elementValue.addEventListener('mouseenter', () => {
|
||||
this._valueHovered = true;
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
elementValue.addEventListener('mouseleave', () => {
|
||||
this._valueHovered = false;
|
||||
this._updateStyles();
|
||||
});
|
||||
}
|
||||
|
||||
public override _generateInnerHTML() {
|
||||
const norm = (this._internalValue - this._min) / (this._max - this._min);
|
||||
|
||||
return `
|
||||
<input class="${this._small ? 'slider-height-small' : 'slider-height-normal'}" type="number" id="${this._getSliderValueId()}" min="${this._min}" max="${this._max}" step="${this._step}" value="${this.getValue().toFixed(this._decimals)}">
|
||||
<div class="new-slider ${this._small ? 'slider-bar-height-small' : 'slider-bar-height-normal'} " id="${this._getId()}" style="flex-grow: 1;">
|
||||
<div class="new-slider-bar" id="${this._getSliderBarId()}" style="width: ${norm * 100}%;">
|
||||
<input class="struct-prop comp-slider-value" type="number" id="${this._getSliderValueId()}" min="${this._min}" max="${this._max}" step="${this._step}" value="${this.getValue().toFixed(this._decimals)}">
|
||||
<div class="struct-prop comp-slider comp-slider-outer" id="${this._getId()}">
|
||||
<div class="struct-prop comp-slider comp-slider-inner" id="${this._getSliderBarId()}" style="width: ${norm * 100}%">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -145,19 +138,20 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
protected override _onEnabledChanged() {
|
||||
super._onEnabledChanged();
|
||||
|
||||
const element = this._getElement();
|
||||
const elementBar = UIUtil.getElementById(this._getSliderBarId());
|
||||
const elementValue = UIUtil.getElementById(this._getSliderValueId()) as HTMLInputElement;
|
||||
elementValue.disabled = this.disabled;
|
||||
|
||||
if (this.getEnabled()) {
|
||||
element.classList.remove('new-slider-disabled');
|
||||
elementBar.classList.remove('new-slider-bar-disabled');
|
||||
elementValue.disabled = false;
|
||||
const elementBar = UIUtil.getElementById(this._getSliderBarId());
|
||||
const elementSlider = UIUtil.getElementById(this._getId());
|
||||
if (this.enabled) {
|
||||
elementBar.classList.add('enabled');
|
||||
elementSlider.classList.add('enabled');
|
||||
} else {
|
||||
element.classList.add('new-slider-disabled');
|
||||
elementBar.classList.add('new-slider-bar-disabled');
|
||||
elementValue.disabled = true;
|
||||
elementBar.classList.remove('enabled');
|
||||
elementSlider.classList.remove('enabled');
|
||||
}
|
||||
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
@ -222,4 +216,27 @@ export class SliderElement extends ConfigUIElement<number, HTMLDivElement> {
|
||||
private _getSliderBarId() {
|
||||
return this._getId() + '-bar';
|
||||
}
|
||||
|
||||
protected override _updateStyles(): void {
|
||||
const elementValue = UIUtil.getElementById(this._getSliderValueId()) as HTMLInputElement;
|
||||
UIUtil.updateStyles(elementValue, {
|
||||
isHovered: this._valueHovered,
|
||||
isActive: false,
|
||||
isEnabled: this.enabled,
|
||||
});
|
||||
|
||||
const elementBar = UIUtil.getElementById(this._getSliderBarId()) as HTMLInputElement;
|
||||
UIUtil.updateStyles(elementBar, {
|
||||
isHovered: this.hovered,
|
||||
isActive: true,
|
||||
isEnabled: this.enabled,
|
||||
});
|
||||
|
||||
const elementSlider = UIUtil.getElementById(this._getId()) as HTMLInputElement;
|
||||
UIUtil.updateStyles(elementSlider, {
|
||||
isHovered: this.hovered,
|
||||
isActive: false,
|
||||
isEnabled: this.enabled,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +1,64 @@
|
||||
import { RGBAUtil } from '../../colour';
|
||||
import { MaterialType, SolidMaterial } from '../../mesh';
|
||||
import { getRandomID } from '../../util';
|
||||
import { UIUtil } from '../../util/ui_util';
|
||||
import { SolidMaterial } from '../../mesh';
|
||||
import { ColourElement } from './colour_element';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
import { MaterialTypeElement } from './material_type_element';
|
||||
import { SliderElement } from './slider';
|
||||
|
||||
export class SolidMaterialElement extends ConfigUIElement<SolidMaterial, HTMLDivElement> {
|
||||
private _materialName: string;
|
||||
private _colourId: string;
|
||||
private _typeElement: MaterialTypeElement;
|
||||
private _colourElement: ColourElement;
|
||||
private _alphaElement: SliderElement;
|
||||
|
||||
public constructor(materialName: string, material: SolidMaterial) {
|
||||
super(material);
|
||||
this._materialName = materialName;
|
||||
this._colourId = getRandomID();
|
||||
|
||||
this._typeElement = new MaterialTypeElement(material);
|
||||
this._typeElement = new MaterialTypeElement(material)
|
||||
.setLabel('Type');
|
||||
|
||||
this._colourElement = new ColourElement(material.colour)
|
||||
.setLabel('Colour');
|
||||
|
||||
this._alphaElement = new SliderElement()
|
||||
.setLabel('Alpha')
|
||||
.setMin(0.0)
|
||||
.setMax(1.0)
|
||||
.setDefaultValue(material.colour.a)
|
||||
.setDecimals(2)
|
||||
.setStep(0.01)
|
||||
.setSmall();
|
||||
.setStep(0.01);
|
||||
}
|
||||
|
||||
public override registerEvents(): void {
|
||||
this._typeElement.registerEvents();
|
||||
this._colourElement.registerEvents();
|
||||
this._alphaElement.registerEvents();
|
||||
|
||||
this._typeElement.onClickChangeTypeDelegate(() => {
|
||||
this._onChangeTypeDelegate?.();
|
||||
});
|
||||
|
||||
this._alphaElement.addValueChangedListener((newAlpha) => {
|
||||
this.getValue().colour.a = newAlpha;
|
||||
this._colourElement.addValueChangedListener((newColour) => {
|
||||
this.getValue().colour.r = newColour.r;
|
||||
this.getValue().colour.g = newColour.g;
|
||||
this.getValue().colour.b = newColour.b;
|
||||
});
|
||||
|
||||
const swatchElement = UIUtil.getElementById(this._colourId) as HTMLInputElement;
|
||||
swatchElement.addEventListener('change', () => {
|
||||
const material = this.getValue();
|
||||
material.colour = RGBAUtil.fromHexString(swatchElement.value);
|
||||
this._alphaElement.addValueChangedListener((newAlpha) => {
|
||||
this.getValue().colour.a = newAlpha;
|
||||
});
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
this._typeElement.finalise();
|
||||
this._colourElement.finalise();
|
||||
this._alphaElement.finalise();
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML(): string {
|
||||
const material = this.getValue();
|
||||
|
||||
const subproperties: string[] = [];
|
||||
const addSubproperty = (key: string, value: string) => {
|
||||
subproperties.push(`
|
||||
<div class="subproperty">
|
||||
<div class="subprop-key-container">
|
||||
${key}
|
||||
</div>
|
||||
<div class="subprop-value-container">
|
||||
${value}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
};
|
||||
|
||||
addSubproperty('Type', this._typeElement._generateInnerHTML());
|
||||
addSubproperty('Colour', `<input class="colour-swatch" type="color" id="${this._colourId}" value="${RGBAUtil.toHexString(material.colour)}">`);
|
||||
addSubproperty('Alpha', this._alphaElement._generateInnerHTML());
|
||||
|
||||
return `
|
||||
<div class="subproperty-container">
|
||||
${subproperties.join('')}
|
||||
<div class="component-group">
|
||||
${this._typeElement.generateHTML()}
|
||||
${this._colourElement.generateHTML()}
|
||||
${this._alphaElement.generateHTML()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -84,6 +68,10 @@ export class SolidMaterialElement extends ConfigUIElement<SolidMaterial, HTMLDiv
|
||||
|
||||
protected override _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
|
||||
this._typeElement.setEnabled(this.enabled);
|
||||
this._colourElement.setEnabled(this.enabled);
|
||||
this._alphaElement.setEnabled(this.enabled);
|
||||
}
|
||||
|
||||
private _onChangeTypeDelegate?: () => void;
|
||||
|
@ -4,6 +4,8 @@ import { MaterialType, TexturedMaterial } from '../../mesh';
|
||||
import { EImageChannel, TTransparencyTypes } from '../../texture';
|
||||
import { getRandomID } from '../../util';
|
||||
import { ASSERT } from '../../util/error_util';
|
||||
import { TTexelInterpolation } from '../../util/type_util';
|
||||
import { HTMLBuilder } from '../misc';
|
||||
import { ComboBoxElement } from './combobox';
|
||||
import { ConfigUIElement } from './config_element';
|
||||
import { ImageElement } from './image_element';
|
||||
@ -11,64 +13,64 @@ import { MaterialTypeElement } from './material_type_element';
|
||||
import { SliderElement } from './slider';
|
||||
|
||||
export class TexturedMaterialElement extends ConfigUIElement<TexturedMaterial, HTMLDivElement> {
|
||||
private _materialName: string;
|
||||
private _colourId: string;
|
||||
private _typeElement: MaterialTypeElement;
|
||||
private _filteringElement: ComboBoxElement<'nearest' | 'linear'>;
|
||||
private _wrapElement: ComboBoxElement<'clamp' | 'repeat'>;
|
||||
private _transparencyElement: ComboBoxElement<TTransparencyTypes>;
|
||||
private _imageElement: ImageElement;
|
||||
private _typeElement: MaterialTypeElement;
|
||||
private _alphaValueElement?: SliderElement;
|
||||
private _alphaMapElement?: ImageElement;
|
||||
private _alphaChannelElement?: ComboBoxElement<EImageChannel>;
|
||||
|
||||
public constructor(materialName: string, material: TexturedMaterial) {
|
||||
super(material);
|
||||
this._materialName = materialName;
|
||||
this._colourId = getRandomID();
|
||||
|
||||
this._filteringElement = new ComboBoxElement<'linear' | 'nearest'>()
|
||||
this._typeElement = new MaterialTypeElement(material)
|
||||
.setLabel('Type');
|
||||
|
||||
this._filteringElement = new ComboBoxElement<TTexelInterpolation>()
|
||||
.setLabel('Filtering')
|
||||
.addItem({ payload: 'linear', displayText: 'Linear' })
|
||||
.addItem({ payload: 'nearest', displayText: 'Nearest' })
|
||||
.setSmall()
|
||||
.setDefaultValue(material.interpolation);
|
||||
|
||||
this._wrapElement = new ComboBoxElement<'clamp' | 'repeat'>()
|
||||
.setLabel('Wrap')
|
||||
.addItem({ payload: 'clamp', displayText: 'Clamp' })
|
||||
.addItem({ payload: 'repeat', displayText: 'Repeat' })
|
||||
.setSmall()
|
||||
.setDefaultValue(material.extension);
|
||||
|
||||
this._transparencyElement = new ComboBoxElement<TTransparencyTypes>()
|
||||
.setLabel('Transparency')
|
||||
.addItem({ payload: 'None', displayText: 'None' })
|
||||
.addItem({ payload: 'UseAlphaMap', displayText: 'Alpha map' })
|
||||
.addItem({ payload: 'UseAlphaValue', displayText: 'Alpha constant' })
|
||||
.addItem({ payload: 'UseDiffuseMapAlphaChannel', displayText: 'Diffuse map alpha channel' })
|
||||
.setSmall()
|
||||
.setDefaultValue(material.transparency.type);
|
||||
|
||||
this._imageElement = new ImageElement(material.diffuse);
|
||||
|
||||
this._typeElement = new MaterialTypeElement(material);
|
||||
this._imageElement = new ImageElement(material.diffuse)
|
||||
.setLabel('Diffuse map');
|
||||
|
||||
switch (material.transparency.type) {
|
||||
case 'UseAlphaValue':
|
||||
this._alphaValueElement = new SliderElement()
|
||||
.setLabel('Alpha')
|
||||
.setMin(0.0)
|
||||
.setMax(1.0)
|
||||
.setDefaultValue(material.transparency.alpha)
|
||||
.setDecimals(2)
|
||||
.setStep(0.01)
|
||||
.setSmall();
|
||||
.setStep(0.01);
|
||||
break;
|
||||
case 'UseAlphaMap':
|
||||
this._alphaMapElement = new ImageElement(material.transparency.alpha);
|
||||
this._alphaMapElement = new ImageElement(material.transparency.alpha)
|
||||
.setLabel('Alpha map');
|
||||
|
||||
this._alphaChannelElement = new ComboBoxElement<EImageChannel>()
|
||||
.setLabel('Alpha channel')
|
||||
.addItem({ payload: EImageChannel.R, displayText: 'Red' })
|
||||
.addItem({ payload: EImageChannel.G, displayText: 'Green' })
|
||||
.addItem({ payload: EImageChannel.B, displayText: 'Blue' })
|
||||
.addItem({ payload: EImageChannel.A, displayText: 'Alpha' })
|
||||
.setSmall()
|
||||
.setDefaultValue(material.transparency.channel);
|
||||
break;
|
||||
}
|
||||
@ -139,39 +141,27 @@ export class TexturedMaterialElement extends ConfigUIElement<TexturedMaterial, H
|
||||
}
|
||||
|
||||
protected override _generateInnerHTML(): string {
|
||||
const subproperties: string[] = [];
|
||||
const addSubproperty = (key: string, value: string) => {
|
||||
subproperties.push(`
|
||||
<div class="subproperty">
|
||||
<div class="subprop-key-container">
|
||||
${key}
|
||||
</div>
|
||||
<div class="subprop-value-container">
|
||||
${value}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
};
|
||||
const builder = new HTMLBuilder();
|
||||
|
||||
addSubproperty('Type', this._typeElement._generateInnerHTML());
|
||||
addSubproperty('Diffuse map', this._imageElement._generateInnerHTML());
|
||||
addSubproperty('Filtering', this._filteringElement._generateInnerHTML());
|
||||
addSubproperty('Wrap', this._wrapElement._generateInnerHTML());
|
||||
addSubproperty('Transparency', this._transparencyElement._generateInnerHTML());
|
||||
if (this._alphaMapElement !== undefined) {
|
||||
ASSERT(this._alphaChannelElement !== undefined);
|
||||
addSubproperty('Alpha map', this._alphaMapElement._generateInnerHTML());
|
||||
addSubproperty('Channel', this._alphaChannelElement._generateInnerHTML());
|
||||
}
|
||||
if (this._alphaValueElement) {
|
||||
addSubproperty('Alpha', this._alphaValueElement._generateInnerHTML());
|
||||
builder.add('<div class="component-group">');
|
||||
{
|
||||
builder.add(this._typeElement.generateHTML());
|
||||
builder.add(this._imageElement.generateHTML());
|
||||
builder.add(this._filteringElement.generateHTML());
|
||||
builder.add(this._wrapElement.generateHTML());
|
||||
builder.add(this._transparencyElement.generateHTML());
|
||||
if (this._alphaMapElement !== undefined) {
|
||||
ASSERT(this._alphaChannelElement !== undefined);
|
||||
builder.add(this._alphaMapElement.generateHTML());
|
||||
builder.add(this._alphaChannelElement.generateHTML());
|
||||
}
|
||||
if (this._alphaValueElement !== undefined) {
|
||||
builder.add(this._alphaValueElement.generateHTML());
|
||||
}
|
||||
}
|
||||
builder.add('</div>');
|
||||
|
||||
return `
|
||||
<div class="subproperty-container">
|
||||
${subproperties.join('')}
|
||||
</div>
|
||||
`;
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
@ -179,6 +169,15 @@ export class TexturedMaterialElement extends ConfigUIElement<TexturedMaterial, H
|
||||
|
||||
protected override _onEnabledChanged(): void {
|
||||
super._onEnabledChanged();
|
||||
|
||||
this._imageElement.setEnabled(this.enabled);
|
||||
this._typeElement.setEnabled(this.enabled);
|
||||
this._filteringElement.setEnabled(this.enabled);
|
||||
this._wrapElement.setEnabled(this.enabled);
|
||||
this._transparencyElement.setEnabled(this.enabled);
|
||||
this._alphaValueElement?.setEnabled(this.enabled);
|
||||
this._alphaMapElement?.setEnabled(this.enabled);
|
||||
this._alphaChannelElement?.setEnabled(this.enabled);
|
||||
}
|
||||
|
||||
public override finalise(): void {
|
||||
|
@ -3,6 +3,7 @@ import { ASSERT } from '../../util/error_util';
|
||||
import { AppPaths } from '../../util/path_util';
|
||||
import { PathUtil } from '../../util/path_util';
|
||||
import { UIUtil } from '../../util/ui_util';
|
||||
import { BaseUIElement } from './base_element';
|
||||
|
||||
export type TToolbarBooleanProperty = 'enabled' | 'active';
|
||||
|
||||
@ -11,18 +12,16 @@ export type TToolbarItemParams = {
|
||||
iconSVG: string;
|
||||
}
|
||||
|
||||
export class ToolbarItemElement {
|
||||
private _id: string;
|
||||
export class ToolbarItemElement extends BaseUIElement<HTMLDivElement> {
|
||||
private _iconSVG: SVGSVGElement;
|
||||
private _isEnabled: boolean;
|
||||
private _isActive: boolean;
|
||||
private _isHovering: boolean;
|
||||
private _small: boolean;
|
||||
private _label: string;
|
||||
private _onClick?: () => void;
|
||||
private _isActive: boolean;
|
||||
|
||||
public constructor(params: TToolbarItemParams) {
|
||||
this._id = params.id + getRandomID();
|
||||
super();
|
||||
|
||||
this._isActive = false;
|
||||
|
||||
{
|
||||
const parser = new DOMParser();
|
||||
@ -31,20 +30,16 @@ export class ToolbarItemElement {
|
||||
ASSERT(svgs.length === 1, 'Missing SVG');
|
||||
|
||||
this._iconSVG = svgs[0];
|
||||
this._iconSVG.id = this._id + '-svg';
|
||||
this._iconSVG.id = this._getId() + '-svg';
|
||||
}
|
||||
|
||||
this._isEnabled = true;
|
||||
this._isActive = false;
|
||||
this._isHovering = false;
|
||||
|
||||
this._small = false;
|
||||
this._label = '';
|
||||
}
|
||||
|
||||
public setSmall() {
|
||||
this._small = true;
|
||||
return this;
|
||||
|
||||
public setActive(isActive: boolean) {
|
||||
this._isActive = isActive;
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
public setLabel(label: string) {
|
||||
@ -55,19 +50,25 @@ export class ToolbarItemElement {
|
||||
public tick() {
|
||||
if (this._isEnabledDelegate !== undefined) {
|
||||
const newIsEnabled = this._isEnabledDelegate();
|
||||
//if (newIsEnabled !== this._isEnabled) {
|
||||
this.setEnabled(newIsEnabled);
|
||||
//}
|
||||
if (newIsEnabled != this.enabled) {
|
||||
this.setEnabled(newIsEnabled);
|
||||
this._updateStyles();
|
||||
}
|
||||
}
|
||||
|
||||
if (this._isActiveDelegate !== undefined) {
|
||||
const newIsActive = this._isActiveDelegate();
|
||||
//if (newIsActive !== this._isActive) {
|
||||
this.setActive(newIsActive);
|
||||
//}
|
||||
if (newIsActive !== this._isActive) {
|
||||
this._isActive = newIsActive;
|
||||
this._updateStyles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected _onEnabledChanged(): void {
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
private _isActiveDelegate?: () => boolean;
|
||||
public isActive(delegate: () => boolean) {
|
||||
this._isActiveDelegate = delegate;
|
||||
@ -88,84 +89,47 @@ export class ToolbarItemElement {
|
||||
|
||||
public generateHTML() {
|
||||
return `
|
||||
<div class="toolbar-item ${this._small ? 'toolbar-item-small' : ''}" id="${this._id}">
|
||||
<div class="struct-prop container-icon-button" id="${this._getId()}">
|
||||
${this._iconSVG.outerHTML} ${this._label}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public registerEvents(): void {
|
||||
const element = document.getElementById(this._id) as HTMLDivElement;
|
||||
const element = document.getElementById(this._getId()) as HTMLDivElement;
|
||||
ASSERT(element !== null);
|
||||
|
||||
element.addEventListener('click', () => {
|
||||
if (this._isEnabled && this._onClick) {
|
||||
if (this.enabled && this._onClick) {
|
||||
this._onClick();
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('mouseenter', () => {
|
||||
this._isHovering = true;
|
||||
this._updateElements();
|
||||
this._setHovered(true);
|
||||
this._updateStyles();
|
||||
});
|
||||
|
||||
element.addEventListener('mouseleave', () => {
|
||||
this._isHovering = false;
|
||||
this._updateElements();
|
||||
this._setHovered(false);
|
||||
this._updateStyles();
|
||||
});
|
||||
}
|
||||
|
||||
this._updateElements();
|
||||
public override finalise(): void {
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
private _getSVGElement() {
|
||||
const svgId = this._id + '-svg';
|
||||
const svgId = this._getId() + '-svg';
|
||||
return UIUtil.getElementById(svgId);
|
||||
}
|
||||
|
||||
private _updateElements() {
|
||||
const element = document.getElementById(this._id) as HTMLDivElement;
|
||||
const svgElement = this._getSVGElement();
|
||||
if (element !== null && svgElement !== null) {
|
||||
element.classList.remove('toolbar-item-active-hover');
|
||||
element.classList.remove('toolbar-item-disabled');
|
||||
element.classList.remove('toolbar-item-active');
|
||||
element.classList.remove('toolbar-item-hover');
|
||||
|
||||
if (this._isEnabled) {
|
||||
if (this._isActive) {
|
||||
if (this._isHovering) {
|
||||
element.classList.add('toolbar-item-active-hover');
|
||||
} else {
|
||||
element.classList.add('toolbar-item-active');
|
||||
}
|
||||
} else {
|
||||
if (this._isHovering) {
|
||||
element.classList.add('toolbar-item-hover');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
element.classList.add('toolbar-item-disabled');
|
||||
}
|
||||
|
||||
svgElement.classList.remove('icon-disabled');
|
||||
svgElement.classList.remove('icon-active');
|
||||
if (this._isEnabled) {
|
||||
if (this._isActive) {
|
||||
svgElement.classList.add('icon-active');
|
||||
}
|
||||
} else {
|
||||
svgElement.classList.add('icon-disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public setEnabled(isEnabled: boolean) {
|
||||
this._isEnabled = isEnabled;
|
||||
this._updateElements();
|
||||
}
|
||||
|
||||
public setActive(isActive: boolean) {
|
||||
this._isActive = isActive;
|
||||
this._updateElements();
|
||||
protected override _updateStyles() {
|
||||
UIUtil.updateStyles(this._getElement(), {
|
||||
isActive: this._isActive,
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this.hovered,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export class VectorSpinboxElement extends ConfigUIElement<Vector3, HTMLDivElemen
|
||||
html += `
|
||||
<div class="spinbox-element-container">
|
||||
<div class="spinbox-key" id="${this._getKeyId('x')}">X</div>
|
||||
<div class="spinbox-value" id="${this._getValueId('x')}">
|
||||
<div class="spinbox-value struct-prop" id="${this._getValueId('x')}">
|
||||
${this.getValue().x}
|
||||
</div>
|
||||
</div>
|
||||
@ -55,7 +55,7 @@ export class VectorSpinboxElement extends ConfigUIElement<Vector3, HTMLDivElemen
|
||||
html += `
|
||||
<div class="spinbox-element-container">
|
||||
<div class="spinbox-key" id="${this._getKeyId('y')}">Y</div>
|
||||
<div class="spinbox-value" id="${this._getValueId('y')}">
|
||||
<div class="spinbox-value struct-prop" id="${this._getValueId('y')}">
|
||||
${this.getValue().y}
|
||||
</div>
|
||||
</div>
|
||||
@ -64,7 +64,7 @@ export class VectorSpinboxElement extends ConfigUIElement<Vector3, HTMLDivElemen
|
||||
html += `
|
||||
<div class="spinbox-element-container">
|
||||
<div class="spinbox-key" id="${this._getKeyId('z')}">Z</div>
|
||||
<div class="spinbox-value" id="${this._getValueId('z')}">
|
||||
<div class="spinbox-value struct-prop" id="${this._getValueId('z')}">
|
||||
${this.getValue().z}
|
||||
</div>
|
||||
</div>
|
||||
@ -86,16 +86,12 @@ export class VectorSpinboxElement extends ConfigUIElement<Vector3, HTMLDivElemen
|
||||
|
||||
elementValue.onmouseenter = () => {
|
||||
this._mouseover = axis;
|
||||
if (this.getEnabled()) {
|
||||
elementValue.classList.add('spinbox-value-hover');
|
||||
}
|
||||
this._updateStyles();
|
||||
};
|
||||
|
||||
elementValue.onmouseleave = () => {
|
||||
this._mouseover = null;
|
||||
if (this._dragging !== axis) {
|
||||
elementValue.classList.remove('spinbox-value-hover');
|
||||
}
|
||||
this._updateStyles();
|
||||
};
|
||||
}
|
||||
|
||||
@ -107,30 +103,26 @@ export class VectorSpinboxElement extends ConfigUIElement<Vector3, HTMLDivElemen
|
||||
this._registerAxis('z');
|
||||
|
||||
document.addEventListener('mousedown', (e: any) => {
|
||||
if (this.getEnabled() && this._mouseover !== null) {
|
||||
if (this.enabled && this._mouseover !== null) {
|
||||
this._dragging = this._mouseover;
|
||||
this._lastClientX = e.clientX;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (e: any) => {
|
||||
if (this.getEnabled() && this._dragging !== null) {
|
||||
if (this.enabled && this._dragging !== null) {
|
||||
this._updateValue(e);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
if (this._dragging !== null) {
|
||||
const elementValue = UIUtil.getElementById(this._getValueId(this._dragging));
|
||||
elementValue.classList.remove('spinbox-value-hover');
|
||||
}
|
||||
|
||||
this._dragging = null;
|
||||
this._updateStyles();
|
||||
});
|
||||
}
|
||||
|
||||
private _updateValue(e: MouseEvent) {
|
||||
ASSERT(this.getEnabled(), 'Not enabled');
|
||||
ASSERT(this.enabled, 'Not enabled');
|
||||
ASSERT(this._dragging !== null, 'Dragging nothing');
|
||||
|
||||
const deltaX = e.clientX - this._lastClientX;
|
||||
@ -152,48 +144,50 @@ export class VectorSpinboxElement extends ConfigUIElement<Vector3, HTMLDivElemen
|
||||
this._setValue(current);
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
super._onEnabledChanged();
|
||||
|
||||
const keyElements = [
|
||||
UIUtil.getElementById(this._getKeyId('x')),
|
||||
UIUtil.getElementById(this._getKeyId('y')),
|
||||
UIUtil.getElementById(this._getKeyId('z')),
|
||||
];
|
||||
const valueElements = [
|
||||
UIUtil.getElementById(this._getValueId('x')),
|
||||
UIUtil.getElementById(this._getValueId('y')),
|
||||
UIUtil.getElementById(this._getValueId('z')),
|
||||
];
|
||||
|
||||
if (this.getEnabled()) {
|
||||
for (const keyElement of keyElements) {
|
||||
keyElement?.classList.remove('spinbox-key-disabled');
|
||||
}
|
||||
for (const valueElement of valueElements) {
|
||||
valueElement?.classList.remove('spinbox-value-disabled');
|
||||
}
|
||||
} else {
|
||||
for (const keyElement of keyElements) {
|
||||
keyElement?.classList.add('spinbox-key-disabled');
|
||||
}
|
||||
for (const valueElement of valueElements) {
|
||||
valueElement?.classList.add('spinbox-value-disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
protected override _updateStyles(): void {
|
||||
const elementXV = UIUtil.getElementById(this._getValueId('x'));
|
||||
const elementYV = UIUtil.getElementById(this._getValueId('y'));
|
||||
const elementZV = UIUtil.getElementById(this._getValueId('z'));
|
||||
|
||||
const current = this.getValue().copy();
|
||||
// Update text
|
||||
{
|
||||
const current = this.getValue().copy();
|
||||
|
||||
elementXV.innerHTML = current.x.toString() + this._units;
|
||||
if (elementYV) {
|
||||
elementYV.innerHTML = current.y.toString() + this._units;
|
||||
elementXV.innerHTML = current.x.toString() + this._units;
|
||||
if (elementYV) {
|
||||
elementYV.innerHTML = current.y.toString() + this._units;
|
||||
}
|
||||
elementZV.innerHTML = current.z.toString() + this._units;
|
||||
}
|
||||
elementZV.innerHTML = current.z.toString() + this._units;
|
||||
|
||||
// Update styles
|
||||
{
|
||||
UIUtil.updateStyles(elementXV, {
|
||||
isActive: false,
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this._dragging === 'x' || (this._mouseover === 'x' && this._dragging === null),
|
||||
});
|
||||
|
||||
UIUtil.updateStyles(elementYV, {
|
||||
isActive: false,
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this._dragging === 'y' || (this._mouseover === 'y' && this._dragging === null),
|
||||
});
|
||||
|
||||
UIUtil.updateStyles(elementZV, {
|
||||
isActive: false,
|
||||
isEnabled: this.enabled,
|
||||
isHovered: this._dragging === 'z' || (this._mouseover === 'z' && this._dragging === null),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override _onEnabledChanged() {
|
||||
super._onEnabledChanged();
|
||||
this._updateStyles();
|
||||
}
|
||||
|
||||
protected override _onValueChanged(): void {
|
||||
this._updateStyles();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// https://tablericons.com/
|
||||
export namespace AppIcons {
|
||||
|
||||
export const MESH = `
|
||||
@ -61,10 +62,11 @@ export namespace AppIcons {
|
||||
`;
|
||||
|
||||
export const BULB = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-sun" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#00abfb" fill="none" stroke-linecap="round" stroke-linejoin="round" id="bulb-svg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-bulb" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
<path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" />
|
||||
<path d="M3 12h1m8 -9v1m8 8h1m-15.4 -6.4l.7 .7m12.1 -.7l-.7 .7" />
|
||||
<path d="M9 16a5 5 0 1 1 6 0a3.5 3.5 0 0 0 -1 3a2 2 0 0 1 -4 0a3.5 3.5 0 0 0 -1 -3" />
|
||||
<line x1="9.7" y1="17" x2="14.3" y2="17" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
@ -209,4 +211,41 @@ export namespace AppIcons {
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const ARROW_DOWN = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-down" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const COLOUR_SWATCH = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-color-swatch" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M19 3h-4a2 2 0 0 0 -2 2v12a4 4 0 0 0 8 0v-12a2 2 0 0 0 -2 -2" />
|
||||
<path d="M13 7.35l-2 -2a2 2 0 0 0 -2.828 0l-2.828 2.828a2 2 0 0 0 0 2.828l9 9" />
|
||||
<path d="M7.3 13h-2.3a2 2 0 0 0 -2 2v4a2 2 0 0 0 2 2h12" />
|
||||
<line x1="17" y1="17" x2="17" y2="17.01" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const IMAGE = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-photo" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="15" y1="8" x2="15.01" y2="8" />
|
||||
<rect x="4" y="4" width="16" height="16" rx="3" />
|
||||
<path d="M4 15l4 -4a3 5 0 0 1 3 0l5 5" />
|
||||
<path d="M14 14l1 -1a3 5 0 0 1 3 0l2 2" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const IMAGE_MISSING = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-photo-off" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="3" y1="3" x2="21" y2="21" />
|
||||
<line x1="15" y1="8" x2="15.01" y2="8" />
|
||||
<path d="M19.121 19.122a3 3 0 0 1 -2.121 .878h-10a3 3 0 0 1 -3 -3v-10c0 -.833 .34 -1.587 .888 -2.131m3.112 -.869h9a3 3 0 0 1 3 3v9" />
|
||||
<path d="M4 15l4 -4c.928 -.893 2.072 -.893 3 0l5 5" />
|
||||
<path d="M16.32 12.34c.577 -.059 1.162 .162 1.68 .66l2 2" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { ASSERT } from '../util/error_util';
|
||||
import { LOG } from '../util/log_util';
|
||||
import { TAxis } from '../util/type_util';
|
||||
import { TDithering } from '../util/type_util';
|
||||
import { UIUtil } from '../util/ui_util';
|
||||
import { TVoxelOverlapRule } from '../voxel_mesh';
|
||||
import { TVoxelisers } from '../voxelisers/voxelisers';
|
||||
import { AppConsole } from './console';
|
||||
@ -18,14 +19,14 @@ import { ButtonElement } from './elements/button';
|
||||
import { CheckboxElement } from './elements/checkbox';
|
||||
import { ComboBoxElement, ComboBoxItem } from './elements/combobox';
|
||||
import { ConfigUIElement } from './elements/config_element';
|
||||
import { FileInputElement } from './elements/file_input';
|
||||
import { ObjFileInputElement } from './elements/file_input';
|
||||
import { HeaderUIElement } from './elements/header_element';
|
||||
import { PaletteElement } from './elements/palette_element';
|
||||
import { SliderElement } from './elements/slider';
|
||||
import { ToolbarItemElement } from './elements/toolbar_item';
|
||||
import { VectorSpinboxElement } from './elements/vector_spinbox';
|
||||
import { AppIcons } from './icons';
|
||||
import { HTMLBuilder } from './misc';
|
||||
import { HTMLBuilder, MiscComponents } from './misc';
|
||||
|
||||
export interface Group {
|
||||
label: string;
|
||||
@ -43,10 +44,9 @@ export class UI {
|
||||
public uiOrder = ['import', 'materials', 'voxelise', 'assign', 'export'];
|
||||
private _ui = {
|
||||
'import': {
|
||||
label: 'Import',
|
||||
label: '1. Import',
|
||||
elements: {
|
||||
'input': new FileInputElement()
|
||||
.setFileExtensions(['obj'])
|
||||
'input': new ObjFileInputElement()
|
||||
.setLabel('Wavefront .obj file'),
|
||||
'rotation': new VectorSpinboxElement()
|
||||
.setLabel('Rotation')
|
||||
@ -61,7 +61,7 @@ export class UI {
|
||||
.setLabel('Load mesh'),
|
||||
},
|
||||
'materials': {
|
||||
label: 'Materials',
|
||||
label: '2. Materials',
|
||||
elements: {
|
||||
},
|
||||
elementsOrder: [],
|
||||
@ -72,7 +72,7 @@ export class UI {
|
||||
.setLabel('Update materials'),
|
||||
},
|
||||
'voxelise': {
|
||||
label: 'Voxelise',
|
||||
label: '3. Voxelise',
|
||||
elements: {
|
||||
'constraintAxis': new ComboBoxElement<TAxis>()
|
||||
.addItem({ payload: 'y', displayText: 'Y (height) (green)' })
|
||||
@ -144,7 +144,7 @@ export class UI {
|
||||
.setLabel('Voxelise mesh'),
|
||||
},
|
||||
'assign': {
|
||||
label: 'Assign',
|
||||
label: '4. Assign',
|
||||
elements: {
|
||||
'textureAtlas': new ComboBoxElement<string>()
|
||||
.addItems(this._getTextureAtlases())
|
||||
@ -242,7 +242,7 @@ export class UI {
|
||||
.setLabel('Assign blocks'),
|
||||
},
|
||||
'export': {
|
||||
label: 'Export',
|
||||
label: '5. Export',
|
||||
elements: {
|
||||
'export': new ComboBoxElement<TExporters>()
|
||||
.addItems([
|
||||
@ -413,6 +413,13 @@ export class UI {
|
||||
document.body.style.cursor = 'default';
|
||||
}
|
||||
|
||||
const canvasColumn = UIUtil.getElementById('col-canvas');
|
||||
if (ArcballCamera.Get.isUserRotating || ArcballCamera.Get.isUserTranslating) {
|
||||
canvasColumn.style.cursor = 'grabbing';
|
||||
} else {
|
||||
canvasColumn.style.cursor = 'grab';
|
||||
}
|
||||
|
||||
for (const groupName in this._toolbarLeftDull) {
|
||||
const toolbarGroup = this._toolbarLeftDull[groupName];
|
||||
for (const toolbarItem of toolbarGroup.elementsOrder) {
|
||||
@ -433,7 +440,7 @@ export class UI {
|
||||
{
|
||||
const sidebarHTML = new HTMLBuilder();
|
||||
|
||||
sidebarHTML.add(`<div class="container">`);
|
||||
sidebarHTML.add(`<div class="container-properties">`);
|
||||
{
|
||||
sidebarHTML.add(HeaderUIElement.Get.generateHTML());
|
||||
|
||||
@ -485,24 +492,16 @@ export class UI {
|
||||
|
||||
Split(['.column-sidebar', '.column-canvas'], {
|
||||
sizes: [20, 80],
|
||||
minSize: [400, 500],
|
||||
minSize: [600, 500],
|
||||
gutterSize: 5,
|
||||
});
|
||||
|
||||
Split(['.column-properties', '.column-console'], {
|
||||
sizes: [90, 10],
|
||||
sizes: [85, 15],
|
||||
minSize: [0, 0],
|
||||
direction: 'vertical',
|
||||
gutterSize: 5,
|
||||
});
|
||||
|
||||
const itemA = document.getElementsByClassName('gutter').item(1);
|
||||
if (itemA !== null) {
|
||||
itemA.innerHTML = `<div class='gutter-line'></div>`;
|
||||
}
|
||||
|
||||
const itemB = document.getElementsByClassName('gutter').item(0);
|
||||
if (itemB !== null) {
|
||||
itemB.innerHTML = `<div class='gutter-line-horizontal'></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
public cacheValues(action: EAction) {
|
||||
@ -539,25 +538,11 @@ export class UI {
|
||||
|
||||
private _getGroupHTML(group: Group) {
|
||||
return `
|
||||
<div class="property" style="padding-top: 20px;">
|
||||
<div style="flex-grow: 1">
|
||||
<div class="h-div">
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-heading">
|
||||
${group.label.toUpperCase()}
|
||||
</div>
|
||||
<div style="flex-grow: 1">
|
||||
<div class="h-div">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="subcomponents_${group.label}">
|
||||
${MiscComponents.createGroupHeader(group.label.toUpperCase())}
|
||||
<div class="component-group" id="subcomponents_${group.label}">
|
||||
${this._getGroupSubcomponentsHTML(group)}
|
||||
</div>
|
||||
<div class="property">
|
||||
${group.submitButton.generateHTML()}
|
||||
</div>
|
||||
${group.submitButton.generateHTML()}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -578,20 +563,25 @@ export class UI {
|
||||
element.finalise();
|
||||
}
|
||||
group.submitButton.registerEvents();
|
||||
group.submitButton.finalise();
|
||||
}
|
||||
|
||||
// Register toolbar left
|
||||
for (const toolbarGroupName of this._toolbarLeft.groupsOrder) {
|
||||
const toolbarGroup = this._toolbarLeftDull[toolbarGroupName];
|
||||
for (const groupElementName of toolbarGroup.elementsOrder) {
|
||||
toolbarGroup.elements[groupElementName].registerEvents();
|
||||
const element = toolbarGroup.elements[groupElementName];
|
||||
element.registerEvents();
|
||||
element.finalise();
|
||||
}
|
||||
}
|
||||
// Register toolbar right
|
||||
for (const toolbarGroupName of this._toolbarRight.groupsOrder) {
|
||||
const toolbarGroup = this._toolbarRightDull[toolbarGroupName];
|
||||
for (const groupElementName of toolbarGroup.elementsOrder) {
|
||||
toolbarGroup.elements[groupElementName].registerEvents();
|
||||
const element = toolbarGroup.elements[groupElementName];
|
||||
element.registerEvents();
|
||||
element.finalise();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,3 +22,15 @@ export class HTMLBuilder {
|
||||
element.innerHTML = this._html;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace MiscComponents {
|
||||
export function createGroupHeader(label: string) {
|
||||
return `
|
||||
<div class="container-group-heading">
|
||||
<div class="group-heading">
|
||||
${label}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,44 @@
|
||||
import { ASSERT } from './error_util';
|
||||
|
||||
export type TStyleParams = {
|
||||
isHovered: boolean,
|
||||
isEnabled: boolean,
|
||||
isActive: boolean,
|
||||
}
|
||||
|
||||
export namespace UIUtil {
|
||||
export function getElementById(id: string) {
|
||||
const element = document.getElementById(id);
|
||||
ASSERT(element !== null, `Attempting to getElement of nonexistent element: ${id}`);
|
||||
return element as HTMLElement;
|
||||
}
|
||||
|
||||
export function clearStyles(element: HTMLElement) {
|
||||
element.classList.remove('style-inactive-disabled');
|
||||
element.classList.remove('style-inactive-enabled');
|
||||
element.classList.remove('style-inactive-hover');
|
||||
element.classList.remove('style-active-disabled');
|
||||
element.classList.remove('style-active-enabled');
|
||||
element.classList.remove('style-active-hover');
|
||||
}
|
||||
|
||||
export function updateStyles(element: HTMLElement, style: TStyleParams) {
|
||||
clearStyles(element);
|
||||
|
||||
let styleToApply = `style`;
|
||||
|
||||
styleToApply += style.isActive ? '-active' : '-inactive';
|
||||
|
||||
if (style.isEnabled) {
|
||||
if (style.isHovered) {
|
||||
styleToApply += '-hover';
|
||||
} else {
|
||||
styleToApply += '-enabled';
|
||||
}
|
||||
} else {
|
||||
styleToApply += '-disabled';
|
||||
}
|
||||
|
||||
element.classList.add(styleToApply);
|
||||
}
|
||||
}
|
||||
|
735
styles.css
735
styles.css
File diff suppressed because it is too large
Load Diff
@ -38,7 +38,7 @@
|
||||
<div class="split column-console" id="console">
|
||||
</div>
|
||||
</div>
|
||||
<div class="split column-canvas">
|
||||
<div class="split column-canvas" id="col-canvas">
|
||||
<canvas id="canvas"></canvas>
|
||||
<div class="toolbar" id="toolbar"></div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user