#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 #include #include FT_FREETYPE_H 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) { try { return $converter.to_bytes(input); } catch(...) { return $converter.to_bytes(L"?"); } } wchar_t to_unicode_char(size_t x, size_t y) { try { string input = $chars[y][x]; // BUG: bounds check this instead return $converter.from_bytes(input)[0]; } catch(...) { return L'?'; } } 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'\u28cc'; 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} {} MIN: {}, MAX: {}", int($fill_char), int($fill_char), WCHAR_MIN, WCHAR_MAX)) | 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; 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) { if(KB::isKeyPressed(KB::Up)) { $start_char = std::max(WCHAR_MIN+1, $start_char - $font_grid.page_size()); $font_grid.render($start_char, false); event_happened = true; $renderer.clear_cache(); } else if(KB::isKeyPressed(KB::Down)) { $start_char = std::min(WCHAR_MAX, $start_char + $font_grid.page_size()); $font_grid.render($start_char, false); $renderer.clear_cache(); } 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(sf::Mouse::isButtonPressed(sf::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); } } else if(sf::Mouse::isButtonPressed(sf::Mouse::Right)) { deselect_cell(); } } 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; }