Refactoring lots of stuff.

This commit is contained in:
2025-05-29 17:45:48 -05:00
parent e6022898e5
commit 474b62f267
7 changed files with 282 additions and 165 deletions

View File

@@ -0,0 +1,11 @@
#pragma once
#include <JUI/Widgets/Window.hpp>
class AboutDialog : public JUI::Window {
public:
protected:
private:
};

View File

@@ -68,8 +68,6 @@ class EditHistoryLogWindow : public JUI::Window {
public:
};
class EditorApp : public OpenGLWindow {
public:
#pragma region GUI Elements
@@ -81,6 +79,9 @@ public:
LayerView* layer_view = nullptr;
JUI::Window* bg_color_tool_window = nullptr;
JUI::ColorPicker* bg_color_tool = nullptr;
JUI::UtilityBar* topbar = nullptr;
JUI::Window* app_info_dialog = nullptr;
#pragma endregion
#pragma region Map Data
@@ -91,6 +92,9 @@ public:
EditorCamera camera;
JUI::Window * CreateAppInfoDialogWindow(JUI::Widget *parent);
Preferences preferences;
@@ -126,8 +130,7 @@ public:
//std::vector<Quad> quads;
/// Fill the tile-id lookup table with their respective partial-sprite bounding boxes.
void PopulateQuads();
/// This provides a mapping from a 1D flat array to a 2D grid.
/// @return The {x,y} coordinate of the grid-cell that maps to the given index.
@@ -142,7 +145,6 @@ public:
EditorApp();
~EditorApp() override
{
//DeleteGrid();
}
void LoadPreferences();
@@ -158,10 +160,7 @@ public:
void SaveTestFile();
void LoadLevel(const std::filesystem::path& level_meta_path);
/// Loads test-data, including a tilesheet, and a binary level-data file.
/// @note Placeholder until FileDialogs are added.
/// @note Placeholder until Tileset json is added.
void LoadMisc();
void SizeCellIndicatorToTilesetSize();
/// Creates a JUI widget that displays the Tileset, and lets you select a tile from it.
@@ -191,6 +190,7 @@ public:
void PlaceTileBrush(const Vector2i& cell);
void EraseTile(const Vector2i& cell);
bool IsMouseFocusedAnyActiveWidget();
void Update(float elapsed);
@@ -207,105 +207,28 @@ public:
void DrawTiles();
void DrawLayer(const Layer* layer) const
{
for (int gx = 0; gx < layer->rows; gx++) {
for (int gy = 0; gy < layer->cols; gy++) {
void DrawLayer(const Layer* layer) const;
auto quad_idx = layer->cells[gx][gy];
Vector2 pos(gx*layer->cell_width, gy*layer->cell_height);
//if ((gx+gy) % 2 == 0)
//J2D::FillRect(Colors::Purples::Fuchsia, pos, {16, 16});
if (quad_idx < 0)
continue;
auto quad = loaded_tileset->quads[quad_idx];
J2D::DrawPartialSprite(loaded_tilesheet, pos,
Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
}
}
}
void DrawLevel(const Level* level)
{
// TODO: Draw Per-Level Background Texture.
// TODO: Draw Per-Layer Background Texture.
for (const auto* layer : level->layers)
{
DrawLayer(layer);
}
// TODO: Support a handful of presets to represent entity shape types.
for (auto entity : level->entities)
{
J2D::DrawPoint(entity->overlay_color, entity->x, entity->y, 1);
J2D::DrawString(Colors::White, std::format("{} - {}", entity->type, entity->name), entity->x, entity->y, 1.f, 12);
}
}
void DrawLevel(const Level* level) const;
void Draw();
// TODO: Closing the app nominally doesn't work on Linux.
void OnClosing() override;
void OnRefresh(float elapsed) override {
Update(elapsed);
Draw();
SwapBuffers();
}
/// Performs a single step of the entire program logic. Update, then Draw, then Swapbuffers.
void OnRefresh(float elapsed) override;
enum JUI::MouseButton ToJUIEnum(const MouseButton& btn) {
if (btn == MouseButtons::Left) return JUI::MouseButton::Left;
if (btn == MouseButtons::Middle) return JUI::MouseButton::Middle;
if (btn == MouseButtons::Right) return JUI::MouseButton::Right;
// Default condition.
return JUI::MouseButton::Left;
}
enum JUI::MouseButton ToJUIEnum(const MouseButton& btn);
void OnMouseButtonDown(const MouseButtonDownEvent &e) override
{
auto btn = ToJUIEnum(e.Button);
void OnMouseButtonDown(const MouseButtonDownEvent &e) override;
if (scene->ObserveMouseInput(btn, true)) return;
void OnMouseButtonUp(const MouseButtonUpEvent &e) override;
// Tile-pick - Middle Click
if (btn == JUI::MouseButton::Middle)
{
Vector2i cell = GetGridCellFromMouse();
selected_quad = loaded_level->layers[0]->cells[cell.x][cell.y];
}
}
void OnMouseMove(const MouseMoveEvent &e) override;
void OnMouseButtonUp(const MouseButtonUpEvent &e) override {
auto btn = ToJUIEnum(e.Button);
if (scene->ObserveMouseInput(btn, false)) return;
void OnKeyDown(const KeyDownEvent &e) override;
if (tileset_viewer->Content()->IsMouseInside()) {
Vector2i cell = GetTilesetCellFromMouse();
int index = CellToIndex(cell, loaded_tileset->rows);
selected_quad = index;
}
// TODO: This always runs even if we release the mouse inside the window. ObserveMouseInput should handle the case.
}
void OnMouseMove(const MouseMoveEvent &e) override {
Vector2 mposv2(e.Position.x, e.Position.y);
if (scene->ObserveMouseMovement(mposv2)) return;
}
void OnKeyDown(const KeyDownEvent &e) override {
if (scene->ObserveKeyInput(e.key, true)) return;
}
void OnKeyUp(const KeyUpEvent &e) override {
if (scene->ObserveKeyInput(e.key, false)) return;
}
void OnKeyUp(const KeyUpEvent &e) override;
};

View File

@@ -3,6 +3,7 @@
#include <string>
#include <JJX/json.hpp>
#include <Color4.hpp>
#include <Utils.hpp>
using namespace JJX;
@@ -43,9 +44,19 @@ class Entity
json::value Serialize() const
{
json::object data;
return data;
data["name"] = this->name;
data["type"] = this->type;
data["x"] = this->x;
data["y"] = this->y;
data["width"] = this->width;
data["height"] = this->height;
data["rotation"] = this->rotation;
data["flip-h"] = this->flip_h;
data["flip-v"] = this->flip_v;
data["z-index"] = (float)this->z_index;
data["color"] = JsonConversions::deparse_color_to_hex(overlay_color);
data["metadata"] = this->metadata;
return data;
}
void Deserialize(const json::value& json)
@@ -56,6 +67,13 @@ class Entity
y = json["y"].number.value();
width = json["width"].number.value_or(0);
height = json["height"].number.value_or(0);
rotation = json["rotation"].number.value_or(0);
//flip_h = json["flip-h"].boolean.value();
//flip_v = json["flip-v"].boolean.value();
z_index = json["z-index"].number.value();
// TODO: sscanf is bad!!!
//overlay_color = JsonConversions::parse_color(json["color"]);
metadata = json["metadata"];
}

