Compare commits

...
Sign in to create a new pull request.

6 commits
main ... dev

74 changed files with 1940 additions and 2707 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
# ---> Beef # ---> Beef
**/build **/build
**/recovery **/recovery
**/deps
BeefSpace_User.toml BeefSpace_User.toml

View file

@ -1,5 +1,5 @@
FileVersion = 1 FileVersion = 1
Dependencies = {corlib = "*", corlib = "*", raylib-beef = "*"} Dependencies = {corlib = "*", corlib = "*", Bofa = "*", raylib-beef = "*"}
[Project] [Project]
Name = "TheaterGui" Name = "TheaterGui"

8
BeefSpace.toml Normal file
View file

@ -0,0 +1,8 @@
FileVersion = 1
Projects = {TheaterGui = {Path = "."}, examples = {Path = "examples"}, Bofa = {Path = "deps/Bofa"}, raylib-beef = {Path = "deps/Raylib/raylib-beef"}, raylib-backend = {Path = "raylib-backend"}}
[Workspace]
StartupProject = "examples"
[Configs.Debug.Win64]
ConfigSelections = {raylib-beef = {Config = "StaticDebug"}}

View file

@ -1,5 +1,5 @@
FileVersion = 1 FileVersion = 1
Dependencies = {corlib = "*", TheaterGui = "*"} Dependencies = {corlib = "*", TheaterGui = "*", raylib-backend = "*"}
[Project] [Project]
Name = "examples" Name = "examples"

View file

@ -1,15 +0,0 @@
namespace examples;
using TheaterGui.Components;
class Box : Container
{
public this() : base("Box")
{
//Add ui items here via AddChild() and terminate the row via EndRow()
Margin = 5;
AddChild(new Checkb());
AddChild(new Radios());
}
}

View file

@ -1,45 +0,0 @@
namespace examples;
using TheaterGui.Components;
class StandartButton : Button
{
public this() : base("Basic Button")
{
Margin = 5;
}
//What happens when the button is clicked
public override void ClickAction()
{
}
}
class LButton : LargeButton
{
public this() : base("Large Button")
{
Margin = 5;
}
//What happens when the button is clicked
public override void ClickAction()
{
}
}
class CButton : NButton
{
public this() : base(400, 50, "Custom sized button")
{
Margin = 5;
}
//What happens when the button is clicked
public override void ClickAction()
{
}
}

View file

@ -1,18 +0,0 @@
namespace examples;
using TheaterGui.Components;
class Checkb : Checkbox
{
public this() : base("Check me!")
{
Checked = true;
Description = "Beef is awesome";
}
//What happens when the button is clicked
public override void OnCheck(bool checkValue)
{
}
}

View file

@ -1,37 +0,0 @@
namespace examples;
using TheaterGui.Components;
class ExampleToolbar : Toolbar
{
public this() : base("Toolbar")
{
AddToolbarCategories(
new .("Help",
new .("Version", new => PrintVersion)
),
new .("About",
new .("TheaterGui", new => PrintAbout),
new .("Booklordofthedings", new => PrintAbout)
)
);
}
public void PrintVersion()
{
#if !BF_PLATFORM_WASM
System.Console.WriteLine("1.0");
#endif
}
public void PrintAbout()
{
#if !BF_PLATFORM_WASM
System.Console.WriteLine("""
TheaterGui by Booklordofthedings
A simple easy to use gui library for tools
""");
#endif
}
}

View file

@ -1,25 +0,0 @@
namespace examples;
using TheaterGui.Components;
class HorSlider : HSlider
{
public this() : base("HorSlider")
{
//Use the thing above to set the length of the bar
Min = 0;
Max = 100;
Value = 50;
ReactToValueChange = true; //This indicates wether OnValueChange gets actually called
Description = "A default horizontal slider";
MarginTop = 50;
}
public override void OnValueChange(float newVal)
{
#if !BF_PLATFORM_WASM
System.Console.WriteLine(Value);
#endif
}
}

View file

@ -1,48 +0,0 @@
namespace examples;
using TheaterGui.Components;
class MainScreen : Screen
{
public this() : base("MainScreen")
{
//Add ui items here via AddChild() and terminate the row via EndRow()
AddChild(new ExampleToolbar());
AddChild(new Label("Changing the width allows you to see these buttons reordered"));
EndRow();
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
AddChild(new OpenButton());
EndRow();
AddChild(new StandartButton());
AddChild(new LButton());
AddChild(new CButton());
EndRow();
AddChild(new HorSlider());
AddChild(new VertSlider());
EndRow();
AddChild(new Box());
AddChild(new Box());
}
}

View file

@ -1,21 +0,0 @@
namespace examples;
using TheaterGui.Components;
class OpenButton : IconButton
{
public this() : base("OpenButton")
{
Icon = .Icon_Folder; //Use this to set the icon sprite
Margin = 5;
MarginTop = 40;
MarginBottom = 40;
Description = "Open a file dialogue";
}
//What happens when the button is clicked
public override void ClickAction()
{
}
}

View file

@ -1,11 +1,74 @@
namespace examples; namespace examples;
using System;
using TheaterGui; using TheaterGui;
using TheaterGui.Core;
using TheaterGui.Components;
using TheaterGui.Components.Internal;
class Program class Program
{ {
public static void Main() public static void Main()
{ {
App.Run<MainScreen>("Example Gui",1920,1080); scope App()
..SetName("TheaterGui Application")
..SetStartingSize(1280, 720)
..SetPlatformLayer(new raylib_backend.PlatformRaylib())
.Run(new MainScreen());
} }
} }
class MainScreen : Screen
{
public this()
{
Toolbar toAdd = new .();
toAdd.AddEntry("File", "Open File", new (rt) => {Console.WriteLine("Open file");});
toAdd.AddEntry("Edit", "Go back", new (rt) => {Console.WriteLine("Go back");});
toAdd.AddEntry("Edit", "Open File", new (rt) => {Console.WriteLine("Open File");});
toAdd.AddEntry("Window", "Open File", new (rt) => {Console.WriteLine("Open File");});
AddChild("Toolbar", toAdd);
AddChild("Button", new Button(new (val) => {Console.WriteLine("Hellau :)");}));
AddChild("dd", new Dropdown("One", "Two", "Three"));
AddChild("Label", new Label("LMAO"));
AddChild("CBox", new Checkbox("Box"));
RadioButton r = new .();
r.Label.Add(new .("One"));
r.Label.Add(new .("Two"));
r.Label.Add(new .("Three"));
AddChild("radio", r);
AddChild("hslider", new HSlider());
AddChild("vslider", new VSlider());
AddChild("lb", new Label("""
Labels can also be used to
contain some larger text since this also
works fine
and without any
issues
"""));
Layout = new (val, width) =>
{
val.Component("Toolbar");
val.Component("Label", 10);
val.Linebreak();
val.Component("Button");
val.Component("CBox", 5);
val.Linebreak();
val.Component("radio", 5);
val.Component("hslider");
val.Component("vslider");
val.Component("lb");
val.Linebreak();
val.Component("dd", 5);
};
}
}

View file

@ -1,19 +0,0 @@
namespace examples;
using TheaterGui.Components;
class Radios : RadioButton
{
public this() : base("Radios")
{
Description = "";
//Use SetBoxes(int, params StringView) to set the elements of the radio buttons
SetBoxes(3, "Windows", "Linux", "Mac", "Web");
}
//React to the check event, or use the Checked field to get the value directly
public override void OnCheck(int32 value)
{
}
}

View file

@ -1,20 +0,0 @@
namespace examples;
using TheaterGui.Components;
class VertSlider : VSlider
{
public this() : base("VertSlider")
{
//Use the thing above to set the length of the bar
Min = 0;
Max = 100;
Value = 50;
ReactToValueChange = true; //This indicates wether OnValueChange gets actually called
}
public override void OnValueChange(float newVal)
{
}
}

20
pkkg Normal file
View file

@ -0,0 +1,20 @@
#This is the pkkg template file
l Name TheaterGui
t Desc A simple retained mode gui library
l Url https://code.booklordofthe.dev/Theater/TheaterGui
a Authors
o Book
l Name Booklordofthedings
l Url https://Boooklordofthe.dev
a Dependencies
o Bofa
l Name Bofa
l Url https://code.booklordofthe.dev/Booklordofthedings/Bofa.git
l Tag 1.1.0
o Raylib
l Name Raylib
l Url https://code.booklordofthe.dev/Extern/raylib-beef.git

View file

@ -0,0 +1,7 @@
FileVersion = 1
Dependencies = {corlib = "*", raylib-beef = "*", TheaterGui = "*"}
[Project]
Name = "raylib-backend"
TargetType = "BeefLib"
StartupObject = "raylib_backend.Program"

View file

@ -0,0 +1,45 @@
namespace raylib_backend;
using TheaterGui;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
using RaylibBeef;
class PlatformRaylib : IPlatformLayer
{
private RenderTexture2D _ScreenTexture;
private TextureManager _TextureManager;
private Color _BackgroundColor = Raylib.WHITE;
private Dictionary<String, Font> _LoadedFonts = new .() ~ DeleteDictionaryAndKeys!(_);
private static uint8[?] _Texture_WindowIcon = Compiler.ReadBinary("../assets/64.png");
public void Initialize(StringView pWindowTitle, int32 pWidth, int32 pHeight)
{
Raylib.SetConfigFlags((.)(ConfigFlags.FLAG_WINDOW_RESIZABLE | ConfigFlags.FLAG_WINDOW_HIGHDPI));
String title = scope .(pWindowTitle);
Raylib.InitWindow(pWidth, pHeight, title);
Raylib.SetExitKey(0);
Raylib.SetWindowMinSize(640, 360);
Raylib.SetTargetFPS(40);
var icon = Raylib.LoadImageFromMemory(".png", (.)&_Texture_WindowIcon, _Texture_WindowIcon.Count);
Raylib.SetWindowIcon(icon);
Raylib.UnloadImage(icon);
_ScreenTexture = Raylib.LoadRenderTexture(Raylib.GetRenderWidth(), Raylib.GetRenderHeight());
_TextureManager = new .();
}
public void Deinitialize()
{
for(var i in _LoadedFonts)
Raylib.UnloadFont(i.value);
Raylib.UnloadRenderTexture(_ScreenTexture);
delete _TextureManager;
}
}

View file

@ -0,0 +1,31 @@
namespace raylib_backend;
using RaylibBeef;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
extension PlatformRaylib
{
///Converts a TGColor into a raylib compatible color
private Color Convert(color pColor)
{
return .(pColor.Red, pColor.Green, pColor.Blue, pColor.Alpha);
}
///Convert a TGRect into a raylib compatible rectangle
private Rectangle Convert(rect pRect)
{
return .(pRect.x, pRect.y, pRect.width, pRect.height);
}
private float[2] Convert(Vector2 pVector)
{
return .(pVector.x, pVector.y);
}
private Vector2 Convert(int32[2] pValues)
{
return .(pValues[0], pValues[1]);
}
}

View file

@ -0,0 +1,134 @@
namespace raylib_backend;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
using RaylibBeef;
extension PlatformRaylib
{
public void StartDrawing()
{
if(Raylib.GetMouseDelta().x + Raylib.GetMouseDelta().y != 0)
{
if(targetFps != 40)
{
Raylib.SetTargetFPS(40);
targetFps = 40;
}
}
else
{
if(targetFps != 20)
{
Raylib.SetTargetFPS(20);
targetFps = 20;
}
}
Raylib.BeginDrawing();
Raylib.ClearBackground(_BackgroundColor);
Raylib.BeginTextureMode(_ScreenTexture);
Raylib.ClearBackground(_BackgroundColor);
}
public void StopDrawing()
{
Raylib.EndTextureMode();
Raylib.DrawTexturePro(
_ScreenTexture.texture,
.(0, 0, _ScreenTexture.texture.width, -_ScreenTexture.texture.height),
.(0, 0, Raylib.GetScreenWidth(), Raylib.GetScreenHeight()),
.(0, 0),
0,
Raylib.WHITE
);
Raylib.EndDrawing();
}
private int32 targetFps = 40;
public void RedrawLastFrame()
{
if(Raylib.GetMouseDelta().x + Raylib.GetMouseDelta().y != 0)
{
if(targetFps != 40)
{
Raylib.SetTargetFPS(40);
targetFps = 40;
}
}
else
{
if(targetFps != 20)
{
Raylib.SetTargetFPS(20);
targetFps = 20;
}
}
Raylib.BeginDrawing();
Raylib.ClearBackground(_BackgroundColor);
Raylib.DrawTexturePro(
_ScreenTexture.texture,
.(0, 0, _ScreenTexture.texture.width, -_ScreenTexture.texture.height),
.(0, 0, Raylib.GetScreenWidth(), Raylib.GetScreenHeight()),
.(0, 0),
0,
Raylib.WHITE
);
#if DEBUG
Raylib.DrawRectangle(0,0,5,5, Raylib.PINK);
#endif
Raylib.EndDrawing();
}
public void ChangeBackgroundColor(color pTint)
{
_BackgroundColor = Convert(pTint);
}
public void LoadTexture(Span<uint8> pData, StringView pName)
{
_TextureManager.[Friend]LoadAsset(pData, pName);
}
public void DrawTexture(StringView pName, rect pTarget, color pTints)
{
if(_TextureManager.GetAsset(pName) case .Ok(let val))
{
Raylib.DrawTexturePro(
*val.Source,
val.SourceRect,
Convert(pTarget),
.(0,0), 0,
Convert(pTints)
);
}
else
Raylib.DrawRectangle(pTarget.x, pTarget.y, pTarget.width, pTarget.height, Raylib.PINK);
}
public int32[2] MeasureTextureSize(StringView pName)
{
if(_TextureManager.GetAsset(pName) case .Ok(let val))
{
return .(val.Width, val.Height);
}
return .(0,0);
}
public void DrawColorRect(rect pTarget, color pTint)
{
Raylib.DrawRectangleRec(Convert(pTarget), Convert(pTint));
}
public void DrawColorRectOutlined(rect pTarget, color pTint, color pOutlineTint)
{
Raylib.DrawRectangleRec(Convert(pTarget), Convert(pTint));
Raylib.DrawRectangleLinesEx(Convert(pTarget), 1, Convert(pOutlineTint));
}
}

