Replaced color usage with OpenTK's implementation.

This commit is contained in:
2019-12-28 15:41:06 -06:00
parent c9ad922341
commit 005d66840d
31 changed files with 155 additions and 139 deletions

View File

@@ -0,0 +1,85 @@
using RecrownedAthenaeum.Graphics.Render;
using RecrownedAthenaeum.Types;
using OpenTK;
using OpenTK.Graphics;
using System;
namespace RecrownedAthenaeum.Graphics.UI.Modular.Modules
{
/// <summary>
/// Represents a texture with more information.
/// </summary>
public class Image : UIModule, ISpecialDrawable
{
/// <summary>
/// The rotation of the image.
/// </summary>
public float rotation = 0f;
/// <summary>
/// The texture to be rendered.
/// </summary>
public Texture2D texture;
/// <summary>
/// Scale of of the X axis.
/// </summary>
public float ScaleX { get { return (float)Width / texture.Width; } set { Width = (int)(texture.Width * value); } }
/// <summary>
/// Scale of the Y axis.
/// </summary>
public float ScaleY { get { return (float)Height / texture.Height; } set { Height = (int)(texture.Height * value); } }
/// <summary>
/// Sets scale of X and Y.
/// </summary>
public float Scale { set { ScaleY = value; ScaleX = value; } }
/// <summary>
/// Constructs an image given a texture.
/// </summary>
/// <param name="texture">Texture to use.</param>
public Image(Texture2D texture)
{
this.texture = texture ?? throw new ArgumentException("Image requires a texture.");
SetPositionAndDimensions(texture.Bounds);
}
/// <summary>
/// Draws the image with default values.
/// </summary>
/// <param name="batch">The batch to use.</param>
public override void Draw(ConsistentSpriteBatch batch)
{
batch.Draw(texture, new Rectangle(X, Y, Width, Height), null, color, rotation, origin, SpriteEffects.None, 0f);
base.Draw(batch);
}
/// <summary>
/// Draws the image with more options.
/// </summary>
/// <param name="spriteBatch">Batch used.</param>
/// <param name="destination">Where to draw texture to.</param>
/// <param name="color">The color tint to use.</param>
/// <param name="rotation">Rotation of image.</param>
/// <param name="origin">Origin for the rotation.</param>
public void Draw(ConsistentSpriteBatch spriteBatch, Rectangle destination, Color4 color, float rotation = 0, Vector2 origin = default(Vector2))
{
this.color = color;
this.rotation = rotation;
this.origin = origin;
SetPositionAndDimensions(destination);
Draw(spriteBatch);
}
/// <summary>
/// Center's the origin to the middle of the dimensions of the texture.
/// </summary>
public override void CenterOrigin()
{
origin.X = texture.Bounds.Width / 2f;
origin.Y = texture.Bounds.Height / 2f;
}
}
}

View File

