Compare commits

...

2 Commits

Author SHA1 Message Date
Jasper Blanckenburg 2e76523bf4 AMI -> MissionSelect 2022-05-22 14:30:42 +02:00
Jasper Blanckenburg 654c71f3ee Select a mission 2022-05-22 14:25:21 +02:00
9 changed files with 133 additions and 22 deletions

View File

@ -39,7 +39,7 @@ add_executable(
src/main.cpp src/main.cpp
src/App.cpp src/App.cpp
src/View.cpp src/View.cpp
src/AMI.cpp src/MissionSelect.cpp
src/widgets.cpp src/widgets.cpp
src/util.cpp src/util.cpp
) )

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include "AMI.h" #include "MissionSelect.h"
#include "defines.h" #include "defines.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <memory> #include <memory>
enum class AppView { AMI, DRIVER, TESTING }; enum class AppView { MISSION_SELECT, AMI, DRIVER, TESTING };
class SDLManager { class SDLManager {
public: public:
@ -33,7 +33,7 @@ private:
// others and its destructor is called after the others. // others and its destructor is called after the others.
SDLManager sdl_manager; SDLManager sdl_manager;
std::unique_ptr<AMI> ami; std::unique_ptr<MissionSelect> mission_select;
bool running; bool running;
AppView view; AppView view;

View File

@ -1,19 +1,22 @@
#pragma once #pragma once
#include "View.h" #include "View.h"
#include "events.h"
#include "widgets.h" #include "widgets.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <memory> #include <memory>
#include <queue>
#include <vector> #include <vector>
class AMI final : public View { class MissionSelect final : public View {
public: public:
AMI(SDL_Renderer* renderer); MissionSelect(SDL_Renderer* renderer);
~AMI(); ~MissionSelect();
void draw() override; void draw() override;
void handle_events(std::queue<Event>& events) override;
private: private:
TTF_Font* avenir; TTF_Font* avenir;

View File

@ -1,13 +1,18 @@
#pragma once #pragma once
#include "events.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <queue>
class View { class View {
public: public:
View(SDL_Renderer* renderer); View(SDL_Renderer* renderer);
virtual ~View(); virtual ~View();
virtual void draw() = 0; virtual void draw() = 0;
virtual void handle_events(std::queue<Event>& events) = 0;
protected: protected:
SDL_Renderer* renderer; SDL_Renderer* renderer;

3
include/events.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
enum class Event { Next, Prev, Confirm };

View File

@ -85,10 +85,16 @@ public:
virtual void draw() override; virtual void draw() override;
void select_next();
void select_prev();
size_t get_selection();
protected: protected:
int element_height; int element_height;
Alignment element_alignment; Alignment element_alignment;
size_t selection;
std::vector<Widget*> elements; std::vector<Widget*> elements;
void place_element(Widget* element, int index); void place_element(Widget* element, int index);

View File

@ -1,14 +1,18 @@
#include "App.h" #include "App.h"
#include "AMI.h" #include "MissionSelect.h"
#include "events.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_image.h> #include <SDL2/SDL_image.h>
#include <SDL2/SDL_keycode.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <queue>
#include <stdexcept> #include <stdexcept>
App::App() : view{AppView::AMI} { init_sdl(); } App::App() : view{AppView::MISSION_SELECT} { init_sdl(); }
App::~App() { App::~App() {
// Destroy window // Destroy window
@ -34,7 +38,7 @@ void App::init_sdl() {
} }
int App::run() { int App::run() {
ami = std::make_unique<AMI>(renderer); mission_select = std::make_unique<MissionSelect>(renderer);
running = true; running = true;
@ -48,18 +52,43 @@ int App::run() {
} }
void App::handle_events() { void App::handle_events() {
std::queue<Event> events;
SDL_Event e; SDL_Event e;
while (SDL_PollEvent(&e) != 0) { while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) { if (e.type == SDL_QUIT) {
running = false; running = false;
} else if (e.type == SDL_KEYUP) {
switch (e.key.keysym.sym) {
case SDLK_DOWN:
case SDLK_RIGHT:
events.push(Event::Next);
break;
case SDLK_UP:
case SDLK_LEFT:
events.push(Event::Prev);
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
events.push(Event::Confirm);
break;
// We can just ignore other keypresses, so no need for a default clause
}
} }
} }
switch (view) {
case AppView::MISSION_SELECT:
mission_select->handle_events(events);
break;
default:
throw std::runtime_error(fmt::format("Unknown view: {}", (int)view));
}
} }
void App::render() { void App::render() {
switch (view) { switch (view) {
case AppView::AMI: case AppView::MISSION_SELECT:
ami->draw(); mission_select->draw();
break; break;
default: default:
throw std::runtime_error(fmt::format("Unknown view: {}", (int)view)); throw std::runtime_error(fmt::format("Unknown view: {}", (int)view));

View File

@ -1,10 +1,14 @@
#include "AMI.h" #include "MissionSelect.h"
#include "defines.h" #include "defines.h"
#include "events.h"
#include "util.h" #include "util.h"
#include "widgets.h" #include "widgets.h"
#include <fmt/format.h>
#include <array> #include <array>
#include <iostream>
#include <memory> #include <memory>
constexpr const char* FT_LOGO_PATH = "resources/Fasttube_Logo-white.bmp"; constexpr const char* FT_LOGO_PATH = "resources/Fasttube_Logo-white.bmp";
@ -20,7 +24,7 @@ constexpr std::array MISSIONS = {"ACCELERATION", "SKIDPAD", "AUTOCROSS",
"TRACKDRIVE", "EBS TEST", "INSPECTION", "TRACKDRIVE", "EBS TEST", "INSPECTION",
"MANUAL DRIVING"}; "MANUAL DRIVING"};
AMI::AMI(SDL_Renderer* renderer) MissionSelect::MissionSelect(SDL_Renderer* renderer)
: View{renderer}, avenir{util::load_font(AVENIR_FONT_PATH, AVENIR_PTS)}, : View{renderer}, avenir{util::load_font(AVENIR_FONT_PATH, AVENIR_PTS)},
chinat{util::load_font(CHINAT_FONT_PATH, CHINAT_PTS)} { chinat{util::load_font(CHINAT_FONT_PATH, CHINAT_PTS)} {
ft_logo = std::make_unique<ImageWidget>(renderer, FT_LOGO_PATH); ft_logo = std::make_unique<ImageWidget>(renderer, FT_LOGO_PATH);
@ -51,15 +55,36 @@ AMI::AMI(SDL_Renderer* renderer)
widgets.push_back(missions_widget.get()); widgets.push_back(missions_widget.get());
} }
AMI::~AMI() { MissionSelect::~MissionSelect() {
TTF_CloseFont(avenir); TTF_CloseFont(avenir);
TTF_CloseFont(chinat); TTF_CloseFont(chinat);
} }
void AMI::draw() { void MissionSelect::draw() {
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
for (const auto& widget : widgets) { for (const auto& widget : widgets) {
widget->draw(); widget->draw();
} }
}
void MissionSelect::handle_events(std::queue<Event>& events) {
while (!events.empty()) {
Event e = events.front();
events.pop();
switch (e) {
case Event::Next:
missions_widget->select_next();
break;
case Event::Prev:
missions_widget->select_prev();
break;
case Event::Confirm:
std::cout << fmt::format("Selected mission {}\n",
missions_widget->get_selection());
break;
default:
throw std::runtime_error(fmt::format("Unknown event: {}", (int)e));
}
}
} }

View File

@ -7,8 +7,19 @@
#include <SDL2/SDL_image.h> #include <SDL2/SDL_image.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <cstdint>
#include <stdexcept> #include <stdexcept>
constexpr uint8_t LIST_SELECTION_BG_R = 0x77;
constexpr uint8_t LIST_SELECTION_BG_G = 0x33;
constexpr uint8_t LIST_SELECTION_BG_B = 0x33;
constexpr uint8_t LIST_OTHER_BG_R = 0x22;
constexpr uint8_t LIST_OTHER_BG_G = 0x22;
constexpr uint8_t LIST_OTHER_BG_B = 0x22;
constexpr uint8_t LIST_NORMAL_BG_R = 0x00;
constexpr uint8_t LIST_NORMAL_BG_G = 0x00;
constexpr uint8_t LIST_NORMAL_BG_B = 0x00;
PositionInfo::PositionInfo() : x{0}, y{0}, align{Alignment::LEFT} {} PositionInfo::PositionInfo() : x{0}, y{0}, align{Alignment::LEFT} {}
Widget::Widget(SDL_Renderer* renderer) : renderer{renderer}, rect{0, 0, 0, 0} {} Widget::Widget(SDL_Renderer* renderer) : renderer{renderer}, rect{0, 0, 0, 0} {}
@ -144,7 +155,7 @@ SDL_Texture* TextWidget::generate_text(const std::string& text) {
ListWidget::ListWidget(SDL_Renderer* renderer, int element_height, ListWidget::ListWidget(SDL_Renderer* renderer, int element_height,
Alignment element_alignment) Alignment element_alignment)
: Widget{renderer}, element_height{element_height}, : Widget{renderer}, element_height{element_height},
element_alignment{element_alignment} {} element_alignment{element_alignment}, selection{0} {}
ListWidget::~ListWidget() {} ListWidget::~ListWidget() {}
@ -163,12 +174,19 @@ void ListWidget::draw() {
// Since the elements are sorted, we can stop the loop now. // Since the elements are sorted, we can stop the loop now.
break; break;
} }
if (i % 2 == 1) { if (i == selection) {
SDL_SetRenderDrawColor(renderer, 0x11, 0x11, 0x11, 0xFF); SDL_SetRenderDrawColor(renderer, LIST_SELECTION_BG_R, LIST_SELECTION_BG_G,
SDL_Rect fill_rect = { LIST_SELECTION_BG_B, 0xFF);
.x = pos.x, .y = element_pos.y, .w = rect.w, .h = element_height}; } else if (i % 2 == 1) {
SDL_RenderFillRect(renderer, &fill_rect); SDL_SetRenderDrawColor(renderer, LIST_OTHER_BG_R, LIST_OTHER_BG_G,
LIST_OTHER_BG_B, 0xFF);
} else {
SDL_SetRenderDrawColor(renderer, LIST_NORMAL_BG_R, LIST_NORMAL_BG_G,
LIST_NORMAL_BG_B, 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(); element->draw();
if (i != elements.size() - 1) { if (i != elements.size() - 1) {
int border_y = element_pos.y + element->get_height(); int border_y = element_pos.y + element->get_height();
@ -178,6 +196,28 @@ void ListWidget::draw() {
} }
} }
void ListWidget::select_next() {
size_t n = elements.size();
if (n == 0) {
return;
}
selection = (selection + 1) % n;
}
void ListWidget::select_prev() {
size_t n = elements.size();
if (n == 0) {
return;
}
if (selection == 0) {
selection = n - 1;
} else {
selection = selection - 1;
}
}
size_t ListWidget::get_selection() { return selection; }
void ListWidget::place_element(Widget* element, int index) { void ListWidget::place_element(Widget* element, int index) {
int x = rect.x; int x = rect.x;
switch (element_alignment) { switch (element_alignment) {