View file

@ -0,0 +1,31 @@
namespace raylib_backend;
using RaylibBeef;
extension PlatformRaylib
{
public bool ShouldClose() => Raylib.WindowShouldClose();
public float[2] MousePosition() => Convert(Raylib.GetMousePosition());
public float[2] MouseDelta() => Convert(Raylib.GetMouseDelta());
public bool MouseLeftClick() => Raylib.IsMouseButtonDown(0);
public bool MouseRightClick() => Raylib.IsMouseButtonDown(1);
public float MouseWheel() => Raylib.GetMouseWheelMoveV().y;
public int32[2] ScreenSize()
{
int32[2] ScreenSize = .();
ScreenSize[0] = Raylib.GetScreenWidth();
ScreenSize[1] = Raylib.GetScreenHeight();
//The size of the render texture is also automatically changed to always fit
if(ScreenSize[0] != _ScreenTexture.texture.width || ScreenSize[1] != _ScreenTexture.texture.height)
{
Raylib.UnloadRenderTexture(_ScreenTexture);
_ScreenTexture = Raylib.LoadRenderTexture(ScreenSize[0], ScreenSize[1]);
}
return ScreenSize;
}
}

View file

@ -0,0 +1,42 @@
namespace raylib_backend;
using TheaterGui.Core.Structs;
using System;
using RaylibBeef;
extension PlatformRaylib
{
public void LoadFont(Span<uint8> pData, StringView pName, uint8 pFontSize)
{
var font = Raylib.LoadFontFromMemory(".ttf", (.)pData.Ptr, (.)pData.Length, pFontSize, null, 256);
if(_LoadedFonts.ContainsKeyAlt<StringView>(pName))
{
Raylib.UnloadFont(_LoadedFonts[scope .(pName)]);
_LoadedFonts[scope .(pName)] = font;
}
else
_LoadedFonts.Add(new .(pName), font);
}
public void DrawText(StringView pText, StringView pFont, int32[2] pPosition, color pTint)
{
if(_LoadedFonts.GetValue(scope .(pFont)) case .Ok(let val))
Raylib.DrawTextEx(val, scope String(pText), Convert(pPosition), val.baseSize, 1, Convert(pTint));
else
Raylib.DrawText(scope String(pText), pPosition[0], pPosition[1], 10, Raylib.PINK);
}
public int32[2] MeasureTextSize(StringView pText, StringView pFont)
{
if(_LoadedFonts.GetValue(scope .(pFont)) case .Ok(let val))
{
var res = Raylib.MeasureTextEx(val, scope String(pText), val.baseSize, 1);
return .((.)res.x, (.)res.y);
}
else
return .(Raylib.MeasureText(scope String(pText), 10), 10);
}
}

View file