@@ -0,0 +1,155 @@
using RecrownedAthenaeum.Types;
using RecrownedAthenaeum.Input;
using RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions;
using RecrownedAthenaeum.Graphics.UI.SkinSystem;
using RecrownedAthenaeum.Graphics.Render;
namespace RecrownedAthenaeum.Graphics.UI.Modular.Modules.Interactive
{
/// <summary>
/// Function to be called when button is clicked.
/// </summary>
/// <param name="button">The button that was clicked.</param>
public delegate void Clicked(Button button);
/// <summary>
/// A very primitive button containing all the basic functions.
/// </summary>
public class Button : UIModule
{
private ButtonSkinDefinition skinDefinition;
private ISpecialDrawable downTexture, upTexture, highlightedTexture, disabledTexture;
/// <summary>
/// Click event listeners.
/// </summary>
public event Clicked Listeners;
private bool pressed;
/// <summary>
/// Whether or not this button should be currently disabled.
/// </summary>
public bool disabled = false;
/// <summary>
/// Whether or not this button is currently being hovered on.
/// </summary>
public bool Highlighted { get; private set; }
/// <summary>
/// Constructs this button using <see cref="ISpecialDrawable"/>s for the different states it could be in.
/// </summary>
/// <param name="down">Button being pressed.</param>
/// <param name="up">Button not being pressed.</param>
/// <param name="disabled">Disabled button.</param>
/// <param name="selected">Button being highlighted.</param>
public Button(ISpecialDrawable down, ISpecialDrawable up, ISpecialDrawable disabled = null, ISpecialDrawable selected = null)
{
this.downTexture = down;
this.upTexture = up;
this.disabledTexture = disabled;
this.highlightedTexture = selected;
}
/// <summary>
/// Constructs this button using the skin system.
/// </summary>
/// <param name="skin">The skin containing the information of the textures and design to follow.</param>
/// <param name="definitionName">The name of the definition in the skin. Can be null to select the default.</param>
public Button(ISkin skin, string definitionName = null)
{
skinDefinition = skin.ObtainDefinition<ButtonSkinDefinition>(definitionName);
downTexture = skin.GetTextureAtlasRegion(skinDefinition.downRegion, true);
upTexture = skin.GetTextureAtlasRegion(skinDefinition.upRegion, true);
disabledTexture = skin.GetTextureAtlasRegion(skinDefinition.disabledRegion);
highlightedTexture = skin.GetTextureAtlasRegion(skinDefinition.selectedRegion);
}
/// <summary>
/// Instantiates a button using a definition.
/// </summary>
/// <param name="skin">The skin the definition is defined in.</param>
/// <param name="skinDefinition">The definition itself.</param>
public Button(ISkin skin, ButtonSkinDefinition skinDefinition) :
this(skin.GetTextureAtlasRegion(skinDefinition.downRegion, true),
skin.GetTextureAtlasRegion(skinDefinition.upRegion, true),
skin.GetTextureAtlasRegion(skinDefinition.disabledRegion),
skin.GetTextureAtlasRegion(skinDefinition.selectedRegion))
{ }
/// <summary>
/// Draws the button.
/// </summary>
/// <param name="batch">Batch used to draw the button.</param>
public override void Draw(ConsistentSpriteBatch batch)
{
if (disabled)
{
disabledTexture?.Draw(batch, Boundaries, color);
}
else
{
if (pressed)
{
downTexture.Draw(batch, Boundaries, color);
}
else if (Highlighted)
{
highlightedTexture?.Draw(batch, Boundaries, color);
}
else
{
upTexture.Draw(batch, Boundaries, color);
}
}
base.Draw(batch);
}
/// <summary>
/// Called when the mouse changes state.
/// </summary>
/// <param name="state">The new state.</param>
/// <returns>Whether or not to continue calling the next mouse change listener.</returns>
public sealed override bool MouseStateChanged(MouseState state)
{
if (InputUtilities.MouseWithinBoundries(Boundaries))
{
if (state.LeftButton == ButtonState.Pressed)
{
pressed = true;
}
else
{
pressed = false;
}
if (InputUtilities.MouseClicked())
{
OnClick();
}
Highlighted = true;
}
else
{
Highlighted = false;
pressed = false;
}
return base.MouseStateChanged(state);
}
/// <summary>
/// Called when the state of the keyboard changes.
/// </summary>
/// <param name="state">The new state.</param>
/// <returns>Whether or not the next keyboard change listener should be called.</returns>
public sealed override bool KeyboardStateChanged(KeyboardState state)
{
return base.KeyboardStateChanged(state);
}
internal void OnClick()
{
Listeners?.Invoke(this);
}
}
}

View File

