Skip to content

Create KeenEyes.UI.Abstractions project #417

@tyevco

Description

@tyevco

Summary

Create a new KeenEyes.UI.Abstractions project that defines UI component types, events, and layout contracts. This establishes the data model for the UI system without implementation details.

Parent: #416
Depends on: #412 (Graphics.Abstractions for FontHandle, TextureHandle)

Proposed Structure

src/KeenEyes.UI.Abstractions/
├── KeenEyes.UI.Abstractions.csproj
├── Components/
│   ├── UIElement.cs               # Base marker + z-index + visibility
│   ├── UIRect.cs                  # Position, size, computed bounds
│   ├── UIStyle.cs                 # Background, border, corner radius, text color
│   ├── UIText.cs                  # Text content, font handle, alignment
│   ├── UIImage.cs                 # Texture handle, scale mode, tint
│   ├── UIInteractable.cs          # IsHovered, IsPressed, IsFocused, JustClicked
│   ├── UIScrollable.cs            # Scroll offset, content size
│   └── UILayout.cs                # Flexbox: direction, gap, padding, alignment
├── Tags/
│   ├── UIRootTag.cs               # Marks layout root elements
│   ├── UIDisabledTag.cs           # Marks disabled elements
│   └── UIHiddenTag.cs             # Marks hidden elements (skip render)
├── Events/
│   ├── UIClickEvent.cs            # Entity that was clicked
│   ├── UIHoverEnterEvent.cs       # Mouse entered element
│   ├── UIHoverExitEvent.cs        # Mouse left element
│   ├── UIFocusEvent.cs            # Element gained focus
│   ├── UIBlurEvent.cs             # Element lost focus
│   ├── UIScrollEvent.cs           # Scroll position changed
│   └── UITextInputEvent.cs        # Character typed in focused text field
├── Layout/
│   ├── LayoutDirection.cs         # Row, Column, RowReverse, ColumnReverse
│   ├── LayoutAlign.cs             # Start, Center, End, Stretch, SpaceBetween
│   ├── LayoutJustify.cs           # Start, Center, End, SpaceBetween, SpaceAround
│   └── LayoutConstraints.cs       # MinWidth, MaxWidth, PreferredWidth, etc.
├── Styling/
│   ├── TextAlignment.cs           # Left, Center, Right
│   ├── ImageScaleMode.cs          # Stretch, Fit, Fill, NineSlice
│   └── Color.cs                   # RGBA color struct (if not in Common)
└── Extensions/
    └── IUIContext.cs              # Public UI API interface

Acceptance Criteria

Project Setup

  • Create KeenEyes.UI.Abstractions.csproj targeting net10.0
  • Reference KeenEyes.Abstractions (for [Component], Entity, etc.)
  • Reference KeenEyes.Graphics.Abstractions (for FontHandle, TextureHandle)
  • Enable nullable reference types
  • Enable TreatWarningsAsErrors
  • Mark as <IsAotCompatible>true</IsAotCompatible>
  • Add to solution file

Core Components

UIElement

[Component]
public partial struct UIElement
{
    public int ZIndex;           // Render order (higher = on top)
    public bool Visible;         // Whether to render (default true)
}

UIRect

[Component]
public partial struct UIRect
{
    // Input (set by user or parent layout)
    public float X, Y;           // Position relative to parent
    public float Width, Height;  // Requested size

    // Output (computed by UILayoutSystem)
    public float ComputedX, ComputedY;         // Absolute screen position
    public float ComputedWidth, ComputedHeight; // Final size after layout
}

UIStyle

[Component]
public partial struct UIStyle
{
    public Color BackgroundColor;
    public Color BorderColor;
    public float BorderWidth;
    public float CornerRadius;
    public Color TextColor;
    public float FontSize;
    public float Opacity;        // 0-1, affects entire element
}

UIText

[Component]
public partial struct UIText
{
    public string Content;
    public FontHandle Font;      // From Graphics.Abstractions
    public TextAlignment Alignment;
    public bool WordWrap;
}

UIImage

