#include "LWidgets.h" #ifndef CC_BUILD_WEB #include "String.h" #include "Gui.h" #include "Drawer2D.h" #include "Launcher.h" #include "ExtMath.h" #include "Window.h" #include "Funcs.h" #include "LWeb.h" #include "Platform.h" #include "LScreens.h" #include "Input.h" #include "Utils.h" #include "LBackend.h" static int flagXOffset, flagYOffset; static int oneX, twoX, fourX; static int oneY, twoY, fourY; void LWidget_CalcOffsets(void) { oneX = Display_ScaleX(1); oneY = Display_ScaleY(1); if (oneX < 1) { oneX = 1; } if (oneY < 1) { oneY = 1; } twoX = oneX * 2; fourX = oneX * 4; twoY = oneY * 2; fourY = oneY * 4; flagXOffset = Display_ScaleX(2); flagYOffset = Display_ScaleY(6); } static void LWidget_DrawInsetBorder(struct Context2D* ctx, BitmapCol color, int insetX, int insetY, int x, int y, int width, int height) { Context2D_Clear(ctx, color, x + insetX, y, width - 2 * insetX, insetY); Context2D_Clear(ctx, color, x + insetX, y + height - insetY, width - 2 * insetX, insetY); Context2D_Clear(ctx, color, x, y + insetY, insetX, height - 2 * insetY); Context2D_Clear(ctx, color, x + width - insetX, y + insetY, insetX, height - 2 * insetY); } void LWidget_DrawBorder(struct Context2D* ctx, BitmapCol color, int borderX, int borderY, int x, int y, int width, int height) { Context2D_Clear(ctx, color, x, y, width, borderY); Context2D_Clear(ctx, color, x, y + height - borderY, width, borderY); Context2D_Clear(ctx, color, x, y, borderX, height); Context2D_Clear(ctx, color, x + width - borderX, y, borderX, height); } /*########################################################################################################################* *------------------------------------------------------ButtonWidget-------------------------------------------------------* *#########################################################################################################################*/ static void LButton_DrawBase(struct Context2D* ctx, int x, int y, int width, int height, cc_bool active) { BitmapCol color = active ? Launcher_Theme.ButtonForeActiveColor : Launcher_Theme.ButtonForeColor; if (Launcher_Theme.ClassicBackground) { Gradient_Noise(ctx, color, 8, x + oneX, y + oneY, width - twoX, height - twoY); } else { Gradient_Vertical(ctx, BitmapColor_Offset(color, 8,8,8), BitmapColor_Offset(color, -8,-8,-8), x + oneX, y + oneY, width - twoX, height - twoY); } } static void LButton_DrawBorder(struct Context2D* ctx, int x, int y, int width, int height) { BitmapCol backColor = Launcher_Theme.ButtonBorderColor; #ifdef CC_BUILD_IOS LWidget_DrawBorder(ctx, backColor, oneX, oneY, x, y, width, height); #else LWidget_DrawInsetBorder(ctx, backColor, oneX, oneY, x, y, width, height); #endif } static void LButton_DrawHighlight(struct Context2D* ctx, int x, int y, int width, int height, cc_bool active) { BitmapCol activeColor = BitmapColor_RGB(189, 198, 255); BitmapCol color = Launcher_Theme.ButtonHighlightColor; if (Launcher_Theme.ClassicBackground) { if (active) color = activeColor; Context2D_Clear(ctx, color, x + twoX, y + oneY, width - fourX, oneY); Context2D_Clear(ctx, color, x + oneX, y + twoY, oneX, height - fourY); } else if (!active) { Context2D_Clear(ctx, color, x + twoX, y + oneY, width - fourX, oneY); } } void LButton_DrawBackground(struct Context2D* ctx, int x, int y, int width, int height, cc_bool active) { LButton_DrawBase( ctx, x, y, width, height, active); LButton_DrawBorder( ctx, x, y, width, height); LButton_DrawHighlight(ctx, x, y, width, height, active); } static void LButton_Draw(void* widget) { struct LButton* w = (struct LButton*)widget; LBackend_ButtonDraw(w); } static void LButton_Hover(void* w, int idx, cc_bool wasOver) { /* only need to redraw when changing from unhovered to active */ if (!wasOver) LBackend_MarkDirty(w); } static void LButton_Unhover(void* w) { LBackend_MarkDirty(w); } static void LButton_OnSelect(void* w, int idx, cc_bool wasSelected) { LBackend_MarkDirty(w); } static void LButton_OnUnselect(void* w, int idx) { LBackend_MarkDirty(w); } static const struct LWidgetVTABLE lbutton_VTABLE = { LButton_Draw, NULL, NULL, NULL, /* Key */ LButton_Hover, LButton_Unhover, /* Hover */ LButton_OnSelect, LButton_OnUnselect /* Select */ }; void LButton_Add(void* screen, struct LButton* w, int width, int height, const char* text, LWidgetFunc onClick, const struct LLayout* layouts) { w->VTABLE = &lbutton_VTABLE; w->type = LWIDGET_BUTTON; w->OnClick = onClick; w->layouts = layouts; w->autoSelectable = true; LBackend_ButtonInit(w, width, height); LButton_SetConst(w, text); LScreen_AddWidget(screen, w); } void LButton_SetConst(struct LButton* w, const char* text) { w->text = String_FromReadonly(text); LBackend_ButtonUpdate(w); } /*########################################################################################################################* *-----------------------------------------------------CheckboxWidget------------------------------------------------------* *#########################################################################################################################*/ static void LCheckbox_Draw(void* widget) { struct LCheckbox* w = (struct LCheckbox*)widget; LBackend_CheckboxDraw(w); } static const struct LWidgetVTABLE lcheckbox_VTABLE = { LCheckbox_Draw, NULL, NULL, NULL, /* Key */ NULL, NULL, /* Hover */ NULL, NULL /* Select */ }; void LCheckbox_Add(void* screen, struct LCheckbox* w, const char* text, LCheckboxChanged onChanged, const struct LLayout* layouts) { w->VTABLE = &lcheckbox_VTABLE; w->type = LWIDGET_CHECKBOX; w->layouts = layouts; w->autoSelectable = true; w->ValueChanged = onChanged; w->text = String_FromReadonly(text); LBackend_CheckboxInit(w); LScreen_AddWidget(screen, w); } void LCheckbox_Set(struct LCheckbox* w, cc_bool value) { w->value = value; LBackend_CheckboxUpdate(w); } /*########################################################################################################################* *------------------------------------------------------InputWidget--------------------------------------------------------* *#########################################################################################################################*/ void LInput_UNSAFE_GetText(struct LInput* w, cc_string* text) { int i; if (w->inputType != KEYBOARD_TYPE_PASSWORD) { *text = w->text; return; } for (i = 0; i < w->text.length; i++) { String_Append(text, '*'); } } static void LInput_Draw(void* widget) { struct LInput* w = (struct LInput*)widget; LBackend_InputDraw(w); } static void LInput_TickCaret(void* widget) { struct LInput* w = (struct LInput*)widget; LBackend_InputTick(w); } static void LInput_Select(void* widget, int idx, cc_bool wasSelected) { struct LInput* w = (struct LInput*)widget; LBackend_InputSelect(w, idx, wasSelected); } static void LInput_Unselect(void* widget, int idx) { struct LInput* w = (struct LInput*)widget; LBackend_InputUnselect(w); } static void LInput_AdvanceCaretPos(struct LInput* w, cc_bool forwards) { if (forwards && w->caretPos == -1) return; if (!forwards && w->caretPos == 0) return; if (w->caretPos == -1 && !forwards) /* caret after text */ w->caretPos = w->text.length; w->caretPos += (forwards ? 1 : -1); if (w->caretPos < 0 || w->caretPos >= w->text.length) w->caretPos = -1; LBackend_InputUpdate(w); } static void LInput_CopyFromClipboard(struct LInput* w) { cc_string text; char textBuffer[2048]; String_InitArray(text, textBuffer); Clipboard_GetText(&text); String_UNSAFE_TrimStart(&text); String_UNSAFE_TrimEnd(&text); if (w->ClipboardFilter) w->ClipboardFilter(&text); LInput_AppendString(w, &text); } /* If caret position is now beyond end of text, resets to -1 */ static CC_INLINE void LInput_ClampCaret(struct LInput* w) { if (w->caretPos >= w->text.length) w->caretPos = -1; } /* Removes the character preceding the caret in the currently entered text */ static void LInput_Backspace(struct LInput* w) { if (!w->text.length || w->caretPos == 0) return; if (w->caretPos == -1) { String_DeleteAt(&w->text, w->text.length - 1); } else { String_DeleteAt(&w->text, w->caretPos - 1); w->caretPos--; if (w->caretPos == -1) w->caretPos = 0; } if (w->TextChanged) w->TextChanged(w); LInput_ClampCaret(w); LBackend_InputUpdate(w); } /* Removes the character at the caret in the currently entered text */ static void LInput_Delete(struct LInput* w) { if (!w->text.length || w->caretPos == -1) return; String_DeleteAt(&w->text, w->caretPos); if (w->caretPos == -1) w->caretPos = 0; if (w->TextChanged) w->TextChanged(w); LInput_ClampCaret(w); LBackend_InputUpdate(w); } static cc_bool LInput_KeyDown(void* widget, int key, cc_bool was) { struct LInput* w = (struct LInput*)widget; if (key == CCKEY_BACKSPACE) { LInput_Backspace(w); } else if (key == CCKEY_DELETE) { LInput_Delete(w); } else if (key == INPUT_CLIPBOARD_COPY) { if (w->text.length) Clipboard_SetText(&w->text); } else if (key == INPUT_CLIPBOARD_PASTE) { LInput_CopyFromClipboard(w); } else if (Input_IsEscapeButton(key)) { if (w->text.length) LInput_SetString(w, &String_Empty); } else if (Input_IsLeftButton(key)) { LInput_AdvanceCaretPos(w, false); } else if (Input_IsRightButton(key)) { LInput_AdvanceCaretPos(w, true); } else { return false; } return true; } static cc_bool LInput_CanAppend(struct LInput* w, char c) { switch (w->inputType) { case KEYBOARD_TYPE_PASSWORD: return true; /* keyboard accepts all characters */ case KEYBOARD_TYPE_INTEGER: return c >= '0' && c <= '9'; } return c >= ' ' && c <= '~' && c != '&'; } /* Appends a character to the currently entered text */ static CC_NOINLINE cc_bool LInput_Append(struct LInput* w, char c) { if (LInput_CanAppend(w, c) && w->text.length < w->text.capacity) { if (w->caretPos == -1) { String_Append(&w->text, c); } else { String_InsertAt(&w->text, w->caretPos, c); w->caretPos++; } return true; } return false; } static void LInput_KeyChar(void* widget, char c) { struct LInput* w = (struct LInput*)widget; cc_bool appended = LInput_Append(w, c); if (appended && w->TextChanged) w->TextChanged(w); if (appended) LBackend_InputUpdate(w); } static void LInput_TextChanged(void* widget, const cc_string* str) { struct LInput* w = (struct LInput*)widget; LInput_SetText(w, str); if (w->TextChanged) w->TextChanged(w); } static const struct LWidgetVTABLE linput_VTABLE = { LInput_Draw, LInput_TickCaret, LInput_KeyDown, LInput_KeyChar, /* Key */ NULL, NULL, /* Hover */ /* TODO: Don't redraw whole thing, just the outer border */ LInput_Select, LInput_Unselect, /* Select */ NULL, LInput_TextChanged /* TextChanged */ }; void LInput_Add(void* screen, struct LInput* w, int width, const char* hintText, const struct LLayout* layouts) { w->VTABLE = &linput_VTABLE; w->type = LWIDGET_INPUT; w->autoSelectable = true; w->opaque = true; w->layouts = layouts; /* Preserve existing input across Add calls */ if (!w->text.buffer) { String_InitArray(w->text, w->_textBuffer); } w->hintText = hintText; w->caretPos = -1; LBackend_InputInit(w, width); LScreen_AddWidget(screen, w); } void LInput_SetText(struct LInput* w, const cc_string* text) { String_Copy(&w->text, text); LInput_ClampCaret(w); LBackend_InputUpdate(w); } void LInput_ClearText(struct LInput* w) { w->text.length = 0; w->caretPos = -1; LBackend_InputUpdate(w); } void LInput_AppendString(struct LInput* w, const cc_string* str) { int i, appended = 0; for (i = 0; i < str->length; i++) { if (LInput_Append(w, str->buffer[i])) appended++; } if (appended && w->TextChanged) w->TextChanged(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); } /*########################################################################################################################* *------------------------------------------------------LabelWidget--------------------------------------------------------* *#########################################################################################################################*/ static void LLabel_Draw(void* widget) { struct LLabel* w = (struct LLabel*)widget; LBackend_LabelDraw(w); } static const struct LWidgetVTABLE llabel_VTABLE = { LLabel_Draw, NULL, NULL, NULL, /* Key */ NULL, NULL, /* Hover */ NULL, NULL /* Select */ }; void LLabel_Add(void* screen, struct LLabel* w, const char* text, const struct LLayout* layouts) { w->VTABLE = &llabel_VTABLE; w->type = LWIDGET_LABEL; w->layouts = layouts; String_InitArray(w->text, w->_textBuffer); LBackend_LabelInit(w); LLabel_SetConst(w, text); LScreen_AddWidget(screen, w); } void LLabel_SetText(struct LLabel* w, const cc_string* text) { String_Copy(&w->text, text); LBackend_LabelUpdate(w); LBackend_LayoutWidget((struct LWidget*)w); } void LLabel_SetConst(struct LLabel* w, const char* text) { cc_string str = String_FromReadonly(text); LLabel_SetText(w, &str); } /*########################################################################################################################* *-------------------------------------------------------LineWidget--------------------------------------------------------* *#########################################################################################################################*/ static void LLine_Draw(void* widget) { struct LLine* w = (struct LLine*)widget; LBackend_LineDraw(w); } static const struct LWidgetVTABLE lline_VTABLE = { LLine_Draw, NULL, NULL, NULL, /* Key */ NULL, NULL, /* Hover */ NULL, NULL /* Select */ }; void LLine_Add(void* screen, struct LLine* w, int width, const struct LLayout* layouts) { w->VTABLE = &lline_VTABLE; w->type = LWIDGET_LINE; w->layouts = layouts; LBackend_LineInit(w, width); LScreen_AddWidget(screen, w); } #define CLASSIC_LINE_COLOR BitmapColor_RGB(128, 128, 128) BitmapCol LLine_GetColor(void) { return Launcher_Theme.ClassicBackground ? CLASSIC_LINE_COLOR : Launcher_Theme.ButtonBorderColor; } /*########################################################################################################################* *------------------------------------------------------SliderWidget-------------------------------------------------------* *#########################################################################################################################*/ static void LSlider_Draw(void* widget) { struct LSlider* w = (struct LSlider*)widget; LBackend_SliderDraw(w); } static const struct LWidgetVTABLE lslider_VTABLE = { LSlider_Draw, NULL, NULL, NULL, /* Key */ NULL, NULL, /* Hover */ NULL, NULL /* Select */ }; void LSlider_Add(void* screen, struct LSlider* w, int width, int height, BitmapCol color, const struct LLayout* layouts) { w->VTABLE = &lslider_VTABLE; w->type = LWIDGET_SLIDER; w->color = color; w->opaque = true; w->layouts = layouts; LBackend_SliderInit(w, width, height); LScreen_AddWidget(screen, w); } void LSlider_SetProgress(struct LSlider* w, int progress) { if (progress == w->value) return; w->value = progress; LBackend_SliderUpdate(w); } /*########################################################################################################################* *------------------------------------------------------TableWidget--------------------------------------------------------* *#########################################################################################################################*/ static void FlagColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Context2D* ctx) { struct Flag* flag = Flags_Get(row); if (!flag) return; Context2D_DrawPixels(ctx, cell->x + flagXOffset, cell->y + flagYOffset, &flag->bmp); } static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Context2D* ctx) { args->text = row->name; } static int NameColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return String_Compare(&b->name, &a->name); } static void PlayersColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Context2D* ctx) { String_Format2(&args->text, "%i/%i", &row->players, &row->maxPlayers); } static int PlayersColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return b->players - a->players; } static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Context2D* ctx) { LTable_FormatUptime(&args->text, row->uptime); } static int UptimeColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return b->uptime - a->uptime; } static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Context2D* ctx) { /* last column, so adjust to fit size of table */ int leftover = cell->table->width - cell->x; cell->width = max(cell->width, leftover); args->text = row->software; } static int SoftwareColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return String_Compare(&b->software, &a->software); } static struct LTableColumn tableColumns[] = { { "", 15, FlagColumn_Draw, NULL, false, false, false }, { "Name", 328, NameColumn_Draw, NameColumn_Sort, true, false, true }, { "Players", 73, PlayersColumn_Draw, PlayersColumn_Sort, true, true, true }, { "Uptime", 73, UptimeColumn_Draw, UptimeColumn_Sort, true, true, true }, { "Software", 143, SoftwareColumn_Draw, SoftwareColumn_Sort, false, true, true } }; void LTable_FormatUptime(cc_string* dst, int uptime) { char unit = 's'; if (uptime >= SECS_PER_DAY * 7) { uptime /= SECS_PER_DAY; unit = 'd'; } else if (uptime >= SECS_PER_HOUR) { uptime /= SECS_PER_HOUR; unit = 'h'; } else if (uptime >= SECS_PER_MIN) { uptime /= SECS_PER_MIN; unit = 'm'; } String_Format2(dst, "%i%r", &uptime, &unit); } void LTable_GetScrollbarCoords(struct LTable* w, int* y, int* height) { float scale; if (!w->rowsCount) { *y = 0; *height = 0; return; } scale = w->height / (float)w->rowsCount; *y = Math_Ceil(w->topRow * scale); *height = Math_Ceil(w->visibleRows * scale); *height = min(*y + *height, w->height) - *y; } void LTable_ClampTopRow(struct LTable* w) { if (w->topRow > w->rowsCount - w->visibleRows) { w->topRow = w->rowsCount - w->visibleRows; } if (w->topRow < 0) w->topRow = 0; } int LTable_GetSelectedIndex(struct LTable* w) { struct ServerInfo* entry; int row; for (row = 0; row < w->rowsCount; row++) { entry = LTable_Get(row); if (String_CaselessEquals(w->selectedHash, &entry->hash)) return row; } return -1; } void LTable_SetSelectedTo(struct LTable* w, int index) { if (!w->rowsCount) return; if (index >= w->rowsCount) index = w->rowsCount - 1; if (index < 0) index = 0; String_Copy(w->selectedHash, <able_Get(index)->hash); LTable_ShowSelected(w); w->OnSelectedChanged(); } void LTable_RowClick(struct LTable* w, int row) { cc_uint64 now; LTable_SetSelectedTo(w, row); now = Stopwatch_Measure(); /* double click on row to join */ if (Stopwatch_ElapsedMS(w->_lastClick, now) < 1000 && row == w->_lastRow) { Launcher_ConnectToServer(<able_Get(row)->hash); } w->_lastRow = LTable_GetSelectedIndex(w); w->_lastClick = now; } cc_bool LTable_HandlesKey(int key) { return Input_IsUpButton(key) || key == CCKEY_PAGEUP || Input_IsDownButton(key) || key == CCKEY_PAGEDOWN; } static cc_bool LTable_KeyDown(void* widget, int key, cc_bool was) { struct LTable* w = (struct LTable*)widget; int index = LTable_GetSelectedIndex(w); if (Input_IsUpButton(key)) { index--; } else if (Input_IsDownButton(key)) { index++; } else if (key == CCKEY_PAGEUP) { index -= w->visibleRows; } else if (key == CCKEY_PAGEDOWN) { index += w->visibleRows; } else { return false; } w->_lastRow = -1; LTable_SetSelectedTo(w, index); return true; } static void LTable_MouseDown(void* widget, int idx, cc_bool wasOver) { struct LTable* w = (struct LTable*)widget; LBackend_TableMouseDown(w, idx); } static void LTable_MouseMove(void* widget, int idx, cc_bool wasOver) { struct LTable* w = (struct LTable*)widget; LBackend_TableMouseMove(w, idx); } static void LTable_MouseUp(void* widget, int idx) { struct LTable* w = (struct LTable*)widget; LBackend_TableMouseUp(w, idx); } static void LTable_MouseWheel(void* widget, float delta) { struct LTable* w = (struct LTable*)widget; w->topRow -= Utils_AccumulateWheelDelta(&w->_wheelAcc, delta); LTable_ClampTopRow(w); LBackend_MarkDirty(w); w->_lastRow = -1; } static void LTable_Draw(void* widget) { struct LTable* w = (struct LTable*)widget; LBackend_TableDraw(w); } static const struct LWidgetVTABLE ltable_VTABLE = { LTable_Draw, NULL, LTable_KeyDown, NULL, /* Key */ LTable_MouseMove, NULL, /* Hover */ LTable_MouseDown, LTable_MouseUp, /* Select */ LTable_MouseWheel, /* Wheel */ }; void LTable_Add(void* screen, struct LTable* w, const struct LLayout* layouts) { int i; w->VTABLE = <able_VTABLE; w->type = LWIDGET_TABLE; w->columns = tableColumns; w->numColumns = Array_Elems(tableColumns); w->sortingCol = -1; w->opaque = true; w->layouts = layouts; for (i = 0; i < w->numColumns; i++) { w->columns[i].width = Display_ScaleX(w->columns[i].width); } LBackend_TableInit(w); LScreen_AddWidget(screen, w); } void LTable_Reset(struct LTable* w) { LBackend_TableMouseUp(w, 0); LBackend_TableReposition(w); w->topRow = 0; w->rowsCount = 0; w->_wheelAcc = 0.0f; w->sortingCol = -1; } static int ShouldShowServer(struct LTable* w, struct ServerInfo* server) { return String_CaselessContains(&server->name, w->filter) && (Launcher_ShowEmptyServers || server->players > 0); } void LTable_ApplyFilter(struct LTable* w) { int i, j, count; count = FetchServersTask.numServers; for (i = 0, j = 0; i < count; i++) { if (ShouldShowServer(w, Servers_Get(i))) { FetchServersTask.servers[j++]._order = FetchServersTask.orders[i]; } } w->rowsCount = j; for (; j < count; j++) { FetchServersTask.servers[j]._order = -100000; } w->_lastRow = -1; LTable_ClampTopRow(w); LBackend_TableUpdate(w); } static int sortingCol; static int LTable_SortOrder(const struct ServerInfo* a, const struct ServerInfo* b) { int order; if (sortingCol >= 0) { order = tableColumns[sortingCol].SortOrder(a, b); return tableColumns[sortingCol].invertSort ? -order : order; } /* Default sort order. (most active server, then by highest uptime) */ if (a->players != b->players) return a->players - b->players; return a->uptime - b->uptime; } static void LTable_QuickSort(int left, int right) { cc_uint16* keys = FetchServersTask.orders; cc_uint16 key; while (left < right) { int i = left, j = right; struct ServerInfo* mid = Servers_Get((i + j) >> 1); /* partition the list */ while (i <= j) { while (LTable_SortOrder(mid, Servers_Get(i)) < 0) i++; while (LTable_SortOrder(mid, Servers_Get(j)) > 0) j--; QuickSort_Swap_Maybe(); } /* recurse into the smaller subset */ QuickSort_Recurse(LTable_QuickSort) } } void LTable_Sort(struct LTable* w) { sortingCol = w->sortingCol; FetchServersTask_ResetOrder(); if (FetchServersTask.numServers) LTable_QuickSort(0, FetchServersTask.numServers - 1); LTable_ApplyFilter(w); LTable_ShowSelected(w); } void LTable_ShowSelected(struct LTable* w) { int i = LTable_GetSelectedIndex(w); if (i == -1) return; if (i >= w->topRow + w->visibleRows) { w->topRow = i - (w->visibleRows - 1); } if (i < w->topRow) w->topRow = i; LTable_ClampTopRow(w); } BitmapCol LTable_RowColor(int row, cc_bool selected, cc_bool featured) { BitmapCol featSelColor = BitmapColor_RGB( 50, 53, 0); BitmapCol featuredColor = BitmapColor_RGB(101, 107, 0); BitmapCol selectedColor = BitmapColor_RGB( 40, 40, 40); if (featured) { return selected ? featSelColor : featuredColor; } else if (selected) { return selectedColor; } if (!Launcher_Theme.ClassicBackground) { return BitmapColor_RGB(20, 20, 10); } else { return (row & 1) == 0 ? Launcher_Theme.BackgroundColor : 0; } } #endif