diff --git a/src/LBackend.c b/src/LBackend.c index 9ad32f35b..1fa906d4d 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -89,6 +89,12 @@ void LBackend_CloseScreen(struct LScreen* s) { activeScreen = NULL; } void LBackend_WidgetRepositioned(struct LWidget* w) { } +void LBackend_MarkDirty(void* widget) { + struct LWidget* w = (struct LWidget*)widget; + pendingRedraw |= REDRAW_SOME; + w->dirty = true; +} + /* Marks the entire window as needing to be redrawn. */ static CC_NOINLINE void MarkAllDirty(void) { dirty_rect.X = 0; dirty_rect.Width = Launcher_Framebuffer.width; @@ -114,13 +120,41 @@ static void DrawBoxBounds(BitmapCol color, int x, int y, int width, int height) xBorder, height); } +static CC_NOINLINE void DrawWidget(struct LWidget* w) { + w->last.X = w->x; w->last.Width = w->width; + w->last.Y = w->y; w->last.Height = w->height; + + w->dirty = false; + w->VTABLE->Draw(w); + Launcher_MarkDirty(w->x, w->y, w->width, w->height); +} + static CC_NOINLINE void RedrawAll(void) { struct LScreen* s = activeScreen; int i; s->DrawBackground(s, &Launcher_Framebuffer); for (i = 0; i < s->numWidgets; i++) { - LWidget_Draw(s->widgets[i]); + DrawWidget(s->widgets[i]); + } + MarkAllDirty(); +} + +static CC_NOINLINE void RedrawDirty(void) { + struct LScreen* s = activeScreen; + struct LWidget* w; + int i; + + for (i = 0; i < s->numWidgets; i++) { + w = s->widgets[i]; + if (!w->dirty) continue; + + /* check if widget might need redrawing of background behind */ + if (!w->opaque || w->last.Width > w->width || w->last.Height > w->height) { + Launcher_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); + Launcher_MarkDirty(w->last.X, w->last.Y, w->last.Width, w->last.Height); + } + DrawWidget(w); } MarkAllDirty(); } @@ -132,13 +166,16 @@ void LBackend_Redraw(void) { void LBackend_Tick(void) { activeScreen->Tick(activeScreen); - if (!dirty_rect.Width) return; if (pendingRedraw & REDRAW_ALL) { RedrawAll(); pendingRedraw = 0; + } else if (pendingRedraw & REDRAW_SOME) { + RedrawDirty(); + pendingRedraw = 0; } + if (!dirty_rect.Width) return; Window_DrawFramebuffer(dirty_rect); dirty_rect.X = 0; dirty_rect.Width = 0; dirty_rect.Y = 0; dirty_rect.Height = 0; @@ -182,6 +219,7 @@ void LBackend_ButtonInit(struct LButton* w, int width, int height) { void LBackend_ButtonUpdate(struct LButton* w) { struct DrawTextArgs args; DrawTextArgs_Make(&args, &w->text, &titleFont, true); + LBackend_MarkDirty(w); w->_textWidth = Drawer2D_TextWidth(&args); w->_textHeight = Drawer2D_TextHeight(&args); @@ -190,7 +228,6 @@ void LBackend_ButtonUpdate(struct LButton* w) { void LBackend_ButtonDraw(struct LButton* w) { struct DrawTextArgs args; int xOffset, yOffset; - Launcher_MarkDirty(w->x, w->y, w->width, w->height); LButton_DrawBackground(w, &Launcher_Framebuffer, w->x, w->y); xOffset = w->width - w->_textWidth; @@ -213,9 +250,9 @@ void LBackend_ButtonDraw(struct LButton* w) { static void LCheckbox_OnClick(void* w) { struct LCheckbox* cb = (struct LCheckbox*)w; - cb->value = !cb->value; + LBackend_MarkDirty(cb); - LWidget_Redraw(cb); + cb->value = !cb->value; if (cb->ValueChanged) cb->ValueChanged(cb); } @@ -285,7 +322,6 @@ void LBackend_CheckboxDraw(struct LCheckbox* w) { BitmapCol boxBottom = BitmapCol_Make(240, 240, 240, 255); struct DrawTextArgs args; int x, y, width, height; - Launcher_MarkDirty(w->x, w->y, w->width, w->height); width = Display_ScaleX(CB_SIZE); height = Display_ScaleY(CB_SIZE); @@ -387,24 +423,21 @@ static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { } } -static void LInput_UpdateDimensions(struct LInput* w, const cc_string* text) { +void LBackend_InputUpdate(struct LInput* w) { + cc_string text; char textBuffer[STRING_SIZE]; struct DrawTextArgs args; int textWidth; - DrawTextArgs_Make(&args, text, &textFont, false); + String_InitArray(text, textBuffer); + LInput_UNSAFE_GetText(w, &text); + LBackend_MarkDirty(w); + + DrawTextArgs_Make(&args, &text, &textFont, false); textWidth = Drawer2D_TextWidth(&args); w->width = max(w->minWidth, textWidth + inputExpand); w->_textHeight = Drawer2D_TextHeight(&args); } -void LBackend_InputUpdate(struct LInput* w) { - cc_string text; char textBuffer[STRING_SIZE]; - String_InitArray(text, textBuffer); - - LInput_UNSAFE_GetText(w, &text); - LInput_UpdateDimensions(w, &text); -} - void LBackend_InputDraw(struct LInput* w) { cc_string text; char textBuffer[STRING_SIZE]; struct DrawTextArgs args; @@ -413,8 +446,6 @@ void LBackend_InputDraw(struct LInput* w) { LInput_UNSAFE_GetText(w, &text); DrawTextArgs_Make(&args, &text, &textFont, false); - /* TODO shouldn't be recalcing size in draw.... */ - LInput_UpdateDimensions(w, &text); LInput_DrawOuterBorder(w); LInput_DrawInnerBorder(w); Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_WHITE, @@ -523,18 +554,16 @@ void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { struct OpenKeyboardArgs args; caretStart = DateTime_CurrentUTC_MS(); LInput_MoveCaretToCursor(w, idx); - /* TODO: Only draw outer border */ if (wasSelected) return; - LWidget_Draw(w); + LBackend_MarkDirty(w); OpenKeyboardArgs_Init(&args, &w->text, w->inputType); Window_OpenKeyboard(&args); } void LBackend_InputUnselect(struct LInput* w) { caretStart = 0; - /* TODO: Only draw outer border */ - LWidget_Draw(w); + LBackend_MarkDirty(w); Window_CloseKeyboard(); } @@ -548,6 +577,7 @@ void LBackend_LabelInit(struct LLabel* w) { } void LBackend_LabelUpdate(struct LLabel* w) { struct DrawTextArgs args; DrawTextArgs_Make(&args, &w->text, LLabel_GetFont(w), true); + LBackend_MarkDirty(w); w->width = Drawer2D_TextWidth(&args); w->height = Drawer2D_TextHeight(&args); @@ -583,7 +613,7 @@ void LBackend_SliderInit(struct LSlider* w, int width, int height) { } void LBackend_SliderUpdate(struct LSlider* w) { - LWidget_Draw(w); + LBackend_MarkDirty(w); } static void LSlider_DrawBoxBounds(struct LSlider* w) { @@ -653,7 +683,7 @@ void LBackend_TableReposition(struct LTable* w) { void LBackend_TableFlagAdded(struct LTable* w) { /* TODO: Only redraw flags */ - LWidget_Draw(w); + LBackend_MarkDirty(w); } /* Draws background behind column headers */ @@ -887,7 +917,7 @@ void LBackend_TableMouseDown(struct LTable* w, int idx) { } else { LTable_RowsClick(w, idx); } - LWidget_Draw(w); + LBackend_MarkDirty(w); } void LBackend_TableMouseMove(struct LTable* w, int idx) { @@ -902,7 +932,7 @@ void LBackend_TableMouseMove(struct LTable* w, int idx) { w->topRow = row; LTable_ClampTopRow(w); - LWidget_Draw(w); + LBackend_MarkDirty(w); } else if (w->draggingColumn >= 0) { col = w->draggingColumn; width = x - w->dragXStart; @@ -914,7 +944,7 @@ void LBackend_TableMouseMove(struct LTable* w, int idx) { Math_Clamp(width, cellMinWidth, maxW - cellMinWidth); if (width == w->columns[col].width) return; w->columns[col].width = width; - LWidget_Draw(w); + LBackend_MarkDirty(w); } } diff --git a/src/LBackend.h b/src/LBackend.h index 9c446d533..c76910180 100644 --- a/src/LBackend.h +++ b/src/LBackend.h @@ -24,6 +24,7 @@ void LBackend_CloseScreen(struct LScreen* s); void LBackend_Redraw(void); void LBackend_Tick(void); void LBackend_WidgetRepositioned(struct LWidget* w); +void LBackend_MarkDirty(void* widget); void LBackend_ButtonInit(struct LButton* w, int width, int height); void LBackend_ButtonUpdate(struct LButton* w); diff --git a/src/LScreens.c b/src/LScreens.c index 31c3ffda8..840bef657 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -545,12 +545,6 @@ static void DirectConnectScreen_UrlFilter(cc_string* str) { str->length = 0; } -CC_NOINLINE static void DirectConnectScreen_SetStatus(const char* text) { - struct LLabel* w = &DirectConnectScreen.lblStatus; - LLabel_SetConst(w, text); - LWidget_Redraw(w); -} - static void DirectConnectScreen_Load(struct DirectConnectScreen* s) { cc_string addr; char addrBuffer[STRING_SIZE]; cc_string mppass; char mppassBuffer[STRING_SIZE]; @@ -580,13 +574,14 @@ static void DirectConnectScreen_StartClient(void* w) { const cc_string* user = &DirectConnectScreen.iptUsername.text; const cc_string* addr = &DirectConnectScreen.iptAddress.text; const cc_string* mppass = &DirectConnectScreen.iptMppass.text; + struct LLabel* status = &DirectConnectScreen.lblStatus; cc_string ip, port; cc_uint16 raw_port; int index = String_LastIndexOf(addr, ':'); if (index == 0 || index == addr->length - 1) { - DirectConnectScreen_SetStatus("&eInvalid address"); return; + LLabel_SetConst(status, "&eInvalid address"); return; } /* support either "[IP]" or "[IP]:[PORT]" */ @@ -599,13 +594,13 @@ static void DirectConnectScreen_StartClient(void* w) { } if (!user->length) { - DirectConnectScreen_SetStatus("&eUsername required"); return; + LLabel_SetConst(status, "&eUsername required"); return; } if (!Socket_ValidAddress(&ip)) { - DirectConnectScreen_SetStatus("&eInvalid ip"); return; + LLabel_SetConst(status, "&eInvalid ip"); return; } if (!Convert_ParseUInt16(&port, &raw_port)) { - DirectConnectScreen_SetStatus("&eInvalid port"); return; + LLabel_SetConst(status, "&eInvalid port"); return; } if (!mppass->length) mppass = &defMppass; @@ -614,7 +609,7 @@ static void DirectConnectScreen_StartClient(void* w) { Options_Set("launcher-dc-port", &port); Options_SetSecure("launcher-dc-mppass", mppass); - DirectConnectScreen_SetStatus(""); + LLabel_SetConst(status, ""); Launcher_StartGame(user, mppass, &ip, &port, &String_Empty); } @@ -787,7 +782,6 @@ CC_NOINLINE static void MainScreen_Error(struct LWebTask* task, const char* acti LWebTask_DisplayError(task, action, &str); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); s->signingIn = false; } @@ -797,12 +791,10 @@ static void MainScreen_DoLogin(void) { cc_string* pass = &s->iptPassword.text; if (!user->length) { - LLabel_SetConst(&s->lblStatus, "&eUsername required"); - LWidget_Redraw(&s->lblStatus); return; + LLabel_SetConst(&s->lblStatus, "&eUsername required"); return; } if (!pass->length) { - LLabel_SetConst(&s->lblStatus, "&ePassword required"); - LWidget_Redraw(&s->lblStatus); return; + LLabel_SetConst(&s->lblStatus, "&ePassword required"); return; } if (GetTokenTask.Base.working) return; @@ -811,7 +803,6 @@ static void MainScreen_DoLogin(void) { GetTokenTask_Run(); LLabel_SetConst(&s->lblStatus, "&eSigning in.."); - LWidget_Redraw(&s->lblStatus); s->signingIn = true; } static void MainScreen_Login(void* w) { MainScreen_DoLogin(); } @@ -855,9 +846,7 @@ static void MainScreen_ResumeHover(void* w) { } else { String_Format3(&str, "&eResume as %s to %s:%s", &info.user, &info.ip, &info.port); } - LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } static void MainScreen_ResumeUnhover(void* w) { @@ -865,7 +854,6 @@ static void MainScreen_ResumeUnhover(void* w) { if (s->signingIn) return; LLabel_SetConst(&s->lblStatus, ""); - LWidget_Redraw(&s->lblStatus); } static void MainScreen_Init(struct LScreen* s_) { @@ -964,20 +952,17 @@ static void MainScreen_TickCheckUpdates(struct MainScreen* s) { } else { LLabel_SetConst(&s->lblUpdate, "&cCheck failed"); } - LWidget_Redraw(&s->lblUpdate); } static void MainScreen_LoginPhase2(struct MainScreen* s, const cc_string* user) { /* website returns case correct username */ if (!String_Equals(&s->iptUsername.text, user)) { LInput_SetText(&s->iptUsername, user); - LWidget_Redraw(&s->iptUsername); } String_Copy(&Launcher_Username, user); FetchServersTask_Run(); LLabel_SetConst(&s->lblStatus, "&eRetrieving servers list.."); - LWidget_Redraw(&s->lblStatus); } static void MainScreen_TickGetToken(struct MainScreen* s) { @@ -1008,7 +993,6 @@ static void MainScreen_TickSignIn(struct MainScreen* s) { if (SignInTask.error) { LLabel_SetConst(&s->lblStatus, SignInTask.error); - LWidget_Redraw(&s->lblStatus); } else if (SignInTask.Base.success) { MainScreen_LoginPhase2(s, &SignInTask.username); } else { @@ -1191,10 +1175,10 @@ static void FetchResourcesScreen_Layout(struct LScreen* s_) { static void FetchResourcesScreen_SetStatus(struct FetchResourcesScreen* s, const cc_string* str) { struct LLabel* w = &s->lblStatus; + /* TODO FIXY FIXY FIXY */ w->opaque = true; /* need to rethink ResetArea for Launcher */ CheckResourcesScreen_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); LLabel_SetText(w, str); - LWidget_Draw(w); } static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, int reqID) { @@ -1293,7 +1277,6 @@ static void ServersScreen_Refresh(void* w) { FetchServersTask_Run(); btn = &ServersScreen.btnRefresh; LButton_SetConst(btn, "&eWorking.."); - LWidget_Redraw(btn); } static void ServersScreen_HashFilter(cc_string* str) { @@ -1313,19 +1296,19 @@ static void ServersScreen_HashFilter(cc_string* str) { static void ServersScreen_SearchChanged(struct LInput* w) { struct ServersScreen* s = &ServersScreen; LTable_ApplyFilter(&s->table); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->table); } static void ServersScreen_HashChanged(struct LInput* w) { struct ServersScreen* s = &ServersScreen; LTable_ShowSelected(&s->table); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->table); } static void ServersScreen_OnSelectedChanged(void) { struct ServersScreen* s = &ServersScreen; - LWidget_Redraw(&s->iptHash); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->iptHash); + LBackend_MarkDirty(&s->table); } static void ServersScreen_ReloadServers(struct ServersScreen* s) { @@ -1394,12 +1377,11 @@ static void ServersScreen_Tick(struct LScreen* s_) { if (FetchServersTask.Base.success) { ServersScreen_ReloadServers(s); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->table); } LButton_SetConst(&s->btnRefresh, FetchServersTask.Base.success ? "Refresh" : "&cFailed"); - LWidget_Redraw(&s->btnRefresh); } static void ServersScreen_Free(struct LScreen* s_) { @@ -1718,7 +1700,6 @@ static void UpdatesScreen_Format(struct LLabel* lbl, const char* prefix, cc_uint UpdatesScreen_FormatTime(&str, delta); } LLabel_SetText(lbl, &str); - LWidget_Redraw(lbl); } static void UpdatesScreen_FormatBoth(struct UpdatesScreen* s) { @@ -1749,7 +1730,6 @@ static void UpdatesScreen_DoFetch(struct UpdatesScreen* s) { UpdatesScreen_UpdateHeader(s, &str); String_AppendConst(&str, ".."); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } static void UpdatesScreen_Get(cc_bool release, int buildIndex) { @@ -1785,7 +1765,6 @@ static void UpdatesScreen_UpdateProgress(struct UpdatesScreen* s, struct LWebTas UpdatesScreen_UpdateHeader(s, &str); String_Format1(&str, " &a%i%%", &s->buildProgress); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } static void UpdatesScreen_FetchTick(struct UpdatesScreen* s) { @@ -1800,7 +1779,6 @@ static void UpdatesScreen_FetchTick(struct UpdatesScreen* s) { String_InitArray(str, strBuffer); LWebTask_DisplayError(&FetchUpdateTask.Base, "fetching update", &str); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } else { /* FetchUpdateTask handles saving the updated file for us */ Launcher_ShouldExit = true; diff --git a/src/LWidgets.c b/src/LWidgets.c index d705c6e76..8c2d9fd64 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -40,21 +40,6 @@ void LWidget_CalcPosition(void* widget) { LBackend_WidgetRepositioned(w); } -void LWidget_Draw(void* widget) { - struct LWidget* w = (struct LWidget*)widget; - w->last.X = w->x; w->last.Width = w->width; - w->last.Y = w->y; w->last.Height = w->height; - - w->VTABLE->Draw(w); - Launcher_MarkDirty(w->x, w->y, w->width, w->height); -} - -void LWidget_Redraw(void* widget) { - struct LWidget* w = (struct LWidget*)widget; - Launcher_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); - LWidget_Draw(w); -} - /*########################################################################################################################* *------------------------------------------------------ButtonWidget-------------------------------------------------------* @@ -132,14 +117,18 @@ static void LButton_Draw(void* widget) { static void LButton_Hover(void* w, int idx, cc_bool wasOver) { /* only need to redraw when changing from unhovered to hovered */ - if (!wasOver) LWidget_Draw(w); + if (!wasOver) LBackend_MarkDirty(w); +} + +static void LButton_Unhover(void* w) { + LBackend_MarkDirty(w); } static const struct LWidgetVTABLE lbutton_VTABLE = { LButton_Draw, NULL, - NULL, NULL, /* Key */ - LButton_Hover, LWidget_Draw, /* Hover */ - NULL, NULL /* Select */ + NULL, NULL, /* Key */ + LButton_Hover, LButton_Unhover, /* Hover */ + NULL, NULL /* Select */ }; void LButton_Init(struct LButton* w, int width, int height, const char* text) { w->VTABLE = &lbutton_VTABLE; @@ -218,7 +207,7 @@ static void LInput_AdvanceCaretPos(struct LInput* w, cc_bool forwards) { w->caretPos += (forwards ? 1 : -1); if (w->caretPos < 0 || w->caretPos >= w->text.length) w->caretPos = -1; - LWidget_Redraw(w); + LBackend_InputUpdate(w); } static void LInput_CopyFromClipboard(struct LInput* w) { @@ -252,7 +241,7 @@ static void LInput_Backspace(struct LInput* w) { if (w->TextChanged) w->TextChanged(w); LInput_ClampCaret(w); - LWidget_Redraw(w); + LBackend_InputUpdate(w); } /* Removes the character at the caret in the currently entered text */ @@ -264,7 +253,7 @@ static void LInput_Delete(struct LInput* w) { if (w->TextChanged) w->TextChanged(w); LInput_ClampCaret(w); - LWidget_Redraw(w); + LBackend_InputUpdate(w); } static void LInput_KeyDown(void* widget, int key, cc_bool was) { @@ -315,7 +304,7 @@ static void LInput_KeyChar(void* widget, char c) { cc_bool appended = LInput_Append(w, c); if (appended && w->TextChanged) w->TextChanged(w); - if (appended) LWidget_Redraw(w); + if (appended) LBackend_InputUpdate(w); } static void LInput_TextChanged(void* widget, const cc_string* str) { @@ -335,6 +324,7 @@ static const struct LWidgetVTABLE linput_VTABLE = { void LInput_Init(struct LInput* w, int width, const char* hintText) { w->VTABLE = &linput_VTABLE; w->tabSelectable = true; + w->opaque = true; String_InitArray(w->text, w->_textBuffer); w->hintText = hintText; @@ -362,13 +352,12 @@ void LInput_AppendString(struct LInput* w, const cc_string* str) { } if (appended && w->TextChanged) w->TextChanged(w); - if (appended) LWidget_Redraw(w); + if (appended) LBackend_InputUpdate(w); } void LInput_SetString(struct LInput* w, const cc_string* str) { LInput_SetText(w, str); if (w->TextChanged) w->TextChanged(w); - LWidget_Redraw(w); } @@ -448,6 +437,7 @@ static const struct LWidgetVTABLE lslider_VTABLE = { void LSlider_Init(struct LSlider* w, int width, int height, BitmapCol color) { w->VTABLE = &lslider_VTABLE; w->color = color; + w->opaque = true; LBackend_SliderInit(w, width, height); } @@ -613,7 +603,7 @@ static void LTable_MouseWheel(void* widget, float delta) { struct LTable* w = (struct LTable*)widget; w->topRow -= Utils_AccumulateWheelDelta(&w->_wheelAcc, delta); LTable_ClampTopRow(w); - LWidget_Draw(w); + LBackend_MarkDirty(w); w->_lastRow = -1; } @@ -641,6 +631,7 @@ void LTable_Init(struct LTable* w, struct FontDesc* rowFont) { w->numColumns = Array_Elems(tableColumns); w->rowFont = rowFont; w->sortingCol = -1; + w->opaque = true; for (i = 0; i < w->numColumns; i++) { w->columns[i].width = Display_ScaleX(w->columns[i].width); diff --git a/src/LWidgets.h b/src/LWidgets.h index 064d8d43d..51dddf4aa 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -39,6 +39,8 @@ struct LWidgetVTABLE { cc_bool selected; /* Whether this widget is last widget to be clicked on */ \ cc_bool tabSelectable; /* Whether this widget gets selected when pressing tab */ \ cc_uint8 horAnchor, verAnchor; /* Specifies the reference point for when this widget is resized */ \ + cc_bool dirty; /* Whether this widget needs to be redrawn */ \ + cc_bool opaque; /* Whethe this widget completely obscures background behind it */ \ int xOffset, yOffset; /* Offset from the reference point */ \ void (*OnClick)(void* widget); /* Called when widget is clicked */ \ void (*OnHover)(void* widget); /* Called when widget is hovered over */ \ @@ -50,8 +52,6 @@ struct LWidgetVTABLE { struct LWidget { LWidget_Layout }; void LWidget_SetLocation(void* widget, cc_uint8 horAnchor, cc_uint8 verAnchor, int xOffset, int yOffset); void LWidget_CalcPosition(void* widget); -void LWidget_Draw(void* widget); -void LWidget_Redraw(void* widget); void LWidget_CalcOffsets(void); struct LButton {