Funkin/source/funkin/ui/AtlasText.hx

284 lines
6 KiB
Haxe
Raw Normal View History

package funkin.ui;
2021-02-23 19:55:24 -05:00
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup;
import flixel.graphics.frames.FlxAtlasFrames;
2021-03-22 08:48:52 -04:00
import flixel.util.FlxStringUtil;
2021-02-23 19:55:24 -05:00
@:forward
abstract BoldText(AtlasText) from AtlasText to AtlasText
{
inline public function new(x = 0.0, y = 0.0, text:String)
2021-02-23 19:55:24 -05:00
{
this = new AtlasText(x, y, text, Bold);
}
}
/**
* Alphabet.hx has a ton of bugs and does a bunch of stuff I don't need, fuck that class
*/
class AtlasText extends FlxTypedSpriteGroup<AtlasChar>
{
2021-03-13 21:11:56 -05:00
static var fonts = new Map<AtlasFont, AtlasFontData>();
static var casesAllowed = new Map<AtlasFont, Case>();
2021-03-13 21:11:56 -05:00
public var text(default, set):String = "";
2021-03-13 21:11:56 -05:00
var font:AtlasFontData;
2021-03-13 21:11:56 -05:00
public var atlas(get, never):FlxAtlasFrames;
inline function get_atlas()
return font.atlas;
2021-03-13 21:11:56 -05:00
public var caseAllowed(get, never):Case;
inline function get_caseAllowed()
return font.caseAllowed;
2021-03-13 21:11:56 -05:00
public var maxHeight(get, never):Float;
inline function get_maxHeight()
return font.maxHeight;
public function new(x = 0.0, y = 0.0, text:String, fontName:AtlasFont = Default)
2021-02-23 19:55:24 -05:00
{
2021-03-13 21:11:56 -05:00
if (!fonts.exists(fontName))
fonts[fontName] = new AtlasFontData(fontName);
font = fonts[fontName];
2021-02-23 19:55:24 -05:00
super(x, y);
2021-02-23 19:55:24 -05:00
this.text = text;
}
2021-02-23 19:55:24 -05:00
function set_text(value:String)
{
2021-03-13 21:11:56 -05:00
if (value == null)
value = "";
2021-03-13 21:11:56 -05:00
var caseValue = restrictCase(value);
var caseText = restrictCase(this.text);
2021-03-13 21:11:56 -05:00
this.text = value;
if (caseText == caseValue)
return value; // cancel redraw
2021-03-13 21:11:56 -05:00
if (caseValue.indexOf(caseText) == 0)
{
// new text is just old text with additions at the end, append the difference
appendTextCased(caseValue.substr(caseText.length));
2021-02-23 19:55:24 -05:00
return this.text;
2021-03-13 21:11:56 -05:00
}
2021-03-13 21:11:56 -05:00
value = caseValue;
2021-02-23 19:55:24 -05:00
group.kill();
2021-03-13 21:11:56 -05:00
if (value == "")
return this.text;
2021-03-22 08:48:52 -04:00
appendTextCased(caseValue);
2021-03-13 21:11:56 -05:00
return this.text;
}
2021-03-13 21:11:56 -05:00
/**
* Adds new characters, without needing to redraw the previous characters
* @param text The text to add.
* @throws String if `text` is null.
*/
public function appendText(text:String)
{
if (text == null)
throw "cannot append null";
2021-03-13 21:11:56 -05:00
if (text == "")
return;
2021-03-13 21:11:56 -05:00
this.text = this.text + text;
}
2021-03-13 21:11:56 -05:00
/**
* Converts all characters to fit the font's `allowedCase`.
* @param text
*/
function restrictCase(text:String)
{
return switch (caseAllowed)
2021-03-13 21:11:56 -05:00
{
case Both: text;
case Upper: text.toUpperCase();
case Lower: text.toLowerCase();
}
}
2021-03-13 21:11:56 -05:00
/**
* Adds new text on top of the existing text. Helper for other methods; DOESN'T CHANGE `this.text`.
* @param text The text to add, assumed to match the font's `caseAllowed`.
*/
function appendTextCased(text:String)
{
var charCount = group.countLiving();
2021-02-23 19:55:24 -05:00
var xPos:Float = 0;
var yPos:Float = 0;
2021-03-13 21:11:56 -05:00
// `countLiving` returns -1 if group is empty
if (charCount == -1)
charCount = 0;
else if (charCount > 0)
{
var lastChar = group.members[charCount - 1];
2021-03-22 22:39:50 -04:00
xPos = lastChar.x + lastChar.width - x;
yPos = lastChar.y + lastChar.height - maxHeight - y;
2021-03-13 21:11:56 -05:00
}
2021-03-13 21:11:56 -05:00
var splitValues = text.split("");
for (i in 0...splitValues.length)
2021-02-23 19:55:24 -05:00
{
switch (splitValues[i])
2021-02-23 19:55:24 -05:00
{
case " ":
{
xPos += 40;
}
2021-02-23 19:55:24 -05:00
case "\n":
{
xPos = 0;
yPos += maxHeight;
}
2021-03-13 21:11:56 -05:00
case char:
2021-02-23 19:55:24 -05:00
{
var charSprite:AtlasChar;
if (group.members.length <= charCount)
charSprite = new AtlasChar(atlas, char);
else
{
charSprite = group.members[charCount];
charSprite.revive();
charSprite.char = char;
charSprite.alpha = 1; // gets multiplied when added
}
charSprite.x = xPos;
charSprite.y = yPos + maxHeight - charSprite.height;
add(charSprite);
xPos += charSprite.width;
charCount++;
2021-02-23 19:55:24 -05:00
}
}
}
}
2021-03-22 08:48:52 -04:00
override function toString()
{
return "InputItem, " + FlxStringUtil.getDebugString([
LabelValuePair.weak("x", x),
LabelValuePair.weak("y", y),
LabelValuePair.weak("text", text)
]);
2021-03-22 08:48:52 -04:00
}
2021-02-23 19:55:24 -05:00
}
class AtlasChar extends FlxSprite
{
public var char(default, set):String;
2021-02-23 19:55:24 -05:00
public function new(x = 0.0, y = 0.0, atlas:FlxAtlasFrames, char:String)
{
super(x, y);
frames = atlas;
this.char = char;
antialiasing = true;
}
2021-02-23 19:55:24 -05:00
function set_char(value:String)
{
if (this.char != value)
{
2021-03-13 21:11:56 -05:00
var prefix = getAnimPrefix(value);
animation.addByPrefix("anim", prefix, 24);
2021-02-23 19:55:24 -05:00
animation.play("anim");
updateHitbox();
}
2021-02-23 19:55:24 -05:00
return this.char = value;
}
2021-02-23 19:55:24 -05:00
function getAnimPrefix(char:String)
{
return switch (char)
{
case '&': return '-andpersand-';
case "😠": '-angry faic-'; // TODO: Do multi-flag characters work?
2021-02-23 19:55:24 -05:00
case "'": '-apostraphie-';
case "\\": '-back slash-';
case ",": '-comma-';
case '-': '-dash-';
case '': '-down arrow-'; // U+2193
case "": '-end quote-'; // U+0022
case "!": '-exclamation point-'; // U+0021
case "/": '-forward slash-'; // U+002F
case '>': '-greater than-'; // U+003E
case '': '-heart-'; // U+2665
case '': '-heart-';
case '': '-left arrow-'; // U+2190
case '<': '-less than-'; // U+003C
2021-02-23 19:55:24 -05:00
case "*": '-multiply x-';
case '.': '-period-'; // U+002E
case "?": '-question mark-';
case '': '-right arrow-'; // U+2192
2021-02-23 19:55:24 -05:00
case "": '-start quote-';
case '': '-up arrow-'; // U+2191
// Default to getting the character itself.
2021-02-23 19:55:24 -05:00
default: char;
}
}
}
2021-03-13 21:11:56 -05:00
private class AtlasFontData
{
static public var upperChar = ~/^[A-Z]\d+$/;
static public var lowerChar = ~/^[a-z]\d+$/;
2021-03-13 21:11:56 -05:00
public var atlas:FlxAtlasFrames;
public var maxHeight:Float = 0.0;
public var caseAllowed:Case = Both;
public function new(name:AtlasFont)
2021-03-13 21:11:56 -05:00
{
atlas = Paths.getSparrowAtlas("fonts/" + name.getName().toLowerCase());
atlas.parent.destroyOnNoUse = false;
atlas.parent.persist = true;
2021-03-13 21:11:56 -05:00
var containsUpper = false;
var containsLower = false;
2021-03-13 21:11:56 -05:00
for (frame in atlas.frames)
{
maxHeight = Math.max(maxHeight, frame.frame.height);
2021-03-13 21:11:56 -05:00
if (!containsUpper)
containsUpper = upperChar.match(frame.name);
2021-03-13 21:11:56 -05:00
if (!containsLower)
containsLower = lowerChar.match(frame.name);
}
2021-03-13 21:11:56 -05:00
if (containsUpper != containsLower)
caseAllowed = containsUpper ? Upper : Lower;
}
}
enum Case
{
Both;
Upper;
Lower;
}
2021-02-23 19:55:24 -05:00
enum AtlasFont
{
Default;
Bold;
}