/* * File: Screen_Settings_Hierarchical_Menu.c * * Created: Created: Sunday September 2025 19:19:34 * Author: Chris */ // ============================================================================================ // Includes #include "../Screens.h" #include "../UI_Control.h" #include "../Hierarchical_Menu.h" #include "../Command_Definition.h" #include "../Display.h" #include "../Display_Objects.h" #include "../Display_Default_Configurations.h" #include // ============================================================================================ // Defines // ============================================================================================ // Variables extern const unsigned char _Font_DejaVu_Sans_Mono_6x12[]; extern const unsigned char _Font_DejaVu_Sans_Mono_7x15[]; extern const unsigned char _Font_DejaVu_Sans_Mono_Bold_7x15[]; extern const unsigned char _Font_DejaVu_Sans_Mono_Bold_15x26[]; static const Hierarchical_Menu* _Menu = NULL; static const Menu_List* _Current_List = NULL; static int32_t _Selected_Item; static int32_t _Scroll_Offset; static int32_t _Visible_Items; // ============================================================================================ // Function Declarations void Screen_Setup_Settings_Hierarchical_Menu(Screen_Transition_Direction direction_out, Screen_Transition_Direction direction_in, Easing type, uint32_t frame_duration, const Hierarchical_Menu* menu, const Menu_List* list, int32_t selected_item); static void Screen_Init (Screen_Transition_Direction direction_out, Screen_Transition_Direction direction_in, Easing type, uint32_t frame_duration); static void Screen_Tick (void); static void Screen_Click (uint button_return_value); static void Screen_Touch_Event (int16_t x, int16_t y); static void Screen_Action_CW (Object_ID object_id); static void Screen_Action_CCW (Object_ID object_id); static void Screen_On_Object_Focused (Object_ID object_id); static void Screen_On_Object_Defocused (Object_ID object_id); static void Screen_On_Object_Select (Object_ID object_id); static void Screen_On_Object_Deselect (Object_ID object_id); static void Navigate_To_List(const Menu_List* new_list); static void Handle_Back_Navigation(void); static void Calculate_Scroll_Offset(void); static void Handle_Item_Selection(void); /******************************************************************* Functions *******************************************************************/ void Screen_Setup_Settings_Hierarchical_Menu(Screen_Transition_Direction direction_out, Screen_Transition_Direction direction_in, Easing type, uint32_t frame_duration, const Hierarchical_Menu* menu, const Menu_List* list, int32_t selected_item) { _Menu = menu; _Current_List = list; _Selected_Item = selected_item; Screen_Init(direction_out, direction_in, type, frame_duration); _Scroll_Offset = 0; _Visible_Items = _Configuration_Menu_Hierarchical_Default.Visible_Items; } void Screen_Init(Screen_Transition_Direction direction_out, Screen_Transition_Direction direction_in, Easing type, uint32_t frame_duration) { _Screen_Tick = Screen_Tick; _Screen_Click = Screen_Click; _Screen_Touch_Event = Screen_Touch_Event; _Screen_Action_CW = Screen_Action_CW; _Screen_Action_CCW = Screen_Action_CCW; _Screen_On_Objects_Focused = Screen_On_Object_Focused; _Screen_On_Objects_Defocused = Screen_On_Object_Defocused; _Screen_On_Object_Select = Screen_On_Object_Select; _Screen_On_Object_Deselect = Screen_On_Object_Deselect; Display_Objects_Clear(); Display_Screen_Transition_Start(direction_out, direction_in, type, frame_duration); ////////////////////////////// // Add Display Objects here // ////////////////////////////// Display_Objects_Add_Menu_Hierarchical(&_Current_List, &_Selected_Item, &_Scroll_Offset, &_Configuration_Menu_Hierarchical_Default); Calculate_Scroll_Offset(); Display_Select_Object(); } void Screen_Tick(void) { } void Screen_Click(uint button_return_value) { } void Screen_Touch_Event(int16_t x, int16_t y) { } void Screen_Action_CW(Object_ID object_id) { // Move selection down (the renderer will automatically show the change) UI_Control_Selector_Inc(&_Selected_Item, 0, (int32_t)_Current_List->Item_Count - 1, false); Calculate_Scroll_Offset(); } void Screen_Action_CCW(Object_ID object_id) { // Move selection up (the renderer will automatically show the change) UI_Control_Selector_Dec(&_Selected_Item, 0, (int32_t)_Current_List->Item_Count - 1, false); Calculate_Scroll_Offset(); } void Screen_On_Object_Focused(Object_ID object_id) { } void Screen_On_Object_Defocused(Object_ID object_id) { } void Screen_On_Object_Select(Object_ID object_id) { } void Screen_On_Object_Deselect(Object_ID object_id) { Handle_Item_Selection(); } /******************************************************************* Internal Functions *******************************************************************/ void Navigate_To_List(const Menu_List* new_list) { if (new_list == NULL) { return; } // Update menu state _Current_List = new_list; _Selected_Item = 0; _Scroll_Offset = 0; Calculate_Scroll_Offset(); Display_Select_Object(); } void Handle_Back_Navigation(void) { if (_Current_List->Parent != NULL) { // Navigate to parent list const Menu_List* Parent_List = _Current_List->Parent->Containing_List; // Find the index of the item that led to current list _Selected_Item = 0; for (uint32_t i = 0; i < Parent_List->Item_Count; i++) { if (Parent_List->Items[i].List == _Current_List) { _Selected_Item = i; break; } } _Current_List = Parent_List; Calculate_Scroll_Offset(); Display_Select_Object(); } else if (_Menu->Parent_Function != NULL) { // Exit to parent screen _Menu->Parent_Function(TRANSITION_DOWN, TRANSITION_DOWN, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, _Menu->Parent_Selected_Setting); } } void Calculate_Scroll_Offset(void) { // Auto-scroll to keep selected item visible if (_Selected_Item < _Scroll_Offset) { _Scroll_Offset = 0; } else { _Scroll_Offset = _Selected_Item - _Visible_Items + 1; } // Ensure scroll offset is within bounds int32_t Max_Scroll = (int32_t)_Current_List->Item_Count - _Visible_Items; if (Max_Scroll < 0) { Max_Scroll = 0; } if (_Scroll_Offset > Max_Scroll) { _Scroll_Offset = Max_Scroll; } if (_Scroll_Offset < 0) { _Scroll_Offset = 0; } } void Handle_Item_Selection(void) { if (_Selected_Item < 0 || _Selected_Item >= (int32_t)_Current_List->Item_Count) { return; } const Menu_Item* Selected_Item = &_Current_List->Items[_Selected_Item]; if (Selected_Item->Is_Back) { Handle_Back_Navigation(); return; } if (Selected_Item->List != NULL) { Navigate_To_List(Selected_Item->List); return; } switch(Selected_Item->Type) { case BOOL: Screen_Setup_Select_Bool(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), (uint8_t*)Selected_Item->Variable, _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case RGB: Screen_Setup_Select_RGB(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), (LED_Data_t*)Selected_Item->Variable, _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case VALUE: if(Selected_Item->Configuration == NULL) { return; } Screen_Setup_Select_Value(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), (int32_t*)Selected_Item->Variable, (const Menu_Configuration_Select_Value*)(Selected_Item->Configuration), _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case LIST: if(Selected_Item->Configuration == NULL) { return; } Screen_Setup_Select_List(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), (int32_t*)Selected_Item->Variable, (const Menu_Configuration_Select_List*)(Selected_Item->Configuration), _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case DEFAULT_NOTES: Screen_Setup_Default_Notes(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case REBOOT: Screen_Setup_Settings_Reboot(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case HUE: Screen_Setup_Select_Hue(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), (int32_t*)Selected_Item->Variable, (const Menu_Configuration_Select_Value*)(Selected_Item->Configuration), _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case MINMAX: Screen_Setup_Select_MinMax(TRANSITION_LEFT, TRANSITION_UP, SCREEN_TRANSITION_DEFAULT_EASING, SCREEN_TRANSITION_DEFAULT_FRAMES, (char*)Selected_Item->Variable_Title, strlen(Selected_Item->Variable_Title), (MinMax_t*)Selected_Item->Variable, (const Menu_Configuration_Select_MinMax*)(Selected_Item->Configuration), _Menu, Selected_Item->Containing_List, _Selected_Item ); break; case NONE: default: break; } }