mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Main Menu: Add coverflow menu style (#314)
* Add coverflow menu style. * Update with new canvas extended function `canvas_draw_icon_animation_ex`
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -294,27 +294,53 @@ void canvas_draw_bitmap(
|
||||
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0);
|
||||
}
|
||||
|
||||
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, width_scaled, height_scaled, icon_data, IconRotation0);
|
||||
}
|
||||
|
||||
void canvas_draw_icon_animation(
|
||||
Canvas* canvas,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
IconAnimation* icon_animation) {
|
||||
furi_check(canvas);
|
||||
furi_check(icon_animation);
|
||||
_canvas_draw_icon_animation(canvas, x, y, 100, 100, icon_animation);
|
||||
}
|
||||
|
||||
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);
|
||||
canvas_draw_u8g2_bitmap(
|
||||
&canvas->fb,
|
||||
x,
|
||||
y,
|
||||
icon_animation_get_width(icon_animation),
|
||||
icon_animation_get_height(icon_animation),
|
||||
icon_data,
|
||||
IconRotation0);
|
||||
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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -440,6 +458,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;
|
||||
}
|
||||
@@ -816,7 +943,9 @@ static void menu_process_left(Menu* menu) {
|
||||
position = position - 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case MenuStyleCoverFlow:
|
||||
position = (position + count - 1) % count;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -880,7 +1009,9 @@ static void menu_process_right(Menu* menu) {
|
||||
position = position - 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case MenuStyleCoverFlow:
|
||||
position = (position + 1) % count;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ typedef enum {
|
||||
MenuStyleC64,
|
||||
MenuStyleCompact,
|
||||
MenuStyleMNTM,
|
||||
MenuStyleCoverFlow,
|
||||
MenuStyleCount,
|
||||
} MenuStyle;
|
||||
|
||||
|
||||
@@ -743,7 +743,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"
|
||||
|
||||
|
@@ -840,6 +840,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"
|
||||
|
||||
|
Reference in New Issue
Block a user