@ -1,40 +1,14 @@
namespace TheaterGui; namespace raylib_backend;
using System; using System;
using System.Collections; using System.Collections;
using RaylibBeef; using RaylibBeef;
class Textures class TextureManager
{ {
private List<Node> _Containers = new .() ~ DeleteContainerAndItems!(_); private List<Node> _Containers = new .() ~ DeleteContainerAndItems!(_);
private List<RenderTexture> _Textures = new .() ~ delete _; private List<RenderTexture> _Textures = new .() ~ delete _;
public this()
{
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);
LoadAsset(Theme.Texture_Button, "button");
LoadAsset(Theme.Texture_NButton, "n_button");
LoadAsset(Theme.Texture_SquareButton, "square_button");
LoadAsset(Theme.Texture_LargeButton, "large_button");
LoadAsset(Theme.Texture_Checkbox, "checkbox");
LoadAsset(Theme.Texture_Checkbox_Checked, "checkbox_checked");
LoadAsset(Theme.Horizontal_Patch, "horizontal_patch");
LoadAsset(Theme.Dropdown, "dropdown");
LoadAsset(Theme.Texture_TheaterIcon, "theater_icon");
LoadAsset(Theme.Texture_FolderIcon, "folder_icon");
sprite.Icon_Folder = GetAsset("folder_icon");
sprite.Icon_Theater = GetAsset("theater_icon");
}
private Dictionary<String, sprite> _TileMap = new .() ~ DeleteDictionaryAndKeys!(_); private Dictionary<String, sprite> _TileMap = new .() ~ DeleteDictionaryAndKeys!(_);
///Attempt to get a existing asset by StringView ///Attempt to get a existing asset by StringView
@ -164,10 +138,6 @@ struct sprite
public int32 Height = 0; public int32 Height = 0;
public float Rotation = 0; public float Rotation = 0;
public static sprite Icon_Folder;
public static sprite Icon_Theater;
public void Render(float x, float y, Color color = Raylib.WHITE) public void Render(float x, float y, Color color = Raylib.WHITE)
{ {
Raylib.DrawTexturePro(*Source, SourceRect, Rectangle(x + Width / 2, y + Height / 2, Width, Height), Vector2(Width / 2, Height / 2), Rotation, color); Raylib.DrawTexturePro(*Source, SourceRect, Rectangle(x + Width / 2, y + Height / 2, Width, Height), Vector2(Width / 2, Height / 2), Rotation, color);

View file

@ -1,263 +1,53 @@
namespace TheaterGui; namespace TheaterGui;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using TheaterGui.Components;
using System; using System;
using System.Collections;
using RaylibBeef;
using internal TheaterGui;
using TheaterGui.Components;
class App class App
{ {
/* private String _Name = new .("TheaterGui Program") ~ delete _;
There are two ways to run TheaterGui. private int32[2] _StartingSize = .(1280, 720);
Either full, by using the Run function or by manually calling the functions in Run. private IPlatformLayer _Platform = null ~ delete _;
This way you can integrate TheaterGui into other raylib applications of your choice. private colorScheme _Scheme = .DefaultScheme;
private theme _Theme = .();
Components are supposed to be created via inheritance for each individual component, except some very simple exeptions (Labels, Placeholders) public Result<void> Run(Screen pScreen)
*/
#region Fields
//For hover items
private static List<Popup> _Popups = new .() ~ DeleteContainerAndItems!(_);
private static Vector2 _OldPos = .(0, 0);
private static double _MovTime = 0;
private static String _HoverText = new .() ~ delete _;
private static Vector2 _OldSize = .(0, 0);
public static Textures Textures ~ delete _; //All loaded textures
public static Component Selected; //Currently selected component
public static bool AllowReorder = true;
private static Screen _CurrentScreen = null ~ delete _;
public static Screen CurrentScreen
{ {
get => _CurrentScreen; if(_Platform == null)
set
{
if (_CurrentScreen != null)
delete _CurrentScreen;
_CurrentScreen = value;
_CurrentScreen.Reorder(Width);
}
};
///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;
}
private static bool _ShouldClose;
public static bool ShouldClose
{
public get
{
return _ShouldClose || Raylib.WindowShouldClose();
}
public set => _ShouldClose = value;
}
#endregion
///Run TheaterGui (Use for fully independent programs)
///@param pName Name of the window
///@param pWidth Width of the window
///@param pHeight Height of the window
///@param pStartScreen The starting Screen of the application
public static void Run<T>(StringView pName, int32 pWidth, int32 pHeight) where T : Screen
{
InitializeRaylib(pName, pWidth, pHeight);
InitializeGui<T>();
#if BF_PLATFORM_WASM
WebAssembly.emscripten_set_main_loop(=> ProcessDrawFrame, 40, 1);
///Afaik the infinite loop means, that the deinitialization functionality will not be called,
///but that should be fine for wasm since it fully cleans up when you exit the page.
///If we dont do it calls the deinitialization directly afterwards
#else
while (!App.ShouldClose)
ProcessDrawFrame();
#endif
DeinitializeGui();
DeinitializeRaylib();
}
///Initializes the raylib enviroment
///@param pName Name of the window
///@param pWidth Width of the window
///@param pHeight Height of the window
public static void InitializeRaylib(StringView pName, int32 pWidth, int32 pHeight)
{
//Default raylib settings
Raylib.SetConfigFlags((.)(ConfigFlags.FLAG_WINDOW_RESIZABLE | ConfigFlags.FLAG_WINDOW_HIGHDPI));
Raylib.InitWindow(pWidth, pHeight, scope String(pName));
Raylib.SetExitKey(0);
#if !BF_PLATFORM_WASM ///Otherwise raylib sleeps and delays browser responsiveness
Raylib.SetTargetFPS(40); //Should be a resonable framerate for gui apps
#endif
Raylib.SetWindowMinSize(640, 360);
///Set the default window icon
var icon = Raylib.LoadImageFromMemory(".png", (.)&Theme.Texture_WindowIcon, Theme.Texture_WindowIcon.Count);
Raylib.SetWindowIcon(icon);
Raylib.UnloadImage(icon);
}
///Initialize the gui enviroment
public static void InitializeGui<T>() where T : Screen
{
Textures = new .();
CurrentScreen = new T();
}
///Cleanup the gui enviroment
public static void DeinitializeGui() => void();
///Cleanup the raylib enviroment
public static void DeinitializeRaylib() => void();
///To be called by emscripten
private static void ProcessDrawFrame()
{
ProcessFrame();
Raylib.BeginDrawing();
Raylib.ClearBackground(Theme.Background);
DrawFrame();
Raylib.EndDrawing();
}
///Process the logic for a single frame of the application
public static void ProcessFrame()
{
Vector2 newSize = .(Width, Height);
if (_OldSize != newSize)
{
CurrentScreen.Reorder((.)newSize.x);
_OldSize = newSize;
}
Input.HandleScrolling(Raylib.GetMouseWheelMove());
if (Raylib.IsMouseButtonPressed(0))
Input.HandleLeftClick();
else if (Raylib.IsMouseButtonDown(0))
Input.HandleLeftDown();
else if (Raylib.IsMouseButtonPressed(1))
Input.HandleRightClick();
else
Input.HandleHover();
if(Selected != null)
Selected.WhileSelected();
}
///Draw the current state of the application
public static void DrawFrame()
{
//Drawing logic
CurrentScreen.Render((.)Wheel);
for (var i in _Popups)
i.Render((.)Wheel);
if (!_HoverText.IsEmpty)
{
RenderHoverText();
_HoverText.Clear();
}
}
private static void RenderHoverText()
{
int32 yOffset = 0;
int32 xOffset = 0;
var measure = Raylib.MeasureTextEx(Theme.FontSmall, _HoverText, Theme.FontSizeSmall, 0);
if (Mouse.x < Width / 2) //Render the textbox to the right of the cursor
xOffset = 15;
else //Render the textbox to the left of the cursor
xOffset = (.)(-1 * measure.x) - 15;
if (Mouse.y < Height / 2) //Render the textbox at the y location of the cursor
yOffset = 0;
else //Render the textbox offset so, that the textbox y ends at the y location of the cursor
yOffset = (.)(-1 * measure.y);
//BoxShadow
//Box
//BoxBorder
//BoxContent
Raylib.DrawRectangle((.)Mouse.x - 3 + xOffset + 3, (.)Mouse.y - 3 + yOffset + 3, (.)measure.x + 6, (.)measure.y + 3, Color(0, 0, 0, 70));
Raylib.DrawRectangle((.)Mouse.x - 3 + xOffset, (.)Mouse.y - 3 + yOffset, (.)measure.x + 6, (.)measure.y + 3, Color(218, 211, 176, 255));
Raylib.DrawRectangleLines((.)Mouse.x - 3 + xOffset, (.)Mouse.y - 3 + yOffset, (.)measure.x + 6, (.)measure.y + 3, Raylib.BLACK);
Raylib.DrawTextEx(Theme.FontSmall, _HoverText, Raymath.Vector2Add(Mouse, Vector2(xOffset, yOffset)), Theme.FontSizeSmall, 0, Raylib.BLACK);
}
public static void ForceReorder()
{
if (AllowReorder && CurrentScreen != null)
CurrentScreen.Reorder(Width);
}
public static void OpenPopup(Popup popup)
{
_Popups.Add(popup);
}
public static Result<Popup> GetTopmostPopup()
{
if(_Popups.Count < 1)
return .Err; return .Err;
return .Ok(_Popups[^1]);
} var runtime = new TGRuntime();
} runtime.[Friend]Launch(_Platform, pScreen, _Name, _StartingSize[0], _StartingSize[1], _Scheme, _Theme);
delete runtime;
return .Ok;
}
public void SetName(StringView pName)
{
_Name.Clear();
_Name.Append(pName);
}
public void SetStartingSize(int32 pWidth, int32 pHeight)
{
if(pWidth > 0 || pHeight > 0)
return;
_StartingSize[0] = pWidth;
_StartingSize[1] = pHeight;
}
public void SetPlatformLayer(IPlatformLayer pPlatformLayer)
{
delete _Platform;
_Platform = pPlatformLayer;
}
public void SetColorScheme(colorScheme pScheme) => _Scheme = pScheme;
public void SetUITheme(theme pTheme) => _Theme = pTheme;
}

View file

@ -1,55 +1,68 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using RaylibBeef;
abstract class Button : Component class Button : Component
{ {
public this(StringView pLabel, StringView pComponentName = "Button") : base(pComponentName, pLabel) private bool _Hovered = false;
private bool _AlreadyHovered = false;
public String Label ~ delete _;
delegate void(TGRuntime rt) Action ~ delete _;
public this(delegate void(TGRuntime rt) pAction, StringView pLabel = "Button")
{ {
Sprite = App.Textures.GetAsset("button"); Label = new .(pLabel);
Width = Sprite.Width; Action = pAction;
Height = Sprite.Height;
} }
public abstract void ClickAction(); //What happends when its clicked public override void Update(TheaterGui.Core.TGRuntime rt)
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{ {
_IsHovered = true; if(_Hovered != _AlreadyHovered)
return this; {
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = false;
} }
public override bool OnClick(int32 x, int32 y) public override void Draw(TGRuntime rt, rect rect)
{ {
if(Rectangle(X, Y, Width, Height).Overlaps(x, y)) rt.Platform.DrawTexture("Button", rect, rt.Scheme.PrimaryColor);
if(_Hovered)
rt.Platform.DrawTexture("Button", rect, rt.Scheme.HoverColor);
var tsize = rt.Platform.MeasureTextSize(Label, "Font_Normal");
rt.Platform.DrawText(Label, "Font_Normal",
.((.)(rect.x + 0.5*rect.width - tsize[0]*0.5),
(.)(rect.y + 0.5*rect.height - tsize[1]*0.5))
,rt.Scheme.TextColor);
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{ {
if(Enabled) return rt.Platform.MeasureTextureSize("Button");
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{ {
ClickAction(); _AlreadyHovered = true;
return true; return true;
} }
return false;
} public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
return false; {
Action.Invoke(rt);
return true;
} }
public override void Render(int32 soy) public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList)
{ {
Sprite.Render(X,Y + soy, Tint); pList.Add((.)(new String(scope $"Click {Label}"), new (rt) => {this.Action.Invoke(rt);}));
var measure = Raylib.MeasureTextEx(Theme.Font, Label.ToScopeCStr!(), Theme.FontSize, 0); return true;
Raylib.DrawTextEx(Theme.Font, Label.ToScopeCStr!(), Vector2(X+Width/2-measure.x/2,Y+Height/2-measure.y/2 + soy), Theme.FontSize, 0, Theme.Text);
if(!Enabled)
{
Sprite.Render(X,Y + soy, Theme.DisabledTint);
}
else
{
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
}
_IsHovered = false;
} }
} }

View file

@ -1,49 +1,73 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using RaylibBeef; using System.Collections;
abstract class Checkbox : Component class Checkbox : Component
{ {
public sprite SpriteChecked; private bool _Hovered = false;
private bool _AlreadyHovered = false;
public String Label ~ delete _;
public bool Checked = false; public bool Checked = false;
public abstract void OnCheck(bool checkValue); public this(StringView pName)
public this(StringView pName) : base("Checkbox", pName)
{ {
Sprite = App.Textures.GetAsset("checkbox"); Label = new .(pName);
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 void Update(TGRuntime rt)
public override Component OnHover(int32 x, int32 y)
{ {
_IsHovered = true; if(_Hovered != _AlreadyHovered)
return this; {
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = false;
} }
public override bool OnClick(int32 x, int32 y) public override void Draw(TGRuntime rt, rect rect)
{ {
Checked = !Checked; var textureSize = rt.Platform.MeasureTextureSize("Checkbox");
OnCheck(Checked); if(!Checked)
rt.Platform.DrawTexture("Checkbox", .() {x = rect.x, y = rect.y, width = textureSize[0], height = textureSize[1]}, rt.Scheme.PrimaryColor);
else
rt.Platform.DrawTexture("Checkbox_Checked", .() {x = rect.x, y = rect.y, width = textureSize[0], height = textureSize[1]}, rt.Scheme.PrimaryColor);
if(_Hovered)
rt.Platform.DrawTexture("Checkbox", .() {x = rect.x, y = rect.y, width = textureSize[0], height = textureSize[1]}, rt.Scheme.HoverColor);
var tsize = rt.Platform.MeasureTextSize(Label, "Font_Normal");
rt.Platform.DrawText(Label, "Font_Normal",
.(rect.x + textureSize[0] + 5
,(.)(rect.y + 0.5*rect.height - tsize[1]*0.5))
,rt.Scheme.TextColor);
}
public override int32[2] Resize(TGRuntime rt, int32 width)
{
var textureSize = rt.Platform.MeasureTextureSize("Checkbox");
var tsize = rt.Platform.MeasureTextSize(Label, "Font_Normal");
textureSize[0] += 5 + tsize[0];
if(tsize[1] > textureSize[1])
textureSize[1] = tsize[1];
return textureSize;
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
_AlreadyHovered = true;
return true; return true;
} }
public override void Render(int32 soy) public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{ {
if(!Checked) Checked = !Checked;
Sprite.Render(X,Y + soy,Tint); return true;
else
SpriteChecked.Render(X,Y + soy,Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label.ToScopeCStr!(), Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label.ToScopeCStr!(), Vector2(X + Sprite.Width + 2, (.)(Y + Sprite.Height/2 - 0.5*measure.y) + soy), Theme.FontSize, 0, Theme.Text);
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
_IsHovered = false;
} }
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => false;
} }

View file

@ -1,107 +1,17 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using RaylibBeef; using System.Collections;
abstract class Component abstract class Component
{ {
public this(StringView pComponentName, StringView pLabel) public abstract void Update(TGRuntime rt);
{ public abstract void Draw(TGRuntime rt, rect rect);
ComponentType = pComponentName; public abstract int32[2] Resize(TGRuntime rt, int32 width);
Label = pLabel; public abstract bool OnHover(TGRuntime rt, int32[2] mPos);
} public abstract bool OnLeftClick(TGRuntime rt, int32[2] mPos);
public abstract bool OnScroll(TGRuntime rt, float pOffset);
/// public abstract bool OnRightClick(TGRuntime rt, int32[2] mPos, List<(String, delegate void(TGRuntime))> pList);
/// 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(int32 soy);
public virtual bool OnClick(int32 x, int32 y) => false;
public virtual void OnRightClick(int32 x, int32 y, Toolbar.ToolbarCategory items) {}
public virtual bool HandleScrollEvent(float scroll, int32 x, int32 y) {return false;};
public virtual bool OnDown(int32 x, int32 y) => false;
public abstract Component OnHover(int32 x, int32 y);
public virtual void WhileSelected() { }
} }

View file

@ -1,195 +1,123 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using System.Collections; using System.Collections;
class Container : Component abstract class Container : Component
{
public this(StringView pName, StringView pComponentName = "Container") : base(pName, pComponentName)
{ {
protected Dictionary<String, Component> _Children = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
protected List<rect> _Drawings = new .() ~ delete _;
public delegate void(Layout, int32 width) Layout = null ~ delete _;
public override void Update(TGRuntime rt)
{
for(var i in _Children)
i.value.Update(rt);
} }
private List<Component> _LayoutData = new .() ~ delete _; public override void Draw(TGRuntime rt, rect rect)
public List<Component> Children = new .() ~ DeleteContainerAndItems!(_);
///Recalculate the layout of all entries
public virtual void Reorder(int32 w)
{ {
/* for(var i in _Drawings)
List<Entries> {Entry, Entry, Entry, RB, Entry, Entry, RB, Entry, RB}
null is a rowbreak while non null isnt a rowbreak
Algorithm
if the current entry is a rowbreak, reset the cursor to x = 0, y = y + height
try to fit the current entry after the cursor
if it fits add it,
if it doesnt fit break
*/
int32 maxWidth = 0; //The highest width line
int32 rowHeight = 0; //How high the current row is
int32 x = 0;
int32 y = 0;
for(var e in _LayoutData)
{ {
if(e == null) if(_Children.ContainsKeyAlt<StringView>(i.name))
{ //Manual row break _Children[scope .(i.name)].Draw(rt, i + rect);
x = 0;
y = y + rowHeight;
rowHeight = 0;
continue;
}
if(e is Container)
{
((Container)e).Reorder(w);
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
x = 0;
y = y + rowHeight;
rowHeight = 0;
}
MoveChildrenRecursive((Container)e, x, y);
}
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
{ //Automatic row break
x = 0;
y = y + rowHeight;
rowHeight = 0;
}
e.[Friend]X = x + e.MarginLeft;
x = x + e.Width + e.MarginLeft + e.MarginRight;
e.[Friend]Y = y + e.MarginTop;
if(x > maxWidth)
maxWidth = x;
if(rowHeight < e.Height+e.MarginTop+e.MarginBottom)
rowHeight = e.Height+e.MarginTop+e.MarginBottom;
}
Width = maxWidth + MarginLeft + MarginRight;
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 public override int32[2] Resize(TGRuntime rt, int32 width)
public void AddChild(Component pToAdd)
{ {
_LayoutData.Add(pToAdd); _Drawings.Clear();
Children.Add(pToAdd);
pToAdd.Parent = this; Layout l = scope .();
l.[Friend]_RT = rt;
l.[Friend]_Children = _Children;
l.[Friend]_Drawings = _Drawings;
l.[Friend]_MaxWidth = width;
Layout.Invoke(l, width);
int32[2] max = .(0,0);
for(var i in _Drawings)
{
if(i.width+i.x > max[0])
max[0] = i.width+i.x;
if(i.height+i.y > max[1])
max[1] = i.height+i.y;
}
return max;
} }
public virtual void InsertBefore(Component pToAdd, Component Position, bool pReorder = true) public override bool OnHover(TGRuntime rt, int32[2] mPos)
{ {
var idx = _LayoutData.IndexOf(Position); for(var i in _Drawings)
if(idx < 0)
{ {
delete pToAdd; //Avoid memory leaks if(_Children.GetValue(scope .(i.name)) case .Ok(let component))
return; {
if(mPos[0] >= i.x && mPos[0] <= i.x + i.width
&& mPos[1] >= i.y && mPos[1] <= i.y + i.height)
{
if(component.OnHover(rt, .(mPos[0]-i.x, mPos[1]-i.y)))
return true;
else
return false;
}
} }
_LayoutData.Insert(idx, pToAdd);
Children.Add(pToAdd);
pToAdd.Parent = this;
if(pReorder)
App.ForceReorder();
} }
public virtual void InsertAfter(Component pToAdd, Component Position, bool pReorder = true) return false;
{
var idx = _LayoutData.IndexOf(Position);
idx++;
if(idx < 0)
{
delete pToAdd; //Avoid memory leaks
return;
}
_LayoutData.Insert(idx, pToAdd);
Children.Add(pToAdd);
pToAdd.Parent = this;
if(pReorder)
App.ForceReorder();
} }
///Forcefully terminates the current row public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList)
public void EndRow() => _LayoutData.Add(null);
public override void Render(int32 soy)
{ {
for(var i in Children) for(var i in _Drawings)
i.Render(soy); {
if(_Children.GetValue(scope .(i.name)) case .Ok(let component))
{
if(mPos[0] >= i.x && mPos[0] <= i.x + i.width
&& mPos[1] >= i.y && mPos[1] <= i.y + i.height)
{
component.OnRightClick(rt, .(mPos[0]-i.x, mPos[1]-i.y), pList);
return true;
}
} }
public override bool OnClick(int32 x, int32 y)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, y))
return e.OnClick(x, y);
} }
return false; return false;
} }
public override bool OnDown(int32 x, int32 y)
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{ {
for(var e in Children) for(var i in _Drawings)
{ {
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, y)) if(_Children.GetValue(scope .(i.name)) case .Ok(let component))
return e.OnDown(x, y); {
if(mPos[0] >= i.x && mPos[0] <= i.x + i.width
&& mPos[1] >= i.y && mPos[1] <= i.y + i.height)
{
if(component.OnLeftClick(rt, .(mPos[0]-i.x, mPos[1]-i.y)))
return true;
else
return true;
} }
}
}
return false; return false;
} }
public override Component OnHover(int32 x, int32 y) public override bool OnScroll(TGRuntime rt, float pOffset)
{ {
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, y))
return e.OnHover(x, y);
}
return null;
}
public override bool HandleScrollEvent(float scroll, int32 x, int32 y)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, y))
return e.HandleScrollEvent(scroll, x, y);
}
return false; return false;
} }
public override void OnRightClick(int32 x, int32 y, Toolbar.ToolbarCategory items) public Result<void> AddChild(StringView pName, Component pComponent)
{ {
for(var e in Children) if(_Children.ContainsKeyAlt<StringView>(pName))
{ return .Err;
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, y)) _Children.Add(new .(pName), pComponent);
e.OnRightClick(x, y, items); return .Ok;
}
} }
} }

View file

@ -1,57 +1,71 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Components.Internal;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using System.Collections; using System.Collections;
using RaylibBeef;
abstract class Dropdown : Component class Dropdown : Component
{ {
public List<String> Entries = new .() ~ DeleteContainerAndItems!(_); private bool _Hovered = false;
public uint32 SelectedEntry = 0; private bool _AlreadyHovered = false;
private int32[2] _Pos = .();
public this() : base("Dropdown", "Label") public List<String> Options = new .() ~ DeleteContainerAndItems!(_);
public int32 Selected = 0;
public this(params Span<StringView> args)
{ {
Margin = 20; for(var i in args)
Sprite = App.Textures.GetAsset("dropdown"); Options.Add(new .(i));
Width = Sprite.Width;
Height = Sprite.Height;
} }
public override void Render(int32 soy) public override void Update(TheaterGui.Core.TGRuntime rt)
{ {
Sprite.Render(X,Y + soy, Tint); if(_Hovered != _AlreadyHovered)
var measure = Raylib.MeasureTextEx(Theme.Font, Entries[(.)SelectedEntry], Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Entries[(.)SelectedEntry], Vector2(X+Width/2-measure.x/2,Y+Height/2-measure.y/2 + soy), Theme.FontSize, 0, Theme.Text);
if(!Enabled)
{ {
Sprite.Render(X,Y + soy, Theme.DisabledTint); _Hovered = _AlreadyHovered;
rt.Dirty();
} }
else _AlreadyHovered = false;
{
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
}
_IsHovered = false;
} }
public override bool OnClick(int32 x, int32 y) public override void Draw(TGRuntime rt, rect rect)
{ {
var list = new List<StringView>(Entries.Count); _Pos = .(rect.x, rect.y + rect.height);
defer delete list; rt.Platform.DrawTexture("Dropdown", rect, rt.Scheme.PrimaryColor);
for(var i in Entries) if(_Hovered)
list.Add(i); rt.Platform.DrawTexture("Dropdown", rect, rt.Scheme.HoverColor);
var popup = new DropdownPopup(X, Y + Height, Width, list);
popup.Owner = this; var tsize = rt.Platform.MeasureTextSize(Options[Selected], "Font_Normal");
App.OpenPopup(popup); rt.Platform.DrawText(Options[Selected], "Font_Normal",
.((.)(rect.x + 0.5*rect.width - tsize[0]*0.5),
(.)(rect.y + 0.5*rect.height - tsize[1]*0.5))
,rt.Scheme.TextColor);
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
return rt.Platform.MeasureTextureSize("Dropdown");
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
_AlreadyHovered = true;
return true; return true;
} }
public abstract void ClickAction(String pNewVal); public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{ {
_IsHovered = true; var p = new DropdownPopup(Options) {Position = _Pos};
return this; p.[Friend]_Click = new [&] (val) => {
this.Selected = val;
};
rt.OpenPopup(p, "Dropdown");
return true;
} }
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => false;
} }

