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")
|
||||
|
||||
@ -57,6 +57,7 @@ add_executable(
|
||||
src/AppState.cpp
|
||||
src/View.cpp
|
||||
src/MissionSelect.cpp
|
||||
src/AMI.cpp
|
||||
src/widgets.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
|
||||
|
||||
#include "AMI.h"
|
||||
#include "AppState.h"
|
||||
#include "MissionSelect.h"
|
||||
#include "defines.h"
|
||||
@ -14,6 +15,13 @@ public:
|
||||
~SDLManager();
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
class KeyboardHandler {
|
||||
public:
|
||||
void handle_keyup(const SDL_Event& ev, AppState& state);
|
||||
};
|
||||
#endif // !NDEBUG
|
||||
|
||||
class App {
|
||||
public:
|
||||
App();
|
||||
@ -35,9 +43,14 @@ private:
|
||||
|
||||
std::unique_ptr<AppState> state;
|
||||
std::unique_ptr<MissionSelect> mission_select;
|
||||
std::unique_ptr<AMI> ami;
|
||||
|
||||
bool running;
|
||||
|
||||
SDL_Window* window;
|
||||
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 };
|
||||
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 {
|
||||
public:
|
||||
@ -19,11 +30,21 @@ public:
|
||||
|
||||
void poll();
|
||||
|
||||
AppView get_view();
|
||||
AppView get_view() const;
|
||||
Mission get_mission() const;
|
||||
|
||||
private:
|
||||
void unmarshal_mission_select(uint8_t* data);
|
||||
|
||||
// This is optional so that we can still run the code on a PC without I2C
|
||||
std::optional<int> i2c_dev_file;
|
||||
|
||||
AppView view;
|
||||
Mission mission;
|
||||
|
||||
#ifndef NDEBUG
|
||||
friend class KeyboardHandler;
|
||||
#endif // !NDEBUG
|
||||
};
|
||||
|
||||
extern AppState app_state;
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppState.h"
|
||||
#include "View.h"
|
||||
#include "events.h"
|
||||
#include "widgets.h"
|
||||
@ -15,8 +16,7 @@ public:
|
||||
MissionSelect(SDL_Renderer* renderer);
|
||||
~MissionSelect();
|
||||
|
||||
void draw() override;
|
||||
void handle_events(std::queue<Event>& events) override;
|
||||
void draw(const AppState& state) override;
|
||||
|
||||
private:
|
||||
TTF_Font* avenir;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppState.h"
|
||||
#include "events.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
@ -11,8 +12,7 @@ public:
|
||||
View(SDL_Renderer* renderer);
|
||||
virtual ~View();
|
||||
|
||||
virtual void draw() = 0;
|
||||
virtual void handle_events(std::queue<Event>& events) = 0;
|
||||
virtual void draw(const AppState& state) = 0;
|
||||
|
||||
protected:
|
||||
SDL_Renderer* renderer;
|
||||
|
@ -3,17 +3,30 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#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 {
|
||||
PositionInfo();
|
||||
|
||||
int x;
|
||||
int y;
|
||||
Alignment align;
|
||||
Alignment align_h;
|
||||
Alignment align_v;
|
||||
};
|
||||
|
||||
class Widget {
|
||||
@ -24,7 +37,7 @@ public:
|
||||
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_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_height();
|
||||
@ -87,6 +100,7 @@ public:
|
||||
|
||||
void select_next();
|
||||
void select_prev();
|
||||
void select(size_t n);
|
||||
size_t get_selection();
|
||||
|
||||
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 "AMI.h"
|
||||
#include "AppState.h"
|
||||
#include "MissionSelect.h"
|
||||
#include "events.h"
|
||||
@ -58,6 +59,7 @@ void App::init_state() {
|
||||
|
||||
int App::run() {
|
||||
mission_select = std::make_unique<MissionSelect>(renderer);
|
||||
ami = std::make_unique<AMI>(renderer);
|
||||
|
||||
running = true;
|
||||
|
||||
@ -91,40 +93,22 @@ void App::handle_events() {
|
||||
while (SDL_PollEvent(&e) != 0) {
|
||||
if (e.type == SDL_QUIT) {
|
||||
running = false;
|
||||
#ifndef NDEBUG
|
||||
} 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
|
||||
}
|
||||
keyboard_handler.handle_keyup(e, *state);
|
||||
#endif // !NDEBUG
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
AppView view = state->get_view();
|
||||
switch (view) {
|
||||
case AppView::MISSION_SELECT:
|
||||
mission_select->draw();
|
||||
mission_select->draw(*state);
|
||||
break;
|
||||
case AppView::AMI:
|
||||
ami->draw(*state);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(fmt::format("Unknown view: {}", view));
|
||||
@ -153,3 +137,40 @@ SDLManager::~SDLManager() {
|
||||
TTF_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)
|
||||
: view{AppView::MISSION_SELECT} {
|
||||
: view{AppView::MISSION_SELECT}, mission{Mission::ACCELERATION} {
|
||||
i2c_dev_file = open(i2c_dev_path.c_str(), O_RDWR);
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
i2c_dev_file = std::nullopt;
|
||||
}
|
||||
@ -60,10 +83,25 @@ void AppState::poll() {
|
||||
uint8_t data[32];
|
||||
int count = i2c_smbus_read_block_data(*i2c_dev_file, I2C_POLL_COMMAND, data);
|
||||
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]);
|
||||
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 "AppState.h"
|
||||
#include "defines.h"
|
||||
#include "events.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->set_height(FT_LOGO_HEIGHT);
|
||||
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());
|
||||
|
||||
choose = std::make_unique<TextWidget>(renderer, avenir, "Choose a mission:");
|
||||
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());
|
||||
|
||||
for (const auto mission : MISSIONS) {
|
||||
@ -60,31 +61,13 @@ MissionSelect::~MissionSelect() {
|
||||
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_RenderClear(renderer);
|
||||
for (const auto& widget : widgets) {
|
||||
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_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} {}
|
||||
|
||||
@ -51,8 +65,9 @@ void Widget::set_position(int x, int y) {
|
||||
recalculate_pos();
|
||||
}
|
||||
|
||||
void Widget::set_alignment(Alignment align) {
|
||||
pos.align = align;
|
||||
void Widget::set_alignment(Alignment align_h, Alignment align_v) {
|
||||
pos.align_h = align_h;
|
||||
pos.align_v = align_v;
|
||||
recalculate_pos();
|
||||
}
|
||||
|
||||
@ -65,19 +80,32 @@ const PositionInfo& Widget::get_position() { return pos; }
|
||||
void Widget::recalculate_pos() {
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
switch (pos.align) {
|
||||
case Alignment::LEFT:
|
||||
|
||||
switch (pos.align_h) {
|
||||
case Alignment::START:
|
||||
break;
|
||||
case Alignment::RIGHT:
|
||||
case Alignment::END:
|
||||
x -= rect.w;
|
||||
break;
|
||||
case Alignment::CENTER:
|
||||
x -= rect.w / 2;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
fmt::format("Unknown alignment: {}", (int)pos.align));
|
||||
throw std::runtime_error(fmt::format("Unknown alignment: {}", pos.align_h));
|
||||
}
|
||||
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.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; }
|
||||
|
||||
void ListWidget::place_element(Widget* element, int index) {
|
||||
@ -232,11 +269,11 @@ void ListWidget::place_element(Widget* element, int index) {
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
fmt::format("Unknown alignment: {}", (int)pos.align));
|
||||
fmt::format("Unknown alignment: {}", element_alignment));
|
||||
}
|
||||
// Additional pixel for border
|
||||
int y = rect.y + index * (element_height + 1);
|
||||
element->set_position(x, y);
|
||||
element->set_alignment(element_alignment);
|
||||
element->set_alignment(element_alignment, Alignment::TOP);
|
||||
element->set_height(element_height);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user