#include #include "config.h" #include "config_pins.h" #include "lcd.h" #include "states.h" State::State(State *_parent) : parent(_parent), child(NULL), title("no title") { if (_parent != NULL) { _parent->setChild(this); } } // -------------------------------------- StateText::StateText(State *_parent) : State(_parent) { heading = "no heading"; text = "text missing"; onEnterFunc = []() { }; whenInFunc = [](StateMachineInput smi) { State *s = states_get(); if (smi.click && (s != NULL)) { if (s->getChild() != NULL) { states_go_to(s->getChild()); } else if (s->getParent() != NULL) { states_go_to(s->getParent()); } } }; } void StateText::setHeading(const char *_heading) { heading = _heading; } void StateText::setText(const char *_text) { text = _text; } void StateText::onEnter(EnterFuncPtr func) { onEnterFunc = func; } void StateText::whenIn(InFuncPtr func) { whenInFunc = func; } void StateText::enterState(void) { if (onEnterFunc != NULL) { onEnterFunc(); } lcd_clear(); lcd_set_heading(heading); lcd_set_text(text); } void StateText::inState(struct StateMachineInput smi) { if (whenInFunc != NULL) { whenInFunc(smi); } } // -------------------------------------- StateMenu::StateMenu(State *_parent, bool _show_parent) : State(_parent) { show_parent = _show_parent; menuPos = 0; menuOff = 0; } void StateMenu::setChild(State *_child) { children.push_back(_child); } void StateMenu::addChild(State *_child, int pos) { if (pos < 0) { setChild(_child); } else { array_insert_at_pos(&children, _child, pos); } } void StateMenu::enterState(void) { display(); } void StateMenu::display(void) { lcd_clear(); lcd_set_heading(getTitle()); int size = children.size(); if (show_parent) { size++; } for (int i = menuOff; (i < menuOff + lcd_text_lines()) && (i < size); i++) { String s = ""; if (i == menuPos) { s += "> "; } else { s += " "; } if (i == children.size()) { s += getParent()->getTitle(); } else { s += children.at(i)->getTitle(); } lcd_set_menu_text(i - menuOff, s.c_str()); } } void StateMenu::inState(struct StateMachineInput smi) { int size = children.size(); if (show_parent) { size++; } if (smi.encoder != 0) { menuPos -= smi.encoder; while (menuPos < 0) { menuPos += size; } while (menuPos >= size) { menuPos -= size; } while (menuPos < menuOff) { menuOff--; } while (menuPos >= (menuOff + lcd_text_lines())) { menuOff++; } display(); } if (smi.click) { if (menuPos == children.size()) { menuPos = 0; menuOff = 0; states_go_to(getParent()); } else { states_go_to(children.at(menuPos)); } } } // -------------------------------------- StateDynamicMenu::StateDynamicMenu(State *_parent) : State(_parent) { menuPos = 0; menuOff = 0; } void StateDynamicMenu::dataCount(CountFuncPtr count) { countFunc = count; } void StateDynamicMenu::dataGet(GetFuncPtr get) { getFunc = get; } void StateDynamicMenu::dataCall(CallFuncPtr call) { callFunc = call; } void StateDynamicMenu::display(void) { lcd_clear(); lcd_set_heading(getTitle()); for (int i = menuOff; (i < menuOff + lcd_text_lines()) && (i < count + 1); i++) { String s = ""; if (i == menuPos) { s += "> "; } else { s += " "; } if (i == count) { s += getParent()->getTitle(); } else { s += contents.at(i); } lcd_set_menu_text(i - menuOff, s.c_str()); } } void StateDynamicMenu::enterState(void) { // cache all entries on entering state if (countFunc != NULL) { count = countFunc(); } else { count = 0; } contents.clear(); for (int i = 0; i < count; i++) { if (getFunc != NULL) { contents.push_back(getFunc(i)); } else { contents.push_back("no get func"); } } display(); } void StateDynamicMenu::inState(StateMachineInput smi) { if (smi.encoder != 0) { menuPos -= smi.encoder; while (menuPos < 0) { menuPos += count + 1; } while (menuPos >= count + 1) { menuPos -= count + 1; } while (menuPos < menuOff) { menuOff--; } while (menuPos >= (menuOff + lcd_text_lines())) { menuOff++; } display(); } if (smi.click) { if (menuPos == count) { menuPos = 0; menuOff = 0; states_go_to(getParent()); } else { if (callFunc != NULL) { callFunc(menuPos); } } } } // -------------------------------------- template StateValue::StateValue(State *_parent, T &_value, T _min, T _max) : State(_parent), value(_value) { min = _min; max = _max; updateFunc = NULL; } template void StateValue::onUpdate(UpdateFuncPtr func) { updateFunc = func; } template void StateValue::display(void) { String s = String(min) + " .. " + String(value) + " .. " + String(max); lcd_set_text(s.c_str()); } template void StateValue::enterState(void) { lcd_clear(); lcd_set_heading(getTitle()); display(); } template void StateValue::inState(StateMachineInput smi) { if (smi.encoder != 0) { value -= smi.encoder; if (value < min) { value = min; } if (value > max) { value = max; } display(); } if (smi.click) { if (updateFunc != NULL) { updateFunc(value); } states_go_to(getParent()); } } template class StateValue; // -------------------------------------- template void array_print(Array *arr) { Serial.print("Array length: "); Serial.print(arr->size()); Serial.println(" contents:"); for (int i = 0; i < arr->size(); i++) { Serial.print(i); Serial.print(": "); Serial.println(arr->at(i)->getTitle()); } } template void array_insert_at_pos(Array *arr, T value, size_t pos) { // make additional space arr->push_back(value); if ((pos >= arr->max_size()) || (pos >= arr->size())) { // we can not shift it to the given position return; } for (int i = arr->size() - 2; i >= pos; i--) { arr->at(i + 1) = arr->at(i); } arr->at(pos) = value; }