View file

@ -1,109 +0,0 @@
namespace TheaterGui.Components;
using System;
using System.Collections;
using RaylibBeef;
class DropdownPopup : Popup
{
public NPatchInfo PatchInfo;
public int32 SelectedItem = -1;
public Dropdown Owner = null;
public List<String> Options = new .() ~ DeleteContainerAndItems!(_);
public this(int32 x, int32 y, int32 w, params Span<StringView> options) : base("DropdownPopup", "DropdownPopup")
{
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);
for(var i in options)
Options.Add(new .(i));
Width = w;
Height = ((.)Options.Count * (Theme.FontSize+10))-5;
}
public this(int32 x, int32 y, int32 w, List<StringView> options) : base("DropdownPopup", "DropdownPopup")
{
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);
for(var i in options)
Options.Add(new .(i));
Width = w;
Height = ((.)Options.Count * (Theme.FontSize+10))-5;
}
public override void Render(int32 soy)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
Tint);
int32 y = 5;
for(var i in Options)
{
Raylib.DrawTextEx(Theme.Font, scope String(i), Vector2(X + 5, Y + y + soy), Theme.FontSize, 0, Theme.Text);
if(@i.Index == SelectedItem)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + y - 4 + soy, Width, Theme.FontSize+10),
Vector2(0,0),
0,
HoverTint);
}
y += (.)Theme.FontSize+5;
}
}
public override bool OnClick(int32 x, int32 y)
{
if(!(x >= X && x <= X + Width))
return false;
int32 pos = 5 + Y;
for(var i in Options)
{
if(y >= pos && y <= pos + (.)Theme.FontSize+5)
{
if(Owner != null)
{
Owner.SelectedEntry = (.)@i.Index;
Owner.ClickAction(i);
}
return false;
}
pos += (.)Theme.FontSize+5;
}
return false;
}
public override Component OnHover(int32 x, int32 y)
{
int32 pos = 5 + Y;
for(var i in Options)
{
if(y >= pos && y <= pos + (.)Theme.FontSize+5)
{
SelectedItem = (.)@i.Index;
return this;
}
pos += (.)Theme.FontSize+5;
}
SelectedItem = -1;
return null;
}
public override void OnClose()
{
}
}

View file

@ -1,11 +1,75 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
abstract class HSlider : NHSlider class HSlider : Component
{
public this(StringView pLabel) : base(32 * 6, pLabel, "HSlider")
{ {
private bool _Hovered = false;
private bool _AlreadyHovered = false;
public float Value = 0;
public this()
{
} }
public override void Update(TheaterGui.Core.TGRuntime rt)
{
if (_Hovered != _AlreadyHovered)
{
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = false;
}
public override void Draw(TGRuntime rt, rect rect)
{
var ts = rt.Platform.MeasureTextureSize("Checkbox");
rt.Platform.DrawColorRect(.() { x = rect.x + ts[0], width = rect.width-2*ts[0], y = rect.y + ts[1] / 2 - 4, height = 8 }, rt.Scheme.AcceptColor);
rt.Platform.DrawTexture("Checkbox", .() { x = (.)(rect.x + ts[0] + Value * 10*ts[0]-ts[0]/2), y = rect.y, width = ts[0], height = ts[1] }, rt.Scheme.PrimaryColor);
if (_Hovered)
rt.Platform.DrawTexture("Checkbox", .() { x = (.)(rect.x + ts[0] + Value * 10*ts[0]-ts[0]/2), y = rect.y, width = ts[0], height = ts[1] }, rt.Scheme.HoverColor);
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
var ts = rt.Platform.MeasureTextureSize("Checkbox");
return .(ts[0] * 12, ts[1]);
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
_AlreadyHovered = true;
if(rt.Platform.MouseLeftClick())
{
float oldVal = Value;
OnLeftClick(rt, mPos);
if(Value != oldVal)
rt.Dirty();
}
return true;
}
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
var ts = rt.Platform.MeasureTextureSize("Checkbox");
if(mPos[0] <= ts[0])
Value = 0;
else if(mPos[0] >= ts[0] * 11)
Value = 1;
else
{
float ofst = mPos[0] - ts[0];
Value = Math.Clamp(ofst / (ts[0]*10)
,0,1);
}
return true;
}
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => false;
} }

View file

@ -1,30 +0,0 @@
namespace TheaterGui.Components;
using System;
abstract class IconButton : Button
{
public sprite Icon = App.Textures.GetAsset("folder_icon");
public this(StringView pName) : base(pName, "SquareButton")
{
Sprite = App.Textures.GetAsset("square_button");
Width = Sprite.Width;
Height = Sprite.Height;
}
public override void Render(int32 soy)
{
Sprite.Render(X,Y + soy, Tint);
Icon.Render(X+Width/2-Icon.Width/2+2,Y+Height/2-Icon.Height/2+2 + soy, .(0,0,0,125));
Icon.Render(X+Width/2-Icon.Width/2,Y+Height/2-Icon.Height/2 + soy);
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
_IsHovered = false;
}
}

View file

@ -1,72 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
class InputField : Component
{
public NPatchInfo PatchInfo;
public String InputContent = new .() ~ delete _;
String text = new .() ~ delete _;
public this(StringView pLabel) : base("InputField", pLabel)
{
Label = "Test Data";
Sprite = App.Textures.GetAsset("n_button");
Width = 128 * 2;
Height = 32;
Margin = 10;
Tint = Theme.Background;
PatchInfo = .(Sprite.SourceRect, 7, 7, 7, 7, (.)NPatchLayout.NPATCH_NINE_PATCH);
}
public override void Render(int32 soy)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0, 0),
0,
Tint);
if (_IsHovered || App.Selected == this)
Raylib.DrawTextureNPatch(*Sprite.Source, PatchInfo, Rectangle(X, Y + soy, Width, Height), Vector2(0, 0), 0, Theme.Tint.SetAlpha(120));
_IsHovered = false;
var measure = Raylib.MeasureTextEx(Theme.Font, text, Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, text, Vector2(X + 7, Y + Height / 2 - measure.y / 2 + soy), Theme.FontSize, 0, Theme.Text);
}
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{
_IsHovered = true;
return this;
}
public override bool OnClick(int32 x, int32 y)
{
App.Selected = this;
return true;
}
public override void WhileSelected()
{
int key = Raylib.GetCharPressed();
while (key > 0)
{
// NOTE: Only allow keys in range [32..125]
if ((key >= 32) && (key <= 125) && (text.Length < 100))
text.Append((char8)key);
key = Raylib.GetCharPressed(); // Check next character in the queue
}
if (Raylib.IsKeyPressed((.)KeyboardKey.KEY_BACKSPACE))
text.RemoveFromEnd(1);
if(Raylib.IsKeyPressed((.)KeyboardKey.KEY_ESCAPE))
App.Selected = null;
}
}

View file

@ -0,0 +1,90 @@
namespace TheaterGui.Components.Internal;
using TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
//The clickable text for things like the right click menu and the top toolbar
class ClickableText : Component
{
private bool _Hovered = false;
private bool _AlreadyHovered = false;
public String Label ~ delete _;
public delegate void(TGRuntime rt) Action ~ delete _;
public int32[2] Padding = .(50, 4);
public Alignment TextAlign = .LeftAligned;
public enum Alignment
{
LeftAligned,
Center,
RightAligned
}
public this(delegate void(TGRuntime rt) pToCall, StringView pName = "Button")
{
Action = pToCall;
Label = new .(pName);
}
public override void Update(TheaterGui.Core.TGRuntime rt)
{
if(_Hovered != _AlreadyHovered)
{
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = false;
}
public override void Draw(TGRuntime rt, rect rect)
{
if(_Hovered)
rt.Platform.DrawColorRect(rect , rt.Scheme.HoverColor);
var tsize = rt.Platform.MeasureTextSize(Label, "Font_Normal");
int32 p = 0;
switch(TextAlign)
{
case .LeftAligned:
p = rect.x;
p += 2;
case .Center:
p = (.)(rect.x + 0.5*rect.width - tsize[0]*0.5);
case .RightAligned:
p = (.)(rect.x + rect.width - tsize[0]);
p -= 2;
}
rt.Platform.DrawText(Label, "Font_Normal",
.(p,
(.)(rect.y + 0.5*rect.height - tsize[1]*0.5))
,rt.Scheme.TextColor);
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
var ts = rt.Platform.MeasureTextSize(Label, "Font_Normal");
return .(ts[0] + Padding[0], ts[1] + Padding[1]);
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
_AlreadyHovered = true;
return true;
}
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
Action.Invoke(rt);
return true;
}
public override bool OnScroll(TGRuntime rt, float pOffset)
{
return false;
}
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => true;
}

View file

@ -0,0 +1,117 @@
namespace TheaterGui.Components.Internal;
using TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
class DropdownPopup : Popup
{
private int32 _Hovered = -1;
private int32 _AlreadyHovered = -1;
private int32[2] _Size = .();
private delegate void(int32) _Click ~ delete _;
public List<String> Options = new .() ~ DeleteContainerAndItems!(_);
public int32 Selected = 0;
public this(List<String> args) : this()
{
for(var i in args)
Options.Add(new .(i));
}
public override void Update(TGRuntime rt)
{
if(_Hovered != _AlreadyHovered)
{
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = -1;
}
public override void Draw(TGRuntime rt, rect rect)
{
rt.Platform.DrawColorRectOutlined(
.() {x = Position[0], y = Position[1], width = _Size[0], height = _Size[1]}
,rt.Scheme.PrimaryColor, rt.Scheme.AcceptColor);
int32 offsetY = 2;
for(var i in Options)
{
rt.Platform.DrawText(i, "Font_Normal", .(Position[0] + 2, Position[1] + offsetY), rt.Scheme.TextColor);
var size = rt.Platform.MeasureTextSize(i, "Font_Normal");
if(_Hovered == (.)@i.Index)
{
rt.Platform.DrawColorRect(
.() {x = Position[0], y = Position[1] + offsetY - 2, width = _Size[0], height = 4 + size[1]},
rt.Scheme.HoverColor);
}
offsetY += 4 + size[1];
}
}
public override int32[2] Resize(TGRuntime rt, int32 width)
{
int32[2] size = .();
for(var i in Options)
{
size[0] = rt.Platform.MeasureTextureSize("Dropdown")[0];
var tsize = rt.Platform.MeasureTextSize(i, "Font_Normal");
size[1] += tsize[1] + 4; //4 is the padding
}
_Size = size;
return size;
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
if(mPos[0] >= Position[0] && mPos[0] < Position[0] + _Size[0])
{
if(mPos[1] >= Position[1] && mPos[1] < Position[1] + _Size[1])
{
int32[2] offset = .(mPos[0] - Position[0], mPos[1] - Position[1]);
int32 offsetY = 0;
for(var i in Options)
{
var tsize = rt.Platform.MeasureTextSize(i, "Font_Normal");
if(offset[1] >= offsetY && offset[1] < offsetY + tsize[1] + 4)
{
_AlreadyHovered = (.)@i.Index;
return true;
}
offsetY += tsize[1] + 4;
}
}
}
return false;
}
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
if(mPos[0] >= Position[0] && mPos[0] < Position[0] + _Size[0])
{
if(mPos[1] >= Position[1] && mPos[1] < Position[1] + _Size[1])
{
int32[2] offset = .(mPos[0] - Position[0], mPos[1] - Position[1]);
int32 offsetY = 0;
for(var i in Options)
{
var tsize = rt.Platform.MeasureTextSize(i, "Font_Normal");
if(offset[1] >= offsetY && offset[1] < offsetY + tsize[1] + 4)
{
_Click.Invoke((.)@i.Index);
return false;
}
offsetY += tsize[1] + 4;
}
}
}
return false;
}
}

View file

@ -1,25 +1,40 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using RaylibBeef; using System.Collections;
class Label : Component class Label : Component
{ {
private Vector2 _Measure; public String Label = null ~ delete _;
public this(StringView pName) : base(Label, pName)
public this(StringView pLabel)
{ {
Label = pName; Label = new .(pLabel);
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 Update(TGRuntime rt)
public override void Render(int32 soy)
{ {
Raylib.DrawTextEx(Theme.FontLarge, Label.Ptr, Vector2(X,Y + soy), Theme.FontSizeLarge, 0, Theme.Text);
Raylib.DrawLine(X,Y + (.)_Measure.y + soy,X + (.)_Measure.x, Y + (.)_Measure.y + soy, Theme.Text);
} }
public override void Draw(TGRuntime rt, rect rect)
{
rt.Platform.DrawText(Label, "Font_Normal", .(rect.x, rect.y), rt.Scheme.TextColor);
}
public override int32[2] Resize(TGRuntime rt, int32 width)
{
var s = rt.Platform.MeasureTextSize(Label, "Font_Normal");
return .(s[0], s[1]);
}
public override bool OnHover(TGRuntime rt, int32[2] mPos) => false;
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos) => false;
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, List<(String, delegate void(TGRuntime))> pList) => true;
} }

View file

@ -1,24 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
abstract class LargeButton : Button
{
public this(StringView pName) : base(pName, "LargeButton")
{
Sprite = App.Textures.GetAsset("large_button");
Width = Sprite.Width;
Height = Sprite.Height;
}
public override void Render(int32 soy)
{
Sprite.Render(X,Y + soy, Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label.Ptr, Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label.Ptr, Vector2(X+Width/2-measure.x/2,Y+Height/2-measure.y/2 + soy), Theme.FontSize, 0, Theme.Text);
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
_IsHovered = false;
}
}

View file

