diff --git a/workspace/all/battery/battery.c b/workspace/all/battery/battery.c index 990d63ac7..fc6e064fc 100644 --- a/workspace/all/battery/battery.c +++ b/workspace/all/battery/battery.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -13,9 +14,7 @@ #include #define GRAPH_LINE_WIDTH 1 - -// Space between pixels, higher is more transparent -#define GRAPH_BACKGROUND_OPACITY 4 +#define GRAPH_AREA_ALPHA 127 // A multiple of 4 is recommended #define GRAPH_MAX_FULL_PAGES 8 @@ -304,6 +303,10 @@ void compute_graph(void) } current_index = (graph.layout.graph_max_size - 1) - duration_to_pixel(total_duration); + if (current_index < 0) { + // the history fills the graph; this row is out of range + break; + } graph.graphic[current_index].is_charging = is_charging; if (bat_perc > 100) @@ -389,14 +392,14 @@ void compute_graph(void) } } -void drawBatteryIcon(int percent, SDL_Rect dst) { +void drawBatteryIcon(int percent, SDL_Rect dst, Uint32 color) { // taken from GFX_blitBattery int x = dst.x; int y = dst.y; //SDL_Rect rect = asset_rects[ASSET_BATTERY]; SDL_Rect rect = (SDL_Rect){SCALE4(47,51,17,10)}; - GFX_blitAsset(ASSET_BATTERY, NULL, screen, &(SDL_Rect){x,y}); + GFX_blitAssetColor(ASSET_BATTERY, NULL, screen, &(SDL_Rect){x,y}, color); //rect = asset_rects[ASSET_BATTERY_FILL]; rect = (SDL_Rect){SCALE4(81,33,12,6)}; SDL_Rect clip = rect; @@ -406,11 +409,71 @@ void drawBatteryIcon(int percent, SDL_Rect dst) { clip.x = rect.w - clip.w; clip.y = 0; - GFX_blitAsset(ASSET_BATTERY_FILL, &clip, screen, &(SDL_Rect){x+SCALE1(3)+clip.x,y+SCALE1(2)}); + GFX_blitAssetColor(ASSET_BATTERY_FILL, &clip, screen, &(SDL_Rect){x+SCALE1(3)+clip.x,y+SCALE1(2)}, color); +} + +// Returns pixel drawn over bg at the given alpha (Porter-Duff "over"). The screen surface +// holds premultiplied alpha (the display composes it that way), thus a transparent bg +// gives a translucent pixel and an opaque bg (a grid line) stays visible below. +static Uint32 blendOver(const SDL_PixelFormat *format, Uint32 pixel, Uint32 bg, Uint8 alpha) +{ + Uint8 pr, pg, pb, pa, br, bgg, bb, ba; + SDL_GetRGBA(pixel, format, &pr, &pg, &pb, &pa); + SDL_GetRGBA(bg, format, &br, &bgg, &bb, &ba); + int sa = alpha * pa / 255; // effective alpha of the pixel + int inv = 255 - sa; + return SDL_MapRGBA(format, + (pr * sa + br * inv) / 255, + (pg * sa + bgg * inv) / 255, + (pb * sa + bb * inv) / 255, + sa + ba * inv / 255); +} + +// Returns color1 mixed with color2 (percent1 : 100 - percent1), alpha included +static SDL_Color mix_colors(SDL_Color color1, int percent1, SDL_Color color2) +{ + int percent2 = 100 - percent1; + return (SDL_Color){ + (Uint8)((color1.r * percent1 + color2.r * percent2) / 100), + (Uint8)((color1.g * percent1 + color2.g * percent2) / 100), + (Uint8)((color1.b * percent1 + color2.b * percent2) / 100), + (Uint8)((color1.a * percent1 + color2.a * percent2) / 100)}; +} + +// Returns the RGBA (CFG_getColor) of the theme color with the larger chroma +static uint32_t moreColorfulColor(int colorId1, int colorId2) +{ + int chroma[2]; + int colors[2] = {colorId1, colorId2}; + for (int i = 0; i < 2; i++) { + SDL_Color c = uintToColour(CFG_getColor(colors[i])); + int hi = c.r, lo = c.r; + if (c.g > hi) hi = c.g; + if (c.b > hi) hi = c.b; + if (c.g < lo) lo = c.g; + if (c.b < lo) lo = c.b; + chroma[i] = hi - lo; + } + return CFG_getColor(chroma[1] > chroma[0] ? colorId2 : colorId1); } void renderPage() { + const SDL_Color bg_color = uintToColour(CFG_getColor(COLOR_BACKGROUND)); + const SDL_Color text_color = uintToColour(CFG_getColor(COLOR_LIST_TEXT)); + const SDL_Color grid_color = mix_colors(bg_color, 80, text_color); + const SDL_Color discharging_color = text_color; + const SDL_Color charging_color = uintToColour(moreColorfulColor(COLOR_MAIN, COLOR_ACCENT)); + const SDL_Color estimated_color = uintToColour(CFG_getColor(COLOR_HINT)); + + const Uint32 text_color_rgb = SDL_MapRGBA(screen->format, text_color.r, text_color.g, text_color.b, text_color.a); + const Uint32 grid_color_rgb = SDL_MapRGBA(screen->format, grid_color.r, grid_color.g, grid_color.b, grid_color.a); + const Uint32 discharging_color_rgb = SDL_MapRGBA(screen->format, discharging_color.r, discharging_color.g, discharging_color.b, discharging_color.a); + const Uint32 charging_color_rgb = SDL_MapRGBA(screen->format, charging_color.r, charging_color.g, charging_color.b, charging_color.a); + const Uint32 estimated_color_rgb = SDL_MapRGBA(screen->format, estimated_color.r, estimated_color.g, estimated_color.b, estimated_color.a); + + const uint32_t red_battery_color = 0xff0000ff; + const struct SDL_Point tl = {graph.layout.graph_display_start_x, graph.layout.graph_display_start_y}; const struct SDL_Point br = {graph.layout.graph_display_start_x + graph.layout.graph_display_size_x, graph.layout.graph_display_start_y + graph.layout.graph_display_size_y}; const int grid_step_x = 2 * (int)(graph.layout.graph_display_size_x / GRAPH_SEGMENTS); // every second "segment" @@ -418,11 +481,11 @@ void renderPage() // grid verticals for (int x = tl.x; x <= br.x; x+= grid_step_x) - drawLine(x, tl.y, x, br.y, RGB_DARK_GRAY); - drawLine(br.x, tl.y, br.x, br.y, RGB_DARK_GRAY); // close the last segment early + drawLine(x, tl.y, x, br.y, grid_color_rgb); + drawLine(br.x, tl.y, br.x, br.y, grid_color_rgb); // close the last segment early // grid horizontals for (int y = tl.y; y <= br.y; y += grid_step_y) - drawLine(tl.x, y, br.x, y, RGB_DARK_GRAY); + drawLine(tl.x, y, br.x, y, grid_color_rgb); switch (current_zoom) { @@ -453,52 +516,39 @@ void renderPage() switch_zoom_profile(segment_duration); // x axis labels - renderText(label[0], font.small, COLOR_WHITE, &(SDL_Rect){graph.layout.label1_x, graph.layout.label_y, 32, 32}); - renderText(label[1], font.small, COLOR_WHITE, &(SDL_Rect){graph.layout.label2_x, graph.layout.label_y, 32, 32}); - renderText(label[2], font.small, COLOR_WHITE, &(SDL_Rect){graph.layout.label3_x, graph.layout.label_y, 32, 32}); - renderText(label[3], font.small, COLOR_WHITE, &(SDL_Rect){graph.layout.label4_x, graph.layout.label_y, 32, 32}); + renderText(label[0], font.small, text_color, &(SDL_Rect){graph.layout.label1_x, graph.layout.label_y, 32, 32}); + renderText(label[1], font.small, text_color, &(SDL_Rect){graph.layout.label2_x, graph.layout.label_y, 32, 32}); + renderText(label[2], font.small, text_color, &(SDL_Rect){graph.layout.label3_x, graph.layout.label_y, 32, 32}); + renderText(label[3], font.small, text_color, &(SDL_Rect){graph.layout.label4_x, graph.layout.label_y, 32, 32}); // y axis "labels" - drawBatteryIcon(100, (SDL_Rect){graph.layout.icon_x, graph.layout.icon1_y}); - drawBatteryIcon(66, (SDL_Rect){graph.layout.icon_x, graph.layout.icon2_y}); - drawBatteryIcon(33, (SDL_Rect){graph.layout.icon_x, graph.layout.icon3_y}); - drawBatteryIcon(0, (SDL_Rect){graph.layout.icon_x, graph.layout.icon4_y}); + drawBatteryIcon(100, (SDL_Rect){graph.layout.icon_x, graph.layout.icon1_y}, text_color_rgb); + drawBatteryIcon(66, (SDL_Rect){graph.layout.icon_x, graph.layout.icon2_y}, text_color_rgb); + drawBatteryIcon(33, (SDL_Rect){graph.layout.icon_x, graph.layout.icon3_y}, text_color_rgb); + drawBatteryIcon(0, (SDL_Rect){graph.layout.icon_x, graph.layout.icon4_y}, text_color_rgb); char text_line[255]; sprintf(text_line, "Since Charge: %s", session_duration); - renderText(text_line, font.medium, COLOR_WHITE, &(SDL_Rect){graph.layout.label_session_x, graph.layout.label_session_y, graph.layout.label_size_x, graph.layout.label_size_y}); + renderText(text_line, font.medium, text_color, &(SDL_Rect){graph.layout.label_session_x, graph.layout.label_session_y, graph.layout.label_size_x, graph.layout.label_size_y}); sprintf(text_line, "Current: %s", current_percentage); - renderText(text_line, font.medium, COLOR_WHITE, &(SDL_Rect){graph.layout.label_current_x, graph.layout.label_current_y, graph.layout.label_size_x, graph.layout.label_size_y}); + renderText(text_line, font.medium, text_color, &(SDL_Rect){graph.layout.label_current_x, graph.layout.label_current_y, graph.layout.label_size_x, graph.layout.label_size_y}); sprintf(text_line, "Remaining: %s", session_left); - renderTextAlignRight(text_line, font.medium, COLOR_WHITE, &(SDL_Rect){graph.layout.label_left_x, graph.layout.label_left_y, graph.layout.label_size_x, graph.layout.label_size_y}); + renderTextAlignRight(text_line, font.medium, text_color, &(SDL_Rect){graph.layout.label_left_x, graph.layout.label_left_y, graph.layout.label_size_x, graph.layout.label_size_y}); sprintf(text_line, "Longest: %s", session_best); - renderTextAlignRight(text_line, font.medium, COLOR_WHITE, &(SDL_Rect){graph.layout.label_best_x, graph.layout.label_best_y, graph.layout.label_size_x, graph.layout.label_size_y}); + renderTextAlignRight(text_line, font.medium, text_color, &(SDL_Rect){graph.layout.label_best_x, graph.layout.label_best_y, graph.layout.label_size_x, graph.layout.label_size_y}); int half_line_width = (int)(GRAPH_LINE_WIDTH) / 2; - Uint32 white_pixel_color = RGB_GRAY; - Uint32 red_pixel_color = RGB_WHITE; - Uint32 blue_pixel_color = RGB_LIGHT_GRAY; - Uint32 pixel_color = white_pixel_color; - - // monochrome is more MinUI, but some colours are nice - const bool ilikeitcolourful = true; - if(ilikeitcolourful) { - white_pixel_color = SDL_MapRGBA(screen->format, 255, 255, 255, 255); - red_pixel_color = SDL_MapRGBA(screen->format, 255, 170, 170, 255); - blue_pixel_color = SDL_MapRGBA(screen->format, 89, 167, 255, 255); - pixel_color = white_pixel_color; - } - int x; int y; int x_end = 0; int y_end = 0; int zoom_level = (int)segment_duration / GRAPH_SEGMENT_HIGH; + Uint32 pixel_color = discharging_color_rgb; if (SDL_LockSurface(screen) == 0) { @@ -513,14 +563,12 @@ void renderPage() bool is_charging = graph.graphic[i + current_index].is_charging; bool is_estimated = graph.graphic[i + current_index].is_estimated; - // if ((!is_charging) - if ((!is_charging) && (!is_estimated)) - pixel_color = white_pixel_color; - else if (is_charging) - pixel_color = red_pixel_color; + + if (is_charging) + pixel_color = charging_color_rgb; else if (is_estimated) { - pixel_color = blue_pixel_color; + pixel_color = estimated_color_rgb; // magic numbers everywhere... if (y < 5 && x < graph_display_right) { @@ -528,6 +576,8 @@ void renderPage() y_end = graph_display_bottom - 45; } } + else + pixel_color = discharging_color_rgb; if (x < graph_display_right && y > 0) { @@ -542,22 +592,18 @@ void renderPage() } // Area under the graph - if ((x % GRAPH_BACKGROUND_OPACITY) == 0) + for (int k = y - half_line_width - 1; k > 0; k--) { - for (int k = y; k > 0; k--) - { - if ((k % GRAPH_BACKGROUND_OPACITY) == 0) - { - int index = (graph_display_bottom - k) * screen->pitch + x * screen->format->BytesPerPixel; - *((Uint32 *)((Uint8 *)screen->pixels + index)) = pixel_color; - } - } + int index = (graph_display_bottom - k) * screen->pitch + x * screen->format->BytesPerPixel; + Uint32 *pixel = (Uint32 *)((Uint8 *)screen->pixels + index); + *pixel = blendOver(screen->format, pixel_color, *pixel, GRAPH_AREA_ALPHA); } } } SDL_UnlockSurface(screen); - if (x_end != 0) - GFX_blitAsset(ASSET_BATTERY_LOW, NULL, screen, &(SDL_Rect){x_end, y_end}); + if (x_end != 0) { + GFX_blitAssetColor(ASSET_BATTERY_LOW, NULL, screen, &(SDL_Rect){x_end, y_end}, red_battery_color); + } } } @@ -647,6 +693,8 @@ int main(int argc, char *argv[]) signal(SIGINT, sigHandler); signal(SIGTERM, sigHandler); + const SDL_Color title_color = uintToColour(CFG_getColor(COLOR_HINT)); + initLayout(); compute_graph(); renderPage(); @@ -750,7 +798,7 @@ int main(int argc, char *argv[]) max_width = MIN(max_width, text_width); SDL_Surface *text; - text = TTF_RenderUTF8_Blended(font.large, title, COLOR_WHITE); + text = TTF_RenderUTF8_Blended(font.large, title, title_color); GFX_blitPill(ASSET_BLACK_PILL, screen, &(SDL_Rect){SCALE1(PADDING), SCALE1(PADDING), max_width, SCALE1(PILL_SIZE)}); SDL_BlitSurface(text, &(SDL_Rect){0, 0, max_width - SCALE1(BUTTON_PADDING * 2), text->h}, screen, &(SDL_Rect){SCALE1(PADDING + BUTTON_PADDING), SCALE1(PADDING + 4)}); diff --git a/workspace/all/clock/clock.c b/workspace/all/clock/clock.c index 3734f6329..3c0f5f82b 100644 --- a/workspace/all/clock/clock.c +++ b/workspace/all/clock/clock.c @@ -22,14 +22,15 @@ enum { int main(int argc , char* argv[]) { PWR_setCPUSpeed(CPU_SPEED_AUTO); - SDL_Surface* screen = GFX_init(MODE_MENU); + SDL_Surface* screen = GFX_init(MODE_MAIN); PAD_init(); PWR_init(); InitSettings(); - // TODO: make use of SCALE1() SDL_Surface* digits = SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, SCALE1(120), SCALE1(16), 32, screen->format->format); - SDL_FillRect(digits, NULL, RGB_BLACK); + SDL_FillRect(digits, NULL, SDL_MapRGBA(digits->format, 0, 0, 0, 0)); + SDL_Color text_color = uintToColour(CFG_getColor(COLOR_LIST_TEXT)); + SDL_Color selected_bar_color = uintToColour(CFG_getColor(COLOR_HINT)); SDL_Surface* digit; char* chars[] = { "0","1","2","3","4","5","6","7","8","9","/",":", NULL }; @@ -41,7 +42,8 @@ int main(int argc , char* argv[]) { #define CHAR_SLASH 10 #define CHAR_COLON 11 while (c = chars[i]) { - digit = TTF_RenderUTF8_Blended(font.large, c, COLOR_WHITE); + digit = TTF_RenderUTF8_Blended(font.large, c, text_color); + SDL_SetSurfaceBlendMode(digit, SDL_BLENDMODE_NONE); // copy glyph alpha into the transparent strip int y = i==CHAR_COLON ? SCALE1(-1.5) : 0; // : sits too low naturally // TODO: y offset is wrong here // printf("%s x:%i y:%i SCALE1(DIGIT_HEIGHT):%i SCALE1(DIGIT_HEIGHT) - digit->h:%i\n", c, (i * SCALE1(DIGIT_WIDTH)), y, SCALE1(DIGIT_HEIGHT), SCALE1(DIGIT_HEIGHT) - digit->h); fflush(stdout); @@ -73,7 +75,8 @@ int main(int argc , char* argv[]) { return x + SCALE1(10); } void blitBar(int x, int y, int w) { - GFX_blitPill(ASSET_UNDERLINE, screen, &(SDL_Rect){x,y,w}); + Uint32 bar_color_rgb = SDL_MapRGB(screen->format, selected_bar_color.r, selected_bar_color.g, selected_bar_color.b); + GFX_blitPillColor(ASSET_UNDERLINE, screen, &(SDL_Rect){x,y,w}, bar_color_rgb, RGB_WHITE); } int blitNumber(int num, int x, int y) { int n; @@ -294,7 +297,7 @@ int main(int argc , char* argv[]) { int ampm_w; if (!show_24hour) { x += SCALE1(10); // space - SDL_Surface* text = TTF_RenderUTF8_Blended(font.large, am_selected ? "AM" : "PM", COLOR_WHITE); + SDL_Surface* text = TTF_RenderUTF8_Blended(font.large, am_selected ? "AM" : "PM", text_color); ampm_w = text->w + SCALE1(2); SDL_BlitSurface(text, NULL, screen, &(SDL_Rect){x,y-SCALE1(3)}); SDL_FreeSurface(text); diff --git a/workspace/all/common/api.c b/workspace/all/common/api.c index e107225da..1424b0020 100644 --- a/workspace/all/common/api.c +++ b/workspace/all/common/api.c @@ -2169,6 +2169,11 @@ void GFX_blitButton(char *hint, char *button, SDL_Surface *dst, SDL_Rect *dst_re SDL_FreeSurface(text); } void GFX_blitMessage(TTF_Font *font, char *msg, SDL_Surface *dst, SDL_Rect *dst_rect) +{ + GFX_blitMessageColor(font, msg, uintToColour(CFG_getColor(COLOR_LIST_TEXT)), dst, dst_rect); +} + +void GFX_blitMessageColor(TTF_Font *font, char *msg, SDL_Color color, SDL_Surface *dst, SDL_Rect *dst_rect) { if (!dst_rect) dst_rect = &(SDL_Rect){0, 0, dst->w, dst->h}; @@ -2213,7 +2218,7 @@ void GFX_blitMessage(TTF_Font *font, char *msg, SDL_Surface *dst, SDL_Rect *dst_ if (len) { - text = TTF_RenderUTF8_Blended_Wrapped(font, line, uintToColour(CFG_getColor(COLOR_LIST_TEXT)), dst_rect->w); + text = TTF_RenderUTF8_Blended_Wrapped(font, line, color, dst_rect->w); int x = dst_rect->x; x += (dst_rect->w - text->w) / 2; SDL_BlitSurface(text, NULL, dst, &(SDL_Rect){x, y}); diff --git a/workspace/all/common/api.h b/workspace/all/common/api.h index e7b5d6808..dd219ee4f 100644 --- a/workspace/all/common/api.h +++ b/workspace/all/common/api.h @@ -400,6 +400,7 @@ void GFX_blitBatteryAtPosition(SDL_Surface *dst, int x, int y); int GFX_getButtonWidth(char* hint, char* button); void GFX_blitButton(char* hint, char*button, SDL_Surface* dst, SDL_Rect* dst_rect); void GFX_blitMessage(TTF_Font* font, char* msg, SDL_Surface* dst, SDL_Rect* dst_rect); +void GFX_blitMessageColor(TTF_Font* font, char* msg, SDL_Color color, SDL_Surface* dst, SDL_Rect* dst_rect); int GFX_blitHardwareGroup(SDL_Surface* dst, int show_setting); void GFX_blitHardwareHints(SDL_Surface* dst, int show_setting); diff --git a/workspace/all/gametime/gametime.c b/workspace/all/gametime/gametime.c index a30f67c5c..e7ace0c84 100644 --- a/workspace/all/gametime/gametime.c +++ b/workspace/all/gametime/gametime.c @@ -196,6 +196,17 @@ void renderList(int count, int start, int end, int selected) char average[25]; char plays[25]; + uint32_t main_color_255 = CFG_getColor(COLOR_MAIN); + SDL_Color main_color = uintToColour(main_color_255); + Uint32 selected_row_bg_rgb = SDL_MapRGB(screen->format, main_color.r, main_color.g, main_color.b); + SDL_Color icon_bg_color = uintToColour(CFG_getColor(COLOR_ACCENT)); + Uint32 icon_bg_color_rgb = SDL_MapRGB(screen->format, icon_bg_color.r, icon_bg_color.g, icon_bg_color.b); + + SDL_Color textColorSelectedRow = uintToColour(CFG_getColor(COLOR_LIST_TEXT_SELECTED)); + SDL_Color timeColorSelectedRow = uintToColour(CFG_getColor(COLOR_ACCENT)); + SDL_Color textColorUnselectedRow = uintToColour(CFG_getColor(COLOR_LIST_TEXT)); + SDL_Color timeColorUnselectedRow = uintToColour(CFG_getColor(COLOR_HINT)); + int num_width = 0; const int elemHeight = SCALE1(BIG_PILL_SIZE); @@ -205,16 +216,20 @@ void renderList(int count, int start, int end, int selected) int selected_row = selected - start; for (int index=start,row=0; indexplay_activity[index]; ROM *rom = entry->rom; - renderRoundedRectangle((SDL_Rect){ - layout.list_display_start_x, - layout.list_display_start_y + row * elemHeight, - layout.list_display_size_x, - elemHeight - }, isSelected ? RGB_WHITE : RGB_BLACK, SCALE1(24)); + if (isSelected) { + renderRoundedRectangle((SDL_Rect){ + layout.list_display_start_x, + layout.list_display_start_y + row * elemHeight, + layout.list_display_size_x, + elemHeight + }, selected_row_bg_rgb, SCALE1(24)); + } SDL_Surface *romImage = romImages[index]; if (romImage) { @@ -234,7 +249,7 @@ void renderList(int count, int start, int end, int selected) SCALE1(IMG_MAX_HEIGHT) }; - renderRoundedRectangle(rectRomImage, RGB_DARK_GRAY, SCALE1(18)); + renderRoundedRectangle(rectRomImage, icon_bg_color_rgb, SCALE1(18)); // TODO: no getter exposed for this right now //SDL_Rect rect = asset_rects[ASSET_GAMEPAD]; @@ -244,15 +259,10 @@ void renderList(int count, int start, int end, int selected) x += (SCALE1(IMG_MAX_WIDTH) - rect.w) / 2; y += (SCALE1(IMG_MAX_HEIGHT) - rect.h) / 2; - GFX_blitAssetColor(ASSET_GAMEPAD, NULL, screen, &(SDL_Rect){x,y}, THEME_COLOR1_255); + GFX_blitAssetColor(ASSET_GAMEPAD, NULL, screen, &(SDL_Rect){x,y}, main_color_255); } cleanName(rom_name, rom->name); - SDL_Color textColor = COLOR_WHITE; - if(isSelected) { - //textColor = uintToColour(THEME_COLOR1); - textColor = COLOR_BLACK; - } renderText(rom_name, font.medium, textColor, &(SDL_Rect){ layout.list_display_start_x + num_width + thumbMargin + SCALE1(IMG_MAX_WIDTH), layout.list_display_start_y + thumbMargin / 2 + elemHeight * row, @@ -271,9 +281,7 @@ void renderList(int count, int start, int end, int selected) textHeight }; for (int i = 0; i < 6; i++) { - SDL_Color detailCol = i % 2 == 0 ? COLOR_DARK_TEXT : uintToColour(THEME_COLOR2_255); - //SDL_Color detailCol = uintToColour(i % 2 == 0 ? THEME_COLOR3_255 : THEME_COLOR2_255); - //SDL_Color detailCol = i % 2 == 0 ? COLOR_DARK_TEXT : COLOR_LIGHT_TEXT; + SDL_Color detailCol = i % 2 == 0 ? textColor : timeColor; detailsRect.x += renderText(details[i], font.small, detailCol, &detailsRect); } } @@ -337,6 +345,9 @@ int main(int argc, char *argv[]) initLayout(); preloadRomImages(); + + SDL_Color titleColor = uintToColour(CFG_getColor(COLOR_HINT)); + int count = play_activities->count; int selected = 0; int start = 0; @@ -414,8 +425,7 @@ int main(int argc, char *argv[]) max_width = MIN(max_width, text_width); SDL_Surface *text; - text = TTF_RenderUTF8_Blended(font.large, title, COLOR_WHITE); - GFX_blitPill(ASSET_BLACK_PILL, screen, &(SDL_Rect){SCALE1(PADDING), SCALE1(PADDING), max_width, SCALE1(PILL_SIZE)}); + text = TTF_RenderUTF8_Blended(font.large, title, titleColor); SDL_BlitSurface(text, &(SDL_Rect){0, 0, max_width - SCALE1(BUTTON_PADDING * 2), text->h}, screen, &(SDL_Rect){SCALE1(PADDING + BUTTON_PADDING), SCALE1(PADDING + 4)}); SDL_FreeSurface(text); } diff --git a/workspace/all/ledcontrol/ledcontrol.c b/workspace/all/ledcontrol/ledcontrol.c index 8441d77fc..6b37fb0db 100644 --- a/workspace/all/ledcontrol/ledcontrol.c +++ b/workspace/all/ledcontrol/ledcontrol.c @@ -307,6 +307,10 @@ int main(int argc, char *argv[]) if (name) lightnames[i] = name; } + SDL_Color title_color = uintToColour(CFG_getColor(COLOR_HINT)); + SDL_Color selected_text_color = uintToColour(CFG_getColor(COLOR_LIST_TEXT_SELECTED)); + SDL_Color unselected_text_color = uintToColour(CFG_getColor(COLOR_LIST_TEXT)); + bool running = true; int selected_light = 0; int selected_setting = 0; @@ -381,8 +385,7 @@ int main(int argc, char *argv[]) max_width = MIN(max_width, text_width); SDL_Surface *text; - text = TTF_RenderUTF8_Blended(font.medium, title, COLOR_WHITE); - GFX_blitPill(ASSET_BLACK_PILL, screen, &(SDL_Rect){SCALE1(PADDING), SCALE1(PADDING), max_width, SCALE1(PILL_SIZE)}); + text = TTF_RenderUTF8_Blended(font.medium, title, title_color); SDL_BlitSurface(text, &(SDL_Rect){0, 0, max_width - SCALE1(BUTTON_PADDING * 2), text->h}, screen, &(SDL_Rect){SCALE1(PADDING + BUTTON_PADDING), SCALE1(PADDING + 4)}); SDL_FreeSurface(text); @@ -409,7 +412,7 @@ int main(int argc, char *argv[]) { char setting_text[256]; bool selected = (j == selected_setting); - SDL_Color current_color = selected ? COLOR_BLACK : COLOR_WHITE; + SDL_Color current_color = selected ? selected_text_color : unselected_text_color; int y = SCALE1(PADDING + PILL_SIZE * (j + 1)); @@ -417,8 +420,8 @@ int main(int argc, char *argv[]) snprintf(setting_text, sizeof(setting_text), "%s: %s", settings_labels[j], effect_name_for(selected_light, settings_values[j])); SDL_Surface *text = TTF_RenderUTF8_Blended(font.medium, setting_text, current_color); int text_width = text->w + SCALE1(BUTTON_PADDING * 2); - GFX_blitPill(selected ? ASSET_WHITE_PILL : ASSET_BLACK_PILL, screen, - &(SDL_Rect){SCALE1(PADDING), y, text_width, SCALE1(PILL_SIZE)}); + if (selected) + GFX_blitPillDark(ASSET_WHITE_PILL, screen, &(SDL_Rect){SCALE1(PADDING), y, text_width, SCALE1(PILL_SIZE)}); SDL_BlitSurface(text, &(SDL_Rect){0, 0, text->w, text->h}, screen, &(SDL_Rect){SCALE1(PADDING + BUTTON_PADDING), y + SCALE1(4)}); @@ -427,8 +430,8 @@ int main(int argc, char *argv[]) snprintf(setting_text, sizeof(setting_text), "%s", settings_labels[j]); SDL_Surface *text = TTF_RenderUTF8_Blended(font.medium, setting_text, current_color); int text_width = text->w + SCALE1(BUTTON_PADDING * 2); - GFX_blitPill(selected ? ASSET_WHITE_PILL : ASSET_BLACK_PILL, screen, - &(SDL_Rect){SCALE1(PADDING), y, text_width + SCALE1(BUTTON_MARGIN + BUTTON_SIZE), SCALE1(PILL_SIZE)}); + if (selected) + GFX_blitPillDark(ASSET_WHITE_PILL, screen, &(SDL_Rect){SCALE1(PADDING), y, text_width + SCALE1(BUTTON_MARGIN + BUTTON_SIZE), SCALE1(PILL_SIZE)}); SDL_BlitSurface(text, &(SDL_Rect){0, 0, text->w, text->h}, screen, &(SDL_Rect){SCALE1(PADDING + BUTTON_PADDING), y + SCALE1(4)}); @@ -443,8 +446,8 @@ int main(int argc, char *argv[]) snprintf(setting_text, sizeof(setting_text), "%s: %d", settings_labels[j], settings_values[j]); SDL_Surface *text = TTF_RenderUTF8_Blended(font.medium, setting_text, current_color); int text_width = text->w + SCALE1(BUTTON_PADDING * 2); - GFX_blitPill(selected ? ASSET_WHITE_PILL : ASSET_BLACK_PILL, screen, - &(SDL_Rect){SCALE1(PADDING), y, text_width, SCALE1(PILL_SIZE)}); + if (selected) + GFX_blitPillDark(ASSET_WHITE_PILL, screen, &(SDL_Rect){SCALE1(PADDING), y, text_width, SCALE1(PILL_SIZE)}); SDL_BlitSurface(text, &(SDL_Rect){0, 0, text->w, text->h}, screen, &(SDL_Rect){SCALE1(PADDING + BUTTON_PADDING), y + SCALE1(4)}); diff --git a/workspace/all/minarch/ma_frontend_opts.c b/workspace/all/minarch/ma_frontend_opts.c index 3c786b96c..92f1357b5 100644 --- a/workspace/all/minarch/ma_frontend_opts.c +++ b/workspace/all/minarch/ma_frontend_opts.c @@ -22,7 +22,7 @@ int Menu_messageWithFont(char* message, char** pairs, TTF_Font* f) { GFX_clear(screen); - GFX_blitMessage(f, message, screen, &(SDL_Rect){SCALE1(PADDING),SCALE1(PADDING),screen->w-SCALE1(2*PADDING),screen->h-SCALE1(PILL_SIZE+PADDING)}); + GFX_blitMessageColor(f, message, COLOR_WHITE, screen, &(SDL_Rect){SCALE1(PADDING),SCALE1(PADDING),screen->w-SCALE1(2*PADDING),screen->h-SCALE1(PILL_SIZE+PADDING)}); GFX_blitButtonGroup(pairs, 0, screen, 1); GFX_flip(screen); dirty = 0; diff --git a/workspace/all/minarch/ma_menu.c b/workspace/all/minarch/ma_menu.c index 85ca4341a..d82c0c85c 100644 --- a/workspace/all/minarch/ma_menu.c +++ b/workspace/all/minarch/ma_menu.c @@ -2060,8 +2060,8 @@ void Menu_loop(void) { else { SDL_Rect preview_rect = {ox,oy,hw,hh}; SDL_FillRect(screen, &preview_rect, SDL_MapRGBA(screen->format,0,0,0,255)); - if (menu.save_exists) GFX_blitMessage(font.large, "No Preview", screen, &preview_rect); - else GFX_blitMessage(font.large, "Empty Slot", screen, &preview_rect); + if (menu.save_exists) GFX_blitMessageColor(font.large, "No Preview", COLOR_WHITE, screen, &preview_rect); + else GFX_blitMessageColor(font.large, "Empty Slot", COLOR_WHITE, screen, &preview_rect); } // pagination diff --git a/workspace/all/settings/keyboardprompt.cpp b/workspace/all/settings/keyboardprompt.cpp index 161af210e..0593a7192 100644 --- a/workspace/all/settings/keyboardprompt.cpp +++ b/workspace/all/settings/keyboardprompt.cpp @@ -304,6 +304,20 @@ const KeyboardLayout &KeyboardPrompt::getCurrentLayout(const AppState &state) void KeyboardPrompt::drawKeyboard(SDL_Surface *screen, const AppState &state) { + const SDL_Color title_color = uintToColour(CFG_getColor(COLOR_HINT)); + const SDL_Color theme_bg_color = uintToColour(CFG_getColor(COLOR_BACKGROUND)); + + const SDL_Color key_text_color = uintToColour(CFG_getColor(COLOR_LIST_TEXT)); + const Uint32 key_text_color_rgba = SDL_MapRGBA(screen->format, key_text_color.r, key_text_color.g, key_text_color.b, key_text_color.a); + + // Something in between bg color and text color + const SDL_Color key_body_color = { + (Uint8)((theme_bg_color.r * 80 + key_text_color.r * 20) / 100), + (Uint8)((theme_bg_color.g * 80 + key_text_color.g * 20) / 100), + (Uint8)((theme_bg_color.b * 80 + key_text_color.b * 20) / 100), + (Uint8)((theme_bg_color.a * 80 + key_text_color.a * 20) / 100)}; + const Uint32 key_body_color_rgba = SDL_MapRGBA(screen->format, key_body_color.r, key_body_color.g, key_body_color.b, key_body_color.a); + // determine which keyboard layout to use based on current state const KeyboardLayout *currentLayout = nullptr; if (state.keyboard.layout == 0) @@ -318,13 +332,17 @@ void KeyboardPrompt::drawKeyboard(SDL_Surface *screen, const AppState &state) char *hints[] = {(char *)("Y"), (char *)("EXIT"), (char *)("X"), ((char *)"ENTER"), NULL}; GFX_blitButtonGroup(hints, 1, screen, 1); - // draw keyboard title + // draw keyboard title, centered in the space left of the status pill if (!state.keyboard.title.empty()) { - SDL_Surface *title = TTF_RenderUTF8_Blended(font.large, state.keyboard.title.c_str(), COLOR_WHITE); + int ow = GFX_blitHardwareGroup(screen, 0); + int max_width = screen->w - SCALE1(PADDING * 2) - ow; + char title_text[256]; + GFX_truncateText(font.large, state.keyboard.title.c_str(), title_text, max_width, 0); + SDL_Surface *title = TTF_RenderUTF8_Blended(font.large, title_text, title_color); SDL_Rect title_pos = { - (screen->w - title->w) / 2, // center horizontally - 20, // 20px from top + (screen->w - ow - title->w) / 2, + SCALE1(PADDING + 4), title->w, title->h}; SDL_BlitSurface(title, NULL, screen, &title_pos); @@ -333,8 +351,8 @@ void KeyboardPrompt::drawKeyboard(SDL_Surface *screen, const AppState &state) // draw input field with current text // todo: use TTF_SizeUTF8 to compute the width of the input field - SDL_Surface *input_placeholder = TTF_RenderUTF8_Blended(font.medium, "p", COLOR_WHITE); - SDL_Surface *input = TTF_RenderUTF8_Blended(font.medium, state.keyboard.current_text.c_str(), COLOR_WHITE); + SDL_Surface *input_placeholder = TTF_RenderUTF8_Blended(font.medium, "p", key_text_color); + SDL_Surface *input = TTF_RenderUTF8_Blended(font.medium, state.keyboard.current_text.c_str(), key_text_color); SDL_Rect input_pos = { (screen->w) / 2, input_placeholder->h * 2, @@ -353,7 +371,7 @@ void KeyboardPrompt::drawKeyboard(SDL_Surface *screen, const AppState &state) input_placeholder->h * 2, screen->w - 80, input_placeholder->h}; - SDL_FillRect(screen, &input_bg, SDL_MapRGB(screen->format, TRIAD_DARK_GRAY)); + SDL_FillRect(screen, &input_bg, key_body_color_rgba); SDL_BlitSurface(input, NULL, screen, &input_pos); SDL_FreeSurface(input); @@ -408,7 +426,9 @@ void KeyboardPrompt::drawKeyboard(SDL_Surface *screen, const AppState &state) if (key.empty()) continue; - SDL_Color text_color = (row == state.keyboard.row && col == state.keyboard.col) ? COLOR_BLACK : COLOR_WHITE; + bool key_selected = row == state.keyboard.row && col == state.keyboard.col; + SDL_Color text_color = key_selected ? key_body_color : key_text_color; + Uint32 bg_color = key_selected ? key_text_color_rgba : key_body_color_rgba; SDL_Surface *key_text = TTF_RenderUTF8_Blended(font.medium, key.c_str(), text_color); // special keys are not the same width as the other keys @@ -426,9 +446,6 @@ void KeyboardPrompt::drawKeyboard(SDL_Surface *screen, const AppState &state) default_key_size}; // draw key background - Uint32 bg_color = (row == state.keyboard.row && col == state.keyboard.col) - ? SDL_MapRGB(screen->format, TRIAD_WHITE) - : SDL_MapRGB(screen->format, TRIAD_DARK_GRAY); SDL_FillRect(screen, &key_pos, bg_color); // center text in key diff --git a/workspace/all/settings/menu.cpp b/workspace/all/settings/menu.cpp index b17a0cabb..2740dab8c 100644 --- a/workspace/all/settings/menu.cpp +++ b/workspace/all/settings/menu.cpp @@ -19,6 +19,7 @@ static bool overlayVisible = false; static OverlayDismissMode overlayDismissMode = OverlayDismissMode::None; static SDL_Surface* overlaySurface = nullptr; static Lock overlayLock; +static std::function backgroundPainter; static void drawOverlayLocal(SDL_Surface* screen); @@ -913,15 +914,24 @@ void MenuList::showOverlay(const std::string& message, OverlayDismissMode dismis // We want to force a draw right now since usually we are about to block WriteLock w(overlayLock); if (overlaySurface) { - // Clear the surface first to prevent text ghosting from previous + // Repaint the background first to prevent text ghosting from previous // overlay frames (the semi-transparent shadow doesn't fully obscure // old content, causing overlap when updated repeatedly in a loop) - SDL_FillRect(overlaySurface, NULL, SDL_MapRGB(overlaySurface->format, 0, 0, 0)); + if (backgroundPainter) + backgroundPainter(overlaySurface); + else + GFX_clear(overlaySurface); drawOverlayLocal(overlaySurface); GFX_flip(overlaySurface); } } +void MenuList::setBackgroundPainter(std::function painter) +{ + WriteLock w(overlayLock); + backgroundPainter = std::move(painter); +} + void MenuList::hideOverlay() { WriteLock w(overlayLock); @@ -951,6 +961,10 @@ static void drawOverlayLocal(SDL_Surface* screen) { SDL_FillRect(shadow, NULL, SDL_MapRGB(shadow->format, 0, 0, 0)); SDLX_SetAlpha(shadow, SDL_SRCALPHA, 200); // Semi-transparent black } + + // can't be done in shadow initialization since color may change + SDL_Color bg_color = uintToColour(CFG_getColor(COLOR_BACKGROUND)); + SDL_FillRect(shadow, NULL, SDL_MapRGB(shadow->format, bg_color.r, bg_color.g, bg_color.b)); SDL_BlitSurface(shadow, NULL, screen, NULL); SDL_Rect screenRect = {0, 0, screen->w, screen->h}; diff --git a/workspace/all/settings/menu.hpp b/workspace/all/settings/menu.hpp index 2e3b98181..ce85e2ecb 100644 --- a/workspace/all/settings/menu.hpp +++ b/workspace/all/settings/menu.hpp @@ -327,6 +327,8 @@ class MenuList ~MenuList(); MenuList(MenuList &) = delete; + // painter that showOverlay() uses to repaint the app background before it draws + static void setBackgroundPainter(std::function painter); static void showOverlay(const std::string& message, OverlayDismissMode dismissMode = OverlayDismissMode::None); static void hideOverlay(); static bool isOverlayVisible(); diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index f9f740470..feebb0ad0 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -1171,6 +1171,18 @@ int main(int argc, char *argv[]) bgbmp = scaled; } + auto drawBackground = [bgbmp](SDL_Surface *surface) { + GFX_clear(surface); + if(bgbmp) { + SDL_Rect image_rect = {0, 0, surface->w, surface->h}; + SDL_BlitSurface(bgbmp, NULL, surface, &image_rect); + } else { + SDL_Color bg_color = uintToColour(CFG_getColor(COLOR_BACKGROUND)); + SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, bg_color.r, bg_color.g, bg_color.b, bg_color.a)); + } + }; + MenuList::setBackgroundPainter(drawBackground); + // main content (list) // PADDING all around SDL_Rect listRect = {SCALE1(PADDING), SCALE1(PADDING), ctx.screen->w - SCALE1(PADDING * 2), ctx.screen->h - SCALE1(PADDING * 2)}; @@ -1204,14 +1216,7 @@ int main(int argc, char *argv[]) if (ctx.dirty) { - GFX_clear(ctx.screen); - if(bgbmp) { - SDL_Rect image_rect = {0, 0, ctx.screen->w, ctx.screen->h}; - SDL_BlitSurface(bgbmp, NULL, ctx.screen, &image_rect); - } else { - uint32_t bgc = CFG_getColor(COLOR_BACKGROUND); - SDL_FillRect(ctx.screen, NULL, SDL_MapRGBA(ctx.screen->format, (bgc >> 24) & 0xFF, (bgc >> 16) & 0xFF, (bgc >> 8) & 0xFF, bgc & 0xFF)); - } + drawBackground(ctx.screen); int ow = 0; @@ -1229,8 +1234,9 @@ int main(int argc, char *argv[]) int text_width = GFX_truncateText(font.large, "Some title", display_name, max_width, SCALE1(BUTTON_PADDING * 2)); max_width = MIN(max_width, text_width); + SDL_Color title_color = uintToColour(CFG_getColor(COLOR_HINT)); SDL_Surface *text; - text = TTF_RenderUTF8_Blended(font.large, display_name, COLOR_WHITE); + text = TTF_RenderUTF8_Blended(font.large, display_name, title_color); SDL_Rect target = {SCALE1(PADDING), SCALE1(PADDING), max_width, SCALE1(PILL_SIZE)}; GFX_blitPillLight(ASSET_WHITE_PILL, ctx.screen, &target); SDL_BlitSurfaceCPP(text, {0, 0, max_width - SCALE1(BUTTON_PADDING * 2), text->h}, ctx.screen, {SCALE1(PADDING + BUTTON_PADDING), SCALE1(PADDING + 4)});