using System.Numerics;
using AcDream.App.Rendering;
namespace AcDream.App.UI.Layout;
///
/// Retains the shaped line projection of one value.
/// Controllers publish semantic content; the cache reshapes only when that
/// content or the text widget's layout/font inputs change.
///
internal sealed class UiTextLayoutCache
{
private readonly UiText _target;
private readonly Func> _shape;
private readonly IEqualityComparer _comparer;
private readonly Func? _source;
private T _value = default!;
private bool _hasValue;
private bool _shaped;
private IReadOnlyList _lines = Array.Empty();
private float _width;
private float _padding;
private Vector4 _defaultColor;
private UiDatFont? _datFont;
private BitmapFont? _bitmapFont;
private IReadOnlyList? _fontColorPalette;
public UiTextLayoutCache(
UiText target,
Func> shape,
T initialValue,
IEqualityComparer? comparer = null)
: this(target, shape, source: null, comparer, initialize: true)
{
SetValue(initialValue);
}
public UiTextLayoutCache(
UiText target,
Func> shape,
Func source,
IEqualityComparer? comparer = null)
: this(
target,
shape,
source ?? throw new ArgumentNullException(nameof(source)),
comparer,
initialize: true)
{
}
private UiTextLayoutCache(
UiText target,
Func> shape,
Func? source,
IEqualityComparer? comparer,
bool initialize)
{
_ = initialize;
_target = target ?? throw new ArgumentNullException(nameof(target));
_shape = shape ?? throw new ArgumentNullException(nameof(shape));
_source = source;
_comparer = comparer ?? EqualityComparer.Default;
}
public Func> Provider => GetLines;
public void SetValue(T value)
{
if (_hasValue && _comparer.Equals(_value, value))
return;
_value = value;
_hasValue = true;
_shaped = false;
}
public void Invalidate() => _shaped = false;
public IReadOnlyList GetLines()
{
if (_source is not null)
SetValue(_source());
if (!_hasValue)
return Array.Empty();
if (!_shaped || LayoutChanged())
{
CaptureLayout();
_lines = _shape(_target, _value);
_shaped = true;
}
return _lines;
}
private bool LayoutChanged()
=> _width != _target.Width
|| _padding != _target.Padding
|| _defaultColor != _target.DefaultColor
|| !ReferenceEquals(_datFont, _target.DatFont)
|| !ReferenceEquals(_bitmapFont, _target.Font)
|| !ReferenceEquals(_fontColorPalette, _target.FontColorPalette);
private void CaptureLayout()
{
_width = _target.Width;
_padding = _target.Padding;
_defaultColor = _target.DefaultColor;
_datFont = _target.DatFont;
_bitmapFont = _target.Font;
_fontColorPalette = _target.FontColorPalette;
}
}