[Component]
public partial struct UIImage
{
    public TextureHandle Texture; // From Graphics.Abstractions
    public ImageScaleMode ScaleMode;
    public Color Tint;
    public RectF? SourceRect;    // For texture atlases
}

UIInteractable

[Component]
public partial struct UIInteractable
{
    // State flags (set by UIInputSystem)
    public bool IsHovered;       // Mouse is over element
    public bool IsPressed;       // Mouse button held on element
    public bool IsFocused;       // Has keyboard focus

    // Frame flags (true for one frame only)
    public bool JustClicked;     // Click completed this frame
    public bool JustPressed;     // Press started this frame
    public bool JustReleased;    // Press ended this frame
    public bool JustFocused;     // Focus gained this frame
    public bool JustBlurred;     // Focus lost this frame
}

UILayout

[Component]
public partial struct UILayout
{
    public LayoutDirection Direction;  // Row or Column
    public float Gap;                  // Space between children
    public float PaddingTop, PaddingRight, PaddingBottom, PaddingLeft;
    public LayoutAlign AlignItems;     // Cross-axis alignment
    public LayoutJustify JustifyContent; // Main-axis distribution
}

UIScrollable

[Component]
public partial struct UIScrollable
{
    public float ScrollX, ScrollY;           // Current scroll offset
    public float ContentWidth, ContentHeight; // Total content size
    public bool ScrollHorizontal;            // Allow horizontal scroll
    public bool ScrollVertical;              // Allow vertical scroll
}

Tag Components

  • UIRootTag - Marks elements that are layout roots
  • UIDisabledTag - Marks disabled elements (no interaction)
  • UIHiddenTag - Marks elements to skip during render

Event Types

All events are readonly record structs:

public readonly record struct UIClickEvent(Entity Entity, MouseButton Button);
public readonly record struct UIHoverEnterEvent(Entity Entity);
public readonly record struct UIHoverExitEvent(Entity Entity);
public readonly record struct UIFocusEvent(Entity Entity);
public readonly record struct UIBlurEvent(Entity Entity);
public readonly record struct UIScrollEvent(Entity Entity, float DeltaX, float DeltaY);
public readonly record struct UITextInputEvent(Entity Entity, char Character);

Layout Enums

  • LayoutDirection - Row, Column, RowReverse, ColumnReverse
  • LayoutAlign - Start, Center, End, Stretch
  • LayoutJustify - Start, Center, End, SpaceBetween, SpaceAround, SpaceEvenly
  • TextAlignment - Left, Center, Right
  • ImageScaleMode - Stretch, Fit, Fill, NineSlice, None

IUIContext Interface

/// <summary>
/// Public API for UI operations exposed as a world extension.
/// </summary>
public interface IUIContext
{
    // Focus management
    Entity? FocusedElement { get; }
    void SetFocus(Entity entity);
    void ClearFocus();
    void FocusNext();      // Tab navigation
    void FocusPrevious();  // Shift+Tab navigation

    // Hit testing
    Entity? GetElementAt(float x, float y);
    IEnumerable<Entity> GetElementsAt(float x, float y);

    // Convenience event binding (optional, for app code)
    void OnClick(Entity entity, Action callback);
    void OnHover(Entity entity, Action<bool> callback);
}

Documentation

  • XML docs on all public types
  • README.md explaining component purposes and usage patterns
  • Examples of creating common UI patterns (button, panel, list)

Design Decisions

Why readonly record structs for events?

  • Zero allocation
  • Immutable
  • Value semantics for ECS messaging

Why separate UIRect computed fields?

  • User sets requested position/size
  • Layout system computes actual position/size
  • Clear separation of input vs output

Why flags in UIInteractable instead of just events?

  • Systems can query current state without subscribing
  • "JustClicked" pattern is common and efficient
  • Both polling and events supported

Why FontHandle/TextureHandle from Graphics.Abstractions?

  • UI renders text and images
  • Handles are the abstraction for GPU resources
  • No direct graphics API coupling

Technical Notes

  • All components use [Component] attribute for source generation
  • All tags use [TagComponent] attribute
  • No behavior in components - pure data
  • Events sent via World.Send<T>() from UI systems
  • Layout algorithm based on simplified Flexbox model

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions