526 lines
14 KiB
C#
Executable File
526 lines
14 KiB
C#
Executable File
using GraphicsManager.Enums;
|
|
using GraphicsManager.Interfaces;
|
|
using GraphicsManager.Objects.Core;
|
|
using GraphicsManager.Structs;
|
|
using OpenTK.Graphics.OpenGL4;
|
|
using OpenTK.Mathematics;
|
|
using OpenTK.Windowing.Common;
|
|
using OpenTK.Windowing.Common.Input;
|
|
using OpenTK.Windowing.Desktop;
|
|
using OpenTK.Windowing.GraphicsLibraryFramework;
|
|
|
|
namespace GraphicsManager.Objects;
|
|
|
|
public class Label : ILabel
|
|
{
|
|
public static readonly Dictionary<IGLFWGraphicsContext, Shader> DefaultTextShader = new();
|
|
|
|
public Label(FontFamily fontFamily)
|
|
{
|
|
Font = FontInteraction.Load(fontFamily);
|
|
}
|
|
|
|
public Label(FontInteraction interaction)
|
|
{
|
|
Font = interaction;
|
|
}
|
|
|
|
public BetterContextMenu? ContextMenu { get; set; } = null;
|
|
public IParent? Parent { get; private set; }
|
|
public ObjectAnchor Anchor { get; set; } = ObjectAnchor.Left | ObjectAnchor.Top;
|
|
private Vector2 laf = new(), saf = new();
|
|
|
|
public Vector2 LocationAsFloat { get { return laf; } }
|
|
public Vector2 SizeAsFloat { get { return saf; } }
|
|
private Vector2i? msize = null;
|
|
public Vector2i? MaxSize
|
|
{
|
|
get => msize;
|
|
set
|
|
{
|
|
msize = value;
|
|
text_Calculated = string.Empty;
|
|
float max_x = 0, lines = 1, char_x = 0F;
|
|
for (int i = 0; i < Text.Length; i++)
|
|
{
|
|
char c;
|
|
if (PasswordChar is null)
|
|
c = Text[i];
|
|
else
|
|
c = PasswordChar.Value;
|
|
bool n = (c == '\n');
|
|
|
|
if (n)
|
|
{
|
|
lines++;
|
|
text_Calculated += c;
|
|
char_x = 0f;
|
|
}
|
|
else
|
|
{
|
|
Character ch;
|
|
if (Window is not null) ch = Texture.GetChar(Font, c, Window.Context);
|
|
else ch = Texture.GetChar(Font, c);
|
|
if (i > 0 && text[i-1] == ' ')
|
|
{
|
|
int addc = 0;
|
|
float word_char_x = char_x;
|
|
while (MaxSize is not null)
|
|
{
|
|
if (addc + i == Text.Length) break;
|
|
if (text[addc + i] == ' ') break;
|
|
Character ch2;
|
|
if (Window is not null) ch2 = Texture.GetChar(Font, text[addc + i], Window.Context);
|
|
else ch2 = Texture.GetChar(Font, c);
|
|
word_char_x += (ch2.Advance >> 6) * Scale;
|
|
if (word_char_x > MaxSize.Value.X)
|
|
{
|
|
char_x = 0f;
|
|
lines++;
|
|
text_Calculated += '\n';
|
|
break;
|
|
}
|
|
addc++;
|
|
}
|
|
}
|
|
text_Calculated += c;
|
|
float w = ch.Size.X * Scale;
|
|
float xrel = char_x + ch.Bearing.X * Scale;
|
|
char_x += (ch.Advance >> 6) * Scale;
|
|
if ((xrel + w) >= max_x) max_x = (xrel + w);
|
|
}
|
|
}
|
|
Size = new((int)max_x, (int)(lines * LineHeight) + (int)(lines * Font.ExtraLinePixels));
|
|
}
|
|
}
|
|
private char? pc;
|
|
public char? PasswordChar
|
|
{
|
|
get => pc;
|
|
set
|
|
{
|
|
pc = value;
|
|
if (Parent is not null) Parent.TryDraw();
|
|
}
|
|
}
|
|
private bool _Visible = true;
|
|
|
|
public bool Visible
|
|
{
|
|
get => _Visible;
|
|
set
|
|
{
|
|
_Visible = value;
|
|
if (Parent is not null && Loaded) Parent.TryDraw();
|
|
}
|
|
}
|
|
|
|
public static readonly Dictionary<IGLFWGraphicsContext, Dictionary<FontInteraction, Dictionary<uint, Character>>> _characters = new();
|
|
private string text = string.Empty;
|
|
private string text_Calculated = string.Empty;
|
|
public int VAO { get; private set; }
|
|
|
|
public void Focus()
|
|
{
|
|
|
|
}
|
|
public void UnFocus()
|
|
{
|
|
|
|
}
|
|
public int VBO { get; private set; }
|
|
public Vector2 DIR { get; set; } = new Vector2(1f, 0f);
|
|
|
|
public Vector2i GetSizeOfChar(int Index)
|
|
{
|
|
float mx = 0, my = 0;
|
|
char character;
|
|
if (PasswordChar is null)
|
|
character = Text[Index];
|
|
else
|
|
character = PasswordChar.Value;
|
|
|
|
|
|
if (character == '\n')
|
|
{
|
|
mx = 0f;
|
|
my += Font.PixelHeight * Scale;
|
|
}
|
|
else
|
|
{
|
|
Character cha;
|
|
if (Window is not null) cha = Texture.GetChar(Font, character, Window.Context);
|
|
else cha = Texture.GetChar(Font, character);
|
|
mx = cha.Size.X * Scale;
|
|
my = cha.Size.Y * Scale;
|
|
}
|
|
return new((int)mx, (int)my);
|
|
}
|
|
|
|
public string Text
|
|
{
|
|
get => text;
|
|
set
|
|
{
|
|
if (value is null) value = string.Empty;
|
|
text = value;
|
|
text_Calculated = string.Empty;
|
|
float max_x = 0, lines = 1, char_x = 0F;
|
|
for (int i = 0; i < Text.Length; i++)
|
|
{
|
|
char c;
|
|
if (PasswordChar is null)
|
|
c = Text[i];
|
|
else
|
|
c = PasswordChar.Value;
|
|
bool n = (c == '\n');
|
|
|
|
if (n)
|
|
{
|
|
lines++;
|
|
char_x = 0f;
|
|
text_Calculated += c;
|
|
}
|
|
else
|
|
{
|
|
Character ch;
|
|
if (Window is not null) ch = Texture.GetChar(Font, c, Window.Context);
|
|
else ch = Texture.GetChar(Font, c);
|
|
if (MaxSize is not null && i > 0 && text[i-1] == ' ')
|
|
{
|
|
int addc = 0;
|
|
float word_char_x = char_x;
|
|
while (true)
|
|
{
|
|
if (addc + i == Text.Length) break;
|
|
if (text[addc + i] == ' ') break;
|
|
Character ch2;
|
|
if (Window is not null) ch2 = Texture.GetChar(Font, text[addc + i], Window.Context);
|
|
else ch2 = Texture.GetChar(Font, c);
|
|
word_char_x += (ch2.Advance >> 6) * Scale;
|
|
if (word_char_x > MaxSize.Value.X)
|
|
{
|
|
char_x = 0f;
|
|
lines++;
|
|
text_Calculated += '\n';
|
|
break;
|
|
}
|
|
addc++;
|
|
}
|
|
}
|
|
|
|
text_Calculated += c;
|
|
float w = ch.Size.X * Scale;
|
|
float xrel = char_x + ch.Bearing.X * Scale;
|
|
char_x += (ch.Advance >> 6) * Scale;
|
|
if ((xrel + w) >= max_x) max_x = (xrel + w);
|
|
}
|
|
}
|
|
|
|
|
|
Size = new((int)max_x, (int)(lines * LineHeight) + (int)(lines * Font.ExtraLinePixels));
|
|
if (Loaded)
|
|
{
|
|
|
|
if (Window is not null && Window.CanControleUpdate)
|
|
{
|
|
if (!Window.Context.IsCurrent)
|
|
{
|
|
try
|
|
{
|
|
Window.Context.MakeCurrent();
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
}
|
|
}
|
|
|
|
if (Window.Context.IsCurrent)
|
|
{
|
|
Parent!.TryDraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public uint LineHeight
|
|
{
|
|
get
|
|
{
|
|
return (uint)((Font.PixelHeight * (Font.CurrentFonts[0].Face.Height / (float)Font.CurrentFonts[0].Face.UnitsPerEM)) * Scale);
|
|
}
|
|
}
|
|
public Shader Shader { get; set; } = null!;
|
|
public FontInteraction Font { get; }
|
|
public float Scale { get; set; } = 1.0f;
|
|
public Color4 Color { get; set; } = new Color4(255, 255, 255, 255);
|
|
public Vector2i Distance { get; set; }
|
|
private Vector3i loc_;
|
|
public Vector3i Location
|
|
{
|
|
get
|
|
{
|
|
return loc_;
|
|
}
|
|
set
|
|
{
|
|
loc_ = value;
|
|
if (Window is null || Parent is null) return;
|
|
if (Window.CanControleUpdate && Loaded)
|
|
{
|
|
Parent!.TryDraw();
|
|
if (!Window.Context.IsCurrent) Window.Context.MakeCurrent();
|
|
}
|
|
}
|
|
}
|
|
|
|
private Vector2i _size;
|
|
public Vector2i Size { get => _size;
|
|
set
|
|
{
|
|
_size = value;
|
|
//if (Loaded && Parent is not null) ScissorLocation = Parent.GetParentRelLocPoint() + new Vector2i(Location.X, Parent.Size.Y - (loc_.Y + Size.Y));
|
|
}
|
|
}
|
|
|
|
public Vector2i GetCharLocation(int index)
|
|
{
|
|
float max_x = 0, lines = 0, char_x = 0F;
|
|
for (int i = 0; i < index; i++)
|
|
{
|
|
char c;
|
|
if (PasswordChar is null)
|
|
c = text_Calculated[i];
|
|
else
|
|
c = PasswordChar.Value;
|
|
bool n = (c == '\n');
|
|
|
|
if (n)
|
|
{
|
|
lines++;
|
|
char_x = 0f;
|
|
}
|
|
else
|
|
{
|
|
Character ch;
|
|
if (Window is not null) ch = Texture.GetChar(Font, c, Window.Context);
|
|
else ch = Texture.GetChar(Font, c);
|
|
float w = ch.Size.X * Scale;
|
|
float xrel = char_x + ch.Bearing.X * Scale;
|
|
char_x += (ch.Advance >> 6) * Scale;
|
|
max_x = (xrel + w);
|
|
}
|
|
}
|
|
return new((int)max_x, (int)(lines * LineHeight) + (int)(lines * Font.ExtraLinePixels));
|
|
}
|
|
|
|
public void Clean()
|
|
{
|
|
Tuple<int, int, int> tup = GlobalBuffers[Window!.Context];
|
|
if (tup.Item3 - 1 == 0)
|
|
{
|
|
//Broken, I may fix latter
|
|
|
|
//if (!Window.Context.IsCurrent) Window.Context.MakeCurrent();
|
|
//GL.DeleteBuffer(VBO);
|
|
//GL.DeleteVertexArray(VAO);
|
|
//GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
//GlobalBuffers.Remove(Window!.Context);
|
|
}
|
|
else
|
|
{
|
|
GlobalBuffers[Window!.Context] = new(tup.Item1, tup.Item2, tup.Item3 - 1);
|
|
}
|
|
Size = new(0, 0);
|
|
Loaded = false;
|
|
Visible = false;
|
|
}
|
|
|
|
public event Func<IRenderObject, Task>? SizeChanged;
|
|
|
|
public void Draw(int x, int y, int ww, int hh)
|
|
{
|
|
if (Visible && Loaded && this.Font is not null)
|
|
{
|
|
if (!Window!.Context.IsCurrent) Window.Context.MakeCurrent();
|
|
Shader.Use();
|
|
GL.Enable(EnableCap.Blend);
|
|
GL.Uniform4(Shader.GetUniformLocation("textColor"), Color);
|
|
Matrix4 projectionM = Matrix4.CreateOrthographicOffCenter(0, Window!.Size.X, Window!.Size.Y, 0, -1.0f, 1.0f);
|
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
GL.BlendFunc(0, BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
|
|
GL.UniformMatrix4(1, false, ref projectionM);
|
|
|
|
GL.BindVertexArray(VAO);
|
|
|
|
float angle_rad = (float)Math.Atan2(DIR.Y, DIR.X);
|
|
Matrix4 rotateM = Matrix4.CreateRotationZ(angle_rad);
|
|
Matrix4 transOriginM = Matrix4.CreateTranslation(new Vector3(loc_.X + Window.FloatToInt(Parent!.IntToFloat(0)), loc_.Y + (Font.PixelHeight * Scale) + Window.FloatToInt(Parent!.IntToFloat(0, true), true), 0f));
|
|
float char_x = 0.0f;
|
|
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
|
|
GL.ActiveTexture((Shader.GetUniformLocation("u_texture") switch
|
|
{
|
|
0 => TextureUnit.Texture0,
|
|
1 => TextureUnit.Texture1,
|
|
2 => TextureUnit.Texture2,
|
|
3 => TextureUnit.Texture3,
|
|
4 => TextureUnit.Texture4,
|
|
5 => TextureUnit.Texture5,
|
|
6 => TextureUnit.Texture6,
|
|
7 => TextureUnit.Texture7,
|
|
8 => TextureUnit.Texture8,
|
|
9 => TextureUnit.Texture9,
|
|
}));
|
|
|
|
float hhh = 0f;
|
|
for (int i = 0; i < text_Calculated.Length; i++)
|
|
{
|
|
char c;
|
|
if (PasswordChar is null)
|
|
c = text_Calculated[i];
|
|
else
|
|
c = PasswordChar.Value;
|
|
bool n = (c == '\n');
|
|
if (!_characters[Window!.Context][Font].ContainsKey(c) && !n)
|
|
{
|
|
_ = Texture.TextureForChar(Window!.Context, Font, c, Shader);
|
|
}
|
|
|
|
if (n)
|
|
{
|
|
hhh += LineHeight;
|
|
hhh += Font.ExtraLinePixels;
|
|
char_x = 0f;
|
|
}
|
|
else
|
|
{
|
|
if (!_characters[Window!.Context][Font].ContainsKey(c)) continue;
|
|
Character ch = _characters[Window!.Context][Font][c];
|
|
|
|
float w = ch.Size.X * Scale;
|
|
float h = ch.Size.Y * Scale;
|
|
float xrel = char_x + ch.Bearing.X * Scale;
|
|
float yrel = (ch.Size.Y - ch.Bearing.Y) * Scale;
|
|
yrel += hhh;
|
|
char_x += (ch.Advance >> 6) * Scale;
|
|
Matrix4 scaleM = Matrix4.CreateScale(new Vector3(w, h, 1.0f));
|
|
Matrix4 transRelM = Matrix4.CreateTranslation(new Vector3(xrel, yrel, 0.0f));
|
|
|
|
Matrix4 modelM = scaleM * transRelM * rotateM * transOriginM;
|
|
GL.UniformMatrix4(0, false, ref modelM);
|
|
|
|
ch.Texture.Use();
|
|
|
|
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public void ForceDistanceUpdate()
|
|
{
|
|
}
|
|
|
|
public void ForceDistanceUpdate(IParent parent)
|
|
{
|
|
Distance = new(parent.Size.X - Size.X - Location.X, parent.Size.Y - Size.Y - Location.Y);
|
|
}
|
|
public IWindow? Window { get; private set; }
|
|
private static Dictionary<IGLFWGraphicsContext, Tuple<int, int, int>> GlobalBuffers = new();
|
|
public bool IgnoreHover { get; set; }
|
|
public bool AllowHoverFromBehind { get; set; }
|
|
public void LoadToParent(IParent window, IWindow win)
|
|
{
|
|
if (Loaded) return;
|
|
if (!_characters.ContainsKey(win!.Context)) _characters.Add(win!.Context, new());
|
|
if (!_characters[win!.Context].ContainsKey(Font)) _characters[win!.Context].Add(Font, new Dictionary<uint, Character>());
|
|
if (Shader is null) Shader = DefaultTextShader[win.Context];
|
|
Parent = window;
|
|
Window = win;
|
|
Window.MouseDown += WindowOnMouseDown;
|
|
if (!Window!.Context.IsCurrent) Window.Context.MakeCurrent();
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
|
if (!GlobalBuffers.ContainsKey(win.Context))
|
|
{
|
|
float[] vquad =
|
|
{
|
|
0.0f, -1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f,
|
|
1.0f, 0.0f, 1.0f, 1.0f,
|
|
0.0f, -1.0f, 0.0f, 0.0f,
|
|
1.0f, 0.0f, 1.0f, 1.0f,
|
|
1.0f, -1.0f, 1.0f, 0.0f
|
|
};
|
|
int _VBO = GL.GenBuffer();
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, _VBO);
|
|
GL.BufferData(BufferTarget.ArrayBuffer, 4 * 6 * 4, vquad, BufferUsageHint.StaticDraw);
|
|
int _VAO = GL.GenVertexArray();
|
|
GL.BindVertexArray(_VAO);
|
|
GL.EnableVertexAttribArray(0);
|
|
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * 4, 0);
|
|
GL.EnableVertexAttribArray(1);
|
|
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * 4, 2 * 4);
|
|
GlobalBuffers.Add(win.Context, new(_VBO, _VAO, 0));
|
|
}
|
|
|
|
Tuple<int, int, int> tup = GlobalBuffers[win.Context];
|
|
VBO = tup.Item1;
|
|
VAO = tup.Item2;
|
|
GlobalBuffers[win.Context] = new(VBO, VAO, tup.Item3 + 1);
|
|
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
GL.BindVertexArray(0);
|
|
|
|
Loaded = true;
|
|
Text = Text;
|
|
Location = Location;
|
|
if (Distance.X == 0 && Distance.Y == 0) Distance = new(Parent.Size.X - Size.X - Location.X, Parent.Size.Y - Size.Y - Location.Y);
|
|
if (Distance.X == 0 && Distance.Y == 0) Distance = new(Parent.Size.X - Size.X - Location.X, Parent.Size.Y - Size.Y - Location.Y);
|
|
if (WindowLoaded is not null) WindowLoaded.Invoke(this);
|
|
}
|
|
|
|
private void WindowOnMouseDown(MouseButtonEventArgs obj)
|
|
{
|
|
if (MouseInside && obj.Button == MouseButton.Button1 && Clicked is not null) _ = Clicked.Invoke(this);
|
|
if (MouseInside && obj.Button == MouseButton.Button2 && ContextMenu is not null && Window!.HoveringControl == this) ContextMenu.ShowContext(Window!);
|
|
if (obj.Button != MouseButton.Button2 && ContextMenu is not null && ContextMenu.Visible) ContextMenu.HideContext(Window!);
|
|
}
|
|
|
|
public MouseCursor HoverMouse { get; set; } = MouseCursor.Default;
|
|
|
|
private bool mi;
|
|
|
|
public bool MouseInside
|
|
{
|
|
get => mi;
|
|
set
|
|
{
|
|
if (Window is null) return;
|
|
if (Window.HoveringControl == this && value)
|
|
{
|
|
mi = value;
|
|
if (MouseEnter is not null) MouseEnter.Invoke(this);
|
|
}
|
|
|
|
if (Window.HoveringControl != this && !value)
|
|
{
|
|
mi = value;
|
|
if (MouseLeave is not null) MouseLeave.Invoke(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
public event Func<IRenderObject, Task>? Clicked;
|
|
public event Func<IRenderObject, Task>? WindowLoaded;
|
|
public event Func<IRenderObject, Task>? MouseEnter;
|
|
public event Func<IRenderObject, Task>? MouseLeave;
|
|
public object? Tag { get; set; } = null;
|
|
|
|
public bool Loaded { get; private set; }
|
|
}
|