@ -1,56 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
abstract class NButton : Button
{
public NPatchInfo PatchInfo;
public this(int32 w, int32 h, StringView pName) : base(pName, "NButton")
{
Sprite = App.Textures.GetAsset("n_button");
Width = w;
Height = h;
PatchInfo =.(Sprite.SourceRect, 7,7,7,7,(.)NPatchLayout.NPATCH_NINE_PATCH);
}
public override void Render(int32 soy)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
Tint);
var measure = Raylib.MeasureTextEx(Theme.Font, Label.ToScopeCStr!(), Theme.FontSize, 0);
Raylib.DrawTextEx(Theme.Font, Label.ToScopeCStr!(), Vector2(X+Width/2-measure.x/2,Y+Height/2-measure.y/2 + soy), Theme.FontSize, 0, Theme.Text);
if(!Enabled)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
Theme.DisabledTint);
}
else
{
if(_IsHovered)
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
HoverTint);
}
_IsHovered = false;
}
}

View file

@ -1,78 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
//This slider may be any size
abstract class NHSlider : Component
{
private bool _CurrentlyDragged = false; //Used to stop accidental hovers
public int32 BarWidth = 0;
public float Percentage = 0;
public float Min = 0;
public float Max = 0;
public float Value
{
get => Min + Percentage * (Max-Min);
set => Percentage = (value - Min)/(Max-Min); //This should be mathematically fine, no clue if its actually fine though
}
public bool ReactToValueChange = false; //Calling on value change too frequently might not be good so we clear here
public virtual void OnValueChange(float newVal)
{
}
public this(int32 width, StringView pLabel, StringView pTypename = "NHSlider") : base(pTypename, pLabel)
{
Sprite = App.Textures.GetAsset("checkbox");
Width = width + Sprite.Width;
BarWidth = width;
Height = Sprite.Height;
}
public override void Render(int32 soy)
{
Raylib.DrawRectangle(X + Sprite.Width/2, Y + Sprite.Height/2 - 4 + soy, BarWidth, 8, Raylib.BLACK);
Sprite.Render((.)(X+ Percentage * BarWidth), Y + soy, Tint);
if(_IsHovered)
Sprite.Render((.)(X +Percentage * BarWidth), Y + soy, HoverTint);
_IsHovered = false;
if(!Raylib.IsMouseButtonDown(0))
_CurrentlyDragged = false;
}
public override bool OnClick(int32 x, int32 y)
{
_CurrentlyDragged = true;
return true;
}
public override bool OnDown(int32 x, int32 y)
{
if(_CurrentlyDragged)
{
float np = 0;
np = x - X;
np = Math.Clamp(np, Sprite.Width/2, Width - Sprite.Width/2); //Not rly clamp
np -= Sprite.Width/2;
np = np / (BarWidth); //I fucking hate that this works
float oldp = Percentage;
Percentage = np;
if(ReactToValueChange && oldp != Percentage)
OnValueChange(Min + np * (Max-Min));
return true;
}
return false;
}
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{
_IsHovered = true;
return this;
}
}

View file

@ -1,78 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
//This slider may be any size
abstract class NVSlider : Component
{
private bool _CurrentlyDragged = false; //Used to stop accidental hovers
public int32 BarHeight = 0;
public float Percentage = 0;
public float Min = 0;
public float Max = 0;
public float Value
{
get => Min + Percentage * (Max-Min);
set => Percentage = (value - Min)/(Max-Min); //This should be mathematically fine, no clue if its actually fine though
}
public bool ReactToValueChange = false; //Calling on value change too frequently might not be good so we clear here
public virtual void OnValueChange(float newVal)
{
}
public this(int32 height, StringView pLabel, StringView pTypeName = "NVSlider") : base(pTypeName, pLabel)
{
Sprite = App.Textures.GetAsset("checkbox");
Width = Sprite.Width;
BarHeight = height;
Height = Sprite.Height + height;
}
public override void Render(int32 soy)
{
Raylib.DrawRectangle(X + Sprite.Width/2 - 4, Y + Sprite.Height/2 + soy, 8, BarHeight, Raylib.BLACK);
Sprite.Render(X, Y + soy + Percentage * BarHeight, Tint);
if(_IsHovered)
Sprite.Render(X, Y + soy + Percentage * BarHeight, HoverTint);
_IsHovered = false;
if(!Raylib.IsMouseButtonDown(0))
_CurrentlyDragged = false;
}
public override bool OnClick(int32 x, int32 y)
{
_CurrentlyDragged = true;
return true;
}
public override bool OnDown(int32 x, int32 y)
{
if(_CurrentlyDragged)
{
float np = 0;
np = y - Y;
np = Math.Clamp(np, Sprite.Height/2, Height - Sprite.Height/2); //Not rly clamp
np -= Sprite.Height/2;
np = np / (BarHeight); //I fucking hate that this works
float oldp = Percentage;
Percentage = np;
if(ReactToValueChange && oldp != Percentage)
OnValueChange(Min + np * (Max-Min));
return true;
}
return false;
}
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{
_IsHovered = true;
return this;
}
}

View file

@ -1,13 +0,0 @@
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(int32 soy) {}
}

View file

@ -1,12 +1,154 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core.Structs;
using TheaterGui.Core;
abstract class Popup : Component using System;
{ using System.Collections;
public this(System.StringView pComponentName, System.StringView pLabel) : base(pComponentName, pLabel)
{
///Unscrollable popup window that has a list of available items, clicking on anything but popup windows with the same tag will close it
class Popup : Container
{
private rect _Container = .();
private List<(String, delegate void(TGRuntime))> _ToDelete = null;
public int32[2] Position = .();
public int32 Padding = 0;
public bool DoHeightCheck = true;
public this(int32[2] pPosition)
{
Position = pPosition;
} }
///When something force closes the popup public ~this()
public abstract void OnClose(); {
if(_ToDelete != null)
{
for(var i in _ToDelete)
{
delete i.0;
delete i.1;
}
delete _ToDelete;
}
}
public override void Draw(TheaterGui.Core.TGRuntime rt, rect rect)
{
_Container = rect;
rect offset = .();
int32[2] whLimit = .(0, 0);
for(var i in _Drawings)
{
if(whLimit[0] < i.x + i.width)
whLimit[0] = i.x + i.width;
if(whLimit[1] < i.y + i.height)
whLimit[1] = i.y + i.height;
}
offset.width = whLimit[0];
offset.height = whLimit[1];
if(Position[0] < rt.ScreenSize[0]/2)
offset.x = Position[0];
else
offset.x = Position[0]-offset.width;
if(Position[1] < rt.ScreenSize[1]/2)
offset.y = Position[1];
else
offset.y = Position[1]-offset.height;
rt.Platform.DrawColorRectOutlined(
.() {x = offset.x - Padding, y = offset.y - Padding, width = offset.width + 2*Padding, height = offset.height + 2*Padding}
, rt.Scheme.PrimaryColor, rt.Scheme.AcceptColor);
rt.Platform.DrawColorRect(
.() {x = offset.x - Padding, y = offset.y - Padding, width = offset.width + 2*Padding+4, height = offset.height + 2*Padding+4}
, .(0x00, 0x00, 0x00, 40));
base.Draw(rt, offset);
}
public override bool OnHover(TheaterGui.Core.TGRuntime rt, int32[2] mPos)
{
rect offset = .();
int32[2] whLimit = .(0, 0);
for(var i in _Drawings)
{
if(whLimit[0] < i.x + i.width)
whLimit[0] = i.x + i.width;
if(whLimit[1] < i.y + i.height)
whLimit[1] = i.y + i.height;
}
offset.width = whLimit[0];
offset.height = whLimit[1];
if(Position[0] < rt.ScreenSize[0]/2)
offset.x = Position[0];
else
offset.x = Position[0]-offset.width;
if(Position[1] < rt.ScreenSize[1]/2)
offset.y = Position[1];
else
offset.y = Position[1]-offset.height;
return base.OnHover(rt, .(mPos[0] - offset.x, mPos[1] - offset.y));
}
public override bool OnLeftClick(TheaterGui.Core.TGRuntime rt, int32[2] mPos)
{
rect offset = .();
int32[2] whLimit = .(0, 0);
for(var i in _Drawings)
{
if(whLimit[0] < i.x + i.width)
whLimit[0] = i.x + i.width;
if(whLimit[1] < i.y + i.height)
whLimit[1] = i.y + i.height;
}
offset.width = whLimit[0];
offset.height = whLimit[1];
if(Position[0] < rt.ScreenSize[0]/2)
offset.x = Position[0];
else
offset.x = Position[0]-offset.width;
if(Position[1] < rt.ScreenSize[1]/2)
offset.y = Position[1];
else
offset.y = Position[1]-offset.height;
return base.OnLeftClick(rt, .(mPos[0] - offset.x, mPos[1] - offset.y));
}
public override bool OnScroll(TheaterGui.Core.TGRuntime rt, float pOffset)
{
return false; //Popups dont scroll
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
_Drawings.Clear();
Layout l = scope .();
l.[Friend]_RT = rt;
l.[Friend]_Children = _Children;
l.[Friend]_Drawings = _Drawings;
l.[Friend]_MaxWidth = width;
Layout.Invoke(l, width);
int32[2] max = .(0,0);
for(var i in _Drawings)
{
if(i.width+i.x > max[0])
max[0] = i.width+i.x;
if(i.height+i.y > max[1])
max[1] = i.height+i.y;
}
for(var i in ref _Drawings)
i.width = max[0];
return max;
}
} }

View file

@ -1,62 +1,103 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
using System.Collections;
abstract class RadioButton : Container class RadioButton : Component
{ {
public int32 Checked = -1; private int32 _Checked = 0;
private int32 _Hovered = -1;
private int32 _AlreadyHovered = -1;
public this(StringView pName) : base(pName, "RadioButtons") public List<String> Label = new .() ~ DeleteContainerAndItems!(_);
public this()
{ {
Description = pName;
} }
public virtual void OnCheck(int32 value) public override void Update(TGRuntime rt)
{ {
if(_Hovered != _AlreadyHovered)
{
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = -1;
} }
///Set all of the fields to be used as checkboxes public override void Draw(TGRuntime rt, rect rect)
public void SetBoxes(int32 selectedIndex, params Span<StringView> args)
{ {
Checked = selectedIndex; int32 startingY = 0;
//Clear out old ones var textureSize = rt.Platform.MeasureTextureSize("Checkbox");
this.[Friend]_LayoutData.ClearAndDeleteItems(); for(var i in Label)
Children.Clear();
for(var i in args)
{ {
var toAdd = new RadioCheckbox(i); if(!(_Checked == (.)@i.Index))
toAdd.Description = Description; rt.Platform.DrawTexture("Checkbox", .() {x = rect.x, y = rect.y + startingY, width = textureSize[0], height = textureSize[1]}, rt.Scheme.PrimaryColor);
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 else
((RadioCheckbox)i).Checked = false; rt.Platform.DrawTexture("Checkbox_Checked", .() {x = rect.x, y = rect.y + startingY, width = textureSize[0], height = textureSize[1]}, rt.Scheme.PrimaryColor);
if(_Hovered == @i.Index)
rt.Platform.DrawTexture("Checkbox", .() {x = rect.x, y = rect.y + startingY, width = textureSize[0], height = textureSize[1]}, rt.Scheme.HoverColor);
var tsize = rt.Platform.MeasureTextSize(i, "Font_Normal");
rt.Platform.DrawText(i, "Font_Normal",
.(rect.x + textureSize[0] + 5
,(.)(rect.y + 0.5*textureSize[1] - tsize[1]*0.5 + startingY))
,rt.Scheme.TextColor);
startingY += textureSize[1];
}
} }
private class RadioCheckbox : Checkbox
public override int32[2] Resize(TGRuntime rt, int32 width)
{ {
public this(StringView pName) : base(pName) var textureSize = rt.Platform.MeasureTextureSize("Checkbox");
int32 xAddition = 0;
for(var label in Label)
{ {
Margin = 2; var tsize = rt.Platform.MeasureTextSize(label, "Font_Normal");
if(tsize[0] + 5 > xAddition)
xAddition = tsize[0] + 5;
} }
public override void OnCheck(bool checkValue)
textureSize[0] = textureSize[0] + xAddition;
textureSize[1] = (.)(textureSize[1] * Label.Count);
return textureSize;
}
private int32 GetSelectedItem(TGRuntime rt, int32[2] mPos)
{ {
((RadioButton)Parent).SelectButton(this); int32 startingY = 0;
int32 itemHeight = rt.Platform.MeasureTextureSize("Checkbox")[1];
for(var i in Label)
{
if(mPos[1] >= startingY && mPos[1] < startingY + itemHeight)
{
var textSize = rt.Platform.MeasureTextSize(i, "Font_Normal");
if(mPos[0] <= rt.Platform.MeasureTextureSize("Checkbox")[0] + 5 + textSize[0])
return (.)@i.Index;
} }
startingY += itemHeight;
} }
return -1;
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
_AlreadyHovered = GetSelectedItem(rt, mPos);
return true;
}
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
_Checked = GetSelectedItem(rt, mPos);
return true;
}
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => false;
} }

View file

@ -1,115 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
using System.Collections;
class RightClickPopup : Popup
{
public NPatchInfo PatchInfo;
public Toolbar.ToolbarCategory.ToolbarItem SelectedItem = null;
private Toolbar.ToolbarCategory _Category = null ~ delete _;
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 + 20;
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;
if(x+Width > App.Width)
this.[Friend]X -= Width;
if(y+Height > App.Height)
this.[Friend]Y -= Height;
}
public override void Render(int32 soy)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
Tint);
int32 y = 5;
for(var i in Category.Items)
{
Raylib.DrawTextEx(Theme.Font, scope String(i.Name), Vector2(X + 5, Y + y + soy), Theme.FontSize, 0, Theme.Text);
if(SelectedItem == i)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + y - 5 + soy, Width, i.NameMeasurement.y+10),
Vector2(0,0),
0,
HoverTint);
}
y += (.)i.NameMeasurement.y+5;
}
}
public override Component OnHover(int32 x, int32 y)
{
if(!(x >= X && x <= X + Width))
{
SelectedItem = null;
return null;
}
int32 pos = 5 + Y;
for(var i in Category.Items)
{
if(y >= pos && y <= pos + (.)i.NameMeasurement.y+5)
{
SelectedItem = i;
return this;
}
pos += (.)i.NameMeasurement.y+5;
}
SelectedItem = null;
return null;
}
public override bool OnClick(int32 x, int32 y)
{
if(!(x >= X && x <= X + Width))
return false;
int32 pos = 5 + Y;
for(var i in Category.Items)
{
if(y >= pos && y <= pos + (.)i.NameMeasurement.y+5)
{
if(i.ClickAction != null)
i.ClickAction();
return false;
}
pos += (.)i.NameMeasurement.y+5;
}
return false;
}
public override void OnClose()
{
}
}

