|
| 1 | +#ifndef MY_RECIPE_MODAL_HPP |
| 2 | +#define MY_RECIPE_MODAL_HPP |
| 3 | + |
| 4 | +#include <lvgl.h> |
| 5 | +#include <vector> |
| 6 | +#include <Arduino.h> |
| 7 | + |
| 8 | +// --- Data Models (Local for UI Config) --- |
| 9 | +struct ConfigIngredient { |
| 10 | + String name; |
| 11 | + int pumpId; // 1-4 |
| 12 | + int amountMl; |
| 13 | +}; |
| 14 | + |
| 15 | +struct ConfigCocktail { |
| 16 | + String name; |
| 17 | + const void* icon; // LVGL Image src |
| 18 | + uint32_t color; |
| 19 | + std::vector<ConfigIngredient> ingredients; |
| 20 | +}; |
| 21 | + |
| 22 | +// --- Callback Type --- |
| 23 | +typedef void (*RecipeSaveCallback)(ConfigCocktail* modifiedCocktail); |
| 24 | + |
| 25 | +// --- State Management --- |
| 26 | +static lv_obj_t * g_recipe_modal = NULL; |
| 27 | +static ConfigCocktail * g_current_cocktail = NULL; |
| 28 | +static RecipeSaveCallback g_save_cb = NULL; |
| 29 | + |
| 30 | +// Helper to close modal |
| 31 | +inline void recipe_modal_close_cb(lv_event_t * e) { |
| 32 | + if (g_recipe_modal) { |
| 33 | + lv_obj_del(g_recipe_modal); |
| 34 | + g_recipe_modal = NULL; |
| 35 | + g_current_cocktail = NULL; |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +// Helper to update ml label |
| 40 | +inline void ingredient_slider_cb(lv_event_t * e) { |
| 41 | + lv_obj_t * slider = (lv_obj_t *)lv_event_get_target(e); |
| 42 | + lv_obj_t * label = (lv_obj_t *)lv_event_get_user_data(e); |
| 43 | + |
| 44 | + int val = lv_slider_get_value(slider); |
| 45 | + lv_label_set_text_fmt(label, "%d ml", val); |
| 46 | + |
| 47 | + // Note: In a real implementation we would update the temp struct here |
| 48 | +} |
| 49 | + |
| 50 | +// Create the dynamic modal |
| 51 | +inline void create_recipe_modal(lv_obj_t * parent, ConfigCocktail * cocktail, RecipeSaveCallback on_save) { |
| 52 | + g_current_cocktail = cocktail; |
| 53 | + g_save_cb = on_save; |
| 54 | + |
| 55 | + // 1. Overlay |
| 56 | + g_recipe_modal = lv_obj_create(parent ? parent : lv_scr_act()); |
| 57 | + lv_obj_set_size(g_recipe_modal, LV_PCT(100), LV_PCT(100)); |
| 58 | + lv_obj_set_style_bg_color(g_recipe_modal, lv_color_black(), 0); |
| 59 | + lv_obj_set_style_bg_opa(g_recipe_modal, LV_OPA_80, 0); |
| 60 | + lv_obj_set_style_border_width(g_recipe_modal, 0, 0); |
| 61 | + lv_obj_center(g_recipe_modal); |
| 62 | + lv_obj_add_flag(g_recipe_modal, LV_OBJ_FLAG_CLICKABLE); // Block clicks |
| 63 | + |
| 64 | + // 2. Modal Box |
| 65 | + lv_obj_t * mbox = lv_obj_create(g_recipe_modal); |
| 66 | + lv_obj_set_size(mbox, 500, 350); |
| 67 | + lv_obj_center(mbox); |
| 68 | + lv_obj_set_style_bg_color(mbox, lv_color_hex(0x202020), 0); |
| 69 | + lv_obj_set_style_border_color(mbox, lv_color_hex(cocktail->color), 0); // Border matches drink color |
| 70 | + lv_obj_set_style_border_width(mbox, 2, 0); |
| 71 | + lv_obj_set_flex_flow(mbox, LV_FLEX_FLOW_COLUMN); |
| 72 | + |
| 73 | + // 3. Header |
| 74 | + lv_obj_t * title = lv_label_create(mbox); |
| 75 | + lv_label_set_text_fmt(title, "EDITAR: %s", cocktail->name.c_str()); |
| 76 | + lv_obj_set_style_text_font(title, &lv_font_montserrat_20, 0); |
| 77 | + lv_obj_set_style_text_color(title, lv_color_hex(cocktail->color), 0); |
| 78 | + lv_obj_set_style_align(title, LV_ALIGN_TOP_MID, 0); |
| 79 | + |
| 80 | + // 4. Ingredients List (Scrollable) |
| 81 | + lv_obj_t * list = lv_obj_create(mbox); |
| 82 | + lv_obj_set_size(list, LV_PCT(100), LV_PCT(65)); |
| 83 | + lv_obj_set_style_bg_opa(list, LV_OPA_TRANSP, 0); |
| 84 | + lv_obj_set_style_border_width(list, 0, 0); |
| 85 | + lv_obj_set_flex_flow(list, LV_FLEX_FLOW_COLUMN); |
| 86 | + lv_obj_set_style_pad_row(list, 15, 0); |
| 87 | + |
| 88 | + for (auto &ing : cocktail->ingredients) { |
| 89 | + lv_obj_t * item = lv_obj_create(list); |
| 90 | + lv_obj_set_width(item, LV_PCT(100)); |
| 91 | + lv_obj_set_height(item, LV_SIZE_CONTENT); |
| 92 | + lv_obj_set_style_bg_opa(item, LV_OPA_TRANSP, 0); |
| 93 | + lv_obj_set_style_border_width(item, 0, 0); |
| 94 | + lv_obj_set_style_pad_left(item, 10, 0); |
| 95 | + lv_obj_set_style_pad_right(item, 10, 0); |
| 96 | + |
| 97 | + // Label: Name + Amount |
| 98 | + lv_obj_t * header = lv_obj_create(item); |
| 99 | + lv_obj_set_size(header, LV_PCT(100), LV_SIZE_CONTENT); |
| 100 | + lv_obj_set_style_bg_opa(header, LV_OPA_TRANSP, 0); |
| 101 | + lv_obj_set_style_border_width(header, 0, 0); |
| 102 | + lv_obj_set_style_pad_all(header, 0, 0); |
| 103 | + lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW); |
| 104 | + lv_obj_set_flex_align(header, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); |
| 105 | + |
| 106 | + lv_obj_t * name_lbl = lv_label_create(header); |
| 107 | + lv_label_set_text(name_lbl, ing.name.c_str()); |
| 108 | + lv_obj_set_style_text_color(name_lbl, lv_color_hex(0xAAAAAA), 0); |
| 109 | + |
| 110 | + lv_obj_t * val_lbl = lv_label_create(header); |
| 111 | + lv_label_set_text_fmt(val_lbl, "%d ml", ing.amountMl); |
| 112 | + lv_obj_set_style_text_color(val_lbl, lv_color_white(), 0); |
| 113 | + |
| 114 | + // Slider |
| 115 | + lv_obj_t * slider = lv_slider_create(item); |
| 116 | + lv_obj_set_width(slider, LV_PCT(95)); // Reduce width slightly to avoid edge clipping |
| 117 | + lv_obj_set_align(slider, LV_ALIGN_CENTER); // Center it |
| 118 | + lv_slider_set_range(slider, 0, 300); // 0 to 300ml |
| 119 | + lv_slider_set_value(slider, ing.amountMl, LV_ANIM_OFF); |
| 120 | + |
| 121 | + // Style slider |
| 122 | + lv_obj_set_style_bg_color(slider, lv_color_hex(0x505050), LV_PART_MAIN); |
| 123 | + lv_obj_set_style_bg_color(slider, lv_color_hex(cocktail->color), LV_PART_INDICATOR); |
| 124 | + lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); |
| 125 | + |
| 126 | + lv_obj_add_event_cb(slider, ingredient_slider_cb, LV_EVENT_VALUE_CHANGED, val_lbl); |
| 127 | + } |
| 128 | + |
| 129 | + // 5. Buttons |
| 130 | + lv_obj_t * btn_cont = lv_obj_create(mbox); |
| 131 | + lv_obj_set_size(btn_cont, LV_PCT(100), LV_SIZE_CONTENT); |
| 132 | + lv_obj_set_style_bg_opa(btn_cont, LV_OPA_TRANSP, 0); |
| 133 | + lv_obj_set_style_border_width(btn_cont, 0, 0); |
| 134 | + lv_obj_set_flex_flow(btn_cont, LV_FLEX_FLOW_ROW); |
| 135 | + lv_obj_set_flex_align(btn_cont, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); |
| 136 | + lv_obj_set_style_pad_top(btn_cont, 10, 0); |
| 137 | + |
| 138 | + // Close Button (Full width for now, user didn't ask for generic save logic yet) |
| 139 | + lv_obj_t * btn_close = lv_btn_create(btn_cont); |
| 140 | + lv_obj_set_width(btn_close, LV_PCT(100)); |
| 141 | + lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x444444), 0); |
| 142 | + lv_obj_t * lbl_close = lv_label_create(btn_close); |
| 143 | + lv_label_set_text(lbl_close, "CERRAR"); |
| 144 | + lv_obj_center(lbl_close); |
| 145 | + lv_obj_add_event_cb(btn_close, recipe_modal_close_cb, LV_EVENT_CLICKED, NULL); |
| 146 | + |
| 147 | +} |
| 148 | + |
| 149 | +#endif // MY_RECIPE_MODAL_HPP |
0 commit comments