View File

@@ -129,5 +129,6 @@ protected:
inline int CellToIndex(int cx, int cy);
/// Fill the tile-id lookup table with their respective partial-sprite bounding boxes.
void ComputeQuads();
};

View File

@@ -1,15 +1,79 @@
#include <JUI/Widgets/ImageRect.hpp>
#include <App/EditorApp.hpp>
#include <App/NewMapDialog.hpp>
#include <Data/Format.hpp>
void EditorApp::PopulateQuads()
JUI::Window* EditorApp::CreateAppInfoDialogWindow(JUI::Widget* parent)
{
//quads.reserve(tileset_width*tileset_height);
//for (int i = 0; i < tileset_width*tileset_height; i++) {
// Vector2i cell = IndexToCell(i, tileset_width);
// quads.push_back(Quad{cell.x*grid_pixel_width, cell.y*grid_pixel_height, grid_pixel_width, grid_pixel_height});
//}
// TODO: Implement JUI structure that makes blocks of text easy to impelement.
auto window = new JUI::Window(parent);
window->SetTitle("About Editor2D");
window->Size({350_px, 375_px});
window->MinSize({350, 375});
auto* layout = new JUI::VerticalListLayout(window->Content());
layout->Padding(0_px);
// TODO: Code like this ends up being a common construct in JUI programs: Make a TextList widget.
auto line_item = [&] (const std::string& text, int size = 20, const Color4& color = Colors::White) {
auto* content = new JUI::TextRect(layout);
content->Size({100_percent, JUI::UDim(size+5, 0)});
content->SetContent(text);
content->SetTextSize(size);
content->SetTextColor(color);
content->BGColor(Colors::Transparent);
content->AlignCenterHorizontally();
content->BorderWidth(0);
};
line_item("Redacted Tile Map Editor", 30);
line_item("Version 1.0", 14);
auto* box = new JUI::Rect(layout);
box->Size({100_percent, 200_px});
box->BGColor(Colors::Transparent);
box->BorderWidth(0);
auto* img = new JUI::ImageRect(box);
img->Content(new JGL::Texture("../icon.png"));
img->Size({200_px, 200_px});
img->BGColor(Colors::Transparent);
img->AnchorPoint({.5f, .5f});
img->Position({50_percent, 50_percent});
img->BorderWidth(0);
img->BGColor(Colors::Transparent);
line_item("Developed & Maintained by Josh O'Leary.", 16);
line_item("William R. Maxine H. Ash B.", 12);
line_item("(c) 2024 - 2025 Redacted Software", 16);
auto* btn_box = new JUI::Rect(layout);
btn_box->Size({100_percent, 40_px});
btn_box->BGColor(Colors::Transparent);
btn_box->BorderWidth(0);
auto* btn_layout = new JUI::HorizontalListLayout(btn_box);
btn_layout->Padding(8_px);
auto btn_item = [&] (const std::string& text) -> JUI::TextButton* {
JUI::TextButton* btn = new JUI::TextButton(btn_layout);
btn->SetContent(text);
btn->Size({32_percent, 100_percent});
btn->SetTextColor(Colors::Black);
btn->Center();
return btn;
};
auto* btn_a = btn_item("License");
auto* btn_b = btn_item("Wiki");
auto* btn_c = btn_item("Source");
window->Close();
return window;
}
Vector2i EditorApp::IndexToCell(int index, int width)
@@ -118,71 +182,29 @@ void EditorApp::SavePreferences()
preferences.recent_camera_state.scale = camera.scale.goal;
write_file_contents("preferences.json", json::deparse(preferences.Serialize()));
}
void EditorApp::LoadTestFile()
{
LoadLevel("level.json");
//if (!std::filesystem::exists("test_level/metadata.json"))
//loaded_level = Level(std::filesystem::path("level.json"));
//grid_rows = loaded_level->layers[0].rows;
//grid_cols = loaded_level->layers[0].cols;
//InitGrid();
//std::string level_metadata_text = read_file_contents("test_level/metadata.json");
/*std::ifstream input;
input.open("test.lvl", std::ios::binary | std::ios::in);
input.seekg(0, std::ios::end);
int data_length = input.tellg();
input.seekg(0, std::ios::beg);
char* buffer = new char[data_length];
input.read(buffer, data_length);
input.close();
auto* data = reinterpret_cast<int*>(buffer);
for (int x = 0; x < grid_rows; x++)
{
for (int y = 0; y < grid_cols; y++)
{
int index = (x*grid_cols + y);
grid[x][y] = data[index];
}
}
//memcpy(grid, data, grid_rows * grid_cols * sizeof(int));
delete[] buffer;*/
}
void EditorApp::SaveTestFile()
{
/*int* buffer = new int[grid_rows*grid_cols];
for (int x = 0; x < grid_rows; x++)
for (auto layer : loaded_level->layers)
{
for (int y = 0; y < grid_cols; y++)
{
int index = (x*grid_cols + y);
buffer[index] = grid[x][y];
}
layer->Save();
}
std::ofstream output;
output.open("test.lvl", std::ios::out | std::ios::binary);
output.write(reinterpret_cast<const char*>(buffer), grid_rows * grid_cols * sizeof(int));
output.close();
auto data = loaded_level->Serialize();
write_file_contents("level.json", json::deparse(data));
delete[] buffer;*/
}
void EditorApp::LoadLevel(const std::filesystem::path& level_meta_path)
{
loaded_level = new Level(level_meta_path);
@@ -194,16 +216,11 @@ void EditorApp::LoadLevel(const std::filesystem::path& level_meta_path)
for (auto layer : loaded_level->layers)
layer->Load();
PopulateQuads();
layer_view->UpdateComponents(loaded_level);
data_ready = true;
}
void EditorApp::LoadMisc()
{
LoadTestFile();
}
void EditorApp::SizeCellIndicatorToTilesetSize()
@@ -311,6 +328,8 @@ void EditorApp::CreateWidgets()
{
scene = new JUI::Scene();
app_info_dialog = CreateAppInfoDialogWindow(scene);
console = new JUI::CommandLine(scene);
console->Close();
BindConsoleCallbacks();
@@ -321,7 +340,7 @@ void EditorApp::CreateWidgets()
layer_view = new LayerView(scene);
layer_view->Open();
auto* topbar = new JUI::UtilityBar(scene);
topbar = new JUI::UtilityBar(scene);
auto* file = topbar->AddSubmenu("File");
file->SetFont(JGL::Fonts::Jupiteroid);
@@ -335,7 +354,7 @@ void EditorApp::CreateWidgets()
file->AddButton("Save", [this]{SaveTestFile();});
file->AddButton("Save As");
file->AddSeparator(2_px);
file->AddButton("About");
file->AddButton("About", [this]{app_info_dialog->Toggle(); });
file->AddButton("Preferences");
auto* edit = topbar->AddSubmenu("Edit");
@@ -405,7 +424,7 @@ bool EditorApp::Open()
// 1. Initialize elements of widgets that are independent of the level itself / the level depends on.
// 2. Initialize the level.
// 3. Initialize widgets that are dependent on the level itself.
LoadMisc();
LoadTestFile();
CreateWidgets();
@@ -536,6 +555,39 @@ void EditorApp::EraseTile(const Vector2i& cell)
SetTile(cell, -1);
}
bool EditorApp::IsMouseFocusedAnyActiveWidget()
{
if (bg_color_tool->IsVisible() && bg_color_tool->IsMouseInside())
return true;
if (layer_view->IsVisible() && layer_view->IsMouseInside())
return true;
if (nmd->IsVisible() && nmd->IsMouseInside())
return true;
if (console->IsVisible() && console->IsMouseInside())
return true;
if (console->IsVisible() && console->IsMouseInside())
return true;
if (tileset_viewer->IsVisible() && tileset_viewer->IsMouseInside())
return true;
if (topbar->IsVisible() && topbar->IsMouseInsideDescendants(true))
return true;
//for (auto child : scene->GetDescendants())
//if (child->GetParent()->IsVisible() && child->IsVisible() && child->IsMouseInside())
//return true;
return false;
}
void EditorApp::Update(float elapsed)
{
CameraUpdate(elapsed);
@@ -549,7 +601,16 @@ void EditorApp::Update(float elapsed)
scene->SetViewportSize(Vector2(vSize));
scene->Update(elapsed);
bool mouse_hovering_ui_element = scene->IsMouseInsideDescendants();
// TODO: Widget::IsMouseInsideVisibleDescendants(); I.e. ShouldMouseInputsPayAttention?
//bool mouse_hovering_ui_element = scene->IsMouseInsideDescendants();
bool mouse_hovering_ui_element = IsMouseFocusedAnyActiveWidget();
//for (auto child : scene->GetChildren())
// if (child->IsVisible() && child->IsMouseInsideDescendants(true))
// mouse_hovering_ui_element = true;
if (tileset_viewer->IsMouseInside()) {
if (tileset_viewer->Content()->IsMouseInside()) {
@@ -647,6 +708,47 @@ void EditorApp::DrawTiles()
}*/
}
void EditorApp::DrawLayer(const Layer* layer) const
{
for (int gx = 0; gx < layer->rows; gx++) {
for (int gy = 0; gy < layer->cols; gy++) {
auto quad_idx = layer->cells[gx][gy];
Vector2 pos(gx*layer->cell_width, gy*layer->cell_height);
//if ((gx+gy) % 2 == 0)
//J2D::FillRect(Colors::Purples::Fuchsia, pos, {16, 16});
if (quad_idx < 0)
continue;
auto quad = loaded_tileset->quads[quad_idx];
J2D::DrawPartialSprite(loaded_tilesheet, pos,
Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
}
}
}
void EditorApp::DrawLevel(const Level* level) const
{
// TODO: Draw Per-Level Background Texture.
// TODO: Draw Per-Layer Background Texture.
for (const auto* layer : level->layers)
{
DrawLayer(layer);
}
// TODO: Support a handful of presets to represent entity shape types.
for (auto entity : level->entities)
{
J2D::DrawPoint(entity->overlay_color, entity->x, entity->y, 1);
J2D::DrawString(Colors::White, std::format("{} - {}", entity->type, entity->name), entity->x, entity->y, 1.f, 12);
}
}
void EditorApp::Draw()
{
glClearColor(bg_color.RN(), bg_color.GN(), bg_color.BN(), 1);
@@ -693,3 +795,65 @@ void EditorApp::OnClosing()
SavePreferences();
SaveTestFile();
}
void EditorApp::OnRefresh(float elapsed)
{
Update(elapsed);
Draw();
SwapBuffers();
}
JUI::MouseButton EditorApp::ToJUIEnum(const MouseButton& btn)
{
if (btn == MouseButtons::Left) return JUI::MouseButton::Left;
if (btn == MouseButtons::Middle) return JUI::MouseButton::Middle;
if (btn == MouseButtons::Right) return JUI::MouseButton::Right;
// Default condition.
return JUI::MouseButton::Left;
}
void EditorApp::OnMouseButtonDown(const MouseButtonDownEvent& e)
{
auto btn = ToJUIEnum(e.Button);
if (scene->ObserveMouseInput(btn, true)) return;
// Tile-pick - Middle Click
if (btn == JUI::MouseButton::Middle)
{
Vector2i cell = GetGridCellFromMouse();
selected_quad = loaded_level->layers[0]->cells[cell.x][cell.y];
}
}
void EditorApp::OnMouseButtonUp(const MouseButtonUpEvent& e)
{
auto btn = ToJUIEnum(e.Button);
if (scene->ObserveMouseInput(btn, false)) return;
if (tileset_viewer->Content()->IsMouseInside()) {
Vector2i cell = GetTilesetCellFromMouse();
int index = CellToIndex(cell, loaded_tileset->rows);
selected_quad = index;
}
// TODO: This always runs even if we release the mouse inside the window. ObserveMouseInput should handle the case.
}
void EditorApp::OnMouseMove(const MouseMoveEvent& e)
{
Vector2 mposv2(e.Position.x, e.Position.y);
if (scene->ObserveMouseMovement(mposv2)) return;
}
void EditorApp::OnKeyDown(const KeyDownEvent& e)
{
if (scene->ObserveKeyInput(e.key, true)) return;
}
void EditorApp::OnKeyUp(const KeyUpEvent& e)
{
if (scene->ObserveKeyInput(e.key, false)) return;
}

View File

@@ -3,8 +3,6 @@
#include <fstream>
Color4 JsonConversions::parse_color(const JJX::json::value& v)
{
if (v.type == json::value_type::string)

View File

@@ -1,10 +1,12 @@
#include <ReWindow/types/Window.h>
#include "ReWindow/Logger.h"
#include <Data/Level.hpp>
class TestGameAppWindow : public ReWindow::OpenGLWindow
{
public:
Level* level;
TestGameAppWindow() : ReWindow::OpenGLWindow("TestGameAppWindow", 1024, 768, 2, 1)
{