View file

@ -1,18 +1,64 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui; using TheaterGui.Core;
using System; using System;
using System.Collections;
using RaylibBeef;
/*
While containers are sized depending on their parent, screens are sized depending on the user
*/
class Screen : Container class Screen : Container
{ {
public this(StringView pName) : base(pName, "Screen") /*
Represents a physical view of a screen. It takes up the full window and there can only ever be one screen at a time
*/
private float _ScrollValue = 0;
public override bool OnHover(TheaterGui.Core.TGRuntime rt, int32[2] mPos)
{ {
this.Width = Raylib.GetRenderWidth(); return base.OnHover(rt, .(mPos[0], (.)(mPos[1] + _ScrollValue)));
this.Height = Raylib.GetRenderHeight(); }
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
for(var i in _Drawings)
{
if(_Children.GetValue(scope .(i.name)) case .Ok(let component))
{
if(mPos[0] >= i.x && mPos[0] <= i.x + i.width
&& mPos[1] >= i.y - _ScrollValue && mPos[1] <= i.y + i.height - _ScrollValue)
{
if(component.OnLeftClick(rt, .(mPos[0]-i.x, mPos[1]-i.y)))
return true;
else
return false;
}
}
}
return false;
}
public override void Draw(TheaterGui.Core.TGRuntime rt, TheaterGui.Core.Structs.rect rect)
{
base.Draw(rt, rect + .() {y = -(.)_ScrollValue});
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
return base.Resize(rt, width);
}
public override bool OnScroll(TheaterGui.Core.TGRuntime rt, float pOffset)
{
int32 lowest = 0;
for(var i in _Drawings)
if(i.y + i.height > lowest)
lowest = i.y + i.height;
if(lowest > rt.Platform.ScreenSize()[1])
{
_ScrollValue += pOffset;
_ScrollValue = Math.Clamp(_ScrollValue, 0, lowest - rt.Platform.ScreenSize()[1]);
return true;
}
else
return false; //Unable to scroll
} }
} }

View file

@ -1,193 +0,0 @@
namespace TheaterGui.Components;
using System;
using System.Collections;
using RaylibBeef;
class ScrollableContainer : Container
{
public int32 ContainerHeight = 0;
public float Scroll = 0;
public this(StringView pName, StringView pComponentName = "Container") : base(pName, pComponentName)
{
}
private List<Component> _LayoutData = new .() ~ delete _;
///Recalculate the layout of all entries
public override void Reorder(int32 w)
{
/*
List<Entries> {Entry, Entry, Entry, RB, Entry, Entry, RB, Entry, RB}
null is a rowbreak while non null isnt a rowbreak
Algorithm
if the current entry is a rowbreak, reset the cursor to x = 0, y = y + height
try to fit the current entry after the cursor
if it fits add it,
if it doesnt fit break
*/
int32 maxWidth = 0; //The highest width line
int32 rowHeight = 0; //How high the current row is
int32 x = 0;
int32 y = 0;
for(var e in _LayoutData)
{
if(e == null)
{ //Manual row break
x = 0;
y = y + rowHeight;
rowHeight = 0;
continue;
}
if(e is Container)
{
((Container)e).Reorder(w);
MoveChildrenRecursive((Container)e, x, y);
}
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
{ //Automatic row break
x = 0;
y = y + rowHeight;
rowHeight = 0;
}
e.[Friend]X = x + e.MarginLeft;
x = x + e.Width + e.MarginLeft + e.MarginRight;
e.[Friend]Y = y + e.MarginTop;
if(x > maxWidth)
maxWidth = x;
if(rowHeight < e.Height+e.MarginTop+e.MarginBottom)
rowHeight = e.Height+e.MarginTop+e.MarginBottom;
}
Width = maxWidth + MarginLeft + MarginRight;
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
public new void AddChild(Component pToAdd)
{
_LayoutData.Add(pToAdd);
Children.Add(pToAdd);
pToAdd.Parent = this;
}
public override void InsertBefore(Component pToAdd, Component Position, bool pReorder = true)
{
var idx = _LayoutData.IndexOf(Position);
if(idx < 0)
{
delete pToAdd; //Avoid memory leaks
return;
}
_LayoutData.Insert(idx, pToAdd);
Children.Add(pToAdd);
pToAdd.Parent = this;
if(pReorder)
App.ForceReorder();
}
public override void InsertAfter(Component pToAdd, Component Position, bool pReorder = true)
{
var idx = _LayoutData.IndexOf(Position);
idx++;
if(idx < 0)
{
delete pToAdd; //Avoid memory leaks
return;
}
_LayoutData.Insert(idx, pToAdd);
Children.Add(pToAdd);
pToAdd.Parent = this;
if(pReorder)
App.ForceReorder();
}
///Forcefully terminates the current row
public new void EndRow() => _LayoutData.Add(null);
public override void Render(int32 soy)
{
Raylib.BeginScissorMode(X, Y + soy, Width, Height);
for(var i in Children)
i.Render((.)(soy + Scroll));
Raylib.EndScissorMode();
}
public override bool OnClick(int32 x, int32 y)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, (.)(y - Scroll)))
return e.OnClick(x, (.)(y - Scroll));
}
return false;
}
public override bool OnDown(int32 x, int32 y)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, (.)(y - Scroll)))
return e.OnDown(x, (.)(y - Scroll));
}
return false;
}
public override Component OnHover(int32 x, int32 y)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, (.)(y - Scroll)))
return e.OnHover(x, (.)(y - Scroll));
}
return null;
}
public override void OnRightClick(int32 x, int32 y, Toolbar.ToolbarCategory items)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, (.)(y - Scroll)))
e.OnRightClick(x, (.)(y - Scroll), items);
}
}
public override bool HandleScrollEvent(float scroll, int32 x, int32 y)
{
for(var e in Children)
{
if(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, (.)(y - Scroll)))
if(e.HandleScrollEvent(scroll, x, (.)(y - Scroll)))
return true;
}
Scroll += scroll;
Scroll = Math.Clamp(Scroll,-(Height-ContainerHeight),0);
return true;
}
}

View file

@ -1,144 +1,141 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Components.Internal;
using TheaterGui.Core;
using System; using System;
using System.Collections; using System.Collections;
using RaylibBeef;
abstract class Toolbar : Component class Toolbar : Component
{ {
public NPatchInfo PatchInfo; private Dictionary<String, List<(String, delegate void(TGRuntime))>> _Items = new .() ~ delete _;
public List<ToolbarCategory> Categories = new .() ~ DeleteContainerAndItems!(_); private StringView _Hovered = "";
private ToolbarPopup _CurrentPopup = null; private StringView _AlreadyHovered = "";
private ToolbarCategory _SelectedPopup = null; private int32 _Offset = 0;
public this(StringView pLabel) : base("Toolbar", pLabel) public ~this()
{ {
Height = 24; for(var i in _Items)
Sprite = App.Textures.GetAsset("horizontal_patch"); {
PatchInfo =.(Sprite.SourceRect, 2,2,2,2,(.)NPatchLayout.NPATCH_NINE_PATCH); delete i.key;
for(var j in i.value)
{
delete j.0;
delete j.1;
} }
delete i.value;
public override void Render(int32 soy)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
Tint);
int32 x = 5;
for(var i in Categories)
{
Raylib.DrawTextEx(Theme.Font, scope String(scope $"{i.Name}"), Vector2(X + x, Y + Height/2 - i.NameMeasurement.y/2 + soy), Theme.FontSize, 0, Theme.Text);
if(i == _SelectedPopup)
{
//5 padding in every direction
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X + x-5, Y + soy, i.NameMeasurement.x+10, Height),
Vector2(0,0),
0,
HoverTint);
}
x += (.)i.NameMeasurement.x + 10;
} }
} }
public override bool OnClick(int32 x, int32 y) public override void Update(TGRuntime rt)
{ {
int32 hor = 10; if(_Hovered != _AlreadyHovered)
for(var i in Categories)
{ {
if(x >= hor && x <= hor + i.NameMeasurement.x) _Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = "";
}
public override void Draw(TGRuntime rt, TheaterGui.Core.Structs.rect rect)
{ {
var popup = new ToolbarPopup(hor-10,Height + Y, i); _Offset = rect.x;
popup.Owner = this; rt.Platform.DrawColorRectOutlined(rect, rt.Scheme.PrimaryColor, rt.Scheme.AcceptColor);
App.OpenPopup(popup); int32 xOffset = 0;
_CurrentPopup = popup; for(var i in _Items)
{
var tsize = rt.Platform.MeasureTextSize(i.key, "Font_Normal");
if(_Hovered == i.key)
rt.Platform.DrawColorRect(.() {x = rect.x + xOffset, y = rect.y, width = 10 + tsize[0], height = rect.height}, rt.Scheme.HoverColor);
xOffset += 5;
rt.Platform.DrawText(i.key, "Font_Normal",
.(xOffset+(rect.x),
(.)(rect.y + 0.5*rect.height - tsize[1]*0.5)+1)
,rt.Scheme.TextColor);
xOffset += tsize[0];
xOffset += 5;
}
}
public override int32[2] Resize(TGRuntime rt, int32 width)
{
return .(width, rt.Platform.MeasureTextSize("I", "Font_Normal")[1] + 6);
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
int32 xOffset = 0;
for(var i in _Items)
{
var tsize = rt.Platform.MeasureTextSize(i.key, "Font_Normal");
if(mPos[0] > xOffset && mPos[0] <= xOffset + tsize[0] + 10)
{
_AlreadyHovered = i.key;
if(rt.[Friend]_PopupsName == "Toolbar")
{
rt.ClearPopups();
OpenPopup(rt, .(xOffset, rt.Platform.MeasureTextSize("I", "Font_Normal")[1] + 4), i.key);
}
}
xOffset += tsize[0] + 10;
}
return true; return true;
} }
hor += (.)i.NameMeasurement.x + 10;
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
if(_Hovered == "")
return false;
int32 xOffset = 0;
for(var i in _Items)
{
if(i.key == _Hovered)
{
OpenPopup(rt, .(xOffset + _Offset,rt.Platform.MeasureTextSize("I", "Font_Normal")[1]+4), i.key);
return true;
}
var tsize = rt.Platform.MeasureTextSize(i.key, "Font_Normal");
xOffset += tsize[0] + 10;
} }
return false; return false;
} }
public override Component OnHover(int32 x, int32 y) public override bool OnScroll(TGRuntime rt, float pOffset) => false;
{
int32 hor = 10;
for(var i in Categories)
{
if(x >= hor && x <= hor + i.NameMeasurement.x)
{
_SelectedPopup = i;
if(_CurrentPopup != null && _CurrentPopup.Category != i)
{
_CurrentPopup.Category = i;
_CurrentPopup.[Friend]X = hor-10;
}
return this;
}
hor += (.)i.NameMeasurement.x + 10;
}
_SelectedPopup = null;
return null; private void OpenPopup(TGRuntime rt, int32[2] pos, StringView pName)
{
Popup toPopup = new .(pos);
if(_Items.GetValue(scope String(pName)) case .Ok(let vals))
{
for(var i in vals)
toPopup.AddChild(i.0, new ClickableText(new (rt) => {i.1.Invoke(rt);},i.0));
toPopup.Layout = new [&] (val, width) => {
for(var i in vals)
{
val.Component(i.0);
val.Linebreak();
}
};
}
rt.OpenPopup(toPopup, "Toolbar");
} }
public void AddToolbarCategories(params Span<ToolbarCategory> pCategories) public void AddEntry(StringView pSection, StringView pName, delegate void(TGRuntime) pToRun)
{ {
for(var i in pCategories) if(!_Items.ContainsKeyAlt<StringView>(pSection))
Categories.Add(i); _Items.Add(new .(pSection), new .());
if(_Items.GetValue(scope .(pSection)) case .Ok(let val))
val.Add((new .(pName), pToRun));
} }
///Toolbar data is stored in here public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => true;
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

@ -1,116 +0,0 @@
namespace TheaterGui.Components;
using System;
using RaylibBeef;
class ToolbarPopup : Popup
{
public NPatchInfo PatchInfo;
public Toolbar Owner = null;
public Toolbar.ToolbarCategory.ToolbarItem SelectedItem = null;
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(int32 soy)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + soy, Width, Height),
Vector2(0,0),
0,
Tint);
int32 y = 5;
for(var i in Category.Items)
{
Raylib.DrawTextEx(Theme.Font, scope String(i.Name), Vector2(X + 5 + 32, Y + y + soy), Theme.FontSize, 0, Theme.Text);
if(SelectedItem == i)
{
Raylib.DrawTextureNPatch(
*Sprite.Source,
PatchInfo,
Rectangle(X, Y + y - 5 + soy, Width, i.NameMeasurement.y+10),
Vector2(0,0),
0,
HoverTint);
}
y += (.)i.NameMeasurement.y+5;
}
}
public override Component OnHover(int32 x, int32 y)
{
if(!(x >= X && x <= X + Width))
{
SelectedItem = null;
return null;
}
int32 pos = 5 + Y;
for(var i in Category.Items)
{
if(y >= pos && y <= pos + (.)i.NameMeasurement.y+5)
{
SelectedItem = i;
return this;
}
pos += (.)i.NameMeasurement.y+5;
}
SelectedItem = null;
return null;
}
public override bool OnClick(int32 x, int32 y)
{
if(!(x >= X && x <= X + Width))
return false;
int32 pos = 5 + Y;
for(var i in Category.Items)
{
if(y >= pos && y <= pos + (.)i.NameMeasurement.y+5)
{
if(i.ClickAction != null)
i.ClickAction();
return false;
}
pos += (.)i.NameMeasurement.y+5;
}
return false;
}
public override void OnClose()
{
if(Owner != null)
{
Owner.[Friend]_CurrentPopup = null;
Owner.[Friend]_SelectedPopup = null;
}
}
}

