-
Notifications
You must be signed in to change notification settings - Fork 8
MaslOS2 GUI‐Framework
Here are the most important things surrounding the GUI-Framework in MaslOS2.
If you have not read the Application Development Guide for MaslOS2, please read it now.
(This page is still heavily WIP)
For now the best way would be just to look at some GUI programs that are already implemented in MaslOS2.
For GUI Framework dev important classes are:
- The Window class
- The GuiInstance class
- The Component classes
Please also take a closer look at important functions related to windows if you have not.
Components all derive from a Base Component class. All Components have Base Attributes and Specific Attributes.
NOTE: They should also have a unique ID!
Some IDs are reserved or used by default, which you should change.
The screen component has a default id of 1234
.
Button Components create Subcomponents.
- The inner rectangle component will have the id of
123401
. - The inner text component will have the id of
123402
. - When creating buttons, you should update the Subcomponent ids.
You can create a component in two ways:
An Example would be:
// Create the component
// NOTE: You will need to give it a unique ID!
gui->CreateComponentWithId(123456, ComponentType::BUTTON);
ButtonComponent* button = (ButtonComponent*)gui->GetComponentFromId(123456);
// Set the position and size
button->position.x = 52;
button->position.y = 0;
button->size.FixedX = 50;
button->size.FixedY = 20;
// Set the text
_Free(button->textComp->text);
button->textComp->text = StrCopy("Click me");
An Example would be:
// Create the component
TextComponent* txt = new TextComponent(
gui->screen, // parent
Colors.black, // background color
Colors.white, // foreground color
"Hello!\nThis is an amazing test.", // text, which has several lines
Position(200, 90) // a position
);
// Add the component to the main screen
gui->screen->children->Add(txt);
In contrast to most libm classes, components don't have a Free
function, but rather a Destroy
function.
The important difference is that Destroy
also deletes the instance itself!
You can delete a component in two ways:
The function looks like this:
bool Destroy(bool destroyChildren, void (*callBackFunc)(BaseComponent* comp));
Here is an example of creating and deleting a component:
// Create the component
TextComponent* txt = new TextComponent(
gui->screen, // parent
Colors.black, // background color
Colors.white, // foreground color
"Hello!\nThis is an amazing test.", // text, which has several lines
Position(200, 90) // a position
);
// Add the component to the main screen
gui->screen->children->Add(txt);
// ...
// Destroy
txt->Destroy(true, NULL);
Alternative you can also delete components from the GUI Instance using the ID.
There the function looks similar:
bool GuiInstance::DeleteComponentWithId(int64_t id, bool destroyChildren);
Here is an example of creating and deleting a component:
// Create the component
// NOTE: You will need to give it a unique ID!
gui->CreateComponentWithId(123456, ComponentType::BUTTON);
ButtonComponent* button = (ButtonComponent*)gui->GetComponentFromId(123456);
// Set the position and size
button->position.x = 52;
button->position.y = 0;
button->size.FixedX = 50;
button->size.FixedY = 20;
// Set the text
_Free(button->textComp->text);
button->textComp->text = StrCopy("Click me");
// ...
// Destroy
gui->DeleteComponentWithId(123456, true);
All Windows with a GUI will need to process events and display things and changes, which is why you will need a Render-Loop of some sorts.
The Gui Instance comes with 2 methods:
void Update();
// Will just process the packets/updates received from the desktop.
// This includes:
// - Keyboard Inputs
// - Mouse Inputs
// - Window Updates (Moved, Resized, Activated, Closed, etc)
void Render(bool update);
// This will go through each component, check for changes and then render them.
// This will also send those updates to the desktop.
// NOTE: If you already call the `Update` function, you can set update to false.
You can have two kinds of Render-Loops:
This is useful if your application has barely any changes and uses events to do things.
while (!CheckForWindowClosed(window))
{
gui->Render(true);
programWaitMsg();
}
This is useful if you have things you might need to update/move every frame.
while (!CheckForWindowClosed(window))
{
guiInstance->Update();
UpdateSizes(); // External method
HandleFrame(); // External method
guiInstance->Render(false);
programWaitMsg();
}
Most apps will not need to render at thousands of frames per second.
Especially if the app is mostly waiting for any sort of user interaction.
If that is the case you can use programWaitMsg();
between frames to wait until any sort of interaction occurs.
If your app does something in fixed time intervals, you can use programWait(int timeMs);
.
Or if it is supposed to run as fast as possible, you can use programYield();
.
Just please remember to not be too greedy with the processor time as there are other apps trying to run as well!
class GuiInstance
{
GuiComponentStuff::ScreenComponent* screen;
List<GuiComponentStuff::BaseComponent*>* allComponents;
MouseState mouseState;
GuiInstance(Window* window);
void Init();
void Free();
void Render(bool update);
void Update();
GuiComponentStuff::BaseComponent* GetComponentFromId(uint64_t id);
GuiComponentStuff::BaseComponent* GetChildFromComponentWithId(uint64_t id, int index);
int GetIndexOfChildFromComponentWithId(uint64_t id, uint64_t childId);
bool RemoveChildFromComponentWithId(uint64_t id, int index);
bool DeleteComponentWithId(int64_t id, bool destroyChildren);
bool CreateComponentWithId(int64_t id, GuiComponentStuff::ComponentType type);
bool CreateComponentWithIdAndParent(int64_t id, GuiComponentStuff::ComponentType type, int64_t parentId);
bool SetBaseComponentAttribute(int64_t id, GuiInstanceBaseAttributeType type, uint64_t val);
bool SetSpecificComponentAttribute(int64_t id, int32_t type, uint64_t val);
uint64_t GetBaseComponentAttribute(int64_t id, GuiInstanceBaseAttributeType type);
uint64_t GetSpecificComponentAttribute(int64_t id, int32_t type);
int GetSpecificComponentAttributeSize(int64_t id, int32_t type);
bool SetActiveScreenFromId(int64_t id);
bool ComponentAddChild(int64_t id, GuiComponentStuff::BaseComponent* childComp);
bool ComponentRemoveChild(int64_t id, int64_t childId);
};
enum class GuiInstanceBaseAttributeType : int32_t
{
POSITION_X = 10,
POSITION_Y = 11,
SIZE_FIXED_X = 20,
SIZE_FIXED_Y = 21,
SIZE_SCALED_X = 22,
SIZE_SCALED_Y = 23,
SIZE_IS_FIXED_X = 24,
SIZE_IS_FIXED_Y = 25,
ACTUAL_SIZE_X = 30,
ACTUAL_SIZE_Y = 31,
ID = 40,
PARENT_ID = 41,
IS_HIDDEN = 42,
};
You can learn how to create a simple program here.
Here is the code for a simple GUI program:
#include <libm/gui/guiInstance.h>
#include <libm/gui/guiStuff/components/rectangle/rectangleComponent.h>
#include <libm/wmStuff/wmStuff.h>
#include <libm/cstrTools.h>
#include <libm/heap/heap.h>
using namespace GuiComponentStuff;
int main(int argc, char** argv)
{
initWindowManagerStuff();
// Request Window
Window* window = requestWindow();
if (window == NULL)
return -1;
// Set the title
_Free(window->Title);
window->Title = StrCopy("Hello World!");
// Set the width and height to 200x200
window->Dimensions.width = 200;
window->Dimensions.height = 200;
// Actually send the changes to the desktop
setWindow(window);
// Create the GUI Instance and initialize it
GuiInstance* gui = new GuiInstance(window);
testGui->Init();
// Create a purple rectangle with the size 60x20 which is a child of the main screen component.
RectangleComponent* testRect = new RectangleComponent(Colors.purple, ComponentSize(60, 20), gui->screen);
// Set the position to (100, 20)
testRect->position = Position(100, 20);
// Add the rectangle to the actual screen component.
gui->screen->children->Add(testRect);
// Our program should run as long as our main window is open
while (!CheckForWindowClosed(window))
{
// Update and render the frame
gui->Render(true);
// Wait until we get a message
programWaitMsg();
}
return 0;
}
This component should not exist in it of itself but all components derive from it.
The class can is located in <libm/gui/guiStuff/components/base/baseComponent.h>
.
It looks like this:
class BaseComponent
{
// Basic Attributes
BaseComponent* parent;
ComponentSize size;
Position position;
ComponentType componentType;
ComponentRenderer* renderer;
bool hidden;
int64_t id;
// Function Pointers, which can be overridden from the outside too
void (*RenderFunc)(void* bruh, Field);
void (*CheckUpdatesFunc)(void* bruh);
void (*MouseClickedFunc)(void* bruh, MouseClickEventInfo);
void (*KeyHitFunc)(void* bruh, KeyHitEventInfo);
void (*DestroyFunc)(void* bruh, bool, void (*)(BaseComponent* comp));
ComponentSize (*GetActualComponentSizeFunc)(void* bruh);
bool (*SetAttributeFunc)(void* bruh, int32_t, uint64_t);
uint64_t (*GetAttributeFunc)(void* bruh, int32_t);
int (*GetAttributeSizeFunc)(void* bruh, int32_t);
// The actual functions
ComponentSize GetActualComponentSize();
Position GetAbsoluteComponentPosition();
void* GetGuiInstance();
void* GetScreen();
bool IsVisible();
void MouseClicked(MouseClickEventInfo info);
void KeyHit(KeyHitEventInfo info);
void Render(Field field);
void CheckUpdates();
bool Destroy(bool destroyChildren, void (*callBackFunc)(BaseComponent* comp));
Field GetFieldWithPos();
};
The Component Type enum looks like this:
enum ComponentType : uint8_t
{
NONE = 0,
SCREEN = 1,
RECT = 2,
BOX = 3,
TEXT = 4,
BUTTON = 5,
TEXTFIELD = 6,
IMAGE_RECT = 7,
CANVAS = 8,
ADVANCED_TEXT = 9
};
struct Position
{
int x, y;
// Position in pixels.
Position(int x, int y);
// Position at x,y.
Position();
// Position at 0,0.
Position(ComponentSize fixedSize);
// Position taking the fixed size properties of the provided component size.
};
Important bc it can scale!
NOTE: you can change if any side is fixed or scaled at any point and you can have one size be fixed and one be scaled.
struct ComponentSize
{
bool IsXFixed, IsYFixed;
// Is The (X/Y) Size fixed or does it depend on the parent.
int FixedX, FixedY;
// Size in pixels.
double ScaledX, ScaledY;
// Size in percent of the parent size.
// 1.0 -> 100%, 0 -> 0%, 0.5 -> 50%.
ComponentSize(int x, int y);
// Constructor that creates a fully fixed size.
ComponentSize(double x, double y) // percent like 0.9 for 90%
// Constructor that creates a fully scaled size.
ComponentSize();
// Will create a fixed size of 0x0.
};
The class can is located in <libm/gui/guiStuff/components/rectangle/rectangleComponent.h>
.
It looks like this:
class RectangleComponent
{
uint32_t fillColor;
uint32_t oldFillColor;
RectangleComponent(uint32_t fillColor, ComponentSize size, BaseComponent* parent);
};
The class can is located in <libm/gui/guiStuff/components/box/boxComponent.h>
.
It looks like this:
class BoxComponent
{
uint32_t backgroundColor = 0;
List<BaseComponent*>* children;
List<Field>* childrenFields;
List<bool>* childrenHidden;
BoxComponent(BaseComponent* parent, ComponentSize size, uint32_t bgCol);
};
The class can is located in <libm/gui/guiStuff/components/button/buttonComponent.h>
.
It looks like this:
class ButtonComponent
{
BoxComponent* actualButtonStuff;
TextComponent* textComp;
RectangleComponent* rectComp;
uint32_t textColDef;
uint32_t textColHover;
uint32_t textColClick;
uint32_t bgColDef;
uint32_t bgColHover;
uint32_t bgColClick;
bool stickToDefaultColor = false;
bool mouseHover = false;
bool mouseClick = false;
void (*mouseClickedCallBack)(BaseComponent*, MouseClickEventInfo);
void (*keyHitCallBack)(BaseComponent*, KeyHitEventInfo);
void* OnMouseClickHelp;
void (*OnMouseClickedCallBack)(void*, BaseComponent*, MouseClickEventInfo);
ButtonComponent(const char* text, uint32_t textColDef, uint32_t textColHover, uint32_t textColClick, uint32_t bgColDef, uint32_t bgColHover,
};
The class can is located in <libm/gui/guiStuff/components/text/textComponent.h>
.
It looks like this:
class TextComponent
{
uint32_t bgColor;
uint32_t fgColor;
uint32_t bgColorOld;
uint32_t fgColorOld;
const char* text;
const char* oldText;
bool center = false;
bool oldCenter = false;
bool useFixedSize = false;
TextComponent(BaseComponent* parent, uint32_t bgColor, uint32_t fgColor, const char* text, Position position);
void MouseClicked(MouseClickEventInfo info);
void KeyHit(KeyHitEventInfo info);
void Render(Field field);
void CheckUpdates();
void Destroy(bool destroyChildren, void (*callBackFunc)(BaseComponent* comp));
ComponentSize GetActualComponentSize();
bool SetAttribute(int32_t type, uint64_t val);
uint64_t GetAttribute(int32_t type);
int GetAttributeSize(int32_t type);
};
The class can is located in <libm/gui/guiStuff/components/advancedText/advancedTextComponent.h>
.
It looks like this:
class AdvancedTextComponent
{
AdvancedTextComponent(uint32_t foregroundColor, uint32_t backgroundColor, ComponentSize size, BaseComponent* parent);
bool redirectToSerial;
int32_t scrollX, scrollY, oldScrollX, oldScrollY, oldHeight, oldWidth;
uint32_t backgroundColor;
uint32_t foregroundColor;
List<List<ConsoleChar>*> textData;
ConsoleChar* tempPixels;
ConsoleChar* tempPixels2;
void WriteText(const char* text);
bool RenderCalled;
void Reload();
void DoRender();
void RenderCharChanges();
void Clear();
void SetWindow(Window* window);
void Println();
void Print(const char* msg);
void Print(const char* chrs, const char* var);
void Print(char chr);
void Print(const char* chrs, uint32_t col);
void Print(const char* chrs, const char* var, uint32_t col);
void DeleteLastCharInLine();
void Println(const char* msg);
void Println(const char* chrs, const char* var);
void Println(const char* chrs, uint32_t col);
void Println(const char* chrs, const char* var, uint32_t col);
};
The class can is located in <libm/gui/guiStuff/components/textField/textFieldComponent.h>
.
It looks like this:
class TextFieldComponent
{
BoxComponent* actualTextFieldStuff;
TextComponent* textComp;
RectangleComponent* rectComp;
uint32_t textCol;
uint32_t bgCol;
bool mouseHover = false;
bool mouseClick = false;
void (*mouseClickedCallBack)(BaseComponent*, MouseClickEventInfo);
void (*keyHitCallBack)(BaseComponent*, KeyHitEventInfo);
void* AdvancedKeyHitCallBackHelp;
bool (*AdvancedKeyHitCallBack)(void* bruh, BaseComponent*, KeyHitEventInfo); // true if print
TextFieldComponent(uint32_t textCol, uint32_t bgCol, ComponentSize size, Position position, BaseComponent* parent);
};
The class can is located in <libm/gui/guiStuff/components/imageRect/imageRectangleComponent.h>
.
It looks like this:
class ImageRectangleComponent
{
const char* imagePath = "";
const char* oldPath = "";
ImageStuff::BitmapImage* image = NULL;
void RenderImg();
void GetImageFromPath(const char* path);
ImageRectangleComponent(const char* path, ComponentSize size, BaseComponent* parent);
};
The class can is located in <libm/gui/guiStuff/components/canvas/canvasComponent.h>
.
It looks like this:
class CanvasComponent
{
uint32_t bgColor;
PSF1_FONT* font;
CanvasComponent(uint32_t bgColor, ComponentSize size, BaseComponent* parent);
void Clear();
void Clear(bool update);
void UpdateCanvas();
void UpdateCanvas(int x, int y);
void UpdateCanvas(int x, int y, int w, int h);
void UpdateCanvas(Field field);
void DrawPixel(int x, int y, uint32_t col, bool update);
void DrawBlob(int x, int y, uint32_t col, int size);
void DrawLine(int x1, int y1, int x2, int y2, uint32_t col, int size);
void DrawSquare(int x, int y, int size, uint32_t col, bool fillInside);
void DrawRect(int x, int y, int w, int h, uint32_t col, bool fillInside);
void DrawSquare(int x, int y, int size, uint32_t col, bool fillInside, bool update);
void DrawRect(int x, int y, int w, int h, uint32_t col, bool fillInside, bool update);
void DrawChar(int sX, int sY, uint32_t fg, int size, char c);
void DrawChar(int sX, int sY, uint32_t fg, uint32_t bg, int size, char c);
void DrawText(int sX, int sY, uint32_t fg, uint32_t bg, int size, const char* text);
void DrawText(int sX, int sY, uint32_t col, int size, const char* text);
void DrawTri(int x1, int y1, int x2, int y2, int x3, int y3, uint32_t col, int size);
};
The class can is located in <libm/gui/guiStuff/components/screenComponent/screenComponent.h>
.
It looks like this:
class ScreenComponent
{
public:
BaseComponent* selectedComponent = NULL;
Window* window;
List<BaseComponent*>* children;
uint32_t backgroundColor = 0;
BaseComponent* tempSelectedComponent = NULL;
List<Field>* finalUpdatedFields;
List<Field>* childrenFields;
List<bool>* childrenHidden;
GuiInstance* guiInstance;
};
The class can is located in <libm/gui/guiStuff/componentRenderer.h>
.
It looks like this:
class ComponentRenderer
{
ComponentFramebuffer* componentFrameBuffer;
uint32_t bgCol = 0;
PSF1_FONT* font;
ComponentRenderer(ComponentSize size);
void Resize(ComponentSize size, bool paint);
void Render(Position componentPosition, Field field, ComponentFramebuffer* to);
void Fill(uint32_t col);
void Fill(uint32_t col, Field field);
Position PrintString(char chr, Position pos, uint32_t fgCol, uint32_t bgCol, bool transparent);
Position PrintString(const char *chrs, Position pos, uint32_t fgCol, uint32_t bgCol, bool transparent, bool center);
Position PrintString(char chr, Position pos, uint32_t fgCol);
Position PrintString(const char *chrs, Position pos, uint32_t fgCol);
void Free();
};
MAAB also has access to the GUI Framework, same as in MaslOS.
You can find the documentation for that stuff in the Wiki for MaslOS.