diff --git a/applications/main/momentum_app/scenes/momentum_app_scene_interface_mainmenu.c b/applications/main/momentum_app/scenes/momentum_app_scene_interface_mainmenu.c index 17f17f7295..afad8f7d24 100644 --- a/applications/main/momentum_app/scenes/momentum_app_scene_interface_mainmenu.c +++ b/applications/main/momentum_app/scenes/momentum_app_scene_interface_mainmenu.c @@ -23,6 +23,7 @@ const char* const menu_style_names[MenuStyleCount] = { "C64", "Compact", "MNTM", + "CoverFlow", }; static void momentum_app_scene_interface_mainmenu_menu_style_changed(VariableItem* item) { MomentumApp* app = variable_item_get_context(item); diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 2aace5ca73..0a9ef66123 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -294,27 +294,53 @@ void canvas_draw_bitmap( canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0); } -void canvas_draw_icon_animation( +static void _canvas_draw_icon_animation( Canvas* canvas, int32_t x, int32_t y, + int32_t width_scale, + int32_t height_scale, IconAnimation* icon_animation) { furi_check(canvas); furi_check(icon_animation); + // Ensure scale % is > 0 + furi_assert(width_scale > 0 && height_scale > 0); + // Ensure scale % is <= 100: animated icons > 100% are buggy + // TODO: Future, allow scaling > 100 + furi_assert(width_scale <= 100 && height_scale <= 100); x += canvas->offset_x; y += canvas->offset_y; + uint8_t* icon_data = NULL; compress_icon_decode( canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data); + + int32_t width = icon_animation_get_width(icon_animation); + int32_t height = icon_animation_get_height(icon_animation); + int32_t width_scaled = (width * width_scale) / 100; + int32_t height_scaled = (height * height_scale) / 100; + canvas_draw_u8g2_bitmap( - &canvas->fb, - x, - y, - icon_animation_get_width(icon_animation), - icon_animation_get_height(icon_animation), - icon_data, - IconRotation0); + &canvas->fb, x, y, width_scaled, height_scaled, icon_data, IconRotation0); +} + +void canvas_draw_icon_animation( + Canvas* canvas, + int32_t x, + int32_t y, + IconAnimation* icon_animation) { + _canvas_draw_icon_animation(canvas, x, y, 100, 100, icon_animation); +} + +void canvas_draw_icon_animation_ex( + Canvas* canvas, + int32_t x, + int32_t y, + int32_t width_scale, + int32_t height_scale, + IconAnimation* icon_animation) { + _canvas_draw_icon_animation(canvas, x, y, width_scale, height_scale, icon_animation); } static void canvas_draw_u8g2_bitmap_int( diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index cd4719b3ff..2168a4ea44 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -258,6 +258,9 @@ void canvas_draw_icon_ex( IconRotation rotation); /** Draw animation at position defined by x,y. + * + * This function is retained for backward compatibility and draws the animation + * at the specified position without scaling. * * @param canvas Canvas instance * @param x x coordinate @@ -270,6 +273,26 @@ void canvas_draw_icon_animation( int32_t y, IconAnimation* icon_animation); +/** Draw animation at position defined by x,y with scaling. + * + * This extended version allows scaling of the animation dimensions by percentage. + * The width and height are scaled independently. + * + * @param canvas Canvas instance + * @param x x coordinate + * @param y y coordinate + * @param width_scale Scaled (%) width of the icon (1–100%) + * @param height_scale Scaled (%) height of the icon (1–100%) + * @param icon_animation IconAnimation instance + */ +void canvas_draw_icon_animation_ex( + Canvas* canvas, + int32_t x, + int32_t y, + int32_t width_scale, + int32_t height_scale, + IconAnimation* icon_animation); + /** Draw icon at position defined by x,y. * * @param canvas Canvas instance diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index c3b7bcd67e..e66c151d16 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -81,6 +81,24 @@ static void menu_centered_icon( item->icon); } +static void menu_centered_icon_scaled( + Canvas* canvas, + MenuItem* item, + size_t x, + size_t y, + size_t width, + size_t height, + size_t width_scale, + size_t height_scale) { + canvas_draw_icon_animation_ex( + canvas, + x + (width - item->icon->icon->width) / 2, + y + (height - item->icon->icon->height) / 2, + width_scale, + height_scale, + item->icon); +} + static size_t menu_scroll_counter(MenuModel* model, bool selected) { if(!selected) return 0; size_t scroll_counter = model->scroll_counter; @@ -442,6 +460,115 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { } break; } + case MenuStyleCoverFlow: { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + + // Draw frames + canvas_set_bitmap_mode(canvas, true); + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_draw_frame(canvas, 44, 2, 40, 40); + + // Draw left side albums + canvas_draw_line(canvas, 6, 40, 17, 35); + canvas_draw_line(canvas, 19, 40, 30, 35); + canvas_draw_line(canvas, 32, 40, 43, 35); + canvas_draw_line(canvas, 6, 3, 17, 8); + canvas_draw_line(canvas, 19, 3, 30, 8); + canvas_draw_line(canvas, 32, 3, 43, 8); + canvas_draw_line(canvas, 18, 2, 18, 41); + canvas_draw_line(canvas, 31, 2, 31, 41); + canvas_draw_line(canvas, 5, 2, 5, 41); + canvas_draw_line(canvas, 4, 8, 1, 7); + canvas_draw_line(canvas, 5, 35, 1, 36); + + // Draw right side albums + canvas_draw_line(canvas, 95, 40, 84, 35); + canvas_draw_line(canvas, 108, 40, 97, 35); + canvas_draw_line(canvas, 121, 40, 110, 35); + canvas_draw_line(canvas, 84, 8, 95, 3); + canvas_draw_line(canvas, 97, 8, 108, 3); + canvas_draw_line(canvas, 110, 8, 121, 3); + canvas_draw_line(canvas, 96, 2, 96, 41); + canvas_draw_line(canvas, 109, 2, 109, 41); + canvas_draw_line(canvas, 122, 2, 122, 41); + canvas_draw_line(canvas, 123, 8, 126, 7); + canvas_draw_line(canvas, 123, 35, 126, 36); + + const int32_t pos_x_center = 128 / 2; + const int32_t pos_y_center = 64 / 2; + const int32_t pos_y_offset = 10; + const int32_t icon_size = 20; + const int32_t side_icon_width = icon_size / 2; + const int32_t padding_center_icon = 14; + const int32_t spacing_between_icons = 3; + const int32_t scale_base = 100; + + MenuItem* center_item = NULL; + + // Draw 7 icons, where index 0 is the center icon + // [-3, -2, -1, 0, 1, 2, 3] + for(int8_t i = -3; i <= 3; i++) { + shift_position = (position + items_count + i) % items_count; + item = MenuItemArray_get(model->items, shift_position); + + int32_t pos_x = pos_x_center; + int32_t pos_y = pos_y_center; + + int32_t scale_width = scale_base; + int32_t scale_height = scale_base; + + if(i < 0) { + // Left sided icons + pos_x -= padding_center_icon; + pos_x -= ((-i) * (side_icon_width + spacing_between_icons)); + pos_x -= (side_icon_width / 2) / 2; + pos_y = (pos_y_center - icon_size / 2) - pos_y_offset; + scale_width = 50; + } else if(i > 0) { + // Right sided icons + pos_x += padding_center_icon; + pos_x += (i * (side_icon_width + spacing_between_icons)); + pos_x -= side_icon_width; + pos_y = (pos_y_center - icon_size / 2) - pos_y_offset; + scale_width = 50; + } else if(i == 0) { + // Center icon + pos_x -= icon_size / 2; + pos_y = (pos_y_center - (icon_size / 2)) - pos_y_offset; + // Scaling > 100% doesn't look good, keep 100% for now + scale_width = scale_base; // TODO: 200% + scale_height = scale_base; // TODO: 200% + // Save center item pointer for later + center_item = item; + } + + // Draw the icon + menu_centered_icon_scaled( + canvas, item, pos_x, pos_y, icon_size, icon_size, scale_width, scale_height); + } + + // Draw label for center item + if(center_item) { + FuriString* name = furi_string_alloc(); + menu_get_name(center_item, name, false); + elements_scrollable_text_line_centered( + canvas, + pos_x_center, + (pos_y_center + icon_size / 2) + pos_y_offset, + 126, + name, + 0, + false, + true); + furi_string_free(name); + } + + // Add scrollbar element + elements_scrollbar_horizontal(canvas, 0, 60, 128, position, items_count); + + break; + } default: break; } @@ -818,7 +945,9 @@ static void menu_process_left(Menu* menu) { position = position - 8; } break; - + case MenuStyleCoverFlow: + position = (position + count - 1) % count; + break; default: break; } @@ -882,7 +1011,9 @@ static void menu_process_right(Menu* menu) { position = position - 8; } break; - + case MenuStyleCoverFlow: + position = (position + 1) % count; + break; default: break; } diff --git a/lib/momentum/settings.h b/lib/momentum/settings.h index ee13411313..b34b2b3325 100644 --- a/lib/momentum/settings.h +++ b/lib/momentum/settings.h @@ -29,6 +29,7 @@ typedef enum { MenuStyleC64, MenuStyleCompact, MenuStyleMNTM, + MenuStyleCoverFlow, MenuStyleCount, } MenuStyle; diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index c72569c641..d70b429172 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -737,7 +737,7 @@ Function,+,canvas_draw_dot,void,"Canvas*, int32_t, int32_t" Function,+,canvas_draw_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,canvas_draw_glyph,void,"Canvas*, int32_t, int32_t, uint16_t" Function,+,canvas_draw_icon,void,"Canvas*, int32_t, int32_t, const Icon*" -Function,+,canvas_draw_icon_animation,void,"Canvas*, int32_t, int32_t, IconAnimation*" +Function,+,canvas_draw_icon_animation,void,"Canvas*, int32_t, int32_t, int32_t, int32_t, IconAnimation*" Function,+,canvas_draw_icon_ex,void,"Canvas*, int32_t, int32_t, const Icon*, IconRotation" Function,+,canvas_draw_line,void,"Canvas*, int32_t, int32_t, int32_t, int32_t" Function,+,canvas_draw_rbox,void,"Canvas*, int32_t, int32_t, size_t, size_t, size_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f5e79d7eec..891998b270 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -834,6 +834,7 @@ Function,+,canvas_draw_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,canvas_draw_glyph,void,"Canvas*, int32_t, int32_t, uint16_t" Function,+,canvas_draw_icon,void,"Canvas*, int32_t, int32_t, const Icon*" Function,+,canvas_draw_icon_animation,void,"Canvas*, int32_t, int32_t, IconAnimation*" +Function,+,canvas_draw_icon_animation_ex,void,"Canvas*, int32_t, int32_t, int32_t, int32_t, IconAnimation*" Function,+,canvas_draw_icon_ex,void,"Canvas*, int32_t, int32_t, const Icon*, IconRotation" Function,+,canvas_draw_line,void,"Canvas*, int32_t, int32_t, int32_t, int32_t" Function,+,canvas_draw_rbox,void,"Canvas*, int32_t, int32_t, size_t, size_t, size_t"