mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-03 02:27:08 +02:00
* Shared/Utility: Define new FormattedMessage core types * Shared/Utility: Move MarkupParser to a new namespace, update to new FormattedText * Shared/Serialization: Temporary fix for FormattedMessageSerializer * Scripting/ScriptInstanceShared: Move to new FormattedMessage.Builder * Shared/Utility: Add a FormattedMessage loader to the .Builder * Server/Scripting: Port SciptHost to FormattedMessage.Builder * UserInterface/RichTextEntry: NOP out almost everything not gonna bother fixing it until more groundwork is laid * Shared/Utility: Expand Utility.Extensions a bit strictly for pesonal reasons * Client/UserInterface: Add the base TextLayout engine * Client/Graphics: Add a Font Library manager * Graphics/TextLayout: Finish up implementing the TextLayout engine * Utility/FormattedMessage: Add yet another hack to keep the serializer in service * Commands/Debug: Use FormattedMessage.Builder * Console/Completions: Use FormattedMessage.Builder * Utility/FormattedMessage: Add `AddMessage` methods * Console/ScriptConsole: Use FormattedMessage.Builder * Client/Log: Use FormattedMessage.Builder * CustomControls/DebugConsole: Use FormattedMessage.Builder * Controls/OutputPanel: Use FormattedMessage.Builder, NOP `Draw` pending rewrite * Controls/RichTextLabel: Use FormattedMessage.Builder, NOP `Draw` pending rewrite * UnitTesting: Update FormattedMessage/Markup Tests They will NOT pass yet, but I don't care; it compiles. * Utility/FormattedMessage: Fix some off-by-one Builder bugs * Utility/FormattedMessage: Continue cleanup, test compliance * Utility/FormattedMessage: Work around https://github.com/dotnet/roslyn/issues/57870 * Utility/FormattedMessage: Move ISectionable from TextLayout, implement it for FormattedMessage * UserInterface/TextLayout: Add a `postcreate` function to set up new `TIn`s Apparently Roslyn isn't big-brained enough to understand that a closure of type `Func<T>` means that `var n = new(); return n;` requires `new()` to return a `T`. Ironically, it's significantly less cbt to add this than to convert that big tuple in `Layout` in to a class or struct of some sort (to initialize the `List<>`s). * UserInterface/TextLayout: Throw if `Meta` isn't recognized TODO warning go brrr * Graphics/FontLibrary: Add a `DummyVariant` * UserInterface/UITheme: Move to FontLibraries * UserInterface/TextLayout: Move to an un-nested `ImmutableArray` * UserInterface/RichTextEntry: Go ahead. Draw. * Markup/Basic: Add extension & helpers for FormattedMessage.Builder * Markup/Basic: Add `EscapeText` back in A forgotten casualty of the great Markup separation of 2021 * Graphics/FontLibrary: Clean up bit magic, ensure that at least one font is picked * Graphics/FontLibrary: Add diagnostics to the "no fonts" exception * UserInterface/TextLayout: Scrap `Word`, return to `Offset` * UserInterface/TextLayout: A whole bunch of hard-fought bugfixes * Utility/FormattedMessage: Add a static, empty FormattedMessage * Utility/FormattedMessage: Fix. Bugs. * UserInterface/RichTextEntry: Bug fixin' * UserInterface: CSS teim * Markup/Basic: Add an optional "default" style to use * Utility/FormattedMessage: I'm surprised I only made this mistake once. * Log/DebugConsoleLogHandler: work around lack of a default style
186 lines
5.5 KiB
C#
186 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using Robust.Shared.Utility;
|
|
using Robust.Client.ResourceManagement;
|
|
namespace Robust.Client.Graphics;
|
|
|
|
/// <summary>
|
|
/// Stores a single style (Bold, Italic, Monospace), or any combination thereof.
|
|
/// </summary>
|
|
public record FontVariant (FontStyle Style, FontResource[] Resource)
|
|
{
|
|
public virtual Font ToFont(byte size)
|
|
{
|
|
if (Resource.Length == 1)
|
|
return new VectorFont(Resource[0], size);
|
|
|
|
var fs = new Font[Resource.Length];
|
|
for (var i = 0; i < Resource.Length; i++)
|
|
fs[i] = new VectorFont(Resource[i], size);
|
|
|
|
return new StackedFont(fs);
|
|
}
|
|
};
|
|
|
|
internal record DummyVariant(FontStyle fs) : FontVariant(fs, new FontResource[0])
|
|
{
|
|
public override Font ToFont(byte size) => new DummyFont();
|
|
};
|
|
|
|
public record FontClass
|
|
(
|
|
string Id,
|
|
FontStyle Style,
|
|
FontSize Size
|
|
);
|
|
|
|
/// <summary>
|
|
/// Manages font-based bookkeeping across a single stylesheet.
|
|
/// </summary>
|
|
public interface IFontLibrary
|
|
{
|
|
FontClass Default { get; }
|
|
|
|
/// <summary>Associates a name to a set of font resources.</summary>
|
|
void AddFont(string name, params FontVariant[] variants);
|
|
|
|
/// <summary>Sets a standard size which can be reused across the Font Library.</summary>
|
|
void SetStandardSize(ushort number, byte size);
|
|
|
|
/// <summary>Sets a standard style which can be reused across the Font Library.</summary>
|
|
void SetStandardStyle(ushort number, string name, FontStyle style);
|
|
|
|
/// <summary>
|
|
/// Returns a fancy handle in to the library.
|
|
/// The handle keeps track of relative changes to <paramref name="fst"/> and <paramref name="fsz"/>.
|
|
/// </summary>
|
|
IFontLibrarian StartFont(string id, FontStyle fst, FontSize fsz);
|
|
|
|
IFontLibrarian StartFont(FontClass? fclass = default) =>
|
|
StartFont(
|
|
(fclass ?? Default).Id,
|
|
(fclass ?? Default).Style,
|
|
(fclass ?? Default).Size
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acts as a handle in to an <seealso cref="IFontLibrary"/>.
|
|
/// </summary>
|
|
public interface IFontLibrarian
|
|
{
|
|
Font Current { get; }
|
|
Font Update(FontStyle fst, FontSize fsz);
|
|
}
|
|
|
|
public class FontLibrary : IFontLibrary
|
|
{
|
|
public FontClass Default { get; set; }
|
|
|
|
public FontLibrary(FontClass def)
|
|
{
|
|
Default = def;
|
|
}
|
|
|
|
private Dictionary<string, FontVariant[]> _styles = new();
|
|
private Dictionary<FontStyle, (string, FontStyle)> _standardSt = new();
|
|
private Dictionary<FontSize, byte> _standardSz = new();
|
|
|
|
void IFontLibrary.AddFont(string name, params FontVariant[] variants) =>
|
|
_styles[name] = variants;
|
|
|
|
IFontLibrarian IFontLibrary.StartFont(string id, FontStyle fst, FontSize fsz) =>
|
|
new FontLibrarian(this, id, fst, fsz);
|
|
|
|
void IFontLibrary.SetStandardStyle(ushort number, string name, FontStyle style) =>
|
|
_standardSt[(FontStyle) number | FontStyle.Standard] = (name, style);
|
|
|
|
void IFontLibrary.SetStandardSize(ushort number, byte size) =>
|
|
_standardSz[(FontSize) number | FontSize.Standard] = size;
|
|
|
|
private FontVariant lookup(string id, FontStyle fst)
|
|
{
|
|
if (fst.HasFlag(FontStyle.Standard))
|
|
(id, fst) = _standardSt[fst];
|
|
|
|
FontVariant? winner = default;
|
|
foreach (var vr in _styles[id])
|
|
{
|
|
var winfst = winner?.Style ?? ((FontStyle) 0);
|
|
|
|
// Since the "style" flags are a bitfield, we can just see which one has more bits.
|
|
// More bits == closer to the desired font style. Free fallback!
|
|
|
|
// Variant's bit count
|
|
var vc = BitOperations.PopCount((ulong) (vr.Style & fst));
|
|
// Winner's bit count
|
|
var wc = BitOperations.PopCount((ulong) (winfst & fst));
|
|
|
|
if (winner is null || vc > wc)
|
|
winner = vr;
|
|
}
|
|
|
|
if (winner is null)
|
|
throw new Exception($"no matching font style ({id}, {fst})");
|
|
|
|
return winner;
|
|
}
|
|
|
|
private byte lookupSz(FontSize sz)
|
|
{
|
|
if (sz.HasFlag(FontSize.RelMinus) || sz.HasFlag(FontSize.RelPlus))
|
|
throw new Exception("can't look up a relative font through a library; get a Librarian first");
|
|
|
|
if (sz.HasFlag(FontSize.Standard))
|
|
return _standardSz[sz];
|
|
|
|
return (byte) sz;
|
|
}
|
|
|
|
class FontLibrarian : IFontLibrarian
|
|
{
|
|
public Font Current => _current;
|
|
private Font _current;
|
|
|
|
private FontLibrary _lib;
|
|
private string _id;
|
|
private FontStyle _fst;
|
|
private FontSize _fsz;
|
|
|
|
public FontLibrarian(FontLibrary lib, string id, FontStyle fst, FontSize fsz)
|
|
{
|
|
_id = id;
|
|
_fst = fst;
|
|
_fsz = fsz;
|
|
_lib = lib;
|
|
|
|
// Actual font entry
|
|
var f = lib.lookup(id, fst);
|
|
|
|
// Real size
|
|
var rsz = (byte) lib.lookupSz(fsz);
|
|
_current = f.ToFont(rsz);
|
|
}
|
|
|
|
Font IFontLibrarian.Update(FontStyle fst, FontSize fsz)
|
|
{
|
|
var f = _lib.lookup(_id, fst);
|
|
|
|
byte rsz = (byte) _fsz;
|
|
var msk = (byte) fsz & 0b0000_1111;
|
|
if (fsz.HasFlag(FontSize.Standard))
|
|
rsz = _lib.lookupSz(fsz);
|
|
else if (fsz.HasFlag(FontSize.RelPlus))
|
|
rsz = (byte) (((byte) _fsz) + msk);
|
|
else if (fsz.HasFlag(FontSize.RelMinus))
|
|
rsz = (byte) (((byte) _fsz) - msk);
|
|
|
|
_fsz = (FontSize) rsz;
|
|
_fst = fst;
|
|
|
|
return _current = f.ToFont((byte) rsz);
|
|
}
|
|
}
|
|
}
|