2017-10-10 20:48:50 +00:00
|
|
|
/**
|
|
|
|
* Main way to create UI components
|
|
|
|
*/
|
|
|
|
module TK.SpaceTac.UI {
|
2018-05-15 14:57:45 +00:00
|
|
|
export type UIBuilderParent = UIImage | UIContainer
|
2017-10-10 20:48:50 +00:00
|
|
|
|
2017-10-15 17:54:37 +00:00
|
|
|
export type ShaderValue = number | { x: number, y: number }
|
2018-02-08 15:16:03 +00:00
|
|
|
export type UIOnOffCallback = (on: boolean) => boolean
|
2017-10-15 17:54:37 +00:00
|
|
|
|
2017-10-10 20:48:50 +00:00
|
|
|
/**
|
|
|
|
* Text style interface
|
|
|
|
*/
|
|
|
|
export interface UITextStyleI {
|
|
|
|
size?: number
|
|
|
|
color?: string
|
|
|
|
shadow?: boolean
|
2017-10-11 21:49:56 +00:00
|
|
|
stroke_width?: number
|
|
|
|
stroke_color?: string
|
2017-10-10 20:48:50 +00:00
|
|
|
bold?: boolean
|
|
|
|
center?: boolean
|
|
|
|
vcenter?: boolean
|
|
|
|
width?: number
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Text style
|
|
|
|
*/
|
|
|
|
export class UITextStyle implements UITextStyleI {
|
|
|
|
// Size in points
|
|
|
|
size = 16
|
|
|
|
|
|
|
|
// Font color
|
|
|
|
color = "#ffffff"
|
|
|
|
|
|
|
|
// Shadow under the text
|
|
|
|
shadow = false
|
|
|
|
|
2017-10-11 21:49:56 +00:00
|
|
|
// Stroke around the letters
|
|
|
|
stroke_width = 0
|
|
|
|
stroke_color = "#ffffff"
|
|
|
|
|
2017-10-10 20:48:50 +00:00
|
|
|
// Bold text
|
|
|
|
bold = false
|
|
|
|
|
|
|
|
// Centering
|
|
|
|
center = true
|
|
|
|
vcenter = true
|
|
|
|
|
|
|
|
// Word wrapping
|
|
|
|
width = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main UI builder tool
|
|
|
|
*/
|
|
|
|
export class UIBuilder {
|
2018-03-20 22:06:39 +00:00
|
|
|
view: BaseView
|
2017-10-10 20:48:50 +00:00
|
|
|
private game: MainUI
|
2018-05-15 14:57:45 +00:00
|
|
|
private parent: UIBuilderParent
|
|
|
|
private text_style: UITextStyleI
|
2017-10-10 20:48:50 +00:00
|
|
|
|
2018-05-15 14:57:45 +00:00
|
|
|
constructor(view: BaseView, parent: UIBuilderParent | string = "base", text_style: UITextStyleI = new UITextStyle) {
|
2017-10-10 20:48:50 +00:00
|
|
|
this.view = view;
|
|
|
|
this.game = view.gameui;
|
|
|
|
if (typeof parent == "string") {
|
2017-10-11 20:58:08 +00:00
|
|
|
this.parent = view.getLayer(parent);
|
2017-10-10 20:48:50 +00:00
|
|
|
} else {
|
|
|
|
this.parent = parent;
|
|
|
|
}
|
|
|
|
this.text_style = text_style;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new UIBuilder inside a parent container, or a view layer
|
|
|
|
*
|
|
|
|
* This new builder will inherit the style settings, and will create components in the specified parent
|
|
|
|
*/
|
2018-05-15 14:57:45 +00:00
|
|
|
in(container: UIBuilderParent | string, body?: (builder: UIBuilder) => void): UIBuilder {
|
2017-12-04 18:30:18 +00:00
|
|
|
let result = new UIBuilder(this.view, container, this.text_style);
|
|
|
|
if (body) {
|
|
|
|
body(result);
|
|
|
|
}
|
|
|
|
return result;
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new UIBuilder with style changes
|
|
|
|
*/
|
2017-12-04 18:30:18 +00:00
|
|
|
styled(changes: UITextStyleI, body?: (builder: UIBuilder) => void): UIBuilder {
|
|
|
|
let result = new UIBuilder(this.view, this.parent, merge(this.text_style, changes));
|
|
|
|
if (body) {
|
|
|
|
body(result);
|
|
|
|
}
|
|
|
|
return result;
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear the current container of all component
|
|
|
|
*/
|
|
|
|
clear(): void {
|
2018-05-15 14:57:45 +00:00
|
|
|
if (this.parent instanceof UIImage) {
|
|
|
|
console.error("Cannot clear an image parent, use groups instead");
|
|
|
|
} else {
|
|
|
|
this.parent.removeAll(true);
|
|
|
|
}
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal method to add to the parent
|
|
|
|
*/
|
2018-05-15 14:57:45 +00:00
|
|
|
private add(child: UIText | UIImage | UIButton | UIContainer | UIGraphics): void {
|
|
|
|
if (this.parent instanceof UIImage) {
|
|
|
|
let gparent = this.parent.parentContainer;
|
|
|
|
if (gparent) {
|
|
|
|
let x = this.parent.x + child.x;
|
|
|
|
let y = this.parent.y + child.y;
|
|
|
|
child.setPosition(x, y);
|
|
|
|
gparent.add(child);
|
2018-03-05 23:52:44 +00:00
|
|
|
} else {
|
2018-05-15 14:57:45 +00:00
|
|
|
throw new Error("no parent container");
|
2018-03-05 23:52:44 +00:00
|
|
|
}
|
2017-10-10 20:48:50 +00:00
|
|
|
} else {
|
2018-05-15 14:57:45 +00:00
|
|
|
this.parent.add(child);
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-05-15 14:57:45 +00:00
|
|
|
* Add a container of other components
|
2017-10-10 20:48:50 +00:00
|
|
|
*/
|
2018-05-15 14:57:45 +00:00
|
|
|
container(name: string, x = 0, y = 0, visible = true): UIContainer {
|
|
|
|
let result = new UIContainer(this.view, x, y);
|
|
|
|
result.setName(name);
|
|
|
|
result.setVisible(visible);
|
2017-10-10 20:48:50 +00:00
|
|
|
this.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a text
|
|
|
|
*
|
|
|
|
* Anchor will be defined according to the style centering
|
|
|
|
*/
|
|
|
|
text(content: string, x = 0, y = 0, style_changes: UITextStyleI = {}): UIText {
|
|
|
|
let style = merge(this.text_style, style_changes);
|
2018-05-15 14:57:45 +00:00
|
|
|
let result = new UIText(this.view, x, y, content, {
|
2017-10-10 20:48:50 +00:00
|
|
|
fill: style.color,
|
|
|
|
align: style.center ? "center" : "left"
|
|
|
|
});
|
2018-05-15 14:57:45 +00:00
|
|
|
result.setFont(`${style.bold ? "bold " : ""}${style.size}pt SpaceTac`);
|
|
|
|
result.setOrigin(style.center ? 0.5 : 0, style.vcenter ? 0.5 : 0);
|
2017-10-10 20:48:50 +00:00
|
|
|
if (style.width) {
|
2018-05-15 14:57:45 +00:00
|
|
|
result.setWordWrapWidth(style.width);
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
|
|
|
if (style.shadow) {
|
2018-05-15 14:57:45 +00:00
|
|
|
result.setShadow(3, 4, "rgba(0,0,0,0.6)", 3, true, true);
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
2018-05-15 14:57:45 +00:00
|
|
|
if (style.stroke_width && style.stroke_color) {
|
|
|
|
result.setStroke(style.stroke_color, style.stroke_width);
|
2017-10-11 21:49:56 +00:00
|
|
|
}
|
2017-10-10 20:48:50 +00:00
|
|
|
this.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an image
|
|
|
|
*/
|
2017-12-11 00:31:16 +00:00
|
|
|
image(name: string | string[], x = 0, y = 0, centered = false): UIImage {
|
2017-10-10 22:32:46 +00:00
|
|
|
if (typeof name != "string") {
|
|
|
|
name = this.view.getFirstImage(...name);
|
|
|
|
}
|
|
|
|
|
2017-10-10 20:48:50 +00:00
|
|
|
let info = this.view.getImageInfo(name);
|
2018-05-15 14:57:45 +00:00
|
|
|
let result = new UIImage(this.view, x, y, info.key, info.frame);
|
2017-10-10 20:48:50 +00:00
|
|
|
result.name = name;
|
2018-05-15 14:57:45 +00:00
|
|
|
if (!centered) {
|
|
|
|
result.setOrigin(0);
|
2017-12-11 00:31:16 +00:00
|
|
|
}
|
2017-10-10 20:48:50 +00:00
|
|
|
this.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-08 15:16:03 +00:00
|
|
|
* Add a hoverable and/or clickable button
|
|
|
|
*
|
|
|
|
* If an image with "-hover" suffix is found in atlases, it will be used as hover mask (added as button child)
|
2017-10-10 20:48:50 +00:00
|
|
|
*/
|
2018-02-08 15:16:03 +00:00
|
|
|
button(name: string, x = 0, y = 0, onclick?: Function, tooltip?: TooltipFiller, onoffcallback?: UIOnOffCallback, options: UIButtonOptions = {}): UIButton {
|
2018-05-15 14:57:45 +00:00
|
|
|
options.text_style = merge(this.text_style, options.text_style || {});
|
|
|
|
let result = new UIButton(this.view, name, x, y, onclick, tooltip, onoffcallback, options);
|
2017-10-10 20:48:50 +00:00
|
|
|
this.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-12-20 18:57:28 +00:00
|
|
|
/**
|
|
|
|
* Add a value bar
|
|
|
|
*/
|
|
|
|
valuebar(name: string, x = 0, y = 0, orientation = ValueBarOrientation.EAST): ValueBar {
|
|
|
|
let result = new ValueBar(this.view, name, orientation, x, y);
|
|
|
|
this.add(result.node);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-10-15 17:54:37 +00:00
|
|
|
/**
|
2018-05-15 14:57:45 +00:00
|
|
|
* Add a graphics (for drawing)
|
2017-10-15 17:54:37 +00:00
|
|
|
*/
|
2018-05-15 14:57:45 +00:00
|
|
|
graphics(name: string, x = 0, y = 0, visible = true): UIGraphics {
|
|
|
|
let result = new UIGraphics(this.view, name, visible, x, y);
|
|
|
|
this.add(result);
|
2017-10-15 17:54:37 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-10-10 20:48:50 +00:00
|
|
|
/**
|
2018-05-15 14:57:45 +00:00
|
|
|
* Emit a bunch of particles
|
2017-10-10 20:48:50 +00:00
|
|
|
*/
|
2018-05-15 14:57:45 +00:00
|
|
|
particles(config: ParticlesConfig): void {
|
|
|
|
this.view.particles.emit(config, this.parent instanceof UIContainer ? this.parent : undefined);
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
2018-02-08 15:16:03 +00:00
|
|
|
|
2018-06-03 21:27:35 +00:00
|
|
|
/**
|
|
|
|
* Animation to await something
|
|
|
|
*/
|
|
|
|
awaiter(x = 0, y = 0, visible = true, scale = 1): UIAwaiter {
|
|
|
|
let result = new UIAwaiter(this.view, x, y, visible);
|
|
|
|
result.setScale(scale);
|
|
|
|
this.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-06-06 17:09:31 +00:00
|
|
|
/**
|
|
|
|
* Add a full-view capturing overlay
|
|
|
|
*/
|
|
|
|
overlay(options: UIOverlayOptions): UIOverlay {
|
|
|
|
let result = new UIOverlay(this.view, options);
|
|
|
|
this.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-02-08 15:16:03 +00:00
|
|
|
/**
|
2018-05-15 14:57:45 +00:00
|
|
|
* Change the content of an component
|
2018-02-08 15:16:03 +00:00
|
|
|
*
|
2018-05-15 14:57:45 +00:00
|
|
|
* If the component is a text, its content will be changed.
|
|
|
|
* If the component is an image, its texture will be changed.
|
2018-02-08 15:16:03 +00:00
|
|
|
*/
|
2018-05-15 14:57:45 +00:00
|
|
|
change(component: UIImage | UIText, content: string): void {
|
|
|
|
// TODO Should be moved custom UIImage and UIText classes
|
|
|
|
if (component instanceof UIText) {
|
|
|
|
component.setText(content);
|
2018-02-08 15:16:03 +00:00
|
|
|
} else {
|
2018-05-15 14:57:45 +00:00
|
|
|
let info = this.view.getImageInfo(content);
|
|
|
|
component.setName(content);
|
|
|
|
component.setTexture(info.key, info.frame);
|
2018-02-08 15:16:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Evenly distribute the children of this builder along an axis
|
|
|
|
*/
|
|
|
|
distribute(along: "x" | "y", start: number, end: number): void {
|
2018-05-15 14:57:45 +00:00
|
|
|
if (!(this.parent instanceof UIContainer)) {
|
|
|
|
throw new Error("UIBuilder.distribute only works on groups");
|
|
|
|
}
|
|
|
|
let children = this.parent.list;
|
|
|
|
|
|
|
|
let sizes = children.map(child => {
|
|
|
|
if (UITools.isSpatial(child)) {
|
|
|
|
return UITools.getBounds(child)[along == "x" ? "width" : "height"];
|
2018-02-08 15:16:03 +00:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let spacing = ((end - start) - sum(sizes)) / (sizes.length + 1);
|
|
|
|
let offset = start;
|
2018-05-15 14:57:45 +00:00
|
|
|
children.forEach((child, idx) => {
|
2018-02-08 15:16:03 +00:00
|
|
|
offset += spacing;
|
2018-05-15 14:57:45 +00:00
|
|
|
if (UITools.isSpatial(child)) {
|
|
|
|
child[along] = Math.round(offset);
|
|
|
|
}
|
2018-02-08 15:16:03 +00:00
|
|
|
offset += sizes[idx];
|
|
|
|
});
|
|
|
|
}
|
2017-10-10 20:48:50 +00:00
|
|
|
}
|
|
|
|
}
|