using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem.UI;
#endif
///
/// In-game paint UI. Shows its panels only while the active is
/// (it follows the manager's ModeChanged event) and binds its
/// widgets directly to the live fields, so edits apply immediately.
///
/// The layout is a transparent root holding one independently positioned sub-panel per channel
/// (Albedo, Metallic+Smoothness, Occlusion, Emission) plus a global Brush card, each with a title
/// bar and draggable from any of its non-widget surface. The layout comes from one of two sources:
/// - an authored prefab assigned to (instantiated, then bound), or
/// - generated in code (the fallback when no prefab is set).
/// Either way the *bindings* (listeners, picker gradient sprites, painter values) live in code
/// and are (re)applied at runtime by . A prefab stores only the visual
/// layout, never the behaviour, so both paths run the exact same wiring against the same stable
/// widget names - there is no separate "dead" prefab path.
///
/// Widgets: channel toggles, an HSV colour picker for albedo, metallic / smoothness / occlusion
/// sliders, an HSV picker + intensity for emission, and brush size. Attach to an empty GameObject.
///
[DisallowMultipleComponent]
public partial class PaintHud : MonoBehaviour
{
[Tooltip("Mode manager driving visibility. Empty = found automatically.")]
public GameModeManager modeManager;
[Tooltip("Painter the HUD edits. Empty = the manager's painter, else found automatically.")]
public MeshPainter painter;
[Tooltip("Authored panel layout. If set it is instantiated and bound; empty = build the panel in code.")]
public GameObject layoutPrefab;
// Stable widget names shared by the code-gen builder, the baked prefab and the binder.
private const string PanelSpray = "Panel_Spray";
private const string PanelAlbedo = "Panel_Albedo";
private const string PanelMetallicSmoothness = "Panel_MetallicSmoothness";
private const string PanelOcclusion = "Panel_Occlusion";
private const string PanelEmission = "Panel_Emission";
private const string PanelBrushSize = "Panel_BrushSize";
private const string ToggleAlbedo = "Toggle_Albedo";
private const string ToggleMetallicSmoothness = "Toggle_MetallicSmoothness";
private const string ToggleOcclusion = "Toggle_Occlusion";
private const string ToggleEmission = "Toggle_Emission";
private const string ToggleSpray = "Toggle_Spray";
private const string TogglePipette = "Toggle_Pipette";
private const string PickerAlbedo = "Picker_Albedo";
private const string PickerEmission = "Picker_Emission";
private const string SliderMetallic = "Slider_Metallic";
private const string SliderSmoothness = "Slider_Smoothness";
private const string SliderMetallicSmoothnessAlpha = "Slider_MetallicSmoothnessAlpha";
private const string SliderOcclusion = "Slider_Occlusion";
private const string SliderOcclusionAlpha = "Slider_OcclusionAlpha";
private const string SliderEmissionIntensity = "Slider_EmissionIntensity";
private const string SliderBrushSize = "Slider_BrushSize";
private const string SliderSprayDots = "Slider_SprayDots";
private const string SliderSprayDotSizeMin = "Slider_SprayDotSizeMin";
private const string SliderSprayDotSizeMax = "Slider_SprayDotSizeMax";
private const string SliderSprayDotAlphaMin = "Slider_SprayDotAlphaMin";
private const string SliderSprayDotAlphaMax = "Slider_SprayDotAlphaMax";
private const string MaskSelector = "MaskSelector";
private const string MaskSwatchPrefix = "MaskSwatch_";
// Panels made draggable at runtime - grabbable from any non-widget surface (both prefab and code-gen paths).
private static readonly string[] DraggablePanels =
{
PanelAlbedo,
PanelMetallicSmoothness,
PanelOcclusion,
PanelEmission,
PanelBrushSize,
PanelSpray
};
private GameObject _panel;
private Font _font;
private void Awake()
{
if (!modeManager) modeManager = FindFirstObjectByType();
}
private void OnEnable()
{
if (modeManager) modeManager.ModeChanged += OnModeChanged;
}
private void OnDisable()
{
if (modeManager) modeManager.ModeChanged -= OnModeChanged;
}
private void Start()
{
if (!painter) painter = modeManager ? modeManager.Painter : null;
if (!painter) painter = FindFirstObjectByType();
_font = LoadFont();
EnsureEventSystem();
EnsureCanvas();
if (layoutPrefab)
{
var go = Instantiate(layoutPrefab, transform);
go.name = layoutPrefab.name; // strip the "(Clone)" suffix
_panel = go;
}
WirePanel(_panel.transform);
// Re-sync every widget when the pipette adopts a sampled material (painter -> HUD).
if (painter) painter.Picked += SyncFromPainter;
// Restore the saved panel positions + widget values from the previous session.
RestoreHudState();
// Set initial visibility (the manager's first SwitchTo may have fired before this Start).
OnModeChanged(modeManager ? modeManager.ActiveMode : null);
}
private void OnDestroy()
{
if (painter) painter.Picked -= SyncFromPainter;
SaveHudOnTeardown();
}
private void OnModeChanged(GameMode mode)
{
if (_panel) _panel.SetActive(mode is PaintMode);
}
private static Font LoadFont()
{
var f = Resources.GetBuiltinResource("LegacyRuntime.ttf");
if (!f) f = Resources.GetBuiltinResource("Arial.ttf");
return f;
}
// --- Canvas ---
private void EnsureCanvas()
{
var canvas = GetComponent