checkbox, radio button, and parts of the toolbar work now

This commit is contained in:
Booklordofthedings 2024-06-06 15:31:32 +02:00
parent d9b116f389
commit 70ea6d0555
32 changed files with 788 additions and 282 deletions

View file

@ -1,3 +1,23 @@
# TheaterGui # TheaterGui *A simple ui library*
*Tg* is inspired by Winforms, so most components require their own new classes, generated by the "New File" functionality of BeefIDE
Simple library to create graphical user interfaces ### Components
- Container
- Screen
- Button
### Layouting
*Tg* uses a layouting system thats partially inspired by HTML, though its significantly less powerfull but easier to implement and to grasp.
All components are put next to their immidiate left neighbor.
If an object would overflow outside of the view width, it will instead be forced down onto the next row.
Calling *EndRow()* manually will also force components into the next row.
The vertical position of the next row is determined by the height of the largest component in the row above, so that they never overlap.
If the screen height is larger than the view it will scroll.
Containers are a component, that can be used for more complicated layouts.
A container grows to fits its children but is still treated like a normal components.
So if part of your layout needs to be vertical you would put it inside of a container and then add the container normally
Every component also has a Margin property, to make things look better.
Whenever a component changes its size, the app will need to reorder its layout to fit everything.
*App.ForceReorder()* Is used for that. Most components will call it themselves if their size changes
If you want to change alot of sizes at once consider turning of *App.AllowReorder* and then manually calling it after you are done to increase the total speed.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

BIN
assets/horizontal_patch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

0
docs/Button.md Normal file
View file

View file