@@ -0,0 +1,92 @@
using RecrownedAthenaeum.Graphics.Render;
using OpenTK.Graphics;
using RecrownedAthenaeum.Graphics.UI.SkinSystem;
using RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions;
namespace RecrownedAthenaeum.Graphics.UI.Modular.Modules.Interactive
{
/// <summary>
/// Button that holds a string.
/// </summary>
public class TextButton : Button
{
/// <summary>
/// The text that is used to display the string.
/// </summary>
public readonly Text text;
/// <summary>
/// The color the font should be rendered in.
/// </summary>
public Color4 FontColor { get { return text.color; } set { text.color = value; } }
/// <summary>
/// Constructs text button with the positions represented by <see cref="ISpecialDrawable"/>
/// </summary>
/// <param name="text">The string representing the text to be displayed.</param>
/// <param name="font">The font to be used to display the text.</param>
/// <param name="down">What to draw as button is pushed down.</param>
/// <param name="up">What to draw as button is not pushed.</param>
/// <param name="disabled">What to draw as button is disabled.</param>
/// <param name="selected">What to draw as button is selected.</param>
public TextButton(string text, SpriteFont font, ISpecialDrawable down, ISpecialDrawable up, ISpecialDrawable disabled = null, ISpecialDrawable selected = null) : base(down, up, disabled, selected)
{
this.text = new Text(font, text);
this.text.autoScale = true;
this.text.centered = true;
}
/// <summary>
/// Constructs a text button using a skin and definition.
/// </summary>
/// <param name="text">The text to display.</param>
/// <param name="font">The font to be used.</param>
/// <param name="skin">The skin to use.</param>
/// <param name="definitionName">Name of the definition for this type in the skin given.</param>
public TextButton(string text, SpriteFont font, ISkin skin, string definitionName = null) : base(skin, skin.ObtainDefinition<TextButtonSkinDefinition>(definitionName))
{
TextButtonSkinDefinition skinDefinition = skin.ObtainDefinition<TextButtonSkinDefinition>(definitionName);
this.text = new Text(font, text);
this.text.autoScale = true;
this.text.centered = true;
FontColor = skin.GetColor(skinDefinition.fontColor);
}
/// <summary>
/// Creates a text button with a given definition.
/// </summary>
/// <param name="text">The text to be displayed on this button.</param>
/// <param name="font">The font to use for this button.</param>
/// <param name="skin">The skin the definition is from.</param>
/// <param name="skinDefinition">The definition to use.</param>
public TextButton(string text, SpriteFont font, ISkin skin, TextButtonSkinDefinition skinDefinition) :
this(text,
font,
skin.GetTextureAtlasRegion(skinDefinition.downRegion, true),
skin.GetTextureAtlasRegion(skinDefinition.upRegion, true),
skin.GetTextureAtlasRegion(skinDefinition.disabledRegion),
skin.GetTextureAtlasRegion(skinDefinition.selectedRegion))
{ }
/// <summary>
/// Updates the text button.
/// </summary>
/// <param name="gameTime">Snapshot of information about time for game.</param>
public override void Update(GameTime gameTime)
{
text.SetPositionAndDimensions(Boundaries);
text.Update(gameTime);
base.Update(gameTime);
}
/// <summary>
/// Called whenever game wants to render this button.
/// </summary>
/// <param name="batch">Batch to use. Batch should already be started.</param>
public override void Draw(ConsistentSpriteBatch batch)
{
base.Draw(batch);
text.Draw(batch);
}
}
}

View File

