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
**/build
**/recovery
**/deps
BeefSpace_User.toml

View file

@ -1,5 +1,5 @@
FileVersion = 1
Dependencies = {corlib = "*", corlib = "*", raylib-beef = "*"}
Dependencies = {corlib = "*", corlib = "*", Bofa = "*", raylib-beef = "*"}
[Project]
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
Dependencies = {corlib = "*", TheaterGui = "*"}
Dependencies = {corlib = "*", TheaterGui = "*", raylib-backend = "*"}
[Project]
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;
using System;
using TheaterGui;
using TheaterGui.Core;
using TheaterGui.Components;
using TheaterGui.Components.Internal;
class Program
{
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.Collections;
using RaylibBeef;
class Textures
class TextureManager
{
private List<Node> _Containers = new .() ~ DeleteContainerAndItems!(_);
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!(_);
///Attempt to get a existing asset by StringView
@ -164,10 +138,6 @@ struct sprite
public int32 Height = 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)
{
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;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using TheaterGui.Components;
using System;
using System.Collections;
using RaylibBeef;
using internal TheaterGui;
using TheaterGui.Components;
class App
{
/*
There are two ways to run TheaterGui.
Either full, by using the Run function or by manually calling the functions in Run.
This way you can integrate TheaterGui into other raylib applications of your choice.
private String _Name = new .("TheaterGui Program") ~ delete _;
private int32[2] _StartingSize = .(1280, 720);
private IPlatformLayer _Platform = null ~ delete _;
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)
*/
#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
public Result<void> Run(Screen pScreen)
{
get => _CurrentScreen;
set
{
if (_CurrentScreen != null)
delete _CurrentScreen;
_CurrentScreen = value;
_CurrentScreen.Reorder(Width);
}
};
if(_Platform == null)
return .Err;
var runtime = new TGRuntime();
runtime.[Friend]Launch(_Platform, pScreen, _Name, _StartingSize[0], _StartingSize[1], _Scheme, _Theme);
delete runtime;
///Window width
public static int32 Width
{
get => Raylib.GetRenderWidth();
set => Raylib.SetWindowSize(value, Raylib.GetRenderHeight());
return .Ok;
}
///Window height
public static int32 Height
public void SetName(StringView pName)
{
get => Raylib.GetRenderHeight();
set => Raylib.SetWindowSize(Raylib.GetRenderWidth(), value);
_Name.Clear();
_Name.Append(pName);
}
///Mouse position
public static Vector2 Mouse
public void SetStartingSize(int32 pWidth, int32 pHeight)
{
get => Raylib.GetMousePosition();
if(pWidth > 0 || pHeight > 0)
return;
_StartingSize[0] = pWidth;
_StartingSize[1] = pHeight;
}
//Mouse wheel offset
public static float Wheel
public void SetPlatformLayer(IPlatformLayer pPlatformLayer)
{
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);
delete _Platform;
_Platform = pPlatformLayer;
}
public void SetColorScheme(colorScheme pScheme) => _Scheme = pScheme;
public void SetUITheme(theme pTheme) => _Theme = pTheme;
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 .Ok(_Popups[^1]);
}
}
}

View file

@ -1,55 +1,68 @@
namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
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");
Width = Sprite.Width;
Height = Sprite.Height;
Label = new .(pLabel);
Action = pAction;
}
public abstract void ClickAction(); //What happends when its clicked
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
public override void Update(TheaterGui.Core.TGRuntime rt)
{
_IsHovered = true;
return this;
if(_Hovered != _AlreadyHovered)
{
_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))
{
if(Enabled)
{
ClickAction();
return true;
}
return false;
}
return false;
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 void Render(int32 soy)
public override int32[2] Resize(TheaterGui.Core.TGRuntime rt, int32 width)
{
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;
return rt.Platform.MeasureTextureSize("Button");
}
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) => false;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList)
{
pList.Add((.)(new String(scope $"Click {Label}"), new (rt) => {this.Action.Invoke(rt);}));
return true;
}
}

