using System;
using System.Collections.Generic;
using Luneraria.Core;
using UnityEngine;
using UnityEngine.UIElements;
namespace Luneraria.UI
{
/// Where a HUD click sends the camera: a lane, a map point or a named target (base, enemy_base, grotto, hero).
public sealed class LuneFocusTarget
{
public string Lane = "";
public Vec2? Point;
public string Camera = "";
public bool IsEmpty { get { return Lane.Length == 0 && Point == null && Camera.Length == 0; } }
public static LuneFocusTarget ForLane(string lane) { return new LuneFocusTarget { Lane = lane ?? "" }; }
public static LuneFocusTarget ForPoint(Vec2 point) { return new LuneFocusTarget { Point = point }; }
public static LuneFocusTarget ForCamera(string target) { return new LuneFocusTarget { Camera = target ?? "" }; }
public override string ToString() { return Lane + "|" + (Point.HasValue ? Point.Value.ToString() : "") + "|" + Camera; }
}
///
/// Colours of the armour and damage types (G3 open point, Legion TD principle): a damage type
/// has the colour of the armour it hits for 130 % (DamageMatrix: type i is strong against armour i).
///
public static class LuneTypeColors
{
public static readonly Color[] Types = { LuneUiTheme.Hex("e2b46b"), LuneUiTheme.Hex("7fb2e5"), LuneUiTheme.Hex("c39bf0") };
public static Color Armor(int armorType) { return Types[Mathf.Clamp(armorType, 0, 2)]; }
public static Color Damage(int attackType)
{
int row = Mathf.Clamp(attackType, 0, 2);
for (int armor = 0; armor < 3; armor++)
{
if (LuneUnit.DamageMatrix[row, armor] == 1.30)
return Types[armor];
}
return LuneUiTheme.Muted;
}
}
///
/// The viewer's own gold with the coin symbol and the base income of the next settlement
/// (Godot LuneResourceDisplay). It is only ever fed the own seat's gold.
///
public sealed class LuneResourceDisplay : VisualElement
{
public readonly Label Amount;
public readonly Label Detail;
int _shown = -1;
float _flash;
public LuneResourceDisplay()
{
style.flexDirection = FlexDirection.Row;
style.alignItems = Align.Center;
pickingMode = PickingMode.Position;
LuneUi.Glyph("coin", LuneUiTheme.Gold, 30f, this);
VisualElement column = LuneUi.Box(true, -2f, this);
column.style.marginLeft = 8f;
Amount = LuneUi.Label("0", "HudGold", column);
Detail = LuneUi.Label("", "HudMuted", column);
}
public void SetValues(int gold, int income, bool showIncome = true)
{
if (gold != _shown)
{
if (_shown >= 0 && !LunePreferences.ReducedMotion)
_flash = .45f;
_shown = gold;
Amount.text = LuneUiTheme.Number(gold);
}
string line = showIncome ? "+" + LuneUiTheme.Number(income) + " nächste Runde" : "";
if (Detail.text != line)
Detail.text = line;
LuneUi.Show(Detail, showIncome);
}
public void Tick(float delta)
{
if (_flash <= 0f)
return;
_flash = Mathf.Max(0f, _flash - delta);
// Godot modulate (1.35, 1.2, .8) fading back to white.
float t = _flash / .45f;
Color gold = LuneUiTheme.Gold;
var flash = new Color(Mathf.Min(1f, gold.r * 1.35f), Mathf.Min(1f, gold.g * 1.2f), gold.b * .8f, 1f);
LuneUi.SetColor(Amount, Color.Lerp(gold, flash, t));
}
}
///
/// The own team (Godot LuneTeamPanel): per seat the name, lane, hero (committed hero of the
/// others, own draft for the viewer), readiness during planning and whether a human, a bot or –
/// after a disconnect – a bot on a human's seat plays it. Gold or plans of others never appear.
///
public sealed class LuneTeamPanel : VisualElement
{
sealed class Row
{
public VisualElement Element;
public LuneGlyphElement Status;
public LunePortraitElement Portrait;
public Label Name;
public Label Lane;
public LuneStatusBadge Badge;
public LuneHudModel.MemberInfo Data;
}
readonly List _rows = new List();
readonly Label _header;
readonly LuneGlyphElement _emblem;
string _signature = "";
public LuneTeamPanel()
{
pickingMode = PickingMode.Ignore;
var column = (LuneBox)LuneUi.Box(true, 3f, this);
VisualElement head = LuneUi.Box(false, 6f, column);
_emblem = LuneUi.Glyph("emblem_0", LuneUiTheme.Team[0], 15f, head);
_header = LuneUi.Label("DEIN TEAM", "HudCaps", head);
for (int index = 0; index < 3; index++)
{
VisualElement element = LuneUi.Box(false, 6f, column);
element.pickingMode = PickingMode.Position;
element.style.minHeight = 28f;
var row = new Row { Element = element };
row.Status = LuneUi.Glyph("human", LuneUiTheme.Muted, 15f, element);
row.Portrait = new LunePortraitElement();
row.Portrait.style.width = 26f;
row.Portrait.style.height = 26f;
element.Add(row.Portrait);
row.Name = LuneUi.Clip(LuneUi.Label("", "HudText", element));
row.Name.style.flexGrow = 1f;
row.Lane = LuneUi.Label("", "HudMuted", element);
row.Badge = new LuneStatusBadge();
element.Add(row.Badge);
_rows.Add(row);
int captured = index;
LuneTooltip.Attach(element, () => Tip(captured));
}
}
public void Apply(List members, int team, bool planning, bool networked)
{
string signature = string.Join(",", members) + "|" + team + "|" + planning + "|" + networked;
if (signature == _signature)
return;
_signature = signature;
_emblem.Glyph = "emblem_" + team;
_emblem.Color = LuneUiTheme.TeamColor(team);
_header.text = "DEIN TEAM · " + LuneUi.Upper(LuneUiTheme.TeamName(team));
for (int index = 0; index < Mathf.Min(3, members.Count); index++)
{
LuneHudModel.MemberInfo member = members[index];
Row row = _rows[index];
row.Data = member;
string status = member.Status;
row.Status.Glyph = status == "self" ? "star" : status == "bot" ? "bot" : status == "offline" ? "offline" : "human";
row.Status.Color = status == "self" ? LuneUiTheme.BronzeLight : status == "offline" ? LuneUiTheme.Danger : LuneUiTheme.Muted;
row.Name.text = member.Name;
LuneUi.SetColor(row.Name, member.IsSelf ? LuneUiTheme.BronzeLight : LuneUiTheme.Text);
row.Lane.text = LuneUi.Upper(LuneMap.LaneNames[member.Lane]);
LuneUi.Show(row.Portrait, member.HeroId.Length > 0);
if (member.HeroId.Length > 0)
row.Portrait.ShowModel(LuneContent.Heroes[member.HeroId].Model);
if (status == "offline")
row.Badge.SetState("BOT ÜBERNIMMT", LuneUiTheme.Danger, "offline");
else if (status == "bot")
row.Badge.SetState("BOT", LuneUiTheme.Muted, "bot");
else if (planning && member.Ready)
row.Badge.SetState("BEREIT", LuneUiTheme.Good, "check");
else if (planning)
row.Badge.SetState("PLANT", LuneUiTheme.Warn, "clock");
else
row.Badge.SetState("IM KAMPF", LuneUiTheme.Info);
}
}
LuneTip Tip(int index)
{
LuneHudModel.MemberInfo member = _rows[index].Data;
if (member == null)
return null;
string kind = member.Status == "self" ? "Du" : member.Status == "human" ? "Mensch" : member.Status == "bot" ? "Bot" : "Verbindung getrennt – ein Bot spielt den Platz weiter";
string hero = "kein Held";
if (member.HeroId.Length > 0)
hero = LuneContent.Heroes[member.HeroId].Name + " · Stufe " + member.HeroLevel;
var tip = new LuneTip
{
Title = member.Name,
Subtitle = LuneMap.LaneNames[member.Lane] + "-Lane · " + ContentData.FactionNames.GetValueOrDefault(member.Bund, ""),
Note = member.IsSelf ? "" : "Gold und laufende Planung anderer Spieler bleiben privat.",
};
tip.Stat("human", "Spieler", kind).Stat("crown", "Held", hero).Stat("check", "Planung", member.Ready ? "bereit" : "plant noch");
return tip;
}
}
///
/// Purchase card (Godot LuneUnitCard): real-model portrait, name, role, gold price, hotkey and
/// availability; the armour and damage type as coloured symbols (G3 open point). The champion
/// variant uses the copper card style, a hatched portrait and the "1 WELLE" band at the bottom
/// of the portrait, so temporary attack champions never read like permanent troops.
///
public sealed class LuneUnitCard : LuneButton
{
static readonly Dictionary RoleGlyphs = new Dictionary
{ { "front", "tank" }, { "melee", "melee" }, { "ranged", "ranged" }, { "splash", "splash" }, { "support", "support" }, { "schutz", "tank" }, { "druck", "tempo" }, { "siege", "siege" } };
static readonly Dictionary RoleText = new Dictionary
{ { "front", "Tank" }, { "melee", "Nahkampf" }, { "ranged", "Fernkampf" }, { "splash", "Fläche" }, { "support", "Support" }, { "schutz", "Schutz" }, { "druck", "Druck" }, { "siege", "Belagerung" } };
public string TypeId = "";
public bool Champion;
public int Cost;
public string Reason = "";
public readonly LunePortraitElement Portrait;
readonly Label _name;
readonly LuneGlyphElement _roleGlyph;
readonly Label _role;
readonly Label _cost;
readonly Label _key;
readonly VisualElement _band;
readonly LuneGlyphElement _lock;
readonly LuneGlyphElement _armor;
readonly LuneGlyphElement _attack;
public LuneUnitCard() : base(null, LuneButtons.HudCard)
{
style.width = 116f;
style.height = 132f;
style.flexDirection = FlexDirection.Column;
style.alignItems = Align.Stretch;
style.justifyContent = Justify.FlexStart;
style.paddingLeft = 6f;
style.paddingRight = 6f;
style.paddingTop = 6f;
style.paddingBottom = 5f;
Portrait = new LunePortraitElement();
Portrait.style.minHeight = 62f;
Portrait.style.flexGrow = 1f;
Add(Portrait);
_key = LuneUi.Label("", "HudCaps", Portrait);
_key.style.position = Position.Absolute;
_key.style.left = 4f;
_key.style.top = 1f;
// Armour and damage type, top right (Legion TD: what it is good against).
VisualElement types = LuneUi.Box(false, 2f, Portrait);
types.style.position = Position.Absolute;
types.style.right = 3f;
types.style.top = 3f;
_attack = LuneUi.Glyph("sword", LuneUiTheme.Muted, 13f, types);
_armor = LuneUi.Glyph("armor", LuneUiTheme.Muted, 13f, types);
_band = LuneUi.Box(false, 2f, Portrait);
_band.style.position = Position.Absolute;
_band.style.left = 0f;
_band.style.right = 0f;
_band.style.bottom = 0f;
_band.style.height = 16f;
_band.style.justifyContent = Justify.Center;
_band.style.backgroundColor = new Color(0f, 0f, 0f, .62f);
LuneUi.Glyph("hourglass", LuneUiTheme.Champion, 12f, _band);
LuneUi.SetColor(LuneUi.Label("1 WELLE", "HudCaps", _band, 14f), LuneUiTheme.Champion);
LuneUi.Show(_band, false);
_lock = LuneUi.Glyph("lock", LuneUiTheme.Muted, 18f, Portrait);
_lock.style.position = Position.Absolute;
_lock.style.left = new Length(50f, LengthUnit.Percent);
_lock.style.top = new Length(50f, LengthUnit.Percent);
_lock.style.translate = new Translate(new Length(-50f, LengthUnit.Percent), new Length(-50f, LengthUnit.Percent));
LuneUi.Show(_lock, false);
_name = LuneUi.Clip(LuneUi.Label("", "HudText", this));
_name.style.unityTextAlign = TextAnchor.MiddleCenter;
_name.style.marginTop = 1f;
VisualElement roleRow = LuneUi.Box(false, 4f, this);
roleRow.style.justifyContent = Justify.Center;
roleRow.style.marginTop = 1f;
_roleGlyph = LuneUi.Glyph("", LuneUiTheme.Muted, 14f, roleRow);
_role = LuneUi.Label("", "HudMuted", roleRow);
VisualElement costRow = LuneUi.Box(false, 4f, this);
costRow.style.justifyContent = Justify.Center;
costRow.style.marginTop = 1f;
LuneUi.Glyph("coin", LuneUiTheme.Gold, 15f, costRow);
_cost = LuneUi.Label("", "HudValue", costRow);
}
public void Setup(string id, string modelId, string title, string roleKey, int price, string hotkey = "", bool champion = false, UnitDef definition = null)
{
TypeId = id;
Champion = champion;
Cost = price;
Look = champion ? LuneButtons.HudChampionCard : LuneButtons.HudCard;
Portrait.ShowModel(modelId);
_name.text = title;
_roleGlyph.Glyph = RoleGlyphs.TryGetValue(roleKey ?? "", out string glyph) ? glyph : "melee";
_role.text = RoleText.TryGetValue(roleKey ?? "", out string text) ? text : roleKey;
_cost.text = price.ToString();
_key.text = hotkey;
LuneUi.Show(_key, hotkey.Length > 0);
LuneUi.Show(_band, champion);
Portrait.Hatch = champion ? LuneUiTheme.WithAlpha(LuneUiTheme.Champion, .13f) : Color.clear;
if (definition != null)
{
_attack.Color = LuneTypeColors.Damage(definition.AttackType);
_armor.Color = LuneTypeColors.Armor(definition.ArmorType);
}
}
/// ok = purchasable now; otherwise `why` explains it (tooltip) and the card greys out.
public void SetAvailable(bool ok, string why = "", bool locked = false, bool unaffordable = false)
{
Reason = why ?? "";
Disabled = !ok;
Portrait.Dimmed = !ok;
LuneUi.Show(_lock, locked);
LuneUi.SetColor(_cost, unaffordable ? LuneUiTheme.Danger : LuneUiTheme.Bright);
LuneUi.SetColor(_name, ok ? LuneUiTheme.Text : LuneUiTheme.Muted);
}
}
///
/// One own troop in the army strip (Godot LuneArmyChip): real-model portrait, a band with its
/// lane (T, M, B or G) and stage (II for upgraded troops). Selected = pressed style. In combat a
/// fallen troop is dimmed and crossed out.
///
public sealed class LuneArmyChip : LuneButton
{
public const float Band = 15f;
public string Uid = "";
public string TypeId = "";
public string Lane = "mid";
public readonly LunePortraitElement Portrait;
readonly Label _lane;
readonly Label _stage;
bool _fallen;
public LuneArmyChip() : base(null, LuneButtons.HudChip)
{
style.flexDirection = FlexDirection.Column;
style.alignItems = Align.Stretch;
style.justifyContent = Justify.FlexStart;
style.paddingLeft = 2f;
style.paddingRight = 2f;
style.paddingTop = 2f;
style.paddingBottom = 1f;
Portrait = new LunePortraitElement();
Portrait.style.flexGrow = 1f;
Add(Portrait);
VisualElement band = LuneUi.Box(false, 0f, this);
band.style.height = Band;
band.style.backgroundColor = new Color(0f, 0f, 0f, .55f);
band.style.justifyContent = Justify.SpaceBetween;
band.style.paddingLeft = 3f;
band.style.paddingRight = 3f;
_lane = LuneUi.Label("", "HudCaps", band, 14f);
_stage = LuneUi.Label("II", "HudCaps", band, 14f);
LuneUi.SetColor(_stage, LuneUiTheme.Gold);
}
public void Setup(RosterEntry entry, float edge)
{
Uid = entry.Uid;
TypeId = entry.TypeId;
Lane = entry.Lane;
style.width = edge;
style.height = edge + Band;
Portrait.ShowModel(TypeId);
bool grotto = Lane == LuneMatch.Grotto;
_lane.text = grotto ? "G" : LuneMap.LaneNames.TryGetValue(Lane, out string name) ? name.Substring(0, 1) : "?";
LuneUi.SetColor(_lane, grotto ? LuneUiTheme.Team[2] : LuneUiTheme.BronzeLight);
LuneUi.Show(_stage, LuneStats.StageOf(TypeId) == 2);
}
public bool Fallen
{
get { return _fallen; }
set
{
if (value == _fallen)
return;
_fallen = value;
Portrait.Dimmed = value;
Portrait.Crossed = value;
}
}
}
///
/// A hero item (Godot LuneItemSlot): one of the three slots (compact: symbol only) or a card in
/// the item shop (symbol, name, cost, main effect, state). States say why something cannot be
/// done: equipped, available, unaffordable, incompatible (hero level), unique (already carried),
/// slots full, locked (plan committed) or an empty slot.
///
public sealed class LuneItemSlot : LuneButton
{
public enum State { Empty, Equipped, Available, Unaffordable, Incompatible, Unique, Full, Locked }
static readonly Dictionary StateText = new Dictionary
{
{ State.Equipped, "AUSGERÜSTET" }, { State.Available, "KAUFBAR" }, { State.Unaffordable, "ZU TEUER" }, { State.Incompatible, "STUFE FEHLT" },
{ State.Unique, "GETRAGEN" }, { State.Full, "PLÄTZE VOLL" }, { State.Locked, "GESPERRT" }, { State.Empty, "LEER" },
};
static readonly Dictionary ItemColors = new Dictionary
{
{ "basaltschild", LuneUiTheme.Hex("9fb2c8") }, { "glutklinge", LuneUiTheme.Hex("ff9a5a") }, { "rankenmantel", LuneUiTheme.Hex("8fd08a") },
{ "funkenfokus", LuneUiTheme.Hex("f5d76a") }, { "quellamulett", LuneUiTheme.Hex("7ad6e6") }, { "kristallsplitter", LuneUiTheme.Hex("c7a2ff") },
};
public string ItemId = "";
public State Current = State.Empty;
public readonly bool Compact;
readonly LuneGlyphElement _glyph;
readonly Label _name;
readonly Label _effect;
readonly Label _cost;
readonly LuneStatusBadge _badge;
public static Color ColorOf(string id)
{
return ItemColors.TryGetValue(id ?? "", out Color color) ? color : LuneUiTheme.Muted;
}
public LuneItemSlot(bool compact = true) : base(null, compact ? LuneButtons.HudChip : LuneButtons.HudCard)
{
Compact = compact;
if (compact)
{
style.width = 30f;
style.height = 30f;
// Godot: the glyph fills the slot with 5 px insets at any slot size (46 in the hero window).
_glyph = LuneUi.Glyph("", Color.white, 20f, this);
_glyph.style.position = Position.Absolute;
_glyph.style.left = 5f;
_glyph.style.top = 5f;
_glyph.style.right = 5f;
_glyph.style.bottom = 5f;
_glyph.style.width = StyleKeyword.Auto;
_glyph.style.height = StyleKeyword.Auto;
return;
}
style.minHeight = 74f;
style.justifyContent = Justify.FlexStart;
style.paddingLeft = 10f;
style.paddingRight = 8f;
style.paddingTop = 6f;
style.paddingBottom = 6f;
_glyph = LuneUi.Glyph("", Color.white, 34f, this);
VisualElement column = LuneUi.Box(true, 0f, this);
column.style.marginLeft = 10f;
column.style.flexGrow = 1f;
column.style.flexShrink = 1f;
column.style.minWidth = 0f;
column.style.justifyContent = Justify.Center;
VisualElement head = LuneUi.Box(false, 6f, column);
_name = LuneUi.Clip(LuneUi.Label("", "HudValue", head));
_name.style.flexGrow = 1f;
LuneUi.Glyph("coin", LuneUiTheme.Gold, 14f, head);
_cost = LuneUi.Label("", "HudValue", head);
_effect = LuneUi.Clip(LuneUi.Label("", "HudMuted", column));
_badge = new LuneStatusBadge();
_badge.style.alignSelf = Align.FlexStart;
column.Add(_badge);
}
public void SetItem(string id, State state)
{
ItemId = id ?? "";
Current = state;
Color tint = ColorOf(ItemId);
bool usable = state == State.Available || state == State.Equipped;
_glyph.Glyph = ItemId;
_glyph.Color = usable || Compact ? tint : LuneUiTheme.WithAlpha(tint, .45f);
if (Compact)
{
Disabled = ItemId.Length == 0;
return;
}
LuneContent.Items.TryGetValue(ItemId, out ItemSpec item);
_name.text = item != null ? item.Name : "";
_cost.text = item != null ? item.Cost.ToString() : "";
LuneUi.SetColor(_cost, state == State.Unaffordable ? LuneUiTheme.Danger : LuneUiTheme.Bright);
_effect.text = item != null ? item.Text : "";
Color badge = state == State.Equipped ? LuneUiTheme.Good : state == State.Available ? LuneUiTheme.BronzeLight : LuneUiTheme.Muted;
if (state == State.Unaffordable)
badge = LuneUiTheme.Danger;
string glyph = state == State.Equipped || state == State.Unique ? "check" : state == State.Incompatible || state == State.Locked ? "lock" : "";
_badge.SetState(StateText[state], badge, glyph, state == State.Equipped);
Disabled = !usable;
}
}
///
/// Short event list (Godot LuneEventFeed): tower falls, champions, boss, critical Ancients,
/// round results. At most five entries, newest on top; each fades after a while. A click on an
/// entry with a place moves the camera there. It never lists every kill.
///
public sealed class LuneEventFeed : VisualElement
{
public const int Limit = 5;
public const float Lifetime = 14f;
public event Action FocusRequested;
sealed class Entry
{
public string Text;
public string Level;
public float Born;
public LuneButton Node;
}
readonly List _entries = new List();
float _clock;
public LuneEventFeed()
{
pickingMode = PickingMode.Ignore;
LuneUi.Show(this, false);
}
public IReadOnlyList Texts
{
get { return _entries.ConvertAll(e => e.Text); }
}
static Color LevelColor(string level)
{
return level == "good" ? LuneUiTheme.Good : level == "warn" ? LuneUiTheme.Warn : level == "danger" ? LuneUiTheme.Danger : LuneUiTheme.Info;
}
/// level: info, good, warn, danger. target: null or a lane, point or camera target.
public void Push(string text, string level = "info", string glyph = "", LuneFocusTarget target = null)
{
foreach (Entry entry in _entries)
{
if (entry.Text == text && _clock - entry.Born < 4f)
return;
}
var button = new LuneButton(null, LuneButtons.HudRow);
button.style.minHeight = 30f;
button.style.justifyContent = Justify.FlexStart;
button.style.paddingLeft = 8f;
button.style.paddingRight = 8f;
button.style.marginBottom = 3f;
Color tint = LevelColor(level);
LuneUi.Glyph(glyph.Length > 0 ? glyph : "star", tint, 15f, button).style.marginRight = 7f;
Label label = LuneUi.Clip(LuneUi.Label(text, "HudText", button));
label.style.flexGrow = 1f;
LuneUi.SetColor(label, Color.Lerp(tint, LuneUiTheme.Text, .45f));
bool hasTarget = target != null && !target.IsEmpty;
button.Clicked += () =>
{
if (hasTarget)
FocusRequested?.Invoke(target);
};
LuneTooltip.Attach(button, () => new LuneTip { Title = text, Note = hasTarget ? "Klick: Kamera dorthin" : "" });
Insert(0, button);
_entries.Insert(0, new Entry { Text = text, Level = level, Born = _clock, Node = button });
while (_entries.Count > Limit)
{
Entry old = _entries[_entries.Count - 1];
_entries.RemoveAt(_entries.Count - 1);
old.Node.RemoveFromHierarchy();
}
LuneUi.Show(this, true);
}
public void ClearAll()
{
foreach (Entry entry in _entries)
entry.Node.RemoveFromHierarchy();
_entries.Clear();
LuneUi.Show(this, false);
}
public void Tick(float delta)
{
_clock += delta;
for (int index = _entries.Count - 1; index >= 0; index--)
{
Entry entry = _entries[index];
float age = _clock - entry.Born;
if (age > Lifetime)
{
entry.Node.RemoveFromHierarchy();
_entries.RemoveAt(index);
}
else if (age > Lifetime - 2f)
{
entry.Node.style.opacity = Mathf.Clamp01((Lifetime - age) / 2f);
}
}
LuneUi.Show(this, _entries.Count > 0);
}
}
///
/// A window over the field for detail work (hero, recruiting; Godot LuneModalPanel). A light
/// backdrop keeps clicks away from the world and closes the window; Esc and the × close it too.
/// The window never covers the whole screen and only opens on request.
///
public sealed class LuneModalPanel : VisualElement
{
public event Action Closed;
public readonly LunePanel Panel;
public readonly VisualElement Body;
public readonly Label Title;
readonly Vector2 _box;
public LuneModalPanel(Vector2 size)
{
_box = size;
style.position = Position.Absolute;
style.left = 0f;
style.top = 0f;
style.right = 0f;
style.bottom = 0f;
pickingMode = PickingMode.Position;
var backdrop = new VisualElement();
backdrop.style.position = Position.Absolute;
backdrop.style.left = 0f;
backdrop.style.top = 0f;
backdrop.style.right = 0f;
backdrop.style.bottom = 0f;
backdrop.style.backgroundColor = new Color(.01f, .015f, .025f, .38f);
backdrop.RegisterCallback(evt =>
{
if (evt.button == 0)
Close();
});
Add(backdrop);
Panel = new LunePanel(LunePanels.Modal);
Panel.style.position = Position.Absolute;
Add(Panel);
VisualElement column = LuneUi.Box(true, 12f, Panel);
column.style.flexGrow = 1f;
VisualElement head = LuneUi.Box(false, 0f, column);
Title = LuneUi.Label("", "HudTitle", head);
Title.style.flexGrow = 1f;
var close = new LuneButton(null, LuneButtons.HudIconButton, head);
close.style.width = 36f;
close.style.height = 34f;
LuneUi.Glyph("close", LuneUiTheme.Muted, 14f, close);
close.Clicked += Close;
LuneTooltip.Attach(close, () => new LuneTip { Title = "Schließen", Text = "Esc oder Klick neben das Fenster." });
Body = LuneUi.Box(true, 10f, column);
Body.style.flexGrow = 1f;
RegisterCallback(_ => Layout());
RegisterCallback(evt => evt.StopPropagation());
LuneUi.Show(this, false);
}
public bool Open { get { return style.display != DisplayStyle.None; } }
public void OpenWith(string heading)
{
Title.text = heading;
LuneUi.Show(this, true);
BringToFront();
Layout();
}
public void Close()
{
if (!Open)
return;
LuneUi.Show(this, false);
Closed?.Invoke();
}
void Layout()
{
float width = layout.width;
float height = layout.height;
if (float.IsNaN(width) || width <= 0f)
return;
var edge = new Vector2(Mathf.Min(_box.x, width - 40f), Mathf.Min(_box.y, height - 40f));
Panel.style.width = edge.x;
Panel.style.height = edge.y;
Panel.style.left = Mathf.Floor((width - edge.x) * .5f);
Panel.style.top = Mathf.Floor((height - edge.y) * .5f);
}
}
}