@ -1,14 +1,75 @@
namespace TheaterGui; namespace TheaterGui;
using TheaterGui.Controls; using TheaterGui.Components;
using System; using System;
using System.Collections;
using RaylibBeef; using RaylibBeef;
class App class App
{ {
//All loaded textures
private static List<Component> Popups = new .() ~ DeleteContainerAndItems!(_);
public static Textures Textures ~ delete _;
public static Component Selected; //Currently selected component
public static bool AllowReorder = true;
private static Screen _CurrentScreen = null;
public static Screen CurrentScreen
{
get => _CurrentScreen;
set
{
if(_CurrentScreen != null)
delete _CurrentScreen;
_CurrentScreen = value;
}
};
///Window width
public static int32 Width
{
get => Raylib.GetRenderWidth();
set => Raylib.SetWindowSize(value, Raylib.GetRenderHeight());
}
///Window height
public static int32 Height
{
get => Raylib.GetRenderHeight();
set => Raylib.SetWindowSize(Raylib.GetRenderWidth(), value);
}
///Mouse position
public static Vector2 Mouse
{
get => Raylib.GetMousePosition();
}
//Mouse wheel offset
public static float Wheel
{
get;
set;
}
public static void ForceReorder()
{
if(AllowReorder && CurrentScreen != null)
CurrentScreen.Reorder(Width);
}
public static void OpenPopup(Component popup)
{
Popups.Add(popup);
}
///Initialize the TheaterGui app ///Initialize the TheaterGui app
public static void Initialize(StringView pAppTitle = "TheaterApp") public static void Initialize(StringView pAppTitle = "TheaterApp")
{ {
//Default raylib settings
Raylib.SetConfigFlags((.)(ConfigFlags.FLAG_WINDOW_RESIZABLE | ConfigFlags.FLAG_WINDOW_HIGHDPI)); Raylib.SetConfigFlags((.)(ConfigFlags.FLAG_WINDOW_RESIZABLE | ConfigFlags.FLAG_WINDOW_HIGHDPI));
Raylib.InitWindow(1280, 720, scope String(pAppTitle)); Raylib.InitWindow(1280, 720, scope String(pAppTitle));
Raylib.SetExitKey(0); Raylib.SetExitKey(0);
@ -22,7 +83,10 @@ class App
//Load textures //Load textures
Textures = new .(); Textures = new .();
Theme.Font = Raylib.LoadFontFromMemory(".ttf", (.)&Theme.Din, Theme.Din.Count, 16, null, 256); Theme.Font = Raylib.LoadFontFromMemory(".ttf", (.)&Theme.Din, Theme.Din.Count, Theme.FontSize, null, 256);
Theme.FontSmall = Raylib.LoadFontFromMemory(".ttf", (.)&Theme.Din, Theme.Din.Count, Theme.FontSizeSmall, null, 256);
Theme.FontLarge = Raylib.LoadFontFromMemory(".ttf", (.)&Theme.Din, Theme.Din.Count, Theme.FontSizeLarge, null, 256);
} }
///Deinitialize the TheaterGui app ///Deinitialize the TheaterGui app
@ -61,10 +125,34 @@ class App
if(Raylib.IsMouseButtonPressed(0)) if(Raylib.IsMouseButtonPressed(0))
{ {
if(Popups.Count > 0)
{
var top = Popups[Popups.Count-1];
if(Rectangle(top.X, top.Y, top.Width, top.Height).Overlaps(Raylib.GetMouseX(), Raylib.GetMouseY()))
top.OnClick(Raylib.GetMouseX(), Raylib.GetMouseY());
else
{
Popups.Remove(top);
delete top;
CurrentScreen.OnClick(Raylib.GetMouseX(), (.)(Raylib.GetMouseY()+App.Wheel));
}
}
else
CurrentScreen.OnClick(Raylib.GetMouseX(), (.)(Raylib.GetMouseY()+App.Wheel)); CurrentScreen.OnClick(Raylib.GetMouseX(), (.)(Raylib.GetMouseY()+App.Wheel));
} }
else else
{
if(Popups.Count > 0)
{
var top = Popups[Popups.Count-1];
if(Rectangle(top.X, top.Y, top.Width, top.Height).Overlaps(Raylib.GetMouseX(), Raylib.GetMouseY()))
{
}
}
else
{ {
var hov = CurrentScreen.OnHover(Raylib.GetMouseX(), (.)(Raylib.GetMouseY()+App.Wheel)); var hov = CurrentScreen.OnHover(Raylib.GetMouseX(), (.)(Raylib.GetMouseY()+App.Wheel));
if(hov != null) if(hov != null)
@ -81,18 +169,20 @@ class App
else else
Raylib.SetMouseCursor((.)MouseCursor.MOUSE_CURSOR_DEFAULT); Raylib.SetMouseCursor((.)MouseCursor.MOUSE_CURSOR_DEFAULT);
} }
}
//Drawing logic //Drawing logic
Raylib.BeginDrawing(); Raylib.BeginDrawing();
Raylib.ClearBackground(Theme.Background); Raylib.ClearBackground(Theme.Background);
CurrentScreen.Render(); CurrentScreen.Render();
for(var i in Popups)
i.Render();
if(hoverText != String.Empty) if(hoverText != String.Empty)
{ { //TODO: When you move outside of the window, the hover fucks up
int32 yOffset = 0; int32 yOffset = 0;
int32 xOffset = 0; int32 xOffset = 0;
//TODO: Curently just goes offscreen when it doesnt work var measure = Raylib.MeasureTextEx(Theme.FontSmall, hoverText, Theme.FontSizeSmall, 0);
var measure = Raylib.MeasureTextEx(Theme.Font, hoverText, Theme.FontSize, 0);
if(Raylib.GetMouseX() < Width/2) if(Raylib.GetMouseX() < Width/2)
xOffset = 15; xOffset = 15;
else else
@ -104,7 +194,7 @@ class App
Raylib.DrawRectangle((.)Mouse.x-3+xOffset+3, (.)Mouse.y-3+yOffset+3, (.)measure.x+6, (.)measure.y+3, .(0, 0, 0, 70)); Raylib.DrawRectangle((.)Mouse.x-3+xOffset+3, (.)Mouse.y-3+yOffset+3, (.)measure.x+6, (.)measure.y+3, .(0, 0, 0, 70));
Raylib.DrawRectangle((.)Mouse.x-3+xOffset, (.)Mouse.y-3+yOffset, (.)measure.x+6, (.)measure.y+3, .(218, 211, 176, 255)); Raylib.DrawRectangle((.)Mouse.x-3+xOffset, (.)Mouse.y-3+yOffset, (.)measure.x+6, (.)measure.y+3, .(218, 211, 176, 255));
Raylib.DrawRectangleLines((.)Mouse.x-3+xOffset, (.)Mouse.y-3+yOffset, (.)measure.x+6, (.)measure.y+3, Raylib.BLACK); Raylib.DrawRectangleLines((.)Mouse.x-3+xOffset, (.)Mouse.y-3+yOffset, (.)measure.x+6, (.)measure.y+3, Raylib.BLACK);
Raylib.DrawTextEx(Theme.Font, hoverText, Raymath.Vector2Add(Mouse, .(xOffset,yOffset)), Theme.FontSize, 0, Raylib.BLACK); Raylib.DrawTextEx(Theme.FontSmall, hoverText, Raymath.Vector2Add(Mouse, .(xOffset,yOffset)), Theme.FontSizeSmall, 0, Raylib.BLACK);
} }
Raylib.EndDrawing(); Raylib.EndDrawing();
@ -114,55 +204,14 @@ class App
} }
} }
// ///Checks wether any popup catches the hover, returns wether it has been caught
///Platform public bool HandleHoverPopups()
// {
//Topmost first
for(var i in Popups.Reversed)
{
//All loaded textures
public static Textures Textures ~ delete _;
private static Screen _CurrentScreen = null;
public static Screen CurrentScreen
{
get => _CurrentScreen;
set
{
if(_CurrentScreen != null)
delete _CurrentScreen;
_CurrentScreen = value;
} }
};
///Window width
public static int32 Width
{
get => Raylib.GetRenderWidth();
set => Raylib.SetWindowSize(value, Raylib.GetRenderHeight());
}
///Window height
public static int32 Height
{
get => Raylib.GetRenderHeight();
set => Raylib.SetWindowSize(Raylib.GetRenderWidth(), value);
}
///Mouse position
public static Vector2 Mouse
{
get => Raylib.GetMousePosition();
}
//Mouse wheel offset
public static float Wheel
{
get;
set;
}
public static GuiObject Selected
{
get;
set;
} }
public static void ForceReorder()
{
CurrentScreen.Reorder(Width);
}
} }

