further changes, lazy loading, new modular backend system

This commit is contained in:
Booklordofthedings 2024-10-06 22:23:40 +02:00
parent c0e0c30eb8
commit c0dd04127c
68 changed files with 735 additions and 2865 deletions

View file

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

View file

@ -1,5 +1,5 @@
FileVersion = 1 FileVersion = 1
Projects = {TheaterGui = {Path = "."}, examples = {Path = "examples"}} Projects = {TheaterGui = {Path = "."}, examples = {Path = "examples"}, Bofa = {Path = "deps/Bofa"}, raylib-beef = {Path = "deps/Raylib/raylib-beef"}, raylib-backend = {Path = "raylib-backend"}}
[Workspace] [Workspace]
StartupProject = "examples" StartupProject = "examples"

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,34 @@
namespace examples; namespace examples;
using TheaterGui; using TheaterGui;
using TheaterGui.Core;
using TheaterGui.Components;
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 : Container
{
public this()
{
AddChild("Main Button", new Button());
AddChild("Other Button", new Button());
Layout = new (val, width) =>
{
val.Component("Main Button", 5);
val.Component("Other Button", 10);
};
}
}

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)
{
}
}

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,95 @@
namespace raylib_backend;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
using RaylibBeef;
extension PlatformRaylib
{
public void StartDrawing()
{
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();
}
public void RedrawLastFrame()
{
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
);
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(Container 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,51 @@
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 _;
public this(StringView pName = "Button")
{ {
Sprite = App.Textures.GetAsset("button"); Label = new .(pName);
Width = Sprite.Width;
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;
}
return false;
}
public override void Render(int32 soy)
{
Sprite.Render(X,Y + soy, 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)
{
Sprite.Render(X,Y + soy, Theme.DisabledTint);
}
else
{
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
}
_IsHovered = false;
}
} }

View file

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

View file

@ -1,107 +1,11 @@
namespace TheaterGui.Components; namespace TheaterGui.Components;
using TheaterGui.Core;
using System; using TheaterGui.Core.Structs;
using RaylibBeef;
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);
}
///
/// 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,80 @@
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) public override int32[2] Resize(TGRuntime rt, int32 width)
{ {
((Container)e).Reorder(w); _Drawings.Clear();
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);
}
Layout l = scope .();
l.[Friend]_RT = rt;
l.[Friend]_Children = _Children;
l.[Friend]_Drawings = _Drawings;
l.[Friend]_MaxWidth = width;
if(e is Toolbar) Layout.Invoke(l, width);
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 int32[2] max = .(0,0);
{ //Automatic row break for(var i in _Drawings)
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) 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 override bool OnHover(TGRuntime rt, int32[2] mPos)
{ {
i.[Friend]X += x + MarginLeft + c.MarginLeft; for(var i in _Drawings)
i.[Friend]Y += y + MarginTop + c.MarginTop; {
if(i is Container) if(_Children.GetValue(scope .(i.name)) case .Ok(let component))
MoveChildrenRecursive((Container)i, 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.OnHover(rt, .(mPos[0]-i.x, mPos[1]-i.y)))
return true;
else
return false;
}
} }
} }
///Add a new item to the list of items
public void AddChild(Component pToAdd)
{
_LayoutData.Add(pToAdd);
Children.Add(pToAdd);
pToAdd.Parent = this;
}
public virtual 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 virtual 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 void EndRow() => _LayoutData.Add(null);
public override void Render(int32 soy)
{
for(var i in Children)
i.Render(soy);
}
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 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);
return e.OnDown(x, y); return .Ok;
}
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))
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;
}
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))
e.OnRightClick(x, y, items);
}
} }
} }

View file

@ -1,57 +0,0 @@
namespace TheaterGui.Components;
using System;
using System.Collections;
using RaylibBeef;
abstract class Dropdown : Component
{
public List<String> Entries = new .() ~ DeleteContainerAndItems!(_);
public uint32 SelectedEntry = 0;
public this() : base("Dropdown", "Label")
{
Margin = 20;
Sprite = App.Textures.GetAsset("dropdown");
Width = Sprite.Width;
Height = Sprite.Height;
}
public override void Render(int32 soy)
{
Sprite.Render(X,Y + soy, Tint);
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);
}
else
{
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
}
_IsHovered = false;
}
public override bool OnClick(int32 x, int32 y)
{
var list = new List<StringView>(Entries.Count);
defer delete list;
for(var i in Entries)
list.Add(i);
var popup = new DropdownPopup(X, Y + Height, Width, list);
popup.Owner = this;
App.OpenPopup(popup);
return true;
}
public abstract void ClickAction(String pNewVal);
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
{
_IsHovered = true;
return this;
}
}

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 +0,0 @@
namespace TheaterGui.Components;
using System;
abstract class HSlider : NHSlider
{
public this(StringView pLabel) : base(32 * 6, pLabel, "HSlider")
{
}
}

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

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

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 +0,0 @@
namespace TheaterGui.Components;
abstract class Popup : Component
{
public this(System.StringView pComponentName, System.StringView pLabel) : base(pComponentName, pLabel)
{
}
///When something force closes the popup
public abstract void OnClose();
}

