// Copyright (c) Microsoft. All rights reserved. namespace Harness.ConsoleReactiveFramework; /// /// Abstract base class for all console UI components. Provides layout properties /// (position and size) and a method for drawing to the console. /// Derive from instead of this class directly. /// public abstract class ConsoleReactiveComponent { internal ConsoleReactiveComponent() { } /// Gets or sets the 1-based column position of the component. public int X { get; set; } /// Gets or sets the 1-based row position of the component. public int Y { get; set; } /// Gets or sets the width of the component in columns. public int Width { get; set; } /// Gets or sets the height of the component in rows. public int Height { get; set; } /// Renders the component to the console at its current position. public abstract void Render(); } /// /// Generic base class for console UI components with typed props and state. /// Props represent externally supplied configuration; state represents internal mutable data. /// /// The type of the component's props (external configuration). /// The type of the component's internal state. public abstract class ConsoleReactiveComponent : ConsoleReactiveComponent where TProps : ConsoleReactiveProps where TState : ConsoleReactiveState { private readonly object _renderLock = new(); private TProps? _lastRenderedProps; private TState? _lastRenderedState; /// Gets or sets the component's props (external configuration). public TProps? Props { get; set; } /// Gets or sets the component's internal state. protected TState? State { get; set; } /// /// Updates the component's state and triggers a re-render. /// /// The new state value. public void SetState(TState newState) { this.State = newState; this.Render(); } /// /// Renders the component using the current props and state. /// Uses a lock to prevent concurrent renders from multiple sources. /// Skips rendering if neither props nor state have changed since the last render. /// public override void Render() { lock (this._renderLock) { if (this.Props is null) { return; } if (ReferenceEquals(this.Props, this._lastRenderedProps) && ReferenceEquals(this.State, this._lastRenderedState)) { return; } this.RenderCore(this.Props, this.State!); this._lastRenderedProps = this.Props; this._lastRenderedState = this.State; } } /// /// Called by to perform the actual rendering. Override this in derived classes. /// /// The current props. /// The current state. public abstract void RenderCore(TProps props, TState state); } /// /// Base record for component props. Provides an optional collection /// for composing child components. /// public record ConsoleReactiveProps { /// Gets the child components to render within this component. public IReadOnlyList Children { get; init; } = []; } /// /// Base record for component state. /// public record ConsoleReactiveState;