|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <chrono> // for operator""s, chrono_literals
|
|
|
|
#include <thread> // for sleep_for
|
|
|
|
#include "dinkyecs.hpp"
|
|
|
|
#include "events.hpp"
|
|
|
|
#include "dbc.hpp"
|
|
|
|
#include "render.hpp"
|
|
|
|
#include "lights.hpp"
|
|
|
|
#include <filesystem>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include "constants.hpp"
|
|
|
|
#include "point.hpp"
|
|
|
|
#include <fmt/core.h>
|
|
|
|
#include <locale>
|
|
|
|
#include <codecvt>
|
|
|
|
#include <vector>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
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<vector<FontGridCell>> $grid;
|
|
|
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
|
|
|
|
vector<wstring> $wcharmap;
|
|
|
|
vector<string> $charmap;
|
|
|
|
|
|
|
|
FontGrid(size_t width, size_t height) :
|
|
|
|
width(width), height(height),
|
|
|
|
$grid(height, vector<FontGridCell>(width, {0, "", L""}))
|
|
|
|
{
|
|
|
|
configure_font();
|
|
|
|
}
|
|
|
|
|
|
|
|
void configure_font() {
|
|
|
|
std::ifstream in_file("./fontlist.json");
|
|
|
|
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:
|
|
|
|
DinkyECS::World& $world;
|
|
|
|
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(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);
|
|
|
|
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() {
|
|
|
|
int flip_it = 0;
|
|
|
|
for(size_t y = 0; y < $font_grid.height; y++) {
|
|
|
|
for(size_t x = 0; x < $font_grid.width; x++, flip_it++) {
|
|
|
|
$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 / (flip_it % 2 + 1));
|
|
|
|
pixel.background_color = Color::HSV($bg_color.h, $bg_color.s, $bg_color.v / (flip_it % 2 + 1));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
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::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()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(10ms);
|
|
|
|
} while(gui.$renderer.is_open());
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|