#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::updateText(void) { lcd_clear(); if (heading != NULL) { lcd_set_heading(heading); } else if (getTitle() != NULL) { lcd_set_heading(getTitle()); } if (text != NULL) { lcd_set_text(text); } } void StateText::enterState(void) { if (onEnterFunc != NULL) { onEnterFunc(); } updateText(); } 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 = F("> "); } else { s = F(" "); } 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; prefix = ""; countFunc = NULL; getFunc = NULL; callFunc = NULL; } 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 = F("> "); } else { s = F(" "); } if (i == count) { s += getParent()->getTitle(); } else { s += contents.at(i); } lcd_set_menu_text(i - menuOff, s.c_str()); } } void StateDynamicMenu::setPrefix(String pre) { prefix = pre; } 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(prefix + String(getFunc(i))); } else { contents.push_back(prefix + String(i + 1)); } } 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; heading = NULL; text = NULL; onEnterFunc = NULL; updateFunc = NULL; updateLiveFunc = NULL; } template void StateValue::setHeading(const char *_heading) { heading = _heading; } template void StateValue::setText(const char *_text) { text = _text; } template void StateValue::onEnter(EnterFuncPtr func) { onEnterFunc = func; } template void StateValue::onUpdate(UpdateFuncPtr func) { updateFunc = func; } template void StateValue::onLiveUpdate(UpdateFuncPtr func) { updateLiveFunc = func; } template void StateValue::display(void) { lcd_clear(); if (heading == NULL) { lcd_set_heading(getTitle()); } else { lcd_set_heading(heading); } String s = String(min) + F(" .. ") + String(value) + F(" .. ") + String(max); if (text != NULL) { s = text + String(F("\n")) + s; } lcd_set_text(s.c_str()); } template void StateValue::enterState(void) { if (onEnterFunc != NULL) { onEnterFunc(); } display(); } template void StateValue::inState(StateMachineInput smi) { if (smi.encoder != 0) { float vf = smi.encoder; vf *= 1.0 + ((float)smi.rpm / ENCODER_RPM_VALUE_FACTOR); int v = vf; value -= v; if (value < min) { value = min; } if (value > max) { value = max; } if (updateLiveFunc != NULL) { updateLiveFunc(value); } display(); } if (smi.click) { if (updateFunc != NULL) { updateFunc(value); } states_go_to(getParent()); } } template class StateValue; template class StateValue; // -------------------------------------- template StateValues::StateValues(State *_parent) : State(_parent) { heading = NULL; onEnterFunc = NULL; updateFunc = NULL; updateLiveFunc = NULL; pos = 0; editing = false; } template void StateValues::setData(size_t index, const char *name, T *value, T min, T max) { if (index >= N) { return; } values[index] = value; mins[index] = min; maxs[index] = max; texts[index] = name; } template void StateValues::setHeading(const char *_heading) { heading = _heading; } template void StateValues::onEnter(EnterFuncPtr func) { onEnterFunc = func; } template void StateValues::onUpdate(UpdateFuncPtr func) { updateFunc = func; } template void StateValues::onLiveUpdate(UpdateFuncPtr func) { updateLiveFunc = func; } template void StateValues::display(void) { lcd_clear(); if (heading == NULL) { lcd_set_heading(getTitle()); } else { lcd_set_heading(heading); } for (size_t i = 0; i < (N + 1); i++) { String s; if (i == pos) { if (editing) { s = F("# "); } else { s = F("> "); } } else { s = F(" "); } if (i < N) { s += texts[i] + String(*(values[i])) + F(" (") + String(mins[i]) + F("/") + String(maxs[i]) + F(")"); } else { if (getChild() != NULL) { s += F("Continue"); } else { s += F("Done"); } } lcd_set_menu_text(i, s.c_str()); } } template void StateValues::enterState(void) { pos = 0; if (onEnterFunc != NULL) { onEnterFunc(); } display(); } template void StateValues::inState(StateMachineInput smi) { if (editing) { if (smi.encoder != 0) { float vf = smi.encoder; vf *= 1.0 + ((float)smi.rpm / ENCODER_RPM_VALUE_FACTOR); int v = vf; *(values[pos]) -= v; if (*(values[pos]) < mins[pos]) { *(values[pos]) = mins[pos]; } if (*(values[pos]) > maxs[pos]) { *(values[pos]) = maxs[pos]; } if (updateLiveFunc != NULL) { updateLiveFunc(pos, *(values[pos])); } display(); } if (smi.click) { editing = false; display(); } } else { if (smi.encoder != 0) { int tmp = pos; tmp -= smi.encoder; while (tmp < 0) { tmp += N + 1; } while (tmp >= (N + 1)) { tmp -= N + 1; } pos = tmp; display(); } if (smi.click) { if (pos < N) { editing = true; display(); } else { if (updateFunc != NULL) { for (size_t i = 0; i < N; i++) { updateFunc(i, *(values[i])); } } if (getChild() != NULL) { states_go_to(getChild()); } else if (getParent() != NULL) { states_go_to(getParent()); } } } } } template class StateValues; template class StateValues; // -------------------------------------- template void array_print(Array *arr) { Serial.print(F("Array length: ")); Serial.print(arr->size()); Serial.println(F(" contents:")); for (int i = 0; i < arr->size(); i++) { Serial.print(i); Serial.print(F(": ")); 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; }