View file

@ -1,49 +1,73 @@
namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
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 abstract void OnCheck(bool checkValue);
public this(StringView pName) : base("Checkbox", pName)
public this(StringView 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;
Label = new .(pName);
}
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
public override void Update(TGRuntime rt)
{
_IsHovered = true;
return this;
if(_Hovered != _AlreadyHovered)
{
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = false;
}
public override bool OnClick(int32 x, int32 y)
public override void Draw(TGRuntime rt, rect rect)
{
Checked = !Checked;
OnCheck(Checked);
var textureSize = rt.Platform.MeasureTextureSize("Checkbox");
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;
}
public override void Render(int32 soy)
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
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;
Checked = !Checked;
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,107 +1,17 @@
namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
using RaylibBeef;
using System.Collections;
abstract class Component
{
public this(StringView pComponentName, StringView pLabel)
{
ComponentType = pComponentName;
Label = pLabel;
}
///
/// Fields
///
public Container Parent;
public bool Enabled = true;
public Color Tint = Theme.Tint;
public Color HoverTint = Theme.HoverTint;
public sprite Sprite;
public int32 MarginTop = 0;
public int32 MarginBottom = 0;
public int32 MarginLeft = 0;
public int32 MarginRight = 0;
///
/// Properties
///
public bool Selected
{ //When something is selected its allowed to update every frame
get;
set
{
Selected = value;
App.Selected = this;
}
}
public int32 Margin
{
//There seems to be no reasonable way to implement a getter here
set
{
MarginTop = value;
MarginBottom = value;
MarginLeft = value;
MarginRight = value;
}
}
private int32 _X = 0;
public int32 X
{
get => _X;
private set
{
_X = value;
} //You may set this, but it might fuck up layouting
};
private int32 _Y = 0;
public int32 Y
{
get => _Y;
private set
{
_Y = value;
} //You may set this, but it might fuck up layouting
};
public int32 Width
{
public get;
protected set; //Please do not
}
public int32 Height
{
public get;
protected set; //Please do not
}
[DynamicString] //What this type of component is called
private String _ComponentType = new .() ~ delete _;
[DynamicString] //What the component says on it when rendered
private String _Label = null ~ delete _;
[DynamicString] //Usually only used by the OnHover tooltip
private String _Description = null ~ delete _;
///
/// Functions
///
public abstract void Render(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() { }
public abstract void Update(TGRuntime rt);
public abstract void Draw(TGRuntime rt, rect rect);
public abstract int32[2] Resize(TGRuntime rt, int32 width);
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);
}

View file

@ -1,195 +1,123 @@
namespace TheaterGui.Components;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
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 List<Component> Children = new .() ~ DeleteContainerAndItems!(_);
///Recalculate the layout of all entries
public virtual void Reorder(int32 w)
public override void Draw(TGRuntime rt, rect rect)
{
/*
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)
for(var i in _Drawings)
{
if(e == null)
{ //Manual row break
x = 0;
y = y + rowHeight;
rowHeight = 0;
continue;
}
if(_Children.ContainsKeyAlt<StringView>(i.name))
_Children[scope .(i.name)].Draw(rt, i + rect);
}
}
if(e is Container)
public override int32[2] Resize(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;
}
return max;
}
public override bool OnHover(TGRuntime rt, int32[2] mPos)
{
for(var i in _Drawings)
{
if(_Children.GetValue(scope .(i.name)) case .Ok(let component))
{
((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;
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;
}
MoveChildrenRecursive((Container)e, x, y);
}
}
return false;
}
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;
public override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList)
{
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 && mPos[1] <= i.y + i.height)
{
component.OnRightClick(rt, .(mPos[0]-i.x, mPos[1]-i.y), pList);
return true;
}
}
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 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;
}
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))
return e.OnDown(x, y);
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)
{
if(component.OnLeftClick(rt, .(mPos[0]-i.x, mPos[1]-i.y)))
return true;
else
return true;
}
}
}
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;
}
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(RaylibBeef.Rectangle(e.X, e.Y, e.Width, e.Height).Overlaps(x, y))
e.OnRightClick(x, y, items);
}
if(_Children.ContainsKeyAlt<StringView>(pName))
return .Err;
_Children.Add(new .(pName), pComponent);
return .Ok;
}
}

View file

@ -1,57 +1,71 @@
namespace TheaterGui.Components;
using TheaterGui.Components.Internal;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
using System.Collections;
using RaylibBeef;
abstract class Dropdown : Component
class Dropdown : Component
{
public List<String> Entries = new .() ~ DeleteContainerAndItems!(_);
public uint32 SelectedEntry = 0;
private bool _Hovered = false;
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;
Sprite = App.Textures.GetAsset("dropdown");
Width = Sprite.Width;
Height = Sprite.Height;
for(var i in args)
Options.Add(new .(i));
}
public override void Render(int32 soy)
public override void Update(TheaterGui.Core.TGRuntime rt)
{
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)
if(_Hovered != _AlreadyHovered)
{
Sprite.Render(X,Y + soy, Theme.DisabledTint);
_Hovered = _AlreadyHovered;
rt.Dirty();
}
else
{
if(_IsHovered)
Sprite.Render(X,Y + soy, HoverTint);
}
_IsHovered = false;
_AlreadyHovered = false;
}
public override bool OnClick(int32 x, int32 y)
public override void Draw(TGRuntime rt, rect rect)
{
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);
_Pos = .(rect.x, rect.y + rect.height);
rt.Platform.DrawTexture("Dropdown", rect, rt.Scheme.PrimaryColor);
if(_Hovered)
rt.Platform.DrawTexture("Dropdown", rect, rt.Scheme.HoverColor);
var tsize = rt.Platform.MeasureTextSize(Options[Selected], "Font_Normal");
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;
}
public abstract void ClickAction(String pNewVal);
protected bool _IsHovered = false;
public override Component OnHover(int32 x, int32 y)
public override bool OnLeftClick(TGRuntime rt, int32[2] mPos)
{
_IsHovered = true;
return this;
var p = new DropdownPopup(Options) {Position = _Pos};
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;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
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;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
using System;
using RaylibBeef;
using System.Collections;
class Label : Component
{
private Vector2 _Measure;
public this(StringView pName) : base(Label, pName)
public String Label = null ~ delete _;
public this(StringView pLabel)
{
Label = pName;
Margin = 3;
_Measure = Raylib.MeasureTextEx(Theme.FontLarge, Label.Ptr, Theme.FontSizeLarge, 0);
Height = (.)_Measure.y;
Width = (.)_Measure.x;
Label = new .(pLabel);
}
public override Component OnHover(int32 x, int32 y) => null;
public override void Render(int32 soy)
public override void Update(TGRuntime rt)
{
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;
using TheaterGui.Core.Structs;
using TheaterGui.Core;
abstract class Popup : Component
using System;
using System.Collections;
///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
{
public this(System.StringView pComponentName, System.StringView pLabel) : base(pComponentName, pLabel)
{
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 abstract void OnClose();
public ~this()
{
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;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
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)
{
}
///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)
if(_Hovered != _AlreadyHovered)
{
var toAdd = new RadioCheckbox(i);
toAdd.Description = Description;
if(@i.Index == selectedIndex)
toAdd.Checked = true;
AddChild(toAdd);
EndRow();
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = -1;
}
private void SelectButton(RadioCheckbox toSelect)
public override void Draw(TGRuntime rt, rect rect)
{
for(var i in Children)
if(i == toSelect)
{
((RadioCheckbox)i).Checked = true;
Checked = (.)@i.Index;
OnCheck((.)@i.Index);
}
int32 startingY = 0;
var textureSize = rt.Platform.MeasureTextureSize("Checkbox");
for(var i in Label)
{
if(!(_Checked == (.)@i.Index))
rt.Platform.DrawTexture("Checkbox", .() {x = rect.x, y = rect.y + startingY, width = textureSize[0], height = textureSize[1]}, rt.Scheme.PrimaryColor);
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;
}
public override void OnCheck(bool checkValue)
{
((RadioButton)Parent).SelectButton(this);
var tsize = rt.Platform.MeasureTextSize(label, "Font_Normal");
if(tsize[0] + 5 > xAddition)
xAddition = tsize[0] + 5;
}
textureSize[0] = textureSize[0] + xAddition;
textureSize[1] = (.)(textureSize[1] * Label.Count);
return textureSize;
}
}
private int32 GetSelectedItem(TGRuntime rt, int32[2] mPos)
{
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;
using TheaterGui;
using TheaterGui.Core;
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")
/*
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();
this.Height = Raylib.GetRenderHeight();
return base.OnHover(rt, .(mPos[0], (.)(mPos[1] + _ScrollValue)));
}
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;
using TheaterGui.Components.Internal;
using TheaterGui.Core;
using System;
using System.Collections;
using RaylibBeef;
abstract class Toolbar : Component
class Toolbar : Component
{
public NPatchInfo PatchInfo;
public List<ToolbarCategory> Categories = new .() ~ DeleteContainerAndItems!(_);
private ToolbarPopup _CurrentPopup = null;
private ToolbarCategory _SelectedPopup = null;
private Dictionary<String, List<(String, delegate void(TGRuntime))>> _Items = new .() ~ delete _;
private StringView _Hovered = "";
private StringView _AlreadyHovered = "";
private int32 _Offset = 0;
public this(StringView pLabel) : base("Toolbar", pLabel)
public ~this()
{
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)
for(var i in _Items)
{
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)
delete i.key;
for(var j in i.value)
{
//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);
delete j.0;
delete j.1;
}
x += (.)i.NameMeasurement.x + 10;
delete i.value;
}
}
public override bool OnClick(int32 x, int32 y)
public override void Update(TGRuntime rt)
{
int32 hor = 10;
for(var i in Categories)
if(_Hovered != _AlreadyHovered)
{
if(x >= hor && x <= hor + i.NameMeasurement.x)
_Hovered = _AlreadyHovered;
rt.Dirty();
}
_AlreadyHovered = "";
}
public override void Draw(TGRuntime rt, TheaterGui.Core.Structs.rect rect)
{
_Offset = rect.x;
rt.Platform.DrawColorRectOutlined(rect, rt.Scheme.PrimaryColor, rt.Scheme.AcceptColor);
int32 xOffset = 0;
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)
{
var popup = new ToolbarPopup(hor-10,Height + Y, i);
popup.Owner = this;
App.OpenPopup(popup);
_CurrentPopup = popup;
_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;
}
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;
}
hor += (.)i.NameMeasurement.x + 10;
var tsize = rt.Platform.MeasureTextSize(i.key, "Font_Normal");
xOffset += tsize[0] + 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;
public override bool OnScroll(TGRuntime rt, float pOffset) => false;
if(_CurrentPopup != null && _CurrentPopup.Category != i)
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)
{
_CurrentPopup.Category = i;
_CurrentPopup.[Friend]X = hor-10;
val.Component(i.0);
val.Linebreak();
}
return this;
}
hor += (.)i.NameMeasurement.x + 10;
}
_SelectedPopup = null;
};
return null;
}
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)
Categories.Add(i);
if(!_Items.ContainsKeyAlt<StringView>(pSection))
_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 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 override bool OnRightClick(TGRuntime rt, int32[2] mPos, System.Collections.List<(String, delegate void(TGRuntime))> pList) => true;
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;
using TheaterGui.Core;
using TheaterGui.Core.Structs;
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
}