diff --git a/ClassicalSharp/Commands/Commands.cs b/ClassicalSharp/Commands/Commands.cs index 1364aac5a..c6217135a 100644 --- a/ClassicalSharp/Commands/Commands.cs +++ b/ClassicalSharp/Commands/Commands.cs @@ -129,7 +129,11 @@ namespace ClassicalSharp.Commands { } public override void Execute(string[] args) { - foreach (IGameComponent comp in game.Components) { comp.Dispose(); } + if (args.Length == 1) { + game.Chat.Add("&e/client model: &cYou didn't specify a model name."); + } else { + game.LocalPlayer.SetModel(args[1]); + } } } diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj index b4b83c732..1bfeefeec 100644 --- a/src/ClassiCube.vcxproj +++ b/src/ClassiCube.vcxproj @@ -210,6 +210,8 @@ + + @@ -273,6 +275,8 @@ + + diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters index 21f341a01..fa7421eea 100644 --- a/src/ClassiCube.vcxproj.filters +++ b/src/ClassiCube.vcxproj.filters @@ -312,6 +312,12 @@ Header Files\Launcher + + Header Files\Launcher + + + Header Files\Launcher + @@ -557,5 +563,11 @@ Source Files\Launcher + + Source Files\Launcher + + + Source Files\Launcher + \ No newline at end of file diff --git a/src/LScreens.c b/src/LScreens.c new file mode 100644 index 000000000..6a87281d8 --- /dev/null +++ b/src/LScreens.c @@ -0,0 +1,149 @@ +#include "LScreens.h" +#include "LWidgets.h" +#include "Launcher.h" +#include "Gui.h" + +static void LScreen_NullFunc(struct LScreen* s) { } +static void LScreen_DrawAll(struct LScreen* s) { + struct LWidget* widget; + int i; + + for (i = 0; i < s->NumWidgets; i++) { + widget = s->Widgets[i]; + widget->VTABLE->Redraw(widget); + } +} + +static void LScreen_HoverWidget(struct LScreen* s, struct LWidget* w) { + if (!w) return; + w->Hovered = true; + if (w->VTABLE->OnHover) w->VTABLE->OnHover(w); +} + +static void LScreen_UnhoverWidget(struct LScreen* s, struct LWidget* w) { + if (!w) return; + w->Hovered = false; + if (w->VTABLE->OnUnhover) w->VTABLE->OnUnhover(w); +} + +CC_NOINLINE static void LScreen_Reset(struct LScreen* s) { + int i; + + s->Init = NULL; /* screens should always override this */ + s->Free = LScreen_NullFunc; + s->DrawAll = LScreen_DrawAll; + s->Tick = LScreen_NullFunc; + s->OnDisplay = LScreen_NullFunc; + s->HoverWidget = LScreen_HoverWidget; + s->UnhoverWidget = LScreen_UnhoverWidget; + + /* reset all widgets to unselected */ + for (i = 0; i < s->NumWidgets; i++) { + s->Widgets[i]->Hovered = false; + } +} + + +/*########################################################################################################################* +*---------------------------------------------------------Widget init-----------------------------------------------------* +*#########################################################################################################################*/ +CC_NOINLINE static void LScreen_Button(struct LScreen* s, struct LButton* w, int width, int height, const char* text, + uint8_t horAnchor, uint8_t verAnchor, int xOffset, int yOffset) { + String str = String_FromReadonly(text); + LButton_Init(w, width, height); + LButton_SetText(w, &str, &Launcher_TitleFont); + + w->Hovered = false; + s->Widgets[s->NumWidgets++] = (struct LWidget*)w; + LWidget_SetLocation(w, horAnchor, verAnchor, xOffset, yOffset); +} + +CC_NOINLINE static void LScreen_Label(struct LScreen* s, struct LLabel* w, const char* text, + uint8_t horAnchor, uint8_t verAnchor, int xOffset, int yOffset) { + String str = String_FromReadonly(text); + LLabel_Init(w); + LLabel_SetText(w, &str, &Launcher_TextFont); + + w->Hovered = false; + s->Widgets[s->NumWidgets++] = (struct LWidget*)w; + LWidget_SetLocation(w, horAnchor, verAnchor, xOffset, yOffset); +} + +CC_NOINLINE static void LScreen_Slider(struct LScreen* s, struct LSlider* w, int width, int height, + int initValue, int maxValue, BitmapCol progressCol, + uint8_t horAnchor, uint8_t verAnchor, int xOffset, int yOffset) { + LSlider_Init(w, width, height); + w->Value = initValue; w->MaxValue = maxValue; + w->ProgressCol = progressCol; + + w->Hovered = false; + s->Widgets[s->NumWidgets++] = (struct LWidget*)w; + LWidget_SetLocation(w, horAnchor, verAnchor, xOffset, yOffset); +} + +/*########################################################################################################################* +*--------------------------------------------------------SettingsScreen---------------------------------------------------* +*#########################################################################################################################*/ +/*static struct SettingsScreen { + LScreen_Layout + struct LWidget* Widgets[7]; + struct LButton BtnUpdates, BtnMode, BtnColours, BtnBack; + struct LLabel LblUpdates, LblMode, LblColours; +} SettingsScreen_Instance; + +static void SettingsScreen_InitWidgets(struct SettingsScreen* s) { + struct LScreen* s_ = (struct LScreen*)s; + + LScreen_Button(s_, &s->BtnUpdates, 110, 35, "Updates", + ANCHOR_CENTRE, ANCHOR_CENTRE, -135, -120); + LScreen_Label(s_, &s->LblUpdates, "&eGet the latest stuff", + ANCHOR_CENTRE, ANCHOR_CENTRE, 10, -120); + + LScreen_Button(s_, &s->BtnMode, 110, 35, "Mode", + ANCHOR_CENTRE, ANCHOR_CENTRE, -135, -70); + LScreen_Label(s_, &s->LblMode, "&eChange the enabled features", + ANCHOR_CENTRE, ANCHOR_CENTRE, 55, -70); + + LScreen_Button(s_, &s->BtnColours, 110, 35, "Colours", + ANCHOR_CENTRE, ANCHOR_CENTRE, -135, -20); + LScreen_Label(s_, &s->LblColours, "&eChange how the launcher looks", + ANCHOR_CENTRE, ANCHOR_CENTRE, 65, -20); + + LScreen_Button(s_, &s->BtnBack, 80, 35, "Back", + ANCHOR_CENTRE, ANCHOR_CENTRE, 0, 170); +} + +static void SwitchToChooseMode(void* w, int x, int y) { + Launcher_SetScreen(ChooseModeScreen_MakeInstance(false)); +} +static void SwitchToUpdates(void* w, int x, int y) { + Launcher_SetScreen(UpdatesScreen_MakeInstance()); +} +static void SwitchToColours(void* w, int x, int y) { + Launcher_SetScreen(ColoursScreen_MakeInstance()); +} +static void SwitchToMain(void* w, int x, int y) { + Launcher_SetScreen(MainScreen_MakeInstance()); +} + +static void SettingsScreen_Init(struct LScreen* s_) { + struct SettingsScreen* s = (struct SettingsScreen*)s_; + if (!s->NumWidgets) SettingsScreen_InitWidgets(s); + + s->BtnColours.Hidden = Launcher_ClassicBackground; + s->LblColours.Hidden = Launcher_ClassicBackground; + + s->BtnMode.OnClick = SwitchToChooseMode; + s->BtnUpdates.OnClick = SwitchToUpdates; + s->BtnColours.OnClick = SwitchToColours; + s->BtnBack.OnClick = SwitchToMain; + s->DrawAll(s); +} + +struct LScreen* SettingsScreen_MakeInstance(void) { + struct SettingsScreen* s = &SettingsScreen_Instance; + LScreen_Reset((struct LScreen*)s); + s->Init = SettingsScreen_Init; + return (struct LScreen*)s; +} +*/ \ No newline at end of file diff --git a/src/LScreens.h b/src/LScreens.h new file mode 100644 index 000000000..1f97a4aa2 --- /dev/null +++ b/src/LScreens.h @@ -0,0 +1,41 @@ +#ifndef CC_LSCREENS_H +#define CC_LSCREENS_H +#include "AsyncDownloader.h" +#include "String.h" +/* Implements screens/menus for the launcher. + Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +*/ +struct LWidget; +struct LScreen; + +typedef void (*LScreen_Func)(struct LScreen* s); +typedef void (*LWidget_Func)(struct LScreen* s, struct LWidget* w); + +#define LScreen_Layout \ + LScreen_Func Init; /* Initialises widgets and other data. */ \ + LScreen_Func Free; /* Cleans up all native resources. */ \ + LScreen_Func DrawAll; /* Redraws all widgets. */ \ + LScreen_Func Tick; /* Repeatedly called multiple times every second. */ \ + LScreen_Func OnDisplay; /* Called when framebuffer is about to be displayed. */ \ + LWidget_Func HoverWidget; /* Called when mouse is moved over a given widget. */ \ + LWidget_Func UnhoverWidget; /* Called when the mouse is moved away from a previously hovered widget. */ \ + struct LWidget* HoveredWidget; /* Widget the mouse is currently hovering over. */ \ + int NumWidgets; /* Number of widgets actually used. */ + +struct LScreen { + LScreen_Layout + /* All widgets in the screen. */ + /* NOTE: This is vriable sized, so must be the last member. */ + /* (e.g. derived screens might use: struct LWidget* Widgets[10];) */ + struct LWidget** Widgets; +}; + +struct LScreen* ChooseModeScreen_MakeInstance(void); +struct LScreen* ColoursScreen_MakeInstance(void); +struct LScreen* DirectConnectScreen_MakeInstance(void); +struct LScreen* MainScreen_MakeInstance(void); +struct LScreen* ResourcesScreen_MakeInstance(void); +struct LScreen* ServersScreen_MakeInstance(void); +struct LScreen* SettingsScreen_MakeInstance(void); +struct LScreen* UpdateScreen_MakeInstance(void); +#endif diff --git a/src/LWeb.c b/src/LWeb.c new file mode 100644 index 000000000..2cdf4af22 --- /dev/null +++ b/src/LWeb.c @@ -0,0 +1,277 @@ +#include "LWeb.h" +#include "Platform.h" + +static void LWebTask_Reset(struct LWebTask* task) { + task->Completed = false; + task->Success = false; + task->Start = DateTime_CurrentUTC_MS(); +} + +void LWebTask_StartAsync(struct LWebTask* task) { + LWebTask_Reset(task); + task->Begin(task); +} + +void LWebTask_Tick(struct LWebTask* task) { + struct AsyncRequest req; + int delta; + if (task->Completed) return; + + if (!AsyncDownloader_Get(&task->Identifier, &req)) return; + delta = (int)(DateTime_CurrentUTC_MS() - task->Start); + Platform_Log2("%s took %i", &task->Identifier, &delta); + + task->Completed = true; + task->Success = req.ResultData && req.ResultSize; + if (task->Success) task->Handle(task, req.ResultData, req.ResultSize); +} + +static void LWebTask_DefaultBegin(struct LWebTask* task) { + AsyncDownloader_GetData(&task->URL, false, &task->Identifier); +} + +/*protected static JsonObject ParseJson(Request req) { + JsonContext ctx = new JsonContext(); + ctx.Val = (string)req.Data; + return (JsonObject)Json.ParseStream(ctx); +} + +public sealed class GetCSRFTokenTask : WebTask { + public GetCSRFTokenTask() { + identifier = "CC get login"; + uri = "https://www.classicube.net/api/login/"; + } + public string Token; + + protected override void Handle(Request req) { + JsonObject data = ParseJson(req); + Token = (string)data["token"]; + } +} + +public sealed class SignInTask : WebTask { + public SignInTask() { + identifier = "CC post login"; + uri = "https://www.classicube.net/api/login/"; + } + public string Username, Password, Token, Error; + + protected override void Begin() { + string data = String.Format( + "username={0}&password={1}&token={2}", + Uri.EscapeDataString(Username), + Uri.EscapeDataString(Password), + Uri.EscapeDataString(Token) + ); + Game.Downloader.AsyncPostString(uri, false, identifier, data); + } + + protected override void Handle(Request req) { + JsonObject data = ParseJson(req); + Error = GetLoginError(data); + Username = (string)data["username"]; + } + + static string GetLoginError(JsonObject obj) { + List errors = (List)obj["errors"]; + if (errors.Count == 0) return null; + + string err = (string)errors[0]; + if (err == "username" || err == "password") return "Wrong username or password"; + if (err == "verification") return "Account verification required"; + return "Unknown error occurred"; + } +} + + +public class ServerListEntry { + public string Hash, Name, Players, MaxPlayers, Flag; + public string Uptime, IPAddress, Port, Mppass, Software; + public bool Featured; +} + +public sealed class FetchServerTask : WebTask { + public FetchServerTask(string user, string hash) { + Username = user; + identifier = "CC get servers"; + uri = "https://www.classicube.net/api/server/" + hash; + } + + public static ServerListEntry ParseEntry(JsonObject obj) { + ServerListEntry entry = new ServerListEntry(); + entry.Hash = (string)obj["hash"]; + entry.Name = (string)obj["name"]; + entry.Players = (string)obj["players"]; + entry.MaxPlayers = (string)obj["maxplayers"]; + entry.Uptime = (string)obj["uptime"]; + entry.Mppass = (string)obj["mppass"]; + entry.IPAddress = (string)obj["ip"]; + entry.Port = (string)obj["port"]; + entry.Software = (string)obj["software"]; + + if (obj.ContainsKey("featured")) { + entry.Featured = (bool)obj["featured"]; + } + if (obj.ContainsKey("country_abbr")) { + entry.Flag = Utils.ToLower((string)obj["country_abbr"]); + } + return entry; + } + + public string Username; + public ClientStartData Info; + + protected override void Handle(Request req) { + JsonObject root = ParseJson(req); + List list = (List)root["servers"]; + + JsonObject obj = (JsonObject)list[0]; + ServerListEntry entry = ParseEntry(obj); + Info = new ClientStartData(Username, entry.Mppass, entry.IPAddress, entry.Port, entry.Name); + } +} + +public sealed class FetchServersTask : WebTask { + public FetchServersTask() { + identifier = "CC get servers"; + uri = "https://www.classicube.net/api/servers"; + } + + public List Servers; + protected override void Reset() { + base.Reset(); + Servers = new List(); + } + + protected override void Handle(Request req) { + JsonObject root = ParseJson(req); + List list = (List)root["servers"]; + + for (int i = 0; i < list.Count; i++) { + JsonObject obj = (JsonObject)list[i]; + ServerListEntry entry = FetchServerTask.ParseEntry(obj); + Servers.Add(entry); + } + } +} + + +public class Build { + public DateTime TimeBuilt; + public string DirectXPath, OpenGLPath; + public int DirectXSize, OpenGLSize; + public string Version; +} + +public sealed class UpdateCheckTask : WebTask { + public UpdateCheckTask() { + identifier = "CC update check"; + uri = "http://cs.classicube.net/builds.json"; + } + + public Build LatestDev, LatestStable; + + protected override void Handle(Request req) { + JsonObject data = ParseJson(req); + JsonObject latest = (JsonObject)data["latest"]; + + LatestDev = MakeBuild(latest, false); + JsonObject releases = (JsonObject)data["releases"]; + + DateTime releaseTime = DateTime.MinValue; + foreach(KeyValuePair pair in releases) { + Build build = MakeBuild((JsonObject)pair.Value, true); + if (build.TimeBuilt < releaseTime) continue; + + LatestStable = build; + releaseTime = build.TimeBuilt; + } + } + + static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + Build MakeBuild(JsonObject obj, bool release) { + Build build = new Build(); + string timeKey = release ? "release_ts" : "ts"; + double rawTime = Double.Parse((string)obj[timeKey], CultureInfo.InvariantCulture); + build.TimeBuilt = epoch.AddSeconds(rawTime).ToLocalTime(); + + build.DirectXSize = Int32.Parse((string)obj["dx_size"]); + build.DirectXPath = (string)obj["dx_file"]; + build.OpenGLSize = Int32.Parse((string)obj["ogl_size"]); + build.OpenGLPath = (string)obj["ogl_file"]; + if (obj.ContainsKey("version")) + build.Version = (string)obj["version"]; + return build; + } +} + +public sealed class UpdateDownloadTask : WebTask { + public UpdateDownloadTask(string dir) { + identifier = "CC update download"; + uri = "http://cs.classicube.net/" + dir; + } + + public byte[] ZipFile; + + protected override void Begin() { + Game.Downloader.AsyncGetData(uri, false, identifier); + } + + protected override void Handle(Request req) { + ZipFile = (byte[])req.Data; + } +} + +public sealed class UpdateCClientTask : WebTask { + public UpdateCClientTask(string file) { + identifier = "CC CClient download"; + uri = "http://cs.classicube.net/c_client/latest/" + file; + } + + public byte[] File; + + protected override void Begin() { + Game.Downloader.AsyncGetData(uri, false, identifier); + } + + protected override void Handle(Request req) { + File = (byte[])req.Data; + } +} + + +public sealed class FetchFlagsTask : WebTask { + public FetchFlagsTask() { + identifier = "CC get flag"; + } + + public bool PendingRedraw; + public static int DownloadedCount; + public static List Flags = new List(); + public static List Bitmaps = new List(); + + public void AsyncGetFlag(string flag) { + for (int i = 0; i < Flags.Count; i++) { + if (Flags[i] == flag) return; + } + Flags.Add(flag); + } + + protected override void Begin() { + if (Flags.Count == DownloadedCount) return; + uri = "http://static.classicube.net/img/flags/" + Flags[DownloadedCount] + ".png"; + Game.Downloader.AsyncGetImage(uri, false, identifier); + } + + protected override void Handle(Request req) { + Bitmap bmp = (Bitmap)req.Data; + FastBitmap fast = new FastBitmap(bmp, true, true); + Bitmaps.Add(fast); + DownloadedCount++; + PendingRedraw = true; + + Reset(); + Begin(); + } +} +}*/ \ No newline at end of file diff --git a/src/LWeb.h b/src/LWeb.h new file mode 100644 index 000000000..0e025ca32 --- /dev/null +++ b/src/LWeb.h @@ -0,0 +1,30 @@ +#ifndef CC_LWEB_H +#define CC_LWEB_H +#include "AsyncDownloader.h" +#include "String.h" +/* Implements asynchronous web tasks for the launcher. + Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +*/ + +struct LWebTask; +struct LWebTask { + /* Whether the task has finished executing. */ + bool Completed; + /* Whether the task completed successfully. */ + bool Success; + ReturnCode Res; + /* Unique identifier for this web task. */ + String Identifier; + /* URL this task is downloading from/uploading to. */ + String URL; + /* Point in time this task was started at. */ + TimeMS Start; + /* Function called to begin downloading/uploading. */ + void (*Begin)(struct LWebTask* task); + /* Called when task successfully downloaded/uploaded data. */ + void (*Handle)(struct LWebTask* task, uint8_t* data, uint32_t len); +}; + +void LWebTask_StartAsync(struct LWebTask* task); +void LWebTask_Tick(struct LWebTask* task); +#endif diff --git a/src/LWidgets.c b/src/LWidgets.c index 4f34c16b7..e2a8b0f90 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -20,8 +20,8 @@ void LWidget_CalcPosition(void* widget) { void LWidget_Reset(void* widget) { struct LWidget* w = widget; - w->Active = false; - w->Hidden = false; + w->Hovered = false; + w->Hidden = false; w->X = 0; w->Y = 0; w->Width = 0; w->Height = 0; w->HorAnchor = ANCHOR_MIN; @@ -30,7 +30,7 @@ void LWidget_Reset(void* widget) { w->TabSelectable = false; w->OnClick = NULL; - w->Redraw = NULL; + w->VTABLE = NULL; } @@ -52,12 +52,12 @@ static void LButton_DrawBackground(struct LButton* w) { BitmapCol col; if (Launcher_ClassicBackground) { - col = w->Active ? activeCol : inactiveCol; + col = w->Hovered ? activeCol : inactiveCol; Gradient_Noise(&Launcher_Framebuffer, col, 8, w->X + BTN_BORDER, w->Y + BTN_BORDER, w->Width - 2 * BTN_BORDER, w->Height - 2 * BTN_BORDER); } else { - col = w->Active ? Launcher_ButtonForeActiveCol : Launcher_ButtonForeCol; + col = w->Hovered ? Launcher_ButtonForeActiveCol : Launcher_ButtonForeCol; Gradient_Vertical(&Launcher_Framebuffer, LButton_Expand(col, 8), LButton_Expand(col, -8), w->X + BTN_BORDER, w->Y + BTN_BORDER, w->Width - 2 * BTN_BORDER, w->Height - 2 * BTN_BORDER); @@ -88,14 +88,14 @@ static void LButton_DrawHighlight(struct LButton* w) { BitmapCol highlightCol; if (Launcher_ClassicBackground) { - highlightCol = w->Active ? activeCol : inactiveCol; + highlightCol = w->Hovered ? activeCol : inactiveCol; Drawer2D_Clear(&Launcher_Framebuffer, highlightCol, w->X + BTN_BORDER * 2, w->Y + BTN_BORDER, w->Width - BTN_BORDER * 4, BTN_BORDER); Drawer2D_Clear(&Launcher_Framebuffer, highlightCol, w->X + BTN_BORDER, w->Y + BTN_BORDER * 2, BTN_BORDER, w->Height - BTN_BORDER * 4); - } else if (!w->Active) { + } else if (!w->Hovered) { Drawer2D_Clear(&Launcher_Framebuffer, Launcher_ButtonHighlightCol, w->X + BTN_BORDER * 2, w->Y + BTN_BORDER, w->Width - BTN_BORDER * 4, BTN_BORDER); @@ -116,17 +116,22 @@ static void LButton_Redraw(void* widget) { LButton_DrawBorder(w); LButton_DrawHighlight(w); - if (!w->Active) Drawer2D_Cols['f'] = Drawer2D_Cols['7']; + if (!w->Hovered) Drawer2D_Cols['f'] = Drawer2D_Cols['7']; Drawer2D_DrawText(&Launcher_Framebuffer, &args, w->X + xOffset / 2, w->Y + yOffset / 2); - if (!w->Active) Drawer2D_Cols['f'] = Drawer2D_Cols['F']; + + if (!w->Hovered) Drawer2D_Cols['f'] = Drawer2D_Cols['F']; + Launcher_Dirty = true; } +static struct LWidgetVTABLE lbutton_VTABLE = { + LButton_Redraw, LButton_Redraw, LButton_Redraw, NULL, NULL +}; void LButton_Init(struct LButton* w, int width, int height) { Widget_Reset(w); + w->VTABLE = &lbutton_VTABLE; w->TabSelectable = true; w->Width = width; w->Height = height; - w->Redraw = LButton_Redraw; String_InitArray(w->Text, w->__TextBuffer); } @@ -186,7 +191,7 @@ bool LInput_Backspace(struct LInput* w) { if (w->CaretPos == -1) w->CaretPos = 0; } - if (w->TextChanged) TextChanged(w); + if (w->TextChanged) w->TextChanged(w); if (w->CaretPos >= w->Text.length) w->CaretPos = -1; return true; } @@ -225,11 +230,15 @@ static void LLabel_Redraw(void* widget) { DrawTextArgs_Make(&args, &w->Text, &w->Font, true); Drawer2D_DrawText(&Launcher_Framebuffer, &args, w->X, w->Y); + Launcher_Dirty = true; } +static struct LWidgetVTABLE llabel_VTABLE = { + LLabel_Redraw, NULL, NULL, NULL, NULL +}; void LLabel_Init(struct LLabel* w) { Widget_Reset(w); - w->Redraw = LLabel_Redraw; + w->VTABLE = &llabel_VTABLE; String_InitArray(w->Text, w->__TextBuffer); } @@ -287,11 +296,15 @@ static void LSlider_Redraw(void* widget) { Drawer2D_Clear(&Launcher_Framebuffer, w->ProgressCol, w->X, w->Y, (int)(w->Width * w->Value / w->MaxValue), w->Height); + Launcher_Dirty = true; } +static struct LWidgetVTABLE lslider_VTABLE = { + LSlider_Redraw, NULL, NULL, NULL, NULL +}; void LSlider_Init(struct LSlider* w, int width, int height) { Widget_Reset(w); + w->VTABLE = &lslider_VTABLE; w->Width = width; w->Height = height; - w->Redraw = LSlider_Redraw; w->MaxValue = 100; } diff --git a/src/LWidgets.h b/src/LWidgets.h index 032bcba9e..f3fe8b9b2 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -7,15 +7,30 @@ Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 */ +struct LWidgetVTABLE { + /* Called to redraw contents of this widget */ + void (*Redraw)(void* widget); + /* Called when mouse hovers over this widget. */ + void (*OnHover)(void* widget); + /* Called when mouse moves away from this widget. */ + void (*OnUnhover)(void* widget); + /* Called when mouse clicks on this widget. */ + /* NOTE: This function is just for general widget behaviour. */ + /* OnClick callback is for per-widget instance behaviour. */ + void (*OnSelected)(void* widget); + /* Called when mouse clicks on another widget. */ + void (*OnUnselected)(void* widget); +}; + #define LWidget_Layout \ + struct LWidgetVTABLE* VTABLE; /* General widget functions */ \ int X, Y, Width, Height; /* Top left corner, and dimensions, of this widget */ \ - bool Active; /* Whether this widget is currently being moused over*/ \ + bool Hovered; /* Whether this widget is currently being moused over*/ \ bool Hidden; /* Whether this widget is hidden from view */ \ bool TabSelectable; /* Whether this widget gets selected when pressing tab */ \ uint8_t HorAnchor, VerAnchor; /* Specifies the reference point for when this widget is resized */ \ int XOffset, YOffset; /* Offset from the reference point */ \ void (*OnClick)(void* widget, int x, int y); /* Called when widget is clicked */ \ - void (*Redraw)(void* widget); /* Called to redraw contents of this widget */ /* Represents an individual 2D gui component in the launcher. */ struct LWidget { LWidget_Layout }; diff --git a/src/Launcher.h b/src/Launcher.h index be614f50b..fdad3ffc5 100644 --- a/src/Launcher.h +++ b/src/Launcher.h @@ -17,6 +17,8 @@ extern Rect2D Launcher_DirtyArea; extern Bitmap Launcher_Framebuffer; /* Whether to use stone tile background like minecraft.net. */ extern bool Launcher_ClassicBackground; +/* Default font for buttons and labels. */ +extern FontDesc Launcher_TitleFont, Launcher_TextFont; /* Whether at the next tick, the launcher window should proceed to stop displaying frames and subsequently exit. */ extern bool Launcher_ShouldExit;