1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-05-01 16:07:50 +02:00

Compare commits

...

5 commits

22 changed files with 279 additions and 133 deletions

View file

@ -2,7 +2,7 @@
MLEM tries to adhere to [semantic versioning](https://semver.org/). Potentially breaking changes are written in **bold**.
Jump to version:
- [6.3.2](#632-in-development)
- [7.0.0](#700-in-development)
- [6.3.1](#631)
- [6.3.0](#630)
- [6.2.0](#620)
@ -13,7 +13,16 @@ Jump to version:
- [5.1.0](#510)
- [5.0.0](#500)
## 6.3.2 (In Development)
## 7.0.0 (In Development)
### MLEM
Additions
- **Added the ability for formatted (tokenized) strings to be drawn with custom rotation, origin and flipping**
### MLEM.Ui
Additions
- Added the ability to set the anchor that should be used when a tooltip attaches to an element or the mouse
- Added the ability to display tooltips using the auto-nav style even when using the mouse
## 6.3.1

View file

@ -77,7 +77,8 @@ namespace Demos {
this.activeDemo = demo.Value.Item2.Invoke(this);
this.activeDemo.LoadContent();
},
PositionOffset = new Vector2(0, 1)
PositionOffset = new Vector2(0, 1),
Tooltip = {DisplayInAutoNavMode = true}
});
}

View file

@ -27,6 +27,7 @@ namespace Demos {
private TokenizedString tokenizedText;
private GenericFont font;
private bool drawBounds;
private bool transform;
private float Scale {
get {
// calculate our scale based on how much larger the window is, so that the text scales with the window
@ -85,7 +86,11 @@ namespace Demos {
}
// draw the text itself (start and end indices are optional)
this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, this.Scale, 0, this.startIndex, this.endIndex);
this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, this.Scale, 0,
this.transform ? 0.25F : 0,
this.transform ? new Vector2(size.X / this.Scale, 128) : Vector2.Zero,
this.transform ? SpriteEffects.FlipHorizontally : SpriteEffects.None,
this.startIndex, this.endIndex);
this.SpriteBatch.End();
}
@ -97,6 +102,8 @@ namespace Demos {
// change some demo showcase info based on keybinds
if (this.InputHandler.IsPressed(Keys.B))
this.drawBounds = !this.drawBounds;
if (this.InputHandler.IsPressed(Keys.T))
this.transform = !this.transform;
if (this.startIndex > 0 && this.InputHandler.IsDown(Keys.Left))
this.startIndex--;
if (this.startIndex < this.tokenizedText.String.Length && this.InputHandler.IsDown(Keys.Right))

View file

@ -38,7 +38,7 @@ namespace MLEM.Extended.Font {
}
/// <inheritdoc />
protected override void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
public override void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
batch.DrawString(this.Font, character, position, color, rotation, Vector2.Zero, scale, effects, layerDepth);
}

View file

@ -47,7 +47,7 @@ namespace MLEM.Extended.Font {
}
/// <inheritdoc />
protected override void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
public override void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
this.Font.DrawText(batch, character, position, color, rotation, Vector2.Zero, scale, layerDepth, this.CharacterSpacing, this.LineSpacing);
}

View file

@ -141,12 +141,12 @@ namespace MLEM.Ui.Elements {
}
/// <summary>
/// The inclusive index in this paragraph's <see cref="Text"/> to start drawing at.
/// This value is passed to <see cref="TokenizedString.Draw"/>.
/// This value is passed to <see cref="TokenizedString.Draw(Microsoft.Xna.Framework.GameTime,Microsoft.Xna.Framework.Graphics.SpriteBatch,Microsoft.Xna.Framework.Vector2,MLEM.Font.GenericFont,Microsoft.Xna.Framework.Color,Vector2,float,float,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Graphics.SpriteEffects,System.Nullable{int},System.Nullable{int})"/>.
/// </summary>
public int? DrawStartIndex;
/// <summary>
/// The exclusive index in this paragraph's <see cref="Text"/> to stop drawing at.
/// This value is passed to <see cref="TokenizedString.Draw"/>.
/// This value is passed to <see cref="TokenizedString.Draw(Microsoft.Xna.Framework.GameTime,Microsoft.Xna.Framework.Graphics.SpriteBatch,Microsoft.Xna.Framework.Vector2,MLEM.Font.GenericFont,Microsoft.Xna.Framework.Color,Vector2,float,float,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Graphics.SpriteEffects,System.Nullable{int},System.Nullable{int})"/>.
/// </summary>
public int? DrawEndIndex;
@ -246,7 +246,7 @@ namespace MLEM.Ui.Elements {
var pos = this.DisplayArea.Location + new Vector2(this.GetAlignmentOffset(), 0);
var sc = this.TextScale * this.TextScaleMultiplier * this.Scale;
var color = this.TextColor.OrDefault(Color.White) * alpha;
this.TokenizedText.Draw(time, batch, pos, this.RegularFont, color, sc, 0, this.DrawStartIndex, this.DrawEndIndex);
this.TokenizedText.Draw(time, batch, pos, this.RegularFont, color, sc, 0, 0, Vector2.Zero, SpriteEffects.None, this.DrawStartIndex, this.DrawEndIndex);
base.Draw(time, batch, alpha, context);
}

View file

@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using Microsoft.Xna.Framework;
using MLEM.Input;
using MLEM.Ui.Style;
using Color = Microsoft.Xna.Framework.Color;
using RectangleF = MLEM.Misc.RectangleF;
#if FNA
using MLEM.Extensions;
#endif
@ -23,14 +26,26 @@ namespace MLEM.Ui.Elements {
public readonly List<Paragraph> Paragraphs = new List<Paragraph>();
/// <summary>
/// The offset that this tooltip's top left corner should have from the mouse position
/// The offset that this tooltip should have from the mouse position
/// </summary>
public StyleProp<Vector2> MouseOffset;
/// <summary>
/// The offset that this tooltip's top center coordinate should have from the bottom center of the element snapped to when <see cref="DisplayInAutoNavMode"/> is true.
/// The offset that this tooltip should have from the element snapped to when <see cref="DisplayInAutoNavMode"/> is true.
/// </summary>
public StyleProp<Vector2> AutoNavOffset;
/// <summary>
/// The anchor that should be used when this tooltip is displayed using the mouse. The <see cref="MouseOffset"/> will be applied.
/// </summary>
public StyleProp<Anchor> MouseAnchor;
/// <summary>
/// The anchor that should be used when this tooltip is displayed using auto-nav mode. The <see cref="AutoNavOffset"/> will be applied.
/// </summary>
public StyleProp<Anchor> AutoNavAnchor;
/// <summary>
/// If this is <see langword="true"/>, and the mouse is used, the tooltip will attach to the hovered element in a static position using the <see cref="AutoNavOffset"/> and <see cref="AutoNavAnchor"/> properties, rather than following the mouse cursor exactly.
/// </summary>
public StyleProp<bool> UseAutoNavBehaviorForMouse;
/// <summary>
/// The amount of time that the mouse has to be over an element before it appears
/// </summary>
public StyleProp<TimeSpan> Delay;
@ -79,6 +94,7 @@ namespace MLEM.Ui.Elements {
/// The position that this tooltip should be following (or snapped to) instead of the <see cref="InputHandler.ViewportMousePosition"/>.
/// If this value is unset, <see cref="InputHandler.ViewportMousePosition"/> will be used as the snap position.
/// Note that <see cref="MouseOffset"/> is still applied with this value set.
/// Note that, if <see cref="UseAutoNavBehaviorForMouse"/> is <see langword="true"/>, this value is ignored.
/// </summary>
public virtual Vector2? SnapPosition { get; set; }
@ -151,6 +167,9 @@ namespace MLEM.Ui.Elements {
this.Texture = this.Texture.OrStyle(style.TooltipBackground);
this.MouseOffset = this.MouseOffset.OrStyle(style.TooltipOffset);
this.AutoNavOffset = this.AutoNavOffset.OrStyle(style.TooltipAutoNavOffset);
this.MouseAnchor = this.MouseAnchor.OrStyle(style.TooltipMouseAnchor);
this.AutoNavAnchor = this.AutoNavAnchor.OrStyle(style.TooltipAutoNavAnchor);
this.UseAutoNavBehaviorForMouse = this.UseAutoNavBehaviorForMouse.OrStyle(style.TooltipUseAutoNavBehaviorForMouse);
this.Delay = this.Delay.OrStyle(style.TooltipDelay);
this.ParagraphTextColor = this.ParagraphTextColor.OrStyle(style.TooltipTextColor);
this.ParagraphTextScale = this.ParagraphTextScale.OrStyle(style.TextScale);
@ -201,11 +220,10 @@ namespace MLEM.Ui.Elements {
public void SnapPositionToMouse() {
Vector2 snapPosition;
if (this.snapElement != null) {
// center our snap position below the snap element
snapPosition = new Vector2(this.snapElement.DisplayArea.Center.X, this.snapElement.DisplayArea.Bottom) + this.AutoNavOffset;
snapPosition.X -= this.DisplayArea.Width / 2F;
snapPosition = this.GetSnapOffset(this.AutoNavAnchor, this.snapElement.DisplayArea, this.AutoNavOffset);
} else {
snapPosition = (this.SnapPosition ?? this.Input.ViewportMousePosition.ToVector2()) + this.MouseOffset.Value;
var mouseBounds = new RectangleF(this.SnapPosition ?? this.Input.ViewportMousePosition.ToVector2(), Vector2.Zero);
snapPosition = this.GetSnapOffset(this.MouseAnchor, mouseBounds, this.MouseOffset);
}
var viewport = this.System.Viewport;
@ -255,8 +273,19 @@ namespace MLEM.Ui.Elements {
/// </summary>
/// <param name="elementToHover">The element that should automatically cause the tooltip to appear and disappear when hovered and not hovered, respectively</param>
public void AddToElement(Element elementToHover) {
elementToHover.OnMouseEnter += e => this.Display(e.System, $"{e.GetType().Name}Tooltip");
elementToHover.OnMouseExit += e => this.Remove();
// mouse controls
elementToHover.OnMouseEnter += e => {
if (this.UseAutoNavBehaviorForMouse)
this.snapElement = e;
this.Display(e.System, $"{e.GetType().Name}Tooltip");
};
elementToHover.OnMouseExit += e => {
this.Remove();
if (this.UseAutoNavBehaviorForMouse)
this.snapElement = null;
};
// auto-nav controls
elementToHover.OnSelected += e => {
if (this.DisplayInAutoNavMode && e.Controls.IsAutoNavMode) {
this.snapElement = e;
@ -312,5 +341,30 @@ namespace MLEM.Ui.Elements {
paragraph.AutoAdjustWidth = true;
}
private Vector2 GetSnapOffset(Anchor anchor, RectangleF snapBounds, Vector2 offset) {
switch (anchor) {
case Anchor.TopLeft:
return snapBounds.Location - this.DisplayArea.Size - offset;
case Anchor.TopCenter:
return new Vector2(snapBounds.Center.X - this.DisplayArea.Width / 2F, snapBounds.Top - this.DisplayArea.Height) - offset;
case Anchor.TopRight:
return new Vector2(snapBounds.Right + offset.X, snapBounds.Top - this.DisplayArea.Height - offset.Y);
case Anchor.CenterLeft:
return new Vector2(snapBounds.X - this.DisplayArea.Width - offset.X, snapBounds.Center.Y - this.DisplayArea.Height / 2 + offset.Y);
case Anchor.Center:
return snapBounds.Center - this.DisplayArea.Size / 2 + offset;
case Anchor.CenterRight:
return new Vector2(snapBounds.Right, snapBounds.Center.Y - this.DisplayArea.Height / 2) + offset;
case Anchor.BottomLeft:
return new Vector2(snapBounds.X - this.DisplayArea.Width - offset.X, snapBounds.Bottom + offset.Y);
case Anchor.BottomCenter:
return new Vector2(snapBounds.Center.X - this.DisplayArea.Width / 2F, snapBounds.Bottom) + offset;
case Anchor.BottomRight:
return snapBounds.Location + snapBounds.Size + offset;
default:
throw new NotSupportedException($"Tooltip anchors don't support the {anchor} value");
}
}
}
}

View file

@ -167,6 +167,18 @@ namespace MLEM.Ui.Style {
/// </summary>
public Vector2 TooltipAutoNavOffset = new Vector2(0, 8);
/// <summary>
/// The auto-nav anchor that is used or tooltips by default.
/// </summary>
public Anchor TooltipAutoNavAnchor = Anchor.BottomCenter;
/// <summary>
/// The mouse anchor that is used for tooltips by default.
/// </summary>
public Anchor TooltipMouseAnchor = Anchor.BottomRight;
/// <summary>
/// Whether tooltips should use auto-nav rendering behavior for tooltips even when using a mouse by default.
/// </summary>
public bool TooltipUseAutoNavBehaviorForMouse;
/// <summary>
/// The color that the text of a <see cref="Tooltip"/> should have
/// </summary>
public Color TooltipTextColor = Color.White;

View file

@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Formatting;
using MLEM.Formatting.Codes;
using MLEM.Misc;
namespace MLEM.Font {
@ -59,7 +61,7 @@ namespace MLEM.Font {
/// <summary>
/// Draws the given code point with the given data for use in <see cref="DrawString(Microsoft.Xna.Framework.Graphics.SpriteBatch,System.Text.StringBuilder,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Color,float,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Graphics.SpriteEffects,float)"/>.
/// Note that this method is only called internally.
/// Note that this method should only be called internally for rendering of more complex strings, like in <see cref="TextFormatter"/> <see cref="Code"/> implementations.
/// </summary>
/// <param name="batch">The sprite batch to draw with.</param>
/// <param name="codePoint">The code point which will be drawn.</param>
@ -70,7 +72,7 @@ namespace MLEM.Font {
/// <param name="scale">A scaling of this character.</param>
/// <param name="effects">Modificators for drawing. Can be combined.</param>
/// <param name="layerDepth">A depth of the layer of this character.</param>
protected abstract void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth);
public abstract void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth);
///<inheritdoc cref="SpriteBatch.DrawString(SpriteFont,string,Vector2,Color,float,Vector2,float,SpriteEffects,float)"/>
public void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
@ -174,6 +176,82 @@ namespace MLEM.Font {
return GenericFont.SplitStringSeparate(Enumerable.Repeat(new DecoratedCodePointSource(new CodePointSource(text), this, 0), 1), width, scale).First();
}
/// <summary>
/// Calculates a transformation matrix for drawing a string with the given data.
/// </summary>
/// <param name="position">The position to draw at.</param>
/// <param name="rotation">The rotation to draw with.</param>
/// <param name="origin">The origin to subtract from the position.</param>
/// <param name="scale">The scale to draw with.</param>
/// <param name="effects">The flipping to draw with.</param>
/// <param name="flipSize">The size of the string, which is only used when <paramref name="effects"/> is not <see cref="SpriteEffects.None"/>.</param>
/// <returns>A transformation matrix.</returns>
public Matrix CalculateStringTransform(Vector2 position, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Vector2 flipSize) {
var (flipX, flipY) = (0F, 0F);
var flippedV = (effects & SpriteEffects.FlipVertically) != 0;
var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0;
if (flippedV || flippedH) {
if (flippedH) {
origin.X *= -1;
flipX = -flipSize.X;
}
if (flippedV) {
origin.Y *= -1;
flipY = this.LineHeight - flipSize.Y;
}
}
var trans = Matrix.Identity;
if (rotation == 0) {
trans.M11 = flippedH ? -scale.X : scale.X;
trans.M22 = flippedV ? -scale.Y : scale.Y;
trans.M41 = (flipX - origin.X) * trans.M11 + position.X;
trans.M42 = (flipY - origin.Y) * trans.M22 + position.Y;
} else {
var sin = (float) Math.Sin(rotation);
var cos = (float) Math.Cos(rotation);
trans.M11 = (flippedH ? -scale.X : scale.X) * cos;
trans.M12 = (flippedH ? -scale.X : scale.X) * sin;
trans.M21 = (flippedV ? -scale.Y : scale.Y) * -sin;
trans.M22 = (flippedV ? -scale.Y : scale.Y) * cos;
trans.M41 = (flipX - origin.X) * trans.M11 + (flipY - origin.Y) * trans.M21 + position.X;
trans.M42 = (flipX - origin.X) * trans.M12 + (flipY - origin.Y) * trans.M22 + position.Y;
}
return trans;
}
/// <summary>
/// Moves the passed <paramref name="charPos"/> based on the given flipping data.
/// </summary>
/// <param name="charPos">The position to move.</param>
/// <param name="effects">The flipping to move based on.</param>
/// <param name="charSize">The size of the object to move, which is only used when <paramref name="effects"/> is not <see cref="SpriteEffects.None"/>.</param>
/// <returns>The moved position.</returns>
public Vector2 MoveFlipped(Vector2 charPos, SpriteEffects effects, Vector2 charSize) {
if ((effects & SpriteEffects.FlipHorizontally) != 0)
charPos.X += charSize.X;
if ((effects & SpriteEffects.FlipVertically) != 0)
charPos.Y += charSize.Y - this.LineHeight;
return charPos;
}
/// <summary>
/// Transforms the position of a single character to draw.
/// In general, it is efficient to calculate the transformation matrix once at the start (using <see cref="CalculateStringTransform"/>) and to then apply flipping data for each character individually (using <see cref="MoveFlipped"/>).
/// </summary>
/// <param name="stringPos">The position that the string is drawn at.</param>
/// <param name="charPosOffset">The offset from the <paramref name="stringPos"/> that the current character is drawn at.</param>
/// <param name="rotation">The rotation to draw with.</param>
/// <param name="origin">The origin to subtract from the position.</param>
/// <param name="scale">The scale to draw with.</param>
/// <param name="effects">The flipping to draw with.</param>
/// <param name="stringSize">The size of the string, which is only used when <paramref name="effects"/> is not <see cref="SpriteEffects.None"/>.</param>
/// <param name="charSize">The size of the current character, which is only used when <paramref name="effects"/> is not <see cref="SpriteEffects.None"/>.</param>
/// <returns>The transformed final draw position.</returns>
public Vector2 TransformSingleCharacter(Vector2 stringPos, Vector2 charPosOffset, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
return Vector2.Transform(this.MoveFlipped(charPosOffset, effects, charSize), this.CalculateStringTransform(stringPos, rotation, origin, scale, effects, stringSize));
}
private Vector2 MeasureString(CodePointSource text, bool ignoreTrailingSpaces) {
var size = Vector2.Zero;
if (text.Length <= 0)
@ -219,37 +297,8 @@ namespace MLEM.Font {
}
private void DrawString(SpriteBatch batch, CodePointSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
var (flipX, flipY) = (0F, 0F);
var flippedV = (effects & SpriteEffects.FlipVertically) != 0;
var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0;
if (flippedV || flippedH) {
var size = this.MeasureString(text, false);
if (flippedH) {
origin.X *= -1;
flipX = -size.X;
}
if (flippedV) {
origin.Y *= -1;
flipY = this.LineHeight - size.Y;
}
}
var trans = Matrix.Identity;
if (rotation == 0) {
trans.M11 = flippedH ? -scale.X : scale.X;
trans.M22 = flippedV ? -scale.Y : scale.Y;
trans.M41 = (flipX - origin.X) * trans.M11 + position.X;
trans.M42 = (flipY - origin.Y) * trans.M22 + position.Y;
} else {
var sin = (float) Math.Sin(rotation);
var cos = (float) Math.Cos(rotation);
trans.M11 = (flippedH ? -scale.X : scale.X) * cos;
trans.M12 = (flippedH ? -scale.X : scale.X) * sin;
trans.M21 = (flippedV ? -scale.Y : scale.Y) * -sin;
trans.M22 = (flippedV ? -scale.Y : scale.Y) * cos;
trans.M41 = (flipX - origin.X) * trans.M11 + (flipY - origin.Y) * trans.M21 + position.X;
trans.M42 = (flipX - origin.X) * trans.M12 + (flipY - origin.Y) * trans.M22 + position.Y;
}
var flipSize = effects != SpriteEffects.None ? this.MeasureString(text, false) : Vector2.Zero;
var trans = this.CalculateStringTransform(position, rotation, origin, scale, effects, flipSize);
var offset = Vector2.Zero;
var index = 0;
@ -261,14 +310,7 @@ namespace MLEM.Font {
} else {
var character = CodePointSource.ToString(codePoint);
var charSize = this.MeasureString(character);
var charPos = offset;
if (flippedH)
charPos.X += charSize.X;
if (flippedV)
charPos.Y += charSize.Y - this.LineHeight;
Vector2.Transform(ref charPos, ref trans, out charPos);
var charPos = Vector2.Transform(this.MoveFlipped(offset, effects, charSize), trans);
this.DrawCharacter(batch, codePoint, character, charPos, color, rotation, scale, effects, layerDepth);
offset.X += charSize.X;
}

View file

@ -39,7 +39,7 @@ namespace MLEM.Font {
}
/// <inheritdoc />
protected override void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
public override void DrawCharacter(SpriteBatch batch, int codePoint, string character, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
batch.DrawString(this.Font, character, position, color, rotation, Vector2.Zero, scale, effects, layerDepth);
}

View file

@ -92,12 +92,12 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc cref="Formatting.Token.DrawCharacter"/>
public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
return false;
}
/// <inheritdoc cref="Formatting.Token.DrawSelf"/>
public virtual void DrawSelf(GameTime time, SpriteBatch batch, Token token, Vector2 pos, GenericFont font, Color color, float scale, float depth) {}
public virtual void DrawSelf(GameTime time, SpriteBatch batch, Token token, Vector2 stringPos, Vector2 charPosOffset, GenericFont font, Color color, Vector2 scale, float rotation, Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize) {}
/// <summary>
/// Creates a new formatting code from the given regex and regex match.

View file

@ -36,9 +36,11 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override void DrawSelf(GameTime time, SpriteBatch batch, Token token, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
public override void DrawSelf(GameTime time, SpriteBatch batch, Token token, Vector2 stringPos, Vector2 charPosOffset, GenericFont font, Color color, Vector2 scale, float rotation, Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize) {
var actualColor = this.copyTextColor ? color : Color.White.CopyAlpha(color);
batch.Draw(this.image.CurrentRegion, new RectangleF(pos, new Vector2(font.LineHeight * scale)), actualColor);
var size = new Vector2(font.LineHeight);
var finalPos = font.TransformSingleCharacter(stringPos, charPosOffset, rotation, origin, scale, effects, stringSize, size);
batch.Draw(this.image.CurrentRegion, new RectangleF(finalPos, size * scale), actualColor, rotation, Vector2.Zero, effects, 0);
}
}

View file

@ -40,9 +40,9 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
// since we inherit from UnderlineCode, we can just call base if selected
return this.IsSelected() && base.DrawCharacter(time, batch, codePoint, character, token, indexInToken, ref pos, font, ref color, ref scale, depth);
return this.IsSelected() && base.DrawCharacter(time, batch, codePoint, character, token, indexInToken, stringPos, ref charPosOffset, font, ref color, ref scale, ref rotation, ref origin, depth, effects, stringPos, charSize);
}
}

View file

@ -22,10 +22,11 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
foreach (var dir in this.diagonals ? Direction2Helper.AllExceptNone : Direction2Helper.Adjacent) {
var offset = Vector2.Normalize(dir.Offset().ToVector2()) * (this.thickness * scale);
font.DrawString(batch, character, pos + offset, this.color.CopyAlpha(color), 0, Vector2.Zero, scale, SpriteEffects.None, depth);
var offset = Vector2.Normalize(dir.Offset().ToVector2()) * this.thickness;
var finalPos = font.TransformSingleCharacter(stringPos, charPosOffset + offset, rotation, origin, scale, effects, stringSize, charSize);
font.DrawCharacter(batch, codePoint, character, finalPos, this.color.CopyAlpha(color), rotation, scale, effects, depth);
}
return false;
}

View file

@ -18,8 +18,9 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
font.DrawString(batch, character, pos + this.offset * scale, this.color.CopyAlpha(color), 0, Vector2.Zero, scale, SpriteEffects.None, depth);
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
var finalPos = font.TransformSingleCharacter(stringPos, charPosOffset + this.offset, rotation, origin, scale, effects, stringSize, charSize);
font.DrawCharacter(batch, codePoint, character, finalPos, this.color.CopyAlpha(color), rotation, scale, effects, depth);
// we return false since we still want regular drawing to occur
return false;
}

View file

@ -15,8 +15,8 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
pos.Y += this.offset * font.LineHeight * scale;
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
charPosOffset.Y += this.offset * font.LineHeight * scale.Y;
return false;
}

View file

@ -19,13 +19,12 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
// don't underline spaces at the end of lines
if (codePoint == ' ' && token.DisplayString.Length > indexInToken + 1 && token.DisplayString[indexInToken + 1] == '\n')
return false;
var size = font.MeasureString(character) * scale;
var t = size.Y * this.thickness;
batch.Draw(batch.GetBlankTexture(), new RectangleF(pos.X, pos.Y + this.yOffset * size.Y - t, size.X, t), color);
var finalPos = font.TransformSingleCharacter(stringPos, charPosOffset + new Vector2(0, (this.yOffset - this.thickness) * charSize.Y), rotation, origin, scale, effects, stringSize, charSize);
batch.Draw(batch.GetBlankTexture(), new RectangleF(finalPos.X, finalPos.Y, charSize.X * scale.X, this.thickness * charSize.Y * scale.Y), null, color, rotation, Vector2.Zero, SpriteEffects.None, depth);
return false;
}

View file

@ -28,9 +28,8 @@ namespace MLEM.Formatting.Codes {
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
var offset = new Vector2(0, (float) Math.Sin(token.Index + indexInToken + this.TimeIntoAnimation.TotalSeconds * this.modifier) * font.LineHeight * this.heightModifier * scale);
pos += offset;
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, Vector2 stringPos, ref Vector2 charPosOffset, GenericFont font, ref Color color, ref Vector2 scale, ref float rotation, ref Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
charPosOffset += new Vector2(0, (float) Math.Sin(token.Index + indexInToken + this.TimeIntoAnimation.TotalSeconds * this.modifier) * font.LineHeight * this.heightModifier * scale.Y);
// we return false since we still want regular drawing to occur, we just changed the position
return false;
}

View file

@ -104,14 +104,19 @@ namespace MLEM.Formatting {
/// </summary>
/// <param name="time">The time</param>
/// <param name="batch">The sprite batch to use</param>
/// <param name="pos">The position to draw the token at</param>
/// <param name="stringPos">The position the string is drawn at.</param>
/// <param name="charPosOffset">The offset from the <paramref name="stringPos"/> that the current character is drawn at.</param>
/// <param name="font">The font to use to draw</param>
/// <param name="color">The color to draw with</param>
/// <param name="scale">The scale to draw at</param>
/// <param name="scale">The scale to draw with.</param>
/// <param name="rotation">The rotation to draw with.</param>
/// <param name="origin">The origin to subtract from the position.</param>
/// <param name="depth">The depth to draw at</param>
public void DrawSelf(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
/// <param name="effects">The flipping to draw with.</param>
/// <param name="stringSize">The size of the string.</param>
public void DrawSelf(GameTime time, SpriteBatch batch, Vector2 stringPos, Vector2 charPosOffset, GenericFont font, Color color, Vector2 scale, float rotation, Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize) {
foreach (var code in this.AppliedCodes)
code.DrawSelf(time, batch, this, pos, font, color, scale, depth);
code.DrawSelf(time, batch, this, stringPos, charPosOffset, font, color, scale, rotation, origin, depth, effects, stringSize);
}
/// <summary>
@ -122,19 +127,26 @@ namespace MLEM.Formatting {
/// <param name="codePoint">The code point of the character to draw</param>
/// <param name="character">The string representation of the character to draw</param>
/// <param name="indexInToken">The index within this token that the character is at</param>
/// <param name="pos">The position to draw the token at</param>
/// <param name="stringPos">The position the string is drawn at.</param>
/// <param name="charPosOffset">The offset from the <paramref name="stringPos"/> that the current character is drawn at.</param>
/// <param name="font">The font to use to draw</param>
/// <param name="color">The color to draw with</param>
/// <param name="scale">The scale to draw at</param>
/// <param name="scale">The scale to draw with.</param>
/// <param name="rotation">The rotation to draw with.</param>
/// <param name="origin">The origin to subtract from the position.</param>
/// <param name="depth">The depth to draw at</param>
public void DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, int indexInToken, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
/// <param name="effects">The flipping to draw with.</param>
/// <param name="stringSize">The size of the string.</param>
/// <param name="charSize">The size of the current character.</param>
public void DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, int indexInToken, Vector2 stringPos, Vector2 charPosOffset, GenericFont font, Color color, Vector2 scale, float rotation, Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) {
foreach (var code in this.AppliedCodes) {
if (code.DrawCharacter(time, batch, codePoint, character, this, indexInToken, ref pos, font, ref color, ref scale, depth))
if (code.DrawCharacter(time, batch, codePoint, character, this, indexInToken, stringPos, ref charPosOffset, font, ref color, ref scale, ref rotation, ref origin, depth, effects, stringSize, charSize))
return;
}
// if no code drew, we have to do it ourselves
font.DrawString(batch, character, pos, color, 0, Vector2.Zero, scale, SpriteEffects.None, depth);
var finalPos = font.TransformSingleCharacter(stringPos, charPosOffset, rotation, origin, scale, effects, stringSize, charSize);
font.DrawCharacter(batch, codePoint, character, finalPos, color, rotation, scale, effects, depth);
}
/// <summary>

View file

@ -181,8 +181,13 @@ namespace MLEM.Formatting {
}
/// <inheritdoc cref="GenericFont.DrawString(SpriteBatch,string,Vector2,Color,float,Vector2,float,SpriteEffects,float)"/>
public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth, int? startIndex = null, int? endIndex = null) {
var innerOffset = new Vector2(this.initialInnerOffset * scale, 0);
public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth, float rotation = 0, Vector2 origin = default, SpriteEffects effects = SpriteEffects.None, int? startIndex = null, int? endIndex = null) {
this.Draw(time, batch, pos, font, color, new Vector2(scale), depth, rotation, origin, effects, startIndex, endIndex);
}
/// <inheritdoc cref="GenericFont.DrawString(SpriteBatch,string,Vector2,Color,float,Vector2,float,SpriteEffects,float)"/>
public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, Vector2 scale, float depth, float rotation = 0, Vector2 origin = default, SpriteEffects effects = SpriteEffects.None, int? startIndex = null, int? endIndex = null) {
var innerOffset = new Vector2(this.initialInnerOffset, 0);
for (var t = 0; t < this.Tokens.Length; t++) {
var token = this.Tokens[t];
if (endIndex != null && token.Index >= endIndex)
@ -192,8 +197,8 @@ namespace MLEM.Formatting {
var drawColor = token.GetColor(color);
if (startIndex == null || token.Index >= startIndex)
token.DrawSelf(time, batch, pos + innerOffset, drawFont, drawColor, scale, depth);
innerOffset.X += token.GetSelfWidth(drawFont) * scale;
token.DrawSelf(time, batch, pos, innerOffset, drawFont, drawColor, scale, rotation, origin, depth, effects, this.area.Size);
innerOffset.X += token.GetSelfWidth(drawFont);
var indexInToken = 0;
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
@ -205,19 +210,20 @@ namespace MLEM.Formatting {
var (codePoint, length) = line.GetCodePoint(cpsIndex);
var character = CodePointSource.ToString(codePoint);
var charSize = drawFont.MeasureString(character);
if (startIndex == null || token.Index + indexInToken >= startIndex)
token.DrawCharacter(time, batch, codePoint, character, indexInToken, pos + innerOffset, drawFont, drawColor, scale, depth);
token.DrawCharacter(time, batch, codePoint, character, indexInToken, pos, innerOffset, drawFont, drawColor, scale, rotation, origin, depth, effects, this.area.Size, charSize);
innerOffset.X += drawFont.MeasureString(character).X * scale;
innerOffset.X += charSize.X;
indexInToken += length;
cpsIndex += length;
}
// only split at a new line, not between tokens!
if (l < token.SplitDisplayString.Length - 1) {
innerOffset.X = token.InnerOffsets[l] * scale;
innerOffset.Y += drawFont.LineHeight * scale;
innerOffset.X = token.InnerOffsets[l];
innerOffset.Y += drawFont.LineHeight;
}
}
}

View file

@ -11,6 +11,7 @@ using MLEM.Data;
using MLEM.Data.Content;
using MLEM.Extended.Font;
using MLEM.Extensions;
using MLEM.Font;
using MLEM.Formatting;
using MLEM.Formatting.Codes;
using MLEM.Graphics;
@ -70,9 +71,7 @@ public class GameImpl : MlemGame {
//var font = new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/TestFont"));
//var font = new GenericBitmapFont(LoadContent<BitmapFont>("Fonts/Regular"));
var font = new GenericStashFont(system.GetFont(32));
/*
var spriteFont = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("Fonts/TestFont"));
*/
this.UiSystem.Style = new UntexturedStyle(this.SpriteBatch) {
Font = font,
TextScale = 0.5F,
@ -258,43 +257,45 @@ public class GameImpl : MlemGame {
}).SetData("Ref", "Main");
this.UiSystem.Add("SpillTest", spillPanel);
/* var regularFont = spriteFont.Font;
var genericFont = spriteFont;
var regularFont = spriteFont.Font;
var genericFont = spriteFont;
var index = 0;
var pos = new Vector2(100, 20);
var scale = 1F;
var origin = Vector2.Zero;
var rotation = 0F;
var effects = SpriteEffects.None;
var index = 0;
var pos = new Vector2(100, 20);
var scale = 1F;
var origin = Vector2.Zero;
var rotation = 0F;
var effects = SpriteEffects.None;
this.OnDraw += (_, _) => {
const string testString = "This is a\ntest string\n\twith long lines.\nLet's write some more stuff. Let's\r\nsplit lines weirdly.";
if (MlemGame.Input.IsKeyPressed(Keys.I)) {
index++;
if (index == 1) {
scale = 2;
} else if (index == 2) {
origin = new Vector2(15, 15);
} else if (index == 3) {
rotation = 0.25F;
} else if (index == 4) {
effects = SpriteEffects.FlipHorizontally;
} else if (index == 5) {
effects = SpriteEffects.FlipVertically;
} else if (index == 6) {
effects = SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically;
}
}
const string testString = "This is a\ntest string\n\twith long lines.\nLet's write some more stuff. Let's\r\nsplit lines weirdly.";
var formatted = formatter.Tokenize(genericFont, testString);
this.SpriteBatch.Begin();
if (MlemGame.Input.IsKeyDown(Keys.LeftShift)) {
this.SpriteBatch.DrawString(regularFont, testString, pos, Color.Red, rotation, origin, scale, effects, 0);
} else {
genericFont.DrawString(this.SpriteBatch, testString, pos, Color.Green, rotation, origin, scale, effects, 0);
}
this.SpriteBatch.End();
};*/
this.OnDraw += (_, time) => {
if (MlemGame.Input.IsPressed(Keys.I)) {
index++;
if (index == 1) {
scale = 2;
} else if (index == 2) {
origin = new Vector2(15, 15);
} else if (index == 3) {
rotation = 0.25F;
} else if (index == 4) {
effects = SpriteEffects.FlipHorizontally;
} else if (index == 5) {
effects = SpriteEffects.FlipVertically;
} else if (index == 6) {
effects = SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically;
}
}
this.SpriteBatch.Begin();
if (MlemGame.Input.IsDown(Keys.LeftShift)) {
this.SpriteBatch.DrawString(regularFont, testString, pos, Color.Red, rotation, origin, scale, effects, 0);
} else {
formatted.Draw(time, this.SpriteBatch, pos, genericFont, Color.Green, scale, 0, rotation, origin, effects);
}
this.SpriteBatch.End();
};
/*var viewport = new BoxingViewportAdapter(this.Window, this.GraphicsDevice, 1280, 720);
var newPanel = new Panel(Anchor.TopLeft, new Vector2(200, 100), new Vector2(10, 10));

View file

@ -2,7 +2,7 @@
#tool dotnet:?package=docfx&version=2.75.3
// this is the upcoming version, for prereleases
var version = Argument("version", "6.3.2");
var version = Argument("version", "7.0.0");
var target = Argument("target", "Default");
var gitRef = Argument("ref", "refs/heads/main");
var buildNum = Argument("buildNum", "");