diff options
| author | hazel <hazel@hazelthats.me> | 2026-01-26 22:04:39 +0100 |
|---|---|---|
| committer | hazel <hazel@hazelthats.me> | 2026-01-26 22:04:39 +0100 |
| commit | 567c422f8cd42eba2437f9a8c2522716a1649be7 (patch) | |
| tree | 93c5b296f3b7c14b626d0aadf5cad37764c41c74 /source/ui/elements | |
| download | celesteia-567c422f8cd42eba2437f9a8c2522716a1649be7.tar.gz celesteia-567c422f8cd42eba2437f9a8c2522716a1649be7.tar.bz2 celesteia-567c422f8cd42eba2437f9a8c2522716a1649be7.zip | |
celesteia archive, last updated april 9th 2024
Signed-off-by: hazel <hazel@hazelthats.me>
Diffstat (limited to 'source/ui/elements')
| -rw-r--r-- | source/ui/elements/Button.cs | 173 | ||||
| -rw-r--r-- | source/ui/elements/Clickable.cs | 21 | ||||
| -rw-r--r-- | source/ui/elements/Container.cs | 91 | ||||
| -rw-r--r-- | source/ui/elements/Element.cs | 53 | ||||
| -rw-r--r-- | source/ui/elements/IClickable.cs | 9 | ||||
| -rw-r--r-- | source/ui/elements/IContainer.cs | 12 | ||||
| -rw-r--r-- | source/ui/elements/IElement.cs | 52 | ||||
| -rw-r--r-- | source/ui/elements/Image.cs | 59 | ||||
| -rw-r--r-- | source/ui/elements/Label.cs | 62 | ||||
| -rw-r--r-- | source/ui/elements/game/CraftingRecipeSlot.cs | 173 | ||||
| -rw-r--r-- | source/ui/elements/game/CraftingWindow.cs | 62 | ||||
| -rw-r--r-- | source/ui/elements/game/InventorySlot.cs | 191 | ||||
| -rw-r--r-- | source/ui/elements/game/InventoryWindow.cs | 56 | ||||
| -rw-r--r-- | source/ui/elements/game/PauseMenu.cs | 74 | ||||
| -rw-r--r-- | source/ui/elements/game/controls/ControlTips.cs | 51 | ||||
| -rw-r--r-- | source/ui/elements/game/tooltips/CraftingTooltipDisplay.cs | 97 | ||||
| -rw-r--r-- | source/ui/elements/game/tooltips/ItemDisplay.cs | 21 | ||||
| -rw-r--r-- | source/ui/elements/game/tooltips/ItemTooltipDisplay.cs | 62 | ||||
| -rw-r--r-- | source/ui/elements/game/tooltips/TooltipDisplay.cs | 8 |
19 files changed, 1327 insertions, 0 deletions
diff --git a/source/ui/elements/Button.cs b/source/ui/elements/Button.cs new file mode 100644 index 0000000..81a6aed --- /dev/null +++ b/source/ui/elements/Button.cs @@ -0,0 +1,173 @@ +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Input; +using MonoGame.Extended.TextureAtlases; + +namespace Celesteia.UI.Elements { + public class Button : Clickable { + public Button(Rect rect) { + SetRect(rect); + } + + public Button SetNewRect(Rect rect) { + SetRect(rect); + return this; + } + + public Button SetPivotPoint(Vector2 pivot) { + SetPivot(pivot); + return this; + } + + // TEXT PROPERTIES + + private TextProperties _text; + + public Button SetTextProperties(TextProperties text) { + _text = text; + return this; + } + + public Button SetText(string text) { + _text.SetText(text); + return this; + } + + // COLOR PROPERTIES + + private ButtonColorGroup _colorGroup = new ButtonColorGroup(Color.White); + private Color ButtonColor = Color.White; + private bool ButtonEnabled = true; + + public Button SetColorGroup(ButtonColorGroup colorGroup) { + _colorGroup = colorGroup; + return this; + } + + public Button SetButtonEnabled(bool enabled) { + ButtonEnabled = enabled; + return this; + } + + // CLICKING PROPERTIES + + private ClickEvent _onMouseDown = null; + private ClickEvent _onMouseUp = null; + + public Button SetOnMouseDown(ClickEvent func) { + _onMouseDown = func; + return this; + } + + public Button SetOnMouseUp(ClickEvent func) { + _onMouseUp = func; + return this; + } + + public override void OnMouseDown(MouseButton button, Point position) { + base.OnMouseDown(button, position); + _onMouseDown?.Invoke(button, position); + } + + public override void OnMouseUp(MouseButton button, Point position) { + base.OnMouseUp(button, position); + _onMouseUp?.Invoke(button, position); + } + + // DRAWING PROPERTIES + + private Texture2D _texture; + private TextureAtlas _patches; + private int _patchSize; + + public Button SetTexture(Texture2D texture) { + _texture = texture; + return this; + } + + public Button MakePatches(int size) { + if (_texture != null) { + _patchSize = size; + _patches = TextureAtlas.Create("buttonPatches", _texture, _patchSize, _patchSize); + } + return this; + } + + // https://gamedev.stackexchange.com/a/118255 + private float _colorAmount = 0.0f; + private bool _prevMouseOver = false; + private bool _prevClicked = false; + public override void Update(GameTime gameTime, out bool clickedAnything) { + clickedAnything = false; + if (ButtonColor == GetTargetColor()) return; + + if (_prevMouseOver != GetMouseOver() || _prevClicked != GetClicked()) _colorAmount = 0.0f; + + _colorAmount += (float)gameTime.ElapsedGameTime.TotalSeconds / 0.5f; + + if (_colorAmount > 1.0f) + _colorAmount = 0.0f; + + ButtonColor = Color.Lerp(ButtonColor, GetTargetColor(), _colorAmount); + + _prevMouseOver = GetMouseOver(); + _prevClicked = GetClicked(); + } + + Rectangle rectangle; + public override void Draw(SpriteBatch spriteBatch) + { + rectangle = GetRectangle(); + + // Draw the button's texture. + if (_patches != null) ImageUtilities.DrawPatched(spriteBatch, rectangle, _patches, _patchSize, ButtonColor); + else spriteBatch.Draw(GetTexture(spriteBatch), rectangle, null, ButtonColor); + + TextUtilities.DrawAlignedText(spriteBatch, rectangle, _text); + } + + public Texture2D GetTexture(SpriteBatch spriteBatch) { + if (_texture == null) { + _texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1); + _texture.SetData(new[] { Color.Gray }); + } + + return _texture; + } + + private Color GetTargetColor() { + return ButtonEnabled ? (GetMouseOver() ? (GetClicked() ? _colorGroup.Active : _colorGroup.Hover) : _colorGroup.Normal) : _colorGroup.Disabled; + } + + public Button Clone() { + return new Button(GetRect()) + .SetPivotPoint(GetPivot()) + .SetOnMouseDown(_onMouseDown) + .SetOnMouseUp(_onMouseUp) + .SetTexture(_texture) + .MakePatches(_patchSize) + .SetTextProperties(_text) + .SetColorGroup(_colorGroup); + } + } + + public struct ButtonColorGroup { + public Color Normal; + public Color Disabled; + public Color Hover; + public Color Active; + + public ButtonColorGroup(Color normal, Color disabled, Color hover, Color active) { + Normal = normal; + Disabled = disabled; + Hover = hover; + Active = active; + } + + public ButtonColorGroup(Color normal, Color disabled, Color hover) : this (normal, disabled, hover, normal) {} + + public ButtonColorGroup(Color normal, Color disabled) : this (normal, disabled, normal, normal) {} + public ButtonColorGroup(Color normal) : this (normal, normal, normal, normal) {} + } +}
\ No newline at end of file diff --git a/source/ui/elements/Clickable.cs b/source/ui/elements/Clickable.cs new file mode 100644 index 0000000..01436a3 --- /dev/null +++ b/source/ui/elements/Clickable.cs @@ -0,0 +1,21 @@ +using Microsoft.Xna.Framework; +using MonoGame.Extended.Input; + +namespace Celesteia.UI.Elements { + public class Clickable : Element, IClickable + { + private bool _clicked; + + public override void OnMouseOut() { + _clicked = false; + base.OnMouseOut(); + } + public virtual void OnMouseDown(MouseButton button, Point position) => _clicked = true; + public virtual void OnMouseUp(MouseButton button, Point position) => _clicked = false; + + public bool GetClicked() => _clicked; + } + + public delegate void HoverEvent(); + public delegate void ClickEvent(MouseButton button, Point position); +}
\ No newline at end of file diff --git a/source/ui/elements/Container.cs b/source/ui/elements/Container.cs new file mode 100644 index 0000000..9080999 --- /dev/null +++ b/source/ui/elements/Container.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using Celesteia.Game.Input; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Input; + +namespace Celesteia.UI.Elements { + public class Container : Element, IContainer + { + private List<IElement> Children; + + public Container(Rect rect) { + SetRect(rect); + Children = new List<IElement>(); + } + + public void AddChild(IElement element) { + Children.Add(element); + element.SetParent(this); + } + + public List<IElement> GetChildren() => Children; + + public override void Draw(SpriteBatch spriteBatch) + { + Children.ForEach(element => { if (element.GetEnabled()) element.Draw(spriteBatch); }); + } + + private Point _mousePosition; + + public override void Update(GameTime gameTime, out bool clickedAnything) + { + clickedAnything = false; + if (!UIReferences.GUIEnabled) return; + + foreach (IElement element in Children) { + element.Update(gameTime, out clickedAnything); + } + + _mousePosition = MouseHelper.Position; + + if (MouseHelper.Pressed(MouseButton.Left)) clickedAnything = ResolveMouseDown(MouseButton.Left); + else if (MouseHelper.Pressed(MouseButton.Right)) clickedAnything = ResolveMouseDown(MouseButton.Right); + + if (MouseHelper.Released(MouseButton.Left)) clickedAnything = ResolveMouseUp(MouseButton.Left); + else if (MouseHelper.Released(MouseButton.Right)) clickedAnything = ResolveMouseUp(MouseButton.Right); + + ResolveMouseOver(); + } + + public bool ResolveMouseDown(MouseButton button) { + bool clicked = false; + Children.FindAll(x => x is Clickable).ForEach(element => { + if (!element.GetEnabled()) return; + Clickable clickable = element as Clickable; + + if (clicked = clickable.GetRectangle().Contains(_mousePosition)) + clickable.OnMouseDown(button, _mousePosition); + }); + return clicked; + } + + public bool ResolveMouseUp(MouseButton button) { + bool clicked = false; + Children.FindAll(x => x is Clickable).ForEach(element => { + if (!element.GetEnabled()) return; + Clickable clickable = element as Clickable; + + if (clickable.GetRectangle().Contains(_mousePosition)) { + clicked = true; + clickable.OnMouseUp(button, _mousePosition); + } + }); + return clicked; + } + + public void ResolveMouseOver() { + Children.ForEach(element => { + if (!element.GetEnabled()) return; + bool over = element.GetRectangle().Contains(_mousePosition); + if (over && !element.GetMouseOver()) { + element.OnMouseIn(); + } else if (!over && element.GetMouseOver()) element.OnMouseOut(); + }); + } + + public void Dispose() { + Children.Clear(); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/Element.cs b/source/ui/elements/Element.cs new file mode 100644 index 0000000..b95a693 --- /dev/null +++ b/source/ui/elements/Element.cs @@ -0,0 +1,53 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements { + public class Element : IElement + { + private bool _enabled = true; + private Rect _rect; + private bool _isMouseOver; + private IContainer _parent; + private Vector2 _pivot; + + public virtual bool GetEnabled() => _enabled && (_parent == null || _parent.GetEnabled()); + public virtual IContainer GetParent() => _parent; + public virtual Vector2 GetPivot() => _pivot; + public virtual Rect GetRect() => _rect; + public virtual void MoveTo(Point point) { + _rect.SetX(AbsoluteUnit.WithValue(point.X)); + _rect.SetY(AbsoluteUnit.WithValue(point.Y)); + } + + // Get element rectangle with pivot. + public virtual Rectangle GetRectangle() { + Rectangle r = GetRect().ResolveRectangle(); + + if (GetParent() != null) { + r.X += GetParent().GetRectangle().X; + r.Y += GetParent().GetRectangle().Y; + } + + r.X -= (int)MathF.Round(_pivot.X * r.Width); + r.Y -= (int)MathF.Round(_pivot.Y * r.Height); + + return r; + } + + // Interface setters. + public virtual bool SetEnabled(bool value) => _enabled = value; + public virtual void SetParent(IContainer container) => _parent = container; + public virtual void SetPivot(Vector2 pivot) => _pivot = pivot; + public virtual void SetRect(Rect rect) => _rect = rect; + + // Mouse functions + public virtual void OnMouseIn() => _isMouseOver = true; + public virtual void OnMouseOut() => _isMouseOver = false; + public virtual bool GetMouseOver() => _isMouseOver; + + // Engine functions. + public virtual void Update(GameTime gameTime, out bool clickedAnything) { clickedAnything = false; } + public virtual void Draw(SpriteBatch spriteBatch) { } + } +}
\ No newline at end of file diff --git a/source/ui/elements/IClickable.cs b/source/ui/elements/IClickable.cs new file mode 100644 index 0000000..19c2848 --- /dev/null +++ b/source/ui/elements/IClickable.cs @@ -0,0 +1,9 @@ +using Microsoft.Xna.Framework; +using MonoGame.Extended.Input; + +namespace Celesteia.UI.Elements { + public interface IClickable : IElement { + void OnMouseDown(MouseButton button, Point position); + void OnMouseUp(MouseButton button, Point position); + } +}
\ No newline at end of file diff --git a/source/ui/elements/IContainer.cs b/source/ui/elements/IContainer.cs new file mode 100644 index 0000000..0d3550c --- /dev/null +++ b/source/ui/elements/IContainer.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Celesteia.UI.Elements { + public interface IContainer : IElement, IDisposable { + // Get the element's children. + List<IElement> GetChildren(); + + // Add to the element's children. + void AddChild(IElement element); + } +}
\ No newline at end of file diff --git a/source/ui/elements/IElement.cs b/source/ui/elements/IElement.cs new file mode 100644 index 0000000..094d7b7 --- /dev/null +++ b/source/ui/elements/IElement.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements { + public interface IElement { + // Is the element enabled? + bool GetEnabled(); + + // Set whether the element is enabled. + bool SetEnabled(bool value); + + // Get the containing rect of the element. + Rect GetRect(); + + // Set the containing rect of the element. + void SetRect(Rect rect); + + // Move to a point. + void MoveTo(Point point); + + // Get the rectangle with a pivot point. + Rectangle GetRectangle(); + + // Gets the pivot point of the element; + Vector2 GetPivot(); + + // Sets the pivot point of the element; + void SetPivot(Vector2 pivot); + + // Called when the mouse position is within the element's containing rect. + void OnMouseIn(); + + // Called when the mouse position is within the element's containing rect. + void OnMouseOut(); + + // Get if the element has the mouse over it. + bool GetMouseOver(); + + // Update the element. + void Update(GameTime gameTime, out bool clickedAnything); + + // Draw the element. + void Draw(SpriteBatch spriteBatch); + + // Get the element's parent. + IContainer GetParent(); + + // Set the element's parent. + void SetParent(IContainer container); + } +}
\ No newline at end of file diff --git a/source/ui/elements/Image.cs b/source/ui/elements/Image.cs new file mode 100644 index 0000000..de661e8 --- /dev/null +++ b/source/ui/elements/Image.cs @@ -0,0 +1,59 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.TextureAtlases; + +namespace Celesteia.UI.Elements { + public class Image : Element { + private Texture2D _texture; + public Color _color; + + public Image(Rect rect) { + SetRect(rect); + } + + public Image SetTexture(Texture2D texture) { + _texture = texture; + return this; + } + + public Image SetColor(Color color) { + _color = color; + return this; + } + + public Image SetPivotPoint(Vector2 pivot) { + SetPivot(pivot); + return this; + } + + private TextureAtlas _patches; + private int _patchSize; + public Image MakePatches(int size) { + if (_texture != null) { + _patchSize = size; + _patches = TextureAtlas.Create("patches", _texture, _patchSize, _patchSize); + } + return this; + } + + public override void Draw(SpriteBatch spriteBatch) + { + if (_patches != null) ImageUtilities.DrawPatched(spriteBatch, GetRectangle(), _patches, _patchSize, _color); + else spriteBatch.Draw(GetTexture(spriteBatch), GetRectangle(), null, _color); + } + + public Texture2D GetTexture(SpriteBatch spriteBatch) + { + if (_texture == null) { + // Make a new texture. + _texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1); + + // Set the default texture to one gray pixel. + _texture.SetData(new[] { Color.Gray }); + } + + return _texture; + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/Label.cs b/source/ui/elements/Label.cs new file mode 100644 index 0000000..c875791 --- /dev/null +++ b/source/ui/elements/Label.cs @@ -0,0 +1,62 @@ +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements { + public class Label : Element { + private Texture2D _background; + private TextProperties _text; + + public Label(Rect rect) { + SetRect(rect); + } + + public Label SetNewRect(Rect rect) { + SetRect(rect); + return this; + } + + public Label SetPivotPoint(Vector2 pivot) { + SetPivot(pivot); + return this; + } + + public Label SetBackground(Texture2D background) { + SetTexture(background); + return this; + } + + public Label SetText(string text) { + _text.SetText(text); + return this; + } + + public Label SetColor(Color color) { + _text.SetColor(color); + return this; + } + + public Label SetTextProperties(TextProperties text) { + _text = text; + return this; + } + + public override void Draw(SpriteBatch spriteBatch) + { + // Draw the label's background, if present. + if (_background != null) spriteBatch.Draw(GetTexture(), GetRectangle(), null, Color.White); + + TextUtilities.DrawAlignedText(spriteBatch, GetRectangle(), _text); + } + + public Texture2D GetTexture() => _background; + public void SetTexture(Texture2D background) => _background = background; + + public Label Clone() { + return new Label(GetRect()) + .SetPivotPoint(GetPivot()) + .SetBackground(GetTexture()) + .SetTextProperties(_text); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/CraftingRecipeSlot.cs b/source/ui/elements/game/CraftingRecipeSlot.cs new file mode 100644 index 0000000..72101d8 --- /dev/null +++ b/source/ui/elements/game/CraftingRecipeSlot.cs @@ -0,0 +1,173 @@ +using System; +using Celesteia.Game.Components.Items; +using Celesteia.Resources.Types; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Input; +using MonoGame.Extended.TextureAtlases; + +namespace Celesteia.UI.Elements.Game { + public class CraftingRecipeSlot : Clickable { + public const float SLOT_SIZE = 64f; + public const float SLOT_SPACING = 16f; + + public Inventory referenceInv; + + public CraftingRecipeSlot(Rect rect) { + SetRect(rect); + } + + public CraftingRecipeSlot SetNewRect(Rect rect) { + SetRect(rect); + return this; + } + + // RECIPE REFERENCE PROPERTIES + private Recipe _recipe; + public CraftingRecipeSlot SetRecipe(Recipe recipe) { + _recipe = recipe; + return this; + } + + // DRAWING PROPERTIES + + private Texture2D _texture; + private TextureAtlas _patches; + private int _patchSize; + + public CraftingRecipeSlot SetTexture(Texture2D texture) { + _texture = texture; + return this; + } + + public CraftingRecipeSlot SetPatches(TextureAtlas patches, int size) { + if (_texture != null) { + _patchSize = size; + _patches = patches; + } + return this; + } + + // TEXT PROPERTIES + + private TextProperties _text; + + public CraftingRecipeSlot SetTextProperties(TextProperties text) { + _text = text; + return this; + } + + public CraftingRecipeSlot SetText(string text) { + _text.SetText(text); + return this; + } + + // CLICKING PROPERTIES + + private ClickEvent _onMouseDown = null; + private ClickEvent _onMouseUp = null; + public delegate void CraftHoverEvent(Recipe recipe); + private CraftHoverEvent _onMouseIn = null; + private HoverEvent _onMouseOut = null; + + public CraftingRecipeSlot SetOnMouseDown(ClickEvent func) { + _onMouseDown = func; + return this; + } + + public CraftingRecipeSlot SetOnMouseUp(ClickEvent func) { + _onMouseUp = func; + return this; + } + + public CraftingRecipeSlot SetOnMouseIn(CraftHoverEvent func) { + _onMouseIn = func; + return this; + } + + public CraftingRecipeSlot SetOnMouseOut(HoverEvent func) { + _onMouseOut = func; + return this; + } + + public override void OnMouseDown(MouseButton button, Point position) { + base.OnMouseDown(button, position); + _onMouseDown?.Invoke(button, position); + } + + public override void OnMouseUp(MouseButton button, Point position) { + base.OnMouseUp(button, position); + _onMouseUp?.Invoke(button, position); + } + + public override void OnMouseIn() { + base.OnMouseIn(); + if (_recipe != null) _onMouseIn?.Invoke(_recipe); + } + + public override void OnMouseOut() { + base.OnMouseOut(); + _onMouseOut?.Invoke(); + } + + private Rectangle GetScaledTriangle(Rectangle r, float scale) { + int newWidth = (int)Math.Round(r.Width * scale); + int newHeight = (int)Math.Round(r.Height * scale); + return new Rectangle( + (int)Math.Round(r.X + ((r.Width - newWidth) / 2f)), + (int)Math.Round(r.Y + ((r.Height - newHeight) / 2f)), + newWidth, + newHeight + ); + } + + Color color; + public override void Update(GameTime gameTime, out bool clickedAnything) { + base.Update(gameTime, out clickedAnything); + + if (!this.GetEnabled()) return; + color = _recipe.Craftable(referenceInv) ? Color.White : Color.Gray; + } + + Rectangle rectangle; + Rectangle itemRectangle; + Rectangle textRectangle; + public override void Draw(SpriteBatch spriteBatch) + { + if (_recipe == null) return; + + rectangle = GetRectangle(); + itemRectangle = GetScaledTriangle(rectangle, .6f); + textRectangle = GetScaledTriangle(rectangle, .4f); + + // Draw the slot's texture. + if (_patches != null) ImageUtilities.DrawPatched(spriteBatch, rectangle, _patches, _patchSize, color); + else spriteBatch.Draw(GetTexture(spriteBatch), rectangle, null, color); + + spriteBatch.Draw(_recipe.Result.GetItemType().Sprite, itemRectangle, color); + TextUtilities.DrawAlignedText(spriteBatch, textRectangle, _text.GetFont(), _recipe.Result.Amount + "", _text.GetColor(), _text.GetAlignment(), _text.GetFontSize()); + } + + public Texture2D GetTexture(SpriteBatch spriteBatch) { + if (_texture == null) { + _texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1); + _texture.SetData(new[] { Color.Gray }); + } + + return _texture; + } + + public CraftingRecipeSlot Clone() { + return new CraftingRecipeSlot(GetRect()) + .SetRecipe(_recipe) + .SetOnMouseDown(_onMouseDown) + .SetOnMouseUp(_onMouseUp) + .SetOnMouseIn(_onMouseIn) + .SetOnMouseOut(_onMouseOut) + .SetTextProperties(_text) + .SetTexture(_texture) + .SetPatches(_patches, _patchSize); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/CraftingWindow.cs b/source/ui/elements/game/CraftingWindow.cs new file mode 100644 index 0000000..e5bcc71 --- /dev/null +++ b/source/ui/elements/game/CraftingWindow.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Celesteia.Game.Components.Items; +using Celesteia.GUIs.Game; +using Celesteia.Resources; +using Celesteia.Resources.Management; +using Celesteia.Resources.Types; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements.Game { + public class CraftingWindow : Container { + private Inventory _referenceInventory; + private Image background; + private GameGUI _gameGui; + + public CraftingWindow(GameGUI gameGui, Rect rect, Texture2D backgroundImage, Inventory inventory, CraftingRecipeSlot template) : base(rect) { + _gameGui = gameGui; + + _referenceInventory = inventory; + + background = new Image(Rect.RelativeFull(rect)).SetTexture(backgroundImage).MakePatches(4).SetColor(Color.White); + AddChild(background); + + AddRecipes(27, template); + } + + int columns = 9; + private void AddRecipes(int amountPerPage, CraftingRecipeSlot template) { + int rows = (int)Math.Ceiling(amountPerPage / (double)columns); + + float o = CraftingRecipeSlot.SLOT_SPACING; + int index = 0; + int i = 0; + for (int row = 0; row < rows; row++) + for (int column = 0; column < columns; column++) { + if (i >= ResourceManager.Recipes.Recipes.Count) break; + + int slotNumber = i; + Recipe recipe = ResourceManager.Recipes.Recipes[index]; + CraftingRecipeSlot slot = template.Clone() + .SetNewRect(template.GetRect() + .SetX(AbsoluteUnit.WithValue(column * CraftingRecipeSlot.SLOT_SIZE + (column * CraftingRecipeSlot.SLOT_SPACING) + o)) + .SetY(AbsoluteUnit.WithValue(row * CraftingRecipeSlot.SLOT_SIZE + (row * CraftingRecipeSlot.SLOT_SPACING) + o)) + ) + .SetRecipe(recipe) + .SetOnMouseUp((button, point) => { + if (button == MonoGame.Extended.Input.MouseButton.Left) { + recipe.Craft(_referenceInventory); + } + }); + slot.referenceInv = _referenceInventory; + slot.SetPivot(new Vector2(0f, 0f)); + + index++; + i++; + + AddChild(slot); + } + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/InventorySlot.cs b/source/ui/elements/game/InventorySlot.cs new file mode 100644 index 0000000..01663b4 --- /dev/null +++ b/source/ui/elements/game/InventorySlot.cs @@ -0,0 +1,191 @@ +using System; +using Celesteia.Game.Components.Items; +using Celesteia.Resources.Types; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Input; +using MonoGame.Extended.TextureAtlases; + +namespace Celesteia.UI.Elements.Game { + public class InventorySlot : Clickable { + public const float SLOT_SIZE = 64f; + public const float SLOT_SPACING = 16f; + + public InventorySlot(Rect rect) { + SetRect(rect); + } + + public InventorySlot SetNewRect(Rect rect) { + SetRect(rect); + return this; + } + + // SELECTION + + private bool _selected = false; + public InventorySlot SetSelected(bool selected) { + _selected = selected; + return this; + } + + public bool GetSelected() { + return _selected; + } + + + // INVENTORY REFERENCE PROPERTIES + + private Inventory _inventory; + private int _slot; + + public InventorySlot SetReferenceInventory(Inventory inventory) { + _inventory = inventory; + return this; + } + + public InventorySlot SetSlot(int slot) { + _slot = slot; + return this; + } + + // DRAWING PROPERTIES + + private Texture2D _texture; + private TextureAtlas _patches; + private int _patchSize; + + public InventorySlot SetTexture(Texture2D texture) { + _texture = texture; + return this; + } + + public InventorySlot SetPatches(TextureAtlas patches, int size) { + if (_texture != null) { + _patchSize = size; + _patches = patches; + } + return this; + } + + // TEXT PROPERTIES + + private TextProperties _text; + + public InventorySlot SetTextProperties(TextProperties text) { + _text = text; + return this; + } + + public InventorySlot SetText(string text) { + _text.SetText(text); + return this; + } + + // CLICKING PROPERTIES + + private ClickEvent _onMouseDown = null; + private ClickEvent _onMouseUp = null; + public delegate void ItemHoverEvent(ItemType type); + private ItemHoverEvent _onMouseIn = null; + private HoverEvent _onMouseOut = null; + + public InventorySlot SetOnMouseDown(ClickEvent func) { + _onMouseDown = func; + return this; + } + + public InventorySlot SetOnMouseUp(ClickEvent func) { + _onMouseUp = func; + return this; + } + + public InventorySlot SetOnMouseIn(ItemHoverEvent func) { + _onMouseIn = func; + return this; + } + + public InventorySlot SetOnMouseOut(HoverEvent func) { + _onMouseOut = func; + return this; + } + + public override void OnMouseDown(MouseButton button, Point position) { + base.OnMouseDown(button, position); + _onMouseDown?.Invoke(button, position); + } + + public override void OnMouseUp(MouseButton button, Point position) { + base.OnMouseUp(button, position); + _onMouseUp?.Invoke(button, position); + } + + public override void OnMouseIn() { + base.OnMouseIn(); + if (_inventory.GetSlot(_slot) != null) _onMouseIn?.Invoke(_inventory.GetSlot(_slot).Type); + } + + public override void OnMouseOut() { + base.OnMouseOut(); + _onMouseOut?.Invoke(); + } + + private Rectangle GetScaledTriangle(Rectangle r, float scale) { + int newWidth = (int)Math.Round(r.Width * scale); + int newHeight = (int)Math.Round(r.Height * scale); + return new Rectangle( + (int)Math.Round(r.X + ((r.Width - newWidth) / 2f)), + (int)Math.Round(r.Y + ((r.Height - newHeight) / 2f)), + newWidth, + newHeight + ); + } + + Rectangle rectangle; + Rectangle itemRectangle; + Rectangle textRectangle; + ItemStack inSlot; + Color slightlyTransparent = new Color(255, 255, 255, 100); + public override void Draw(SpriteBatch spriteBatch) + { + if (_inventory == null) return; + + rectangle = GetRectangle(); + itemRectangle = GetScaledTriangle(rectangle, .6f); + textRectangle = GetScaledTriangle(rectangle, .4f); + + // Draw the slot's texture. + if (_patches != null) ImageUtilities.DrawPatched(spriteBatch, rectangle, _patches, _patchSize, _selected ? Color.DarkViolet : Color.White); + else spriteBatch.Draw(GetTexture(spriteBatch), rectangle, null, Color.White); + + // Draw item if present. + inSlot = _inventory.GetSlot(_slot); + if (inSlot != null) { + spriteBatch.Draw(inSlot.Type.Sprite, itemRectangle, Color.White); + if (inSlot.Amount > 1) TextUtilities.DrawAlignedText(spriteBatch, textRectangle, _text.GetFont(), $"{inSlot.Amount}", _text.GetColor(), _text.GetAlignment(), _text.GetFontSize()); + } else TextUtilities.DrawAlignedText(spriteBatch, rectangle, _text.GetFont(), $"{_slot + 1}", slightlyTransparent, TextAlignment.Center, 24f); + } + + public Texture2D GetTexture(SpriteBatch spriteBatch) { + if (_texture == null) { + _texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1); + _texture.SetData(new[] { Color.Gray }); + } + + return _texture; + } + + public InventorySlot Clone() { + return new InventorySlot(GetRect()) + .SetReferenceInventory(_inventory) + .SetOnMouseDown(_onMouseDown) + .SetOnMouseUp(_onMouseUp) + .SetOnMouseIn(_onMouseIn) + .SetOnMouseOut(_onMouseOut) + .SetSlot(_slot) + .SetTextProperties(_text) + .SetTexture(_texture) + .SetPatches(_patches, _patchSize); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/InventoryWindow.cs b/source/ui/elements/game/InventoryWindow.cs new file mode 100644 index 0000000..c8dba41 --- /dev/null +++ b/source/ui/elements/game/InventoryWindow.cs @@ -0,0 +1,56 @@ +using System; +using Celesteia.Game.Components.Items; +using Celesteia.GUIs.Game; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements.Game { + public class InventoryWindow : Container { + private Inventory _referenceInventory; + private Image background; + private GameGUI _gameGui; + + public InventoryWindow(GameGUI gameGui, Rect rect, Texture2D backgroundImage, Inventory inventory, int slots, int offset, InventorySlot template) : base(rect) { + _gameGui = gameGui; + + background = new Image(Rect.RelativeFull(rect)).SetTexture(backgroundImage).MakePatches(4).SetColor(Color.White); + AddChild(background); + + _referenceInventory = inventory; + + AddSlots(slots, offset, template); + } + + int columns = 9; + private void AddSlots(int amount, int offset, InventorySlot template) { + int rows = (int)Math.Ceiling(amount / (double)columns); + + float o = InventorySlot.SLOT_SPACING; + int i = 0; + for (int row = 0; row < rows; row++) + for (int column = 0; column < 9; column++) { + if (i > amount) break; + + int slotNumber = i + offset; + InventorySlot slot = template.Clone() + .SetNewRect(template.GetRect() + .SetX(AbsoluteUnit.WithValue(column * InventorySlot.SLOT_SIZE + (column * InventorySlot.SLOT_SPACING) + o)) + .SetY(AbsoluteUnit.WithValue(row * InventorySlot.SLOT_SIZE + (row * InventorySlot.SLOT_SPACING) + o)) + ) + .SetSlot(slotNumber) + .SetOnMouseUp((button, point) => { + ItemStack itemInSlot = _referenceInventory.GetSlot(slotNumber); + if ((int)_gameGui.State > 0) { + _referenceInventory.SetSlot(slotNumber, _gameGui.CursorItem); + _gameGui.CursorItem = itemInSlot; + } + }); + slot.SetPivot(new Vector2(0f, 0f)); + + i++; + + AddChild(slot); + } + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/PauseMenu.cs b/source/ui/elements/game/PauseMenu.cs new file mode 100644 index 0000000..39281ee --- /dev/null +++ b/source/ui/elements/game/PauseMenu.cs @@ -0,0 +1,74 @@ +using Celesteia.Game.Components.Items; +using Celesteia.GUIs.Game; +using Celesteia.Resources; +using Celesteia.Screens; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; + +namespace Celesteia.UI.Elements.Game { + public class PauseMenu : Container { + private Image background; + private IContainer centerMenu; + private GameGUI _gameGui; + + private float buttonRow(int number) => number * (buttonHeight + buttonSpacing); + private float buttonHeight = 56f; + private float buttonSpacing = 10f; + + public PauseMenu(GameGUI gameGui, Rect rect, Button buttonTemplate) : base(rect) { + _gameGui = gameGui; + + background = new Image(Rect.RelativeFull(rect)).SetColor(new Color(0, 0, 0, 100)); + AddChild(background); + + centerMenu = new Container(new Rect( + new RelativeUnit(0.5f, GetRect(), RelativeUnit.Orientation.Horizontal), + new RelativeUnit(0.5f, GetRect(), RelativeUnit.Orientation.Vertical), + AbsoluteUnit.WithValue(350f), + AbsoluteUnit.WithValue(2 * (buttonHeight + buttonSpacing) - buttonSpacing) + )); + AddChild(centerMenu); + + AddButtons(buttonTemplate); + } + + private void AddButtons(Button template) { + centerMenu.AddChild(new Label(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(buttonRow(-1)), + new RelativeUnit(1f, centerMenu.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(buttonHeight) + )) + .SetPivotPoint(new Vector2(0.5f, 0.5f)) + .SetTextProperties(new TextProperties().SetColor(Color.White).SetFont(ResourceManager.Fonts.GetFontType("Hobo")).SetFontSize(24f).SetTextAlignment(TextAlignment.Center)) + .SetText("Paused") + ); + + centerMenu.AddChild(template.Clone() + .SetNewRect(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(buttonRow(0)), + new RelativeUnit(1f, centerMenu.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(buttonHeight) + )) + .SetText("Back to Game") + .SetOnMouseUp((button, point) => { + _gameGui.TogglePause(); + }) + ); + + centerMenu.AddChild(template.Clone() + .SetNewRect(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(buttonRow(1)), + new RelativeUnit(1f, centerMenu.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(buttonHeight) + )) + .SetText("Return to Title") + .SetOnMouseUp((button, point) => { + _gameGui.Game.LoadScreen(new MainMenuScreen(_gameGui.Game),new MonoGame.Extended.Screens.Transitions.FadeTransition(_gameGui.Game.GraphicsDevice, Color.Black)); + }) + ); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/controls/ControlTips.cs b/source/ui/elements/game/controls/ControlTips.cs new file mode 100644 index 0000000..904b3cc --- /dev/null +++ b/source/ui/elements/game/controls/ControlTips.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Celesteia.Game.Input; +using Celesteia.Resources; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Celesteia.UI.Elements.Game.Controls { + public class ControlTips : Container { + private TextProperties _properties; + private Dictionary<Keys, string> _keyboardControls = new Dictionary<Keys, string>(); + private List<string> _lines = new List<string>(); + private List<Label> _labels = new List<Label>(); + + public ControlTips(Rect rect) : base(rect) { + _properties = new TextProperties() + .SetColor(Color.White) + .SetFont(ResourceManager.Fonts.DEFAULT) + .SetFontSize(12f) + .SetTextAlignment(TextAlignment.Left); + } + + private int lineHeight = 16; + private Rect LineRect(int line) => new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(line * lineHeight), + new RelativeUnit(1f, GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(lineHeight) + ); + + private void UpdateLines() { + _labels.Clear(); + + foreach (Keys keys in _keyboardControls.Keys) _lines.Add($"[{keys}] {_keyboardControls[keys]}"); + + for (int i = 0; i < _lines.Count; i++) { + Label label = new Label(LineRect(i - (_lines.Count / 2))) + .SetTextProperties(_properties) + .SetText(_lines[i]); + label.SetParent(this); + _labels.Add(label); + } + } + + public override void Draw(SpriteBatch spriteBatch) + { + foreach (Label l in _labels) l.Draw(spriteBatch); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/tooltips/CraftingTooltipDisplay.cs b/source/ui/elements/game/tooltips/CraftingTooltipDisplay.cs new file mode 100644 index 0000000..a7c6e2f --- /dev/null +++ b/source/ui/elements/game/tooltips/CraftingTooltipDisplay.cs @@ -0,0 +1,97 @@ +using Celesteia.Resources.Types; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements.Game.Tooltips { + public class CraftingTooltipDisplay : TooltipDisplay + { + private const float OUTER_SPACING = 16f; + private const float INNER_SPACING = 8f; + public readonly Container Content; + public readonly Label Title; + public readonly ItemDisplay Item; + public Container Recipe; + + public CraftingTooltipDisplay(Rect rect, Texture2D background) : base(rect) { + AddChild(new Image(Rect.RelativeFull(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(256f + (2 * OUTER_SPACING)), + AbsoluteUnit.WithValue(64f + (1 * INNER_SPACING) + (2 * OUTER_SPACING)) + ))).SetTexture(background).MakePatches(4).SetColor(Color.White)); + + Content = new Container(new Rect( + AbsoluteUnit.WithValue(OUTER_SPACING), + AbsoluteUnit.WithValue(OUTER_SPACING), + AbsoluteUnit.WithValue(256f), + AbsoluteUnit.WithValue(64f + (1 * INNER_SPACING)) + )); + + Container titleCard = new Container(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(0f), + new RelativeUnit(1f, Content.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(32f) + )); + titleCard.AddChild(Item = new ItemDisplay(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(32f), + AbsoluteUnit.WithValue(32f) + )) { + Text = new TextProperties().Standard().SetTextAlignment(TextAlignment.Bottom | TextAlignment.Right) + }); + titleCard.AddChild(Title = new Label(new Rect( + AbsoluteUnit.WithValue(72f), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(150f), + AbsoluteUnit.WithValue(32f) + )).SetTextProperties(new Properties.TextProperties().Standard().SetTextAlignment(TextAlignment.Left)).SetPivotPoint(new Vector2(0f, 0f))); + Content.AddChild(titleCard); + + Recipe = new Container(new Rect( + new RelativeUnit(.5f, Content.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(32f + INNER_SPACING), + new RelativeUnit(1f, Content.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(32f) + )); + Content.AddChild(Recipe); + + AddChild(Content); + + SetEnabled(false); + } + + public void SetRecipe(Recipe recipe) { + Item.Item = recipe.Result.GetItemType(); + Title.SetText(recipe.Result.GetItemType().Name); + + if (Recipe != null) Recipe.Dispose(); + Recipe = new Container(new Rect( + new RelativeUnit(0f, Content.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(32f + INNER_SPACING), + new RelativeUnit(1f, Content.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(32f) + )); + Recipe.SetPivot(new Vector2(0f, 0f)); + + for (int i = 0; i < recipe.Ingredients.Count; i++) + Recipe.AddChild(new ItemDisplay(new Rect( + AbsoluteUnit.WithValue((i * INNER_SPACING) + (i * 32)), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(32f), + AbsoluteUnit.WithValue(32f) + )) { + Item = recipe.Ingredients[i].GetItemType(), + Amount = recipe.Ingredients[i].Amount, + Text = new TextProperties().Standard() + .SetTextAlignment(TextAlignment.Bottom | TextAlignment.Right) + .SetFontSize(12f) + .SetText(recipe.Ingredients[i].Amount.ToString()) + }); + + Content.AddChild(Recipe); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/tooltips/ItemDisplay.cs b/source/ui/elements/game/tooltips/ItemDisplay.cs new file mode 100644 index 0000000..d3b1524 --- /dev/null +++ b/source/ui/elements/game/tooltips/ItemDisplay.cs @@ -0,0 +1,21 @@ +using Celesteia.Resources.Types; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.TextureAtlases; + +namespace Celesteia.UI.Elements.Game.Tooltips { + public class ItemDisplay : Element { + public ItemType Item; + public int Amount; + public TextProperties Text; + + public ItemDisplay(Rect rect) => SetRect(rect); + + public override void Draw(SpriteBatch spriteBatch) + { + spriteBatch.Draw(Item.Sprite, GetRectangle(), Color.White, null); + if (Amount > 1) TextUtilities.DrawAlignedText(spriteBatch, GetRectangle(), Text); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/tooltips/ItemTooltipDisplay.cs b/source/ui/elements/game/tooltips/ItemTooltipDisplay.cs new file mode 100644 index 0000000..9b62af4 --- /dev/null +++ b/source/ui/elements/game/tooltips/ItemTooltipDisplay.cs @@ -0,0 +1,62 @@ +using Celesteia.Resources.Types; +using Celesteia.UI.Properties; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Celesteia.UI.Elements.Game.Tooltips { + public class ItemTooltipDisplay : TooltipDisplay + { + private const float OUTER_SPACING = 16f; + private const float INNER_SPACING = 8f; + public readonly Container Content; + public readonly Label Title; + public readonly ItemDisplay Item; + + public ItemTooltipDisplay(Rect rect, Texture2D background) : base(rect) { + AddChild(new Image(Rect.RelativeFull(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(256f + (2 * OUTER_SPACING)), + AbsoluteUnit.WithValue(32f + (2 * OUTER_SPACING)) + ))).SetTexture(background).MakePatches(4).SetColor(Color.White)); + + Content = new Container(new Rect( + AbsoluteUnit.WithValue(OUTER_SPACING), + AbsoluteUnit.WithValue(OUTER_SPACING), + AbsoluteUnit.WithValue(256f), + AbsoluteUnit.WithValue(32f) + )); + + Container titleCard = new Container(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(0f), + new RelativeUnit(1f, Content.GetRect(), RelativeUnit.Orientation.Horizontal), + AbsoluteUnit.WithValue(32f) + )); + titleCard.AddChild(Item = new ItemDisplay(new Rect( + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(32f), + AbsoluteUnit.WithValue(32f) + )) { + Text = new TextProperties().Standard().SetTextAlignment(TextAlignment.Bottom | TextAlignment.Right) + }); + titleCard.AddChild(Title = new Label(new Rect( + AbsoluteUnit.WithValue(72f), + AbsoluteUnit.WithValue(0f), + AbsoluteUnit.WithValue(150f), + AbsoluteUnit.WithValue(32f) + )).SetTextProperties(new Properties.TextProperties().Standard().SetTextAlignment(TextAlignment.Left)).SetPivotPoint(new Vector2(0f, 0f))); + Content.AddChild(titleCard); + + AddChild(Content); + + SetEnabled(false); + } + + public void SetItem(ItemType type) { + Item.Item = type; + Title.SetText(type.Name); + } + } +}
\ No newline at end of file diff --git a/source/ui/elements/game/tooltips/TooltipDisplay.cs b/source/ui/elements/game/tooltips/TooltipDisplay.cs new file mode 100644 index 0000000..2ff051d --- /dev/null +++ b/source/ui/elements/game/tooltips/TooltipDisplay.cs @@ -0,0 +1,8 @@ +using Microsoft.Xna.Framework; + +namespace Celesteia.UI.Elements.Game.Tooltips { + public class TooltipDisplay : Container + { + public TooltipDisplay(Rect rect) : base(rect) {} + } +}
\ No newline at end of file |
