#include #include #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 #include using namespace nlohmann; 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 FontGridCell { size_t cm_index; string as_string; wstring as_wstring; }; struct FontGrid { size_t width; size_t height; vector> $grid; string $font_list; std::wstring_convert> $converter; vector $wcharmap; vector $charmap; FontGrid(string font_list, size_t width, size_t height) : width(width), height(height), $grid(height, vector(width, {0, "", L""})), $font_list(font_list) { configure_font(); } void configure_font() { dbc::check(fs::exists($font_list), format("font listing {} does not exist", $font_list)); std::ifstream in_file($font_list); json input = json::parse(in_file); for(auto inchar : input) { $charmap.push_back(inchar); $wcharmap.push_back($converter.from_bytes(inchar)); } } size_t max_chars() { return $wcharmap.size(); } void render(size_t start_char, bool fill) { dbc::check(start_char > 0 && start_char < $charmap.size(), format("attempt render from bad start {}", start_char)); size_t next_char = start_char; for(size_t y = 0; y < height; ++y) { for(size_t x = 0; x < width; ++x) { if(!fill) { next_char++; } // just get out of here, nothing more to render if(next_char >= $charmap.size()) return; $grid[y][x] = { .cm_index = next_char, .as_string = $charmap[next_char], .as_wstring = $wcharmap[next_char] }; } } } size_t charmap_index(size_t x, size_t y) { FontGridCell &cell = $grid[y][x]; return cell.cm_index; } string& as_string(size_t x, size_t y) { return $grid[y][x].as_string; } wstring& as_wstring(size_t x, size_t y) { return $grid[y][x].as_wstring; } wchar_t as_wchar(size_t cm_index) { return $wcharmap[cm_index][0]; } 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: Panel $font_view; Panel $status_ui; Canvas $canvas; SFMLRender $renderer; FontGrid $font_grid; size_t $start_char = 1; size_t $fill_char = 1; WhatTheColor $fg_color; WhatTheColor $bg_color; Component $fg_settings; Component $bg_settings; GUI(string font_list) : $font_view(GAME_MAP_POS, 0, GRID_SIZE.x, GRID_SIZE.y, true), $status_ui(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), $font_grid(font_list, 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); render_grid($start_char, false); } void render_grid(size_t start_char, bool fill) { $font_grid.render(start_char, fill); } 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.as_string(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([&]{ wchar_t fill_char = $font_grid.as_wchar($fill_char); return hbox({ hflow( vbox( text(format("\\u{:x} {} IDX: {}, MIN: {}, MAX: {}", int(fill_char), int(fill_char), $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.charmap_index(pos.x, pos.y); render_grid($fill_char, true); } void deselect_cell() { render_grid($start_char, false); } bool handle_ui_events() { bool event_happened; using KB = sf::Keyboard; sf::Event event; Point pos; int font_size = $renderer.font_size(); int page_size = $font_grid.page_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(1, int($start_char) - page_size); render_grid($start_char, false); event_happened = true; $renderer.clear_cache(); } else if(KB::isKeyPressed(KB::Down)) { $start_char = std::min($font_grid.max_chars() - page_size, $start_char + page_size); render_grid($start_char, false); $renderer.clear_cache(); } else if(KB::isKeyPressed(KB::Tab)) { $status_ui.key_press(Event::Tab); } else if(KB::isKeyPressed(KB::Tab)) { $status_ui.key_press(Event::Return); } 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($renderer.mouse_position($font_view, pos)) { if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) { select_cell(pos); event_happened = true; } else if(sf::Mouse::isButtonPressed(sf::Mouse::Right)) { deselect_cell(); } } else if($renderer.mouse_position($status_ui, pos)) { if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) { $status_ui.mouse_click(Mouse::Button::Left, pos); } else if(event.type == sf::Event::MouseMoved) { $status_ui.mouse_release(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[]) { try { dbc::check(argc == 2, "USAGE: designer fontlist.json"); string font_list{argv[1]}; GUI gui(font_list); gui.create_renderer(); do { gui.render_scene(); if(gui.handle_ui_events()) { } std::this_thread::sleep_for(10ms); } while(gui.$renderer.is_open()); return 0; } catch(const dbc::Error &e) { println("ERROR: {}", e.message); } }