More font properties, including True Type fonts

This commit is contained in:
Jordan Eldredge 2021-06-16 13:27:08 -07:00
parent 09d1fef1ab
commit e82f3ccdb3
8 changed files with 223 additions and 3 deletions

View file

@ -1,9 +1,11 @@
import Bitmap from "./skin/Bitmap";
import TrueTypeFont from "./skin/TrueTypeFont";
import { assert } from "./utils";
class UIRoot {
// Just a temporary place to stash things
_bitmaps: Bitmap[] = [];
_trueTypeFonts: TrueTypeFont[] = [];
addBitmap(bitmap: Bitmap) {
this._bitmaps.push(bitmap);
@ -17,6 +19,19 @@ class UIRoot {
assert(found != null, `Could not find bitmap with id ${id}.`);
return found;
}
addTrueTypeFont(font: TrueTypeFont) {
this._trueTypeFonts.push(font);
}
getTrueTypeFont(id: string): TrueTypeFont {
const found = this._trueTypeFonts.find(
(font) => font._id.toLowerCase() === id.toLowerCase()
);
assert(found != null, `Could not find bitmap with id ${id}.`);
return found;
}
}
// Global Singleton for now

View file

@ -44,6 +44,10 @@ export default class Bitmap {
return true;
}
getId() {
return this._id;
}
getWidth() {
return this._width;
}
@ -73,9 +77,19 @@ export default class Bitmap {
this.setUrl(imgUrl);
}
getBackgrondCSSAttribute(): string {
getBackgrondImageCSSAttribute(): string {
return `url(${this._url})`;
}
getBackgrondPositionCSSAttribute(): string {
const x = Utils.px(-this._x);
const y = Utils.px(-this._y);
return `${x} ${y}`;
}
getBackgrondSizeCSSAttribute(): string {
const width = Utils.px(this._width);
const height = Utils.px(this._height);
return `url(${this._url}) ${width} ${height}`;
return `${width} ${height}`;
}
}

View file

@ -1,18 +1,41 @@
import GuiObj from "./GuiObj";
import UI_ROOT from "../UIRoot";
import { px } from "../utils";
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Cbutton.2F.3E_.26_.3Ctogglebutton.2F.3E
export default class Button extends GuiObj {
_image: string;
setXmlAttr(key: string, value: string): boolean {
if (super.setXmlAttr(key, value)) {
return true;
}
switch (key) {
case "image":
this._image = value;
break;
default:
return false;
}
return true;
}
getDebugDom(): HTMLDivElement {
const div = super.getDebugDom();
div.setAttribute("data-obj-name", "Button");
if (this._image != null) {
const bitmap = UI_ROOT.getBitmap(this._image);
div.style.backgroundImage = bitmap.getBackgrondImageCSSAttribute();
div.style.backgroundPosition = bitmap.getBackgrondPositionCSSAttribute();
if (div.style.width === "" && bitmap.getWidth()) {
div.style.width = px(bitmap.getWidth());
}
if (div.style.height === "" && bitmap.getHeight()) {
div.style.height = px(bitmap.getHeight());
}
}
return div;
}
/*
extern Button.onActivate(int activated);
extern Button.onLeftClick();

View file

@ -25,7 +25,8 @@ export default class Layer extends GuiObj {
div.setAttribute("data-obj-name", "Layer");
if (this._image != null) {
const bitmap = UI_ROOT.getBitmap(this._image);
div.style.background = bitmap.getBackgrondCSSAttribute();
div.style.backgroundImage = bitmap.getBackgrondImageCSSAttribute();
div.style.backgroundPosition = bitmap.getBackgrondPositionCSSAttribute();
if (div.style.width === "" && bitmap.getWidth()) {
div.style.width = px(bitmap.getWidth());
}

View file

@ -1,18 +1,118 @@
import GuiObj from "./GuiObj";
import * as Utils from "../utils";
import UI_ROOT from "../UIRoot";
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Ctext.2F.3E_.26_.3CWasabi:Text.2F.3E
export default class Text extends GuiObj {
_display: string;
_text: string;
_bold: boolean;
_forceupcase: boolean;
_forceuppercase: boolean;
_forcelocase: boolean;
_forcelowercase: boolean;
_align: string;
_font: string;
_fontSize: number;
setXmlAttr(key: string, value: string): boolean {
if (super.setXmlAttr(key, value)) {
return true;
}
switch (key) {
case "display":
// (str) Either a specific system display string or the string identifier of a text feed. Setting this value will override the text parameter. See below.
this._display = value;
break;
case "text":
// (str) A static string to be displayed.
this._text = value;
break;
case "bold":
// (str) A static string to be displayed.
this._bold = Utils.toBool(value);
break;
case "forceupcase":
// (bool) Force the system to make the display string all uppercase before display.
this._forceupcase = Utils.toBool(value);
break;
case "forceuppercase":
// (bool) Force the system to make the display string all uppercase before display.
this._forceuppercase = Utils.toBool(value);
break;
case "font":
// (id) The id of a bitmapfont or truetypefont element. If no element with that id can be found, the OS will be asked for a font with that name instead.
this._font = value;
break;
case "align":
// (str) One of the following three possible strings: "left" "center" "right" -- Default is "left."
this._align = value;
break;
case "fontsize":
// (int) The size to render the chosen font.
this._fontSize = Utils.num(value);
/*
ticker - (bool) Setting this flag causes the object to scroll left and right if the text does not fit the rectangular area of the text object.
antialias - (bool) Setting this flag causes the text to be rendered antialiased if possible.
default - (str) A parameter alias for text.
align - (str) One of the following three possible strings: "left" "center" "right" -- Default is "left."
valign - (str) One of the following three possible strings: "top" "center" "bottom" -- Default is "top."
color - (int) The comma delimited RGB color of the text.
shadowcolor - (int) The comma delimited RGB color for underrendered shadow text.
shadowx - (int) The x offset of the shadowrender.
shadowy - (int) The y offset of the shadowrender.
timeroffstyle - (int) How to display an empty timer: "0" = " : ", "1" = "00:00", and "2"="" (if one is displaying time)
timecolonwidth - (int) How many extra pixels wider or smaller should the colon be when displaying time. Default is -1.
nograb - (bool) Setting this flag will cause the text object to ignore left button down messages. Default is off.
showlen - (bool) Setting this flag will cause the text display to be appended with the length in minutes and seconds of the current song. Default is off.
forcefixed - (bool) Force the system to attempt to render the display string with fixed-width font spacing.
forcelocase - (bool) Force the system to make the display string all lowercase before display.
forcelowercase - (bool) Force the system to make the display string all lowercase before display.
bold - (bool) Render the display string in bold.
wrap - (bool) Setting this flag will cause the text to wrap in its rectangular space. Default is off.
dblclickaction - (str) A string in the form "SWITCH;layout" where layout is the id of a layout in this object's parent container. No other actions function on this object. This action is deprecated.
offsetx - (int) Extra pixels to be added to or subtracted from the calculated x value for the display string to render.
offsety - (int) Extra pixels to be added to or subtracted from the calculated x value for the display string to render.
*/
default:
return false;
}
return true;
}
getText() {
switch (this._display) {
case "time":
return "01:58";
case "songname":
return "Niente da Caprie (3";
case "songinfo":
return "112kbps stereo 44.";
default:
throw new Error(`Unknown text display name: "${this._display}".`);
}
return this._display ?? this._text;
}
getDebugDom(): HTMLDivElement {
const div = super.getDebugDom();
div.innerText = this.getText();
if (this._bold) {
div.style.fontWeight = "bold";
}
if (this._align) {
div.style.textAlign = this._align;
}
if (this._font) {
const font = UI_ROOT.getTrueTypeFont(this._font);
div.style.fontFamily = font.getFontFamily();
}
div.style.fontSize = Utils.px(this._fontSize ?? 14);
return div;
}
/*
extern Text.setText(String txt); // changes the display/text="something" param

View file

@ -0,0 +1,55 @@
import * as Utils from "../utils";
import ImageManager from "./ImageManager";
export default class TrueTypeFont {
_file: string;
_id: string;
_fontFace: FontFace;
setXmlAttributes(attributes: { [attrName: string]: string }) {
for (const [key, value] of Object.entries(attributes)) {
this.setXmlAttr(key, value);
}
}
setXmlAttr(key: string, value: string) {
switch (key) {
case "id":
this._id = value;
break;
case "file":
this._file = value;
default:
return false;
}
return true;
}
getId() {
return this._id;
}
getFontFamily() {
return this._fontFace.family;
}
dispose() {
document.fonts.delete(this._fontFace);
}
// Ensure we've loaded the font into our asset loader.
async ensureFontLoaded(imageManager: ImageManager) {
Utils.assert(
this._fontFace == null,
"Tried to ensure a TrueTypeFont was laoded more than once."
);
const fontUrl = await imageManager.getUrl(this._file);
const sanitizedFile = this._file.replace(/\./, "_");
const fontFamily = `font-${Utils.getId()}-${this.getId()}-${sanitizedFile}`;
const font = new FontFace(fontFamily, `url(${fontUrl})`);
this._fontFace = await font.load();
document.fonts.add(this._fontFace);
}
}

View file

@ -15,6 +15,7 @@ import Status from "./Status";
import { parse as parseMaki } from "../maki/parser";
import SystemObject from "./SystemObject";
import ToggleButton from "./ToggleButton";
import TrueTypeFont from "./TrueTypeFont";
class ParserContext {
container: Container | null = null;
@ -166,6 +167,7 @@ export default class SkinParser {
node.children.length === 0,
"Unexpected children in <bitmapFont> XML node."
);
console.log(node);
}
async text(node: XmlElement) {
@ -416,6 +418,11 @@ export default class SkinParser {
node.children.length === 0,
"Unexpected children in <truetypefont> XML node."
);
const font = new TrueTypeFont();
font.setXmlAttributes(node.attributes);
await font.ensureFontLoaded(this._imageManager);
UI_ROOT.addTrueTypeFont(font);
}
async include(node: XmlElement) {

View file

@ -33,3 +33,8 @@ export function toBool(str: string) {
assert(str === "0" || str === "1", 'Expected bool value to be "0" or "1".');
return str === "1";
}
let id = 0;
export function getId(): number {
return id++;
}