Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
using FontStashSharp;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Theriapolis.Game.CodexUI.Core;
|
||||
|
||||
namespace Theriapolis.Game.CodexUI.Widgets;
|
||||
|
||||
public enum CodexButtonVariant { Primary, Ghost, Small }
|
||||
|
||||
/// <summary>
|
||||
/// Codex-styled push button. The three variants match the React design's
|
||||
/// <c>.btn.primary / .btn.ghost / .btn.small</c>:
|
||||
/// - Primary: gilded fill on parchment, used for Confirm + Next-style actions.
|
||||
/// - Ghost: ink border, transparent fill, used for Back + secondary actions.
|
||||
/// - Small: smaller padding/font, used inline (Reroll / Auto-assign / Clear).
|
||||
/// </summary>
|
||||
public sealed class CodexButton : CodexWidget
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public CodexButtonVariant Variant { get; set; }
|
||||
public System.Action? OnClick { get; set; }
|
||||
public int? FixedWidth { get; set; }
|
||||
|
||||
private readonly CodexAtlas _atlas;
|
||||
private readonly SpriteFontBase _font;
|
||||
private bool _hovered;
|
||||
private bool _pressed;
|
||||
|
||||
public CodexButton(string text, CodexAtlas atlas, CodexButtonVariant variant = CodexButtonVariant.Ghost,
|
||||
System.Action? onClick = null, int? fixedWidth = null)
|
||||
{
|
||||
Text = text;
|
||||
_atlas = atlas;
|
||||
Variant = variant;
|
||||
OnClick = onClick;
|
||||
FixedWidth = fixedWidth;
|
||||
_font = variant == CodexButtonVariant.Small ? CodexFonts.MonoTag : CodexFonts.DisplaySmall;
|
||||
}
|
||||
|
||||
protected override Point MeasureCore(Point available)
|
||||
{
|
||||
var s = _font.MeasureString(Text);
|
||||
int padX = Variant == CodexButtonVariant.Small ? 12 : 22;
|
||||
int padY = Variant == CodexButtonVariant.Small ? 6 : 10;
|
||||
int w = FixedWidth ?? ((int)s.X + padX * 2);
|
||||
int h = (int)System.MathF.Ceiling(_font.LineHeight) + padY * 2;
|
||||
return new Point(System.Math.Min(w, available.X), h);
|
||||
}
|
||||
|
||||
protected override void ArrangeCore(Rectangle bounds) { }
|
||||
|
||||
public override void Update(GameTime gt, CodexInput input)
|
||||
{
|
||||
bool wasHovered = _hovered;
|
||||
_hovered = ContainsPoint(input.MousePosition);
|
||||
if (_hovered && input.LeftJustPressed) _pressed = true;
|
||||
if (input.LeftJustReleased)
|
||||
{
|
||||
if (_pressed && _hovered && Enabled) OnClick?.Invoke();
|
||||
_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch sb, GameTime gt)
|
||||
{
|
||||
Color fill, border, textColor;
|
||||
switch (Variant)
|
||||
{
|
||||
case CodexButtonVariant.Primary:
|
||||
fill = _hovered ? CodexColors.Seal2 : CodexColors.Seal;
|
||||
border = CodexColors.Seal2;
|
||||
textColor = CodexColors.Bg;
|
||||
break;
|
||||
case CodexButtonVariant.Ghost:
|
||||
fill = _hovered ? CodexColors.Ink : CodexColors.Bg;
|
||||
border = CodexColors.Ink;
|
||||
textColor = _hovered ? CodexColors.Bg : CodexColors.Ink;
|
||||
break;
|
||||
default:
|
||||
fill = _hovered ? CodexColors.Bg2 : CodexColors.Bg;
|
||||
border = CodexColors.Rule;
|
||||
textColor = CodexColors.InkSoft;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
// 40% opacity per .btn[disabled]
|
||||
fill = new Color(fill.R, fill.G, fill.B, (byte)(fill.A * 0.4f));
|
||||
textColor = new Color(textColor.R, textColor.G, textColor.B, (byte)(textColor.A * 0.6f));
|
||||
}
|
||||
|
||||
// Body fill
|
||||
sb.Draw(_atlas.Pixel, Bounds, fill);
|
||||
// 1-px border outline
|
||||
DrawBorder(sb, Bounds, border, 1);
|
||||
|
||||
var s = _font.MeasureString(Text);
|
||||
float tx = Bounds.X + (Bounds.Width - s.X) / 2f;
|
||||
float ty = Bounds.Y + (Bounds.Height - _font.LineHeight) / 2f;
|
||||
_font.DrawText(sb, Text, new Vector2(tx, ty), textColor);
|
||||
}
|
||||
|
||||
private void DrawBorder(SpriteBatch sb, Rectangle r, Color c, int t)
|
||||
{
|
||||
sb.Draw(_atlas.Pixel, new Rectangle(r.X, r.Y, r.Width, t), c);
|
||||
sb.Draw(_atlas.Pixel, new Rectangle(r.X, r.Bottom - t, r.Width, t), c);
|
||||
sb.Draw(_atlas.Pixel, new Rectangle(r.X, r.Y, t, r.Height), c);
|
||||
sb.Draw(_atlas.Pixel, new Rectangle(r.Right - t, r.Y, t, r.Height), c);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user