/* * Copyright (C) 2019 Max Regan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include "Bsp/macros.h" #include "Application/SystemFonts.h" #include "Application/ScreenManager.h" #include "Application/Screens/Screen.h" struct MenuScreenItem { enum class Type { NONE, SCREEN, FUNCTION, BACK, }; MenuScreenItem() : m_text(nullptr) , m_type(Type::NONE) {} MenuScreenItem(const char *item, Screen &m_screen) : m_text(item) , m_screen(&m_screen) , m_type(Type::SCREEN) {} MenuScreenItem(const char *item, void (*function)()) : m_text(item) , m_function(function) , m_type(Type::FUNCTION) {} MenuScreenItem(const char *item) : m_text(item) , m_type(Type::BACK) {} const char *m_text; union { Screen *m_screen; void (* m_function)(); }; Type m_type; }; class MenuScreen : public Screen { public: static constexpr std::size_t MAX_ITEMS = 5; MenuScreen(BSP::DisplayDriver &display, ScreenManager &manager, const char *title, std::initializer_list items) : m_driver(display) , m_manager(manager) , m_title(title) , m_items() , m_num_items{0} , m_selected{0} { for (auto &item : items) { m_items[m_num_items++] = item; // Leave room for "back" if (m_num_items == MAX_ITEMS - 1) { break; } } new (&m_items[m_num_items++]) MenuScreenItem("Back"); } void render() { const struct font &font = default_font; m_driver.clear(); uint32_t x = 0; uint32_t y = 0; m_driver.string_at(&x, y, m_title, &font); y += font.height + 1; m_driver.draw_hline(0, y++, m_driver.get_width()); m_driver.draw_hline(0, y++, m_driver.get_width()); for (std::size_t i = 0; i < m_num_items; i++) { const char *spacer = " "; if (i == m_selected) { spacer = ">"; } x = 0; m_driver.string_at(&x, y, spacer, &font); m_driver.string_at(&x, y, m_items[i].m_text, &font); y += font.height; } m_driver.refresh(); } BSP::ReturnCode add_item(MenuScreenItem item) { if (m_num_items == MAX_ITEMS) { return BSP::ReturnCode::FAIL; } m_items[m_num_items++] = item; return BSP::ReturnCode::OK; } BSP::ReturnCode init() { return BSP::ReturnCode::OK; } BSP::Schedule::NextTime execute() override { return BSP::Schedule::NextTime::never(); } void enable() override { m_selected = 0; render(); } void disable() override { } void notify_up_button() override { if (m_selected == 0) { m_selected = m_num_items - 1; } else { m_selected--; } render(); } void notify_middle_button() override { MenuScreenItem &item = m_items[m_selected]; if (item.m_type == MenuScreenItem::Type::SCREEN) { m_manager.push_screen(*item.m_screen); } else if (item.m_type == MenuScreenItem::Type::FUNCTION) { item.m_function(); m_manager.pop_screen(); } else if (item.m_type == MenuScreenItem::Type::BACK) { m_manager.pop_screen(); } } void notify_down_button() override { if (m_selected == m_num_items - 1) { m_selected = 0; } else { m_selected++; } render(); } private: BSP::DisplayDriver &m_driver; ScreenManager &m_manager; const char *m_title; MenuScreenItem m_items[MAX_ITEMS]; std::size_t m_num_items; std::size_t m_selected; };