@@ -0,0 +1,206 @@
using OpenTK;
using RecrownedAthenaeum.Graphics.Render;
using System;
using System.Text;
namespace RecrownedAthenaeum.Graphics.UI.Modular.Modules
{
/// <summary>
/// Represents text for the UI.
/// </summary>
public class Text : UIModule
{
private SpriteFont font;
private float scale = 1f;
private Vector2 position;
private string originalText;
private string displayedText;
private Vector2 modifiedTextSize;
/// <summary>
/// Centers the text int bounds.
/// </summary>
public bool centered;
/// <summary>
/// Whether or not to try and wrap text automatically. Meaning will check and attempt to wrap every update.
/// </summary>
public bool autoWrap;
/// <summary>
/// Whether or not to automatically scale the text every update. Happens after auto wrap if enabled.
/// </summary>
public bool autoScale;
/// <summary>
/// Should this use ellipses? Will perform this operation before auto wrapping or auto scalling.
/// </summary>
public bool useEllipses;
/// <summary>
/// The text to use for the ellipsis.
/// </summary>
public string ellipsis = "...";
private string ModifiedText { get { return displayedText; } set { displayedText = value; modifiedTextSize = font.MeasureString(value); } }
/// <summary>
/// The string to be displayed.
/// </summary>
public string Content { get { return originalText; } set { originalText = value; if (value == null) value = ellipsis; modifiedTextSize = font.MeasureString(value); displayedText = value; } }
/// <summary>
/// Creates a UI text object.
/// </summary>
/// <param name="font">The font to use.</param>
/// <param name="content">The string for the text.</param>
public Text(SpriteFont font, string content = null)
{
this.font = font ?? throw new ArgumentNullException("Font cannot be null.");
Content = content;
}
/// <summary>
/// Updates the positioning and attempts to perform any operations that were marked automatic.
/// </summary>
/// <param name="gameTime">The game time.</param>
public override void Update(GameTime gameTime)
{
position.X = X;
position.Y = Y;
if (useEllipses) AttemptToApplyEllipsis();
if (autoWrap) AttemptToWrapText();
if (autoScale) AttemptToScaleFont();
if (centered) Center();
base.Update(gameTime);
}
/// <summary>
/// Draws the text.
/// </summary>
/// <param name="batch">Batch to use.</param>
public override void Draw(ConsistentSpriteBatch batch)
{
batch.DrawString(font, Content, position, color, 0f, default(Vector2), scale, SpriteEffects.None, 0f);
base.Draw(batch);
}
/// <summary>
/// Attempts to apply ellipsis. Checks of nessecary.
/// </summary>
public void AttemptToApplyEllipsis()
{
if (modifiedTextSize.X * scale > Width && ModifiedText.Length > ellipsis.Length + 1)
{
RemoveLineBreaks();
StringBuilder stringBuilder = new StringBuilder(ModifiedText);
do
{
stringBuilder.Remove(stringBuilder.Length, ellipsis.Length - 1);
stringBuilder.Insert(stringBuilder.Length, ellipsis);
}
while (font.MeasureString(stringBuilder).X * scale > Width);
ModifiedText = stringBuilder.ToString();
}
}
/// <summary>
/// Attempts to scale the font. Checks if nessecary.
/// </summary>
public void AttemptToScaleFont()
{
if (Width < Height)
{
if (Math.Round(modifiedTextSize.X * scale ) != Width)
{
scale = Width / modifiedTextSize.X;
}
}
else
{
if (Math.Round(modifiedTextSize.Y * scale ) != Height)
{
scale = Height / (modifiedTextSize.Y);
}
}
}
/// <summary>
/// Removes line breaks.
/// </summary>
public void RemoveLineBreaks()
{
ModifiedText = ModifiedText.Replace("\n", " ");
}
/// <summary>
/// Resets to original text.
/// </summary>
public void ResetToOriginalText()
{
Content = originalText;
}
/// <summary>
/// Attempts to wrap text. Checks if nessecary.
/// </summary>
/// <param name="unwrap">If true, will first unwrap text, and the wrap again. This occurs before nessecity check.</param>
public void AttemptToWrapText(bool unwrap = false)
{
if (unwrap) RemoveLineBreaks();
if (modifiedTextSize.X * scale > Width)
{
ModifiedText = ModifiedText.Replace("\n", " ");
string[] words = ModifiedText.Split(' ');
StringBuilder stringBuilder = new StringBuilder();
float currentScaledLineWidth = 0f;
for (int w = 0; w < words.Length; w++)
{
string word = words[w];
float scaledWidth = font.MeasureString(word).X * scale;
if (currentScaledLineWidth + scaledWidth <= Width)
{
stringBuilder.Append(word);
currentScaledLineWidth += scaledWidth;
}
else
{
stringBuilder.AppendLine();
currentScaledLineWidth = 0;
}
}
ModifiedText = stringBuilder.ToString();
}
}
private bool Center()
{
Vector2 textSize = new Vector2(modifiedTextSize.X * scale, modifiedTextSize.Y * scale);
if (textSize.X <= Width)
{
position.X = X + (Width - textSize.X) / 2f;
}
else
{
return false;
}
if (textSize.Y <= Height)
{
position.Y = Y + (Height - textSize.Y) / 2f;
}
else
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,451 @@
using RecrownedAthenaeum.Input;
using RecrownedAthenaeum.Graphics.Render;
using RecrownedAthenaeum.Types;
using RecrownedAthenaeum.Graphics.UI.SkinSystem;
using RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions;
using OpenTK;
using OpenTK.Graphics;
using System;
namespace RecrownedAthenaeum.Graphics.UI.Modular.Modules
{
public class UIScrollable : UIModule
{
BasicScissor basicScissor;
Rectangle viewport;
UIModuleGroup group;
Color4 scrollBarColor = Color4.White;
float opacityOfBar = 1f;
bool showingBars;
private bool mouseWasPressed;
private bool horizontalSelected;
private Vector2 mouseRelativePosition;
float shiftX, shiftY;
public float XScrollPosition
{
get
{
return (Width - horizontalScrollBarBounds.Width) * (-shiftX / (group.Width - viewport.Width));
}
set
{
if (value > Width - verticalScrollBarBounds.Height)
{
value = Width - verticalScrollBarBounds.Height;
}
if (value < 0) value = 0;
shiftX = -(group.Width - viewport.Width) * (value / (Width - horizontalScrollBarBounds.Width));
WidthOrXChange(true);
}
}
public float YScrollPosition
{
get
{
return (Height - verticalScrollBarBounds.Height) * (-shiftY / (group.Height - viewport.Height));
}
set
{
if (value > Height - verticalScrollBarBounds.Height)
{
value = Height - verticalScrollBarBounds.Height;
}
if (value < 0) value = 0;
shiftY = -(group.Height - viewport.Height) * (value / (Height - verticalScrollBarBounds.Height));
HeightOrYChange(true);
}
}
UIModule furthestXModule, furthestYMod;
bool horScrollAvailable, vertScrollAvailable;
Rectangle horizontalScrollBarBounds, verticalScrollBarBounds;
/// <summary>
/// How fast the bars fade away in opacity (0 to 254) per second.
/// </summary>
public float barFadeSpeed = 250;
ISpecialDrawable horizontalScrollBar, verticalScrollBar;
ISpecialDrawable background, horizontalBarTrack, verticalBarTrack;
private int topPadding, bottomPadding, leftPadding, rightPadding;
/// <summary>
///
/// </summary>
public int PadTop { get { return topPadding; } set { topPadding = value; HeightOrYChange(); } }
public int PadBottom { get { return bottomPadding; } set { bottomPadding = value; HeightOrYChange(); } }
public int PadLeft { get { return leftPadding; } set { leftPadding = value; WidthOrXChange(); } }
public int PadRight { get { return rightPadding; } set { rightPadding = value; WidthOrXChange(); } }
/// <summary>
/// The minimum bar length for the scroll bar.
/// </summary>
public int minimumBarLength = 16;
public int HorizontalBarThickness { get { return horizontalScrollBarBounds.Height; } set { horizontalScrollBarBounds.Height = value; HeightOrYChange(); } }
public int VerticalBarThickness { get { return verticalScrollBarBounds.Width; } set { verticalScrollBarBounds.Width = value; WidthOrXChange(); } }
bool hideScrollBars;
/// <summary>
/// Whether or not to hide scroll bars.
/// </summary>
public bool HideScrollBars
{
get { return hideScrollBars; }
set
{
hideScrollBars = value;
WidthOrXChange(true);
HeightOrYChange(true);
if (!value)
{
opacityOfBar = 1f;
scrollBarColor = color.ReturnMultipliedByFloat(opacityOfBar);
}
}
}
/// <summary>
/// Set to true to change from the normal position to the new position.
/// </summary>
public bool verticalBarLeftPosition, horizontalBarTopPosition;
public override int Width
{
get
{
return base.Width;
}
set
{
base.Width = value;
WidthOrXChange();
}
}
public override int Height
{
get
{
return base.Height;
}
set
{
base.Height = value;
HeightOrYChange();
}
}
public override int X
{
get
{
return base.X;
}
set
{
WidthOrXChange(true);
base.X = value;
}
}
public override int Y
{
get
{
return base.Y;
}
set
{
HeightOrYChange(true);
base.Y = value;
}
}
public UIScrollable(ISpecialDrawable horizontalScrollBar, ISpecialDrawable verticalScrollBar, ISpecialDrawable horizontalBarTrack = null, ISpecialDrawable verticalBarTrack = null, ISpecialDrawable background = null)
{
this.horizontalScrollBar = horizontalScrollBar;
this.verticalScrollBar = verticalScrollBar;
this.horizontalBarTrack = horizontalBarTrack;
this.verticalBarTrack = verticalBarTrack;
this.background = background;
basicScissor = new BasicScissor();
group = new UIModuleGroup();
HorizontalBarThickness = 12;
VerticalBarThickness = 12;
}
public UIScrollable(ISkin skin, string definition = null) :
this(skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().horizontalBar, true),
skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().verticalBar, true),
skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().horizontalBarTrack),
skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().verticalBarTrack),
skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().background))
{
}
public override void Update(GameTime gameTime)
{
if (hideScrollBars)
{
if (!showingBars && !mouseWasPressed)
{
if (opacityOfBar > 0f)
{
opacityOfBar -= (barFadeSpeed / 255f) * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
}
else
{
opacityOfBar = 1f;
}
scrollBarColor = color.ReturnMultipliedByFloat(opacityOfBar);
}
if (horScrollAvailable)
{
horizontalScrollBarBounds.X = (int)XScrollPosition;
}
if (vertScrollAvailable)
{
verticalScrollBarBounds.Y = (int)YScrollPosition;
}
base.Update(gameTime);
}
public override void Draw(ConsistentSpriteBatch spriteBatch)
{
background?.Draw(spriteBatch, Boundaries, color, origin: origin);
spriteBatch.End();
basicScissor.Begin(viewport, spriteBatch);
group.Draw(spriteBatch);
basicScissor.End();
spriteBatch.Begin();
if (horScrollAvailable)
{
horizontalScrollBar.Draw(spriteBatch, horizontalScrollBarBounds, scrollBarColor);
}
if (vertScrollAvailable)
{
horizontalScrollBar.Draw(spriteBatch, verticalScrollBarBounds, scrollBarColor);
}
base.Draw(spriteBatch);
}
public void AddModules(params UIModule[] addModules)
{
group.AddModules(addModules);
for (int i = 0; i < addModules.Length; i++)
{
UIModule m = addModules[i];
int mFurthestX = m.Boundaries.X + m.Boundaries.Width;
int mFurthestY = m.Boundaries.Y + m.Boundaries.Height;
if (mFurthestX > group.Width)
{
furthestXModule = m;
group.Width = mFurthestX;
}
if (mFurthestY > group.Height)
{
furthestYMod = m;
group.Height = mFurthestY;
}
}
WidthOrXChange();
HeightOrYChange();
}
public void RemoveModule(UIModule module)
{
group.RemoveModule(module);
if (module == furthestXModule)
{
group.Width = 0;
UIModule[] modules = group.GetCopyOfModules();
for (int i = 0; i < modules.Length; i++)
{
UIModule m = modules[i];
int mFurthestX = m.Boundaries.X + m.Boundaries.Width;
if (mFurthestX > group.Width)
{
furthestXModule = m;
group.Width = mFurthestX;
}
}
}
if (module == furthestYMod)
{
group.Height = 0;
UIModule[] modules = group.GetCopyOfModules();
for (int i = 0; i < modules.Length; i++)
{
UIModule m = modules[i];
int mFurthestY = m.Boundaries.Y + m.Boundaries.Height;
if (mFurthestY > group.Height)
{
furthestYMod = m;
group.Height = mFurthestY;
}
}
}
}
private void WidthOrXChange(bool onlyXChange = false)
{
group.X = X + leftPadding + (int)shiftX;
if (!onlyXChange)
{
viewport.X = X + leftPadding;
viewport.Width = Width - rightPadding - leftPadding;
if (Width < group.Width)
{
horScrollAvailable = true;
horizontalScrollBarBounds.Width = (int)(Width * ((float)Width / group.Width));
horizontalScrollBarBounds.Width = Math.Max(horizontalScrollBarBounds.Width, minimumBarLength);
}
else { horScrollAvailable = false; }
verticalScrollBarBounds.X = X;
if (!hideScrollBars)
{
viewport.Width -= VerticalBarThickness;
if (!verticalBarLeftPosition)
{
verticalScrollBarBounds.X += viewport.Width;
}
}
else
{
if (!verticalBarLeftPosition)
{
verticalScrollBarBounds.X += viewport.Width - verticalScrollBarBounds.Width;
}
}
}
}
private void HeightOrYChange(bool onlyYChange = false)
{
group.Y = Y + bottomPadding + (int)shiftY;
if (!onlyYChange)
{
viewport.Y = Y + bottomPadding;
viewport.Height = Height - bottomPadding - topPadding;
if (Height < group.Height)
{
vertScrollAvailable = true;
verticalScrollBarBounds.Height = (int)(Height * ((float)Height / group.Height));
verticalScrollBarBounds.Height = Math.Max(verticalScrollBarBounds.Height, minimumBarLength);
}
else { vertScrollAvailable = false; }
horizontalScrollBarBounds.Y = Y;
if (!hideScrollBars)
{
viewport.Height -= HorizontalBarThickness;
if (!horizontalBarTopPosition)
{
horizontalScrollBarBounds.Y += viewport.Height;
}
else
{
viewport.Y += horizontalScrollBarBounds.Height;
}
}
else
{
if (!horizontalBarTopPosition)
{
horizontalScrollBarBounds.Y += viewport.Height - horizontalScrollBarBounds.Height;
}
}
}
}
public override bool KeyboardStateChanged(KeyboardState state)
{
if (state.IsKeyDown(Keys.Right))
{
XScrollPosition += 1;
}
return base.KeyboardStateChanged(state);
}
public override bool MouseStateChanged(MouseState state)
{
if (InputUtilities.MouseWithinBoundries(Boundaries))
{
showingBars = true;
}
else
{
showingBars = false;
}
if (InputUtilities.MouseWithinBoundries(horizontalScrollBarBounds))
{
if (state.LeftButton == ButtonState.Pressed)
{
mouseWasPressed = true;
horizontalSelected = true;
}
if (!mouseWasPressed)
{
mouseRelativePosition.X = state.X - horizontalScrollBarBounds.X;
}
}
if (InputUtilities.MouseWithinBoundries(verticalScrollBarBounds))
{
if (state.LeftButton == ButtonState.Pressed)
{
mouseWasPressed = true;
horizontalSelected = false;
}
if (!mouseWasPressed)
{
mouseRelativePosition.Y = state.Y - verticalScrollBarBounds.Y;
}
}
if (mouseWasPressed)
{
if (horizontalSelected)
{
float latestPosition = state.X - mouseRelativePosition.X - X;
XScrollPosition = latestPosition;
}
else
{
float latestPosition = state.Y - mouseRelativePosition.Y - Y;
YScrollPosition = latestPosition;
}
if (state.LeftButton == ButtonState.Released)
{
mouseWasPressed = false;
}
}
return base.MouseStateChanged(state);
}
}
}

