using System; using UnityEngine; using UnityEngine.UIElements; namespace Luneraria.UI { /// The plate styles of the HUD panels (Godot "panel" styleboxes of lune_ui_theme.gd and the menu theme). public static class LunePanels { public static readonly LunePlateStyle Hud = LunePlateStyle.Hud(LuneUiTheme.SlateTop, LuneUiTheme.SlateBottom, LuneUiTheme.Edge, 1f, 6f, 12f, 10f); public static readonly LunePlateStyle HudTight = LunePlateStyle.Hud(LuneUiTheme.SlateTop, LuneUiTheme.SlateBottom, LuneUiTheme.Edge, 1f, 5f, 8f, 6f); public static readonly LunePlateStyle Modal = LunePlateStyle.Hud(new Color(.07f, .09f, .125f, .98f), new Color(.045f, .06f, .085f, .98f), LuneUiTheme.Hex("5c4a33"), 1.5f, 10f, 22f, 18f) .WithShadow(new Color(0f, 0f, 0f, .55f), 16f); public static readonly LunePlateStyle BannerDanger = LunePlateStyle.Hud(new Color(.16f, .075f, .05f, .94f), new Color(.11f, .05f, .035f, .95f), LuneUiTheme.Hex("a0523a"), 1.5f, 6f, 16f, 8f); public static readonly LunePlateStyle BannerOwn = LunePlateStyle.Hud(new Color(.07f, .1f, .16f, .94f), new Color(.05f, .07f, .11f, .95f), LuneUiTheme.Hex("5f86b8"), 1.5f, 6f, 16f, 8f); public static readonly LunePlateStyle Tooltip = LunePlateStyle.Hud(new Color(.06f, .08f, .11f, .98f), new Color(.045f, .06f, .085f, .98f), LuneUiTheme.Hex("6b5a44"), 1f, 6f, 12f, 9f) .WithShadow(new Color(0f, 0f, 0f, .5f), 8f); public static readonly LunePlateStyle Menu = MenuPlate(); static LunePlateStyle MenuPlate() { LunePlateStyle style = LunePlateStyle.Menu(new Color(.078f, .094f, .114f, .95f), new Color(.055f, .066f, .08f, .96f), LuneUiTheme.Hex("5c4a33"), 1.5f, 12f, 30f, 26f) .WithShadow(new Color(0f, 0f, 0f, .45f), 18f); style.Ornaments = true; style.OrnamentColor = LuneUiTheme.Hex("8a6a3c"); return style; } } /// A panel with a drawn plate background (Godot PanelContainer with a ForgedStyle). public class LunePanel : VisualElement { LunePlateStyle _plate; public LunePanel(LunePlateStyle plate, bool stopsMouse = true) { pickingMode = stopsMouse ? PickingMode.Position : PickingMode.Ignore; Plate = plate; generateVisualContent += context => { if (_plate != null) LunePlates.Draw(context, new Rect(0f, 0f, layout.width, layout.height), _plate); }; } public LunePlateStyle Plate { get { return _plate; } set { if (value == _plate) return; _plate = value; value?.ApplyPadding(this); MarkDirtyRepaint(); } } } /// Look of a button variation: plates per state and the text colours (Godot Button theme items). public sealed class LuneButtonLook { public LunePlateStyle Normal; public LunePlateStyle Hover; public LunePlateStyle Pressed; public LunePlateStyle Disabled; public LunePlateStyle HoverPressed; /// Flat tab look: background and bottom line per state instead of plates. public bool Tab; public string TextVariation = "HudText"; public float TextSize = 15f; public Color Text = LuneUiTheme.Text; public Color TextHover = Color.white; public Color? TextPressed; public Color? TextHoverPressed; public Color TextDisabled = LuneUiTheme.Faint; public LunePlateStyle PlateFor(bool disabled, bool pressed, bool hover) { if (disabled) return Disabled ?? Normal; if (pressed && hover) return HoverPressed ?? Pressed ?? Normal; if (pressed) return Pressed ?? Normal; if (hover) return Hover ?? Normal; return Normal; } public Color TextFor(bool disabled, bool pressed, bool hover) { if (disabled) return TextDisabled; if (pressed && hover) return TextHoverPressed ?? TextPressed ?? TextHover; if (pressed) return TextPressed ?? Text; return hover ? TextHover : Text; } } /// The button variations of lune_ui_theme.gd and the menu theme. public static class LuneButtons { static LunePlateStyle P(string top, string bottom, Color edge, float width, float cut, float x, float y) { return LunePlateStyle.Hud(LuneUiTheme.Hex(top), LuneUiTheme.Hex(bottom), edge, width, cut, x, y); } static LunePlateStyle P(Color top, Color bottom, Color edge, float width, float cut, float x, float y) { return LunePlateStyle.Hud(top, bottom, edge, width, cut, x, y); } static LunePlateStyle M(string top, string bottom, Color edge, float width, float cut, float x, float y) { return LunePlateStyle.Menu(LuneUiTheme.Hex(top), LuneUiTheme.Hex(bottom), edge, width, cut, x, y); } static Color H(string hex) { return LuneUiTheme.Hex(hex); } public static readonly LuneButtonLook HudButton = new LuneButtonLook { Normal = P("1b232d", "131920", H("4a5a6c"), 1f, 5f, 12f, 7f), Hover = P("26303c", "1a212a", LuneUiTheme.BronzeLight, 1f, 5f, 12f, 7f), Pressed = P("10151b", "171d25", LuneUiTheme.Bronze, 1.5f, 5f, 12f, 7f), Disabled = P("141a20", "10141a", H("2c333c"), 1f, 5f, 12f, 7f), TextSize = 15f, }; public static readonly LuneButtonLook HudIconButton = new LuneButtonLook { Normal = P("1b232d", "131920", H("3c4a5a"), 1f, 5f, 9f, 6f), Hover = P("26303c", "1a212a", LuneUiTheme.BronzeLight, 1f, 5f, 9f, 6f), Pressed = P("10151b", "171d25", LuneUiTheme.Bronze, 1.5f, 5f, 9f, 6f), Disabled = P("141a20", "10141a", H("2c333c"), 1f, 5f, 9f, 6f), TextSize = 15f, }; public static readonly LuneButtonLook HudPrimary = new LuneButtonLook { Normal = M("e8c47e", "b98a4a", H("fff0cc"), 1.5f, 7f, 16f, 8f), Hover = M("f6d898", "cc9c56", Color.white, 1.5f, 7f, 16f, 8f), Pressed = M("b98a4a", "d5aa66", H("fff0cc"), 2f, 7f, 16f, 8f), Disabled = M("4a4234", "3a3328", H("6a5c46"), 1f, 7f, 16f, 8f), TextVariation = "HudPrimary", TextSize = 16f, Text = LuneUiTheme.Ink, TextHover = LuneUiTheme.Ink, TextPressed = LuneUiTheme.Ink, TextDisabled = H("9c8f78"), }; /// Godot PrimaryButton of the menu theme (Bereit, Neue Partie). public static readonly LuneButtonLook Primary = new LuneButtonLook { Normal = M("e8c47e", "b98a4a", H("fff0cc"), 1.5f, 9f, 30f, 15f), Hover = M("f6d898", "cc9c56", Color.white, 1.5f, 9f, 30f, 15f), Pressed = M("b98a4a", "d5aa66", H("fff0cc"), 2f, 9f, 30f, 15f), Disabled = M("5a4c38", "463a2a", H("7d6a4c"), 1.5f, 9f, 30f, 15f), TextVariation = "PrimaryButton", TextSize = 22f, Text = LuneUiTheme.Ink, TextHover = LuneUiTheme.Ink, TextPressed = LuneUiTheme.Ink, TextDisabled = H("2a2218"), }; /// Godot Button of the menu theme (secondary). public static readonly LuneButtonLook Secondary = new LuneButtonLook { Normal = M("1d232b", "141920", H("6b5a44"), 1.5f, 7f, 20f, 11f), Hover = M("27303a", "1a2028", LuneUiTheme.BronzeLight, 1.5f, 7f, 20f, 11f), Pressed = M("10141a", "171c23", LuneUiTheme.Bronze, 2f, 7f, 20f, 11f), Disabled = M("15191e", "111418", H("3a352e"), 1.5f, 7f, 20f, 11f), TextSize = 17f, Text = LuneUiTheme.Parchment, }; public static readonly LuneButtonLook HudCard = Card(new LuneButtonLook { Normal = P("19222c", "10161d", H("34424f"), 1f, 7f, 6f, 6f), Hover = P("212c38", "141b23", LuneUiTheme.Bronze, 1.5f, 7f, 6f, 6f), Pressed = P("2a2820", "17171a", LuneUiTheme.BronzeLight, 2f, 7f, 6f, 6f), Disabled = P("12171d", "0d1116", H("252c34"), 1f, 7f, 6f, 6f), }); public static readonly LuneButtonLook HudChampionCard = Card(new LuneButtonLook { Normal = P("261c14", "15100c", H("6e4a2c"), 1f, 7f, 6f, 6f), Hover = P("302216", "1b140e", LuneUiTheme.Champion, 1.5f, 7f, 6f, 6f), Pressed = P("3a2818", "1e150e", H("ffc88c"), 2f, 7f, 6f, 6f), Disabled = P("16110d", "0f0c0a", H("3a2c20"), 1f, 7f, 6f, 6f), }); public static readonly LuneButtonLook HudChip = Card(new LuneButtonLook { Normal = P("1a232d", "11171e", H("33404d"), 1f, 4f, 2f, 2f), Hover = P("222d39", "151c24", LuneUiTheme.Bronze, 1f, 4f, 2f, 2f), Pressed = P("2c2a22", "17171a", LuneUiTheme.BronzeLight, 2f, 4f, 2f, 2f), Disabled = P("12171d", "0d1116", H("252c34"), 1f, 4f, 2f, 2f), }); public static readonly LuneButtonLook HudRow = new LuneButtonLook { Normal = P(new Color(.09f, .12f, .16f, .55f), new Color(.06f, .08f, .11f, .55f), H("2a3645"), 1f, 5f, 10f, 7f), Hover = P(new Color(.13f, .17f, .22f, .75f), new Color(.08f, .11f, .15f, .75f), LuneUiTheme.Bronze, 1f, 5f, 10f, 7f), Pressed = P(new Color(.16f, .15f, .12f, .8f), new Color(.09f, .09f, .1f, .8f), LuneUiTheme.BronzeLight, 1.5f, 5f, 10f, 7f), Disabled = P(new Color(.09f, .12f, .16f, .55f), new Color(.06f, .08f, .11f, .55f), H("2a3645"), 1f, 5f, 10f, 7f), }; public static readonly LuneButtonLook HudToggle = Toggle(); static LuneButtonLook Toggle() { LunePlateStyle on = P("3a3222", "262117", LuneUiTheme.BronzeLight, 1.5f, 5f, 10f, 6f).WithInner(LuneUiTheme.WithAlpha(LuneUiTheme.BronzeLight, .35f)); return new LuneButtonLook { Normal = P("171e27", "11161d", H("3a4756"), 1f, 5f, 10f, 6f), Hover = P("212a35", "171d25", LuneUiTheme.Bronze, 1f, 5f, 10f, 6f), Pressed = on, HoverPressed = on, Disabled = P("11151a", "0e1115", H("242a31"), 1f, 5f, 10f, 6f), TextVariation = "HudBadge", TextSize = 14f, Text = H("cfd5dc"), TextHover = Color.white, TextPressed = H("fff4dc"), TextHoverPressed = H("fff4dc"), TextDisabled = H("59616b"), }; } public static readonly LuneButtonLook HudTab = new LuneButtonLook { Tab = true, TextVariation = "HudCaps", TextSize = 14f, Text = LuneUiTheme.Muted, TextHover = LuneUiTheme.Text, TextPressed = LuneUiTheme.BronzeLight, TextHoverPressed = LuneUiTheme.BronzeLight, TextDisabled = LuneUiTheme.Faint, }; static LuneButtonLook Card(LuneButtonLook look) { look.HoverPressed = look.Pressed; return look; } } /// /// Button of the HUD (Godot Button with a theme variation). Draws the plate of its state; /// `Disabled` greys it out and blocks clicks but keeps hover, so tooltips can say why (as /// in G3). Toggle buttons keep their pressed state. /// public class LuneButton : VisualElement { public event Action Clicked; public readonly Label TextLabel; public bool ToggleMode; LuneButtonLook _look; bool _hover; bool _down; bool _on; bool _disabled; public LuneButton(string text, LuneButtonLook look, VisualElement parent = null) { _look = look; focusable = false; style.flexDirection = FlexDirection.Row; style.alignItems = Align.Center; style.justifyContent = Justify.Center; if (text != null) { TextLabel = LuneUi.Label(text, look.TextVariation, this, look.TextSize); TextLabel.style.unityTextAlign = TextAnchor.MiddleCenter; } ApplyPadding(); generateVisualContent += Draw; RegisterCallback(_ => SetHover(true)); RegisterCallback(_ => SetHover(false)); RegisterCallback(OnDown); RegisterCallback(OnUp); RegisterCallback(_ => { _down = false; Refresh(); }); Refresh(); parent?.Add(this); } public LuneButtonLook Look { get { return _look; } set { if (value == _look) return; _look = value; if (TextLabel != null) LuneUi.Apply(TextLabel, value.TextVariation, value.TextSize); ApplyPadding(); Refresh(); } } public string Text { get { return TextLabel != null ? TextLabel.text : ""; } set { if (TextLabel != null && TextLabel.text != value) TextLabel.text = value; } } public bool Disabled { get { return _disabled; } set { if (value == _disabled) return; _disabled = value; Refresh(); } } /// Pressed state of a toggle (Godot set_pressed_no_signal). public bool On { get { return _on; } set { if (value == _on) return; _on = value; Refresh(); } } public bool Hovered { get { return _hover; } } void ApplyPadding() { if (_look.Tab) { style.paddingLeft = 12f; style.paddingRight = 12f; style.paddingTop = 6f; style.paddingBottom = 6f; } else { _look.Normal?.ApplyPadding(this); } } void SetHover(bool value) { _hover = value; Refresh(); } void OnDown(PointerDownEvent evt) { if (evt.button != 0 || _disabled) return; _down = true; this.CapturePointer(evt.pointerId); Refresh(); evt.StopPropagation(); } void OnUp(PointerUpEvent evt) { if (evt.button != 0 || !_down) return; _down = false; this.ReleasePointer(evt.pointerId); bool inside = ContainsPoint(evt.localPosition); Refresh(); evt.StopPropagation(); if (inside && !_disabled) Click(); } /// Godot `pressed`: toggles first, then raises Clicked (also for tests and hotkeys). public void Click() { if (_disabled) return; if (ToggleMode) On = !_on; Clicked?.Invoke(); } void Refresh() { bool pressed = _down || _on; if (TextLabel != null) LuneUi.SetColor(TextLabel, _look.TextFor(_disabled, pressed, _hover)); MarkDirtyRepaint(); } void Draw(MeshGenerationContext context) { var rect = new Rect(0f, 0f, layout.width, layout.height); bool pressed = _down || _on; if (_look.Tab) { Painter2D painter = context.painter2D; Color fill = pressed ? LuneUiTheme.WithAlpha(LuneUiTheme.BronzeLight, .08f) : _hover && !_disabled ? new Color(1f, 1f, 1f, .04f) : Color.clear; Color line = pressed ? LuneUiTheme.BronzeLight : _hover && !_disabled ? LuneUiTheme.WithAlpha(LuneUiTheme.Bronze, .5f) : Color.clear; if (fill.a > 0f) LunePlates.Polygon(painter, new[] { rect.min, new Vector2(rect.xMax, rect.yMin), rect.max, new Vector2(rect.xMin, rect.yMax) }, fill); if (line.a > 0f) LunePlates.Line(painter, new Vector2(rect.xMin, rect.yMax - 1f), new Vector2(rect.xMax, rect.yMax - 1f), line, 2f); return; } LunePlateStyle plate = _look.PlateFor(_disabled, pressed, _hover && !_disabled); if (plate != null) LunePlates.Draw(context, rect, plate); } } /// A LuneGlyphs symbol as an element (Godot LuneGlyph). Ignores the mouse. public sealed class LuneGlyphElement : VisualElement { string _glyph; Color _color; public Color Accent; public LuneGlyphElement(string glyph, Color color, float edge = 18f) { _glyph = glyph; _color = color; pickingMode = PickingMode.Ignore; style.width = edge; style.height = edge; style.flexShrink = 0f; style.alignSelf = Align.Center; generateVisualContent += context => { if (!string.IsNullOrEmpty(_glyph)) LuneGlyphs.Draw(context.painter2D, _glyph, new Rect(0f, 0f, layout.width, layout.height), _color, Accent); }; } public string Glyph { get { return _glyph; } set { if (value == _glyph) return; _glyph = value; MarkDirtyRepaint(); } } public Color Color { get { return _color; } set { if (value == _color) return; _color = value; MarkDirtyRepaint(); } } public float Edge { set { style.width = value; style.height = value; } } } /// Thin value bar drawn directly: dark track, fill, a top highlight and optional ticks (Godot LuneHpBar). public sealed class LuneHpBar : VisualElement { float _fraction = 1f; Color _fill = LuneUiTheme.Good; public int Ticks; public Color Track = new Color(0f, 0f, 0f, .55f); public LuneHpBar(float width = 60f, float height = 6f) { pickingMode = PickingMode.Ignore; style.minWidth = width; style.height = height; style.flexShrink = 0f; generateVisualContent += Draw; } public float Fraction { get { return _fraction; } set { value = Mathf.Clamp01(value); if (Mathf.Abs(value - _fraction) <= .0005f) return; _fraction = value; MarkDirtyRepaint(); } } public Color Fill { get { return _fill; } set { if (value == _fill) return; _fill = value; MarkDirtyRepaint(); } } void Draw(MeshGenerationContext context) { Painter2D painter = context.painter2D; var rect = new Rect(0f, 0f, layout.width, layout.height); LunePlates.Polygon(painter, Corners(rect), Track); if (_fraction > 0f) { var filled = new Rect(0f, 0f, Mathf.Max(1f, rect.width * _fraction), rect.height); LunePlates.Polygon(painter, Corners(filled), _fill); LunePlates.Line(painter, new Vector2(0f, .5f), new Vector2(filled.width, .5f), new Color(1f, 1f, 1f, .28f), 1f); } for (int index = 1; index < Ticks; index++) { float x = Mathf.Round(rect.width * index / Ticks); LunePlates.Line(painter, new Vector2(x, 0f), new Vector2(x, rect.height), new Color(0f, 0f, 0f, .45f), 1f); } LunePlates.Outline(painter, Corners(new Rect(.5f, .5f, rect.width - 1f, rect.height - 1f)), new Color(1f, 1f, 1f, .12f), 1f); } public static Vector2[] Corners(Rect rect) { return new[] { rect.min, new Vector2(rect.xMax, rect.yMin), rect.max, new Vector2(rect.xMin, rect.yMax) }; } /// Green above 60 %, amber above 30 %, red below; the team colour when given. public static Color HealthColor(float value, Color? baseColor = null) { if (value > .6f) return baseColor ?? LuneUiTheme.Good; if (value > .3f) return LuneUiTheme.Warn; return LuneUiTheme.Danger; } } /// /// One building as a symbol in the team's shape language (round = Blau, angular = Rot) with its /// HP as a bar underneath (Godot LuneTowerIndicator). Destroyed buildings remain as a hollow /// outline with a cross. /// public sealed class LuneTowerIndicator : VisualElement { public int Team; public string Tier = "outer"; public bool Alive = true; public float Fraction = 1f; public bool Attackable; public LuneTowerIndicator(float width = 22f, float height = 27f) { pickingMode = PickingMode.Position; style.width = width; style.height = height; style.flexShrink = 0f; generateVisualContent += Draw; } public bool SetState(bool alive, float fraction, bool attackable) { float value = Mathf.Round(Mathf.Clamp01(fraction) * 100f) / 100f; if (alive == Alive && Mathf.Approximately(value, Fraction) && attackable == Attackable) return false; Alive = alive; Fraction = value; Attackable = attackable; MarkDirtyRepaint(); return true; } void Draw(MeshGenerationContext context) { Painter2D painter = context.painter2D; float edge = Mathf.Min(layout.width, layout.height - 5f); var box = new Rect((layout.width - edge) * .5f, 0f, edge, edge); Color tint = LuneUiTheme.TeamColor(Team); string shape = "shape_" + Mathf.Clamp(Team, 0, 1); if (Alive) { LuneGlyphs.Draw(painter, shape, box, LuneUiTheme.Lightened(LuneUiTheme.TeamDark[Mathf.Clamp(Team, 0, 1)], .1f)); LuneGlyphs.Draw(painter, shape, Grow(box, -edge * .08f), LuneUiTheme.WithAlpha(tint, .35f)); LuneGlyphs.Draw(painter, Tier == "core" ? "ancient" : "tower", Grow(box, -edge * .24f), LuneUiTheme.Hex("f4efe4")); var bar = new Rect(box.x + 1f, layout.height - 3f, box.width - 2f, 3f); LunePlates.Polygon(painter, LuneHpBar.Corners(bar), new Color(0f, 0f, 0f, .6f)); if (Fraction > 0f) LunePlates.Polygon(painter, LuneHpBar.Corners(new Rect(bar.x, bar.y, bar.width * Fraction, bar.height)), LuneHpBar.HealthColor(Fraction, tint)); } else { LuneGlyphs.Draw(painter, shape, box, new Color(0f, 0f, 0f, .35f)); LuneGlyphs.Draw(painter, "close", Grow(box, -edge * .18f), new Color(.5f, .52f, .55f, .8f)); } } static Rect Grow(Rect rect, float amount) { return new Rect(rect.x - amount, rect.y - amount, rect.width + amount * 2f, rect.height + amount * 2f); } } /// /// Small labelled state (RUHIG, KRITISCH, BEREIT, BOT …; Godot LuneStatusBadge). Always colour /// plus text and, where it helps, a symbol; never colour alone. /// public sealed class LuneStatusBadge : VisualElement { readonly LuneGlyphElement _glyph; readonly Label _label; public string Text { get; private set; } = ""; Color _color; bool _filled; public LuneStatusBadge() { pickingMode = PickingMode.Ignore; style.flexDirection = FlexDirection.Row; style.alignItems = Align.Center; style.alignSelf = Align.Center; style.flexShrink = 0f; style.paddingLeft = 7f; style.paddingRight = 7f; style.paddingTop = 1f; style.paddingBottom = 1f; style.borderTopLeftRadius = 4f; style.borderTopRightRadius = 4f; style.borderBottomLeftRadius = 4f; style.borderBottomRightRadius = 4f; style.borderLeftWidth = 1f; style.borderRightWidth = 1f; style.borderTopWidth = 1f; style.borderBottomWidth = 1f; _glyph = LuneUi.Glyph("", Color.white, 12f, this); _glyph.style.marginRight = 4f; LuneUi.Show(_glyph, false); _label = LuneUi.Label("", "HudBadge", this); } public void SetState(string text, Color color, string glyph = "", bool filled = false) { if (text == Text && color == _color && filled == _filled && glyph == _glyph.Glyph) return; Text = text; _color = color; _filled = filled; _label.text = text; Color ink = filled ? LuneUiTheme.Hex("15110b") : LuneUiTheme.Lightened(color, .15f); LuneUi.SetColor(_label, ink); LuneUi.Show(_glyph, !string.IsNullOrEmpty(glyph)); _glyph.Glyph = glyph; _glyph.Color = ink; Color background = filled ? LuneUiTheme.WithAlpha(color, .92f) : LuneUiTheme.WithAlpha(LuneUiTheme.Darkened(color, .75f), .85f); Color border = filled ? LuneUiTheme.Lightened(color, .2f) : color; style.backgroundColor = background; style.borderLeftColor = border; style.borderRightColor = border; style.borderTopColor = border; style.borderBottomColor = border; } } /// Tabs with a bronze underline for the open one (Godot LuneTabBar). Select changes only the display; TabChanged fires on input. public sealed class LuneTabBar : VisualElement { public event Action TabChanged; public int Current { get; private set; } public readonly System.Collections.Generic.List Tabs = new System.Collections.Generic.List(); public LuneTabBar() { pickingMode = PickingMode.Ignore; style.flexDirection = FlexDirection.Row; } public LuneButton AddTab(string title, string tipTitle = "", string tipText = "") { var tab = new LuneButton(title, LuneButtons.HudTab, this); int index = Tabs.Count; if (index > 0) tab.style.marginLeft = 2f; tab.Clicked += () => { Select(index); TabChanged?.Invoke(index); }; if (tipTitle.Length > 0) LuneTooltip.Attach(tab, () => new LuneTip { Title = tipTitle, Text = tipText }); Tabs.Add(tab); tab.On = index == 0; return tab; } public void Select(int index) { Current = Mathf.Clamp(index, 0, Tabs.Count - 1); for (int i = 0; i < Tabs.Count; i++) Tabs[i].On = i == Current; } } }