using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
[RequireComponent(typeof(UIDocument))]
public class WhiskersPanel : MonoBehaviour
{
private UIDocument _doc;
private VisualElement _panel;
private VisualElement _body;
private bool _collapsed = false;
// Drag State
private bool _isDragging = false;
private Vector2 _dragOffset;
// Form State
private readonly List<string> _surfaces = new() { "Wobbler", "Opartica", "Autobahn" };
private readonly List<string> _blendModes = new() { "normal", "lighten", "darken", "screen", "multiply", "add" };
private int _surfaceIdx = 0;
private int _layerIdx = 0;
private int _blendIdx = 0;
void OnEnable()
{
_doc = GetComponent<UIDocument>();
var root = _doc.rootVisualElement;
root.style.flexGrow = 1;
// 1. Floating Panel Window
_panel = new VisualElement();
_panel.style.position = Position.Absolute;
_panel.style.left = 30;
_panel.style.top = 30;
_panel.style.width = 210;
_panel.style.backgroundColor = new Color(0.13f, 0.13f, 0.13f, 0.92f);
_panel.style.borderTopColor = _panel.style.borderBottomColor =
_panel.style.borderLeftColor = _panel.style.borderRightColor = new Color(0.27f, 0.27f, 0.27f);
_panel.style.borderTopWidth = _panel.style.borderBottomWidth =
_panel.style.borderLeftWidth = _panel.style.borderRightWidth = 3;
_panel.style.borderTopLeftRadius = _panel.style.borderTopRightRadius =
_panel.style.borderBottomLeftRadius = _panel.style.borderBottomRightRadius = 10;
root.Add(_panel);
// 2. Draggable Title Bar
var titleBar = new VisualElement();
titleBar.style.height = 34;
titleBar.style.backgroundColor = new Color(0.17f, 0.17f, 0.17f);
titleBar.style.flexDirection = FlexDirection.Row;
titleBar.style.justifyContent = Justify.SpaceBetween;
titleBar.style.alignItems = Align.Center;
titleBar.style.paddingLeft = titleBar.style.paddingRight = 10;
var titleLabel = CreateLabel("Whiskers", 13, Color.white, true);
var collapseBtn = new Button(() => {
_collapsed = !_collapsed;
_body.style.display = _collapsed ? DisplayStyle.None : DisplayStyle.Flex;
}) { text = "−" };
collapseBtn.style.backgroundColor = Color.clear;
collapseBtn.style.color = new Color(0.7f, 0.7f, 0.7f);
titleBar.Add(titleLabel);
titleBar.Add(collapseBtn);
_panel.Add(titleBar);
// Pointer Drag Events
titleBar.RegisterCallback<PointerDownEvent>(e => {
_isDragging = true;
_dragOffset = e.localPosition;
titleBar.CapturePointer(e.pointerId);
});
titleBar.RegisterCallback<PointerMoveEvent>(e => {
if (!_isDragging) return;
_panel.style.left = _panel.resolvedStyle.left + (e.localPosition.x - _dragOffset.x);
_panel.style.top = _panel.resolvedStyle.top + (e.localPosition.y - _dragOffset.y);
});
titleBar.RegisterCallback<PointerUpEvent>(e => {
_isDragging = false;
titleBar.ReleasePointer(e.pointerId);
});
// 3. Panel Body (Flex Layout Stack)
_body = new VisualElement();
_body.style.paddingTop = _body.style.paddingBottom = 12;
_body.style.paddingLeft = _body.style.paddingRight = 10;
_body.style.alignItems = Align.Center;
_panel.Add(_body);
// Surface Stepper
_body.Add(CreateStepper(_surfaces, _surfaceIdx, idx => _surfaceIdx = idx));
// Lock & Hide Row
var lockHideRow = CreateRow();
lockHideRow.Add(CreateToggle("LOCK"));
lockHideRow.Add(CreateToggle("HIDE"));
_body.Add(lockHideRow);
// Layer Controls
_body.Add(CreateLabel("LAYER", 11, new Color(0.85f, 0.85f, 0.85f)));
_body.Add(CreateStepper(new List<string> { "0", "1", "2" }, _layerIdx, idx => _layerIdx = idx));
// 2-Column Crop Matrix (Sliders Left + Vertical Slider Right)
var cropBox = CreateRow();
cropBox.style.alignItems = Align.FlexEnd;
var leftCol = new VisualElement();
leftCol.style.alignItems = Align.Center;
leftCol.Add(CreateToggle("CROP"));
leftCol.Add(CreateLabel("SCALE", 10, new Color(0.6f, 0.6f, 0.6f)));
leftCol.Add(CreateSlider(0.2f, 3.0f, 1.0f, 85));
leftCol.Add(CreateSlider(-100f, 100f, 0f, 85));
leftCol.Add(CreateLabel("SHIFT X", 10, new Color(0.6f, 0.6f, 0.6f)));
var rightCol = new VisualElement();
rightCol.style.alignItems = Align.Center;
rightCol.Add(CreateSlider(-100f, 100f, 0f, 60, SliderDirection.Vertical));
rightCol.Add(CreateLabel("SHIFT Y", 10, new Color(0.6f, 0.6f, 0.6f)));
cropBox.Add(leftCol);
cropBox.Add(rightCol);
_body.Add(cropBox);
// Mask Row
var maskRow = CreateRow();
maskRow.Add(new Toggle());
maskRow.Add(CreateButton("MASK", new Color(0.36f, 0.14f, 0.51f), 65, 22));
_body.Add(maskRow);
// Feather
_body.Add(CreateLabel("FEATHER", 11, new Color(0.85f, 0.85f, 0.85f)));
_body.Add(CreateSlider(0.0f, 1.0f, 0.2f, 105));
// Blend
_body.Add(CreateLabel("BLEND", 11, new Color(0.85f, 0.85f, 0.85f)));
_body.Add(CreateStepper(_blendModes, _blendIdx, idx => _blendIdx = idx));
// Reset Button
_body.Add(CreateButton("RESET", new Color(0.86f, 0.2f, 0.2f), 85, 22));
// Global Divider & Label
var divider = new VisualElement();
divider.style.width = 170;
divider.style.height = 2;
divider.style.backgroundColor = new Color(0.2f, 0.2f, 0.2f);
divider.style.marginTop = 6;
_body.Add(divider);
_body.Add(CreateLabel("GLOBAL", 10, new Color(0.5f, 0.5f, 0.5f)));
// Action Buttons
_body.Add(CreateButton("IMPORT", new Color(0.33f, 0.33f, 0.33f), 85, 22));
_body.Add(CreateButton("EXPORT", new Color(0.33f, 0.33f, 0.33f), 85, 22));
_body.Add(CreateButton("PRESENT", new Color(0.11f, 0.43f, 0.2f), 95, 24));
}
// --- HELPER BUILDERS ---
private Label CreateLabel(string text, int size, Color color, bool bold = false)
{
var l = new Label(text);
l.style.fontSize = size;
l.style.color = color;
l.style.unityFontStyleAndWeight = bold ? FontStyle.Bold : FontStyle.Normal;
l.style.marginBottom = l.style.marginTop = 2;
return l;
}
private Button CreateButton(string text, Color bg, float width, float height)
{
var b = new Button { text = text };
b.style.width = width;
b.style.height = height;
b.style.backgroundColor = bg;
b.style.color = Color.white;
b.style.fontSize = 11;
b.style.unityFontStyleAndWeight = FontStyle.Bold;
b.style.borderTopLeftRadius = b.style.borderTopRightRadius =
b.style.borderBottomLeftRadius = b.style.borderBottomRightRadius = 4;
b.style.marginBottom = 4;
return b;
}
private VisualElement CreateStepper(List<string> list, int initialIdx, System.Action<int> onChange)
{
int idx = initialIdx;
var box = new VisualElement();
box.style.flexDirection = FlexDirection.Row;
box.style.justifyContent = Justify.SpaceBetween;
box.style.alignItems = Align.Center;
box.style.width = 125;
box.style.height = 24;
box.style.backgroundColor = new Color(0.12f, 0.12f, 0.12f);
box.style.borderTopColor = box.style.borderBottomColor =
box.style.borderLeftColor = box.style.borderRightColor = new Color(0.27f, 0.27f, 0.27f);
box.style.borderTopWidth = box.style.borderBottomWidth =
box.style.borderLeftWidth = box.style.borderRightWidth = 1;
box.style.borderTopLeftRadius = box.style.borderTopRightRadius =
box.style.borderBottomLeftRadius = box.style.borderBottomRightRadius = 4;
box.style.marginBottom = 6;
var valLabel = CreateLabel(list[idx], 11, Color.white, true);
var prev = new Button(() => { idx = (idx - 1 + list.Count) % list.Count; valLabel.text = list[idx]; onChange(idx); }) { text = "<" };
var next = new Button(() => { idx = (idx + 1) % list.Count; valLabel.text = list[idx]; onChange(idx); }) { text = ">" };
prev.style.backgroundColor = next.style.backgroundColor = Color.clear;
prev.style.color = next.style.color = Color.grey;
box.Add(prev);
box.Add(valLabel);
box.Add(next);
return box;
}
private Slider CreateSlider(float min, float max, float val, float size, SliderDirection dir = SliderDirection.Horizontal)
{
var s = new Slider(min, max, dir) { value = val };
if (dir == SliderDirection.Horizontal) s.style.width = size;
else s.style.height = size;
s.style.marginBottom = 4;
return s;
}
private VisualElement CreateToggle(string text)
{
var t = new Toggle(text);
t.style.fontSize = 11;
t.style.color = Color.white;
t.style.unityFontStyleAndWeight = FontStyle.Bold;
return t;
}
private VisualElement CreateRow()
{
var r = new VisualElement();
r.style.flexDirection = FlexDirection.Row;
r.style.justifyContent = Justify.Center;
r.style.alignItems = Align.Center;
r.style.marginBottom = 6;
return r;
}
}