778 lines
24 KiB
C
778 lines
24 KiB
C
/*
|
|
* ui_menu_internal.c
|
|
*
|
|
* Created on: 24.12.2016
|
|
* Author: danilo
|
|
*/
|
|
|
|
#include "ui_menu.h"
|
|
#include "ui_menu_internal.h"
|
|
#include "ui_lcd_hy28.h" // for colors!
|
|
#include <stdio.h>
|
|
|
|
// we show MENUSIZE items at the same time to the user.
|
|
// right now the render code uses this global variable since
|
|
// only a single active menu is supported right now.
|
|
|
|
//Because of dynamic resolution change we have to declare the maximum amount of items displayed at once for the highest option
|
|
MenuDisplaySlot menu[MAX_MENUSIZE];
|
|
|
|
bool init_done = false;
|
|
|
|
|
|
// actions [this is an internal, not necessarily complete or accurate sketch of the used algorithms /API
|
|
// read the source to find out how it is done. Left for the purpose of explaining the basic idea
|
|
// show menu -> was previously displayed -> yes -> simply display all slots
|
|
// -> no -> get first menu group, get first entry, fill first slot, run get next entry until all slots are filled
|
|
|
|
// find next MenuEntry -> is menu group -> yes -> is unfolded -> yes -> get first item from menu group
|
|
// -> no -> treat as normal menu entry
|
|
// -> is there one more entry in my menu group ? -> yes -> takes this one
|
|
// -> no -> go one level up -> get next entry in this group
|
|
// -> there is no such entry -> fill with dummy entry
|
|
|
|
// find prev MenuEntry -> there is prev entry in menu group -> yes -> is this unfolded menu group -> yes -> get last entry of this menu group
|
|
// -> no -> fill slot with entry
|
|
// -> no -> go one level up -> get prev entry of this level
|
|
|
|
// unfold menu group -> mark as unfold, run get next entry until all menu display slots are filled
|
|
// fold menu group -> mark as fold, run get next entry until all menu display slots are filled
|
|
|
|
// move to next/previous page -> (this is n times prev/next)
|
|
|
|
// ===================== BEGIN MENU LOW LEVEL MANAGEMENT =====================
|
|
const MenuGroupDescriptor* UiMenu_GetParentGroupForEntry(const MenuDescriptor* me)
|
|
{
|
|
return me==NULL?NULL:&groups[me->menuId];
|
|
}
|
|
|
|
const MenuGroupDescriptor* UiMenu_GetGroupForGroupEntry(const MenuDescriptor* me)
|
|
{
|
|
return me==NULL?NULL:&groups[me->number];
|
|
}
|
|
|
|
//inline bool UiMenu_IsEnabled(const MenuDescriptor *entry)
|
|
bool UiMenu_IsEnabled(const MenuDescriptor *entry)
|
|
{
|
|
return entry==NULL?false:(entry->enabled != NULL?*entry->enabled:true);
|
|
}
|
|
|
|
|
|
inline bool UiMenu_IsGroup(const MenuDescriptor *entry)
|
|
{
|
|
return entry==NULL?false:entry->kind == MENU_GROUP;
|
|
}
|
|
inline bool UiMenu_IsItem(const MenuDescriptor *entry)
|
|
{
|
|
return entry==NULL?false:entry->kind == MENU_ITEM;
|
|
}
|
|
inline bool UiMenu_IsInfo(const MenuDescriptor *entry)
|
|
{
|
|
return entry==NULL?false:entry->kind == MENU_INFO;
|
|
}
|
|
|
|
|
|
inline bool UiMenu_SlotIsEmpty(MenuDisplaySlot* slot)
|
|
{
|
|
return slot==NULL?false:slot->entryItem == NULL;
|
|
}
|
|
inline bool UiMenu_GroupIsUnfolded(const MenuDescriptor *group)
|
|
{
|
|
return group==NULL?false:groups[group->number].state->unfolded;
|
|
}
|
|
inline uint16_t UiMenu_MenuGroupMemberCount(const MenuGroupDescriptor* gd)
|
|
{
|
|
uint16_t retval = 0;
|
|
if (gd != NULL)
|
|
{
|
|
if (gd->state->count == 0)
|
|
{
|
|
const MenuDescriptor* entry;
|
|
for (entry = gd->entries; entry->kind != MENU_STOP; entry++)
|
|
{
|
|
gd->state->count++;
|
|
}
|
|
}
|
|
retval = gd->state->count;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
inline void UiMenu_GroupFold(const MenuDescriptor* entry, bool fold)
|
|
{
|
|
if (UiMenu_IsGroup(entry))
|
|
{
|
|
groups[entry->number].state->unfolded = fold == false;
|
|
}
|
|
}
|
|
|
|
inline const MenuDescriptor* UiMenu_GroupGetLast(const MenuGroupDescriptor *group)
|
|
{
|
|
const MenuDescriptor* retval = NULL;
|
|
uint16_t count = UiMenu_MenuGroupMemberCount(group);
|
|
if (count>0)
|
|
{
|
|
retval = &(group->entries[count-1]);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
inline const MenuDescriptor* UiMenu_GroupGetFirst(const MenuGroupDescriptor *group)
|
|
{
|
|
const MenuDescriptor* retval = NULL;
|
|
uint16_t count = UiMenu_MenuGroupMemberCount(group);
|
|
if (count>0)
|
|
{
|
|
retval = group->entries;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
const MenuDescriptor* UiMenu_GetNextEntryInGroup(const MenuDescriptor* me)
|
|
{
|
|
const MenuDescriptor* retval = NULL;
|
|
|
|
if (me != NULL)
|
|
{
|
|
const MenuGroupDescriptor* group_ptr = UiMenu_GetParentGroupForEntry(me);
|
|
for (const MenuDescriptor* nxt = me + 1; UiMenu_GroupGetLast(group_ptr)>= nxt; nxt++)
|
|
{
|
|
if (UiMenu_IsEnabled(nxt))
|
|
{
|
|
retval = nxt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
const MenuDescriptor* UiMenu_GetPrevEntryInGroup(const MenuDescriptor* me)
|
|
{
|
|
const MenuGroupDescriptor* group_ptr = UiMenu_GetParentGroupForEntry(me);
|
|
const MenuDescriptor* retval = NULL;
|
|
if (me != NULL && me != &group_ptr->entries[0])
|
|
{
|
|
for (const MenuDescriptor* prv = me - 1; prv >= &group_ptr->entries[0]; prv--)
|
|
{
|
|
if (UiMenu_IsEnabled(prv))
|
|
{
|
|
retval = prv;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
const MenuDescriptor* UiMenu_GetParentForEntry(const MenuDescriptor* me)
|
|
{
|
|
const MenuDescriptor* retval = NULL;
|
|
if (me != NULL)
|
|
{
|
|
const MenuGroupDescriptor* gd = UiMenu_GetParentGroupForEntry(me);
|
|
if (gd->parent != NULL)
|
|
{
|
|
if (gd->state->me == NULL )
|
|
{
|
|
const MenuGroupDescriptor* gdp = &groups[gd->parent->menuId];
|
|
uint16_t count = UiMenu_MenuGroupMemberCount(gdp);
|
|
uint16_t idx;
|
|
for(idx = 0; idx < count; idx++)
|
|
{
|
|
if ((gdp->entries[idx].kind == MENU_GROUP) && (gdp->entries[idx].number == me->menuId))
|
|
{
|
|
gd->state->me = &gdp->entries[idx];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
retval = gd->state->me;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* @returns true if this is the last ACTIVE entry in the menu group
|
|
*/
|
|
inline bool UiMenu_IsLastActiveItemInMenuGroup(const MenuDescriptor* here)
|
|
{
|
|
const MenuGroupDescriptor* gd = UiMenu_GetParentGroupForEntry(here);
|
|
const MenuDescriptor* lastActive = UiMenu_GroupGetLast(gd);
|
|
|
|
for (; lastActive != NULL && UiMenu_IsEnabled(lastActive) == false && lastActive != here; lastActive = UiMenu_GetPrevEntryInGroup(lastActive) )
|
|
{
|
|
// we need to do nothing here
|
|
}
|
|
return lastActive == here;
|
|
}
|
|
inline bool UiMenu_IsFirstInMenuGroup(const MenuDescriptor* here)
|
|
{
|
|
const MenuGroupDescriptor* gd = UiMenu_GetParentGroupForEntry(here);
|
|
return UiMenu_GroupGetFirst(gd) == here;
|
|
}
|
|
|
|
|
|
// ===================== END MENU LOW LEVEL MANAGEMENT =====================
|
|
|
|
|
|
// ===================== BEGIN MENU ITERATION STRATEGY =====================
|
|
// this code implements a specific strategy to walk through a menu structure
|
|
|
|
// Helper Functions
|
|
const MenuDescriptor* UiMenu_FindNextEntryInUpperLevel(const MenuDescriptor* here)
|
|
{
|
|
const MenuDescriptor* next = NULL, *focus = here;
|
|
if (here != NULL)
|
|
{
|
|
while(focus != NULL && next == NULL)
|
|
{
|
|
// we have a parent group, we are member of a sub menu group,
|
|
// we need next entry in containing menu group, no matter if our menu group is folded or not
|
|
next = UiMenu_GetNextEntryInGroup(UiMenu_GetParentForEntry(focus));
|
|
if (next == NULL)
|
|
{
|
|
focus = UiMenu_GetParentForEntry(focus);
|
|
}
|
|
}
|
|
}
|
|
return next;
|
|
}
|
|
|
|
const MenuDescriptor* UiMenu_FindLastEntryInLowerLevel(const MenuDescriptor* here)
|
|
{
|
|
const MenuDescriptor *last = here;
|
|
while (UiMenu_IsGroup(here) && UiMenu_GroupIsUnfolded(here) && here == last)
|
|
{
|
|
const MenuDescriptor* last = UiMenu_GroupGetLast(UiMenu_GetGroupForGroupEntry(here));
|
|
if (last)
|
|
{
|
|
here = last;
|
|
}
|
|
}
|
|
return here;
|
|
}
|
|
|
|
|
|
// Main Strategy Functions
|
|
/*
|
|
* Strategy: Provide a 'virtual' flat list of menu entries, list members are dynamically inserted/removed if menu groups are (un)folded.
|
|
* External code navigates through only with next/prev operations.
|
|
*
|
|
*/
|
|
/*
|
|
* @brief Get next menu entry. If a menu group is unfolded, next entry after menu group item is first item from menu group
|
|
*
|
|
*/
|
|
const MenuDescriptor* UiMenu_NextMenuEntry(const MenuDescriptor* here)
|
|
{
|
|
const MenuDescriptor* next = NULL;
|
|
|
|
if (here != NULL)
|
|
{
|
|
if (UiMenu_IsGroup(here))
|
|
{
|
|
// is group entry
|
|
|
|
if (UiMenu_GroupIsUnfolded(here))
|
|
{
|
|
const MenuGroupDescriptor* group = &groups[here->number];
|
|
next = UiMenu_GroupGetFirst(group);
|
|
if (next == NULL)
|
|
{
|
|
// this is an empty menu group, should not happen, does make sense
|
|
// but we handle this anyway
|
|
next = UiMenu_FindNextEntryInUpperLevel(here);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// folded group, so we behave like a normal entry
|
|
next = UiMenu_GetNextEntryInGroup(here);
|
|
}
|
|
}
|
|
if (next == NULL)
|
|
{
|
|
// we are currently at a normal entry or a folded group or empty group (in this case these are treated as simple entries)
|
|
// only 3 cases possible:
|
|
// - final entry of menu, fill next slot with blank entry, return false
|
|
// - next entry is normal entry (group or entry, no difference), just use this one
|
|
// - last entry in menu group, go up, and search for next entry in this parent menu (recursively).
|
|
|
|
if (UiMenu_IsLastActiveItemInMenuGroup(here))
|
|
{
|
|
// we need the parent menu in order to ask for the entry after our
|
|
// menu group entry
|
|
// if we cannot find the parent group, we are top level and the last menu entry
|
|
// so there is no further entry
|
|
next = UiMenu_FindNextEntryInUpperLevel(here);
|
|
}
|
|
else
|
|
{
|
|
next = UiMenu_GetNextEntryInGroup(here);
|
|
}
|
|
}
|
|
}
|
|
return next;
|
|
}
|
|
|
|
|
|
/*
|
|
* @brief Get previous menu entry. If on first item of a menu group, show the last entry of the previous menu group/menu item
|
|
*
|
|
*/
|
|
const MenuDescriptor* UiMenu_PrevMenuEntry(const MenuDescriptor* here)
|
|
{
|
|
const MenuDescriptor* prev = NULL;
|
|
|
|
|
|
if (here != NULL)
|
|
{
|
|
if (UiMenu_IsFirstInMenuGroup(here))
|
|
{
|
|
// we go up, get previous entry
|
|
// if first entry, go one further level up, ...
|
|
// if not first entry -> get prev entry
|
|
// if normal entry or folded menu -> we are done
|
|
// if unfolded menu_entry -> go to last entry
|
|
// -> if normal entry or folded menu -> we are done
|
|
// -> if unfolded menu entry -> go to last entry
|
|
prev = UiMenu_GetParentForEntry(here);
|
|
}
|
|
else
|
|
{
|
|
prev = UiMenu_GetPrevEntryInGroup(here);
|
|
if (UiMenu_IsGroup(prev) && UiMenu_GroupIsUnfolded(prev))
|
|
{
|
|
prev = UiMenu_FindLastEntryInLowerLevel(prev);
|
|
}
|
|
}
|
|
}
|
|
return prev;
|
|
}
|
|
|
|
|
|
|
|
bool UiMenu_FillSlotWithEntry(MenuDisplaySlot* here, const MenuDescriptor* entry)
|
|
{
|
|
bool retval = false;
|
|
if (entry != NULL)
|
|
{
|
|
here->entryItem = entry;
|
|
retval = true;
|
|
}
|
|
else
|
|
{
|
|
here->entryItem = NULL;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
// DISPLAY SPECIFIC CODE BEGIN
|
|
void UiMenu_DisplayValue(const char* value,uint32_t clr,uint16_t pos)
|
|
{
|
|
UiLcdHy28_PrintTextRight(ts.Layout->MENU_CURSOR_X - 4, ts.Layout->MENU_IND.y + (pos * 12), value, clr, Black, 0); // yes, normal position
|
|
}
|
|
static void UiMenu_DisplayLabel(const char* label,uint32_t clr,uint16_t pos)
|
|
{
|
|
UiLcdHy28_PrintText(ts.Layout->MENU_IND.x, ts.Layout->MENU_IND.y + (12*(pos)),label,clr,Black,0);
|
|
}
|
|
static void UiMenu_DisplayCursor(const char* label,uint32_t clr,uint16_t pos)
|
|
{
|
|
UiLcdHy28_PrintText(ts.Layout->MENU_CURSOR_X, ts.Layout->MENU_IND.y + (12*(pos)),label,clr,Black,0);
|
|
}
|
|
// DISPLAY SPECIFIC CODE END
|
|
|
|
|
|
void UiMenu_MoveCursor(uint32_t newpos)
|
|
{
|
|
static uint32_t oldpos = 999; // y position of option cursor, previous
|
|
if(oldpos != 999) // was the position of a previous cursor stored?
|
|
{
|
|
UiMenu_DisplayCursor(" ", Green, oldpos);
|
|
}
|
|
oldpos = newpos; // save position of new "old" cursor position
|
|
if (newpos != 999)
|
|
{
|
|
UiMenu_DisplayCursor("<", Green, newpos);
|
|
}
|
|
}
|
|
|
|
|
|
static void UiMenu_UpdateHWInfoLines(uchar index, MenuProcessingMode_t mode, int pos)
|
|
{
|
|
uint32_t m_clr;
|
|
const char* outs = UiMenu_GetSystemInfo(&m_clr, index);
|
|
UiMenu_DisplayValue(outs,m_clr,pos);
|
|
}
|
|
|
|
/**
|
|
* @brief Display and and change line items
|
|
* @param select item to display/change
|
|
* @param mode 0=display/update 1=change item 3=set default
|
|
* @param pos (0-5) use this line as position
|
|
*/
|
|
static void UiMenu_UpdateLines(uint16_t select, MenuProcessingMode_t mode, int pos)
|
|
{
|
|
char options[32];
|
|
const char* txt_ptr = NULL; // if filled, we use this string for display, otherwise options
|
|
uint32_t clr = White; // color used it display of adjusted options
|
|
|
|
int var; // holder for the menu item value change
|
|
|
|
|
|
if(mode == MENU_RENDER_ONLY) // are we in update/display mode?
|
|
{
|
|
var = 0; // prevent any change of variable
|
|
}
|
|
else // this is "change" mode
|
|
{
|
|
var = ts.menu_var; // change from encoder
|
|
ts.menu_var = 0; // clear encoder change detect
|
|
}
|
|
|
|
strcpy(options, "ERROR"); // pre-load to catch error condition
|
|
|
|
UiMenu_UpdateItem(select, mode, pos, var, options,&txt_ptr , &clr);
|
|
|
|
if (txt_ptr == NULL)
|
|
{
|
|
txt_ptr = options;
|
|
}
|
|
UiMenu_DisplayValue(txt_ptr,clr,pos);
|
|
if(mode == MENU_PROCESS_VALUE_CHANGE) // Shifted over
|
|
{
|
|
UiMenu_MoveCursor(pos);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Render a menu entry on a given menu position
|
|
*/
|
|
void UiMenu_UpdateMenuEntry(const MenuDescriptor* entry, MenuProcessingMode_t mode, uint8_t pos)
|
|
{
|
|
uint32_t m_clr;
|
|
m_clr = Yellow;
|
|
char out[40];
|
|
|
|
char blank[40] = " ";
|
|
blank[ts.Layout->MENU_TEXT_SIZE_MAX-1]=0;
|
|
//#else
|
|
// const char blank[34] = " ";
|
|
//#endif
|
|
|
|
if (entry != NULL && (entry->kind == MENU_ITEM || entry->kind == MENU_GROUP || entry->kind == MENU_INFO || entry->kind == MENU_TEXT) )
|
|
{
|
|
if (mode == MENU_RENDER_ONLY)
|
|
{
|
|
uint16_t level = 0;
|
|
const MenuDescriptor* parent = entry;
|
|
do
|
|
{
|
|
parent = UiMenu_GetParentForEntry(parent);
|
|
level++;
|
|
}
|
|
while (parent != NULL);
|
|
level--;
|
|
|
|
// level = 3;
|
|
// uint16_t labellen = strlen(entry->id)+strlen(entry->label) + 1;
|
|
uint16_t labellen = level+strlen(entry->label);
|
|
// snprintf(out,34,"%s-%s%s",entry->id,entry->label,(&blank[labellen>33?33:labellen]));
|
|
//#ifdef USE_DISP_480_320
|
|
//snprintf(out,ts.40,"%s%s%s",(&blank[level>5?37-5:37-level]),entry->label,(&blank[labellen>39?39:labellen]));
|
|
snprintf(out,ts.Layout->MENU_TEXT_SIZE_MAX,"%s%s%s",
|
|
(&blank[level>5?ts.Layout->MENU_TEXT_SIZE_MAX-8:ts.Layout->MENU_TEXT_SIZE_MAX-3-level]),
|
|
entry->label,(&blank[labellen>ts.Layout->MENU_TEXT_SIZE_MAX-1?ts.Layout->MENU_TEXT_SIZE_MAX-1:labellen]));
|
|
|
|
// snprintf(out,34,"%s%s%s",(&blank[level>5?31-5:31-level]),entry->label,(&blank[labellen>33?33:labellen]));
|
|
|
|
UiMenu_DisplayLabel(out,m_clr,pos);
|
|
}
|
|
switch(entry->kind)
|
|
{
|
|
case MENU_ITEM:
|
|
// TODO: Better Handler Selection with need for change in this location to add new handlers
|
|
UiMenu_UpdateLines(entry->number,mode,pos);
|
|
break;
|
|
case MENU_INFO:
|
|
UiMenu_UpdateHWInfoLines(entry->number,mode,pos);
|
|
break;
|
|
case MENU_TEXT:
|
|
break;
|
|
case MENU_GROUP:
|
|
if (mode == MENU_PROCESS_VALUE_CHANGE)
|
|
{
|
|
bool old_state = UiMenu_GroupIsUnfolded(entry);
|
|
if (ts.menu_var < 0 )
|
|
{
|
|
UiMenu_GroupFold(entry, !(ts.flags2 & FLAGS2_UI_INVERSE_SCROLLING));
|
|
}
|
|
if (ts.menu_var > 0 )
|
|
{
|
|
UiMenu_GroupFold(entry, ts.flags2 & FLAGS2_UI_INVERSE_SCROLLING);
|
|
}
|
|
if (old_state != UiMenu_GroupIsUnfolded(entry))
|
|
{
|
|
int idx;
|
|
for (idx = pos+1; idx < ts.Layout->MENUSIZE; idx++)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[idx],UiMenu_NextMenuEntry(menu[idx-1].entryItem));
|
|
UiMenu_UpdateMenuEntry(menu[idx].entryItem, MENU_RENDER_ONLY, idx);
|
|
}
|
|
}
|
|
ts.menu_var = 0;
|
|
}
|
|
|
|
UiMenu_DisplayValue(
|
|
UiMenu_GroupIsUnfolded(entry)?"HIDE":"SHOW",
|
|
m_clr,pos);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UiMenu_DisplayLabel(blank,m_clr,pos);
|
|
}
|
|
if (mode == MENU_PROCESS_VALUE_CHANGE)
|
|
{
|
|
UiMenu_MoveCursor(pos);
|
|
}
|
|
}
|
|
|
|
void UiMenu_DisplayInitSlots(const MenuDescriptor* entry)
|
|
{
|
|
int idx;
|
|
for (idx=0; idx < ts.Layout->MENUSIZE; idx++)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[idx],entry);
|
|
entry = UiMenu_NextMenuEntry(entry);
|
|
}
|
|
}
|
|
void UiMenu_DisplayInitSlotsBackwards(const MenuDescriptor* entry)
|
|
{
|
|
int idx;
|
|
for (idx=ts.Layout->MENUSIZE; idx > 0; idx--)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[idx-1],entry);
|
|
entry = UiMenu_PrevMenuEntry(entry);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @returns true if at least one slot was moved, false if no change done
|
|
*/
|
|
bool UiMenu_DisplayMoveSlotsBackwards(int16_t change)
|
|
{
|
|
int idx;
|
|
int dist = (change % ts.Layout->MENUSIZE);
|
|
int screens = change / ts.Layout->MENUSIZE;
|
|
bool retval = false; // n
|
|
for (idx = 0; idx < screens; idx++)
|
|
{
|
|
const MenuDescriptor *prev = UiMenu_PrevMenuEntry(menu[0].entryItem);
|
|
if (prev != NULL)
|
|
{
|
|
retval = true;
|
|
UiMenu_DisplayInitSlotsBackwards(prev);
|
|
}
|
|
else
|
|
{
|
|
// we stop here, since no more previous elements.
|
|
// TODO: Decide if roll over, in this case we would have to get very last element and
|
|
// then continue from there.
|
|
dist = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dist != 0)
|
|
{
|
|
retval = true;
|
|
for (idx = ts.Layout->MENUSIZE-dist; idx > 0; idx--)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[ts.Layout->MENUSIZE-idx],menu[ts.Layout->MENUSIZE-(dist+idx)].entryItem);
|
|
}
|
|
|
|
for (idx = ts.Layout->MENUSIZE-dist; idx >0; idx--)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[idx-1],UiMenu_PrevMenuEntry(menu[idx].entryItem));
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
/*
|
|
* @returns true if at least one slot was moved, false if no change done
|
|
*/
|
|
bool UiMenu_DisplayMoveSlotsForward(int16_t change)
|
|
{
|
|
int idx;
|
|
int dist = (change % ts.Layout->MENUSIZE);
|
|
int screens = change / ts.Layout->MENUSIZE;
|
|
bool retval = false;
|
|
// first jump screens. we have to iterate through the menu structure one by one
|
|
// in order to respect fold/unfold state etc.
|
|
for (idx = 0; idx < screens; idx++)
|
|
{
|
|
const MenuDescriptor *next = UiMenu_NextMenuEntry(menu[ts.Layout->MENUSIZE-1].entryItem);
|
|
if (next != NULL)
|
|
{
|
|
UiMenu_DisplayInitSlots(next);
|
|
retval = true;
|
|
}
|
|
else
|
|
{
|
|
// stop here
|
|
// TODO: Rollover?
|
|
dist = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (dist != 0)
|
|
{
|
|
retval = true;
|
|
for (idx = 0; idx < ts.Layout->MENUSIZE-dist; idx++)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[idx],menu[dist+idx].entryItem);
|
|
}
|
|
for (idx = ts.Layout->MENUSIZE-dist; idx < ts.Layout->MENUSIZE; idx++)
|
|
{
|
|
UiMenu_FillSlotWithEntry(&menu[idx],UiMenu_NextMenuEntry(menu[idx-1].entryItem));
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
* @brief Display and change menu items
|
|
* @param mode 0=show all, 1=update current item, 3=restore default setting for selected item
|
|
*
|
|
*/
|
|
void UiMenu_RenderMenu(MenuProcessingMode_t mode)
|
|
{
|
|
if (init_done == false )
|
|
{
|
|
UiMenu_DisplayInitSlots(groups[MENU_START_IDX].entries);
|
|
init_done = true;
|
|
}
|
|
// UiMenu_DisplayMoveSlotsForward(6);
|
|
// UiMenu_DisplayMoveSlotsForward(3);
|
|
// UiMenu_DisplayMoveSlotsBackwards(10);
|
|
switch (mode)
|
|
{
|
|
case MENU_RENDER_ONLY: // (re)draw all labels and values
|
|
{
|
|
int idx;
|
|
for (idx = 0; idx < ts.Layout->MENUSIZE; idx++)
|
|
{
|
|
UiMenu_UpdateMenuEntry(menu[idx].entryItem,mode, idx);
|
|
}
|
|
UiMenu_MoveCursor(ts.menu_item%ts.Layout->MENUSIZE); //redraw of cursor
|
|
}
|
|
break;
|
|
|
|
case MENU_PROCESS_VALUE_SETDEFAULT:
|
|
case MENU_PROCESS_VALUE_CHANGE:
|
|
{
|
|
// wrapping to next screen (and from end to start and vice versa)
|
|
if (ts.menu_item >= ts.Layout->MENUSIZE)
|
|
{
|
|
if (UiMenu_RenderNextScreen() == false)
|
|
{
|
|
UiMenu_RenderFirstScreen();
|
|
}
|
|
}
|
|
else if (ts.menu_item < 0)
|
|
{
|
|
if (UiMenu_RenderPrevScreen() == false)
|
|
{
|
|
UiMenu_RenderLastScreen();
|
|
}
|
|
|
|
}
|
|
|
|
ts.menu_item%=ts.Layout->MENUSIZE;
|
|
if (ts.menu_item < 0) ts.menu_item+=ts.Layout->MENUSIZE;
|
|
|
|
uint16_t current_item = ts.menu_item%ts.Layout->MENUSIZE;
|
|
UiMenu_UpdateMenuEntry(menu[current_item].entryItem,mode, current_item);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void UiMenu_RenderChangeItemValue(int16_t pot_diff)
|
|
{
|
|
if(pot_diff < 0)
|
|
{
|
|
ts.menu_var--; // increment selected item
|
|
}
|
|
else
|
|
{
|
|
ts.menu_var++; // decrement selected item
|
|
}
|
|
UiMenu_RenderMenu(MENU_PROCESS_VALUE_CHANGE); // perform update of selected item
|
|
}
|
|
|
|
void UiMenu_RenderChangeItem(int16_t pot_diff)
|
|
{
|
|
if(ts.flags2 & FLAGS2_UI_INVERSE_SCROLLING)
|
|
{
|
|
pot_diff = -pot_diff;
|
|
}
|
|
if(pot_diff < 0)
|
|
{
|
|
ts.menu_item--;
|
|
}
|
|
else if(pot_diff > 0)
|
|
{
|
|
ts.menu_item++;
|
|
}
|
|
ts.menu_var = 0; // clear variable that is used to change a menu item
|
|
UiMenu_RenderMenu(MENU_PROCESS_VALUE_CHANGE); // Update that menu item
|
|
}
|
|
|
|
void UiMenu_RenderLastScreen()
|
|
{
|
|
while (menu[ts.Layout->MENUSIZE-1].entryItem != NULL && UiMenu_NextMenuEntry(menu[ts.Layout->MENUSIZE-1].entryItem) != NULL )
|
|
{
|
|
UiMenu_DisplayMoveSlotsForward(ts.Layout->MENUSIZE);
|
|
}
|
|
UiMenu_RenderMenu(MENU_RENDER_ONLY);
|
|
}
|
|
|
|
void UiMenu_RenderFirstScreen()
|
|
{
|
|
init_done = false;
|
|
UiMenu_RenderMenu(MENU_RENDER_ONLY);
|
|
}
|
|
|
|
bool UiMenu_RenderNextScreen()
|
|
{
|
|
bool retval = UiMenu_DisplayMoveSlotsForward(ts.Layout->MENUSIZE);
|
|
if (retval)
|
|
{
|
|
UiMenu_RenderMenu(MENU_RENDER_ONLY);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool UiMenu_RenderPrevScreen()
|
|
{
|
|
bool retval = UiMenu_DisplayMoveSlotsBackwards(ts.Layout->MENUSIZE);
|
|
if (retval)
|
|
{
|
|
UiMenu_RenderMenu(MENU_RENDER_ONLY);
|
|
}
|
|
return retval;
|
|
}
|