#include "low_level_render.h" #include "low_level_gui.h" #include #include LowLevelWindow* validate_or_create_window(LowLevelWindow* window, LowLevelRenderer* renderer) { if (window == nullptr) { return new LowLevelWindow(0, 0, renderer->get_width(), renderer->get_height(), "Default Window"); } return window; } LowLevelGUI::LowLevelGUI(LowLevelRenderer *rend, const Font& font) : renderer(rend), current_font(&font) {} LowLevelWindow* LowLevelGUI::draw_new_window(int x, int y, int width, int height, const char *title) { LowLevelWindow* w = new LowLevelWindow(x, y, width, height, title); draw_window(w); return w; } void LowLevelGUI::draw_window(LowLevelWindow* window){ // Draw window border if (use_rounded_corners) { //shadow renderer->draw_rounded_rectangle(window->x+3, window->y+3, window->width, window->height, 10, true, true); renderer->draw_rounded_rectangle(window->x-2, window->y-2, window->width+2, window->height+2, 10, false, true); renderer->draw_rounded_rectangle(window->x, window->y, window->width, window->height, 10, true); } else { renderer->draw_filled_rectangle(window->x + 3, window->y + 3, window->width + 2, window->height + 2, true, 2); renderer->draw_filled_rectangle(window->x - 2, window->y - 2, window->width + 2, window->height + 2, false, 2); renderer->draw_rectangle(window->x, window->y, window->width, window->height, true, 2); } renderer->draw_line(window->x, window->y + 20, window->x + window->width - 1, window->y + 20, true, 1); // draw closing 'X' button int close_size = 12; int close_x = window->x + window->width - close_size - 4; int close_y = window->y + 4; //renderer->draw_rectangle(close_x, close_y, close_size, close_size, true, 1); renderer->draw_line(close_x + 3, close_y + 3, close_x + close_size - 4, close_y + close_size - 4, true, 1); renderer->draw_line(close_x + close_size - 4, close_y + 3, close_x + 3, close_y + close_size - 4, true, 1); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); renderer->draw_string_scaled(window->x + 10, window->y + 3, window->title, 2); renderer->set_font(&original_font); } void LowLevelGUI::draw_button(LowLevelWindow* window, int x, int y, const char *label, bool pressed, bool rounded) { window = validate_or_create_window(window, renderer); Font original_font = renderer->get_current_font(); bool original_text_color = renderer->get_current_text_color(); renderer->set_font(current_font); int text_x = window->x + x + 5; int text_y = window->y + y + 5; int height = renderer->get_current_font().get_char_height() * 2 + 10; int width = int(renderer->draw_string_scaled(text_x, text_y, label, 2) * 1) + 30; if (pressed) { renderer->draw_rounded_rectangle(window->x + x - 1, window->y + y -1, width + 2, height+ 2, rounded ? 5 : 0, false, true); renderer->draw_rounded_rectangle(window->x + x, window->y + y, width, height, rounded ? 5 : 0, true, true); renderer->draw_rounded_rectangle(window->x + x + 2, window->y + y + 2, width - 4, height - 4, rounded ? 5 : 0, false, false); } else { renderer->draw_rounded_rectangle(window->x + x - 1, window->y + y -1, width + 2, height+ 2, rounded ? 5 : 0, false, true); renderer->draw_rounded_rectangle(window->x + x, window->y + y, width, height, rounded ? 5 : 0, false, true); renderer->draw_rounded_rectangle(window->x + x + 2, window->y + y + 2, width - 4, height - 4, rounded ? 5 : 0, true, false); } renderer->set_text_color(!pressed); renderer->draw_string_scaled(text_x, text_y, label, 2); renderer->set_font(&original_font); renderer->set_text_color(original_text_color); } void LowLevelGUI::draw_checkbox(LowLevelWindow* window, int x, int y, const char *label, bool checked) { window = validate_or_create_window(window, renderer); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); int box_size = renderer->get_current_font().get_char_height() * 2 ; int box_x = window->x + x; int box_y = window->y + y; // Draw checkbox square renderer->draw_rectangle(box_x, box_y, box_size, box_size, true, 1); if (checked) { // Draw check mark renderer->draw_line(box_x + 2, box_y + box_size / 2, box_x + box_size / 2, box_y + box_size - 3, true, 1); renderer->draw_line(box_x + box_size / 2, box_y + box_size - 3, box_x + box_size - 2, box_y + 2, true, 1); } // Draw label renderer->set_text_color(true); renderer->draw_string_scaled(box_x + box_size + 5, box_y - 1, label, 2); renderer->set_font(&original_font); } void LowLevelGUI::draw_radio_button(LowLevelWindow* window, int x, int y, const char *label, bool selected) { window = validate_or_create_window(window, renderer); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); int radius = renderer->get_current_font().get_char_height(); int center_x = window->x + x + radius; int center_y = window->y + y + radius; // Draw outer circle renderer->draw_circle(center_x, center_y, radius, true); if (selected) { // Draw inner filled circle renderer->draw_filled_circle(center_x, center_y, radius - 4, true); } // Draw label renderer->set_text_color(true); renderer->draw_string_scaled(center_x + radius + 5, center_y - radius / 2, label, 2); renderer->set_font(&original_font); } void LowLevelGUI::draw_slider(LowLevelWindow* window, int x, int y, int width, int height, int position, char* label) { window = validate_or_create_window(window, renderer); int slider_x = window->x + x; int slider_y = window->y + y + (label != nullptr ? 20 : 0); position = std::max(0, std::min(100, position)); // Draw slider track renderer->draw_filled_rectangle(slider_x, slider_y + height / 2 - 2, width, 4, true, 1); // Draw slider handle, considering position to be within [0, 100] int handle_x = slider_x + (position * width / 100); renderer->draw_filled_rectangle(handle_x - 5, slider_y, 10, height, true, 1); renderer->draw_rectangle(handle_x - 6, slider_y - 1, 12, height + 2, true, 1); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); // draw current position value label on top of the slider char pos_label[10]; snprintf(pos_label, sizeof(pos_label), "%d", position); renderer->draw_string_scaled(slider_x + width + 10, slider_y + (height / 2) - 5, pos_label, 1); // Draw label if provided if (label != nullptr) { renderer->draw_string_scaled(slider_x, window->y + y, label, 2); } renderer->set_font(&original_font); } void LowLevelGUI::draw_calendar(LowLevelWindow* window, int x, int y, int month, int year) { window = validate_or_create_window(window, renderer); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); // 1. Draw Month and Year Header char title[32]; const char* month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; snprintf(title, sizeof(title), "%s %04d", month_names[month - 1], year); renderer->draw_string_scaled(window->x + x, window->y + y, title, 1); // 2. Draw Days of the Week labels const char* days[] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}; for (int i = 0; i < 7; i++) { renderer->draw_string_scaled(window->x + x + (i * 20), window->y + y + 15, days[i], 1); } // 3. Calculate Month Metadata // Get starting day of the week (0 = Sunday) using a simplified formula struct tm first_day = {0}; first_day.tm_mday = 1; first_day.tm_mon = month - 1; first_day.tm_year = year - 1900; mktime(&first_day); int start_col = first_day.tm_wday; // Get number of days in the month int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { days_in_month[1] = 29; } int total_days = days_in_month[month - 1]; // 4. Draw the Days Grid for (int day = 1; day <= total_days; day++) { int index = start_col + day - 1; int col = index % 7; int row = index / 7; int cell_x = window->x + x + (col * 20); int cell_y = window->y + y + 30 + (row * 20); // Draw cell border/background renderer->draw_rectangle(cell_x, cell_y, 20, 20, false, 1); // Draw day number char day_str[3]; snprintf(day_str, sizeof(day_str), "%d", day); // Center the text slightly within the 20x20 cell int offset_x = (day < 10) ? 7 : 2; renderer->draw_string_scaled(cell_x + offset_x, cell_y + 5, day_str, 1); } renderer->set_font(&original_font); } void LowLevelGUI::draw_textbox(LowLevelWindow* window, int x, int y, int width, int height, const char* content, bool focused) { window = validate_or_create_window(window, renderer); // Draw textbox border int box_x = window->x + x; int box_y = window->y + y; if(focused) { renderer->draw_filled_rectangle(box_x, box_y, width, height, true, 1); renderer->draw_rectangle(box_x - 1, box_y - 1, width + 2, height + 2, false, 1); } else { renderer->draw_filled_rectangle(box_x, box_y, width, height, false, 1); renderer->draw_rectangle(box_x - 1, box_y - 1, width + 2, height + 2, true, 1); } // Draw content text inside the textbox Font original_font = renderer->get_current_font(); renderer->set_font(current_font); renderer->set_text_color(true); int text_x = box_x + 5; int text_y = box_y + 5; renderer->draw_string_scaled(text_x, text_y, content, 1); renderer->set_font(&original_font); } void LowLevelGUI::draw_tab(LowLevelWindow* window, int x, int y, int width, int height, const char* label, bool selected) { window = validate_or_create_window(window, renderer); int tab_x = window->x + x; int tab_y = window->y + y; if (selected) { renderer->draw_filled_rectangle(tab_x, tab_y, width, height, true, 1); renderer->draw_rectangle(tab_x - 1, tab_y - 1, width + 2, height + 2, false, 1); } else { renderer->draw_filled_rectangle(tab_x, tab_y, width, height, false, 1); renderer->draw_rectangle(tab_x - 1, tab_y - 1, width + 2, height + 2, true, 1); } Font original_font = renderer->get_current_font(); renderer->set_font(current_font); renderer->set_text_color(true); // Center the label within the tab int text_width = int(renderer->draw_string_scaled(0, 0, label, 1) * 0.75); int text_x = tab_x + (width - text_width) / 2; int text_y = tab_y + (height - renderer->get_current_font().get_char_height()) / 2; renderer->draw_string_scaled(text_x, text_y, label, 1); renderer->set_font(&original_font); } void LowLevelGUI::draw_status_bar(LowLevelWindow* window, int x, int y, int width, const char* label, const char* sublabel, int percentage, const char* value_text) { window = validate_or_create_window(window, renderer); int base_x = window->x + x; int base_y = window->y + y; Font original_font = renderer->get_current_font(); renderer->set_font(current_font); // Draw Main Label (e.g., "PANELS") renderer->draw_string_scaled(base_x, base_y, label, 2); // Draw Sublabel and Value (e.g., "Weekly Average Charge" and "190KWH") renderer->draw_string_scaled(base_x, base_y + 15, sublabel, 1); int val_width = strlen(value_text) * 8; // Approximation renderer->draw_string_scaled(base_x + width - val_width, base_y + 15, value_text, 1); // Draw Bar Container (Rounded) int bar_y = base_y + 30; int bar_height = 12; renderer->draw_rounded_rectangle(base_x, bar_y, width, bar_height, 6, true); // Draw Progress Fill int fill_width = (percentage * width) / 100; if (fill_width > 4) { renderer->draw_rounded_rectangle(base_x + 2, bar_y + 2, fill_width - 4, bar_height - 4, 4, true, true); } renderer->set_font(&original_font); } void LowLevelGUI::draw_circular_gauge(LowLevelWindow* window, int x, int y, int width, const char* label, int percentage) { window = validate_or_create_window(window, renderer); int base_x = window->x + x; int base_y = window->y + y; int height = 50; // Draw pill-shaped container renderer->draw_rounded_rectangle(base_x, base_y, width, height, height/2, true); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); // Draw Label renderer->draw_string_scaled(base_x + 20, base_y + 18, label, 2); // Draw Circular Gauge on the right int centerX = base_x + width - 30; int centerY = base_y + 25; int radius = 18; // Draw background track (dimmed) renderer->draw_circle(centerX, centerY, radius, true); // Draw percentage text inside circle char buf[5]; snprintf(buf, sizeof(buf), "%d%%", percentage); renderer->draw_string_scaled(centerX - 10, centerY - 5, buf, 1); // Note: If your renderer supports arcs: // renderer->draw_arc(centerX, centerY, radius, 0, (percentage * 360) / 100); renderer->set_font(&original_font); } void LowLevelGUI::draw_notification(LowLevelWindow* window, int x, int y, int width, const char* time, const char* message) { // window = validate_or_create_window(window, renderer); int base_x = window->x + x; int base_y = window->y + y; // Draw dark background renderer->draw_rounded_rectangle(base_x, base_y, width, 100, 15, true, true); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); renderer->set_text_color(false); // Assume false is white/light on dark renderer->draw_string_scaled(base_x + 15, base_y + 10, time, 1); renderer->draw_string_scaled(base_x + width - 20, base_y + 10, "x", 1); // Simple word wrap or multi-line manual draw for the message // This is a simplified version renderer->draw_string_scaled(base_x + 15, base_y + 30, message, 2); renderer->set_text_color(true); renderer->set_font(&original_font); } void LowLevelGUI::draw_large_clock(LowLevelWindow* window, int x, int y, const char* time_str) { window = validate_or_create_window(window, renderer); Font original_font = renderer->get_current_font(); renderer->set_font(current_font); // Draw the time significantly larger (scale 5 or 6) renderer->draw_string_scaled(window->x + x, window->y + y, time_str, 6); renderer->set_font(&original_font); }