View file

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

View file

@ -1,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 +0,0 @@
namespace TheaterGui.Components;
using TheaterGui;
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
{
public this(StringView pName) : base(pName, "Screen")
{
this.Width = Raylib.GetRenderWidth();
this.Height = Raylib.GetRenderHeight();
}
}

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 +0,0 @@
namespace TheaterGui.Components;
using System;
using System.Collections;
using RaylibBeef;
abstract class Toolbar : Component
{
public NPatchInfo PatchInfo;
public List<ToolbarCategory> Categories = new .() ~ DeleteContainerAndItems!(_);
private ToolbarPopup _CurrentPopup = null;
private ToolbarCategory _SelectedPopup = null;
public this(StringView pLabel) : base("Toolbar", pLabel)
{
Height = 24;
Sprite = App.Textures.GetAsset("horizontal_patch");
PatchInfo =.(Sprite.SourceRect, 2,2,2,2,(.)NPatchLayout.NPATCH_NINE_PATCH);
}
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)
{
int32 hor = 10;
for(var i in Categories)
{
if(x >= hor && x <= hor + i.NameMeasurement.x)
{
var popup = new ToolbarPopup(hor-10,Height + Y, i);
popup.Owner = this;
App.OpenPopup(popup);
_CurrentPopup = popup;
return true;
}
hor += (.)i.NameMeasurement.x + 10;
}
return false;
}
public override Component OnHover(int32 x, int32 y)
{
int32 hor = 10;
for(var i in Categories)
{
if(x >= hor && x <= hor + i.NameMeasurement.x)
{
_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;
}
public void AddToolbarCategories(params Span<ToolbarCategory> pCategories)
{
for(var i in pCategories)
Categories.Add(i);
}
///Toolbar data is stored in here
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 +0,0 @@
namespace TheaterGui.Components;
using System;
abstract class VSlider : NVSlider
{
public this(StringView pLabel) : base(32 * 6, pLabel, "VSlider")
{
}
}

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);
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;
}
}

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

@ -0,0 +1,25 @@
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[?] 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[?] Texture_Checkbox = Compiler.ReadBinary("assets/checkbox.png");
private static uint8[?] Texture_Checkbox_Checked = Compiler.ReadBinary("assets/checkbox_checked.png");
private static uint8[?] Horizontal_Patch = Compiler.ReadBinary("assets/horizontal_patch.png");
private static uint8[?] Dropdown = Compiler.ReadBinary("assets/dropdown.png");
*/
public (Span<uint8>, uint8) Font_Normal = (
.(&_Theme_Font, _Theme_Font.Count),
16
);
public Span<uint8> Button = .(&_Theme_Button, _Theme_Button.Count);
}

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

@ -0,0 +1,40 @@
namespace TheaterGui.Core;
using TheaterGui.Components;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
extension TGRuntime
{
public theme Theme;
public colorScheme Scheme;;
private List<Container> _Screens = new .() ~ DeleteContainerAndItems!(_);
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;
}
}

View file

@ -0,0 +1,67 @@
namespace TheaterGui.Core;
using TheaterGui.Core.Structs;
using TheaterGui.Components;
using System;
class TGRuntime
{
private void Launch(IPlatformLayer platform, Container 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;
_Screens.Add(screen);
for(var i in _Screens)
i.Resize(this, width);
#if BF_PLATFORM_WASM
WebAssembly.emscripten_set_main_loop(=> ProcessFrame, 60, 1);
#else
while (!ShouldClose)
ProcessFrame();
#endif
platform.Deinitialize();
}
private void ProcessFrame()
{
//Meta
ShouldClose = Platform.ShouldClose();
var ss = Platform.ScreenSize();
if(ss != ScreenSize)
{ //Resultion update
Dirty();
for(var i in _Screens)
i.Resize(this, ss[0]);
ScreenSize = ss;
}
var mPos = Platform.MousePosition();
_Screens[0].OnHover(this, .((.)mPos[0], (.)mPos[1]));
//Logic
for(var screen in _Screens)
screen.Update(this);
//Drawing
if(_Dirty)
{
Platform.StartDrawing();
for(var screen in _Screens)
screen.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");
}
}

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

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
}