View file

@ -1,22 +1,22 @@
namespace TheaterGui.Controls; namespace TheaterGui.Components;
using System; using System;
using RaylibBeef; using RaylibBeef;
abstract class Button : GuiObject abstract class Button : Component
{ {
public sprite Sprite; public this(StringView pLabel, StringView pComponentName = "Button") : base(pComponentName, pLabel)
public this(StringView pName, StringView pComponentName = "Button") : base(pComponentName, pName)
{ {
Sprite = App.Textures.GetAsset("button"); Sprite = App.Textures.GetAsset("button");
Label.Append(pName);
Width = Sprite.Width; Width = Sprite.Width;
Height = Sprite.Height; Height = Sprite.Height;
} }
public abstract void ClickAction(); //What happends when its clicked
protected bool _IsHovered = false; protected bool _IsHovered = false;
public override GuiObject OnHover(int32 x, int32 y) public override Component OnHover(int32 x, int32 y)
{ {
_IsHovered = true; _IsHovered = true;
return this; return this;
@ -39,8 +39,8 @@ abstract class Button : GuiObject
public override void Render() public override void Render()
{ {
Sprite.Render(X,Y, Tint); Sprite.Render(X,Y, Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label, Theme.FontSize, 0); var measure = Raylib.MeasureTextEx(Theme.Font, Label.ToScopeCStr!(), Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label, .(X+Width/2-measure.x/2,Y+Height/2-measure.y/2), Theme.FontSize, 0, Theme.Text); Raylib.DrawTextEx(Theme.Font, Label.ToScopeCStr!(), .(X+Width/2-measure.x/2,Y+Height/2-measure.y/2), Theme.FontSize, 0, Theme.Text);
if(!Enabled) if(!Enabled)
{ {
Sprite.Render(X,Y, Theme.DisabledTint); Sprite.Render(X,Y, Theme.DisabledTint);
@ -52,6 +52,4 @@ abstract class Button : GuiObject
} }
_IsHovered = false; _IsHovered = false;
} }
public abstract void ClickAction();
} }

View file

@ -0,0 +1,49 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
abstract class Checkbox : Component
{
public sprite SpriteChecked;
public bool Checked = false;
public abstract void OnCheck(bool checkValue);
public this(StringView pName) : base("Checkbox", pName)
{
Sprite = App.Textures.GetAsset("checkbox");
SpriteChecked = App.Textures.GetAsset("checkbox_checked");
var measure = Raylib.MeasureTextEx(Theme.Font, Label.Ptr, Theme.FontSize, 0);
Width = Sprite.Width + (.)measure.x + 2;
Height = measure.y > Sprite.Height ? (.)measure.y : Sprite.Height;
}
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{
_IsHovered = true;
return this;
}
public override bool OnClick(int32 x, int32 y)
{
Checked = !Checked;
OnCheck(Checked);
return true;
}
public override void Render()
{
if(!Checked)
Sprite.Render(X,Y,Tint);
else
SpriteChecked.Render(X,Y,Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label.ToScopeCStr!(), Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label.ToScopeCStr!(), .(X + Sprite.Width + 2, (.)(Y + Sprite.Height/2 - 0.5*measure.y)), Theme.FontSize, 0, Theme.Text);
if(_IsHovered)
Sprite.Render(X,Y, HoverTint);
_IsHovered = false;
}
}

103
src/Components/Component.bf Normal file
View file

@ -0,0 +1,103 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
abstract class Component
{
public this(StringView pComponentName, StringView pLabel)
{
_ComponentType.Append(pComponentName);
Label = pLabel;
}
///
/// Fields
///
public Container Parent;
public bool Enabled = true;
public Color Tint = Theme.Tint;
public Color HoverTint = Theme.HoverTint;
public sprite Sprite;
public int32 MarginTop = 0;
public int32 MarginBottom = 0;
public int32 MarginLeft = 0;
public int32 MarginRight = 0;
///
/// Properties
///
public bool Selected
{ //When something is selected its allowed to update every frame
get;
set
{
Selected = value;
App.Selected = this;
}
}
public int32 Margin
{
//There seems to be no reasonable way to implement a getter here
set
{
MarginTop = value;
MarginBottom = value;
MarginLeft = value;
MarginRight = value;
}
}
private int32 _X = 0;
public int32 X
{
get => _X;
private set
{
_X = value;
} //You may set this, but it might fuck up layouting
};
private int32 _Y = 0;
public int32 Y
{
get => _Y;
private set
{
_Y = value;
} //You may set this, but it might fuck up layouting
};
public int32 Width
{
public get;
protected set; //Please do not
}
public int32 Height
{
public get;
protected set; //Please do not
}
[DynamicString] //What this type of component is called
private String _ComponentType = new .() ~ delete _;
[DynamicString] //What the component says on it when rendered
private String _Label = null ~ delete _;
[DynamicString] //Usually only used by the OnHover tooltip
private String _Description = null ~ delete _;
///
/// Functions
///
public abstract void Render();
public virtual bool OnClick(int32 x, int32 y) => false;
public abstract Component OnHover(int32 x, int32 y);
public virtual void WhileSelected() { }
}

View file

@ -1,20 +1,21 @@
namespace TheaterGui.Controls; namespace TheaterGui.Components;
using System; using System;
using System.Collections; using System.Collections;
class Container : GuiObject class Container : Component
{ {
private List<GuiObject> _LayoutData = new .() ~ delete _;
public List<GuiObject> Children = new .() ~ DeleteContainerAndItems!(_); //Entries => LayoutData without nulls
public this(StringView pName, StringView pComponentName = "Container") : base(pName, pComponentName) public this(StringView pName, StringView pComponentName = "Container") : base(pName, pComponentName)
{ {
} }
///Reorder all entries private List<Component> _LayoutData = new .() ~ delete _;
/// @params w The Maximum width that the container should take public List<Component> Children = new .() ~ DeleteContainerAndItems!(_);
///Recalculate the layout of all entries
public virtual void Reorder(int32 w) public virtual void Reorder(int32 w)
{ {
/* /*
@ -47,13 +48,12 @@ class Container : GuiObject
if(e is Container) if(e is Container)
{ {
((Container)e).Reorder(w); ((Container)e).Reorder(w);
for(var i in ((Container)e).Children) MoveChildrenRecursive((Container)e, x, y);
{
i.X += x + MarginLeft + e.MarginLeft;
i.Y += y + MarginTop + e.MarginTop;
}
} }
if(e is Toolbar)
e.Width = App.Width;
if(x+e.Width+e.MarginLeft > w-MarginLeft) //Change both instances of padding to 2*Padding to ensure proper padding on the leftmost side of the screen if(x+e.Width+e.MarginLeft > w-MarginLeft) //Change both instances of padding to 2*Padding to ensure proper padding on the leftmost side of the screen
{ //Automatic row break { //Automatic row break
x = 0; x = 0;
@ -61,9 +61,9 @@ class Container : GuiObject
rowHeight = 0; rowHeight = 0;
} }
e.X = x + e.MarginLeft; e.[Friend]X = x + e.MarginLeft;
x = x + e.Width + e.MarginLeft + e.MarginRight; x = x + e.Width + e.MarginLeft + e.MarginRight;
e.Y = y + e.MarginTop; e.[Friend]Y = y + e.MarginTop;
if(x > maxWidth) if(x > maxWidth)
maxWidth = x; maxWidth = x;
@ -75,15 +75,26 @@ class Container : GuiObject
Height = y + rowHeight + MarginTop + MarginBottom; Height = y + rowHeight + MarginTop + MarginBottom;
} }
private void MoveChildrenRecursive(Container c, int32 x, int32 y)
{
for(var i in c.Children)
{
i.[Friend]X += x + MarginLeft + c.MarginLeft;
i.[Friend]Y += y + MarginTop + c.MarginTop;
if(i is Container)
MoveChildrenRecursive((Container)i, x, y);
}
}
///Add a new item to the list of items ///Add a new item to the list of items
public void AddChild(GuiObject pToAdd) public void AddChild(Component pToAdd)
{ {
_LayoutData.Add(pToAdd); _LayoutData.Add(pToAdd);
Children.Add(pToAdd); Children.Add(pToAdd);
pToAdd.Parent = this; pToAdd.Parent = this;
} }
public virtual void InsertBefore(GuiObject pToAdd, GuiObject Position, bool pReorder = true) public virtual void InsertBefore(Component pToAdd, Component Position, bool pReorder = true)
{ {
var idx = _LayoutData.IndexOf(Position); var idx = _LayoutData.IndexOf(Position);
if(idx < 0) if(idx < 0)
@ -99,7 +110,7 @@ class Container : GuiObject
App.ForceReorder(); App.ForceReorder();
} }
public virtual void InsertAfter(GuiObject pToAdd, GuiObject Position, bool pReorder = true) public virtual void InsertAfter(Component pToAdd, Component Position, bool pReorder = true)
{ {
var idx = _LayoutData.IndexOf(Position); var idx = _LayoutData.IndexOf(Position);
idx++; idx++;
@ -122,8 +133,10 @@ class Container : GuiObject
public override void Render() public override void Render()
{ {
for(var i in Children) for(var i in Children)
{
i.Render(); i.Render();
} }
}
public override bool OnClick(int32 x, int32 y) public override bool OnClick(int32 x, int32 y)
{ {
@ -135,7 +148,7 @@ class Container : GuiObject
return false; return false;
} }
public override GuiObject OnHover(int32 x, int32 y) public override Component OnHover(int32 x, int32 y)
{ {
for(var e in Children) for(var e in Children)
{ {

View file

@ -1,4 +1,4 @@
namespace TheaterGui.Controls; namespace TheaterGui.Components;
using System; using System;

25
src/Components/Label.bf Normal file
View file

@ -0,0 +1,25 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
class Label : Component
{
private Vector2 _Measure;
public this(StringView pName) : base(Label, pName)
{
Label = pName;
Margin = 3;
_Measure = Raylib.MeasureTextEx(Theme.FontLarge, Label.Ptr, Theme.FontSizeLarge, 0);
Height = (.)_Measure.y;
Width = (.)_Measure.x;
}
public override Component OnHover(int32 x, int32 y) => null;
public override void Render()
{
Raylib.DrawTextEx(Theme.FontLarge, Label.Ptr, .(X,Y), Theme.FontSizeLarge, 0, Theme.Text);
Raylib.DrawLine(X,Y + (.)_Measure.y,X + (.)_Measure.x, Y + (.)_Measure.y, Theme.Text);
}
}

View file

@ -1,4 +1,4 @@
namespace TheaterGui.Controls; namespace TheaterGui.Components;
using System; using System;
using RaylibBeef; using RaylibBeef;
@ -15,8 +15,8 @@ abstract class LargeButton : Button
public override void Render() public override void Render()
{ {
Sprite.Render(X,Y, Tint); Sprite.Render(X,Y, Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label, Theme.FontSize, 0); var measure = Raylib.MeasureTextEx(Theme.Font, Label.Ptr, Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label, .(X+Width/2-measure.x/2,Y+Height/2-measure.y/2), Theme.FontSize, 0, Theme.Text); Raylib.DrawTextEx(Theme.Font, Label.Ptr, .(X+Width/2-measure.x/2,Y+Height/2-measure.y/2), Theme.FontSize, 0, Theme.Text);
if(_IsHovered) if(_IsHovered)
Sprite.Render(X,Y, HoverTint); Sprite.Render(X,Y, HoverTint);
_IsHovered = false; _IsHovered = false;

View file

@ -1,4 +1,4 @@
namespace TheaterGui.Controls; namespace TheaterGui.Components;
using System; using System;
using RaylibBeef; using RaylibBeef;
@ -27,8 +27,8 @@ abstract class NButton : Button
0, 0,
Tint); Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label, Theme.FontSize, 0); var measure = Raylib.MeasureTextEx(Theme.Font, Label.ToScopeCStr!(), Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label, .(X+Width/2-measure.x/2,Y+Height/2-measure.y/2), Theme.FontSize, 0, Theme.Text); Raylib.DrawTextEx(Theme.Font, Label.ToScopeCStr!(), .(X+Width/2-measure.x/2,Y+Height/2-measure.y/2), Theme.FontSize, 0, Theme.Text);
if(!Enabled) if(!Enabled)
{ {

View file

@ -0,0 +1,13 @@
namespace TheaterGui.Components;
class Placeholder : Component
{
public this(int32 w, int32 h) : base("Placeholder", "Placeholder")
{
Width = w;
Height = h;
}
public override Component OnHover(int32 x, int32 y) => null;
public override void Render() {}
}

View file

@ -0,0 +1,62 @@
namespace TheaterGui.Components;
using System;
abstract class RadioButton : Container
{
public int32 Checked = -1;
public this(StringView pName) : base(pName, "RadioButtons")
{
Description = pName;
}
public virtual void OnCheck(int32 value)
{
}
///Set all of the fields to be used as checkboxes
public void SetBoxes(int32 selectedIndex, params Span<StringView> args)
{
Checked = selectedIndex;
//Clear out old ones
this.[Friend]_LayoutData.ClearAndDeleteItems();
Children.Clear();
for(var i in args)
{
var toAdd = new RadioCheckbox(i);
toAdd.Description = Description;
if(@i.Index == selectedIndex)
toAdd.Checked = true;
AddChild(toAdd);
EndRow();
}
}
private void SelectButton(RadioCheckbox toSelect)
{
for(var i in Children)
if(i == toSelect)
{
((RadioCheckbox)i).Checked = true;
Checked = (.)@i.Index;
OnCheck((.)@i.Index);
}
else
((RadioCheckbox)i).Checked = false;
}
private class RadioCheckbox : Checkbox
{
public this(StringView pName) : base(pName)
{
Margin = 2;
}
public override void OnCheck(bool checkValue)
{
((RadioButton)Parent).SelectButton(this);
}
}
}

View file

@ -1,4 +1,4 @@
namespace TheaterGui.Controls; namespace TheaterGui.Components;
using TheaterGui; using TheaterGui;
using System; using System;

142
src/Components/Toolbar.bf Normal file
View file

@ -0,0 +1,142 @@
namespace TheaterGui.Components;
using System;
using System.Collections;
using RaylibBeef;
class Toolbar : Component
{
public NPatchInfo PatchInfo;
public List<ToolbarCategory> Categories = new .() ~ DeleteContainerAndItems!(_);
private ToolbarPopup _CurrentPopup = null;
private ToolbarCategory _HoveredPopup = null;
public this(StringView pLabel) : base("Toolbar", pLabel)
{
Height = 24;
Sprite = App.Textures.GetAsset("horizontal_patch");
PatchInfo =.(Sprite.SourceRect, 2,2,2,2,(.)NPatchLayout.NPATCH_NINE_PATCH);
Categories.Add(new .("File", new .("Load file",null), new .("Save file",null), new .("Recent Files",null)));
Categories.Add(new .("Edit", new .("Revert",null), new .("Really long thing goes here",null)));
Categories.Add(new .("Secret third thing", new .("Load file",null), new .("Save file",null), new .("Recent Files",null)));
}
public override void Render()
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
.(X, Y, Width, Height),
.(0,0),
0,
Tint);
int32 x = 5;
for(var i in Categories)
{
Raylib.DrawTextEx(Theme.Font, scope String(scope $"{i.Name}"), .(X + x, Y + Height/2 - i.NameMeasurement.y/2), Theme.FontSize, 0, Theme.Text);
if(i == _HoveredPopup)
{
//5 padding in every direction
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
.(X + x-5, Y, i.NameMeasurement.x+10, Height),
.(0,0),
0,
HoverTint);
}
x += (.)i.NameMeasurement.x + 10;
}
}
public override bool OnClick(int32 x, int32 y)
{
int32 hor = 10;
for(var i in Categories)
{
if(x >= hor && x <= hor + i.NameMeasurement.x)
{
var popup = new ToolbarPopup(hor-10,Height, i);
App.OpenPopup(popup);
_CurrentPopup = popup;
return true;
}
hor += (.)i.NameMeasurement.x + 10;
}
return false;
}
public override Component OnHover(int32 x, int32 y)
{
int32 hor = 10;
for(var i in Categories)
{
if(x >= hor && x <= hor + i.NameMeasurement.x)
{
_HoveredPopup = i;
if(_CurrentPopup != null && _CurrentPopup.Category != i)
{
_CurrentPopup.Category = i;
_CurrentPopup.[Friend]X = hor-10;
}
return this;
}
hor += (.)i.NameMeasurement.x + 10;
}
_HoveredPopup = null;
return null;
}
public class ToolbarCategory
{
private String _Name = null ~ delete _;
public Vector2 NameMeasurement = .(0,0);
public StringView Name
{
public get => (_Name != null) ? _Name : default;
public set
{
String.NewOrSet!(_Name, value);
NameMeasurement = Raylib.MeasureTextEx(Theme.Font, _Name, Theme.FontSize, 0);
}
}
public this(StringView pName, params Span<ToolbarItem> pItems)
{
Name = pName;
for(var i in pItems)
Items.Add(i);
}
public class ToolbarItem
{
private String _Name = null ~ delete _;
public Vector2 NameMeasurement = .(0,0);
public StringView Name
{
public get => (_Name != null) ? _Name : default;
public set
{
String.NewOrSet!(_Name, value);
NameMeasurement = Raylib.MeasureTextEx(Theme.Font, _Name, Theme.FontSize, 0);
}
}
public this(StringView pName, delegate void() pClickAction)
{
Name = pName;
ClickAction = pClickAction;
}
public delegate void() ClickAction = null ~ delete _;
}
public List<ToolbarItem> Items = new .() ~ DeleteContainerAndItems!(_);
}
}

View file

@ -0,0 +1,57 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
class ToolbarPopup : Component
{
public NPatchInfo PatchInfo;
private Toolbar.ToolbarCategory _Category = null;
public Toolbar.ToolbarCategory Category
{
get => _Category;
set
{
_Category = value;
int32 maxWidth = 0;
for(var i in _Category.Items)
if(maxWidth <= i.NameMeasurement.x)
maxWidth = (.)i.NameMeasurement.x;
Width = maxWidth + 10 + 32;
Height = (.)(5 +((_Category.NameMeasurement.y + 5) * _Category.Items.Count));
}
}
public this(int32 x, int32 y, Toolbar.ToolbarCategory category) : base("ToolbarPopup", category.Name)
{
this.[Friend]X = x;
this.[Friend]Y = y-1;
Sprite = App.Textures.GetAsset("horizontal_patch");
PatchInfo =.(Sprite.SourceRect, 2,2,2,2,(.)NPatchLayout.NPATCH_NINE_PATCH);
Category = category;
}
public override void Render()
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
.(X, Y, Width, Height),
.(0,0),
0,
Tint);
int32 y = 5;
for(var i in Category.Items)
{
Raylib.DrawTextEx(Theme.Font, scope String(i.Name), .(X + 5 + 32, Y + y), Theme.FontSize, 0, Theme.Text);
y += (.)i.NameMeasurement.y+5;
}
}
public override Component OnHover(int32 x, int32 y)
{
return default;
}
}

View file

@ -1,21 +0,0 @@
namespace TheaterGui.Controls;
using System;
class Checkbox : GuiObject
{
public sprite Sprite;
public sprite SpriteChecked;
public override GuiObject OnHover(int32 x, int32 y)
{
return null;
}
public this(StringView pName) : base("Checkbox", pName)
{
Sprite = App.Textures.GetAsset("checkbox");
SpriteChecked = App.Textures.GetAsset("checkbox_checked");
Label.Append(pName);
}
}

View file

@ -1,109 +0,0 @@
namespace TheaterGui.Controls;
using System;
using System.Collections;
using RaylibBeef;
abstract class GuiObject
{
public String ComponentName = new .() ~ delete _;
public String Name = new .() ~ delete _;
public String Label = new .() ~ delete _;
public String Description = new .() ~ delete _;
public Color Tint = Theme.Tint;
public Color HoverTint = Theme.HoverTint;
public bool Enabled = true;
public this(StringView pComponentName, StringView pName)
{
ComponentName.Append(pComponentName);
Name.Append(pName);
}
private Container _Parent = null;
public Container Parent
{
get => _Parent;
set
{
OnParentChange(value);
_Parent = value;
}
};
///Margin stuff
public int32 MarginTop = 0;
public int32 MarginBottom = 0;
public int32 MarginLeft = 0;
public int32 MarginRight = 0;
public int32 Margin
{
//There seems to be no reasonable way to implement a getter here
set
{
MarginTop = value;
MarginBottom = value;
MarginLeft = value;
MarginRight = value;
}
};
public int32 X
{
get;
set;
} = 0;
public int32 Y
{
get;
set;
} = 0;
public int32 Width
{
public get;
protected set;
};
public int32 Height
{
public get;
protected set;
};
public bool Selected
{
get;
set
{
Selected = value;
App.Selected = this;
}
}
public Vector2 SizeOf
{
get => .(Width + MarginLeft + MarginRight, Height + MarginTop + MarginBottom);
}
///Returns true if the onclick has been handled
public virtual bool OnClick(int32 x, int32 y)
{
return false;
}
public abstract GuiObject OnHover(int32 x, int32 y);
//Called every frame if the object is selected
public virtual void WhileSelected()
{
}
public virtual void OnParentChange(Container pNewParent)
{
}
public virtual void Render()
{
}
}

View file

@ -1,28 +0,0 @@
namespace TheaterGui.Controls;
using System;
using RaylibBeef;
class Label : GuiObject
{
private Vector2 _Measure;
public this(StringView pName) : base(Label, pName)
{
Label.Append(pName);
Margin = 3;
_Measure = Raylib.MeasureTextEx(Theme.Font, Label, Theme.FontSize, 0);
Height = (.)_Measure.y;
Width = (.)_Measure.x;
}
public override GuiObject OnHover(int32 x, int32 y)
{
return null;
}
public override void Render()
{
Raylib.DrawTextEx(Theme.Font, Label, .(X,Y), Theme.FontSize, 0, Theme.Text);
Raylib.DrawLine(X,Y + (.)_Measure.y,X + (.)_Measure.x, Y + (.)_Measure.y, Theme.Text);
}
}

View file

@ -1,12 +0,0 @@
namespace TheaterGui.Controls;
class Placeholder : GuiObject
{
public this(int32 w, int32 h) : base("Placeholder", "Placeholder")
{
Width = w;
Height = h;
}
public override GuiObject OnHover(int32 x, int32 y) => null;
}

View file

@ -1,15 +1,34 @@
namespace TheaterGui;
namespace RaylibBeef namespace RaylibBeef
{ {
extension Rectangle extension Rectangle
{ {
public bool Overlaps(int32 x, int32 y) public bool Overlaps(int32 x, int32 y)
{ {
if(x >= this.x && x <= this.x + this.width) if (x >= this.x && x <= this.x + this.width)
if(y >= this.y && y <= this.y + this.height) if (y >= this.y && y <= this.y + this.height)
return true; return true;
return false; return false;
} }
} }
} }
namespace System
{
using System.Reflection;
[AttributeUsage(.Field)]
struct DynamicStringAttribute : Attribute, IOnFieldInit
{
[Comptime]
public void OnFieldInit(FieldInfo fieldInfo, Self* prev)
{
Compiler.EmitTypeBody(fieldInfo.DeclaringType, scope $"""
public StringView {fieldInfo.Name.Substring(1)}
{{
public get => {fieldInfo.Name};
public set => String.NewOrSet!({fieldInfo.Name}, value);
}};
""");
}
}
}

View file

@ -34,7 +34,7 @@ class ButtonGenerator : Compiler.Generator
public this() : base("{name}") public this() : base("{name}")
{{ {{
{!enabled ? "Enabled = false;" : ""} {!enabled ? "Enabled = false;" : ""}
Description.Append("{description}"); Description = "{description}";
}} }}
//What happens when the button is clicked //What happens when the button is clicked

View file

@ -0,0 +1,48 @@
namespace TheaterGui.Generators;
using System;
class CheckboxGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Generate Checkbox"
public override void InitUI()
{
AddEdit("name", "Checkbox Name", "");
AddCheckbox("enabled", "Enabled", true);
AddEdit("description", "Description", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
var enabled = bool.Parse(mParams["enabled"]);
var description = mParams["description"];
outFileName.Append(scope $"TG{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Controls;
class TG{name} : Checkbox
{{
public this() : base("{name}")
{{
{!enabled ? "Enabled = false;" : ""}
Description = "{description}";
}}
//What happens when the button is clicked
public override void OnCheck(bool checkValue)
{{
}}
}}
""");
}
}

View file

@ -35,7 +35,7 @@ class IconButtonGenerator : Compiler.Generator
{{ {{
Icon = .Icon_Folder; //Use this to set the icon sprite Icon = .Icon_Folder; //Use this to set the icon sprite
{!enabled ? "Enabled = false;" : ""} {!enabled ? "Enabled = false;" : ""}
Description.Append("{description}"); Description = "{description}";
}} }}
//What happens when the button is clicked //What happens when the button is clicked

View file

@ -34,7 +34,7 @@ class LargeButtonGenerator : Compiler.Generator
public this() : base("{name}") public this() : base("{name}")
{{ {{
{!enabled ? "Enabled = false;" : ""} {!enabled ? "Enabled = false;" : ""}
Description.Append("{description}"); Description = "{description}";
}} }}
//What happens when the button is clicked //What happens when the button is clicked

View file

@ -34,7 +34,7 @@ class NButtonGenerator : Compiler.Generator
public this() : base("{name}") public this() : base("{name}")
{{ {{
{!enabled ? "Enabled = false;" : ""} {!enabled ? "Enabled = false;" : ""}
Description.Append("{description}"); Description = "{description}";
}} }}
//What happens when the button is clicked //What happens when the button is clicked

View file

@ -0,0 +1,48 @@
namespace TheaterGui.Generators;
using System;
class RadioButtonGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Generate Radio Button"
public override void InitUI()
{
AddEdit("name", "ComponentName Name", "");
AddCheckbox("enabled", "Enabled", true);
AddEdit("description", "Description", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
var enabled = bool.Parse(mParams["enabled"]);
var description = mParams["description"];
outFileName.Append(scope $"TG{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Controls;
class TG{name} : RadioButton
{{
public this() : base("{name}")
{{
{!enabled ? "Enabled = false;" : ""}
Description = "{description}";
//Use SetBoxes(int, params StringView) to set the elements of the radio buttons
}}
//React to the check event, or use the Checked field to get the value directly
public override void OnCheck(int32 value)
{{
}}
}}
""");
}
}

21
src/PlatformLayer.bf Normal file
View file

@ -0,0 +1,21 @@
namespace TheaterGui;
//Which backend does theatergui use
#define BACKEND_RAYLIB
#if BACKEND_RAYLIB
using RaylibBeef;
#endif
class PlatformLayer
{
///
public static void DrawSprite()
{
}
}
//TODO: Actually implement this

View file

@ -17,6 +17,8 @@ class Textures
LoadAsset(Theme.Texture_LargeButton, "large_button"); LoadAsset(Theme.Texture_LargeButton, "large_button");
LoadAsset(Theme.Texture_Checkbox, "checkbox"); LoadAsset(Theme.Texture_Checkbox, "checkbox");
LoadAsset(Theme.Texture_Checkbox_Checked, "checkbox_checked"); LoadAsset(Theme.Texture_Checkbox_Checked, "checkbox_checked");
LoadAsset(Theme.Horizontal_Patch, "horizontal_patch");
LoadAsset(Theme.Texture_TheaterIcon, "theater_icon"); LoadAsset(Theme.Texture_TheaterIcon, "theater_icon");
LoadAsset(Theme.Texture_FolderIcon, "folder_icon"); LoadAsset(Theme.Texture_FolderIcon, "folder_icon");

View file

@ -6,7 +6,12 @@ using RaylibBeef;
public class Theme public class Theme
{ {
public static Font Font ~ Raylib.UnloadFont(_); public static Font Font ~ Raylib.UnloadFont(_);
public static Font FontSmall ~ Raylib.UnloadFont(_);
public static Font FontLarge ~ Raylib.UnloadFont(_);
public static int32 FontSize = 16; public static int32 FontSize = 16;
public static int32 FontSizeLarge = 20;
public static int32 FontSizeSmall = 12;
public static Color Background = .(45, 45, 49, 255); public static Color Background = .(45, 45, 49, 255);
public static Color Tint = .(153, 36, 72, 255); public static Color Tint = .(153, 36, 72, 255);
@ -29,5 +34,7 @@ public class Theme
public static uint8[?] Texture_Button = Compiler.ReadBinary("assets/button.png"); public static uint8[?] Texture_Button = Compiler.ReadBinary("assets/button.png");
public static uint8[?] Texture_Checkbox = Compiler.ReadBinary("assets/checkbox.png"); public static uint8[?] Texture_Checkbox = Compiler.ReadBinary("assets/checkbox.png");
public static uint8[?] Texture_Checkbox_Checked = Compiler.ReadBinary("assets/checkbox_checked.png"); public static uint8[?] Texture_Checkbox_Checked = Compiler.ReadBinary("assets/checkbox_checked.png");
public static uint8[?] Horizontal_Patch = Compiler.ReadBinary("assets/horizontal_patch.png");
} }