/* * widgets.c * * Created on: Jan 12, 2021 * Author: David Original work by Jose (PTDreamer), 2017 */ #include "widgets.h" #include "screen.h" #include "oled.h" #include "gui.h" char displayString[32]; displayOnly_widget_t * extractDisplayPartFromWidget(widget_t *w) { comboBox_widget_t* combo; if(!w) return NULL; switch (w->type) { case widget_display: return (displayOnly_widget_t*)w->content; case widget_editable: case widget_multi_option: return &((editable_widget_t*)w->content)->inputData; case widget_combo: combo = (comboBox_widget_t*)w->content; if((combo->currentItem) && ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption))){ return &((comboBox_widget_t*)w->content)->currentItem->widget->inputData; } default: return NULL; break; } } editable_widget_t * extractEditablePartFromWidget(widget_t *w) { comboBox_widget_t* combo; if(!w) return NULL; switch (w->type) { case widget_editable: case widget_multi_option: return (editable_widget_t*)w->content; case widget_combo: combo = (comboBox_widget_t*)w->content; if((combo->currentItem) && ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption))){ return ((comboBox_widget_t*)w->content)->currentItem->widget; } default: return NULL; break; } } selectable_widget_t * extractSelectablePartFromWidget(widget_t *w) { if(!w) return NULL; switch (w->type) { case widget_editable: case widget_multi_option: return &((editable_widget_t*)w->content)->selectable; case widget_button: case widget_bmp_button: return &((button_widget_t*)w->content)->selectable; case widget_combo: return &((comboBox_widget_t*)w->content)->selectable; default: return NULL; break; } } void widgetDefaultsInit(widget_t *w, widgetType t, void* content){ if(!w || !content){ return; } w->type = t; w->draw = &default_widgetDraw; w->update = &default_widgetUpdate; w->enabled = 1; w->frameType = frame_auto; w->refresh = refresh_idle; w->radius=-1; w->posX = 0; w->posY = 0; w->content = content; w->next_widget = NULL; selectable_widget_t *sel = extractSelectablePartFromWidget(w); displayOnly_widget_t *dis = extractDisplayPartFromWidget(w); editable_widget_t *edit = extractEditablePartFromWidget(w); if(sel) { sel->previous_state = widget_idle; sel->state = widget_idle; sel->tab = 0; sel->processInput = &default_widgetProcessInput; sel->longPressAction = NULL; } if(dis){ dis->font = default_font; dis->getData = NULL; dis->reservedChars = 0; dis->number_of_dec = 0; dis->type = field_int32; dis->displayString=displayString; dis->endString=NULL; dis->stringStart=0; dis->last_value = 0; dis->textAlign = align_center; dis->dispAlign = align_disabled; } if(edit){ edit->step=1; edit->big_step=1; edit->current_edit=0; edit->max_value=2147483647; edit->min_value=0; edit->setData=NULL; } switch (t) { case widget_multi_option: edit->max_value = 255; edit->numberOfOptions = 0; edit->options = NULL; break; case widget_combo: { comboBox_widget_t* combo = content; combo->currentItem = NULL; w->frameType = frame_combo; combo->first = NULL; w->draw = &comboBoxDraw; combo->currentScroll = 0; combo->selectable.processInput = &comboBoxProcessInput; w->parent->current_widget = w; combo->font = default_font; break; } case widget_button: { button_widget_t* button = (button_widget_t*)content; button->action = NULL; button->displayString = NULL; button->xbm = NULL; button->last_xbm = NULL; button->font = default_font; break; } default: break; } sel = extractSelectablePartFromWidget(w); dis = extractDisplayPartFromWidget(w); edit = extractEditablePartFromWidget(w); } void editableDefaultsInit(editable_widget_t* editable, widgetType type){ widget_t w; widgetDefaultsInit(&w, type, editable); } static void insertDot(char *str, uint8_t dec) { for(int x = strlen(str); x > (int)strlen(str) - (int)dec - 2; --x) { str[x + 1] = str[x]; } str[strlen(str) - dec - 1] = '.'; } void widgetEnable(widget_t* w){ if(w && !w->enabled){ w->enabled = 1; w->refresh = refresh_triggered; } } void widgetDisable(widget_t* w){ if(w && w->enabled){ w->enabled = 0; widgetClearField(w); } } void default_widgetUpdate(widget_t *w) { if(!w){ return ; } if(!w->enabled){ w->refresh=refresh_idle; return; } button_widget_t* button; bmp_widget_t* bmp; int32_t val_ui=0; displayOnly_widget_t* dis = extractDisplayPartFromWidget(w); editable_widget_t *edit = extractEditablePartFromWidget(w); switch(w->type){ case widget_bmp_button: button = ((button_widget_t*)w->content); if(!button->xbm){ widgetDisable(w); return; } break; case widget_bmp: bmp = ((bmp_widget_t*)w->content); if(!bmp->xbm){ widgetDisable(w); return; } break; case widget_button: button = ((button_widget_t*)w->content); if(!button->displayString){ widgetDisable(w); return; } val_ui=strsum(button->displayString); // Get string sum break; case widget_multi_option: if(!dis){ widgetDisable(w); return; }; val_ui=strsum(edit->options[*(uint8_t*)dis->getData()]); // Get string sum break; case widget_display: case widget_editable: if(!dis){ widgetDisable(w); return; } if(dis->type == field_string){ val_ui=strsum(dis->getData()); // Get string sum break; } else{ val_ui = *(int32_t*)dis->getData(); uint8_t sum = strsum(dis->endString); widgetDetectChange(w,(val_ui + sum)); // Check for changes in value and endString return; } break; default: return; } widgetDetectChange(w,val_ui); } void widgetDetectChange(widget_t* w, int32_t val){ displayOnly_widget_t* dis; bmp_widget_t* bmp; button_widget_t* button; bool refresh=0; switch(w->type){ case widget_display: case widget_editable: case widget_multi_option: dis=extractDisplayPartFromWidget(w); if(dis->last_value!=val){ dis->last_value=val; refresh=1; } break; case widget_button: button = (button_widget_t*)w->content; if(button->last_value != val){ button->last_value = val; refresh=1; } break; case widget_bmp_button: button = (button_widget_t*)w->content; if(button->last_xbm != button->xbm){ button->last_xbm = button->xbm; refresh=1; } break; case widget_bmp: bmp = (bmp_widget_t*)w->content; if(bmp->last_xbm != bmp->xbm){ bmp->last_xbm = bmp->xbm; refresh=1; } break; default: break; } if(refresh && w->refresh==refresh_idle){ w->refresh=refresh_triggered; } } // Clear widget field before drawing widget with new data void widgetClearField(widget_t* w){ if(!w){ return; } selectable_widget_t* sel = extractSelectablePartFromWidget(w); displayOnly_widget_t* dis = extractDisplayPartFromWidget(w); button_widget_t* button; bmp_widget_t* bmp; uint8_t cHeight = u8g2_GetMaxCharHeight(&u8g2); if(dis && dis->font==u8g2_font_iron){ cHeight+=3; } // TODO this font reports lower height, needs to be manually adjusted uint8_t r; if(w->frameType!=frame_combo){ u8g2_SetDrawColor(&u8g2, BLACK); if(w->parent->refresh < screenRefresh_eraseNow ){ switch((uint8_t)w->type){ case widget_bmp: bmp = (bmp_widget_t*)w->content; u8g2_DrawBox(&u8g2, w->posX, w->posY, bmp->xbm[0], bmp->xbm[1]); break; case widget_bmp_button: button = (button_widget_t*)w->content; u8g2_DrawBox(&u8g2, w->posX, w->posY, button->xbm[0]+4, button->xbm[1]+4); break; case widget_button: button = (button_widget_t*)w->content; if(button->font==u8g2_font_iron){ cHeight+=3; } // TODO this font reports lower height, needs to be manually adjusted u8g2_DrawBox(&u8g2, w->posX, w->posY, w->width, cHeight+1); break; case widget_display: u8g2_DrawBox(&u8g2, w->posX, w->posY, w->width, cHeight+1); // Draw black square to erase previous data break; case widget_editable: case widget_multi_option: if(dis->font==u8g2_font_iron){ cHeight+=3; } // TODO this font reports lower height, needs to be manually adjusted u8g2_DrawBox(&u8g2, w->posX ,w->posY, w->width, cHeight+1); break; } } if( (sel && (sel->state==widget_edit) && (dis->type!=field_string) && (w->frameType==frame_auto)) || (w->frameType==frame_solid) ){ u8g2_SetDrawColor(&u8g2, WHITE); if(w->radius<0){ r=(cHeight-1)/2; } else{ r = w->radius; } if(sel){ u8g2_DrawRBox(&u8g2, w->posX ,w->posY, w->width, cHeight+1,r ); } else{ u8g2_DrawRBox(&u8g2, w->posX ,w->posY, w->width+2, cHeight+1, r); } u8g2_SetDrawColor(&u8g2, XOR); return; } u8g2_SetDrawColor(&u8g2, WHITE); } else{ // In combo if(sel->state==widget_edit){ // Being edited u8g2_SetDrawColor(&u8g2, XOR); // Set XOR color } else{ // Not editing u8g2_SetDrawColor(&u8g2, WHITE); // Set white color } } } void widgetAlign(widget_t* w){ if(!w || w->frameType==frame_combo){ return; } uint8_t strWidth=0, stringStart=0, textAlign=align_disabled, dispAlign=align_disabled; displayOnly_widget_t *dis=extractDisplayPartFromWidget(w); selectable_widget_t *sel=extractSelectablePartFromWidget(w); editable_widget_t *edit = extractEditablePartFromWidget(w); button_widget_t* button = NULL; switch(w->type){ case widget_button: button = (button_widget_t *)w->content; strWidth=u8g2_GetStrWidth(&u8g2, button->displayString); break; case widget_display: case widget_editable: if(dis->type == field_string){ strWidth=u8g2_GetStrWidth(&u8g2, (char *)dis->getData()); } else{ strWidth=u8g2_GetStrWidth(&u8g2, displayString); } break; case widget_multi_option: strWidth=u8g2_GetStrWidth(&u8g2, (char *)edit->options[*(uint8_t*)dis->getData()]); break; default: return; } if(!strWidth){ // Empty widget, wrong settings widgetDisable(w); // Disable widget return; } if(dis){ textAlign = dis->textAlign; dispAlign = dis->dispAlign; } else if(w->type == widget_button){ textAlign = align_center; dispAlign = align_disabled; } else{ return; } if(sel && (w->frameType!=frame_disabled)){ // If selectable, extra space for not overlapping the frame if(w->width < strWidth+7){ // If width too small if(strWidth+7width=strWidth+7; // Use width from str width } else{ w->width = strWidth; // Else, don't add extra space } } } else{ if(w->width < strWidth){ // If width too small w->width=strWidth; // Use width from str width } } if(w->width > OledWidth){ if(textAlign != align_disabled) textAlign = align_left; if(dispAlign != align_disabled) dispAlign = align_left; } switch(dispAlign){ case align_disabled: break; case align_center: w->posX =(OledWidth - w->width)/2; break; case align_right: w->posX = OledWidth - w->width; break; case align_left: default: w->posX = 0; break; } switch(textAlign){ case align_disabled: break; case align_center: stringStart = (w->posX + ((w->width-strWidth)/2)); break; case align_right: stringStart =(w->posX + (w->width-strWidth)); if(sel){ if(stringStart >= 3){ stringStart -= 3; } } break; case align_left: default: if(sel && ((w->posX+3)<=OledWidth)){ stringStart=w->posX+3; } else{ stringStart = w->posX; } break; } if(w->type == widget_button){ button->stringStart=stringStart; } else if(dis){ dis->stringStart = stringStart; } } void default_widgetDraw(widget_t *w) { if(!w || !w->enabled){ return; } bool frameDraw = 0; bool frameColor = BLACK; displayOnly_widget_t* dis = extractDisplayPartFromWidget(w); editable_widget_t* edit = extractEditablePartFromWidget(w); selectable_widget_t* sel = extractSelectablePartFromWidget(w); button_widget_t* button = NULL; bmp_widget_t* bmp = NULL; const uint8_t* font = NULL; uint8_t refresh = w->refresh | w->parent->refresh; uint8_t cHeight = 0; if( dis ){ u8g2_SetFont(&u8g2, dis->font); cHeight = u8g2_GetMaxCharHeight(&u8g2); font = dis->font; } else if((w->type == widget_button)||(w->type == widget_bmp_button)){ button = (button_widget_t*)w->content; if(w->type == widget_button){ u8g2_SetFont(&u8g2, button->font); cHeight = u8g2_GetMaxCharHeight(&u8g2); font = button->font; } } else if(w->type == widget_bmp){ bmp = (bmp_widget_t*)w->content; } if(w->frameType==frame_outline){ frameDraw=1; frameColor = WHITE; } else if(w->frameType==frame_auto){ if(sel) { if(refresh==refresh_idle){ // If not forced refresh, check for changes switch (sel->state) { case widget_selected: if(sel->previous_state != widget_selected){ frameDraw=1; frameColor = WHITE; if(sel->previous_state == widget_edit){ refresh=refresh_triggered; } } break; case widget_idle: if(sel->previous_state != widget_idle){ frameDraw=1; } break; case widget_edit: if(sel->previous_state != widget_edit){ refresh=refresh_triggered; } break; default: return; } } else{ // Else, redraw frames switch (sel->state) { case widget_selected: frameDraw=1; frameColor = WHITE; break; case widget_idle: case widget_edit: break; default: return; } } } } if(refresh){ if(dis && dis->type==field_int32){ int32_t val_ui = *(int32_t*)dis->getData(); uint8_t decimals = dis->number_of_dec+1; if(val_ui<0){ decimals++; } if(decimals>10){ decimals=10; } snprintf(dis->displayString, dis->reservedChars+1, "%0*ld", decimals, (int32_t)val_ui); // Convert value into string uint8_t dispLen=strlen(dis->displayString); uint8_t endLen=strlen(dis->endString); if(dis->number_of_dec){ // If there're decimals if(dis->reservedChars >= (dispLen+1)){ // Ensure there's enough space in the string for adding the decimal point insertDot(dis->displayString, dis->number_of_dec); // Insert decimal dot } } if(dis->reservedChars >= (dispLen+endLen)){ // Ensure there's enough space in the string for adding the end String strcat(dis->displayString, dis->endString); // Append endString } dis->displayString[dis->reservedChars]=0; // Ensure last string char is 0 } widgetAlign(w); // Align widgetClearField(w); // Clear old field if(w->refresh==refresh_triggered){ w->refresh=refresh_idle; } switch(w->type){ case widget_bmp: u8g2_SetDrawColor(&u8g2, WHITE); u8g2_DrawXBMP(&u8g2, w->posX, w->posY, bmp->xbm[0], bmp->xbm[1], &bmp->xbm[2]); return; case widget_bmp_button: u8g2_SetDrawColor(&u8g2, WHITE); u8g2_DrawXBMP(&u8g2, w->posX+2, w->posY+2, button->xbm[0], button->xbm[1], &button->xbm[2]); break; case widget_button: u8g2_DrawStr(&u8g2, button->stringStart, w->posY+2, button->displayString); break; case widget_display: if(dis->type == field_string){ u8g2_DrawStr(&u8g2, dis->stringStart, w->posY, dis->getData()); } else{ u8g2_DrawStr(&u8g2, dis->stringStart, w->posY, dis->displayString); } break; case widget_editable: if(dis->type == field_string){ u8g2_DrawStr(&u8g2, dis->stringStart, w->posY+2, dis->getData()); if(sel->state == widget_edit){ char t[20]; strcpy(t,dis->getData()); t[edit->current_edit+1]=0; uint8_t x2=u8g2_GetStrWidth(&u8g2, t); t[edit->current_edit]=0; uint8_t x1=u8g2_GetStrWidth(&u8g2, t); u8g2_SetDrawColor(&u8g2, BLACK); u8g2_DrawBox(&u8g2, dis->stringStart, w->posY+ cHeight, w->width, 2); u8g2_SetDrawColor(&u8g2, WHITE); u8g2_DrawBox(&u8g2, dis->stringStart+x1, w->posY+ cHeight, x2-x1, 2); } else if(sel->previous_state == widget_edit){ u8g2_SetDrawColor(&u8g2, BLACK); u8g2_DrawBox(&u8g2, dis->stringStart, w->posY+ cHeight, w->width, 2); } } else{ u8g2_DrawStr(&u8g2,dis->stringStart, w->posY+2, dis->displayString); } break; case widget_multi_option: u8g2_DrawStr(&u8g2, dis->stringStart, w->posY+2, edit->options[*(uint8_t*)dis->getData()]);// Draw string break; default: return; } } if(sel) { sel->previous_state = sel->state; } if(frameDraw || w->frameType==frame_outline){ if(font==u8g2_font_iron){ cHeight+=3; } switch(w->type){ case widget_bmp_button: u8g2_SetDrawColor(&u8g2, frameColor); u8g2_DrawRFrame(&u8g2, w->posX, w->posY, button->xbm[0]+4, button->xbm[1]+4, 4); break; case widget_editable: case widget_button: case widget_multi_option: u8g2_SetDrawColor(&u8g2, frameColor); uint8_t r; if(w->radius<0){ r=(cHeight-1)/2; } else{ r = w->radius; } u8g2_DrawRFrame(&u8g2, w->posX ,w->posY, w->width, cHeight+1, r); default: break; } } u8g2_SetDrawColor(&u8g2, WHITE); } void comboBoxDraw(widget_t *w) { uint16_t yDim = OledHeight - w->posY; uint8_t height; int8_t frameY=0; int8_t posY;; uint8_t drawFrame=1; comboBox_widget_t* combo = (comboBox_widget_t*)w->content; comboBox_item_t *item = combo->first; uint8_t scroll = 0; uint8_t r; if(!w || ((w->refresh==refresh_idle) && (w->parent->refresh==screenRefresh_idle))){ return; } if(w->refresh==refresh_triggered){ w->refresh=refresh_idle; } if(w->parent->refresh < screenRefresh_eraseNow){ // If screen not erased already w->parent->refresh = screenRefresh_alreadyErased; FillBuffer(BLACK, fill_dma); // Erase fast using dma } if(!item){ return; } // Return if null item u8g2_SetFont(&u8g2, combo->font); height= u8g2_GetMaxCharHeight(&u8g2)+1; // +1 to allow separation between combobox items if(w->radius<0){ r=(height-1)/2; } else{ r = w->radius; } while(scroll < combo->currentScroll) { if(!item->next_item) break; item = item->next_item; if(item->enabled) ++scroll; } for(uint8_t y = 0; y < yDim / height; ++y) { // Store Y position if(item == combo->currentItem) { // If the current combo item is selected frameY=y * height + w->posY; } if ((item->type==combo_Screen)||(item->type==combo_Action)){ // If screen or action item, just draw the label u8g2_SetDrawColor(&u8g2, WHITE); if(item->dispAlign==align_left){ u8g2_DrawStr(&u8g2, 4, y * height + w->posY +2, item->text); } else{ uint8_t len = u8g2_GetStrWidth(&u8g2, item->text); if(item->dispAlign==align_right){ u8g2_DrawStr(&u8g2, OledWidth-3-len, y * height + w->posY +2, item->text); } else{ u8g2_DrawStr(&u8g2, (OledWidth-1-len)/2, y * height + w->posY +2, item->text); } } } else if ((item->type==combo_Editable)||(item->type==combo_MultiOption)){ editable_widget_t* edit = item->widget; selectable_widget_t *sel = &edit->selectable; displayOnly_widget_t* dis = &edit->inputData; default_widgetUpdate(w); uint8_t len=0; if(sel->state==widget_edit){ // If edit mode drawFrame=0; u8g2_SetDrawColor(&u8g2, WHITE); u8g2_DrawRBox(&u8g2, 0, frameY, OledWidth, height, r); u8g2_SetDrawColor(&u8g2, XOR); } else{ u8g2_SetDrawColor(&u8g2, WHITE); } if(item->type==combo_MultiOption){ len = u8g2_GetStrWidth(&u8g2,edit->options[*(uint8_t*)dis->getData()]); } else if(item->type==combo_Editable){ int32_t val_ui = *(int32_t*)dis->getData(); // Get data uint8_t decimals = dis->number_of_dec+1; // Load decimal count if(val_ui<0){ // If negative, add a decimal (decimals are just used as min char output in sprintf) decimals++; } if(decimals>10){ decimals=10; } // Limit max decimals snprintf(dis->displayString, dis->reservedChars+1, "%0*ld", decimals, (int32_t)val_ui); // Convert value into string uint8_t dispLen=strlen(dis->displayString); // Get string len uint8_t endLen=strlen(dis->endString); // Get endStr len if(dis->number_of_dec){ // If there're decimals if(dis->reservedChars >= (dispLen+1)){ // Ensure there's enough space in the string for adding the decimal point insertDot(dis->displayString, dis->number_of_dec); // Insert decimal dot } } if(dis->reservedChars >= (dispLen+endLen)){ // Ensure there's enough space in the string for adding the end String strcat(dis->displayString, dis->endString); // Append endString } dis->displayString[dis->reservedChars]=0; // Ensure last string char is 0 len=u8g2_GetStrWidth(&u8g2,dis->displayString); } posY = y * height + w->posY; // Set widget Ypos same as the current combo option dis->stringStart = OledWidth-len-5; // Align to the left measuring actual string width if(item->type==combo_Editable){ u8g2_DrawStr(&u8g2,dis->stringStart, posY+2, dis->displayString); // Draw widget data } else{ u8g2_DrawStr(&u8g2,dis->stringStart, posY+2, edit->options[*(uint8_t*)dis->getData()]); } u8g2_DrawStr(&u8g2, 4, y * height + w->posY +2, item->text); // Draw the combo item label } do { item = item->next_item; }while(item && !item->enabled); if(!item){ break; } } if(drawFrame){ u8g2_SetDrawColor(&u8g2, WHITE); u8g2_DrawRFrame(&u8g2, 0, frameY, OledWidth, height, r); } return; } uint8_t comboItemToIndex(widget_t *combo, comboBox_item_t *item) { if(!combo || !item){ return 0; } uint8_t index = 0; comboBox_item_t *i = ((comboBox_widget_t*)combo->content)->first; while(i && i != item) { i = i->next_item; if(i->enabled) ++ index; } return index; } int comboBoxProcessInput(widget_t *w, RE_Rotation_t input, RE_State_t *state) { comboBox_widget_t* combo = (comboBox_widget_t*)w->content; if(!w || (input == Rotate_Nothing)){ return -1; } u8g2_SetFont(&u8g2, combo->font); uint8_t firstIndex = combo->currentScroll; uint16_t yDim = OledHeight - w->posY; uint16_t height = u8g2_GetMaxCharHeight(&u8g2)+1; uint8_t maxIndex = yDim / height; uint8_t lastIndex = combo->currentScroll + maxIndex -1; selectable_widget_t *sel; if(w->refresh==refresh_idle){ w->refresh=refresh_triggered; // Update in combo erases whole screen (to avoid possible leftovers) } if ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption)){ // If combo editable type sel = &combo->currentItem->widget->selectable; // Get selectable data } if((input == Click)||(input == LongClick)){ // If clicked if ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption)){ // If combo editable type if(sel->state==widget_idle){ // If widget idle sel->state=widget_edit; // Change to edit } else if(sel->state==widget_edit){ // If widget in edit mode sel->state=widget_idle; // Change to idle } return -1; // Do nothing else } if (combo->currentItem->type==combo_Action){ // If combo Action type return combo->currentItem->action(); // Process action } else if (combo->currentItem->type==combo_Screen){ // If combo screen type return combo->currentItem->action_screen; // Return screen index } } if((input == Rotate_Increment) || (input == Rotate_Decrement) || // If rotation data (input == Rotate_Increment_while_click) || (input == Rotate_Decrement_while_click)){ if ((combo->currentItem->type==combo_Editable)||(combo->currentItem->type==combo_MultiOption)){ // If combo option type if(sel->state==widget_edit){ // If widget in edit mode default_widgetProcessInput(w, input, state); // Process widget return -1; // Return } } if(input == Rotate_Increment) { //Set comboBox item to next comboBox comboBox_item_t *current = combo->currentItem->next_item; // if comboBox is disabled, skip. While comboBox still valid while(current && !current->enabled) { current = current->next_item; } // If comboBox valid(didn't reach end) if(current) { combo->currentItem = current; uint8_t index = comboItemToIndex(w, current); if(index > lastIndex) ++combo->currentScroll; } } else if(input == Rotate_Decrement) { comboBox_item_t *current = NULL; // If comboBox is not the first element if(combo->currentItem != combo->first){ do { current = combo->first; while(current->next_item != combo->currentItem) { current = current->next_item; } combo->currentItem = current; }while(!current->enabled); uint8_t index = comboItemToIndex(w, current); if(index < firstIndex) --combo->currentScroll; } } } return -1; } int32_t strsum(char* str){ int32_t sum=0; while(*str){ sum+=*str; str++; } return sum; } //returns -1 if processed, -2 if not processed, or next screen int default_widgetProcessInput(widget_t *w, RE_Rotation_t input, RE_State_t *state) { displayOnly_widget_t* dis = extractDisplayPartFromWidget(w); selectable_widget_t* sel = extractSelectablePartFromWidget(w); editable_widget_t* edit = extractEditablePartFromWidget(w); comboBox_widget_t* combo = NULL; if(!w || (input == Rotate_Nothing)){ return -1; } if(!sel) { return -2; } if(w->type==widget_combo){ combo = (comboBox_widget_t*)w->content; } if(input == LongClick) { if(sel->longPressAction){ return sel->longPressAction(w); } else{ input = Click; } } if(input == Click) { switch (sel->state) { case widget_selected: if(w->refresh==refresh_idle){ w->refresh = refresh_triggered; } if((w->type == widget_button)||(w->type == widget_bmp_button)){ return ((button_widget_t*)w->content)->action(w); } if(dis->type == field_string){ strcpy(dis->displayString, (char*)edit->inputData.getData()); edit->current_edit = 0; } sel->state = widget_edit; sel->previous_state = widget_selected; break; case widget_edit: if(w->refresh==refresh_idle){ w->refresh = refresh_triggered; } if(dis->type == field_string) { ++edit->current_edit; if(edit->current_edit== dis->reservedChars){ sel->state = widget_selected; sel->previous_state = widget_edit; edit->current_edit = 0; } } else { sel->state = widget_selected; sel->previous_state = widget_edit; } break; default: break; } return -1; } if(((w->type == widget_editable) && (sel->state == widget_edit)) || ((w->type == widget_combo)&&combo->currentItem->type==combo_Editable)) { // Combo only calls this when incrementing or decrementing int32_t val_ui; int32_t inc; if(w->refresh==refresh_idle){ w->refresh=refresh_triggered; } if(input == Rotate_Increment_while_click){ input = Rotate_Increment; inc = edit->big_step; } else if(input == Rotate_Decrement_while_click){ input = Rotate_Decrement; inc = edit->big_step; } else{ inc = edit->step; } if(state->Diff < 0){ inc = -inc; } if(edit->inputData.type==field_string){ int16_t current_edit = (char)dis->displayString[edit->current_edit]; current_edit += inc; switch(current_edit){// \0, ' ', 0-9, A-Z case '0'-1: current_edit =' '; break; case ' '-1: current_edit='Z'; break; case 'Z'+1: current_edit=' '; break; case ' '+1: current_edit = '0'; break; case '9'+1: current_edit ='A'; break; case 'A'-1: current_edit ='9'; break; default: break; } dis->displayString[edit->current_edit]=(char)current_edit; edit->setData(dis->displayString); } else if(edit->inputData.type==field_int32){ if(!dis->displayString){ // If empty widgetDisable(w); // This shouldn't happen. Disable widget to avoid possible errors. return -1; } val_ui = *(int32_t*)dis->getData(); if( (inc>0) && ((val_ui+inc) < val_ui) ){ // Check for overflow val_ui = edit->max_value; } else if( (inc<0) && ((val_ui+inc) > val_ui) ){ // Check for underflow val_ui = edit->min_value; } else{ val_ui += inc; if(val_ui < edit->min_value) { // Check limits val_ui = edit->min_value; } if(val_ui > edit->max_value) { val_ui= edit->max_value; } } edit->setData(&val_ui); } return -1; } else if( ((w->type == widget_multi_option) && (sel->state == widget_edit)) || ((w->type == widget_combo)&&combo->currentItem->type==combo_MultiOption)) { if(w->refresh==refresh_idle){ w->refresh=refresh_triggered; } int temp = *(uint8_t*)dis->getData(); if(input == Rotate_Increment) ++temp; else if(input == Rotate_Decrement) --temp; if(temp < 0) temp = edit->numberOfOptions - 1; else if(temp > edit->numberOfOptions -1) temp = 0; edit->setData(&temp); } else if (sel->state == widget_selected) { uint8_t next = 0xFF; int previous = -1; widget_t *next_w = NULL; widget_t *previous_w = NULL; widget_t *first_w = w; widget_t *last_w = w; widget_t *scan = w->parent->widgets; while(scan) { selectable_widget_t *e = extractSelectablePartFromWidget(scan); if(e) { if((e->tab > sel->tab) && (e->tab < next) && scan->enabled) { next = e->tab; next_w =scan; } if((e->tab < sel->tab) && (e->tab > previous) && scan->enabled) { previous = e->tab; previous_w = scan; } if((e->tab < extractSelectablePartFromWidget(first_w)->tab) && scan->enabled) first_w = scan; if((e->tab > extractSelectablePartFromWidget(last_w)->tab) && scan->enabled) last_w = scan; } scan = scan->next_widget; } if(next_w == NULL) next_w = first_w; if(previous_w == NULL) previous_w = last_w; if((input == Rotate_Increment) && next_w && (next_w != w)) { sel->state = widget_idle; sel->previous_state = widget_selected; w->parent->current_widget = next_w; extractSelectablePartFromWidget(next_w)->previous_state = extractSelectablePartFromWidget(next_w)->state; extractSelectablePartFromWidget(next_w)->state = widget_selected; return -1; } else if((input == Rotate_Decrement) && previous_w && (previous_w != w)) { sel->state = widget_idle; sel->previous_state = widget_selected; w->parent->current_widget = previous_w; extractSelectablePartFromWidget(previous_w)->previous_state = extractSelectablePartFromWidget(previous_w)->state; extractSelectablePartFromWidget(previous_w)->state = widget_selected; return -1; } } return -2; } void comboAddScreen(comboBox_item_t* item,widget_t *combo, char *label, uint8_t actionScreen){ if(!item || !combo || !label){ return; } comboBox_widget_t* comboW = (comboBox_widget_t*)combo->content; if(combo->type==widget_combo){ item->text = label; item->next_item = NULL; item->action_screen = actionScreen; item->type = combo_Screen; item->enabled = 1; item->dispAlign= align_center; comboBox_item_t *next = comboW->first; if(!next) { comboW->first = item; comboW->currentItem = item; return; } while(next->next_item){ next = next->next_item; } next->next_item = item; } } // Only allows Editable or multioption widgets void comboAddEditable(comboBox_item_t* item, widget_t *combo, char *label, editable_widget_t *editable){ if(!item || !combo || !label || !editable ){ return; } comboBox_widget_t* comboW = (comboBox_widget_t*)combo->content; item->text = label; item->next_item = NULL; item->widget = editable; item->type = combo_Editable; item->enabled = 1; comboBox_item_t *next = comboW->first; if(!next) { comboW->first = item; comboW->currentItem = item; return; } while(next->next_item){ next = next->next_item; } next->next_item = item; } // Only allows Editable or multioption widgets void comboAddMultiOption(comboBox_item_t* item, widget_t *combo, char *label, editable_widget_t *editable){ if(!item || !combo || !label || !editable ){ return; } comboBox_widget_t* comboW = (comboBox_widget_t*)combo->content; item->text = label; item->next_item = NULL; item->widget = editable; item->type = combo_MultiOption; item->enabled = 1; comboBox_item_t *next = comboW->first; if(!next) { comboW->first = item; comboW->currentItem = item; return; } while(next->next_item){ next = next->next_item; } next->next_item = item; } void comboAddAction(comboBox_item_t* item, widget_t *combo, char *label, int (*action)()){ if(!item || !combo || !label || !action){ return; } comboBox_widget_t* comboW = (comboBox_widget_t*)combo->content; if(action){ // If not null item->text = label; item->next_item = NULL; item->action = action; item->type = combo_Action; item->enabled = 1; item->dispAlign= align_center; comboBox_item_t *next = comboW->first; if(!next) { comboW->first = item; comboW->currentItem = item; return; } while(next->next_item){ next = next->next_item; } next->next_item = item; } } void comboResetIndex(widget_t *combo){ comboBox_widget_t* comboW = (comboBox_widget_t*)combo->content; if((comboW->currentItem->type==combo_Editable)||(comboW->currentItem->type==combo_MultiOption)){ selectable_widget_t* sel = &comboW->currentItem->widget->selectable; if(sel){ sel->state = widget_idle; sel->previous_state = widget_idle; } } comboW->currentItem = comboW->first; comboW->currentScroll=0; }