View file

@ -1,11 +1,76 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System; using System;
abstract class VSlider : NVSlider class VSlider : Component
{
public this(StringView pLabel) : base(32 * 6, pLabel, "VSlider")
{ {
private bool _Hovered = false;
private bool _AlreadyHovered = false;
public float Value = 0;
public this()
{
} }
public override void Update(TheaterGui.Core.TGRuntime rt)
{
if (_Hovered != _AlreadyHovered)
{
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = false;
}
public override void Draw(TGRuntime rt, rect rect)
{
var ts = rt.Platform.MeasureTextureSize("Checkbox");
rt.Platform.DrawColorRect(.() { x = rect.x + ts[0] / 2 - 4, width = 8, y = rect.y + ts[1], height = rect.height-2*ts[1] }, rt.Scheme.AcceptColor);
rt.Platform.DrawTexture("Checkbox", .() { y = (.)(rect.y + ts[1] + Value * 10*ts[1]-ts[1]/2), x = rect.x, height = ts[1], width = ts[0] }, rt.Scheme.PrimaryColor);
if (_Hovered)
rt.Platform.DrawTexture("Checkbox", .() { y = (.)(rect.y + ts[1] + Value * 10*ts[1]-ts[1]/2), x = rect.x, height = ts[1], width = ts[0] }, rt.Scheme.HoverColor);
}
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
var ts = rt.Platform.MeasureTextureSize("Checkbox");
return .(ts[0], ts[1] * 12);
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
_AlreadyHovered = true;
if(rt.Platform.MouseLeftClick())
{
float oldVal = Value;
OnLeftClick(rt, mPos);
if(Value != oldVal)
rt.Dirty();
}
return true;
}
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
var ts = rt.Platform.MeasureTextureSize("Checkbox");
if(mPos[1] <= ts[1])
Value = 0;
else if(mPos[1] >= ts[1] * 11)
Value = 1;
else
{
float ofst = mPos[1] - ts[1];
Value = Math.Clamp(ofst / (ts[1]*10)
,0,1);
}
return true;
}
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => false;
} }

59
src/Core/EventSystem.bf Normal file
View file

@ -0,0 +1,59 @@
namespace TheaterGui.Core;
using System;
using System.Collections;
class EventSystem
{
private Dictionary<String, List<(uint64, delegate void(void*))>> _Events = new .() ~ delete _;
private uint64 _Id = 0;
public ~this()
{
for(var i in _Events)
{
delete i.key;
for(var j in i.value)
delete j.1;
delete i.value;
}
}
///Invoke a event, conditionally pass in a event object, and return the amount of events called
public int32 Invoke(StringView name, void* obj)
{
if(!_Events.ContainsKeyAlt<StringView>(name))
return -1;
int32 toReturn = 0;
for(var i in _Events[scope .(name)])
{
i.1(obj);
toReturn++;
}
return toReturn;
}
///Returns the deregistration id to be used when deregistering
public uint64 Register(StringView name, delegate void(void*) func)
{
if(!_Events.ContainsKeyAlt<StringView>(name))
_Events.Add(new .(name), new .());
_Events[scope .(name)].Add((_Id+1, func));
_Id++;
return _Id;
}
public void DeRegister(StringView name, uint64 id)
{
if(!_Events.ContainsKeyAlt<StringView>(name))
return;
for(var i in _Events[scope .(name)])
{
if(i.0 == id)
{
delete i.1;
@i.Remove();
}
}
}
}

64
src/Core/Layout.bf Normal file
View file

@ -0,0 +1,64 @@
namespace TheaterGui.Core;
using TheaterGui.Components;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
class Layout
{
private TGRuntime _RT;
private Dictionary<String, Component> _Children;
private List<rect> _Drawings;
private int32 _X = 0;
private int32 _Y = 0;
private int32 _LineHeight = 0;
private int32 _MaxWidth = 0;
public void Linebreak(int32 padding = 0)
{
_X = 0;
_Y += _LineHeight + padding;
_LineHeight = 0;
}
public void Placeholder(int32 width, int32 height)
{
if(height > _LineHeight)
_LineHeight = height;
if(!(_X + width > _MaxWidth))
_X += width;
}
public void Component(StringView name, int32 padding) => Component(name, padding, padding, padding, padding);
public void Component(StringView name, int32 padding_top = 0, int32 padding_bottom = 0, int32 padding_left = 0, int32 padding_right = 0)
{
if(_Children.ContainsKeyAlt<StringView>(name))
{
var item = _Children[scope .(name)];
var size = item.Resize(_RT, _MaxWidth-_X);
if(_X + size[0] + padding_left + padding_right > _MaxWidth)
{
_X = 0;
_Y += _LineHeight;
_LineHeight = 0;
}
rect toAdd = .();
toAdd.name = name;
toAdd.width = size[0];
toAdd.height = size[1];
toAdd.x = _X + padding_left;
toAdd.y = _Y + padding_top;
_Drawings.Add(toAdd);
_X += size[0] + padding_left + padding_right;
if(size[1] + padding_top + padding_bottom > _LineHeight)
_LineHeight = size[1] + padding_top + padding_bottom;
}
}
}

38
src/Core/Structs/color.bf Normal file
View file

@ -0,0 +1,38 @@
namespace TheaterGui.Core.Structs;
struct color
{
public uint8 Red = 0;
public uint8 Green = 0;
public uint8 Blue = 0;
public uint8 Alpha = 255;
public this()
{
}
public this(uint8 red, uint8 green, uint8 blue, uint8 alpha = 255)
{
Red = red;
Green = green;
Blue = blue;
Alpha = alpha;
}
public this(uint8[3] data)
{
Red = data[0];
Green = data[1];
Blue = data[2];
}
public this(uint8[4] data)
{
Red = data[0];
Green = data[1];
Blue = data[2];
Alpha = data[3];
}
}

View file

@ -0,0 +1,16 @@
namespace TheaterGui.Core.Structs;
struct colorScheme
{
public color TextColor = .(216, 197, 215);
public color BackgroundColor = .(45, 45, 49);
public color PrimaryColor = .(153, 36, 72);
public color AcceptColor = .();
public color WarningColor = .();
public color ErrorColor = .();
public color DisabledColor = .(90, 90, 90, 125);
public color HoverColor = .(255, 255, 255, 50);
public static colorScheme DefaultScheme = .();
}

23
src/Core/Structs/rect.bf Normal file
View file

@ -0,0 +1,23 @@
namespace TheaterGui.Core.Structs;
using System;
struct rect
{
public StringView name;
public int32 x = 0;
public int32 y = 0;
public int32 width;
public int32 height;
public static rect operator+(rect lhs, rect rhs)
{
rect toReturn = .();
toReturn.name = lhs.name;
toReturn.x = rhs.x + lhs.x;
toReturn.y = rhs.y + lhs.y;
toReturn.width = lhs.width;
toReturn.height = lhs.height;
return toReturn;
}
}

27
src/Core/Structs/theme.bf Normal file
View file

@ -0,0 +1,27 @@
namespace TheaterGui.Core.Structs;
using System;
struct theme
{
private static uint8[?] _Theme_Font = Compiler.ReadBinary("assets/Din.ttf");
private static uint8[?] _Theme_Button = Compiler.ReadBinary("assets/button.png");
private static uint8[?] _Theme_Checkbox = Compiler.ReadBinary("assets/checkbox.png");
private static uint8[?] _Theme_Checkbox_Checked = Compiler.ReadBinary("assets/checkbox_checked.png");
private static uint8[?] _Theme_Dropdown = Compiler.ReadBinary("assets/dropdown.png");
/*
private static uint8[?] Texture_SquareButton = Compiler.ReadBinary("assets/square_button.png");
private static uint8[?] Texture_LargeButton = Compiler.ReadBinary("assets/large_button.png");
private static uint8[?] Texture_NButton = Compiler.ReadBinary("assets/n_button.png");
private static uint8[?] Horizontal_Patch = Compiler.ReadBinary("assets/horizontal_patch.png");
*/
public (Span<uint8>, uint8) Font_Normal = (.(&_Theme_Font, _Theme_Font.Count),16);
public Span<uint8> Button = .(&_Theme_Button, _Theme_Button.Count);
public Span<uint8> Checkbox = .(&_Theme_Checkbox, _Theme_Checkbox.Count);
public Span<uint8> Checkbox_Checked = .(&_Theme_Checkbox_Checked, _Theme_Checkbox_Checked.Count);
public Span<uint8> Dropdown = .(&_Theme_Dropdown, _Theme_Dropdown.Count);
}

68
src/Core/TGRuntime.api.bf Normal file
View file

@ -0,0 +1,68 @@
namespace TheaterGui.Core;
using TheaterGui.Components;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
extension TGRuntime
{
public EventSystem Events = new .() ~ delete _;
public theme Theme;
public colorScheme Scheme;;
private Screen _CurrentScreen ~ delete _;
private bool _ShouldClose = false;
public bool ShouldClose
{
get => _ShouldClose;
set
{
if(value)
_ShouldClose = value;
}
}
public IPlatformLayer Platform
{
get;
private set;
}
private bool _Dirty = true;
public void Dirty() => _Dirty = true;
public int32[2] ScreenSize
{
get;
private set;
}
private List<Popup> _Popups = new .() ~ DeleteContainerAndItems!(_);
private String _PopupsName = new .("") ~ delete _;
public void OpenPopup(Popup pPopup, StringView pName)
{
if(pPopup == null)
{
ClearPopups();
return;
}
else if(pName != _PopupsName)
{
ClearPopups();
_PopupsName.Append(pName);
}
pPopup.Resize(this, ScreenSize[0]);
_Popups.Add(pPopup);
}
public void ClearPopups()
{
Dirty();
_PopupsName..Clear()
.Append("");
ClearAndDeleteItems(_Popups);
}
}

146
src/Core/TGRuntime.core.bf Normal file
View file

@ -0,0 +1,146 @@
namespace TheaterGui.Core;
using TheaterGui.Core.Structs;
using TheaterGui.Components;
using TheaterGui.Components.Internal;
using System;
using System.Collections;
class TGRuntime
{
private void Launch(IPlatformLayer platform, Screen screen, StringView name, int32 width, int32 height, colorScheme scheme, theme theme)
{
Platform = platform;
Platform.Initialize(name, width, height);
LoadTextures(theme, scheme);
Theme = theme;
Scheme = scheme;
_CurrentScreen = screen;
_CurrentScreen.Resize(this, width);
ScreenSize = Platform.ScreenSize();
#if BF_PLATFORM_WASM
WebAssembly.emscripten_set_main_loop(=> ProcessFrame, 60, 1);
#else
while (!ShouldClose)
ProcessFrame();
#endif
platform.Deinitialize();
}
bool _MBDownLastFrame = false;
bool _MBRightDownLastFrame = false;
private void ProcessFrame()
{
//Meta
ShouldClose = Platform.ShouldClose();
var ss = Platform.ScreenSize();
if (ss != ScreenSize)
{ //Resultion update
Dirty();
ClearPopups();
_CurrentScreen.Resize(this, ss[0]);
ScreenSize = ss;
}
if (Platform.MouseWheel() != 0)
if (_CurrentScreen.OnScroll(this, -5 * Platform.MouseWheel()))
{
Dirty();
ClearPopups();
}
var mPos = Platform.MousePosition();
bool hasHovered = false;
for (var i in _Popups)
if (i.OnHover(this, .((.)mPos[0], (.)mPos[1])))
hasHovered = true;
if (!hasHovered)
_CurrentScreen.OnHover(this, .((.)mPos[0], (.)mPos[1]));
if (_MBDownLastFrame && !Platform.MouseLeftClick())
{
bool hasLClicked = false;
for (var i in _Popups)
if (i.OnLeftClick(this, .((.)mPos[0], (.)mPos[1])))
hasLClicked = true;
if (!hasLClicked)
{
ClearPopups();
_CurrentScreen.OnLeftClick(this, .((.)mPos[0], (.)mPos[1]));
}
}
_MBDownLastFrame = Platform.MouseLeftClick();
if (_MBRightDownLastFrame && !Platform.MouseRightClick())
{
bool hasLClicked = false;
List<(String, delegate void(TGRuntime))> data = new .();
for (var i in _Popups)
if (i.OnRightClick(this, .((.)mPos[0], (.)mPos[1]), data))
{
hasLClicked = true;
}
if (!hasLClicked)
{
ClearPopups();
_CurrentScreen.OnRightClick(this, .((.)mPos[0], (.)mPos[1]), data);
}
if(data.Count > 0)
OpenRightClickPopup(this, .((.)mPos[0], (.)mPos[1]), data);
else
delete data;
}
_MBRightDownLastFrame = Platform.MouseRightClick();
//Logic
for (var i in _Popups)
i.Update(this);
_CurrentScreen.Update(this);
//Drawing
if (_Dirty)
{
Platform.StartDrawing();
_CurrentScreen.Draw(this, .() { width = ScreenSize[0], height = ScreenSize[1] });
for (var i in _Popups)
i.Draw(this, .() { width = ScreenSize[0], height = ScreenSize[1] });
Platform.StopDrawing();
_Dirty = false;
}
else
Platform.RedrawLastFrame();
}
private void LoadTextures(theme pStartingTheme, colorScheme pScheme)
{
Platform.ChangeBackgroundColor(pScheme.BackgroundColor);
Platform.LoadFont(pStartingTheme.Font_Normal.0, "Font_Normal", pStartingTheme.Font_Normal.1);
Platform.LoadTexture(pStartingTheme.Button, "Button");
Platform.LoadTexture(pStartingTheme.Checkbox, "Checkbox");
Platform.LoadTexture(pStartingTheme.Dropdown, "Dropdown");
Platform.LoadTexture(pStartingTheme.Checkbox_Checked, "Checkbox_Checked");
}
private void OpenRightClickPopup(TGRuntime rt, int32[2] pos, List<(String, delegate void(TGRuntime))> data)
{
Popup toPopup = new .(pos);
toPopup.Padding = 5;
toPopup.[Friend]_ToDelete = data;
for (var i in data)
toPopup.AddChild(i.0, new ClickableText(new (rt) => { i.1.Invoke(rt); }, i.0));
toPopup.Layout = new [&] (val, width) =>
{
for (var i in data)
{
val.Component(i.0);
val.Linebreak();
}
};
OpenPopup(toPopup, "Rightclick");
}
}

