#include "email_address_page.h" #include "app_state.h" #include "db/db_keys.h" #include "provisioning.h" #include "validation.h" #include "logical_size.h" #include namespace { constexpr size_t MAX_EMAIL_LENGTH = 254; } // anonymous namespace void EmailAddressPage::onPush(AppState& state) { state_ = &state; TTF_Init(); createFonts(); storedValue_ = state.db.getKV(radiantwave::db::KV_EMAIL_ADDRESS); input_ = storedValue_; statusMessage_ = "Enter or edit the email address, then press ENTER"; if (state.window) { SDL_StartTextInput(state.window); } } void EmailAddressPage::onResume(AppState& state) { state_ = &state; createFonts(); storedValue_ = state.db.getKV(radiantwave::db::KV_EMAIL_ADDRESS); input_ = storedValue_; if (state.window) { SDL_StartTextInput(state.window); } } void EmailAddressPage::onPause(AppState& state) { state.keybinds.unbindAll(); if (state.window) { SDL_StopTextInput(state.window); } destroyFonts(); destroyTextures(); } void EmailAddressPage::onPop(AppState& state) { if (state.window) { SDL_StopTextInput(state.window); } destroyFonts(); destroyTextures(); TTF_Quit(); } void EmailAddressPage::createFonts() { destroyFonts(); fontTitle_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 52); fontHeader_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 28); fontBody_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 32); fontSmall_ = TTF_OpenFont("assets/fonts/Roboto-Regular.ttf", 22); } void EmailAddressPage::destroyFonts() { if (fontTitle_) { TTF_CloseFont(fontTitle_); fontTitle_ = nullptr; } if (fontHeader_) { TTF_CloseFont(fontHeader_); fontHeader_ = nullptr; } if (fontBody_) { TTF_CloseFont(fontBody_); fontBody_ = nullptr; } if (fontSmall_) { TTF_CloseFont(fontSmall_); fontSmall_ = nullptr; } } void EmailAddressPage::destroyTextures() { titleText_.destroy(); storedText_.destroy(); valueText_.destroy(); statusText_.destroy(); hintText_.destroy(); } void EmailAddressPage::submit() { if (input_.empty()) { if (state_->db.deleteKV(radiantwave::db::KV_EMAIL_ADDRESS)) { storedValue_.clear(); statusMessage_ = "Cleared"; returnTimer_ = 1.0f; // The credential pair changed: re-validate in the background. state_->licenseTracker.refresh(); } else { statusMessage_ = "Failed to clear email address"; } return; } if (!radiantwave::validation::isValidEmail(input_)) { statusMessage_ = "Invalid email address"; return; } if (state_->db.setKV(radiantwave::db::KV_EMAIL_ADDRESS, input_)) { storedValue_ = input_; statusMessage_ = "Saved"; returnTimer_ = 1.0f; // The credential pair changed: re-validate in the background. state_->licenseTracker.refresh(); } else { statusMessage_ = "Failed to save email address"; } } void EmailAddressPage::handleEvent(const SDL_Event& event) { if (!state_) return; if (event.type == SDL_EVENT_TEXT_INPUT) { if (input_.size() < MAX_EMAIL_LENGTH) { input_ += event.text.text; } return; } if (event.type == SDL_EVENT_KEY_DOWN) { SDL_Keycode key = event.key.key; if (key == SDLK_BACKSPACE) { if (!input_.empty()) { input_.pop_back(); } } else if (key == SDLK_RETURN || key == SDLK_KP_ENTER) { submit(); } else if (key == SDLK_ESCAPE) { exitPage(*state_); } } } void EmailAddressPage::update(float dt) { if (returnTimer_ > 0.0f) { returnTimer_ -= dt; if (returnTimer_ <= 0.0f) { returnTimer_ = -1.0f; if (state_) { exitPage(*state_); } } } } // In the First-Run walkthrough a finished step moves on to the next missing // prerequisite; otherwise the page returns to whatever pushed it (Settings). void EmailAddressPage::exitPage(AppState& state) { if (state.provisioningMode) { radiantwave::provisioning::advance(state); } else { state.pages.pop(state); } } void EmailAddressPage::render(SDL_Renderer* renderer) { int winW, winH; radiantwave::getLayoutSize(renderer, &winW, &winH); float fw = static_cast(winW); float fh = static_cast(winH); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, 20, 20, 30, 255); SDL_FRect bgRect{0, 0, fw, fh}; SDL_RenderFillRect(renderer, &bgRect); if (!fontTitle_ || !fontHeader_ || !fontBody_ || !fontSmall_) return; SDL_Color white = {255, 255, 255, 255}; SDL_Color yellow = {255, 255, 0, 255}; SDL_Color cyan = {100, 220, 255, 255}; SDL_Color gray = {180, 180, 180, 255}; float currentY = 30.0f; // Input box (centered on screen); messages align to its left edge float boxW = 700.0f; float boxH = 70.0f; float boxX = (fw - boxW) / 2.0f; titleText_.ensure(renderer, fontTitle_, "Email Address", white); if (titleText_.texture) { float x = (fw - titleText_.width) / 2.0f; SDL_FRect dst{x, currentY, titleText_.width, titleText_.height}; SDL_RenderTexture(renderer, titleText_.texture, nullptr, &dst); currentY += titleText_.height + 30.0f; } std::string storedStr = storedValue_.empty() ? "Not set" : storedValue_; storedText_.ensure(renderer, fontHeader_, "Saved: " + storedStr, storedValue_.empty() ? gray : yellow); if (storedText_.texture) { SDL_FRect dst{boxX, currentY, storedText_.width, storedText_.height}; SDL_RenderTexture(renderer, storedText_.texture, nullptr, &dst); } currentY += 50.0f; SDL_SetRenderDrawColor(renderer, 35, 40, 60, 255); SDL_FRect boxRect{boxX, currentY, boxW, boxH}; SDL_RenderFillRect(renderer, &boxRect); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderRect(renderer, &boxRect); std::string display = input_ + "_"; valueText_.ensure(renderer, fontBody_, display, cyan); if (valueText_.texture) { SDL_FRect dst{boxX + 20.0f, currentY + (boxH - valueText_.height) / 2.0f, valueText_.width, valueText_.height}; SDL_RenderTexture(renderer, valueText_.texture, nullptr, &dst); } currentY += boxH + 30.0f; // Status message (left-aligned with input box) if (!statusMessage_.empty()) { statusText_.ensure(renderer, fontHeader_, statusMessage_, gray); if (statusText_.texture) { SDL_FRect dst{boxX, currentY, statusText_.width, statusText_.height}; SDL_RenderTexture(renderer, statusText_.texture, nullptr, &dst); } currentY += 50.0f; } hintText_.ensure(renderer, fontSmall_, "[Type] Enter email [ENTER] Save [BACKSPACE] Delete [ESC] Back", gray); if (hintText_.texture) { float x = (fw - hintText_.width) / 2.0f; SDL_FRect dst{x, fh - 40.0f, hintText_.width, hintText_.height}; SDL_RenderTexture(renderer, hintText_.texture, nullptr, &dst); } }