View File

@@ -0,0 +1,190 @@
using RecrownedAthenaeum.Types;
using OpenTK;
using OpenTK.Graphics;
using RecrownedAthenaeum.Input;
using RecrownedAthenaeum.Graphics.Render;
using System;
namespace RecrownedAthenaeum.Graphics.UI.Modular
{
/// <summary>
/// Module for UI layout.
/// </summary>
public abstract class UIModule : IInputListener
{
/// <summary>
/// The width of the module.
/// </summary>
public virtual int Width { get; set; }
/// <summary>
/// The height of the module.
/// </summary>
public virtual int Height { get; set; }
/// <summary>
/// The X position of the module.
/// </summary>
public virtual int X { get; set; }
/// <summary>
/// The Y position of the module.
/// </summary>
public virtual int Y { get; set; }
/// <summary>
/// Bounds of this module (after factoring in the origin).
/// </summary>
public Rectangle Boundaries
{
get
{
return new Rectangle((int)(X - origin.X), (int)(Y - origin.Y), Width, Height);
}
}
/// <summary>
/// Origin of this module.
/// </summary>
public Vector2 origin;
/// <summary>
/// The parent of this module. May be null.
/// </summary>
public UIModuleGroup parent;
/// <summary>
/// Name of this module. For organizational/referencial purposes mostly.
/// </summary>
public string name;
/// <summary>
/// The color tint of this module.
/// </summary>
public Color4 color = Color4.White;
/// <summary>
/// Called every frame to update this module. Calculations and movement should go here.
/// </summary>
/// <param name="gameTime">Game time information.</param>
public virtual void Update(GameTime gameTime)
{
}
/// <summary>
/// Called every frame to draw this module. Anything that needs to be drawn should go here.
/// </summary>
/// <param name="batch">Batch used to draw.</param>
public virtual void Draw(ConsistentSpriteBatch batch)
{
}
/// <summary>
/// Converts the given rectangle to the coordinates of the parent.
/// </summary>
/// <param name="rectangle">Rectangle to convert.</param>
/// <returns></returns>
public Rectangle ConvertToParentCoordinates(Rectangle rectangle)
{
if (parent != null)
{
Rectangle parentHitbox = parent.ConvertToParentCoordinates(rectangle);
int tX = rectangle.X + parentHitbox.X;
int tY = rectangle.Y + parentHitbox.Y;
return new Rectangle(tX, tY, rectangle.Width, rectangle.Height);
}
else
{
return rectangle;
}
}
/// <summary>
/// Removes this module from the parent.
/// </summary>
public void RemoveFromParent()
{
if (parent == null) throw new InvalidOperationException("Parent is null.");
parent.RemoveModule(this);
}
/// <summary>
/// Called whenever the keyboard state is changed.
/// </summary>
/// <param name="state">The current keyboard state.</param>
/// <returns>Returning whether or not to continue to call the next listener.</returns>
public virtual bool KeyboardStateChanged(KeyboardState state)
{
return true;
}
/// <summary>
/// Called whenever the state of the mouse changes. This includes movement.
/// </summary>
/// <param name="state">The current state of the mouse.</param>
/// <returns>Returning whether or not to continue to call the next listener.</returns>
public virtual bool MouseStateChanged(MouseState state)
{
return true;
}
/// <summary>
/// Sets the origin to be the center using the <see cref="Width"/> and <see cref="Height"/>.
/// </summary>
public virtual void CenterOrigin()
{
origin.X = Width / 2f;
origin.Y = Height / 2f;
}
/// <summary>
/// Centers this module's on the horizontal axis relative to the parent <see cref="UIModuleGroup"/>.
/// </summary>
/// <returns>True if possible and false if not.</returns>
public bool CenterHorizontally()
{
if (parent != null)
{
Rectangle rectangle = parent.Boundaries;
if (parent != null && rectangle.Width >= Boundaries.Width)
{
X = rectangle.Width / 2 + X;
return true;
}
}
return false;
}
/// <summary>
/// Centers this module's origin on the vertical axis relative to the parent <see cref="UIModuleGroup"/>.
/// </summary>
/// <returns>True if possible.</returns>
public virtual bool CenterVertically()
{
if (parent != null)
{
Rectangle rectangle = parent.Boundaries;
if (rectangle.Height >= Boundaries.Height)
{
Y = rectangle.Height / 2 + Y;
return true;
}
}
return false;
}
/// <summary>
/// Sets the position and dimension of this module.
/// </summary>
/// <param name="rectangle">The rectangle that represents the position and dimensions of the module.</param>
public virtual void SetPositionAndDimensions(Rectangle rectangle)
{
X = rectangle.X;
Y = rectangle.Y;
Width = rectangle.Width;
Height = rectangle.Height;
}
}
}

