Adding tooltip and TileMetaDialog to TilesetView
This commit is contained in:
@@ -46,7 +46,7 @@ CPMAddPackage(NAME JGL
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-58.zip)
|
||||
|
||||
CPMAddPackage(NAME JUI
|
||||
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-6.3.zip)
|
||||
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-6.4.zip)
|
||||
|
||||
|
||||
if (UNIX)
|
||||
|
@@ -1,25 +1,17 @@
|
||||
{
|
||||
"author":"",
|
||||
"cols":80.000000,
|
||||
"cols":50.000000,
|
||||
"file-path":"tileset.json",
|
||||
"first-gid":0.000000,
|
||||
"name":"tileset",
|
||||
"rows":64.000000,
|
||||
"texture-height":1280.000000,
|
||||
"texture-height":800.000000,
|
||||
"texture-path":"assets/megacommando.png",
|
||||
"texture-width":1024.000000,
|
||||
"tile-height":16.000000,
|
||||
"tile-spacing":0.000000,
|
||||
"tile-width":16.000000,
|
||||
"tiles":[
|
||||
{
|
||||
"id":5000.000000,
|
||||
"metadata":{
|
||||
"color":"#FFAAFF"
|
||||
},
|
||||
"name":"Special Tile",
|
||||
"quad":[0.000000, 0.000000, 16.000000, 16.000000]
|
||||
},
|
||||
{
|
||||
"id":896,
|
||||
"collides": false,
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 42 KiB |
@@ -135,8 +135,6 @@ public:
|
||||
|
||||
//std::vector<Quad> quads;
|
||||
|
||||
|
||||
|
||||
/// 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.
|
||||
Vector2i IndexToCell(int index, int width);
|
||||
@@ -164,6 +162,8 @@ public:
|
||||
/// @note Placeholder until FileDialogs are added.
|
||||
void SaveCurrentLevel() const;
|
||||
|
||||
void SaveTileset() const;
|
||||
|
||||
void SaveCurrentLevelAs(const std::string &filename) const;
|
||||
|
||||
void LoadLevel(const std::filesystem::path& level_meta_path);
|
||||
|
38
include/App/TileMetaDialog.hpp
Normal file
38
include/App/TileMetaDialog.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <Data/Tileset.hpp>
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
#include <JUI/Widgets/TextInputForm.hpp>
|
||||
|
||||
class TileMetaDialog : public JUI::Window, public JUI::Hoverable {
|
||||
public:
|
||||
Event<bool> CollidesChanged;
|
||||
Event<std::string> NameChanged;
|
||||
|
||||
TileMetaDialog();
|
||||
|
||||
explicit TileMetaDialog(JUI::Widget* parent);
|
||||
|
||||
void SetTileMetadata(const Tile& tile);
|
||||
|
||||
[[nodiscard]] int CurrentTileID() const;
|
||||
|
||||
[[nodiscard]] std::string CurrentTileName() const;
|
||||
|
||||
void OnExit(const Vector2 &MousePos) override;
|
||||
|
||||
JUI::TextRect* name_label;
|
||||
JUI::TextInputForm* name_form;
|
||||
JUI::TextRect* collides_label;
|
||||
JUI::Checkbox* collides_form;
|
||||
|
||||
int cur_tile_id;
|
||||
std::string cur_tile_name;
|
||||
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
private:
|
||||
};
|
@@ -2,11 +2,17 @@
|
||||
|
||||
#include <App/Grid.hpp>
|
||||
|
||||
#include "TileMetaDialog.hpp"
|
||||
|
||||
class TilesetView : public JUI::Window {
|
||||
public:
|
||||
Tileset* stored_tileset;
|
||||
Texture* stored_texture;
|
||||
Event<int> TileSelected;
|
||||
|
||||
Event<int, std::string> TileNameChanged;
|
||||
Event<int, bool> TileCollidesChanged;
|
||||
|
||||
bool is_focusing;
|
||||
TilesetView() : JUI::Window() {
|
||||
Title("Tileset View");
|
||||
@@ -50,6 +56,19 @@ public:
|
||||
cell_indicator->BGColor(Colors::Transparent);
|
||||
|
||||
SetCellSizeIndicatorToTilesetSize();
|
||||
|
||||
tile_info_tooltip = new JUI::Tooltip(this->Content());
|
||||
|
||||
tile_meta_dialog = new TileMetaDialog(this->Content());
|
||||
|
||||
|
||||
tile_meta_dialog->CollidesChanged += [&] (bool collides) {
|
||||
TileCollidesChanged(tile_meta_dialog->CurrentTileID(), collides);
|
||||
};
|
||||
|
||||
tile_meta_dialog->NameChanged += [&] (const std::string& new_name) {
|
||||
TileNameChanged(tile_meta_dialog->CurrentTileID(), new_name);
|
||||
};
|
||||
}
|
||||
TilesetView(Widget* parent) : TilesetView() {
|
||||
Parent(parent);
|
||||
@@ -90,8 +109,17 @@ public:
|
||||
rel.x *= stored_tileset->tile_width;
|
||||
rel.y *= stored_tileset->tile_height;
|
||||
|
||||
Vector2i cell = GetTilesetCellFromMouse(last_known_mouse_pos);
|
||||
int index = CellToIndex(cell, stored_tileset->rows);
|
||||
|
||||
|
||||
auto tile = stored_tileset->tiles[index];
|
||||
|
||||
cell_indicator->Position(JUI::UDim2::FromPixels(rel.x, rel.y));
|
||||
|
||||
tile_info_tooltip->Content(std::format("#{}: {}", tile.id, tile.name));
|
||||
tile_info_tooltip->AnchorPoint({0, 0.5f});
|
||||
tile_info_tooltip->Position(cell_indicator->Position());
|
||||
} else {
|
||||
cell_indicator->Visible(false);
|
||||
is_focusing = false;
|
||||
@@ -107,6 +135,29 @@ public:
|
||||
int index = CellToIndex(cell, stored_tileset->rows);
|
||||
TileSelected.Invoke(index);
|
||||
}
|
||||
|
||||
|
||||
if (Content()->IsMouseInside() && pressed == false && btn == JUI::MouseButton::Right) {
|
||||
|
||||
auto ipair = InputService::GetMousePosition();
|
||||
Vector2 mpos(ipair.x, ipair.y);
|
||||
|
||||
Vector2i cell = GetTilesetCellFromMouse(last_known_mouse_pos);
|
||||
int index = CellToIndex(cell, stored_tileset->rows);
|
||||
auto tile = stored_tileset->tiles[index];
|
||||
|
||||
Vector2 rel = Vector2(GetTilesetCellFromMouse(mpos));
|
||||
|
||||
rel.x *= stored_tileset->tile_width;
|
||||
rel.y *= stored_tileset->tile_height;
|
||||
tile_meta_dialog->SetTileMetadata(tile);
|
||||
tile_meta_dialog->Position(JUI::UDim2::FromPixels(rel.x, rel.y));
|
||||
|
||||
tile_meta_dialog->Open();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -126,9 +177,11 @@ protected:
|
||||
JUI::Image* tilesheet = nullptr;
|
||||
JGL::Texture* grid_overlay = nullptr;
|
||||
JUI::Rect* cell_indicator = nullptr;
|
||||
JUI::Tooltip* tile_info_tooltip = nullptr;
|
||||
TileMetaDialog* tile_meta_dialog = nullptr;
|
||||
protected:
|
||||
Color4 grid_overlay_color = {255, 255, 255, 64};
|
||||
Color4 cell_pointer_outline_color = Colors::Blues::LightSteelBlue;
|
||||
private:
|
||||
|
||||
};
|
||||
};
|
||||
|
@@ -26,6 +26,8 @@ struct Tile
|
||||
Quad quad;
|
||||
json::value metadata;
|
||||
bool collides;
|
||||
|
||||
bool persistent = false;
|
||||
};
|
||||
|
||||
/// TODO: Only generate tile entry for tiles that are used in the level, or have been assigned custom metadata.
|
||||
@@ -88,11 +90,11 @@ public:
|
||||
tileset.author = "J. O'Leary";
|
||||
tileset.name = "Sample Tileset";
|
||||
tileset.file_path = "tileset.json";
|
||||
tileset.cols = 80;
|
||||
tileset.cols = 50;
|
||||
tileset.rows = 64;
|
||||
tileset.texture_path = "assets/megacommando.png";
|
||||
tileset.tex_width = 1024;
|
||||
tileset.tex_height = 1280;
|
||||
tileset.tex_height = 800;
|
||||
return tileset;
|
||||
}
|
||||
|
||||
@@ -136,6 +138,7 @@ protected:
|
||||
void FillTiles() {
|
||||
// Generate some tiles
|
||||
tiles.reserve(rows*cols);
|
||||
tile_count = rows*cols;
|
||||
for (int i = 0 ; i < this->rows*this->cols; i++)
|
||||
{
|
||||
Tile tile;
|
||||
|
@@ -210,6 +210,10 @@ void EditorApp::SaveCurrentLevel() const
|
||||
write_file_contents("level.json", json::deparse(data));
|
||||
}
|
||||
|
||||
void EditorApp::SaveTileset() const {
|
||||
write_file_contents("tileset.json", json::deparse(loaded_tileset->Serialize()));
|
||||
}
|
||||
|
||||
void EditorApp::SaveCurrentLevelAs(const std::string& filename) const {
|
||||
|
||||
// TODO: Is it decent for the layers to be written to file separate?
|
||||
@@ -236,12 +240,36 @@ void EditorApp::LoadLevel(const std::filesystem::path& level_meta_path)
|
||||
for (auto layer : loaded_level->layers)
|
||||
layer->Load();
|
||||
|
||||
// Check layers for tiles that are too large
|
||||
for (auto layer : loaded_level->layers) {
|
||||
for (int x = 0; x < layer->rows; x++) {
|
||||
for (int y = 0; y < layer->cols; y++) {
|
||||
int id = layer->cells[x][y];
|
||||
|
||||
if (id >= loaded_tileset->tile_count)
|
||||
layer->cells[x][y] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tileset_view = new TilesetView(loaded_tileset, loaded_tilesheet, scene);
|
||||
|
||||
tileset_view->TileSelected += [this](int id) mutable {
|
||||
selected_quad = id;
|
||||
};
|
||||
|
||||
tileset_view->TileCollidesChanged += [this](int id, bool collides) mutable {
|
||||
loaded_tileset->tiles[id].collides = collides;
|
||||
loaded_tileset->tiles[id].persistent = true;
|
||||
SaveTileset();
|
||||
};
|
||||
|
||||
tileset_view->TileNameChanged += [this](int id, const std::string& new_name) mutable {
|
||||
loaded_tileset->tiles[id].name = new_name;
|
||||
loaded_tileset->tiles[id].persistent = true;
|
||||
SaveTileset();
|
||||
};
|
||||
|
||||
layer_view->UpdateComponents(loaded_level);
|
||||
layer_view->Open();
|
||||
|
||||
@@ -268,13 +296,13 @@ void EditorApp::CreateTestLevel() {
|
||||
loaded_tileset = new Tileset();
|
||||
loaded_tileset->name = "MegaCommando";
|
||||
loaded_tileset->author = "dawsh";
|
||||
loaded_tileset->tex_height = 1280;
|
||||
loaded_tileset->tex_height = 800;
|
||||
loaded_tileset->tex_width = 1024;
|
||||
loaded_tileset->tile_width = 16;
|
||||
loaded_tileset->tile_height = 16;
|
||||
loaded_tileset->tile_spacing = 0;
|
||||
loaded_tileset->cols = 80;
|
||||
loaded_tileset->rows = 60;
|
||||
loaded_tileset->cols = 50;
|
||||
loaded_tileset->rows = 64;
|
||||
loaded_tileset->file_path = "tileset.json";
|
||||
loaded_tileset->texture_path = "assets/megacommando.png";
|
||||
loaded_tilesheet = new JGL::Texture("assets/megacommando.png");
|
||||
@@ -474,7 +502,7 @@ void EditorApp::CreateWidgets()
|
||||
BindConsoleCallbacks();
|
||||
|
||||
nmd = new NewMapDialog(scene);
|
||||
nmd->Open();
|
||||
nmd->Close();
|
||||
|
||||
layer_view = new LayerView(scene);
|
||||
layer_view->Close();
|
||||
@@ -953,9 +981,6 @@ void EditorApp::Draw()
|
||||
}
|
||||
J2D::End();
|
||||
|
||||
|
||||
|
||||
|
||||
scene->Draw();
|
||||
}
|
||||
|
||||
|
66
src/App/TileMetaDialog.cpp
Normal file
66
src/App/TileMetaDialog.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <App/TileMetaDialog.hpp>
|
||||
|
||||
|
||||
TileMetaDialog::TileMetaDialog(): JUI::Window() {
|
||||
Name("TileMetaDialog");
|
||||
Title("Tile Metadata: #0");
|
||||
|
||||
Size({150_px, 60_px});
|
||||
AnchorPoint({0.5, 0.5});
|
||||
|
||||
name_label = new JUI::TextRect(this->Content());
|
||||
name_label->Size({50_px, 20_px});
|
||||
name_label->Center();
|
||||
name_label->Content("Tile Name:");
|
||||
name_form = new JUI::TextInputForm(this->Content());
|
||||
name_form->Size({70_px, 20_px});
|
||||
name_form->Position({50_px, 0_px});
|
||||
//name_form->SetInputBuffer("MyTile");
|
||||
// TODO: autocomplete_text_enabled does nothing.
|
||||
name_form->SetAutocompleteTextEnabled(false);
|
||||
name_form->SetAutoCompleteText("");
|
||||
name_form->Content("Another One");
|
||||
name_form->ClearTextOnReturn(false);
|
||||
|
||||
|
||||
collides_label = new JUI::TextRect(this->Content());
|
||||
collides_label->Size({50_px, 20_px});
|
||||
collides_label->Position({0_px, 20_px});
|
||||
collides_label->Center();
|
||||
collides_label->Content("Collides:");
|
||||
collides_form = new JUI::Checkbox(this->Content());
|
||||
collides_form->Size({20_px, 20_px});
|
||||
collides_form->Position({50_px, 20_px});
|
||||
|
||||
|
||||
name_form->OnReturn += [&] (const std::string& new_name) {
|
||||
NameChanged(new_name);
|
||||
};
|
||||
|
||||
collides_form->OnReleaseEvent += [&](auto pos, auto btn, bool value) mutable {
|
||||
CollidesChanged(!collides_form->IsChecked());
|
||||
};
|
||||
}
|
||||
|
||||
TileMetaDialog::TileMetaDialog(JUI::Widget *parent): TileMetaDialog() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
void TileMetaDialog::SetTileMetadata(const Tile &tile) {
|
||||
|
||||
cur_tile_id = tile.id;
|
||||
cur_tile_name = tile.name;
|
||||
|
||||
name_form->Content(tile.name);
|
||||
collides_form->SetChecked(tile.collides);
|
||||
|
||||
Title(std::format("Tile Metadata: {} #{}", tile.name, tile.id));
|
||||
}
|
||||
|
||||
int TileMetaDialog::CurrentTileID() const { return cur_tile_id;}
|
||||
|
||||
std::string TileMetaDialog::CurrentTileName() const { return cur_tile_name;}
|
||||
|
||||
void TileMetaDialog::OnExit(const Vector2 &MousePos) {
|
||||
this->Close();
|
||||
}
|
@@ -27,8 +27,6 @@ Tileset::Tileset(const std::string& name, const std::filesystem::path& texture_p
|
||||
this->rows = J3ML::Math::Floor(this->tex_width / this->tile_width);
|
||||
this->cols = J3ML::Math::Floor(this->tex_height / this->tile_height);
|
||||
|
||||
|
||||
|
||||
ComputeQuads();
|
||||
FillTiles();
|
||||
}
|
||||
@@ -71,9 +69,18 @@ void Tileset::Deserialize(const json::value& json)
|
||||
for (json::value& v : tilemeta)
|
||||
{
|
||||
int id = v["id"].number.value();
|
||||
tiles[id].name = v["name"].as_string();
|
||||
if (v.contains("collides"))
|
||||
tiles[id].collides = v["collides"].boolean.value_or(true);
|
||||
|
||||
if (id < this->tile_count) {
|
||||
tiles[id].persistent = true;
|
||||
|
||||
// TODO: Warning, tile out of tileset ID bounds!
|
||||
tiles[id].name = v["name"].as_string();
|
||||
if (v.contains("collides"))
|
||||
tiles[id].collides = v["collides"].boolean.value_or(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// this->tiles.push_back(v);
|
||||
}
|
||||
@@ -81,6 +88,7 @@ void Tileset::Deserialize(const json::value& json)
|
||||
|
||||
}
|
||||
|
||||
|
||||
json::value Tileset::Serialize() const
|
||||
{
|
||||
json::object tileset;
|
||||
@@ -89,7 +97,6 @@ json::value Tileset::Serialize() const
|
||||
tileset["texture-path"] = this->texture_path;
|
||||
tileset["author"] = this->author;
|
||||
|
||||
|
||||
tileset["tile-width"] = (float) this->tile_width;
|
||||
tileset["tile-height"] = (float) this->tile_height;
|
||||
tileset["tile-spacing"] = (float)this->tile_spacing;
|
||||
@@ -116,18 +123,20 @@ json::value Tileset::Serialize() const
|
||||
json::array tiles;
|
||||
for (auto& tile : this->tiles)
|
||||
{
|
||||
json::object tile_json;
|
||||
tile_json["name"] = tile.name;
|
||||
tile_json["id"] = (float)tile.id;
|
||||
tile_json["metadata"] = tile.metadata;
|
||||
tile_json["collides"] = json::boolean(tile.collides);
|
||||
json::array quad;
|
||||
quad.push_back((float)tile.quad.x);
|
||||
quad.push_back((float)tile.quad.y);
|
||||
quad.push_back((float)tile.quad.w);
|
||||
quad.push_back((float)tile.quad.h);
|
||||
tile_json["quad"] = quad;
|
||||
tiles.push_back(tile_json);
|
||||
if (tile.persistent) {
|
||||
json::object tile_json;
|
||||
tile_json["name"] = tile.name;
|
||||
tile_json["id"] = (float)tile.id;
|
||||
tile_json["metadata"] = tile.metadata;
|
||||
tile_json["collides"] = json::boolean(tile.collides);
|
||||
//json::array quad;
|
||||
//quad.push_back((float)tile.quad.x);
|
||||
//quad.push_back((float)tile.quad.y);
|
||||
//quad.push_back((float)tile.quad.w);
|
||||
//quad.push_back((float)tile.quad.h);
|
||||
//tile_json["quad"] = quad;
|
||||
tiles.push_back(tile_json);
|
||||
}
|
||||
}
|
||||
tileset["tiles"] = tiles;
|
||||
|
||||
|
Reference in New Issue
Block a user