View file

@ -1,53 +0,0 @@
namespace RaylibBeef
{
extension Rectangle
{
public bool Overlaps(int32 x, int32 y)
{
if (x >= this.x && x <= this.x + this.width)
if (y >= this.y && y <= this.y + this.height)
return true;
return false;
}
}
extension Color
{
public Color SetRed(uint8 red) => .(red, g, b, a);
public Color SetGreen(uint8 green) => .(r, green, b, a);
public Color SetBlue(uint8 blue) => .(r, g, blue, a);
public Color SetAlpha(uint8 alpha) => .(r, g, b, alpha);
}
}
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} == null ? "" : {fieldInfo.Name}..EnsureNullTerminator();
public set => String.NewOrSet!({fieldInfo.Name}, value);
}};
""");
}
}
}
namespace System
{
#if BF_PLATFORM_WASM
extension WebAssembly
{
[CLink, CallingConvention(.Stdcall)]
public static extern void emscripten_set_main_loop(function void() func, int32 fps, int32 simulateInfinteLoop);
}
#endif
}

View file

@ -1,48 +0,0 @@
namespace TheaterGui.Generators;
using System;
class ButtonGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Button"
public override void InitUI()
{
AddEdit("name", "Button 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 $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : Button
{{
public this() : base("{name}")
{{
{!enabled ? "Enabled = false;" : ""}
Description = "{description}";
}}
//What happens when the button is clicked
public override void ClickAction()
{{
}}
}}
""");
}
}

View file

@ -1,48 +0,0 @@
namespace TheaterGui.Generators;
using System;
class CheckboxGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> 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 $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {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

@ -1,37 +0,0 @@
namespace TheaterGui.Generators;
using System;
class ContainerGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Container"
public override void InitUI()
{
AddEdit("name", "Container Name", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
outFileName.Append(scope $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : Container
{{
public this() : base("{name}")
{{
//Add ui items here via AddChild() and terminate the row via EndRow()
Margin = 5;
}}
}}
""");
}
}

View file

@ -1,54 +0,0 @@
namespace TheaterGui.Generators;
using System;
class DropdownGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Dropdown"
public override void InitUI()
{
AddEdit("name", "Button Name", "");
AddEdit("description", "Description", "");
AddEdit("content", "Entries", "Seperate entries via ';'");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
var description = mParams["description"];
var entries = mParams["content"];
outFileName.Append(scope $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
using System;
class TG{name} : Dropdown
{{
public this() : base()
{{
Description = "{description}";
""");
var split = entries.Split(';');
for(var i in split)
outText.Append(scope $"\t\tEntries.Add(new .(\"{i}\"));\n");
outText.Append(scope $"""
}}
//What happens when the button is clicked
public override void ClickAction(String newValue)
{{
}}
}}
""");
}
}

View file

@ -1,49 +0,0 @@
namespace TheaterGui.Generators;
using System;
class IconButtonGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Icon Button"
public override void InitUI()
{
AddEdit("name", "Button 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 $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : IconButton
{{
public this() : base("{name}")
{{
Icon = .Icon_Folder; //Use this to set the icon sprite
{!enabled ? "Enabled = false;" : ""}
Description = "{description}";
}}
//What happens when the button is clicked
public override void ClickAction()
{{
}}
}}
""");
}
}

View file

@ -1,48 +0,0 @@
namespace TheaterGui.Generators;
using System;
class LargeButtonGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Large Button"
public override void InitUI()
{
AddEdit("name", "Button 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 $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : LargeButton
{{
public this() : base("{name}")
{{
{!enabled ? "Enabled = false;" : ""}
Description = "{description}";
}}
//What happens when the button is clicked
public override void ClickAction()
{{
}}
}}
""");
}
}

View file

@ -1,48 +0,0 @@
namespace TheaterGui.Generators;
using System;
class NButtonGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> NButton"
public override void InitUI()
{
AddEdit("name", "Button 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 $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : NButton
{{
public this() : base("{name}")
{{
{!enabled ? "Enabled = false;" : ""}
Description = "{description}";
}}
//What happens when the button is clicked
public override void ClickAction()
{{
}}
}}
""");
}
}

View file

@ -1,48 +0,0 @@
namespace TheaterGui.Generators;
using System;
class RadioButtonGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Radio Button"
public override void InitUI()
{
AddEdit("name", "Component 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 $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {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)
{{
}}
}}
""");
}
}

View file

@ -1,36 +0,0 @@
namespace TheaterGui.Generators;
using System;
class ScreenGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Screen"
public override void InitUI()
{
AddEdit("name", "Screen Name", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
outFileName.Append(scope $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : Screen
{{
public this() : base("{name}")
{{
//Add UI items here via AddChild() and terminate the row via EndRow()
}}
}}
""");
}
}

View file

@ -1,57 +0,0 @@
namespace TheaterGui.Generators;
using System;
class SliderGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Slider"
public override void InitUI()
{
AddEdit("name", "Slider Name", "");
AddCombo("orientation", "Orientation", "Horizontal", .(scope StringView[]("Horizontal", "Vertical")));
AddCheckbox("length", "Fixed Length", true);
AddEdit("min", "Minimal Value", "0");
AddEdit("max", "Maximal Value", "100");
AddEdit("dfault", "Starting Value", "50");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
var orientation = mParams["orientation"];
var fixedLength = mParams["length"];
var min = mParams["min"];
var max = mParams["max"];
var dfault = mParams["dfault"];
outFileName.Append(scope $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : {(fixedLength == "True") ? "" : "N" }{(orientation == "Horizontal") ? "H" : "V"}Slider
{{
public this() : base({(fixedLength == "True") ? "" : "64, "}"{name}")
{{
//Use the thing above to set the length of the bar
Min = {min};
Max = {max};
Value = {dfault};
ReactToValueChange = true; //This indicates wether OnValueChange gets actually called
}}
public override void OnValueChange(float newVal)
{{
}}
}}
""");
}
}

View file

@ -1,44 +0,0 @@
namespace TheaterGui.Generators;
using System;
class ToolbarGenerator : Compiler.Generator
{
public override String Name => "TheaterGui -> Toolbar"
public override void InitUI()
{
AddEdit("name", "Toolbar Name", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
outFileName.Append(scope $"{name}");
outText.Append(scope $"""
namespace {Namespace};
using TheaterGui.Components;
class {name} : Toolbar
{{
public this() : base("{name}")
{{
/*
AddToolbarCategories(
new .("File",
new .("Open Recent", null)
)
);
*/
}}
}}
""");
}
}

43
src/IPlatformLayer.bf Normal file
View file

@ -0,0 +1,43 @@
namespace TheaterGui;
using TheaterGui.Core.Structs;
using System;
interface IPlatformLayer
{
public void Initialize(StringView pWindowTitle, int32 pWidth, int32 pHeight);
public void Deinitialize();
//Drawing
public void StartDrawing();
public void StopDrawing();
public void RedrawLastFrame();
public void ChangeBackgroundColor(color pTint);
public void LoadTexture(Span<uint8> pData, StringView pName);
public void DrawTexture(StringView pName, rect pTarget, color pTint);
public int32[2] MeasureTextureSize(StringView pName);
public void DrawColorRect(rect pTarget, color pTint);
public void DrawColorRectOutlined(rect pTarget, color pTint, color pOutlineTint);
///Text
public void LoadFont(Span<uint8> pData, StringView pName, uint8 pFontSize);
public void DrawText(StringView pText, StringView pFont, int32[2] pPosition, color pTint);
public int32[2] MeasureTextSize(StringView pText, StringView pFont);
///Input
public bool ShouldClose();
public int32[2] ScreenSize();
public float[2] MousePosition();
public float[2] MouseDelta();
public bool MouseLeftClick();
public bool MouseRightClick();
public float MouseWheel();
}

View file

@ -1,122 +0,0 @@
namespace TheaterGui;
using System;
using RaylibBeef;
using TheaterGui.Components;
class Input
{
internal static void HandleScrolling(float pWheelMove)
{
var w = pWheelMove * pWheelMove * pWheelMove;
if (!App.CurrentScreen.HandleScrollEvent(w, (.)App.Mouse.x, (.)(App.Mouse.y - App.Wheel)) && App.CurrentScreen.Height - App.Height > 0)
{
App.Wheel += w;
App.Wheel = Math.Clamp(App.Wheel, -1 * (App.CurrentScreen.Height - App.Height), 0);
}
}
internal static void HandleLeftClick()
{
var mouse = App.Mouse;
if (App.GetTopmostPopup() case .Ok(let top))
{
if (Rectangle(top.X, top.Y, top.Width, top.Height).Overlaps((.)mouse.x, (.)(mouse.y - App.Wheel)))
{
if (top.OnClick((.)mouse.x, (.)mouse.y))
return;
App.[Friend]_Popups.Remove(top);
top.OnClose(); //Any last words ?
delete top;
}
else
{
bool a = top is DropdownPopup; //TODO: Make this less horrible
App.[Friend]_Popups.Remove(top);
top.OnClose(); //Any last words ?
delete top;
if(!a)
App.CurrentScreen.OnClick((.)mouse.x, (.)(mouse.y - App.Wheel)); //So that we dont eat any inputs
}
}
else
App.CurrentScreen.OnClick((.)mouse.x, (.)(mouse.y - App.Wheel));
}
internal static void HandleRightClick()
{
//We dont stack up rightclicks
if (App.GetTopmostPopup() case .Ok(let top))
if (top is RightClickPopup)
delete App.[Friend]_Popups.GetAndRemove(top).Value;
Toolbar.ToolbarCategory popupCat = new .("Items");
App.CurrentScreen.OnRightClick((.)App.Mouse.x, (.)App.Mouse.y, popupCat);
if (popupCat.Items.Count > 0)
App.[Friend]_Popups.Add(new RightClickPopup((.)App.Mouse.x, (.)App.Mouse.y, popupCat));
else
delete popupCat;
}
internal static bool HandleLeftDown()
{
//Topmost first
for (var i in App.[Friend]_Popups.Reversed)
{
var hoverResult = i.OnDown(Raylib.GetMouseX(), Raylib.GetMouseY() - (.)App.Wheel);
if (hoverResult == false)
continue;
return hoverResult;
}
return App.CurrentScreen.OnDown(Raylib.GetMouseX(), (.)(Raylib.GetMouseY() - App.Wheel));
}
internal static void HandleHover()
{
Component hoverRes = HandleHoverPopups();
if (hoverRes == null)
hoverRes = HandleHoverScreen();
if (hoverRes == null)
Raylib.SetMouseCursor((.)MouseCursor.MOUSE_CURSOR_DEFAULT);
else
{
if (hoverRes.Enabled)
Raylib.SetMouseCursor((.)MouseCursor.MOUSE_CURSOR_POINTING_HAND);
if (App.[Friend]_OldPos != App.Mouse)
App.[Friend]_MovTime = Raylib.GetTime();
if (Raylib.GetTime() - App.[Friend]_MovTime >= 0.5)
App.[Friend]_HoverText.Append(hoverRes.Description);
App.[Friend]_OldPos = App.Mouse;
}
}
///Handle the hover effect for all screen objects
private static Component HandleHoverScreen()
{
return App.CurrentScreen.OnHover(Raylib.GetMouseX(), (.)(Raylib.GetMouseY() - App.Wheel));
}
///Checks if any popup catches the hover
private static Component HandleHoverPopups()
{
//Topmost first
for (var i in App.[Friend]_Popups.Reversed)
{
var hoverResult = i.OnHover(Raylib.GetMouseX(), Raylib.GetMouseY() - (.)App.Wheel);
if (hoverResult == null)
continue;
return hoverResult;
}
return null;
}
}

View file

@ -1,21 +0,0 @@
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

17
src/TODO Normal file
View file

@ -0,0 +1,17 @@
TODO:
TabbedContainers
Fixed size containers
Collapseable
Code cleanup
Dropdowns render offscreen
Being Worked on:
Textfields
Textboxes
Done:
Radio Buttons
Horizontal sliders
Vertical sliders
Dropdown

View file

@ -1,41 +0,0 @@
namespace TheaterGui;
using System;
using RaylibBeef;
public class Theme
{
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 FontSizeLarge = 20;
public static int32 FontSizeSmall = 12;
public static Color Background = .(45, 45, 49, 255);
public static Color Tint = .(153, 36, 72, 255);
public static Color HoverTint = .(255,255,255,50); //Color overlay when an object is hovered over
public static Color DisabledTint = .(90,90,90,125);
public static Color Text = .(216, 197, 215, 255);
//Font data
public static uint8[?] Din = Compiler.ReadBinary("assets/Din.ttf");
public static uint8[?] Texture_WindowIcon = Compiler.ReadBinary("assets/64.png");
//Icons
public static uint8[?] Texture_TheaterIcon = Compiler.ReadBinary("assets/icon_theater.png");
public static uint8[?] Texture_FolderIcon = Compiler.ReadBinary("assets/icon_folder.png");
//Controls
public static uint8[?] Texture_SquareButton = Compiler.ReadBinary("assets/square_button.png");
public static uint8[?] Texture_LargeButton = Compiler.ReadBinary("assets/large_button.png");
public static uint8[?] Texture_NButton = Compiler.ReadBinary("assets/n_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_Checked = Compiler.ReadBinary("assets/checkbox_checked.png");
public static uint8[?] Horizontal_Patch = Compiler.ReadBinary("assets/horizontal_patch.png");
public static uint8[?] Dropdown = Compiler.ReadBinary("assets/dropdown.png");
}

10
src/WASM.bf Normal file
View file

@ -0,0 +1,10 @@
namespace System
{
#if BF_PLATFORM_WASM
extension WebAssembly
{
[CLink, CallingConvention(.Stdcall)]
public static extern void emscripten_set_main_loop(function void() func, int32 fps, int32 simulateInfinteLoop);
}
#endif
}