96 lines
3.7 KiB
C#
96 lines
3.7 KiB
C#
using System.Numerics;
|
|
|
|
namespace AcDream.App.UI;
|
|
|
|
/// <summary>
|
|
/// Lightweight, data-bound string list for plugin markup. It owns only row
|
|
/// selection and scroll position; the plugin binding remains the sole owner of
|
|
/// rows and selected index. This deliberately avoids exposing App widget types
|
|
/// through the BCL plugin contract.
|
|
/// </summary>
|
|
public sealed class UiMarkupList : UiElement
|
|
{
|
|
public Func<IReadOnlyList<string>> ItemsSource { get; set; } =
|
|
static () => Array.Empty<string>();
|
|
public Func<IReadOnlyList<uint>> ItemColorsSource { get; set; } =
|
|
static () => Array.Empty<uint>();
|
|
public Func<int> SelectedIndexSource { get; set; } = static () => -1;
|
|
public Action<int>? SelectionChanged { get; set; }
|
|
public UiDatFont? DatFont { get; set; }
|
|
public float RowHeight { get; set; } = 18f;
|
|
public float Padding { get; set; } = 3f;
|
|
public Vector4 BackgroundColor { get; set; } = new(0f, 0f, 0f, 0.92f);
|
|
public Vector4 BorderColor { get; set; } = new(0.46f, 0.37f, 0.16f, 1f);
|
|
public Vector4 TextColor { get; set; } = new(0.91f, 0.87f, 0.76f, 1f);
|
|
public Vector4 SelectedColor { get; set; } = new(0.28f, 0.23f, 0.08f, 0.95f);
|
|
|
|
private int _topRow;
|
|
|
|
public override bool HandlesClick => true;
|
|
|
|
protected override void OnDraw(UiRenderContext context)
|
|
{
|
|
IReadOnlyList<string> items = ItemsSource();
|
|
IReadOnlyList<uint> itemColors = ItemColorsSource();
|
|
int visibleRows = VisibleRows;
|
|
int selected = SelectedIndexSource();
|
|
if (selected >= 0 && selected < items.Count)
|
|
{
|
|
if (selected < _topRow)
|
|
_topRow = selected;
|
|
else if (selected >= _topRow + visibleRows)
|
|
_topRow = selected - visibleRows + 1;
|
|
}
|
|
ClampTop(items.Count, visibleRows);
|
|
|
|
context.DrawFill(0f, 0f, Width, Height, BackgroundColor);
|
|
context.DrawRectOutline(0f, 0f, Width, Height, BorderColor, 1f);
|
|
int end = Math.Min(items.Count, _topRow + visibleRows);
|
|
for (int index = _topRow; index < end; index++)
|
|
{
|
|
float y = (index - _topRow) * RowHeight;
|
|
if (index == selected)
|
|
context.DrawFill(1f, y + 1f, Width - 2f, RowHeight - 1f, SelectedColor);
|
|
string text = items[index];
|
|
Vector4 textColor = index < itemColors.Count
|
|
? Rgb(itemColors[index])
|
|
: TextColor;
|
|
float textY = y + MathF.Max(0f,
|
|
(RowHeight - (DatFont?.LineHeight ?? 14f)) * 0.5f);
|
|
if (DatFont is { } font)
|
|
context.DrawStringDat(font, text, Padding, textY, textColor, true);
|
|
else
|
|
context.DrawString(text, Padding, textY, textColor);
|
|
}
|
|
}
|
|
|
|
public override bool OnEvent(in UiEvent e)
|
|
{
|
|
IReadOnlyList<string> items = ItemsSource();
|
|
if (e.Type == UiEventType.Scroll)
|
|
{
|
|
_topRow -= Math.Sign(e.Data0);
|
|
ClampTop(items.Count, VisibleRows);
|
|
return true;
|
|
}
|
|
if (e.Type != UiEventType.MouseDown || !Enabled)
|
|
return false;
|
|
int row = (int)MathF.Floor(e.Data2 / MathF.Max(1f, RowHeight));
|
|
int index = _topRow + row;
|
|
if (row >= 0 && row < VisibleRows && index >= 0 && index < items.Count)
|
|
SelectionChanged?.Invoke(index);
|
|
return true;
|
|
}
|
|
|
|
private int VisibleRows => Math.Max(1, (int)MathF.Floor(
|
|
Height / MathF.Max(1f, RowHeight)));
|
|
|
|
private void ClampTop(int count, int visibleRows) =>
|
|
_topRow = Math.Clamp(_topRow, 0, Math.Max(0, count - visibleRows));
|
|
|
|
private static Vector4 Rgb(uint value) => new(
|
|
((value >> 16) & 0xFFu) / 255f,
|
|
((value >> 8) & 0xFFu) / 255f,
|
|
(value & 0xFFu) / 255f,
|
|
1f);
|
|
}
|