Editor/Tileset/TilesetTileDataWidget.cs

Editor UI widget that shows and edits per-tile data for a TilesetResource. It displays title, tile rect info, and a property sheet for the selected TileDefinition, and notifies a callback when tags change.

File Access
using Editor;
using Saandy.Tilemapper;
using Sandbox;
using System;

namespace Saandy.Editor.Tilemapper;

public sealed class TilesetTileDataWidget : Widget
{
	private readonly TilesetResource _tileset;
	private readonly Action _onChanged;

	private int _selectedTileIndex = -1;

	public float PreferredContentHeight => GetSelectedTile() == null ? 96.0f : 154.0f;

	public TilesetTileDataWidget( Widget parent, TilesetResource tileset, Action onChanged ) : base( parent )
	{
		_tileset = tileset;
		_onChanged = onChanged;

		Name = "Tile Data";
		MinimumHeight = 96;
		MouseTracking = true;
		HorizontalSizeMode = SizeMode.Flexible;
		VerticalSizeMode = SizeMode.CanShrink;

		Layout = Layout.Column();
		Layout.Margin = 10;
		Layout.Spacing = 8;

		RefreshFromData();
	}

	protected override void OnPaint()
	{
		base.OnPaint();

		Paint.ClearPen();
		Paint.SetBrush( Color.Black.WithAlpha( 0.30f ) );
		Paint.DrawRect( LocalRect, 4.0f );

		Paint.SetPen( Color.White.WithAlpha( 0.13f ), 1.0f );
		Paint.ClearBrush();
		Paint.DrawRect( LocalRect.Shrink( 0.5f ), 4.0f );
	}

	public void SetSelectedTileIndex( int selectedTileIndex )
	{
		_selectedTileIndex = selectedTileIndex;
		RefreshFromData();
	}

	public void RefreshFromData()
	{
		if ( Layout == null )
			return;

		Layout.Clear( true );
		Layout.Margin = 10;
		Layout.Spacing = 8;

		_tileset?.EnsureTagSets();

		var title = new Label( GetTitleText(), this );
		title.SetStyles( "font-weight: 700; font-size: 16px;" );
		Layout.Add( title );

		var tile = GetSelectedTile();

		if ( tile == null )
		{
			var hint = new Label( "Click a rect in the source image to edit per-tile data.", this );
			hint.WordWrap = true;
			hint.SetStyles( "color: #bbb;" );
			Layout.Add( hint );
			Update();
			return;
		}

		tile.Tags ??= new TagSet();

		var info = new Label( $"Rect: {tile.SourceRect.Left:0}, {tile.SourceRect.Top:0}  {tile.SourceRect.Width:0}×{tile.SourceRect.Height:0}", this );
		info.SetStyles( "color: #aaa; font-size: 12px;" );
		Layout.Add( info );

		var serializedTile = tile.GetSerialized();

		var sheet = new ControlSheet();
		sheet.AddObject( serializedTile, prop =>
		{
			if ( prop.HasAttribute<HideAttribute>() )
				return false;

			return prop.Name == nameof( TileDefinition.Tags );
		} );

		serializedTile.OnPropertyChanged += prop =>
		{
			if ( prop == null || prop.Name == nameof( TileDefinition.Tags ) )
				_onChanged?.Invoke();
		};

		Layout.Add( sheet );

		Update();
	}

	private string GetTitleText()
	{
		if ( _selectedTileIndex < 0 )
			return "Tile Data";

		return $"Tile Data - Tile {_selectedTileIndex}";
	}

	private TileDefinition GetSelectedTile()
	{
		if ( _tileset?.Tiles == null )
			return null;

		if ( _selectedTileIndex < 0 || _selectedTileIndex >= _tileset.Tiles.Count )
			return null;

		return _tileset.Tiles[_selectedTileIndex];
	}
}