178 lines
6.7 KiB
C++
178 lines
6.7 KiB
C++
#include <array>
|
|
#include <bits/locale_classes.h>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
|
|
#include "Font5x7FixedMono.h"
|
|
#include "alarm_sound.h"
|
|
#include "control.h"
|
|
#include "display.h"
|
|
#include "gc9a01.h"
|
|
#include "macros.h"
|
|
#include "lwip/arch.h"
|
|
#include "multicore_events.h"
|
|
#include "pico/multicore.h"
|
|
#include "pico/time.h"
|
|
#include "sound.h"
|
|
#include "timezones.h"
|
|
|
|
|
|
bool secondary_button_override = false;
|
|
|
|
struct configuration {
|
|
char* text;
|
|
void (*set_text)(const configuration* self);
|
|
void (*update)(configuration* self,bool hard);
|
|
uint32_t local_conf; // can be used as diferent data types
|
|
uint8_t additional_data;
|
|
bool override_secondary_button;
|
|
};
|
|
|
|
void timezone_set_text(const configuration* self) {sprintf(self->text, "TIMEZONE: %s", timezones[self->local_conf].zone_name);}
|
|
void timezone_update(configuration* self,bool hard) {
|
|
uint16_t selected_option = get_knob_percentage();
|
|
self->additional_data %= std::size(timezones)/100;
|
|
selected_option+= self->additional_data*100;
|
|
self->local_conf = selected_option >= std::size(timezones) ? std::size(timezones)-1 : selected_option;
|
|
if (hard) {
|
|
timezone_index = self->local_conf;
|
|
// todo store to flash
|
|
}
|
|
}
|
|
|
|
void volume_set_text(const configuration* self) {sprintf(self->text, "VOLUME: %0.1f", *(float*)&self->local_conf);}
|
|
void volume_update(configuration* self,bool hard)
|
|
{
|
|
*(float*)&self->local_conf = static_cast<float>(get_knob_percentage())/10.0f;
|
|
if (hard) {
|
|
volume_multiplier = *(float*)&self->local_conf;
|
|
// todo store to flash
|
|
}
|
|
}
|
|
|
|
void config_page() {
|
|
char volume_str[17];
|
|
char timezone_str[41];
|
|
std::array<configuration, 2> options = {{{timezone_str,&timezone_set_text,&timezone_update,timezone_index, 0,true}, {volume_str,&volume_set_text,&volume_update,*(uint32_t*)&volume_multiplier, 0, false}}};
|
|
const GFXfont *font = &Font5x7FixedMono;
|
|
|
|
draw_ui_circle(RED);
|
|
multicore_fifo_drain();
|
|
uint8_t selected_option;
|
|
bool configuring = false;
|
|
while (true) {
|
|
for (uint8_t i = 0; i < ARRAY_LENGTH(options); i++) {
|
|
options[i].set_text(&options[i]);
|
|
}
|
|
|
|
if (!configuring)
|
|
{
|
|
selected_option = std::min(
|
|
static_cast<uint8_t>((get_knob_percentage() / 100.0f) * options.size()),
|
|
static_cast<uint8_t>(options.size() - 1));
|
|
}
|
|
|
|
|
|
// Find the actual maximum text width that will be rendered
|
|
uint16_t max_text_width_pixels = 0;
|
|
for (uint8_t i = 0; i < options.size(); i++) {
|
|
uint16_t text_width = strlen(options[i].text) * 5;
|
|
if (text_width > max_text_width_pixels) {
|
|
max_text_width_pixels = text_width;
|
|
}
|
|
}
|
|
|
|
// Add proper padding to ensure text doesn't touch cyan lines
|
|
const uint8_t horizontal_padding = 15; // Increased padding
|
|
const uint8_t half_boundary_width = (max_text_width_pixels + (2 * horizontal_padding)) / 2;
|
|
constexpr uint8_t center_pos = (MAX_X / 2);
|
|
|
|
// Calculate cyan line positions with proper spacing
|
|
uint16_t left_cyan_x = center_pos - half_boundary_width;
|
|
uint16_t right_cyan_x = center_pos + half_boundary_width;
|
|
|
|
// Get vertical boundaries
|
|
std::array<uint16_t, 2> circle_y_pos = get_ui_circle_vertical_pos(right_cyan_x);
|
|
uint16_t y_offset = circle_y_pos[0] + 2;
|
|
|
|
// right
|
|
gc9a01_vline(&display, right_cyan_x, y_offset,
|
|
(circle_y_pos[1] - 1) - y_offset, CYAN);
|
|
// left
|
|
gc9a01_vline(&display, left_cyan_x, y_offset,
|
|
(circle_y_pos[1] - 1) - y_offset, CYAN);
|
|
|
|
const uint8_t half_y_font = static_cast<uint8_t>(
|
|
roundf(static_cast<float>(7) / static_cast<float>(2)));
|
|
y_offset += 20;
|
|
|
|
// Render text with proper horizontal centering
|
|
for (uint8_t i = 0; i < options.size(); i++) {
|
|
uint16_t text_width_pixels = strlen(options[i].text) * 5;
|
|
|
|
// Center each text line horizontally within the cyan boundaries
|
|
uint16_t x_pos = (center_pos - (text_width_pixels / 2));
|
|
|
|
// Ensure text stays within cyan line boundaries
|
|
if (x_pos < left_cyan_x + horizontal_padding) {
|
|
x_pos = left_cyan_x + horizontal_padding;
|
|
} else if (x_pos + text_width_pixels > right_cyan_x - horizontal_padding) {
|
|
x_pos = right_cyan_x - horizontal_padding - text_width_pixels;
|
|
}
|
|
|
|
gc9a01_text_gfx_buffered(&display, font, options[i].text, x_pos-10 /*MAGIC IDK WHY*/, y_offset, i == selected_option ? configuring ? RED : GREEN : WHITE, BLACK);
|
|
y_offset += half_y_font;
|
|
|
|
// Draw separator line within cyan boundaries
|
|
gc9a01_hline(&display, left_cyan_x + 2, y_offset,
|
|
(right_cyan_x - left_cyan_x) - 4, WHITE);
|
|
y_offset += half_y_font * 2;
|
|
}
|
|
|
|
switch (static_cast<multicore_event_t>(multicore_fifo_pop_blocking())) {
|
|
case PRIMARY_BUTTON_PRESSED:
|
|
if (configuring) {
|
|
options[selected_option].update(&options[selected_option], true);
|
|
secondary_button_override = false;
|
|
} else {
|
|
if (options[selected_option].override_secondary_button) {
|
|
secondary_button_override = true;
|
|
}
|
|
gc9a01_fill_rect(&display,left_cyan_x,circle_y_pos[0]+1,right_cyan_x-left_cyan_x,(circle_y_pos[1]-circle_y_pos[0])-2,BLACK);
|
|
options[selected_option].update(&options[selected_option], false);
|
|
}
|
|
configuring = !configuring;
|
|
break;
|
|
case SECONDARY_BUTTON_PRESSED:
|
|
options[selected_option].additional_data++;
|
|
gc9a01_fill_rect(&display,left_cyan_x,circle_y_pos[0]+1,right_cyan_x-left_cyan_x,(circle_y_pos[1]-circle_y_pos[0])-2,BLACK);
|
|
options[selected_option].update(&options[selected_option], false);
|
|
break;
|
|
case KNOB_CHANGE:
|
|
if (configuring) {
|
|
gc9a01_fill_rect(&display,left_cyan_x,circle_y_pos[0]+1,right_cyan_x-left_cyan_x,(circle_y_pos[1]-circle_y_pos[0])-2,BLACK);
|
|
options[selected_option].update(&options[selected_option], false);
|
|
}
|
|
break;
|
|
}
|
|
multicore_fifo_drain(); // prevent event stucking
|
|
}
|
|
}
|
|
|
|
|
|
void time_page() {
|
|
draw_ui_circle(MAGENTA);
|
|
print_time(true);
|
|
while (true) {
|
|
multicore_fifo_drain();
|
|
sleep_ms(500);
|
|
print_time();
|
|
}
|
|
}
|
|
|
|
void alarm_set_page() { draw_ui_circle(BLUE); }
|
|
|
|
std::array<void (*)(), 3> page_functions = {config_page, time_page,
|
|
alarm_set_page};
|