|
| 1 | +#include "displayapp/screens/Dice.h" |
| 2 | +#include "displayapp/screens/Screen.h" |
| 3 | +#include "displayapp/screens/Symbols.h" |
| 4 | +#include "components/settings/Settings.h" |
| 5 | +#include "components/motor/MotorController.h" |
| 6 | +#include "components/motion/MotionController.h" |
| 7 | + |
| 8 | +using namespace Pinetime::Applications::Screens; |
| 9 | + |
| 10 | +namespace { |
| 11 | + lv_obj_t* MakeLabel(lv_font_t* font, |
| 12 | + lv_color_t color, |
| 13 | + lv_label_long_mode_t longMode, |
| 14 | + uint8_t width, |
| 15 | + lv_label_align_t labelAlignment, |
| 16 | + const char* text, |
| 17 | + lv_obj_t* reference, |
| 18 | + lv_align_t alignment, |
| 19 | + int8_t x, |
| 20 | + int8_t y) { |
| 21 | + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); |
| 22 | + lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); |
| 23 | + lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color); |
| 24 | + lv_label_set_long_mode(label, longMode); |
| 25 | + if (width != 0) { |
| 26 | + lv_obj_set_width(label, width); |
| 27 | + } |
| 28 | + lv_label_set_align(label, labelAlignment); |
| 29 | + lv_label_set_text(label, text); |
| 30 | + lv_obj_align(label, reference, alignment, x, y); |
| 31 | + return label; |
| 32 | + } |
| 33 | + |
| 34 | + void btnRollEventHandler(lv_obj_t* obj, lv_event_t event) { |
| 35 | + auto* screen = static_cast<Dice*>(obj->user_data); |
| 36 | + if (event == LV_EVENT_CLICKED) { |
| 37 | + screen->Roll(); |
| 38 | + } |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +Dice::Dice(Controllers::MotionController& motionController, |
| 43 | + Controllers::MotorController& motorController, |
| 44 | + Controllers::Settings& settingsController) |
| 45 | + : motorController {motorController}, motionController {motionController}, settingsController {settingsController} { |
| 46 | + std::seed_seq sseq {static_cast<uint32_t>(xTaskGetTickCount()), |
| 47 | + static_cast<uint32_t>(motionController.X()), |
| 48 | + static_cast<uint32_t>(motionController.Y()), |
| 49 | + static_cast<uint32_t>(motionController.Z())}; |
| 50 | + gen.seed(sseq); |
| 51 | + |
| 52 | + lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20, |
| 53 | + LV_COLOR_WHITE, |
| 54 | + LV_LABEL_LONG_EXPAND, |
| 55 | + 0, |
| 56 | + LV_LABEL_ALIGN_CENTER, |
| 57 | + "count", |
| 58 | + lv_scr_act(), |
| 59 | + LV_ALIGN_IN_TOP_LEFT, |
| 60 | + 0, |
| 61 | + 0); |
| 62 | + |
| 63 | + lv_obj_t* dCounterLabel = MakeLabel(&jetbrains_mono_bold_20, |
| 64 | + LV_COLOR_WHITE, |
| 65 | + LV_LABEL_LONG_EXPAND, |
| 66 | + 0, |
| 67 | + LV_LABEL_ALIGN_CENTER, |
| 68 | + "sides", |
| 69 | + nCounterLabel, |
| 70 | + LV_ALIGN_OUT_RIGHT_MID, |
| 71 | + 20, |
| 72 | + 0); |
| 73 | + |
| 74 | + nCounter.Create(); |
| 75 | + lv_obj_align(nCounter.GetObject(), nCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); |
| 76 | + nCounter.SetValue(1); |
| 77 | + |
| 78 | + dCounter.Create(); |
| 79 | + lv_obj_align(dCounter.GetObject(), dCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); |
| 80 | + dCounter.SetValue(6); |
| 81 | + |
| 82 | + std::uniform_int_distribution<> distrib(0, resultColors.size() - 1); |
| 83 | + currentColorIndex = distrib(gen); |
| 84 | + |
| 85 | + resultTotalLabel = MakeLabel(&jetbrains_mono_42, |
| 86 | + resultColors[currentColorIndex], |
| 87 | + LV_LABEL_LONG_BREAK, |
| 88 | + 120, |
| 89 | + LV_LABEL_ALIGN_CENTER, |
| 90 | + "", |
| 91 | + lv_scr_act(), |
| 92 | + LV_ALIGN_IN_TOP_RIGHT, |
| 93 | + 11, |
| 94 | + 38); |
| 95 | + resultIndividualLabel = MakeLabel(&jetbrains_mono_bold_20, |
| 96 | + resultColors[currentColorIndex], |
| 97 | + LV_LABEL_LONG_BREAK, |
| 98 | + 90, |
| 99 | + LV_LABEL_ALIGN_CENTER, |
| 100 | + "", |
| 101 | + resultTotalLabel, |
| 102 | + LV_ALIGN_OUT_BOTTOM_MID, |
| 103 | + 0, |
| 104 | + 10); |
| 105 | + |
| 106 | + Roll(); |
| 107 | + openingRoll = false; |
| 108 | + |
| 109 | + btnRoll = lv_btn_create(lv_scr_act(), nullptr); |
| 110 | + btnRoll->user_data = this; |
| 111 | + lv_obj_set_event_cb(btnRoll, btnRollEventHandler); |
| 112 | + lv_obj_set_size(btnRoll, 240, 50); |
| 113 | + lv_obj_align(btnRoll, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); |
| 114 | + |
| 115 | + btnRollLabel = MakeLabel(&jetbrains_mono_bold_20, |
| 116 | + LV_COLOR_WHITE, |
| 117 | + LV_LABEL_LONG_EXPAND, |
| 118 | + 0, |
| 119 | + LV_LABEL_ALIGN_CENTER, |
| 120 | + Symbols::dice, |
| 121 | + btnRoll, |
| 122 | + LV_ALIGN_CENTER, |
| 123 | + 0, |
| 124 | + 0); |
| 125 | + |
| 126 | + // Spagetti code in motion controller: it only updates the shake speed when shake to wake is on... |
| 127 | + enableShakeForDice = !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake); |
| 128 | + if (enableShakeForDice) { |
| 129 | + settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, true); |
| 130 | + } |
| 131 | + refreshTask = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); |
| 132 | +} |
| 133 | + |
| 134 | +Dice::~Dice() { |
| 135 | + // reset the shake to wake mode. |
| 136 | + if (enableShakeForDice) { |
| 137 | + settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, false); |
| 138 | + enableShakeForDice = false; |
| 139 | + } |
| 140 | + lv_task_del(refreshTask); |
| 141 | + lv_obj_clean(lv_scr_act()); |
| 142 | +} |
| 143 | + |
| 144 | +void Dice::Refresh() { |
| 145 | + // we only reset the hysteresis when at rest |
| 146 | + if (motionController.CurrentShakeSpeed() >= settingsController.GetShakeThreshold()) { |
| 147 | + if (currentRollHysteresis <= 0) { |
| 148 | + // this timestamp is used for the screen timeout |
| 149 | + lv_disp_get_next(NULL)->last_activity_time = lv_tick_get(); |
| 150 | + |
| 151 | + Roll(); |
| 152 | + } |
| 153 | + } else if (currentRollHysteresis > 0) |
| 154 | + --currentRollHysteresis; |
| 155 | +} |
| 156 | + |
| 157 | +void Dice::Roll() { |
| 158 | + uint8_t resultIndividual; |
| 159 | + uint16_t resultTotal = 0; |
| 160 | + std::uniform_int_distribution<> distrib(1, dCounter.GetValue()); |
| 161 | + |
| 162 | + lv_label_set_text(resultIndividualLabel, ""); |
| 163 | + |
| 164 | + if (nCounter.GetValue() == 1) { |
| 165 | + resultTotal = distrib(gen); |
| 166 | + if (dCounter.GetValue() == 2) { |
| 167 | + switch (resultTotal) { |
| 168 | + case 1: |
| 169 | + lv_label_set_text(resultIndividualLabel, "HEADS"); |
| 170 | + break; |
| 171 | + case 2: |
| 172 | + lv_label_set_text(resultIndividualLabel, "TAILS"); |
| 173 | + break; |
| 174 | + } |
| 175 | + } |
| 176 | + } else { |
| 177 | + for (uint8_t i = 0; i < nCounter.GetValue(); i++) { |
| 178 | + resultIndividual = distrib(gen); |
| 179 | + resultTotal += resultIndividual; |
| 180 | + lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str()); |
| 181 | + if (i < (nCounter.GetValue() - 1)) { |
| 182 | + lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, "+"); |
| 183 | + } |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + lv_label_set_text_fmt(resultTotalLabel, "%d", resultTotal); |
| 188 | + if (openingRoll == false) { |
| 189 | + motorController.RunForDuration(30); |
| 190 | + NextColor(); |
| 191 | + currentRollHysteresis = rollHysteresis; |
| 192 | + } |
| 193 | +} |
| 194 | + |
| 195 | +void Dice::NextColor() { |
| 196 | + currentColorIndex = (currentColorIndex + 1) % resultColors.size(); |
| 197 | + lv_obj_set_style_local_text_color(resultTotalLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, resultColors[currentColorIndex]); |
| 198 | + lv_obj_set_style_local_text_color(resultIndividualLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, resultColors[currentColorIndex]); |
| 199 | +} |
0 commit comments