diff --git a/include/AMI.h b/include/AMI.h index d4db314..7d68f17 100644 --- a/include/AMI.h +++ b/include/AMI.h @@ -8,9 +8,6 @@ #include #include -constexpr const char* FT_LOGO_PATH = "resources/Fasttube_Logo-white.bmp"; -constexpr const char* AVENIR_FONT_PATH = "resources/Avenir-Book.ttf"; - class AMI final : public View { public: AMI(SDL_Renderer* renderer); @@ -24,4 +21,6 @@ private: ImageWidget ft_logo; TextWidget choose; + ListWidget missions_widget; + std::vector> missions; }; \ No newline at end of file diff --git a/include/widgets.h b/include/widgets.h index 8a29aa4..f5c9b59 100644 --- a/include/widgets.h +++ b/include/widgets.h @@ -5,6 +5,7 @@ #include #include +#include enum class Alignment { LEFT, RIGHT, CENTER }; struct PositionInfo { @@ -25,6 +26,10 @@ public: virtual void set_position(int x, int y); virtual void set_alignment(Alignment align); + int get_width(); + int get_height(); + const PositionInfo& get_position(); + virtual void draw() = 0; protected: @@ -68,4 +73,23 @@ protected: std::string text; SDL_Texture* generate_text(const std::string& text); +}; + +class ListWidget : public Widget { +public: + ListWidget(SDL_Renderer* renderer, int element_height, + Alignment element_alignment); + virtual ~ListWidget(); + + void add_element(Widget* element); + + virtual void draw() override; + +protected: + int element_height; + Alignment element_alignment; + + std::vector elements; + + void place_element(Widget* element, int index); }; \ No newline at end of file diff --git a/src/AMI.cpp b/src/AMI.cpp index 5f53be1..e979db0 100644 --- a/src/AMI.cpp +++ b/src/AMI.cpp @@ -2,18 +2,49 @@ #include "defines.h" #include "util.h" +#include "widgets.h" + +#include + +constexpr const char* FT_LOGO_PATH = "resources/Fasttube_Logo-white.bmp"; +constexpr const char* AVENIR_FONT_PATH = "resources/Avenir-Book.ttf"; +constexpr int AVENIR_PTS = 20; +constexpr int FT_LOGO_HEIGHT = 40; +constexpr int GAP = 5; +constexpr int CHOOSE_Y = FT_LOGO_HEIGHT + GAP; +constexpr int MISSION_HEIGHT = 30; + +constexpr std::array MISSIONS = {"ACCELERATION", "SKIDPAD", "AUTOCROSS", + "TRACKDRIVE", "EBS TEST", "INSPECTION", + "MANUAL DRIVING"}; AMI::AMI(SDL_Renderer* renderer) - : View{renderer}, avenir{util::load_font(AVENIR_FONT_PATH, 20)}, + : View{renderer}, avenir{util::load_font(AVENIR_FONT_PATH, AVENIR_PTS)}, ft_logo{renderer, FT_LOGO_PATH}, choose{renderer, avenir, - "Choose a mission:"} { - ft_logo.set_height(40); + "Choose a mission:"}, + missions_widget{renderer, MISSION_HEIGHT, Alignment::CENTER} { + ft_logo.set_height(FT_LOGO_HEIGHT); ft_logo.set_position(SCREEN_WIDTH / 2, 0); ft_logo.set_alignment(Alignment::CENTER); widgets.push_back(&ft_logo); - choose.set_position(SCREEN_WIDTH / 2, 45); + + choose.set_position(SCREEN_WIDTH / 2, CHOOSE_Y); choose.set_alignment(Alignment::CENTER); widgets.push_back(&choose); + + int choose_height = choose.get_height(); + int missions_y = CHOOSE_Y + choose_height + GAP; + int missions_height = SCREEN_HEIGHT - CHOOSE_Y - choose_height - 1; + missions_widget.set_position(0, missions_y); + missions_widget.set_width(SCREEN_WIDTH, false); + missions_widget.set_height(missions_height, false); + for (const auto mission : MISSIONS) { + missions.push_back(std::make_unique(renderer, avenir, mission)); + } + for (const auto& mission : missions) { + missions_widget.add_element(mission.get()); + } + widgets.push_back(&missions_widget); } void AMI::draw() { diff --git a/src/App.cpp b/src/App.cpp index fe34268..183bc59 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -2,6 +2,7 @@ #include "AMI.h" +#include #include #include diff --git a/src/widgets.cpp b/src/widgets.cpp index 330feb6..da7361e 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -1,7 +1,9 @@ #include "widgets.h" +#include "defines.h" #include "util.h" +#include #include #include @@ -40,6 +42,12 @@ void Widget::set_alignment(Alignment align) { recalculate_rect(); } +int Widget::get_width() { return rect.w; } + +int Widget::get_height() { return rect.h; } + +const PositionInfo& Widget::get_position() { return pos; } + void Widget::recalculate_rect() { int x = pos.x; int y = pos.y; @@ -129,4 +137,63 @@ SDL_Texture* TextWidget::generate_text(const std::string& text) { "Unable to create texture from text surface: {}", SDL_GetError())); } return texture; -} \ No newline at end of file +} + +ListWidget::ListWidget(SDL_Renderer* renderer, int element_height, + Alignment element_alignment) + : Widget{renderer}, element_height{element_height}, + element_alignment{element_alignment} {} + +ListWidget::~ListWidget() {} + +void ListWidget::add_element(Widget* element) { + int new_index = elements.size(); + elements.push_back(element); + place_element(element, new_index); +} + +void ListWidget::draw() { + int max_y = rect.y + rect.h; + for (size_t i = 0; i < elements.size(); i++) { + const auto& element = elements[i]; + const PositionInfo& element_pos = element->get_position(); + if (element_pos.y >= max_y) { + // Since the elements are sorted, we can stop the loop now. + break; + } + if (i % 2 == 1) { + SDL_SetRenderDrawColor(renderer, 0x11, 0x11, 0x11, 0xFF); + SDL_Rect fill_rect = { + .x = pos.x, .y = element_pos.y, .w = rect.w, .h = element_height}; + SDL_RenderFillRect(renderer, &fill_rect); + } + element->draw(); + if (i != elements.size() - 1) { + int border_y = element_pos.y + element->get_height(); + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_RenderDrawLine(renderer, rect.x, border_y, rect.x + rect.w, border_y); + } + } +} + +void ListWidget::place_element(Widget* element, int index) { + int x = rect.x; + switch (element_alignment) { + case Alignment::LEFT: + break; + case Alignment::RIGHT: + x += rect.w; + break; + case Alignment::CENTER: + x += rect.w / 2; + break; + default: + throw std::runtime_error( + fmt::format("Unknown alignment: {}", (int)pos.align)); + } + // Additional pixel for border + int y = rect.y + index * (element_height + 1); + element->set_position(x, y); + element->set_alignment(element_alignment); + element->set_height(element_height); +}