diff --git a/Makefile b/Makefile index adb9559..3c47a6b 100644 --- a/Makefile +++ b/Makefile @@ -34,3 +34,7 @@ debug_run: build cover: gcovr --html coverage/index.html --gcov-ignore-errors=no_working_dir_found --exclude "scratchpad.*" --exclude "subprojects.*" --html-nested coverage/ + +designer: build + powershell "cp ./builddir/designer.exe ." + ./designer diff --git a/ansi_parser.cpp b/ansi_parser.cpp index 9e70b7a..59af13a 100644 --- a/ansi_parser.cpp +++ b/ansi_parser.cpp @@ -10,7 +10,7 @@ using namespace fmt; -#line 121 "ansi_parser.rl" +#line 122 "ansi_parser.rl" @@ -25,23 +25,23 @@ static const char _ansi_parser_actions[] = { static const char _ansi_parser_key_offsets[] = { 0, 0, 1, 2, 10, 11, 13, 16, - 19, 20, 24, 25, 26, 27, 28, 30, - 33, 35, 38, 40, 43, 44, 47, 48, - 49, 50, 51, 52 + 20, 21, 25, 26, 27, 28, 29, 31, + 34, 36, 39, 41, 44, 45, 48, 49, + 50, 51, 52, 53 }; static const int _ansi_parser_trans_keys[] = { 27, 91, 49, 50, 51, 52, 55, 57, 48, 54, 109, 48, 109, 34, 48, 55, - 50, 55, 109, 109, 49, 56, 57, 109, - 109, 59, 50, 59, 48, 57, 59, 48, - 57, 48, 57, 59, 48, 57, 48, 57, - 109, 48, 57, 109, 56, 57, 109, 59, - 50, 109, 109, 27, 27, 0 + 50, 52, 55, 109, 109, 49, 56, 57, + 109, 109, 59, 50, 59, 48, 57, 59, + 48, 57, 48, 57, 59, 48, 57, 48, + 57, 109, 48, 57, 109, 56, 57, 109, + 59, 50, 109, 109, 27, 27, 0 }; static const char _ansi_parser_single_lengths[] = { - 0, 1, 1, 6, 1, 2, 3, 3, + 0, 1, 1, 6, 1, 2, 3, 4, 1, 4, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 1, 1, 1, 1, 1 @@ -56,35 +56,35 @@ static const char _ansi_parser_range_lengths[] = { static const char _ansi_parser_index_offsets[] = { 0, 0, 2, 4, 12, 14, 17, 21, - 25, 27, 32, 34, 36, 38, 40, 42, - 45, 47, 50, 52, 55, 57, 61, 63, - 65, 67, 69, 71 + 26, 28, 33, 35, 37, 39, 41, 43, + 46, 48, 51, 53, 56, 58, 62, 64, + 66, 68, 70, 72 }; static const char _ansi_parser_trans_targs[] = { 2, 1, 3, 0, 5, 7, 9, 21, 25, 6, 4, 0, 27, 0, 6, 27, - 0, 4, 4, 4, 0, 4, 8, 27, - 0, 27, 0, 10, 11, 20, 27, 0, - 27, 0, 12, 0, 13, 0, 14, 0, - 15, 0, 16, 15, 0, 17, 0, 18, - 17, 0, 19, 0, 27, 19, 0, 27, - 0, 22, 24, 27, 0, 23, 0, 13, - 0, 27, 0, 27, 0, 2, 1, 2, - 1, 0 + 0, 4, 4, 4, 0, 4, 4, 8, + 27, 0, 27, 0, 10, 11, 20, 27, + 0, 27, 0, 12, 0, 13, 0, 14, + 0, 15, 0, 16, 15, 0, 17, 0, + 18, 17, 0, 19, 0, 27, 19, 0, + 27, 0, 22, 24, 27, 0, 23, 0, + 13, 0, 27, 0, 27, 0, 2, 1, + 2, 1, 0 }; static const char _ansi_parser_trans_actions[] = { 0, 7, 0, 0, 21, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 17, - 0, 15, 0, 0, 0, 0, 0, 0, - 19, 0, 0, 0, 3, 0, 0, 0, - 1, 0, 25, 0, 0, 1, 0, 28, - 0, 0, 1, 0, 34, 0, 0, 9, - 0, 0, 0, 0, 0, 0, 0, 5, - 0, 11, 0, 13, 0, 0, 7, 23, - 31, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 17, 0, 15, 0, 0, 0, 0, 0, + 0, 19, 0, 0, 0, 3, 0, 0, + 0, 1, 0, 25, 0, 0, 1, 0, + 28, 0, 0, 1, 0, 34, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 11, 0, 13, 0, 0, 7, + 23, 31, 0 }; static const char _ansi_parser_eof_actions[] = { @@ -101,7 +101,7 @@ static const int ansi_parser_error = 0; static const int ansi_parser_en_main = 26; -#line 124 "ansi_parser.rl" +#line 125 "ansi_parser.rl" #include @@ -128,7 +128,7 @@ bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_ cs = ansi_parser_start; } -#line 145 "ansi_parser.rl" +#line 146 "ansi_parser.rl" #line 121 "ansi_parser.cpp" { @@ -349,7 +349,7 @@ _again: _out: {} } -#line 146 "ansi_parser.rl" +#line 147 "ansi_parser.rl" bool good = pe - p == 0; diff --git a/ansi_parser.rl b/ansi_parser.rl index f31f64f..43fc283 100644 --- a/ansi_parser.rl +++ b/ansi_parser.rl @@ -108,6 +108,7 @@ using namespace fmt; "7" %invert | "31" %red_text | "22" | + "24" | "27" %reset_invert | "9" ["0"-"7"] | "10" ["0"-"7"] | diff --git a/assets/config.json b/assets/config.json index 6328805..3f52ab1 100644 --- a/assets/config.json +++ b/assets/config.json @@ -4,7 +4,8 @@ "FLOOR_TILE": "\u2849", "PLAYER_TILE": "\ua66b", "ENEMY_TILE": "Ω", - "BG_TILE": "█" + "BG_TILE": "█", + "WATER_TILE": "\u26c6" }, "enemy": { "HEARING_DISTANCE": 8 diff --git a/components.hpp b/components.hpp index fdd5b29..b17ca75 100644 --- a/components.hpp +++ b/components.hpp @@ -43,6 +43,7 @@ namespace components { std::string PLAYER_TILE; std::string ENEMY_TILE; std::string BG_TILE; + std::string WATER_TILE; }; struct EnemyConfig { diff --git a/constants.hpp b/constants.hpp index 38df811..3be61e5 100644 --- a/constants.hpp +++ b/constants.hpp @@ -12,3 +12,8 @@ const int WALL_LIGHT_LEVEL = 3; const int WORLDBUILD_DIVISION = 4; const int WORLDBUILD_SHRINK = 2; const int WORLDBUILD_MAX_PATH = 200; +const int GAME_MAP_POS = 600; +const int MAX_FONT_SIZE = 140; +const int MIN_FONT_SIZE = 20; +const int SCREEN_WIDTH = 40; +const int SCREEN_HEIGHT = 30; diff --git a/gui.cpp b/gui.cpp index 2174fdb..9ca6de3 100644 --- a/gui.cpp +++ b/gui.cpp @@ -21,9 +21,7 @@ #include "events.hpp" #include "render.hpp" #include "save.hpp" - -const int MAX_FONT_SIZE = 140; -const int MIN_FONT_SIZE = 20; +#include "constants.hpp" using std::string; using namespace fmt; @@ -210,13 +208,11 @@ bool GUI::handle_ui_events() { $status_ui.$component->OnEvent(Event::Return); } } else if(MOUSE::isButtonPressed(MOUSE::Left)) { - Point pos = $renderer.mouse_position(); - Mouse mev{ - .button=Mouse::Button::Left, - .x=int(pos.x), .y=int(pos.y) - }; - - $status_ui.$component->OnEvent(Event::Mouse("", mev)); + Point pos; + if($renderer.mouse_position($status_ui, pos)) { + $status_ui.mouse_click(Mouse::Button::Left, pos); + event_happened = true; + } } } diff --git a/gui.hpp b/gui.hpp index cc3256e..f5c261f 100644 --- a/gui.hpp +++ b/gui.hpp @@ -24,9 +24,6 @@ using std::string; using ftxui::Canvas, ftxui::Component, ftxui::Screen, ftxui::Button; using lighting::LightRender; -constexpr int SCREEN_WIDTH = 40; -constexpr int SCREEN_HEIGHT = 30; - struct ActionLog { std::deque messages; @@ -38,7 +35,6 @@ struct ActionLog { } }; -const int GAME_MAP_POS = 600; class GUI { string $status_text = "NOT DEAD"; diff --git a/meson.build b/meson.build index 1bcd3fe..eb6ddbe 100644 --- a/meson.build +++ b/meson.build @@ -77,6 +77,20 @@ roguish = executable('roguish', [ ], dependencies: dependencies) +designer = executable('designer', [ + 'matrix.cpp', + 'dbc.cpp', + 'rand.cpp', + 'ansi_parser.cpp', + 'render.cpp', + 'config.cpp', + 'panel.cpp', + 'pathing.cpp', + 'lights.cpp', + 'tools/designer.cpp' + ], + dependencies: dependencies) + img2ansi = executable('img2ansi', [ 'dbc.cpp', 'panel.cpp', diff --git a/panel.cpp b/panel.cpp index 30b9ec8..6ab1abf 100644 --- a/panel.cpp +++ b/panel.cpp @@ -35,6 +35,16 @@ const std::wstring& Panel::to_string() { return $screenout; } +void Panel::mouse_click(ftxui::Mouse::Button btn, Point pos) { + ftxui::Mouse mev{ + .button=btn, + .x=int(pos.x), .y=int(pos.y) + }; + + $component->OnEvent(ftxui::Event::Mouse("", mev)); +} + + const Screen &Panel::screen() { return $screen; } diff --git a/panel.hpp b/panel.hpp index 05fcb8e..0883dbf 100644 --- a/panel.hpp +++ b/panel.hpp @@ -1,6 +1,7 @@ #pragma once #include // for Render #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include "color.hpp" +#include "point.hpp" const int UI_PANEL_BORDER_PX=5; @@ -49,6 +51,7 @@ struct Panel { void set_renderer(Component renderer); void add(Component child); void render(); + void mouse_click(ftxui::Mouse::Button btn, Point pos); const std::wstring &to_string(); const Screen &screen(); }; diff --git a/render.cpp b/render.cpp index f471268..582f1ee 100644 --- a/render.cpp +++ b/render.cpp @@ -32,7 +32,7 @@ SFMLRender::SFMLRender() : $ui_text.setCharacterSize($config.ui_font_size); $ui_text.setFillColor(ColorValue::LIGHT_MID); sf::Glyph glyph = $font.getGlyph($config.ui_base_char, $config.ui_font_size, false); - $ui_bounds = glyph.bounds; + $text_bounds = glyph.bounds; } sf::Sprite &SFMLRender::get_text_sprite(wchar_t tile) { @@ -62,18 +62,18 @@ void SFMLRender::resize_grid(int new_size, Panel &panel_out) { $sprites.clear(); // need to reset the sprites for the new size $line_spacing = $font.getLineSpacing($map_font_size); $bg_sprite = get_text_sprite($config.bg_tile); - $bg_bounds = $bg_sprite.getLocalBounds(); + $grid_bounds = $bg_sprite.getLocalBounds(); panel_out.resize(view_x, view_y); } -inline void configure_tile(const sf::Sprite &sprite, sf::FloatRect &sp_bounds, sf::FloatRect bg_bounds, float &width_delta, float &height_delta) { +inline void configure_tile(const sf::Sprite &sprite, sf::FloatRect &sp_bounds, sf::FloatRect grid_bounds, float &width_delta, float &height_delta) { // BUG: I think I could create a struct that kept this info for all sprites loaded // should look into caching all this instead of calcing it each time sp_bounds = sprite.getLocalBounds(); // calculate where to center the sprite, but only if it's smaller - width_delta = bg_bounds.width > sp_bounds.width ? (bg_bounds.width - sp_bounds.width) / 2 : 0; - height_delta = bg_bounds.height > sp_bounds.width ? (bg_bounds.height - sp_bounds.height) / 2 : 0; + width_delta = grid_bounds.width > sp_bounds.width ? (grid_bounds.width - sp_bounds.width) / 2 : 0; + height_delta = grid_bounds.height > sp_bounds.width ? (grid_bounds.height - sp_bounds.height) / 2 : 0; } void SFMLRender::render_grid(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y) { @@ -106,7 +106,7 @@ void SFMLRender::render_grid(const std::wstring &text, sf::Color default_fg, sf: // only get a new sprite if the tile changed if(last_tile != tile) { sprite = get_text_sprite(tile); - configure_tile(sprite, sp_bounds, $bg_bounds, width_delta, height_delta); + configure_tile(sprite, sp_bounds, $grid_bounds, width_delta, height_delta); last_tile = tile; // update last tile seen } @@ -129,13 +129,13 @@ void SFMLRender::render_grid(const std::wstring &text, sf::Color default_fg, sf: } inline sf::FloatRect draw_chunk(sf::RenderWindow& window, - sf::FloatRect ui_bounds, sf::Text& text, sf::Color default_bg, + sf::FloatRect text_bounds, sf::Text& text, sf::Color default_bg, sf::Color bgcolor, int bg_box_offset, float x, float y, std::wstring &out) { text.setString(out); text.setPosition({x, y}); // get a base character for the cell size - sf::FloatRect bounds(x, y, ui_bounds.width * out.size(), ui_bounds.height); + sf::FloatRect bounds(x, y, text_bounds.width * out.size(), text_bounds.height); if(default_bg != bgcolor) { sf::RectangleShape backing({bounds.width, bounds.height}); @@ -162,7 +162,7 @@ void SFMLRender::render_text(const std::wstring &text, sf::Color default_fg, sf: [&](auto fg, auto bg) { if(out.size() > 0 ) { auto bounds = draw_chunk($window, - $ui_bounds, $ui_text, + $text_bounds, $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out); x += bounds.width; } @@ -176,7 +176,7 @@ void SFMLRender::render_text(const std::wstring &text, sf::Color default_fg, sf: sf::FloatRect bounds; if(out.size() > 0) { - bounds = draw_chunk($window, $ui_bounds, + bounds = draw_chunk($window, $text_bounds, $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out); } else { bounds = $ui_text.getLocalBounds(); @@ -194,7 +194,7 @@ void SFMLRender::render_text(const std::wstring &text, sf::Color default_fg, sf: ); if(out.size() > 0) { - draw_chunk($window, $ui_bounds, $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out); + draw_chunk($window, $text_bounds, $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out); } } @@ -205,8 +205,7 @@ void SFMLRender::render_text(const std::wstring &text, sf::Color default_fg, sf: void SFMLRender::draw(Panel &panel, float x_offset, float y_offset) { const std::wstring &panelout = panel.to_string(); - // BUG: ui vs bg doesn't make sense. maybe grid vs. text? - auto bounds = panel.grid ? $bg_bounds : $ui_bounds; + auto bounds = panel.grid ? $grid_bounds : $text_bounds; sf::RectangleShape backing( sf::Vector2f(bounds.width * panel.width + panel.border_px, @@ -229,13 +228,23 @@ void SFMLRender::draw(Panel &panel, float x_offset, float y_offset) { } } -Point SFMLRender::mouse_position() { +bool SFMLRender::mouse_position(Panel &panel, Point &out) { sf::Vector2i pos = sf::Mouse::getPosition($window); + auto bounds = panel.grid ? $grid_bounds : $text_bounds; + + if(pos.x >= panel.x && pos.y >= panel.y + && pos.x <= (panel.x + panel.width * bounds.width) + && pos.y <= (panel.y + panel.height * bounds.height)) + { + out = { + size_t((pos.x - panel.x) / bounds.width), + size_t((pos.y - panel.y) / bounds.height) + }; + + return true; + } - return { - size_t(pos.x / $ui_bounds.width), - size_t(pos.y / $ui_bounds.height) - }; + return false; } void SFMLRender::init_terminal() { diff --git a/render.hpp b/render.hpp index ed7d203..572d9ca 100644 --- a/render.hpp +++ b/render.hpp @@ -36,13 +36,13 @@ struct SFMLRender { sf::Texture $font_texture; sf::Glyph $base_glyph; sf::Sprite $bg_sprite; - sf::FloatRect $bg_bounds; + sf::FloatRect $grid_bounds; sf::Text $ui_text; std::wstring_convert> $converter; sf::Color $default_fg; sf::Color $default_bg; ANSIParser $ansi; - sf::FloatRect $ui_bounds; + sf::FloatRect $text_bounds; SFMLRender(); @@ -67,6 +67,6 @@ struct SFMLRender { int font_size() { return $map_font_size; } void clear() { $window.clear(); } void display() { $window.display(); } - Point mouse_position(); + bool mouse_position(Panel &panel, Point &out); static void init_terminal(); }; diff --git a/save.cpp b/save.cpp index c5b1555..6b03275 100644 --- a/save.cpp +++ b/save.cpp @@ -93,7 +93,8 @@ void save::load_configs(DinkyECS::World &world) { map["FLOOR_TILE"], map["PLAYER_TILE"], map["ENEMY_TILE"], - map["BG_TILE"] + map["BG_TILE"], + map["WATER_TILE"], }); auto enemy = config["enemy"]; diff --git a/status.txt b/status.txt index ff57051..ed01328 100644 --- a/status.txt +++ b/status.txt @@ -1,13 +1,31 @@ TODAY'S GOAL: + +1. Why do Sliders only have to be kept around forever and can't go in containers like everything else? +2. Why are sliders not selected when I click on them? Is it a hover? +3. Why do fonts render blank? Also when I scroll they slowly disappear until there's a column. + +* A designer tool to help find characters for foreground, background, and figure out their colors. + +* renderer's mouse coordinates are totally wrong. Need to put glyph bounds into the panel and then you can ask if a mouse click is on a panel, and what the _panel's_ coordinates are. + +* Use a vector of strings with 1 char each again. + +TODO: + +* Make a unicode helper. + * Fix " room should always be found" * Fix BUG markers as much as possible. * Make room generation have "texture" or state like mossy, flooded, etc. -TODO: +* A condition map that indicates what each tile's condition is, so it can have "watery", "wet", "mossy", "burned", and that changes the color of the foreground/background but not the actual tile. +* A tile will then denote a kind of surface, so stone, water, lava, etc. * Lua integration +* Enemies stuck in walls after generation. + * Save file needs work, it's not saving gold and lights. * Move all keyboard and mouse events into SFMLRender so it's completely abstracted away and can be changed to a different backend if I want. diff --git a/systems.cpp b/systems.cpp index 299c07e..2203ef9 100644 --- a/systems.cpp +++ b/systems.cpp @@ -188,8 +188,8 @@ void System::draw_map(DinkyECS::World &world, Map &game_map, const Matrix &light size_t end_x = std::min(view_x, game_map.width() - start.x); size_t end_y = std::min(view_y, game_map.height() - start.y); - for(size_t x = 0; x < end_x; ++x) { - for(size_t y = 0; y < end_y; ++y) { + for(size_t y = 0; y < end_y; ++y) { + for(size_t x = 0; x < end_x; ++x) { string tile = walls[start.y+y][start.x+x] == 1 ? config.WALL_TILE : config.FLOOR_TILE; int light_value = lighting[start.y+y][start.x+x]; @@ -207,11 +207,24 @@ void System::draw_map(DinkyECS::World &world, Map &game_map, const Matrix &light pixel.foreground_color = Color::HSV(dnum * 20, 150, 200); pixel.background_color = Color::HSV(30, 20, light_value / 5); }); + } else if(tile == config.WATER_TILE) { + canvas.DrawText(x * 2, y * 4, tile, [light_value](auto &pixel) { + pixel.foreground_color = Color::HSV(132, 200, std::min(int(light_value * 1.5), 200)); + pixel.background_color = Color::HSV(147, 220, light_value / 1.5); + }); } else { + canvas.DrawText(x * 2, y * 4, tile, [light_value](auto &pixel) { + pixel.foreground_color = Color::HSV(80, 100, light_value / 1.5); + pixel.background_color = Color::HSV(30, 20, light_value / 3); + }); + + /* + // swapped! canvas.DrawText(x * 2, y * 4, tile, [light_value](auto &pixel) { pixel.foreground_color = Color::HSV(30, 40, light_value); pixel.background_color = Color::HSV(30, 20, light_value / 3); }); + */ } } } diff --git a/tools/designer.cpp b/tools/designer.cpp new file mode 100644 index 0000000..e17708e --- /dev/null +++ b/tools/designer.cpp @@ -0,0 +1,262 @@ +#include // for operator""s, chrono_literals +#include // for sleep_for +#include "dinkyecs.hpp" +#include "events.hpp" +#include "dbc.hpp" +#include "render.hpp" +#include "lights.hpp" +#include +#include +#include "constants.hpp" +#include "point.hpp" +#include +#include +#include +#include + +using namespace fmt; +using namespace ftxui; +using namespace std::chrono_literals; +using lighting::LightSource, lighting::LightRender; +namespace fs = std::filesystem; +using std::string, std::wstring, std::vector; + +const Point GRID_SIZE={15,8}; +const int DEFAULT_FONT_SIZE=200; + +struct FontGrid { + size_t width; + size_t height; + vector> $chars; + std::wstring_convert> $converter; + + FontGrid(size_t width, size_t height) : + width(width), height(height), + $chars(height, vector(width, "")) + { + } + + void render(wchar_t start_char, bool fill) { + wchar_t cur_char = start_char; + + for(size_t y = 0; y < height; ++y) { + for(size_t x = 0; x < width; ++x) { + if(!fill) { + cur_char += (x+1) * (y+1); + } + + wstring out_w{cur_char}; + $chars[y][x] = from_unicode(out_w); + } + } + } + + string from_unicode(wstring input) { + return $converter.to_bytes(input); + } + + wchar_t to_unicode_char(size_t x, size_t y) { + string input = $chars[y][x]; + return $converter.from_bytes(input)[0]; + } + + string at(size_t x, size_t y) { + return $chars[y][x]; + } + + unsigned int page_size() { + return width * height; + } + +}; + +struct WhatTheColor { + int h; + int s; + int v; + Component h_slider; + Component s_slider; + Component v_slider; +}; + +class GUI { + public: + DinkyECS::World& $world; + Panel $font_view; + Panel $status_ui; + Canvas $canvas; + SFMLRender $renderer; + FontGrid $font_grid; + wchar_t $start_char = L'\ua66b'; + wchar_t $fill_char = WCHAR_MIN; + WhatTheColor $fg_color; + WhatTheColor $bg_color; + Component $fg_settings; + Component $bg_settings; + + GUI(DinkyECS::World &world) : + $world(world), + $font_view(GAME_MAP_POS, 0, GRID_SIZE.x, GRID_SIZE.y, true), + $status_ui(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), + $font_grid(GRID_SIZE.x, GRID_SIZE.y), + $fg_color{.h=20, .s=50, .v=20}, + $bg_color{.h=100, .s=100, .v=100} + { + resize_fonts(DEFAULT_FONT_SIZE); + $font_grid.render($start_char, false); + } + + void resize_fonts(int new_size) { + println("RESIZE MAP TO {}", new_size); + $renderer.resize_grid(new_size, $font_view); + // set canvas to best size + $canvas = Canvas($font_view.width * 2, $font_view.height * 4); + } + + void draw_font_grid() { + for(size_t y = 0; y < $font_grid.height; y++) { + for(size_t x = 0; x < $font_grid.width; x++) { + $canvas.DrawText(x * 2, y * 4, $font_grid.at(x, y), [&](auto &pixel) { + pixel.foreground_color = Color::HSV($fg_color.h, $fg_color.s, $fg_color.v); + pixel.background_color = Color::HSV($bg_color.h, $bg_color.s, $bg_color.v); + }); + } + } + } + + void create_renderer() { + $renderer.init_terminal(); + + $font_view.set_renderer(Renderer([&]{ + draw_font_grid(); + return canvas($canvas); + })); + + $fg_color.h_slider = Slider("FG H:", &$fg_color.h, 0, 255, 1); + $fg_color.s_slider = Slider("FG S:", &$fg_color.s, 0, 255, 1); + $fg_color.v_slider = Slider("FG V:", &$fg_color.v, 0, 255, 1); + $bg_color.h_slider = Slider("BG H:", &$bg_color.h, 0, 255, 1); + $bg_color.s_slider = Slider("BG S:", &$bg_color.s, 0, 255, 1); + $bg_color.v_slider = Slider("BG V:", &$bg_color.v, 0, 255, 1); + + $status_ui.set_renderer(Renderer([&]{ + return hbox({ + hflow( + vbox( + text(format("\\u{:x}", int($fill_char))) | border, + separator(), + text(format("FG H: {}, S: {}, V: {}", + $fg_color.h, $fg_color.s, $fg_color.v)) | border, + $fg_color.h_slider->Render(), + separator(), + $fg_color.s_slider->Render(), + separator(), + $fg_color.v_slider->Render(), + separator(), + text(format("BG H: {}, S: {}, V: {}", + $bg_color.h, $bg_color.s, $bg_color.v)) | border, + $bg_color.h_slider->Render(), + separator(), + $bg_color.s_slider->Render(), + separator(), + $bg_color.v_slider->Render() + ) | flex_grow + ), + separator(), + hbox(), + }); + })); + + $status_ui.add($fg_color.h_slider); + $status_ui.add($fg_color.s_slider); + $status_ui.add($fg_color.v_slider); + $status_ui.add($bg_color.h_slider); + $status_ui.add($bg_color.s_slider); + $status_ui.add($bg_color.v_slider); + } + + void shutdown() { + $renderer.close(); + } + + void select_cell(Point pos) { + $fill_char = $font_grid.to_unicode_char(pos.x, pos.y); + $font_grid.render($fill_char, true); + } + + void deselect_cell() { + $font_grid.render($start_char, false); + } + + bool handle_ui_events() { + bool event_happened; + using KB = sf::Keyboard; + using MOUSE = sf::Mouse; + sf::Event event; + int font_size = $renderer.font_size(); + + while($renderer.poll_event(event)) { + if(event.type == sf::Event::Closed) { + shutdown(); + return true; + } else if(event.type == sf::Event::KeyPressed) { + println("KEY PRESSED"); + if(KB::isKeyPressed(KB::Up)) { + $start_char = std::min(WCHAR_MAX, $start_char + $font_grid.page_size()); + $font_grid.render($start_char, false); + event_happened = true; + } else if(KB::isKeyPressed(KB::Down)) { + $start_char = std::max(WCHAR_MIN+1, $start_char - $font_grid.page_size()); + $font_grid.render($start_char, false); + } else if(KB::isKeyPressed(KB::Equal)) { + resize_fonts(font_size + 10); + } else if(KB::isKeyPressed(KB::Hyphen)) { + resize_fonts(font_size - 10); + event_happened = true; + } + } else if(MOUSE::isButtonPressed(MOUSE::Left)) { + Point pos; + if($renderer.mouse_position($font_view, pos)) { + select_cell(pos); + event_happened = true; + } else if($renderer.mouse_position($status_ui, pos)) { + $status_ui.mouse_click(Mouse::Button::Left, pos); + } + } + } + + return event_happened; + } + + void render_scene() { + $renderer.clear(); + + $status_ui.render(); + $renderer.draw($status_ui); + + $font_view.render(); + $renderer.draw($font_view); + + $renderer.display(); + } +}; + +int main(int argc, char *argv[]) { + DinkyECS::World world; + + GUI gui(world); + + gui.create_renderer(); + + do { + gui.render_scene(); + + if(gui.handle_ui_events()) { + println("THERE WERE EVENTS"); + } + + std::this_thread::sleep_for(10ms); + } while(gui.$renderer.is_open()); + + return 0; +}