View File

@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using RecrownedAthenaeum.Graphics.Render;
namespace RecrownedAthenaeum.Graphics.UI.Modular
{
/// <summary>
/// Contains a group of modules and has its own relative coordinate system.
/// </summary>
public class UIModuleGroup : UIModule
{
List<UIModule> modules = new List<UIModule>();
/// <summary>
/// Set this to crop anything that flows out of the boundaries of this group. Will not crop if this is null.
/// </summary>
public BasicScissor basicScissor;
/// <summary>
/// Instantiates the UI module group.
/// </summary>
/// <param name="basicScissor">Sets the <see cref="basicScissor"/> field.</param>
public UIModuleGroup(BasicScissor basicScissor = null)
{
this.basicScissor = basicScissor;
}
/// <summary>
/// Draws this group of modules. If scissoring, will use the matrix and effect designated in the <see cref="BasicScissor"/> to begin the batch normally again.
/// </summary>
/// <param name="spriteBatch">Batch used to draw the group.</param>
public override void Draw(ConsistentSpriteBatch spriteBatch)
{
if (basicScissor != null)
{
spriteBatch.End();
basicScissor.Begin(Boundaries, spriteBatch);
}
foreach (UIModule module in modules)
{
int offsetX = module.X;
int offsetY = module.Y;
module.X = X + offsetX;
module.Y = Y + offsetY;
module.Draw(spriteBatch);
module.X = offsetX;
module.Y = offsetY;
}
if (basicScissor != null)
{
basicScissor.End();
spriteBatch.Begin();
}
}
/// <summary>
/// Updates the group of modules.
/// </summary>
/// <param name="gameTime">Game time used.</param>
public override void Update(GameTime gameTime)
{
foreach (UIModule module in modules)
{
module.Update(gameTime);
}
}
/// <summary>
/// Adds module(s) to this group.
/// </summary>
/// <param name="addModules">The module(s) to add.</param>
public virtual void AddModules(params UIModule[] addModules)
{
foreach (UIModule module in addModules)
{
if (modules.Contains(module))
{
throw new InvalidOperationException(module.ToString() + " already exists in " + this.ToString());
}
module.parent = this;
modules.Add(module);
}
}
/// <summary>
/// Removes given module from group.
/// </summary>
/// <param name="module">module to remove.</param>
public virtual void RemoveModule(UIModule module)
{
module.parent = null;
modules.Remove(module);
}
/// <summary>
/// Obtains an array snapshot of all the modules.
/// </summary>
public UIModule[] GetCopyOfModules()
{
return modules.ToArray();
}
/// <summary>
/// Updates the keyboard state of the modules in this group.
/// </summary>
/// <param name="state">The new state.</param>
/// <returns>Whether or not to continue updating the other listeners.</returns>
public override bool KeyboardStateChanged(KeyboardState state)
{
foreach (UIModule module in modules)
{
module.KeyboardStateChanged(state);
}
return base.KeyboardStateChanged(state);
}
/// <summary>
/// Updates the moues state of the modules in this group.
/// </summary>
/// <param name="state">The new state.</param>
/// <returns>Whether or not to continue updating other listeners.</returns>
public override bool MouseStateChanged(MouseState state)
{
foreach (UIModule module in modules)
{
module.MouseStateChanged(state);
}
return base.MouseStateChanged(state);
}
}
}