diff --git a/Makefile.pc b/Makefile.pc index f8f9b73..c148eb8 100644 --- a/Makefile.pc +++ b/Makefile.pc @@ -9,7 +9,7 @@ endef test : pc_main/main.cpp pc_main/pc_launch.c \ common/menu.c common/font.c common/language.c common/launch.c \ - common/menu-entry.c common/menu-list.c common/text.c \ + common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \ common/nanojpeg.c common/ui.c common/math.c common/theme.c \ build_pc/tahoma24.o build_pc/tahoma12.o build_pc/interuimedium20.o build_pc/interuimedium30.o \ build_pc/interuiregular14.o build_pc/interuiregular18.o \ diff --git a/common/common.h b/common/common.h index b05dd5e..b68093f 100644 --- a/common/common.h +++ b/common/common.h @@ -137,3 +137,4 @@ static inline color_t FetchPixelColor(uint32_t x, uint32_t y) void DrawPixel(uint32_t x, uint32_t y, color_t clr); void DrawText(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text); void DrawTextTruncate(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text); +void GetTextDimensions(const ffnt_header_t* font, const char* text, uint32_t* width_out, uint32_t* height_out); \ No newline at end of file diff --git a/common/font.c b/common/font.c index 9123d90..7714716 100644 --- a/common/font.c +++ b/common/font.c @@ -166,3 +166,35 @@ void DrawTextTruncate(const ffnt_header_t* font, uint32_t x, uint32_t y, color_t { DrawText_(font, x, y, clr, text, max_width, end_text); } + +void GetTextDimensions(const ffnt_header_t* font, const char* text, uint32_t* width_out, uint32_t* height_out) +{ + uint32_t x = 0; + uint32_t width = 0, height = 0; + while (*text) + { + glyph_t glyph; + uint32_t codepoint = DecodeUTF8(&text); + + if (codepoint == '\n') + { + x = 0; + height += font->height; + continue; + } + + if (!FontLoadGlyph(&glyph, font, codepoint)) + { + if (!FontLoadGlyph(&glyph, font, '?')) + continue; + } + + x += glyph.advance; + + if (x > width) + width = x; + } + + *width_out = width; + *height_out = height; +} diff --git a/common/menu-entry.c b/common/menu-entry.c index 3f5c8a2..430a101 100644 --- a/common/menu-entry.c +++ b/common/menu-entry.c @@ -335,11 +335,21 @@ void menuEntryParseIcon(menuEntry_s* me) { njDone(); - me->icon_gfx_small = downscaleIcon(me->icon_gfx); + me->icon_gfx_small = downscaleImg(me->icon_gfx, 256, 256, 140, 140, IMAGE_MODE_RGB24); } -uint8_t *downscaleIcon(const uint8_t *image) { - uint8_t *out = (uint8_t*)malloc(140*140*3); +uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) { + uint8_t *out; + + switch (mode) { + case IMAGE_MODE_RGBA32: + out = (uint8_t*)malloc(destWidth*destHeight*4); + break; + + default: + out = (uint8_t*)malloc(destWidth*destHeight*3); + break; + } if (out == NULL) { return NULL; @@ -348,13 +358,13 @@ uint8_t *downscaleIcon(const uint8_t *image) { int tmpx, tmpy; int pos; float sourceX, sourceY; - int destWidth = 140, destHeight = 140; - float xScale = 256.0 / (float)destWidth; - float yScale = 256.0 / (float)destHeight; + float xScale = (float)srcWidth / (float)destWidth; + float yScale = (float)srcHeight / (float)destHeight; int pixelX, pixelY; uint8_t r1, r2, r3, r4; uint8_t g1, g2, g3, g4; uint8_t b1, b2, b3, b4; + uint8_t a1, a2, a3, a4; float fx, fy, fx1, fy1; int w1, w2, w3, w4; @@ -366,26 +376,57 @@ uint8_t *downscaleIcon(const uint8_t *image) { pixelY = (int)sourceY; // get colours from four surrounding pixels - pos = ((pixelY + 0) * 256 + pixelX + 0) * 3; + if (mode == IMAGE_MODE_RGBA32) + pos = ((pixelY + 0) * srcWidth + pixelX + 0) * 4; + else + pos = ((pixelY + 0) * srcWidth + pixelX + 0) * 3; + r1 = image[pos+0]; g1 = image[pos+1]; b1 = image[pos+2]; + + if (mode == IMAGE_MODE_RGBA32) + a1 = image[pos+3]; + + + if (mode == IMAGE_MODE_RGBA32) + pos = ((pixelY + 0) * srcWidth + pixelX + 1) * 4; + else + pos = ((pixelY + 0) * srcWidth + pixelX + 1) * 3; - pos = ((pixelY + 0) * 256 + pixelX + 1) * 3; r2 = image[pos+0]; g2 = image[pos+1]; b2 = image[pos+2]; - pos = ((pixelY + 1) * 256 + pixelX + 0) * 3; + if (mode == IMAGE_MODE_RGBA32) + a2 = image[pos+3]; + + + if (mode == IMAGE_MODE_RGBA32) + pos = ((pixelY + 1) * srcWidth + pixelX + 0) * 4; + else + pos = ((pixelY + 1) * srcWidth + pixelX + 0) * 3; + r3 = image[pos+0]; g3 = image[pos+1]; b3 = image[pos+2]; - pos = ((pixelY + 1) * 256 + pixelX + 1) * 3; + if (mode == IMAGE_MODE_RGBA32) + a3 = image[pos+3]; + + + if (mode == IMAGE_MODE_RGBA32) + pos = ((pixelY + 1) * srcWidth + pixelX + 1) * 4; + else + pos = ((pixelY + 1) * srcWidth + pixelX + 1) * 3; + r4 = image[pos+0]; g4 = image[pos+1]; b4 = image[pos+2]; + if (mode == IMAGE_MODE_RGBA32) + a4 = image[pos+3]; + // determine weights fx = sourceX - pixelX; fy = sourceY - pixelY; @@ -398,10 +439,17 @@ uint8_t *downscaleIcon(const uint8_t *image) { w4 = (int)(fx*fy*256.0); // set output pixels - pos = ((tmpy*destWidth) + tmpx) * 3; + if (mode == IMAGE_MODE_RGBA32) + pos = ((tmpy*destWidth) + tmpx) * 4; + else + pos = ((tmpy*destWidth) + tmpx) * 3; + out[pos+0] = (uint8_t)((r1 * w1 + r2 * w2 + r3 * w3 + r4 * w4) >> 8); out[pos+1] = (uint8_t)((g1 * w1 + g2 * w2 + g3 * w3 + g4 * w4) >> 8); out[pos+2] = (uint8_t)((b1 * w1 + b2 * w2 + b3 * w3 + b4 * w4) >> 8); + + if (mode == IMAGE_MODE_RGBA32) + out[pos+3] = (uint8_t)((a1 * w1 + a2 * w2 + a3 * w3 + a4 * w4) >> 8); } } diff --git a/common/menu-list.c b/common/menu-list.c index 31a9066..d3551c2 100644 --- a/common/menu-list.c +++ b/common/menu-list.c @@ -104,7 +104,7 @@ int menuScan(const char* target) { bool entrytype=0; memset(tmp_path, 0, sizeof(tmp_path)); - snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s", s_menu[!s_curMenu].dirname, dp->d_name); + snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name); #ifdef SWITCH fsdev_dir_t* dirSt = (fsdev_dir_t*)dir->dirData->dirStruct; diff --git a/common/menu.c b/common/menu.c index 92e04b7..5ee8bb4 100644 --- a/common/menu.c +++ b/common/menu.c @@ -14,11 +14,7 @@ void launchMenuEntryTask(menuEntry_s* arg) launchMenuEntry(me); } -typedef enum -{ - IMAGE_MODE_RGB24, - IMAGE_MODE_RGBA32 -} ImageMode; + //Draws an RGB888 or RGBA8888 image. static void drawImage(int x, int y, int width, int height, const uint8_t *image, ImageMode mode) { @@ -47,7 +43,6 @@ static void drawImage(int x, int y, int width, int height, const uint8_t *image, uint8_t *folder_icon_small; uint8_t *invalid_icon_small; -double timer; static void drawEntry(menuEntry_s* me, int off_x, int is_active) { int x, y; @@ -73,7 +68,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) { int shadow_size = 4; if (is_active) { - highlight_multiplier = fmax(0.0, fabs(fmod(timer, 1.0) - 0.5) / 0.5); + highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5); border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255); border_start_x = start_x-6; border_end_x = end_x+6; @@ -234,9 +229,10 @@ void menuStartup() { menuScan(path); - folder_icon_small = downscaleIcon(folder_icon_bin); - invalid_icon_small = downscaleIcon(invalid_icon_bin); + folder_icon_small = downscaleImg(folder_icon_bin, 256, 256, 140, 140, IMAGE_MODE_RGB24); + invalid_icon_small = downscaleImg(invalid_icon_bin, 256, 256, 140, 140, IMAGE_MODE_RGB24); computeFrontGradient(themeCurrent.frontWaveColor, 280); + //menuCreateMsgBox(780, 300, "This is a test"); } color_t waveBlendAdd(color_t a, color_t b, float alpha) { @@ -316,7 +312,7 @@ void drawBackBtn(menu_s* menu, bool emptyDir) { #endif { drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32); - DrawText(interuiregular18, x_text, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Back)); + DrawText(interuimedium20, x_text, 720 - 47, themeCurrent.textColor, textGetString(StrId_Actions_Back)); } } @@ -332,10 +328,10 @@ void menuLoop() { } } - drawWave(0, timer, themeCurrent.backWaveColor, 295, 0.0, 3.0); - drawWave(1, timer, themeCurrent.middleWaveColor, 290, 2.0, 3.5); - drawWave(2, timer, themeCurrent.frontWaveColor, 280, 4.0, -2.5); - timer += 0.05; + drawWave(0, menuTimer, themeCurrent.backWaveColor, 295, 0.0, 3.0); + drawWave(1, menuTimer, themeCurrent.middleWaveColor, 290, 2.0, 3.5); + drawWave(2, menuTimer, themeCurrent.frontWaveColor, 280, 4.0, -2.5); + menuTimer += 0.05; drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32); DrawText(interuiregular14, 180, 46, themeCurrent.textColor, VERSION); @@ -407,4 +403,6 @@ void menuLoop() { drawBackBtn(menu, false); } + + menuDrawMsgBox(); } diff --git a/common/menu.h b/common/menu.h index 9a19f77..31247f3 100644 --- a/common/menu.h +++ b/common/menu.h @@ -50,12 +50,25 @@ struct menuEntry_s_tag NacpStruct *nacp; }; +typedef enum +{ + IMAGE_MODE_RGB24, + IMAGE_MODE_RGBA32 +} ImageMode; + +double menuTimer; + +void menuCreateMsgBox(int width, int height, const char *text); +void menuCloseMsgBox(); +bool menuIsMsgBoxOpen(); +void menuDrawMsgBox(void); + void menuEntryInit(menuEntry_s* me, MenuEntryType type); void menuEntryFree(menuEntry_s* me); bool fileExists(const char* path); bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut); void menuEntryParseIcon(menuEntry_s* me); -uint8_t *downscaleIcon(const uint8_t *image); +uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode); void menuEntryParseNacp(menuEntry_s* me); menu_s* menuGetCurrent(void); diff --git a/common/message-box.c b/common/message-box.c new file mode 100644 index 0000000..5134a45 --- /dev/null +++ b/common/message-box.c @@ -0,0 +1,171 @@ +#include "common.h" + +typedef struct +{ + uint32_t width; + uint32_t height; + color_t *bg; + const char *text; +} MessageBox; + +MessageBox currMsgBox; + +void drawMsgBoxBgToBuff(color_t *buff, int width, int height) { + int x, y; + int off; + int circle_center_x, circle_center_y; + int corner_size = 0; + float rad, alpha; + color_t base_color = MakeColor(255, 255, 255, 255); + color_t color; + + for (y=0; y 0) { + if (xwidth-corner_size && yheight-corner_size) { // bottom left corner + circle_center_x = corner_size-1; + circle_center_y = height-corner_size; + } + else if (x>width-corner_size && y>height-corner_size) { // bottom right corner + circle_center_x = width-corner_size; + circle_center_y = height-corner_size; + } + else { + circle_center_x = -1; + circle_center_y = -1; + } + + if (circle_center_x == -1 && circle_center_y == -1) { + color = base_color; + } + else { + rad = sqrt(pow(circle_center_x - x, 2) + pow(circle_center_y - y, 2)); + alpha = (float)corner_size - rad; + + if (rad < corner_size) { + if (alpha < 1.0) { + color = MakeColor(base_color.r, base_color.g, base_color.b, base_color.a * alpha); + } + else + color = base_color; + } + else + color = MakeColor(0, 0, 0, 0); + } + } + else + color = base_color; + + if (y == height - 80) { + color = themeCurrent.seperatorColor; + } + + off = (y * width + x); + *((uint32_t *)&buff[off]) = color.r | (color.g<<8) | (color.b<<16) | (color.a<<24); + } + } +} + +void menuDrawMsgBox() { + if (!menuIsMsgBoxOpen()) + return; + + int off; + int x, y; + int start_x = 1280 / 2 - currMsgBox.width / 2; + int start_y = 720 / 2 - currMsgBox.height / 2; + int end_x = start_x + currMsgBox.width; + uint32_t text_width, text_height; + color_t curr_color; + + color_t border_color; + int sep_start_y = currMsgBox.height - 80; + int border_thickness = 6; + + int shadow_start_y, shadow_y; + int shadow_inset; + int shadow_size = 4; + float highlight_multiplier = highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5); + color_t shadow_color; + uint8_t shadow_alpha_base = 80; + + border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255); + + // Darken the background + for (y=0; y<720; y++) { + for (x=0; x<1280; x++) { + DrawPixel(x, y, MakeColor(0, 0, 0, 100)); + } + } + + // Draw the message box background + for (y=0; y=currMsgBox.width-border_thickness) && y>sep_start_y) || + (y>sep_start_y && y<=sep_start_y+border_thickness) || (y>=currMsgBox.height-border_thickness)) { + curr_color = border_color; + } + + DrawPixel(start_x+x, start_y+y, curr_color); + } + } + + GetTextDimensions(interuimedium20, currMsgBox.text, &text_width, &text_height); + + if (text_width < currMsgBox.width && text_height < sep_start_y) { + DrawText(interuiregular18, start_x + (currMsgBox.width - text_width) / 2, start_y + (currMsgBox.height - text_height - 80) / 2, MakeColor(0, 0, 0, 255), currMsgBox.text); + } + + DrawText(interuimedium20, start_x + 365, start_y + 245, MakeColor(0, 0, 0, 255), "OK"); + + shadow_start_y = start_y + currMsgBox.height; + + for (shadow_y=shadow_start_y; shadow_y = start_x + shadow_inset && x <= end_x - shadow_inset) { + DrawPixel(x, shadow_y, shadow_color); + } + } + } +} + +void menuCreateMsgBox(int width, int height, const char *text) { + if (menuIsMsgBoxOpen()) + return; + + currMsgBox = (MessageBox) { width, height, NULL, text }; + + currMsgBox.bg = malloc(currMsgBox.width*currMsgBox.height*4); + + if (currMsgBox.bg) { + drawMsgBoxBgToBuff(currMsgBox.bg, currMsgBox.width, currMsgBox.height); + } +} + +bool menuIsMsgBoxOpen() { + return currMsgBox.width != 0 || currMsgBox.height != 0 || currMsgBox.bg || currMsgBox.text; +} + +void menuCloseMsgBox() { + if (currMsgBox.bg) { + free(currMsgBox.bg); + currMsgBox.bg = NULL; + } + + currMsgBox.width = currMsgBox.height = 0; + currMsgBox.text = NULL; +} \ No newline at end of file diff --git a/common/theme.c b/common/theme.c index 8af9de0..5150579 100644 --- a/common/theme.c +++ b/common/theme.c @@ -16,6 +16,7 @@ void themeStartup(ThemePreset preset) { backWaveColor: MakeColor(154, 171, 255, 255), backgroundColor: MakeColor(233, 236, 241, 255), highlightColor: MakeColor(91, 237, 224, 255), + seperatorColor: MakeColor(219, 218, 219, 255), enableWaveBlending: 0, buttonAImage: button_a_light_bin, buttonBImage: button_b_light_bin, @@ -31,6 +32,7 @@ void themeStartup(ThemePreset preset) { backWaveColor: MakeColor(73, 103, 169, 255), backgroundColor: MakeColor(45, 45, 50, 255), highlightColor: MakeColor(91, 237, 224, 255), + seperatorColor: MakeColor(219, 218, 219, 255), enableWaveBlending: 0, buttonAImage: button_a_dark_bin, buttonBImage: button_b_dark_bin, diff --git a/common/theme.h b/common/theme.h index d6d776f..ee95166 100644 --- a/common/theme.h +++ b/common/theme.h @@ -10,6 +10,8 @@ typedef struct color_t backWaveColor; color_t backgroundColor; color_t highlightColor; + color_t seperatorColor; + color_t activeColor; bool enableWaveBlending; const uint8_t *buttonAImage; const uint8_t *buttonBImage; diff --git a/nx_main/main.c b/nx_main/main.c index 97b5c97..f8be1f8 100644 --- a/nx_main/main.c +++ b/nx_main/main.c @@ -80,7 +80,10 @@ bool menuUpdate(void) { if (down & KEY_A) { - if (menu->nEntries > 0) + if (menuIsMsgBoxOpen()) { + menuCloseMsgBox(); + } + else if (menu->nEntries > 0) { int i; menuEntry_s* me; diff --git a/pc_main/main.cpp b/pc_main/main.cpp index e365df1..c1914b1 100644 --- a/pc_main/main.cpp +++ b/pc_main/main.cpp @@ -70,7 +70,10 @@ extern "C" bool menuUpdate(void) { } else if (!new_return_state && return_state) { - if (menu->nEntries > 0) + if (menuIsMsgBoxOpen()) { + menuCloseMsgBox(); + } + else if (menu->nEntries > 0) { int i; menuEntry_s* me;