Implement AMI, more communication with STM etc
It's late and I forgot to commit
This commit is contained in:
parent
c553ea36d4
commit
0cd0ce6f3e
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
project("FT22 Steering Wheel Display")
|
project("FT22 Steering Wheel Display")
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ add_executable(
|
|||||||
src/AppState.cpp
|
src/AppState.cpp
|
||||||
src/View.cpp
|
src/View.cpp
|
||||||
src/MissionSelect.cpp
|
src/MissionSelect.cpp
|
||||||
|
src/AMI.cpp
|
||||||
src/widgets.cpp
|
src/widgets.cpp
|
||||||
src/util.cpp
|
src/util.cpp
|
||||||
)
|
)
|
||||||
|
27
include/AMI.h
Normal file
27
include/AMI.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppState.h"
|
||||||
|
#include "View.h"
|
||||||
|
#include "widgets.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class AMI final : public View {
|
||||||
|
public:
|
||||||
|
AMI(SDL_Renderer* renderer);
|
||||||
|
~AMI();
|
||||||
|
|
||||||
|
void draw(const AppState& state) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TTF_Font* font_medium;
|
||||||
|
TTF_Font* font_large;
|
||||||
|
|
||||||
|
std::unique_ptr<TextWidget> header;
|
||||||
|
std::unordered_map<Mission, std::unique_ptr<TextWidget>> missions;
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "AMI.h"
|
||||||
#include "AppState.h"
|
#include "AppState.h"
|
||||||
#include "MissionSelect.h"
|
#include "MissionSelect.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
@ -14,6 +15,13 @@ public:
|
|||||||
~SDLManager();
|
~SDLManager();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
class KeyboardHandler {
|
||||||
|
public:
|
||||||
|
void handle_keyup(const SDL_Event& ev, AppState& state);
|
||||||
|
};
|
||||||
|
#endif // !NDEBUG
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
public:
|
public:
|
||||||
App();
|
App();
|
||||||
@ -35,9 +43,14 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<AppState> state;
|
std::unique_ptr<AppState> state;
|
||||||
std::unique_ptr<MissionSelect> mission_select;
|
std::unique_ptr<MissionSelect> mission_select;
|
||||||
|
std::unique_ptr<AMI> ami;
|
||||||
|
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
KeyboardHandler keyboard_handler;
|
||||||
|
#endif // !NDEBUG
|
||||||
};
|
};
|
@ -11,6 +11,17 @@ constexpr int I2C_POLL_COMMAND = 0x00;
|
|||||||
|
|
||||||
enum class AppView { MISSION_SELECT, AMI, DRIVER, TESTING };
|
enum class AppView { MISSION_SELECT, AMI, DRIVER, TESTING };
|
||||||
std::ostream& operator<<(std::ostream& os, AppView view);
|
std::ostream& operator<<(std::ostream& os, AppView view);
|
||||||
|
enum class Mission {
|
||||||
|
NONE,
|
||||||
|
ACCELERATION,
|
||||||
|
SKIDPAD,
|
||||||
|
AUTOCROSS,
|
||||||
|
TRACKDRIVE,
|
||||||
|
EBS_TEST,
|
||||||
|
INSPECTION,
|
||||||
|
MANUAL
|
||||||
|
};
|
||||||
|
std::ostream& operator<<(std::ostream& os, Mission mission);
|
||||||
|
|
||||||
class AppState {
|
class AppState {
|
||||||
public:
|
public:
|
||||||
@ -19,11 +30,21 @@ public:
|
|||||||
|
|
||||||
void poll();
|
void poll();
|
||||||
|
|
||||||
AppView get_view();
|
AppView get_view() const;
|
||||||
|
Mission get_mission() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void unmarshal_mission_select(uint8_t* data);
|
||||||
|
|
||||||
// This is optional so that we can still run the code on a PC without I2C
|
// This is optional so that we can still run the code on a PC without I2C
|
||||||
std::optional<int> i2c_dev_file;
|
std::optional<int> i2c_dev_file;
|
||||||
|
|
||||||
AppView view;
|
AppView view;
|
||||||
|
Mission mission;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
friend class KeyboardHandler;
|
||||||
|
#endif // !NDEBUG
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern AppState app_state;
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppState.h"
|
||||||
#include "View.h"
|
#include "View.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "widgets.h"
|
#include "widgets.h"
|
||||||
@ -15,8 +16,7 @@ public:
|
|||||||
MissionSelect(SDL_Renderer* renderer);
|
MissionSelect(SDL_Renderer* renderer);
|
||||||
~MissionSelect();
|
~MissionSelect();
|
||||||
|
|
||||||
void draw() override;
|
void draw(const AppState& state) override;
|
||||||
void handle_events(std::queue<Event>& events) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TTF_Font* avenir;
|
TTF_Font* avenir;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppState.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
@ -11,8 +12,7 @@ public:
|
|||||||
View(SDL_Renderer* renderer);
|
View(SDL_Renderer* renderer);
|
||||||
virtual ~View();
|
virtual ~View();
|
||||||
|
|
||||||
virtual void draw() = 0;
|
virtual void draw(const AppState& state) = 0;
|
||||||
virtual void handle_events(std::queue<Event>& events) = 0;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
|
@ -3,17 +3,30 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <SDL2/SDL_ttf.h>
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
enum class Alignment { LEFT, RIGHT, CENTER };
|
enum class Alignment {
|
||||||
|
START,
|
||||||
|
END,
|
||||||
|
CENTER,
|
||||||
|
LEFT = START,
|
||||||
|
TOP = START,
|
||||||
|
RIGHT = END,
|
||||||
|
BOTTOM = END
|
||||||
|
};
|
||||||
|
std::ostream& operator<<(std::ostream& os, Alignment align);
|
||||||
struct PositionInfo {
|
struct PositionInfo {
|
||||||
PositionInfo();
|
PositionInfo();
|
||||||
|
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
Alignment align;
|
Alignment align_h;
|
||||||
|
Alignment align_v;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Widget {
|
class Widget {
|
||||||
@ -24,7 +37,7 @@ public:
|
|||||||
virtual void set_width(int width, bool preserve_aspect_ratio = true);
|
virtual void set_width(int width, bool preserve_aspect_ratio = true);
|
||||||
virtual void set_height(int height, bool preserve_aspect_ratio = true);
|
virtual void set_height(int height, bool preserve_aspect_ratio = true);
|
||||||
virtual void set_position(int x, int y);
|
virtual void set_position(int x, int y);
|
||||||
virtual void set_alignment(Alignment align);
|
virtual void set_alignment(Alignment align_h, Alignment align_v);
|
||||||
|
|
||||||
int get_width();
|
int get_width();
|
||||||
int get_height();
|
int get_height();
|
||||||
@ -87,6 +100,7 @@ public:
|
|||||||
|
|
||||||
void select_next();
|
void select_next();
|
||||||
void select_prev();
|
void select_prev();
|
||||||
|
void select(size_t n);
|
||||||
size_t get_selection();
|
size_t get_selection();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
64
src/AMI.cpp
Normal file
64
src/AMI.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "AMI.h"
|
||||||
|
|
||||||
|
#include "AppState.h"
|
||||||
|
#include "SDL_render.h"
|
||||||
|
#include "SDL_ttf.h"
|
||||||
|
#include "defines.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "widgets.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
constexpr const char* CHINAT_FONT_PATH = "resources/CHINAT.ttf";
|
||||||
|
constexpr int CHINAT_MEDIUM_PTS = 25;
|
||||||
|
constexpr int CHINAT_LARGE_PTS = 40;
|
||||||
|
constexpr int HEADER_Y = 10;
|
||||||
|
|
||||||
|
AMI::AMI(SDL_Renderer* renderer)
|
||||||
|
: View{renderer}, font_medium{util::load_font(CHINAT_FONT_PATH,
|
||||||
|
CHINAT_MEDIUM_PTS)},
|
||||||
|
font_large{util::load_font(CHINAT_FONT_PATH, CHINAT_LARGE_PTS)} {
|
||||||
|
header =
|
||||||
|
std::make_unique<TextWidget>(renderer, font_medium, "Current mission:");
|
||||||
|
header->set_position(SCREEN_WIDTH / 2, HEADER_Y);
|
||||||
|
header->set_alignment(Alignment::CENTER, Alignment::TOP);
|
||||||
|
|
||||||
|
missions.emplace(Mission::NONE,
|
||||||
|
std::make_unique<TextWidget>(renderer, font_large,
|
||||||
|
"NO MISSION SELECTED"));
|
||||||
|
missions.emplace(
|
||||||
|
Mission::ACCELERATION,
|
||||||
|
std::make_unique<TextWidget>(renderer, font_large, "ACCELERATION"));
|
||||||
|
missions.emplace(Mission::SKIDPAD, std::make_unique<TextWidget>(
|
||||||
|
renderer, font_large, "SKIDPAD"));
|
||||||
|
missions.emplace(Mission::AUTOCROSS, std::make_unique<TextWidget>(
|
||||||
|
renderer, font_large, "AUTOCROSS"));
|
||||||
|
missions.emplace(
|
||||||
|
Mission::TRACKDRIVE,
|
||||||
|
std::make_unique<TextWidget>(renderer, font_large, "TRACKDRIVE"));
|
||||||
|
missions.emplace(Mission::EBS_TEST, std::make_unique<TextWidget>(
|
||||||
|
renderer, font_large, "EBS TEST"));
|
||||||
|
missions.emplace(
|
||||||
|
Mission::INSPECTION,
|
||||||
|
std::make_unique<TextWidget>(renderer, font_large, "INSPECTION"));
|
||||||
|
missions.emplace(
|
||||||
|
Mission::MANUAL,
|
||||||
|
std::make_unique<TextWidget>(renderer, font_large, "MANUAL DRIVING"));
|
||||||
|
for (auto& [mission, widget] : missions) {
|
||||||
|
widget->set_position(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
|
||||||
|
widget->set_alignment(Alignment::CENTER, Alignment::CENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMI::~AMI() {
|
||||||
|
TTF_CloseFont(font_medium);
|
||||||
|
TTF_CloseFont(font_large);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMI::draw(const AppState& state) {
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
|
header->draw();
|
||||||
|
missions[state.get_mission()]->draw();
|
||||||
|
}
|
71
src/App.cpp
71
src/App.cpp
@ -1,5 +1,6 @@
|
|||||||
#include "App.h"
|
#include "App.h"
|
||||||
|
|
||||||
|
#include "AMI.h"
|
||||||
#include "AppState.h"
|
#include "AppState.h"
|
||||||
#include "MissionSelect.h"
|
#include "MissionSelect.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
@ -58,6 +59,7 @@ void App::init_state() {
|
|||||||
|
|
||||||
int App::run() {
|
int App::run() {
|
||||||
mission_select = std::make_unique<MissionSelect>(renderer);
|
mission_select = std::make_unique<MissionSelect>(renderer);
|
||||||
|
ami = std::make_unique<AMI>(renderer);
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
|
|
||||||
@ -91,40 +93,22 @@ void App::handle_events() {
|
|||||||
while (SDL_PollEvent(&e) != 0) {
|
while (SDL_PollEvent(&e) != 0) {
|
||||||
if (e.type == SDL_QUIT) {
|
if (e.type == SDL_QUIT) {
|
||||||
running = false;
|
running = false;
|
||||||
|
#ifndef NDEBUG
|
||||||
} else if (e.type == SDL_KEYUP) {
|
} else if (e.type == SDL_KEYUP) {
|
||||||
switch (e.key.keysym.sym) {
|
keyboard_handler.handle_keyup(e, *state);
|
||||||
case SDLK_DOWN:
|
#endif // !NDEBUG
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppView view = state->get_view();
|
|
||||||
switch (view) {
|
|
||||||
case AppView::MISSION_SELECT:
|
|
||||||
mission_select->handle_events(events);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw std::runtime_error(fmt::format("Unknown view: {}", view));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::render() {
|
void App::render() {
|
||||||
AppView view = state->get_view();
|
AppView view = state->get_view();
|
||||||
switch (view) {
|
switch (view) {
|
||||||
case AppView::MISSION_SELECT:
|
case AppView::MISSION_SELECT:
|
||||||
mission_select->draw();
|
mission_select->draw(*state);
|
||||||
|
break;
|
||||||
|
case AppView::AMI:
|
||||||
|
ami->draw(*state);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error(fmt::format("Unknown view: {}", view));
|
throw std::runtime_error(fmt::format("Unknown view: {}", view));
|
||||||
@ -153,3 +137,40 @@ SDLManager::~SDLManager() {
|
|||||||
TTF_Quit();
|
TTF_Quit();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyboardHandler::handle_keyup(const SDL_Event& ev, AppState& state) {
|
||||||
|
switch (ev.key.keysym.sym) {
|
||||||
|
case SDLK_DOWN:
|
||||||
|
case SDLK_RIGHT:
|
||||||
|
if (state.mission == Mission::MANUAL) {
|
||||||
|
state.mission = Mission::ACCELERATION;
|
||||||
|
} else {
|
||||||
|
state.mission = static_cast<Mission>(static_cast<int>(state.mission) + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDLK_UP:
|
||||||
|
case SDLK_LEFT:
|
||||||
|
if (state.mission == Mission::NONE ||
|
||||||
|
state.mission == Mission::ACCELERATION) {
|
||||||
|
state.mission = Mission::MANUAL;
|
||||||
|
} else {
|
||||||
|
state.mission = static_cast<Mission>(static_cast<int>(state.mission) - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDLK_RETURN:
|
||||||
|
case SDLK_KP_ENTER:
|
||||||
|
switch (state.view) {
|
||||||
|
case AppView::MISSION_SELECT:
|
||||||
|
state.view = AppView::AMI;
|
||||||
|
break;
|
||||||
|
case AppView::AMI:
|
||||||
|
case AppView::DRIVER:
|
||||||
|
case AppView::TESTING:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(fmt::format("Unknown view: {}", state.view));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// We can just ignore other keypresses, so no need for a default clause
|
||||||
|
}
|
||||||
|
}
|
@ -30,17 +30,40 @@ std::ostream& operator<<(std::ostream& os, AppView view) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, Mission mission) {
|
||||||
|
switch (mission) {
|
||||||
|
case Mission::NONE:
|
||||||
|
return os << "NONE";
|
||||||
|
case Mission::ACCELERATION:
|
||||||
|
return os << "ACCELERATION";
|
||||||
|
case Mission::SKIDPAD:
|
||||||
|
return os << "SKIDPAD";
|
||||||
|
case Mission::AUTOCROSS:
|
||||||
|
return os << "AUTOCROSS";
|
||||||
|
case Mission::TRACKDRIVE:
|
||||||
|
return os << "TRACKDRIVE";
|
||||||
|
case Mission::EBS_TEST:
|
||||||
|
return os << "EBS_TEST";
|
||||||
|
case Mission::INSPECTION:
|
||||||
|
return os << "INSPECTION";
|
||||||
|
case Mission::MANUAL:
|
||||||
|
return os << "MANUAL";
|
||||||
|
default:
|
||||||
|
return os << "UNKNOWN_MISSION[" << static_cast<int>(mission) << "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AppState::AppState(const std::string& i2c_dev_path)
|
AppState::AppState(const std::string& i2c_dev_path)
|
||||||
: view{AppView::MISSION_SELECT} {
|
: view{AppView::MISSION_SELECT}, mission{Mission::ACCELERATION} {
|
||||||
i2c_dev_file = open(i2c_dev_path.c_str(), O_RDWR);
|
i2c_dev_file = open(i2c_dev_path.c_str(), O_RDWR);
|
||||||
if (i2c_dev_file < 0) {
|
if (i2c_dev_file < 0) {
|
||||||
spdlog::error("Couldn't open I2C device: {}", errno);
|
spdlog::error("Couldn't open I2C device: {}", strerror(errno));
|
||||||
i2c_dev_file = std::nullopt;
|
i2c_dev_file = std::nullopt;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(*i2c_dev_file, I2C_SLAVE, I2C_SLAVE_ADDR) < 0) {
|
if (ioctl(*i2c_dev_file, I2C_SLAVE, I2C_SLAVE_ADDR) < 0) {
|
||||||
spdlog::error("Couldn't set I2C slave address: {}", errno);
|
spdlog::error("Couldn't set I2C slave address: {}", strerror(errno));
|
||||||
close(*i2c_dev_file);
|
close(*i2c_dev_file);
|
||||||
i2c_dev_file = std::nullopt;
|
i2c_dev_file = std::nullopt;
|
||||||
}
|
}
|
||||||
@ -60,10 +83,25 @@ void AppState::poll() {
|
|||||||
uint8_t data[32];
|
uint8_t data[32];
|
||||||
int count = i2c_smbus_read_block_data(*i2c_dev_file, I2C_POLL_COMMAND, data);
|
int count = i2c_smbus_read_block_data(*i2c_dev_file, I2C_POLL_COMMAND, data);
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
throw std::runtime_error(fmt::format("Couldn't poll I2C slave: {}", errno));
|
throw std::runtime_error(
|
||||||
|
fmt::format("Couldn't poll I2C slave: {}", strerror(errno)));
|
||||||
}
|
}
|
||||||
view = static_cast<AppView>(data[0]);
|
view = static_cast<AppView>(data[0]);
|
||||||
spdlog::info("View after poll: {}", view);
|
spdlog::info("View after poll: {}", view);
|
||||||
|
switch (view) {
|
||||||
|
case AppView::MISSION_SELECT:
|
||||||
|
unmarshal_mission_select(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(fmt::format("Unknown view: {}", view));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppView AppState::get_view() { return view; }
|
AppView AppState::get_view() const { return view; }
|
||||||
|
|
||||||
|
Mission AppState::get_mission() const { return mission; }
|
||||||
|
|
||||||
|
void AppState::unmarshal_mission_select(uint8_t* data) {
|
||||||
|
mission = static_cast<Mission>(data[1]);
|
||||||
|
spdlog::info("Mission after poll: {}", mission);
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
#include "MissionSelect.h"
|
#include "MissionSelect.h"
|
||||||
|
|
||||||
|
#include "AppState.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -30,12 +31,12 @@ MissionSelect::MissionSelect(SDL_Renderer* renderer)
|
|||||||
ft_logo = std::make_unique<ImageWidget>(renderer, FT_LOGO_PATH);
|
ft_logo = std::make_unique<ImageWidget>(renderer, FT_LOGO_PATH);
|
||||||
ft_logo->set_height(FT_LOGO_HEIGHT);
|
ft_logo->set_height(FT_LOGO_HEIGHT);
|
||||||
ft_logo->set_position(SCREEN_WIDTH / 2, 0);
|
ft_logo->set_position(SCREEN_WIDTH / 2, 0);
|
||||||
ft_logo->set_alignment(Alignment::CENTER);
|
ft_logo->set_alignment(Alignment::CENTER, Alignment::TOP);
|
||||||
widgets.push_back(ft_logo.get());
|
widgets.push_back(ft_logo.get());
|
||||||
|
|
||||||
choose = std::make_unique<TextWidget>(renderer, avenir, "Choose a mission:");
|
choose = std::make_unique<TextWidget>(renderer, avenir, "Choose a mission:");
|
||||||
choose->set_position(SCREEN_WIDTH / 2, CHOOSE_Y);
|
choose->set_position(SCREEN_WIDTH / 2, CHOOSE_Y);
|
||||||
choose->set_alignment(Alignment::CENTER);
|
choose->set_alignment(Alignment::CENTER, Alignment::TOP);
|
||||||
widgets.push_back(choose.get());
|
widgets.push_back(choose.get());
|
||||||
|
|
||||||
for (const auto mission : MISSIONS) {
|
for (const auto mission : MISSIONS) {
|
||||||
@ -60,31 +61,13 @@ MissionSelect::~MissionSelect() {
|
|||||||
TTF_CloseFont(chinat);
|
TTF_CloseFont(chinat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MissionSelect::draw() {
|
void MissionSelect::draw(const AppState& state) {
|
||||||
|
size_t n = static_cast<int>(state.get_mission()) - 1;
|
||||||
|
missions_widget->select(n);
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,21 @@ constexpr uint8_t LIST_NORMAL_BG_R = 0x00;
|
|||||||
constexpr uint8_t LIST_NORMAL_BG_G = 0x00;
|
constexpr uint8_t LIST_NORMAL_BG_G = 0x00;
|
||||||
constexpr uint8_t LIST_NORMAL_BG_B = 0x00;
|
constexpr uint8_t LIST_NORMAL_BG_B = 0x00;
|
||||||
|
|
||||||
PositionInfo::PositionInfo() : x{0}, y{0}, align{Alignment::LEFT} {}
|
std::ostream& operator<<(std::ostream& os, Alignment align) {
|
||||||
|
switch (align) {
|
||||||
|
case Alignment::START:
|
||||||
|
return os << "START";
|
||||||
|
case Alignment::CENTER:
|
||||||
|
return os << "CENTER";
|
||||||
|
case Alignment::END:
|
||||||
|
return os << "END";
|
||||||
|
default:
|
||||||
|
return os << "UNKNOWN ALIGNMENT[" << static_cast<int>(align) << "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionInfo::PositionInfo()
|
||||||
|
: x{0}, y{0}, align_h{Alignment::LEFT}, align_v{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} {}
|
||||||
|
|
||||||
@ -51,8 +65,9 @@ void Widget::set_position(int x, int y) {
|
|||||||
recalculate_pos();
|
recalculate_pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::set_alignment(Alignment align) {
|
void Widget::set_alignment(Alignment align_h, Alignment align_v) {
|
||||||
pos.align = align;
|
pos.align_h = align_h;
|
||||||
|
pos.align_v = align_v;
|
||||||
recalculate_pos();
|
recalculate_pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,19 +80,32 @@ const PositionInfo& Widget::get_position() { return pos; }
|
|||||||
void Widget::recalculate_pos() {
|
void Widget::recalculate_pos() {
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
int y = pos.y;
|
int y = pos.y;
|
||||||
switch (pos.align) {
|
|
||||||
case Alignment::LEFT:
|
switch (pos.align_h) {
|
||||||
|
case Alignment::START:
|
||||||
break;
|
break;
|
||||||
case Alignment::RIGHT:
|
case Alignment::END:
|
||||||
x -= rect.w;
|
x -= rect.w;
|
||||||
break;
|
break;
|
||||||
case Alignment::CENTER:
|
case Alignment::CENTER:
|
||||||
x -= rect.w / 2;
|
x -= rect.w / 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(fmt::format("Unknown alignment: {}", pos.align_h));
|
||||||
fmt::format("Unknown alignment: {}", (int)pos.align));
|
|
||||||
}
|
}
|
||||||
|
switch (pos.align_v) {
|
||||||
|
case Alignment::START:
|
||||||
|
break;
|
||||||
|
case Alignment::END:
|
||||||
|
y -= rect.h;
|
||||||
|
break;
|
||||||
|
case Alignment::CENTER:
|
||||||
|
y -= rect.h / 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(fmt::format("Unknown alignment: {}", pos.align_v));
|
||||||
|
}
|
||||||
|
|
||||||
rect.x = x;
|
rect.x = x;
|
||||||
rect.y = y;
|
rect.y = y;
|
||||||
}
|
}
|
||||||
@ -217,6 +245,15 @@ void ListWidget::select_prev() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListWidget::select(size_t n) {
|
||||||
|
if (n > elements.size()) {
|
||||||
|
throw std::runtime_error(fmt::format(
|
||||||
|
"Tried to select element {}, but there are only {} elements!", n,
|
||||||
|
elements.size()));
|
||||||
|
}
|
||||||
|
selection = n;
|
||||||
|
}
|
||||||
|
|
||||||
size_t ListWidget::get_selection() { return selection; }
|
size_t ListWidget::get_selection() { return selection; }
|
||||||
|
|
||||||
void ListWidget::place_element(Widget* element, int index) {
|
void ListWidget::place_element(Widget* element, int index) {
|
||||||
@ -232,11 +269,11 @@ void ListWidget::place_element(Widget* element, int index) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
fmt::format("Unknown alignment: {}", (int)pos.align));
|
fmt::format("Unknown alignment: {}", element_alignment));
|
||||||
}
|
}
|
||||||
// Additional pixel for border
|
// Additional pixel for border
|
||||||
int y = rect.y + index * (element_height + 1);
|
int y = rect.y + index * (element_height + 1);
|
||||||
element->set_position(x, y);
|
element->set_position(x, y);
|
||||||
element->set_alignment(element_alignment);
|
element->set_alignment(element_alignment, Alignment::TOP);
|
||||||
element->set_height(element_height);
|
element->set_height(element_height);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user