initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")

View File

@@ -0,0 +1,369 @@
/**************************************************************************/
/* atlas_merging_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "atlas_merging_dialog.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/control.h"
#include "scene/gui/split_container.h"
#include "scene/resources/image_texture.h"
void AtlasMergingDialog::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
_set(p_property, p_value);
}
void AtlasMergingDialog::_generate_merged(const Vector<Ref<TileSetAtlasSource>> &p_atlas_sources, int p_max_columns) {
merged.instantiate();
merged_mapping.clear();
if (p_atlas_sources.size() >= 2) {
Ref<Image> output_image = Image::create_empty(1, 1, false, Image::FORMAT_RGBA8);
// Compute the new texture region size.
Vector2i new_texture_region_size;
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
new_texture_region_size = new_texture_region_size.max(atlas_source->get_texture_region_size());
}
// Generate the new texture.
Vector2i atlas_offset;
int line_height = 0;
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
Ref<Image> input_image = atlas_source->get_texture()->get_image();
if (input_image->get_format() != Image::FORMAT_RGBA8) {
input_image->convert(Image::FORMAT_RGBA8);
}
merged_mapping.push_back(HashMap<Vector2i, Vector2i>());
// Layout the tiles.
Vector2i atlas_size;
for (int tile_index = 0; tile_index < atlas_source->get_tiles_count(); tile_index++) {
Vector2i tile_id = atlas_source->get_tile_id(tile_index);
atlas_size = atlas_size.max(tile_id + atlas_source->get_tile_size_in_atlas(tile_id));
Rect2i new_tile_rect_in_atlas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id));
// Copy the texture.
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
Vector2i new_position = new_tile_rect_in_atlas.position * new_texture_region_size;
if (frame > 0) {
new_position += src_rect.size * Vector2i(frame, 0);
atlas_size.x = MAX(frame + 1, atlas_size.x);
}
Rect2 dst_rect_wide = Rect2i(new_position, new_tile_rect_in_atlas.size * new_texture_region_size);
if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
}
output_image->blit_rect(input_image, src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
// Add to the mapping.
merged_mapping[source_index][tile_id] = new_tile_rect_in_atlas.position;
}
// Compute the atlas offset.
line_height = MAX(atlas_size.y, line_height);
atlas_offset.x += atlas_size.x;
if (atlas_offset.x >= p_max_columns) {
atlas_offset.x = 0;
atlas_offset.y += line_height;
line_height = 0;
}
}
merged->set_name(p_atlas_sources[0]->get_name());
merged->set_texture(ImageTexture::create_from_image(output_image));
merged->set_texture_region_size(new_texture_region_size);
// Copy the tiles to the merged TileSetAtlasSource.
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
const Ref<TileSetAtlasSource> &atlas_source = p_atlas_sources[source_index];
for (KeyValue<Vector2i, Vector2i> tile_mapping : merged_mapping[source_index]) {
// Create tiles and alternatives, then copy their properties.
for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_mapping.key); alternative_index++) {
int alternative_id = atlas_source->get_alternative_tile_id(tile_mapping.key, alternative_index);
int changed_id = -1;
if (alternative_id == 0) {
merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key));
int count = atlas_source->get_tile_animation_frames_count(tile_mapping.key);
merged->set_tile_animation_frames_count(tile_mapping.value, count);
for (int i = 0; i < count; i++) {
merged->set_tile_animation_frame_duration(tile_mapping.value, i, atlas_source->get_tile_animation_frame_duration(tile_mapping.key, i));
}
merged->set_tile_animation_speed(tile_mapping.value, atlas_source->get_tile_animation_speed(tile_mapping.key));
merged->set_tile_animation_mode(tile_mapping.value, atlas_source->get_tile_animation_mode(tile_mapping.key));
} else {
changed_id = merged->create_alternative_tile(tile_mapping.value, alternative_index);
}
// Copy the properties.
TileData *src_tile_data = atlas_source->get_tile_data(tile_mapping.key, alternative_id);
List<PropertyInfo> properties;
src_tile_data->get_property_list(&properties);
TileData *dst_tile_data = merged->get_tile_data(tile_mapping.value, changed_id == -1 ? alternative_id : changed_id);
for (PropertyInfo property : properties) {
if (!(property.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
Variant value = src_tile_data->get(property.name);
Variant default_value = ClassDB::class_get_default_property_value("TileData", property.name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
continue;
}
dst_tile_data->set(property.name, value);
}
}
}
}
}
}
void AtlasMergingDialog::_update_texture() {
Vector<int> selected = atlas_merging_atlases_list->get_selected_items();
if (selected.size() >= 2) {
Vector<Ref<TileSetAtlasSource>> to_merge;
for (int i = 0; i < selected.size(); i++) {
int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]);
to_merge.push_back(tile_set->get_source(source_id));
}
_generate_merged(to_merge, next_line_after_column);
preview->set_texture(merged->get_texture());
preview->show();
select_2_atlases_label->hide();
get_ok_button()->set_disabled(false);
merge_button->set_disabled(false);
} else {
_generate_merged(Vector<Ref<TileSetAtlasSource>>(), next_line_after_column);
preview->set_texture(Ref<Texture2D>());
preview->hide();
select_2_atlases_label->show();
get_ok_button()->set_disabled(true);
merge_button->set_disabled(true);
}
}
void AtlasMergingDialog::_merge_confirmed(const String &p_path) {
ERR_FAIL_COND(merged.is_null());
Ref<ImageTexture> output_image_texture = merged->get_texture();
output_image_texture->get_image()->save_png(p_path);
ResourceLoader::import(p_path);
Ref<Texture2D> new_texture_resource = ResourceLoader::load(p_path, "Texture2D");
merged->set_texture(new_texture_resource);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Merge TileSetAtlasSource"));
int next_id = tile_set->get_next_source_id();
undo_redo->add_do_method(*tile_set, "add_source", merged, next_id);
undo_redo->add_undo_method(*tile_set, "remove_source", next_id);
if (delete_original_atlases) {
// Delete originals if needed.
Vector<int> selected = atlas_merging_atlases_list->get_selected_items();
for (int i = 0; i < selected.size(); i++) {
int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]);
Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
undo_redo->add_do_method(*tile_set, "remove_source", source_id);
undo_redo->add_undo_method(*tile_set, "add_source", tas, source_id);
// Add the tile proxies.
for (int tile_index = 0; tile_index < tas->get_tiles_count(); tile_index++) {
Vector2i tile_id = tas->get_tile_id(tile_index);
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", source_id, tile_id, next_id, merged_mapping[i][tile_id]);
if (tile_set->has_coords_level_tile_proxy(source_id, tile_id)) {
Array a = tile_set->get_coords_level_tile_proxy(source_id, tile_id);
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", a[0], a[1]);
} else {
undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", source_id, tile_id);
}
}
}
}
undo_redo->commit_action();
committed_actions_count++;
hide();
}
void AtlasMergingDialog::ok_pressed() {
delete_original_atlases = false;
editor_file_dialog->popup_file_dialog();
}
void AtlasMergingDialog::cancel_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < committed_actions_count; i++) {
undo_redo->undo();
}
committed_actions_count = 0;
}
void AtlasMergingDialog::custom_action(const String &p_action) {
if (p_action == "merge") {
delete_original_atlases = true;
editor_file_dialog->popup_file_dialog();
}
}
bool AtlasMergingDialog::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "next_line_after_column" && p_value.get_type() == Variant::INT) {
next_line_after_column = p_value;
_update_texture();
return true;
}
return false;
}
bool AtlasMergingDialog::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "next_line_after_column") {
r_ret = next_line_after_column;
return true;
}
return false;
}
void AtlasMergingDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
_update_texture();
}
} break;
}
}
void AtlasMergingDialog::update_tile_set(Ref<TileSet> p_tile_set) {
ERR_FAIL_COND(p_tile_set.is_null());
tile_set = p_tile_set;
atlas_merging_atlases_list->clear();
for (int i = 0; i < p_tile_set->get_source_count(); i++) {
int source_id = p_tile_set->get_source_id(i);
Ref<TileSetAtlasSource> atlas_source = p_tile_set->get_source(source_id);
if (atlas_source.is_valid()) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (texture.is_valid()) {
String item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
atlas_merging_atlases_list->add_item(item_text, texture);
atlas_merging_atlases_list->set_item_metadata(-1, source_id);
}
}
}
get_ok_button()->set_disabled(true);
merge_button->set_disabled(true);
committed_actions_count = 0;
}
AtlasMergingDialog::AtlasMergingDialog() {
// Atlas merging window.
set_title(TTR("Atlas Merging"));
set_hide_on_ok(false);
// Ok buttons
set_ok_button_text(TTR("Merge (Keep original Atlases)"));
get_ok_button()->set_disabled(true);
merge_button = add_button(TTR("Merge"), true, "merge");
merge_button->set_disabled(true);
HSplitContainer *atlas_merging_h_split_container = memnew(HSplitContainer);
atlas_merging_h_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_h_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
add_child(atlas_merging_h_split_container);
// Atlas sources item list.
atlas_merging_atlases_list = memnew(ItemList);
atlas_merging_atlases_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
atlas_merging_atlases_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200));
atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI);
atlas_merging_atlases_list->set_theme_type_variation("ItemListSecondary");
atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2));
atlas_merging_h_split_container->add_child(atlas_merging_atlases_list);
VBoxContainer *atlas_merging_right_panel = memnew(VBoxContainer);
atlas_merging_right_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_right_panel->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
atlas_merging_h_split_container->add_child(atlas_merging_right_panel);
// Settings.
Label *settings_label = memnew(Label);
settings_label->set_text(TTR("Settings:"));
atlas_merging_right_panel->add_child(settings_label);
columns_editor_property = memnew(EditorPropertyInteger);
columns_editor_property->set_label(TTR("Next Line After Column"));
columns_editor_property->set_object_and_property(this, "next_line_after_column");
columns_editor_property->update_property();
columns_editor_property->connect("property_changed", callable_mp(this, &AtlasMergingDialog::_property_changed));
atlas_merging_right_panel->add_child(columns_editor_property);
// Preview.
Label *preview_label = memnew(Label);
preview_label->set_text(TTR("Preview:"));
atlas_merging_right_panel->add_child(preview_label);
preview = memnew(TextureRect);
preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
preview->hide();
preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
atlas_merging_right_panel->add_child(preview);
select_2_atlases_label = memnew(Label);
select_2_atlases_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
select_2_atlases_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
select_2_atlases_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
select_2_atlases_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
select_2_atlases_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
select_2_atlases_label->set_text(TTR("Please select two atlases or more."));
atlas_merging_right_panel->add_child(select_2_atlases_label);
// The file dialog to choose the texture path.
editor_file_dialog = memnew(EditorFileDialog);
editor_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
editor_file_dialog->add_filter("*.png");
editor_file_dialog->connect("file_selected", callable_mp(this, &AtlasMergingDialog::_merge_confirmed));
add_child(editor_file_dialog);
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* atlas_merging_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_properties.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/2d/tile_set.h"
class EditorFileDialog;
class EditorPropertyVector2i;
class AtlasMergingDialog : public ConfirmationDialog {
GDCLASS(AtlasMergingDialog, ConfirmationDialog);
private:
int committed_actions_count = 0;
bool delete_original_atlases = true;
Ref<TileSetAtlasSource> merged;
LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping;
Ref<TileSet> tile_set;
// Settings.
int next_line_after_column = 30;
// GUI.
ItemList *atlas_merging_atlases_list = nullptr;
EditorPropertyVector2i *texture_region_size_editor_property = nullptr;
EditorPropertyInteger *columns_editor_property = nullptr;
TextureRect *preview = nullptr;
Label *select_2_atlases_label = nullptr;
EditorFileDialog *editor_file_dialog = nullptr;
Button *merge_button = nullptr;
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
void _generate_merged(const Vector<Ref<TileSetAtlasSource>> &p_atlas_sources, int p_max_columns);
void _update_texture();
void _merge_confirmed(const String &p_path);
protected:
virtual void ok_pressed() override;
virtual void cancel_pressed() override;
virtual void custom_action(const String &) override;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _notification(int p_what);
public:
void update_tile_set(Ref<TileSet> p_tile_set);
AtlasMergingDialog();
};

View File

@@ -0,0 +1,766 @@
/**************************************************************************/
/* tile_atlas_view.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_atlas_view.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/2d/tile_map_layer.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/gui/view_panner.h"
void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
if (panner->gui_input(p_event, get_global_rect())) {
accept_event();
}
}
void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
panning += p_scroll_vec;
_update_zoom_and_panning(true);
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);
_update_zoom_and_panning(true, p_origin);
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
Size2i TileAtlasView::_compute_base_tiles_control_size() {
if (tile_set_atlas_source.is_null()) {
return Size2i();
}
// Update the texture.
Vector2i size;
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
size = texture->get_size();
}
return size;
}
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
if (tile_set_atlas_source.is_null()) {
return Size2i();
}
Vector2i size;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
Vector2i line_size;
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
bool transposed = tile_set_atlas_source->get_tile_data(tile_id, alternative_id)->get_transpose();
line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
}
size.x = MAX(size.x, line_size.x);
size.y += line_size.y;
}
return size;
}
void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos, const Vector2 &p_mouse_pos) {
if (tile_set_atlas_source.is_null()) {
return;
}
float zoom = zoom_widget->get_zoom();
// Compute the minimum sizes.
Size2i base_tiles_control_size = _compute_base_tiles_control_size();
base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom);
Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();
alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom);
// Set the texture for the base tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
// Set the scales.
if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {
base_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
} else {
base_tiles_drawing_root->set_scale(Vector2(1, 1));
}
if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {
alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
} else {
alternative_tiles_drawing_root->set_scale(Vector2(1, 1));
}
// Update the margin container's margins.
const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };
for (int i = 0; i < 4; i++) {
margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom);
}
// Update the backgrounds.
background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
// Zoom on the position.
if (p_zoom_on_mouse_pos) {
Vector2 relative_mpos = p_mouse_pos - get_size() / 2;
panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos;
} else {
// Center of panel.
panning = panning * zoom / previous_zoom;
}
button_center_view->set_disabled(panning.is_zero_approx());
previous_zoom = zoom;
center_container->set_begin(panning - center_container->get_minimum_size() / 2);
center_container->set_size(center_container->get_minimum_size());
}
void TileAtlasView::_zoom_widget_changed() {
_update_zoom_and_panning();
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
void TileAtlasView::_center_view() {
panning = Vector2();
button_center_view->set_disabled(true);
_update_zoom_and_panning();
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
}
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
if (tile_set_atlas_source.is_null()) {
return;
}
base_tiles_root_control->set_tooltip_text("");
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();
Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
coords = tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
base_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
}
}
}
}
void TileAtlasView::_draw_base_tiles() {
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
return;
}
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
// Draw the texture where there is no tile.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (rect.size.x > 0 && rect.size.y > 0) {
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
}
}
}
}
// Draw dark overlay after for performance reasons.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (rect.size.x > 0 && rect.size.y > 0) {
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
}
}
// Draw the texture around the grid.
Rect2i rect;
// Top.
rect.position = Vector2i();
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
// Bottom
int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
if (bottom_border < texture->get_size().y) {
rect.position = Vector2i(0, bottom_border);
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Left
rect.position = Vector2i(0, margins.y);
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
// Right.
int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
if (right_border < texture->get_size().x) {
rect.position = Vector2i(right_border, margins.y);
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Draw actual tiles, using their properties (modulation, etc...)
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
// Different materials need to be drawn with different CanvasItems.
RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
// Draw the tile.
TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
}
}
// Draw Dark overlay on separation in its own pass.
if (separation.x > 0 || separation.y > 0) {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
if (separation.x > 0) {
Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
//base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
if (separation.y > 0) {
Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
//base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
}
}
}
}
}
RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map) {
Ref<Material> mat = p_for_data->get_material();
if (mat.is_null()) {
return p_base_item->get_canvas_item();
} else if (p_material_map.has(mat)) {
return p_material_map[mat];
} else {
RID ci_rid = RS::get_singleton()->canvas_item_create();
RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item());
RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid());
RS::get_singleton()->canvas_item_set_default_texture_filter(ci_rid, RS::CanvasItemTextureFilter(p_base_item->get_texture_filter_in_tree()));
p_material_map[mat] = ci_rid;
return ci_rid;
}
}
void TileAtlasView::_clear_material_canvas_items() {
for (KeyValue<Ref<Material>, RID> kv : material_tiles_draw) {
RS::get_singleton()->free(kv.value);
}
material_tiles_draw.clear();
for (KeyValue<Ref<Material>, RID> kv : material_alternatives_draw) {
RS::get_singleton()->free(kv.value);
}
material_alternatives_draw.clear();
}
void TileAtlasView::_draw_base_tiles_texture_grid() {
if (tile_set_atlas_source.is_null()) {
return;
}
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
// Draw each tile texture region.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
if (base_tile_coords == Vector2i(x, y)) {
// Draw existing tile.
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);
}
} else {
// Draw the grid.
base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);
}
}
}
}
}
void TileAtlasView::_draw_base_tiles_shape_grid() {
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
return;
}
// Draw the shapes.
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
Vector2i tile_shape_size = tile_set->get_tile_size();
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Color color = grid_color;
if (frame > 0) {
color.a *= 0.3;
}
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id, frame);
Transform2D tile_xform;
tile_xform.set_origin(Rect2(texture_region).get_center() + in_tile_base_offset);
tile_xform.set_scale(tile_shape_size);
tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
}
}
}
}
void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
alternative_tiles_root_control->set_tooltip_text("");
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();
Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));
Vector2i coords = Vector2i(coords3.x, coords3.y);
int alternative_id = coords3.z;
if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
alternative_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
}
}
}
void TileAtlasView::_draw_alternatives() {
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
return;
}
// Draw the alternative tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2 current_pos;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
current_pos.x = 0;
int y_increment = 0;
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);
bool transposed = tile_data->get_transpose();
// Different materials need to be drawn with different CanvasItems.
RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw);
// Update the y to max value.
Vector2i offset_pos;
if (transposed) {
offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_data->get_texture_origin());
y_increment = MAX(y_increment, texture_region_size.x);
} else {
offset_pos = (current_pos + texture_region_size / 2 + tile_data->get_texture_origin());
y_increment = MAX(y_increment, texture_region_size.y);
}
// Draw the tile.
TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
}
if (alternatives_count > 1) {
current_pos.y += y_increment;
}
}
}
}
void TileAtlasView::_draw_background_left() {
background_left->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_left->get_size()), true);
}
void TileAtlasView::_draw_background_right() {
background_right->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_right->get_size()), true);
}
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
tile_set = Ref<TileSet>(p_tile_set);
tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source);
_clear_material_canvas_items();
if (tile_set.is_null()) {
return;
}
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
source_id = p_source_id;
// Show or hide the view.
bool valid = tile_set_atlas_source->get_texture().is_valid();
hbox->set_visible(valid);
missing_source_label->set_visible(!valid);
// Update the rect cache.
_update_alternative_tiles_rect_cache();
// Update everything.
_update_zoom_and_panning();
base_tiles_drawing_root->set_size(_compute_base_tiles_control_size());
alternative_tiles_drawing_root->set_size(_compute_alternative_tiles_control_size());
// Update.
base_tiles_draw->queue_redraw();
base_tiles_texture_grid->queue_redraw();
base_tiles_shape_grid->queue_redraw();
alternatives_draw->queue_redraw();
background_left->queue_redraw();
background_right->queue_redraw();
}
float TileAtlasView::get_zoom() const {
return zoom_widget->get_zoom();
}
void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) {
zoom_widget->set_zoom(p_zoom);
panning = p_panning;
_update_zoom_and_panning();
}
void TileAtlasView::set_padding(Side p_side, int p_padding) {
ERR_FAIL_COND(p_padding < 0);
margin_container_paddings[p_side] = p_padding;
}
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
if (tile_set_atlas_source.is_null()) {
return Vector2i();
}
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_null()) {
return TileSetSource::INVALID_ATLAS_COORDS;
}
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
// Compute index in atlas.
Vector2 pos = p_pos - margins;
Vector2i ret = (pos / (texture_region_size + separation)).floor();
// Clamp.
if (p_clamp) {
Vector2i size = tile_set_atlas_source->get_atlas_grid_size();
ret = ret.clamp(Vector2i(), size - Vector2i(1, 1));
}
return ret;
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
if (tile_set_atlas_source.is_null()) {
return;
}
alternative_tiles_rect_cache.clear();
Rect2i current;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
int line_height = 0;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
TileData *tile_data = tile_set_atlas_source->get_tile_data(tile_id, alternative_id);
bool transposed = tile_data->get_transpose();
current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
// Update the rect.
if (!alternative_tiles_rect_cache.has(tile_id)) {
alternative_tiles_rect_cache[tile_id] = HashMap<int, Rect2i>();
}
alternative_tiles_rect_cache[tile_id][alternative_id] = current;
current.position.x += transposed ? texture_region_size.y : texture_region_size.x;
line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);
}
current.position.x = 0;
current.position.y += line_height;
}
}
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
for (const KeyValue<Vector2, HashMap<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {
for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {
if (E_alternative.value.has_point(p_pos)) {
return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);
}
}
}
return Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache[p_coords].has(p_alternative_tile), Rect2i(), vformat("No cached rect for tile coords:%s alternative_id:%d", p_coords, p_alternative_tile));
return alternative_tiles_rect_cache[p_coords][p_alternative_tile];
}
void TileAtlasView::queue_redraw() {
base_tiles_draw->queue_redraw();
base_tiles_texture_grid->queue_redraw();
base_tiles_shape_grid->queue_redraw();
alternatives_draw->queue_redraw();
background_left->queue_redraw();
background_right->queue_redraw();
}
void TileAtlasView::_update_theme_item_cache() {
Control::_update_theme_item_cache();
theme_cache.center_view_icon = get_editor_theme_icon(SNAME("CenterView"));
theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
}
void TileAtlasView::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
break;
}
[[fallthrough]];
}
case NOTIFICATION_ENTER_TREE: {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
} break;
case NOTIFICATION_THEME_CHANGED: {
button_center_view->set_button_icon(theme_cache.center_view_icon);
} break;
}
}
void TileAtlasView::_bind_methods() {
ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
}
TileAtlasView::TileAtlasView() {
set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
Panel *panel = memnew(Panel);
panel->set_clip_contents(true);
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->set_h_size_flags(SIZE_EXPAND_FILL);
panel->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(panel);
zoom_widget = memnew(EditorZoomWidget);
add_child(zoom_widget);
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));
zoom_widget->set_shortcut_context(this);
button_center_view = memnew(Button);
button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5);
button_center_view->set_grow_direction_preset(Control::PRESET_TOP_RIGHT);
button_center_view->connect(SceneStringName(pressed), callable_mp(this, &TileAtlasView::_center_view));
button_center_view->set_flat(true);
button_center_view->set_disabled(true);
button_center_view->set_tooltip_text(TTR("Center View"));
add_child(button_center_view);
panner.instantiate();
panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
panner->set_enable_rmb(true);
center_container = memnew(CenterContainer);
center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
center_container->set_anchors_preset(Control::PRESET_CENTER);
center_container->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::gui_input));
center_container->connect(SceneStringName(focus_exited), callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
center_container->set_focus_mode(FOCUS_CLICK);
panel->add_child(center_container);
missing_source_label = memnew(Label);
missing_source_label->set_focus_mode(FOCUS_ACCESSIBILITY);
missing_source_label->set_text(TTR("The selected atlas source has no valid texture. Assign a texture in the TileSet bottom tab."));
center_container->add_child(missing_source_label);
margin_container = memnew(MarginContainer);
margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
center_container->add_child(margin_container);
hbox = memnew(HBoxContainer);
hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
hbox->add_theme_constant_override("separation", 10);
hbox->hide();
margin_container->add_child(hbox);
VBoxContainer *left_vbox = memnew(VBoxContainer);
left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
hbox->add_child(left_vbox);
VBoxContainer *right_vbox = memnew(VBoxContainer);
right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
hbox->add_child(right_vbox);
// Base tiles.
Label *base_tile_label = memnew(Label);
base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);
base_tile_label->set_text(TTR("Base Tiles"));
base_tile_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
left_vbox->add_child(base_tile_label);
base_tiles_root_control = memnew(Control);
base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
base_tiles_root_control->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));
left_vbox->add_child(base_tiles_root_control);
background_left = memnew(Control);
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_left->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_background_left));
base_tiles_root_control->add_child(background_left);
base_tiles_drawing_root = memnew(Control);
base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
base_tiles_root_control->add_child(base_tiles_drawing_root);
base_tiles_draw = memnew(Control);
base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_draw->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles));
base_tiles_drawing_root->add_child(base_tiles_draw);
base_tiles_texture_grid = memnew(Control);
base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_texture_grid->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
base_tiles_drawing_root->add_child(base_tiles_texture_grid);
base_tiles_shape_grid = memnew(Control);
base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_shape_grid->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_label->set_text(TTR("Alternative Tiles"));
alternative_tiles_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
right_vbox->add_child(alternative_tiles_label);
alternative_tiles_root_control = memnew(Control);
alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
alternative_tiles_root_control->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
right_vbox->add_child(alternative_tiles_root_control);
background_right = memnew(Control);
background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_right->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_background_right));
alternative_tiles_root_control->add_child(background_right);
alternative_tiles_drawing_root = memnew(Control);
alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
alternatives_draw = memnew(Control);
alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
alternatives_draw->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_alternatives));
alternative_tiles_drawing_root->add_child(alternatives_draw);
}
TileAtlasView::~TileAtlasView() {
_clear_material_canvas_items();
}

View File

@@ -0,0 +1,171 @@
/**************************************************************************/
/* tile_atlas_view.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/gui/editor_zoom_widget.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/center_container.h"
#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/2d/tile_set.h"
class ViewPanner;
class TileAtlasView : public Control {
GDCLASS(TileAtlasView, Control);
private:
Ref<TileSet> tile_set;
Ref<TileSetAtlasSource> tile_set_atlas_source;
int source_id = TileSet::INVALID_SOURCE;
enum DragType {
DRAG_TYPE_NONE,
DRAG_TYPE_PAN,
};
DragType drag_type = DRAG_TYPE_NONE;
float previous_zoom = 1.0;
EditorZoomWidget *zoom_widget = nullptr;
Button *button_center_view = nullptr;
CenterContainer *center_container = nullptr;
Vector2 panning;
void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false, const Vector2 &p_mouse_pos = Vector2());
void _zoom_widget_changed();
void _center_view();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
Ref<ViewPanner> panner;
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache;
void _update_alternative_tiles_rect_cache();
MarginContainer *margin_container = nullptr;
int margin_container_paddings[4] = { 0, 0, 0, 0 };
HBoxContainer *hbox = nullptr;
Label *missing_source_label = nullptr;
// Background
Control *background_left = nullptr;
void _draw_background_left();
Control *background_right = nullptr;
void _draw_background_right();
// Left side.
Control *base_tiles_root_control = nullptr;
void _base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
Control *base_tiles_drawing_root = nullptr;
Control *base_tiles_draw = nullptr;
HashMap<Ref<Material>, RID> material_tiles_draw;
HashMap<Ref<Material>, RID> material_alternatives_draw;
void _draw_base_tiles();
RID _get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map);
void _clear_material_canvas_items();
Control *base_tiles_texture_grid = nullptr;
void _draw_base_tiles_texture_grid();
Control *base_tiles_shape_grid = nullptr;
void _draw_base_tiles_shape_grid();
Size2i _compute_base_tiles_control_size();
// Right side.
Control *alternative_tiles_root_control = nullptr;
void _alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
Control *alternative_tiles_drawing_root = nullptr;
Control *alternatives_draw = nullptr;
void _draw_alternatives();
Size2i _compute_alternative_tiles_control_size();
struct ThemeCache {
Ref<Texture2D> center_view_icon;
Ref<Texture2D> checkerboard;
} theme_cache;
protected:
virtual void _update_theme_item_cache() override;
void _notification(int p_what);
static void _bind_methods();
public:
// Global.
void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
float get_zoom() const;
void set_transform(float p_zoom, Vector2i p_panning);
void set_padding(Side p_side, int p_padding);
// Left side.
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); }
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); }
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp = false) const;
void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
base_tiles_drawing_root->add_child(p_control);
} else {
base_tiles_root_control->add_child(p_control);
}
p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
}
// Right side.
Vector3i get_alternative_tile_at_pos(const Vector2 p_pos) const;
Rect2i get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile);
void add_control_over_alternative_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
alternative_tiles_drawing_root->add_child(p_control);
} else {
alternative_tiles_root_control->add_child(p_control);
}
p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
}
// Redraw everything.
void queue_redraw();
TileAtlasView();
~TileAtlasView();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,415 @@
/**************************************************************************/
/* tile_data_editors.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "tile_atlas_view.h"
#include "editor/inspector/editor_properties.h"
#include "scene/gui/box_container.h"
class Label;
class MenuButton;
class SpinBox;
class EditorUndoRedoManager;
class TileDataEditor : public VBoxContainer {
GDCLASS(TileDataEditor, VBoxContainer);
private:
bool _tile_set_changed_update_needed = false;
void _tile_set_changed_plan_update();
void _tile_set_changed_deferred_update();
protected:
Ref<TileSet> tile_set;
TileData *_get_tile_data(TileMapCell p_cell);
virtual void _tile_set_changed() {}
static void _bind_methods();
public:
void set_tile_set(Ref<TileSet> p_tile_set);
// Input to handle painting.
virtual Control *get_toolbar() { return nullptr; }
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) {}
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) {}
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) {}
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) {}
// Used to draw the tile data property value over a tile.
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) {}
};
class DummyObject : public Object {
GDCLASS(DummyObject, Object)
private:
HashMap<String, Variant> properties;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
public:
bool has_dummy_property(const StringName &p_name);
void add_dummy_property(const StringName &p_name);
void remove_dummy_property(const StringName &p_name);
void clear_dummy_properties();
};
class GenericTilePolygonEditor : public VBoxContainer {
GDCLASS(GenericTilePolygonEditor, VBoxContainer);
private:
Ref<TileSet> tile_set;
LocalVector<Vector<Point2>> polygons;
bool multiple_polygon_mode = false;
bool use_undo_redo = true;
// UI
int hovered_polygon_index = -1;
int hovered_point_index = -1;
int hovered_segment_index = -1;
Vector2 hovered_segment_point;
enum DragType {
DRAG_TYPE_NONE,
DRAG_TYPE_DRAG_POINT,
DRAG_TYPE_CREATE_POINT,
DRAG_TYPE_PAN,
};
DragType drag_type = DRAG_TYPE_NONE;
int drag_polygon_index = 0;
int drag_point_index = 0;
Vector2 drag_last_pos;
PackedVector2Array drag_old_polygon;
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tools_button_group;
Button *button_expand = nullptr;
Button *button_create = nullptr;
Button *button_edit = nullptr;
Button *button_delete = nullptr;
MenuButton *button_advanced_menu = nullptr;
enum Snap {
SNAP_NONE,
SNAP_HALF_PIXEL,
SNAP_GRID,
};
int current_snap_option = SNAP_HALF_PIXEL;
MenuButton *button_pixel_snap = nullptr;
SpinBox *snap_subdivision = nullptr;
Vector<Point2> in_creation_polygon;
Panel *panel = nullptr;
Control *base_control = nullptr;
EditorZoomWidget *editor_zoom_widget = nullptr;
Button *button_center_view = nullptr;
Vector2 panning;
bool initializing = true;
Ref<TileSetAtlasSource> background_atlas_source;
Vector2i background_atlas_coords;
int background_alternative_id;
Color polygon_color = Color(1.0, 0.0, 0.0);
enum AdvancedMenuOption {
RESET_TO_DEFAULT_TILE,
CLEAR_TILE,
ROTATE_RIGHT,
ROTATE_LEFT,
FLIP_HORIZONTALLY,
FLIP_VERTICALLY,
};
void _base_control_draw();
void _zoom_changed();
void _advanced_menu_item_pressed(int p_item_pressed);
void _center_view();
void _base_control_gui_input(Ref<InputEvent> p_event);
void _set_snap_option(int p_index);
void _store_snap_options();
void _toggle_expand(bool p_expand);
void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist);
void _snap_point(Point2 &r_point);
void _grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index);
void _grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void set_use_undo_redo(bool p_use_undo_redo);
void set_tile_set(Ref<TileSet> p_tile_set);
void set_background_tile(const TileSetAtlasSource *p_atlas_source, const Vector2 &p_atlas_coords, int p_alternative_id);
int get_polygon_count();
int add_polygon(const Vector<Point2> &p_polygon, int p_index = -1);
void remove_polygon(int p_index);
void clear_polygons();
void set_polygon(int p_polygon_index, const Vector<Point2> &p_polygon);
Vector<Point2> get_polygon(int p_polygon_index);
void set_polygons_color(Color p_color);
void set_multiple_polygon_mode(bool p_multiple_polygon_mode);
GenericTilePolygonEditor();
};
class TileDataDefaultEditor : public TileDataEditor {
GDCLASS(TileDataDefaultEditor, TileDataEditor);
private:
// Toolbar
HBoxContainer *toolbar = memnew(HBoxContainer);
Button *picker_button = nullptr;
// UI
Ref<Texture2D> tile_bool_checked;
Ref<Texture2D> tile_bool_unchecked;
Label *label = nullptr;
EditorProperty *property_editor = nullptr;
// Painting state.
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
DRAG_TYPE_PAINT_RECT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2 drag_start_pos;
Vector2 drag_last_pos;
HashMap<TileMapCell, Variant, TileMapCell> drag_modified;
Variant drag_painted_value;
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
protected:
DummyObject *dummy_object = memnew(DummyObject);
StringName type;
String property;
Variant::Type property_type;
void _notification(int p_what);
virtual Variant _get_painted_value();
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile);
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value);
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile);
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value);
public:
virtual Control *get_toolbar() override { return toolbar; }
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void setup_property_editor(Variant::Type p_type, const String &p_property, const String &p_label = "", const Variant &p_default_value = Variant());
Variant::Type get_property_type();
TileDataDefaultEditor();
~TileDataDefaultEditor();
};
class TileDataTextureOriginEditor : public TileDataDefaultEditor {
GDCLASS(TileDataTextureOriginEditor, TileDataDefaultEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
};
class TileDataPositionEditor : public TileDataDefaultEditor {
GDCLASS(TileDataPositionEditor, TileDataDefaultEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
};
class TileDataYSortEditor : public TileDataDefaultEditor {
GDCLASS(TileDataYSortEditor, TileDataDefaultEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
};
class TileDataOcclusionShapeEditor : public TileDataDefaultEditor {
GDCLASS(TileDataOcclusionShapeEditor, TileDataDefaultEditor);
private:
int occlusion_layer = -1;
// UI
GenericTilePolygonEditor *polygon_editor = nullptr;
void _polygon_changed(const PackedVector2Array &p_polygon);
virtual Variant _get_painted_value() override;
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void set_occlusion_layer(int p_occlusion_layer) { occlusion_layer = p_occlusion_layer; }
TileDataOcclusionShapeEditor();
};
class TileDataCollisionEditor : public TileDataDefaultEditor {
GDCLASS(TileDataCollisionEditor, TileDataDefaultEditor);
int physics_layer = -1;
// UI
GenericTilePolygonEditor *polygon_editor = nullptr;
DummyObject *dummy_object = memnew(DummyObject);
HashMap<StringName, EditorProperty *> property_editors;
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
void _property_selected(const StringName &p_path, int p_focusable);
void _polygons_changed();
virtual Variant _get_painted_value() override;
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void set_physics_layer(int p_physics_layer) { physics_layer = p_physics_layer; }
TileDataCollisionEditor();
~TileDataCollisionEditor();
};
class TileDataTerrainsEditor : public TileDataEditor {
GDCLASS(TileDataTerrainsEditor, TileDataEditor);
private:
// Toolbar
HBoxContainer *toolbar = memnew(HBoxContainer);
Button *picker_button = nullptr;
// Painting state.
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT_TERRAIN_SET,
DRAG_TYPE_PAINT_TERRAIN_SET_RECT,
DRAG_TYPE_PAINT_TERRAIN_BITS,
DRAG_TYPE_PAINT_TERRAIN_BITS_RECT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2 drag_start_pos;
Vector2 drag_last_pos;
HashMap<TileMapCell, Variant, TileMapCell> drag_modified;
Variant drag_painted_value;
// UI
Label *label = nullptr;
DummyObject *dummy_object = memnew(DummyObject);
EditorPropertyEnum *terrain_set_property_editor = nullptr;
EditorPropertyEnum *terrain_property_editor = nullptr;
void _property_value_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field);
void _update_terrain_selector();
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual Control *get_toolbar() override { return toolbar; }
virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override;
virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override;
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
TileDataTerrainsEditor();
~TileDataTerrainsEditor();
};
class TileDataNavigationEditor : public TileDataDefaultEditor {
GDCLASS(TileDataNavigationEditor, TileDataDefaultEditor);
private:
int navigation_layer = -1;
PackedVector2Array navigation_polygon;
// UI
GenericTilePolygonEditor *polygon_editor = nullptr;
void _polygon_changed(const PackedVector2Array &p_polygon);
virtual Variant _get_painted_value() override;
virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) override;
virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override;
virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value) override;
protected:
virtual void _tile_set_changed() override;
void _notification(int p_what);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;
void set_navigation_layer(int p_navigation_layer) { navigation_layer = p_navigation_layer; }
TileDataNavigationEditor();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,427 @@
/**************************************************************************/
/* tile_map_layer_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "tile_atlas_view.h"
#include "core/os/thread.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_bar.h"
#include "scene/gui/tree.h"
class TileMapLayer;
class TileMapLayerEditor;
class TileMapLayerSubEditorPlugin : public Object {
protected:
ObjectID edited_tile_map_layer_id;
TileMapLayer *_get_edited_layer() const;
public:
struct TabData {
Control *toolbar = nullptr;
Control *panel = nullptr;
};
virtual Vector<TabData> get_tabs() const {
return Vector<TabData>();
}
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; }
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) {}
virtual void tile_set_changed() {}
virtual void edit(ObjectID p_tile_map_layer_id) {}
virtual void draw_tile_coords_over_viewport(Control *p_overlay, const TileMapLayer *p_edited_layer, Ref<TileSet> p_tile_set, bool p_show_rectangle_size, const Vector2i &p_rectangle_origin);
};
class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
public:
enum TileTransformType {
TRANSFORM_ROTATE_LEFT,
TRANSFORM_ROTATE_RIGHT,
TRANSFORM_FLIP_H,
TRANSFORM_FLIP_V,
};
private:
///// Toolbar /////
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tool_buttons_group;
Button *select_tool_button = nullptr;
Button *paint_tool_button = nullptr;
Button *line_tool_button = nullptr;
Button *rect_tool_button = nullptr;
Button *bucket_tool_button = nullptr;
HBoxContainer *tools_settings = nullptr;
VSeparator *tools_settings_vsep = nullptr;
Button *picker_button = nullptr;
Button *erase_button = nullptr;
HBoxContainer *transform_toolbar = nullptr;
Button *transform_button_rotate_left = nullptr;
Button *transform_button_rotate_right = nullptr;
Button *transform_button_flip_h = nullptr;
Button *transform_button_flip_v = nullptr;
VSeparator *tools_settings_vsep_2 = nullptr;
CheckBox *bucket_contiguous_checkbox = nullptr;
Button *random_tile_toggle = nullptr;
HBoxContainer *scatter_controls_container = nullptr;
float scattering = 0.0;
Label *scatter_label = nullptr;
SpinBox *scatter_spinbox = nullptr;
void _on_random_tile_checkbox_toggled(bool p_pressed);
void _on_scattering_spinbox_changed(double p_value);
void _update_toolbar();
void _update_transform_buttons();
void _set_transform_buttons_state(const Vector<Button *> &p_enabled_buttons, const Vector<Button *> &p_disabled_buttons, const String &p_why_disabled);
///// Tilemap editing. /////
bool has_mouse = false;
void _mouse_exited_viewport();
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_SELECT,
DRAG_TYPE_MOVE,
DRAG_TYPE_PAINT,
DRAG_TYPE_LINE,
DRAG_TYPE_RECT,
DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
DRAG_TYPE_CLIPBOARD_PASTE,
};
DragType drag_type = DRAG_TYPE_NONE;
bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
HashMap<Vector2i, TileMapCell> drag_modified;
TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern);
HashMap<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
void _apply_transform(TileTransformType p_type);
int _get_transformed_alternative(int p_alternative_id, TileTransformType p_transform);
///// Selection system. /////
RBSet<Vector2i> tile_map_selection;
Ref<TileMapPattern> tile_map_clipboard;
Ref<TileMapPattern> selection_pattern;
Ref<TileMapPattern> erase_pattern;
void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
TypedArray<Vector2i> _get_tile_map_selection() const;
RBSet<TileMapCell> tile_set_selection;
void _update_selection_pattern_from_tilemap_selection();
void _update_selection_pattern_from_tileset_tiles_selection();
void _update_selection_pattern_from_tileset_pattern_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
void _fix_invalid_tiles_in_tile_map_selection();
void patterns_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
///// Bottom panel common ////
void _tab_changed();
///// Bottom panel tiles ////
VBoxContainer *tiles_bottom_panel = nullptr;
Label *missing_source_label = nullptr;
Label *invalid_source_label = nullptr;
ItemList *sources_list = nullptr;
MenuButton *source_sort_button = nullptr;
Ref<Texture2D> missing_atlas_texture_icon;
void _update_tile_set_sources_list();
void _update_source_display();
// Atlas sources.
TileMapCell hovered_tile;
TileAtlasView *tile_atlas_view = nullptr;
HSplitContainer *atlas_sources_split_container = nullptr;
bool tile_set_dragging_selection = false;
Vector2i tile_set_drag_start_mouse_pos;
Control *tile_atlas_control = nullptr;
void _tile_atlas_control_mouse_exited();
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
void _tile_atlas_control_draw();
Control *alternative_tiles_control = nullptr;
void _tile_alternatives_control_draw();
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
void _update_atlas_view();
void _set_source_sort(int p_sort);
// Scenes collection sources.
ItemList *scene_tiles_list = nullptr;
void _update_scenes_collection_view();
void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud);
void _scenes_list_multi_selected(int p_index, bool p_selected);
void _scenes_list_lmb_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
///// Bottom panel patterns ////
VBoxContainer *patterns_bottom_panel = nullptr;
ItemList *patterns_item_list = nullptr;
Label *patterns_help_label = nullptr;
void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
bool select_last_pattern = false;
void _update_patterns_list();
// General
void _update_theme();
List<BaseButton *> viewport_shortcut_buttons;
// Update callback
virtual void tile_set_changed() override;
protected:
static void _bind_methods();
public:
virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
virtual void edit(ObjectID p_tile_map_layer_id) override;
TileMapLayerEditorTilesPlugin();
};
class TileMapLayerEditorTerrainsPlugin : public TileMapLayerSubEditorPlugin {
GDCLASS(TileMapLayerEditorTerrainsPlugin, TileMapLayerSubEditorPlugin);
private:
// Toolbar.
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tool_buttons_group;
Button *paint_tool_button = nullptr;
Button *line_tool_button = nullptr;
Button *rect_tool_button = nullptr;
Button *bucket_tool_button = nullptr;
HBoxContainer *tools_settings = nullptr;
VSeparator *tools_settings_vsep = nullptr;
Button *picker_button = nullptr;
Button *erase_button = nullptr;
VSeparator *tools_settings_vsep_2 = nullptr;
CheckBox *bucket_contiguous_checkbox = nullptr;
void _update_toolbar();
// Main vbox.
VBoxContainer *main_vbox_container = nullptr;
// TileMap editing.
bool has_mouse = false;
void _mouse_exited_viewport();
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
DRAG_TYPE_LINE,
DRAG_TYPE_RECT,
DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
};
DragType drag_type = DRAG_TYPE_NONE;
bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
HashMap<Vector2i, TileMapCell> drag_modified;
// Painting
HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const;
HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
enum SelectedType {
SELECTED_TYPE_CONNECT = 0,
SELECTED_TYPE_PATH,
SELECTED_TYPE_PATTERN,
};
SelectedType selected_type;
int selected_terrain_set = -1;
int selected_terrain = -1;
TileSet::TerrainsPattern selected_terrains_pattern;
void _update_selection();
// Bottom panel.
Tree *terrains_tree = nullptr;
ItemList *terrains_tile_list = nullptr;
// Cache.
LocalVector<LocalVector<RBSet<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
List<BaseButton *> viewport_shortcut_buttons;
// Update functions.
void _update_terrains_cache();
void _update_terrains_tree();
void _update_tiles_list();
void _update_theme();
// Update callback
virtual void tile_set_changed() override;
public:
virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
virtual void edit(ObjectID p_tile_map_layer_id) override;
TileMapLayerEditorTerrainsPlugin();
};
class TileMapLayerEditor : public VBoxContainer {
GDCLASS(TileMapLayerEditor, VBoxContainer);
private:
bool tile_map_layer_changed_needs_update = false;
ObjectID edited_tile_map_layer_id;
bool is_multi_node_edit = false;
Vector<TileMapLayer *> tile_map_layers_in_scene_cache;
bool layers_in_scene_list_cache_needs_update = false;
TileMapLayer *_get_edited_layer() const;
void _find_tile_map_layers_in_scene(Node *p_current, const Node *p_owner, Vector<TileMapLayer *> &r_list) const;
void _update_tile_map_layers_in_scene_list_cache();
void _node_change(Node *p_node);
Control *custom_overlay = nullptr;
void _draw_overlay();
// Vector to keep plugins.
Vector<TileMapLayerSubEditorPlugin *> tile_map_editor_plugins;
// Toolbar.
HFlowContainer *tile_map_toolbar = nullptr;
bool show_layers_selector = false;
HBoxContainer *layer_selection_hbox = nullptr;
Button *select_previous_layer = nullptr;
void _select_previous_layer_pressed();
Button *select_next_layer = nullptr;
void _select_next_layer_pressed();
Button *select_all_layers = nullptr;
void _select_all_layers_pressed();
OptionButton *layers_selection_button = nullptr;
void _layers_selection_item_selected(int p_index);
void _update_layers_selector();
Button *toggle_highlight_selected_layer_button = nullptr;
void _clear_all_layers_highlighting();
void _update_all_layers_highlighting();
void _highlight_selected_layer_button_toggled(bool p_pressed);
Button *toggle_grid_button = nullptr;
void _on_grid_toggled(bool p_pressed);
enum {
ADVANCED_MENU_REPLACE_WITH_PROXIES,
ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS,
};
MenuButton *advanced_menu_button = nullptr;
void _advanced_menu_button_id_pressed(int p_id);
// Bottom panel.
Label *cant_edit_label = nullptr;
TabBar *tabs_bar = nullptr;
LocalVector<TileMapLayerSubEditorPlugin::TabData> tabs_data;
LocalVector<TileMapLayerSubEditorPlugin *> tabs_plugins;
void _update_bottom_panel();
// TileMap.
Ref<Texture2D> missing_tile_texture;
Ref<Texture2D> warning_pattern_texture;
// CallBack.
void _tile_map_layer_changed();
void _tab_changed(int p_tab_changed);
// Updates.
void _layers_select_next_or_previous(bool p_next);
// Inspector undo/redo callback.
void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
protected:
void _notification(int p_what);
void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color);
public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(Object *p_tile_map_layer);
void set_show_layer_selector(bool p_show_layer_selector);
TileMapLayerEditor();
~TileMapLayerEditor();
// Static functions.
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
};
VARIANT_ENUM_CAST(TileMapLayerEditorTilesPlugin::TileTransformType);

View File

@@ -0,0 +1,492 @@
/**************************************************************************/
/* tile_proxies_manager_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_proxies_manager_dialog.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector/editor_properties_vector.h"
#include "editor/settings/editor_settings.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/separator.h"
void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list) {
if (p_mouse_button_index != MouseButton::RIGHT) {
return;
}
ItemList *item_list = Object::cast_to<ItemList>(p_item_list);
popup_menu->reset_size();
popup_menu->set_position(get_position() + item_list->get_global_mouse_position());
popup_menu->popup();
}
void TileProxiesManagerDialog::_menu_id_pressed(int p_id) {
if (p_id == 0) {
// Delete.
_delete_selected_bindings();
}
}
void TileProxiesManagerDialog::_delete_selected_bindings() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Tile Proxies"));
Vector<int> source_level_selected = source_level_list->get_selected_items();
for (int i = 0; i < source_level_selected.size(); i++) {
int key = source_level_list->get_item_metadata(source_level_selected[i]);
int val = tile_set->get_source_level_tile_proxy(key);
undo_redo->add_do_method(*tile_set, "remove_source_level_tile_proxy", key);
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", key, val);
}
Vector<int> coords_level_selected = coords_level_list->get_selected_items();
for (int i = 0; i < coords_level_selected.size(); i++) {
Array key = coords_level_list->get_item_metadata(coords_level_selected[i]);
Array val = tile_set->get_coords_level_tile_proxy(key[0], key[1]);
undo_redo->add_do_method(*tile_set, "remove_coords_level_tile_proxy", key[0], key[1]);
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", key[0], key[1], val[0], val[1]);
}
Vector<int> alternative_level_selected = alternative_level_list->get_selected_items();
for (int i = 0; i < alternative_level_selected.size(); i++) {
Array key = alternative_level_list->get_item_metadata(alternative_level_selected[i]);
Array val = tile_set->get_alternative_level_tile_proxy(key[0], key[1], key[2]);
undo_redo->add_do_method(*tile_set, "remove_alternative_level_tile_proxy", key[0], key[1], key[2]);
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", key[0], key[1], key[2], val[0], val[1], val[2]);
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
committed_actions_count += 1;
}
void TileProxiesManagerDialog::_update_lists() {
source_level_list->clear();
coords_level_list->clear();
alternative_level_list->clear();
Array proxies = tile_set->get_source_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
String text = vformat("%s", proxy[0]).rpad(5) + "-> " + vformat("%s", proxy[1]);
int id = source_level_list->add_item(text);
source_level_list->set_item_metadata(id, proxy[0]);
}
proxies = tile_set->get_coords_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
String text = vformat("%s, %s", proxy[0], proxy[1]).rpad(17) + "-> " + vformat("%s, %s", proxy[2], proxy[3]);
int id = coords_level_list->add_item(text);
coords_level_list->set_item_metadata(id, proxy.slice(0, 2));
}
proxies = tile_set->get_alternative_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
String text = vformat("%s, %s, %s", proxy[0], proxy[1], proxy[2]).rpad(24) + "-> " + vformat("%s, %s, %s", proxy[3], proxy[4], proxy[5]);
int id = alternative_level_list->add_item(text);
alternative_level_list->set_item_metadata(id, proxy.slice(0, 3));
}
}
void TileProxiesManagerDialog::_update_enabled_property_editors() {
if (from.source_id == TileSet::INVALID_SOURCE) {
from.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
to.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
coords_from_property_editor->hide();
coords_to_property_editor->hide();
alternative_from_property_editor->hide();
alternative_to_property_editor->hide();
} else if (from.get_atlas_coords().x == -1 || from.get_atlas_coords().y == -1) {
from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
coords_from_property_editor->show();
coords_to_property_editor->show();
alternative_from_property_editor->hide();
alternative_to_property_editor->hide();
} else {
coords_from_property_editor->show();
coords_to_property_editor->show();
alternative_from_property_editor->show();
alternative_to_property_editor->show();
}
source_from_property_editor->update_property();
source_to_property_editor->update_property();
coords_from_property_editor->update_property();
coords_to_property_editor->update_property();
alternative_from_property_editor->update_property();
alternative_to_property_editor->update_property();
}
void TileProxiesManagerDialog::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing) {
_set(p_path, p_value);
}
void TileProxiesManagerDialog::_add_button_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (from.source_id != TileSet::INVALID_SOURCE && to.source_id != TileSet::INVALID_SOURCE) {
Vector2i from_coords = from.get_atlas_coords();
Vector2i to_coords = to.get_atlas_coords();
if (from_coords.x >= 0 && from_coords.y >= 0 && to_coords.x >= 0 && to_coords.y >= 0) {
if (from.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE && to.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE) {
undo_redo->create_action(TTR("Create Alternative-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile, to.source_id, to.get_atlas_coords(), to.alternative_tile);
if (tile_set->has_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile)) {
Array a = tile_set->get_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", to.source_id, to.get_atlas_coords(), to.alternative_tile, a[0], a[1], a[2]);
} else {
undo_redo->add_undo_method(*tile_set, "remove_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile);
}
} else {
undo_redo->create_action(TTR("Create Coords-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", from.source_id, from.get_atlas_coords(), to.source_id, to.get_atlas_coords());
if (tile_set->has_coords_level_tile_proxy(from.source_id, from.get_atlas_coords())) {
Array a = tile_set->get_coords_level_tile_proxy(from.source_id, from.get_atlas_coords());
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", to.source_id, to.get_atlas_coords(), a[0], a[1]);
} else {
undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", from.source_id, from.get_atlas_coords());
}
}
} else {
undo_redo->create_action(TTR("Create source-level Tile Proxy"));
undo_redo->add_do_method(*tile_set, "set_source_level_tile_proxy", from.source_id, to.source_id);
if (tile_set->has_source_level_tile_proxy(from.source_id)) {
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", to.source_id, tile_set->get_source_level_tile_proxy(from.source_id));
} else {
undo_redo->add_undo_method(*tile_set, "remove_source_level_tile_proxy", from.source_id);
}
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
committed_actions_count++;
}
}
void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete All Invalid Tile Proxies"));
undo_redo->add_do_method(*tile_set, "cleanup_invalid_tile_proxies");
Array proxies = tile_set->get_source_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]);
}
proxies = tile_set->get_coords_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]);
}
proxies = tile_set->get_alternative_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]);
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
}
void TileProxiesManagerDialog::_clear_all_button_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete All Tile Proxies"));
undo_redo->add_do_method(*tile_set, "clear_tile_proxies");
Array proxies = tile_set->get_source_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]);
}
proxies = tile_set->get_coords_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]);
}
proxies = tile_set->get_alternative_level_tile_proxies();
for (int i = 0; i < proxies.size(); i++) {
Array proxy = proxies[i];
undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]);
}
undo_redo->add_do_method(this, "_update_lists");
undo_redo->add_undo_method(this, "_update_lists");
undo_redo->commit_action();
}
bool TileProxiesManagerDialog::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "from_source") {
from.source_id = MAX(int(p_value), -1);
} else if (p_name == "from_coords") {
from.set_atlas_coords(Vector2i(p_value).maxi(-1));
} else if (p_name == "from_alternative") {
from.alternative_tile = MAX(int(p_value), -1);
} else if (p_name == "to_source") {
to.source_id = MAX(int(p_value), 0);
} else if (p_name == "to_coords") {
to.set_atlas_coords(Vector2i(p_value).maxi(0));
} else if (p_name == "to_alternative") {
to.alternative_tile = MAX(int(p_value), 0);
} else {
return false;
}
_update_enabled_property_editors();
return true;
}
bool TileProxiesManagerDialog::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "from_source") {
r_ret = from.source_id;
} else if (p_name == "from_coords") {
r_ret = from.get_atlas_coords();
} else if (p_name == "from_alternative") {
r_ret = from.alternative_tile;
} else if (p_name == "to_source") {
r_ret = to.source_id;
} else if (p_name == "to_coords") {
r_ret = to.get_atlas_coords();
} else if (p_name == "to_alternative") {
r_ret = to.alternative_tile;
} else {
return false;
}
return true;
}
void TileProxiesManagerDialog::_unhandled_key_input(Ref<InputEvent> p_event) {
ERR_FAIL_COND(p_event.is_null());
if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event))) {
if (!is_inside_tree() || !is_visible()) {
return;
}
if (popup_menu->activate_item_by_event(p_event, false)) {
set_input_as_handled();
}
}
}
void TileProxiesManagerDialog::cancel_pressed() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < committed_actions_count; i++) {
undo_redo->undo();
}
committed_actions_count = 0;
}
void TileProxiesManagerDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_lists"), &TileProxiesManagerDialog::_update_lists);
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TileProxiesManagerDialog::_unhandled_key_input);
}
void TileProxiesManagerDialog::update_tile_set(Ref<TileSet> p_tile_set) {
ERR_FAIL_COND(p_tile_set.is_null());
tile_set = p_tile_set;
committed_actions_count = 0;
_update_lists();
}
TileProxiesManagerDialog::TileProxiesManagerDialog() {
// Tile proxy management window.
set_title(TTR("Tile Proxies Management"));
set_process_unhandled_key_input(true);
to.source_id = 0;
to.set_atlas_coords(Vector2i());
to.alternative_tile = 0;
VBoxContainer *vbox_container = memnew(VBoxContainer);
vbox_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vbox_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
add_child(vbox_container);
Label *source_level_label = memnew(Label);
source_level_label->set_text(TTR("Source-level proxies"));
vbox_container->add_child(source_level_label);
source_level_list = memnew(ItemList);
source_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
source_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
source_level_list->set_select_mode(ItemList::SELECT_MULTI);
source_level_list->set_allow_rmb_select(true);
source_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(source_level_list));
vbox_container->add_child(source_level_list);
Label *coords_level_label = memnew(Label);
coords_level_label->set_text(TTR("Coords-level proxies"));
vbox_container->add_child(coords_level_label);
coords_level_list = memnew(ItemList);
coords_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
coords_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
coords_level_list->set_select_mode(ItemList::SELECT_MULTI);
coords_level_list->set_allow_rmb_select(true);
coords_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(coords_level_list));
vbox_container->add_child(coords_level_list);
Label *alternative_level_label = memnew(Label);
alternative_level_label->set_text(TTR("Alternative-level proxies"));
vbox_container->add_child(alternative_level_label);
alternative_level_list = memnew(ItemList);
alternative_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
alternative_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
alternative_level_list->set_select_mode(ItemList::SELECT_MULTI);
alternative_level_list->set_allow_rmb_select(true);
alternative_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked).bind(alternative_level_list));
vbox_container->add_child(alternative_level_list);
popup_menu = memnew(PopupMenu);
popup_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_delete"));
popup_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TileProxiesManagerDialog::_menu_id_pressed));
add_child(popup_menu);
// Add proxy panel.
HSeparator *h_separator = memnew(HSeparator);
vbox_container->add_child(h_separator);
Label *add_label = memnew(Label);
add_label->set_text(TTR("Add a new tile proxy:"));
vbox_container->add_child(add_label);
HBoxContainer *hboxcontainer = memnew(HBoxContainer);
vbox_container->add_child(hboxcontainer);
// From
VBoxContainer *vboxcontainer_from = memnew(VBoxContainer);
vboxcontainer_from->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hboxcontainer->add_child(vboxcontainer_from);
source_from_property_editor = memnew(EditorPropertyInteger);
source_from_property_editor->set_label(TTR("From Source"));
source_from_property_editor->set_object_and_property(this, "from_source");
source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
source_from_property_editor->set_selectable(false);
source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
source_from_property_editor->setup(-1, 99999, 1, false, true, false);
vboxcontainer_from->add_child(source_from_property_editor);
coords_from_property_editor = memnew(EditorPropertyVector2i);
coords_from_property_editor->set_label(TTR("From Coords"));
coords_from_property_editor->set_object_and_property(this, "from_coords");
coords_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
coords_from_property_editor->set_selectable(false);
coords_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
coords_from_property_editor->setup(-1, 99999, true);
coords_from_property_editor->hide();
vboxcontainer_from->add_child(coords_from_property_editor);
alternative_from_property_editor = memnew(EditorPropertyInteger);
alternative_from_property_editor->set_label(TTR("From Alternative"));
alternative_from_property_editor->set_object_and_property(this, "from_alternative");
alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
alternative_from_property_editor->set_selectable(false);
alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
alternative_from_property_editor->setup(-1, 99999, 1, false, true, false);
alternative_from_property_editor->hide();
vboxcontainer_from->add_child(alternative_from_property_editor);
// To
VBoxContainer *vboxcontainer_to = memnew(VBoxContainer);
vboxcontainer_to->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hboxcontainer->add_child(vboxcontainer_to);
source_to_property_editor = memnew(EditorPropertyInteger);
source_to_property_editor->set_label(TTR("To Source"));
source_to_property_editor->set_object_and_property(this, "to_source");
source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
source_to_property_editor->set_selectable(false);
source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
source_to_property_editor->setup(-1, 99999, 1, false, true, false);
vboxcontainer_to->add_child(source_to_property_editor);
coords_to_property_editor = memnew(EditorPropertyVector2i);
coords_to_property_editor->set_label(TTR("To Coords"));
coords_to_property_editor->set_object_and_property(this, "to_coords");
coords_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
coords_to_property_editor->set_selectable(false);
coords_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
coords_to_property_editor->setup(-1, 99999, true);
coords_to_property_editor->hide();
vboxcontainer_to->add_child(coords_to_property_editor);
alternative_to_property_editor = memnew(EditorPropertyInteger);
alternative_to_property_editor->set_label(TTR("To Alternative"));
alternative_to_property_editor->set_object_and_property(this, "to_alternative");
alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
alternative_to_property_editor->set_selectable(false);
alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
alternative_to_property_editor->setup(-1, 99999, 1, false, true, false);
alternative_to_property_editor->hide();
vboxcontainer_to->add_child(alternative_to_property_editor);
Button *add_button = memnew(Button);
add_button->set_text(TTR("Add"));
add_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
add_button->connect(SceneStringName(pressed), callable_mp(this, &TileProxiesManagerDialog::_add_button_pressed));
vbox_container->add_child(add_button);
h_separator = memnew(HSeparator);
vbox_container->add_child(h_separator);
// Generic actions.
Label *generic_actions_label = memnew(Label);
generic_actions_label->set_text(TTR("Global actions:"));
vbox_container->add_child(generic_actions_label);
hboxcontainer = memnew(HBoxContainer);
vbox_container->add_child(hboxcontainer);
Button *clear_invalid_button = memnew(Button);
clear_invalid_button->set_text(TTR("Clear Invalid"));
clear_invalid_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
clear_invalid_button->connect(SceneStringName(pressed), callable_mp(this, &TileProxiesManagerDialog::_clear_invalid_button_pressed));
hboxcontainer->add_child(clear_invalid_button);
Button *clear_all_button = memnew(Button);
clear_all_button->set_text(TTR("Clear All"));
clear_all_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
clear_all_button->connect(SceneStringName(pressed), callable_mp(this, &TileProxiesManagerDialog::_clear_all_button_pressed));
hboxcontainer->add_child(clear_all_button);
h_separator = memnew(HSeparator);
vbox_container->add_child(h_separator);
}

View File

@@ -0,0 +1,86 @@
/**************************************************************************/
/* tile_proxies_manager_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_properties.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/resources/2d/tile_set.h"
class EditorPropertyVector2i;
class EditorUndoRedoManager;
class TileProxiesManagerDialog : public ConfirmationDialog {
GDCLASS(TileProxiesManagerDialog, ConfirmationDialog);
private:
int committed_actions_count = 0;
Ref<TileSet> tile_set;
TileMapCell from;
TileMapCell to;
// GUI
ItemList *source_level_list = nullptr;
ItemList *coords_level_list = nullptr;
ItemList *alternative_level_list = nullptr;
EditorPropertyInteger *source_from_property_editor = nullptr;
EditorPropertyVector2i *coords_from_property_editor = nullptr;
EditorPropertyInteger *alternative_from_property_editor = nullptr;
EditorPropertyInteger *source_to_property_editor = nullptr;
EditorPropertyVector2i *coords_to_property_editor = nullptr;
EditorPropertyInteger *alternative_to_property_editor = nullptr;
PopupMenu *popup_menu = nullptr;
void _right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list);
void _menu_id_pressed(int p_id);
void _delete_selected_bindings();
void _update_lists();
void _update_enabled_property_editors();
void _property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing);
void _add_button_pressed();
void _clear_invalid_button_pressed();
void _clear_all_button_pressed();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _unhandled_key_input(Ref<InputEvent> p_event);
virtual void cancel_pressed() override;
static void _bind_methods();
public:
void update_tile_set(Ref<TileSet> p_tile_set);
TileProxiesManagerDialog();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
/**************************************************************************/
/* tile_set_atlas_source_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "tile_atlas_view.h"
#include "tile_data_editors.h"
#include "scene/gui/split_container.h"
#include "scene/resources/2d/tile_set.h"
class Popup;
class TileSet;
class Tree;
class VSeparator;
class TileSetAtlasSourceEditor : public HSplitContainer {
GDCLASS(TileSetAtlasSourceEditor, HSplitContainer);
public:
// A class to store which tiles are selected.
struct TileSelection {
Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS;
int alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
bool operator<(const TileSelection &p_other) const {
if (tile == p_other.tile) {
return alternative < p_other.alternative;
} else {
return tile < p_other.tile;
}
}
};
// -- Proxy object for an atlas source, needed by the inspector --
class TileSetAtlasSourceProxyObject : public Object {
GDCLASS(TileSetAtlasSourceProxyObject, Object);
private:
Ref<TileSet> tile_set;
Ref<TileSetAtlasSource> tile_set_atlas_source;
int source_id = TileSet::INVALID_SOURCE;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
void set_id(int p_id);
int get_id() const;
void edit(Ref<TileSet> p_tile_set, Ref<TileSetAtlasSource> p_tile_set_atlas_source, int p_source_id);
Ref<TileSetAtlasSource> get_edited() { return tile_set_atlas_source; }
};
// -- Proxy object for a tile, needed by the inspector --
class AtlasTileProxyObject : public Object {
GDCLASS(AtlasTileProxyObject, Object);
private:
TileSetAtlasSourceEditor *tiles_set_atlas_source_editor = nullptr;
Ref<TileSetAtlasSource> tile_set_atlas_source;
RBSet<TileSelection> tiles;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
Ref<TileSetAtlasSource> get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; }
RBSet<TileSelection> get_edited_tiles() const { return tiles; }
// Update the proxied object.
void edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, const RBSet<TileSelection> &p_tiles = RBSet<TileSelection>());
AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) {
tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor;
}
};
class TileAtlasControl : public Control {
TileSetAtlasSourceEditor *editor = nullptr;
public:
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
TileAtlasControl(TileSetAtlasSourceEditor *p_editor) { editor = p_editor; }
};
friend class TileAtlasControl;
private:
bool read_only = false;
Ref<TileSet> tile_set;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int tile_set_atlas_source_id = TileSet::INVALID_SOURCE;
Ref<Texture2D> atlas_source_texture;
bool tile_set_changed_needs_update = false;
// -- Properties painting --
ScrollContainer *tile_data_editors_scroll = nullptr;
VBoxContainer *tile_data_painting_editor_container = nullptr;
Label *tile_data_editors_label = nullptr;
Button *tile_data_editor_dropdown_button = nullptr;
Popup *tile_data_editors_popup = nullptr;
Tree *tile_data_editors_tree = nullptr;
void _tile_data_editor_dropdown_button_draw();
void _tile_data_editor_dropdown_button_pressed();
// -- Tile data editors --
String current_property;
HashMap<String, TileDataEditor *> tile_data_editors;
TileDataEditor *current_tile_data_editor = nullptr;
void _tile_data_editors_tree_selected();
// -- Inspector --
AtlasTileProxyObject *tile_proxy_object = nullptr;
EditorInspector *tile_inspector = nullptr;
Label *tile_inspector_no_tile_selected_label = nullptr;
String selected_property;
void _inspector_property_selected(const String &p_property);
TileSetAtlasSourceProxyObject *atlas_source_proxy_object = nullptr;
EditorInspector *atlas_source_inspector = nullptr;
// -- Atlas view --
TileAtlasView *tile_atlas_view = nullptr;
Label *help_label = nullptr;
// Dragging
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_CREATE_TILES,
DRAG_TYPE_CREATE_TILES_USING_RECT,
DRAG_TYPE_CREATE_BIG_TILE,
DRAG_TYPE_REMOVE_TILES,
DRAG_TYPE_REMOVE_TILES_USING_RECT,
DRAG_TYPE_MOVE_TILE,
DRAG_TYPE_RECT_SELECT,
DRAG_TYPE_MAY_POPUP_MENU,
// WARNING: Keep in this order.
DRAG_TYPE_RESIZE_TOP_LEFT,
DRAG_TYPE_RESIZE_TOP,
DRAG_TYPE_RESIZE_TOP_RIGHT,
DRAG_TYPE_RESIZE_RIGHT,
DRAG_TYPE_RESIZE_BOTTOM_RIGHT,
DRAG_TYPE_RESIZE_BOTTOM,
DRAG_TYPE_RESIZE_BOTTOM_LEFT,
DRAG_TYPE_RESIZE_LEFT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2i drag_start_mouse_pos;
Vector2i drag_last_mouse_pos;
Vector2i drag_current_tile;
Rect2i drag_start_tile_shape;
RBSet<Vector2i> drag_modified_tiles;
void _end_dragging();
HashMap<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas);
// Popup functions.
enum MenuOptions {
TILE_CREATE,
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
ADVANCED_CLEANUP_TILES,
};
Vector2i menu_option_coords;
int menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE;
void _menu_option(int p_option);
// Tool buttons.
Ref<ButtonGroup> tools_button_group;
Button *tool_setup_atlas_source_button = nullptr;
Button *tool_select_button = nullptr;
Button *tool_paint_button = nullptr;
Label *tool_tile_id_label = nullptr;
// Tool settings.
HBoxContainer *tool_settings = nullptr;
HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr;
Button *tools_settings_erase_button = nullptr;
MenuButton *tool_advanced_menu_button = nullptr;
TextureRect *outside_tiles_warning = nullptr;
// Selection.
RBSet<TileSelection> selection;
void _set_selection_from_array(const Array &p_selection);
Array _get_selection_as_array();
// A control on the tile atlas to draw and handle input events.
Vector2i hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS;
PopupMenu *base_tile_popup_menu = nullptr;
PopupMenu *empty_base_tile_popup_menu = nullptr;
Ref<Texture2D> resize_handle;
Ref<Texture2D> resize_handle_disabled;
Control *tile_atlas_control = nullptr;
Control *tile_atlas_control_unscaled = nullptr;
void _tile_atlas_control_draw();
void _tile_atlas_control_unscaled_draw();
void _tile_atlas_control_mouse_exited();
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
void _tile_atlas_view_transform_changed();
// A control over the alternative tiles.
Vector3i hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
PopupMenu *alternative_tile_popup_menu = nullptr;
Control *alternative_tiles_control = nullptr;
Control *alternative_tiles_control_unscaled = nullptr;
void _tile_alternatives_create_button_pressed(const Vector2i &p_atlas_coords);
void _tile_alternatives_control_draw();
void _tile_alternatives_control_unscaled_draw();
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
// -- Update functions --
void _update_tile_id_label();
void _update_source_inspector();
void _update_fix_selected_and_hovered_tiles();
void _update_atlas_source_inspector();
void _update_tile_inspector();
void _update_tile_data_editors();
void _update_current_tile_data_editor();
void _update_manage_tile_properties_button();
void _update_atlas_view();
void _update_toolbar();
void _update_buttons();
// -- Misc --
void _auto_create_tiles();
void _auto_remove_tiles();
void _cancel_auto_create_tiles();
AcceptDialog *confirm_auto_create_tiles = nullptr;
Vector<Ref<TileSetAtlasSource>> atlases_to_auto_create_tiles;
Vector2i _get_drag_offset_tile_coords(const Vector2i &p_offset) const;
void _update_source_texture();
void _check_outside_tiles();
void _cleanup_outside_tiles();
void _tile_set_changed();
void _tile_proxy_object_changed(const String &p_what);
void _atlas_source_proxy_object_changed(const String &p_what);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
protected:
void _notification(int p_what);
static void _bind_methods();
// -- input events --
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
void init_new_atlases(const Vector<Ref<TileSetAtlasSource>> &p_atlases);
TileSetAtlasSourceEditor();
~TileSetAtlasSourceEditor();
};
class EditorPropertyTilePolygon : public EditorProperty {
GDCLASS(EditorPropertyTilePolygon, EditorProperty);
StringName count_property;
String element_pattern;
String base_type;
void _add_focusable_children(Node *p_node);
GenericTilePolygonEditor *generic_tile_polygon_editor = nullptr;
void _polygons_changed();
public:
virtual void update_property() override;
void setup_single_mode(const StringName &p_property, const String &p_base_type);
void setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type);
EditorPropertyTilePolygon();
};
class EditorInspectorPluginTileData : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginTileData, EditorInspectorPlugin);
void _occlusion_polygon_set_callback();
void _polygons_changed(Object *p_generic_tile_polygon_editor, Object *p_object, const String &p_path);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,142 @@
/**************************************************************************/
/* tile_set_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "atlas_merging_dialog.h"
#include "scene/gui/tab_bar.h"
#include "scene/resources/2d/tile_set.h"
#include "tile_proxies_manager_dialog.h"
#include "tile_set_atlas_source_editor.h"
#include "tile_set_scenes_collection_source_editor.h"
class AcceptDialog;
class SpinBox;
class HBoxContainer;
class SplitContainer;
class EditorFileDialog;
class EditorInspectorPlugin;
class TileSetEditor : public MarginContainer {
GDCLASS(TileSetEditor, MarginContainer);
static TileSetEditor *singleton;
private:
bool read_only = false;
Ref<TileSet> tile_set;
bool tile_set_changed_needs_update = false;
HSplitContainer *split_container = nullptr;
// TabBar.
HBoxContainer *tile_set_toolbar = nullptr;
TabBar *tabs_bar = nullptr;
// Tiles.
Label *no_source_selected_label = nullptr;
TileSetAtlasSourceEditor *tile_set_atlas_source_editor = nullptr;
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr;
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void _load_texture_files(const Vector<String> &p_paths);
void _update_sources_list(int force_selected_id = -1);
// Sources management.
Button *sources_delete_button = nullptr;
MenuButton *sources_add_button = nullptr;
MenuButton *source_sort_button = nullptr;
MenuButton *sources_advanced_menu_button = nullptr;
ItemList *sources_list = nullptr;
Ref<Texture2D> missing_texture_texture;
void _source_selected(int p_source_index);
void _source_delete_pressed();
void _source_add_id_pressed(int p_id_pressed);
void _sources_advanced_menu_id_pressed(int p_id_pressed);
void _set_source_sort(int p_sort);
EditorFileDialog *texture_file_dialog = nullptr;
AtlasMergingDialog *atlas_merging_dialog = nullptr;
TileProxiesManagerDialog *tile_proxies_manager_dialog = nullptr;
bool first_edit = true;
// Patterns.
ItemList *patterns_item_list = nullptr;
Label *patterns_help_label = nullptr;
void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
bool select_last_pattern = false;
void _update_patterns_list();
// Expanded editor.
PanelContainer *expanded_area = nullptr;
Control *expanded_editor = nullptr;
ObjectID expanded_editor_parent;
LocalVector<SplitContainer *> disable_on_expand;
void _tile_set_changed();
void _tab_changed(int p_tab_changed);
void _move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
protected:
void _notification(int p_what);
public:
_FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
void edit(Ref<TileSet> p_tile_set);
void add_expanded_editor(Control *p_editor);
void remove_expanded_editor();
void register_split(SplitContainer *p_split);
TileSetEditor();
};
class TileSourceInspectorPlugin : public EditorInspectorPlugin {
GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin);
AcceptDialog *id_edit_dialog = nullptr;
Label *id_label = nullptr;
SpinBox *id_input = nullptr;
Object *edited_source = nullptr;
void _show_id_edit_dialog(Object *p_for_source);
void _confirm_change_id();
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};

View File

@@ -0,0 +1,593 @@
/**************************************************************************/
/* tile_set_scenes_collection_source_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tile_set_scenes_collection_source_editor.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/inspector/editor_resource_preview.h"
#include "editor/scene/2d/tiles/tile_set_editor.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/item_list.h"
#include "scene/gui/label.h"
#include "scene/gui/split_container.h"
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id(int p_id) {
ERR_FAIL_COND(p_id < 0);
if (source_id == p_id) {
return;
}
ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet Scenes Collection source ID. Another TileSet source exists with id %d.", p_id));
int previous_source = source_id;
source_id = p_id; // source_id must be updated before, because it's used by the source list update.
tile_set->set_source_id(previous_source, p_id);
emit_signal(CoreStringName(changed), "id");
}
int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id() {
return source_id;
}
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name == "name") {
// Use the resource_name property to store the source's name.
name = "resource_name";
}
bool valid = false;
tile_set_scenes_collection_source->set(name, p_value, &valid);
if (valid) {
emit_signal(CoreStringName(changed), String(name).utf8().get_data());
}
return valid;
}
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!tile_set_scenes_collection_source) {
return false;
}
String name = p_name;
if (name == "name") {
// Use the resource_name property to store the source's name.
name = "resource_name";
}
bool valid = false;
r_ret = tile_set_scenes_collection_source->get(name, &valid);
return valid;
}
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
}
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_bind_methods() {
// -- Shape and layout --
ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id);
ClassDB::bind_method(D_METHOD("get_id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id);
ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id");
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
}
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
ERR_FAIL_COND(p_tile_set.is_null());
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
if (tile_set == p_tile_set && tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && source_id == p_source_id) {
return;
}
// Disconnect to changes.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->disconnect(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed));
}
tile_set = p_tile_set;
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
source_id = p_source_id;
// Connect to changes.
if (tile_set_scenes_collection_source) {
if (!tile_set_scenes_collection_source->is_connected(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed))) {
tile_set_scenes_collection_source->connect(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed));
}
}
notify_property_list_changed();
}
// -- Proxy object used by the tile inspector --
bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_set(const StringName &p_name, const Variant &p_value) {
if (!tile_set_scenes_collection_source) {
return false;
}
if (p_name == "id") {
int as_int = int(p_value);
ERR_FAIL_COND_V(as_int < 0, false);
ERR_FAIL_COND_V(tile_set_scenes_collection_source->has_scene_tile_id(as_int), false);
tile_set_scenes_collection_source->set_scene_tile_id(scene_id, as_int);
scene_id = as_int;
emit_signal(CoreStringName(changed), "id");
for (int i = 0; i < tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_count(); i++) {
if (int(tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_metadata(i)) == scene_id) {
tile_set_scenes_collection_source_editor->scene_tiles_list->select(i);
break;
}
}
return true;
} else if (p_name == "scene") {
tile_set_scenes_collection_source->set_scene_tile_scene(scene_id, p_value);
emit_signal(CoreStringName(changed), "scene");
return true;
} else if (p_name == "display_placeholder") {
tile_set_scenes_collection_source->set_scene_tile_display_placeholder(scene_id, p_value);
emit_signal(CoreStringName(changed), "display_placeholder");
return true;
}
return false;
}
bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!tile_set_scenes_collection_source) {
return false;
}
if (p_name == "id") {
r_ret = scene_id;
return true;
} else if (p_name == "scene") {
r_ret = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id);
return true;
} else if (p_name == "display_placeholder") {
r_ret = tile_set_scenes_collection_source->get_scene_tile_display_placeholder(scene_id);
return true;
}
return false;
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
if (!tile_set_scenes_collection_source) {
return;
}
p_list->push_back(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::OBJECT, "scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"));
p_list->push_back(PropertyInfo(Variant::BOOL, "display_placeholder", PROPERTY_HINT_NONE, ""));
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_scene_id) {
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
if (tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && scene_id == p_scene_id) {
return;
}
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
scene_id = p_scene_id;
notify_property_list_changed();
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_bind_methods() {
ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
}
void TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed(const String &p_what) {
if (p_what == "id") {
emit_signal(SNAME("source_id_changed"), scenes_collection_source_proxy_object->get_id());
}
}
void TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed() {
tile_set_scenes_collection_source_changed_needs_update = true;
}
void TileSetScenesCollectionSourceEditor::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud) {
int index = p_ud;
if (index >= 0 && index < scene_tiles_list->get_item_count()) {
scene_tiles_list->set_item_icon(index, p_preview);
}
}
void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_index) {
Ref<PackedScene> packed_scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_tiles_list->get_item_metadata(p_index));
if (packed_scene.is_valid()) {
EditorNode::get_singleton()->load_scene(packed_scene->get_path());
}
}
void TileSetScenesCollectionSourceEditor::_source_add_pressed() {
if (!scene_select_dialog) {
scene_select_dialog = memnew(EditorFileDialog);
add_child(scene_select_dialog);
scene_select_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
scene_select_dialog->connect("file_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scene_file_selected));
for (const String &E : Vector<String>{ "tscn", "scn" }) {
scene_select_dialog->add_filter("*." + E, E.to_upper());
}
}
scene_select_dialog->popup_file_dialog();
}
void TileSetScenesCollectionSourceEditor::_scene_file_selected(const String &p_path) {
Ref<PackedScene> scene = ResourceLoader::load(p_path);
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", scene, scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->commit_action();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
void TileSetScenesCollectionSourceEditor::_source_delete_pressed() {
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
ERR_FAIL_COND(selected_indices.is_empty());
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id);
undo_redo->commit_action();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
void TileSetScenesCollectionSourceEditor::_update_source_inspector() {
// Update the proxy object.
scenes_collection_source_proxy_object->edit(tile_set, tile_set_scenes_collection_source, tile_set_source_id);
}
void TileSetScenesCollectionSourceEditor::_update_tile_inspector() {
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
bool has_atlas_tile_selected = (selected_indices.size() > 0);
// Update the proxy object.
if (has_atlas_tile_selected) {
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
tile_proxy_object->edit(tile_set_scenes_collection_source, scene_id);
}
// Update visibility.
tile_inspector_label->set_visible(has_atlas_tile_selected);
tile_inspector->set_visible(has_atlas_tile_selected);
}
void TileSetScenesCollectionSourceEditor::_update_action_buttons() {
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
scene_tile_delete_button->set_disabled(selected_indices.is_empty() || read_only);
}
void TileSetScenesCollectionSourceEditor::_update_scenes_list() {
if (!tile_set_scenes_collection_source) {
return;
}
// Get the previously selected id.
Vector<int> selected_indices = scene_tiles_list->get_selected_items();
int old_selected_scene_id = (selected_indices.size() > 0) ? int(scene_tiles_list->get_item_metadata(selected_indices[0])) : -1;
// Clear the list.
scene_tiles_list->clear();
// Rebuild the list.
int to_reselect = -1;
for (int i = 0; i < tile_set_scenes_collection_source->get_scene_tiles_count(); i++) {
int scene_id = tile_set_scenes_collection_source->get_scene_tile_id(i);
Ref<PackedScene> scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id);
int item_index = 0;
if (scene.is_valid()) {
item_index = scene_tiles_list->add_item(vformat("%s (path:%s id:%d)", scene->get_path().get_file().get_basename(), scene->get_path(), scene_id));
Variant udata = i;
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata);
} else {
item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_editor_theme_icon(SNAME("PackedScene")));
}
scene_tiles_list->set_item_metadata(item_index, scene_id);
if (old_selected_scene_id >= 0 && scene_id == old_selected_scene_id) {
to_reselect = i;
}
}
if (scene_tiles_list->get_item_count() == 0) {
scene_tiles_list->add_item(TTR("Drag and drop scenes here or use the Add button."));
scene_tiles_list->set_item_disabled(-1, true);
}
// Reselect if needed.
if (to_reselect >= 0) {
scene_tiles_list->select(to_reselect);
}
// Icon size update.
int int_size = int(EDITOR_GET("filesystem/file_dialog/thumbnail_size")) * EDSCALE;
scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size));
}
void TileSetScenesCollectionSourceEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
scenes_collection_source_inspector->add_custom_property_description("TileSetScenesCollectionProxyObject", "id", TTR("The tile's unique identifier within this TileSet. Each tile stores its source ID, so changing one may make tiles invalid."));
scenes_collection_source_inspector->add_custom_property_description("TileSetScenesCollectionProxyObject", "name", TTR("The human-readable name for the scene collection. Use a descriptive name here for organizational purposes (such as \"obstacles\", \"decoration\", etc.)."));
tile_inspector->edit(tile_proxy_object);
tile_inspector->add_custom_property_description("SceneTileProxyObject", "id", TTR("ID of the scene tile in the collection. Each painted tile has associated ID, so changing this property may cause your TileMaps to not display properly."));
tile_inspector->add_custom_property_description("SceneTileProxyObject", "scene", TTR("Absolute path to the scene associated with this tile."));
tile_inspector->add_custom_property_description("SceneTileProxyObject", "display_placeholder", TTR("If [code]true[/code], a placeholder marker will be displayed on top of the scene's preview. The marker is displayed anyway if the scene has no valid preview."));
} break;
case NOTIFICATION_THEME_CHANGED: {
scene_tile_add_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
scene_tile_delete_button->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
_update_scenes_list();
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (tile_set_scenes_collection_source_changed_needs_update) {
read_only = false;
// Add the listener again and check for read-only status.
if (tile_set.is_valid()) {
read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set);
}
// Update everything.
_update_source_inspector();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
tile_set_scenes_collection_source_changed_needs_update = false;
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
// Update things just in case.
_update_scenes_list();
_update_action_buttons();
} break;
}
}
void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
ERR_FAIL_COND(p_tile_set.is_null());
ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
bool new_read_only_state = false;
if (p_tile_set.is_valid()) {
new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set);
}
if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) {
return;
}
// Remove listener for old objects.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->disconnect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
}
// Change the edited object.
tile_set = p_tile_set;
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
tile_set_source_id = p_source_id;
// Read-only status is false by default
read_only = new_read_only_state;
if (tile_set.is_valid()) {
scenes_collection_source_inspector->set_read_only(read_only);
tile_inspector->set_read_only(read_only);
scene_tile_add_button->set_disabled(read_only);
}
// Add the listener again.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->connect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
}
// Update everything.
_update_source_inspector();
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
if (!_can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
if (p_from == scene_tiles_list) {
// Handle dropping a texture in the list of atlas resources.
Dictionary d = p_data;
Vector<String> files = d["files"];
for (int i = 0; i < files.size(); i++) {
Ref<PackedScene> resource = ResourceLoader::load(files[i]);
if (resource.is_valid()) {
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->commit_action();
}
}
_update_scenes_list();
_update_action_buttons();
_update_tile_inspector();
}
}
bool TileSetScenesCollectionSourceEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
if (p_from == scene_tiles_list) {
Dictionary d = p_data;
if (!d.has("type")) {
return false;
}
// Check if we have a Texture2D.
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
if (files.is_empty()) {
return false;
}
for (int i = 0; i < files.size(); i++) {
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]);
if (!ClassDB::is_parent_class(ftype, "PackedScene")) {
return false;
}
}
return true;
}
}
return false;
}
void TileSetScenesCollectionSourceEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileSetScenesCollectionSourceEditor::_scene_thumbnail_done);
}
TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
// -- Right side --
HSplitContainer *split_container_right_side = memnew(HSplitContainer);
split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(split_container_right_side);
// Middle panel.
ScrollContainer *middle_panel = memnew(ScrollContainer);
middle_panel->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
middle_panel->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
split_container_right_side->add_child(middle_panel);
VBoxContainer *middle_vbox_container = memnew(VBoxContainer);
middle_vbox_container->set_h_size_flags(SIZE_EXPAND_FILL);
middle_panel->add_child(middle_vbox_container);
// Scenes collection source inspector.
scenes_collection_source_inspector_label = memnew(Label);
scenes_collection_source_inspector_label->set_focus_mode(FOCUS_ACCESSIBILITY);
scenes_collection_source_inspector_label->set_text(TTR("Scenes collection properties:"));
middle_vbox_container->add_child(scenes_collection_source_inspector_label);
scenes_collection_source_proxy_object = memnew(TileSetScenesCollectionProxyObject());
scenes_collection_source_proxy_object->connect(CoreStringName(changed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed));
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
scenes_collection_source_inspector->set_use_doc_hints(true);
middle_vbox_container->add_child(scenes_collection_source_inspector);
// Tile inspector.
tile_inspector_label = memnew(Label);
tile_inspector_label->set_text(TTR("Tile properties:"));
tile_inspector_label->hide();
middle_vbox_container->add_child(tile_inspector_label);
tile_proxy_object = memnew(SceneTileProxyObject(this));
tile_proxy_object->connect(CoreStringName(changed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_scenes_list).unbind(1));
tile_proxy_object->connect(CoreStringName(changed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
tile_inspector = memnew(EditorInspector);
tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
tile_inspector->set_use_doc_hints(true);
tile_inspector->set_use_folding(true);
middle_vbox_container->add_child(tile_inspector);
// Scenes list.
VBoxContainer *right_vbox_container = memnew(VBoxContainer);
split_container_right_side->add_child(right_vbox_container);
scene_tiles_list = memnew(ItemList);
scene_tiles_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
SET_DRAG_FORWARDING_CDU(scene_tiles_list, TileSetScenesCollectionSourceEditor);
scene_tiles_list->connect(SceneStringName(item_selected), callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1));
scene_tiles_list->connect(SceneStringName(item_selected), callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
scene_tiles_list->connect("item_activated", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_list_item_activated));
scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
right_vbox_container->add_child(scene_tiles_list);
HBoxContainer *scenes_bottom_actions = memnew(HBoxContainer);
right_vbox_container->add_child(scenes_bottom_actions);
scene_tile_add_button = memnew(Button);
scene_tile_add_button->set_theme_type_variation(SceneStringName(FlatButton));
scene_tile_add_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_add_pressed));
scene_tile_add_button->set_accessibility_name(TTRC("Add"));
scenes_bottom_actions->add_child(scene_tile_add_button);
scene_tile_delete_button = memnew(Button);
scene_tile_delete_button->set_theme_type_variation(SceneStringName(FlatButton));
scene_tile_delete_button->set_disabled(true);
scene_tile_delete_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed));
scene_tile_delete_button->set_accessibility_name(TTRC("Delete"));
scenes_bottom_actions->add_child(scene_tile_delete_button);
EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin));
}
TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() {
memdelete(scenes_collection_source_proxy_object);
memdelete(tile_proxy_object);
}

View File

@@ -0,0 +1,145 @@
/**************************************************************************/
/* tile_set_scenes_collection_source_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_inspector.h"
#include "scene/gui/box_container.h"
#include "scene/resources/2d/tile_set.h"
class Button;
class ItemList;
class Label;
class EditorFileDialog;
class TileSetScenesCollectionSourceEditor : public HBoxContainer {
GDCLASS(TileSetScenesCollectionSourceEditor, HBoxContainer);
private:
// -- Proxy object for an atlas source, needed by the inspector --
class TileSetScenesCollectionProxyObject : public Object {
GDCLASS(TileSetScenesCollectionProxyObject, Object);
private:
Ref<TileSet> tile_set;
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
int source_id = -1;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
void set_id(int p_id);
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
};
// -- Proxy object for a tile, needed by the inspector --
class SceneTileProxyObject : public Object {
GDCLASS(SceneTileProxyObject, Object);
private:
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr;
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
int source_id;
int scene_id;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
// Update the proxied object.
void edit(TileSetScenesCollectionSource *p_tile_set_atlas_source, int p_scene_id);
SceneTileProxyObject(TileSetScenesCollectionSourceEditor *p_tiles_set_scenes_collection_source_editor) {
tile_set_scenes_collection_source_editor = p_tiles_set_scenes_collection_source_editor;
}
};
private:
bool read_only = false;
Ref<TileSet> tile_set;
TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr;
int tile_set_source_id = -1;
bool tile_set_scenes_collection_source_changed_needs_update = false;
// Source inspector.
TileSetScenesCollectionProxyObject *scenes_collection_source_proxy_object = nullptr;
Label *scenes_collection_source_inspector_label = nullptr;
EditorInspector *scenes_collection_source_inspector = nullptr;
// Tile inspector.
SceneTileProxyObject *tile_proxy_object = nullptr;
Label *tile_inspector_label = nullptr;
EditorInspector *tile_inspector = nullptr;
ItemList *scene_tiles_list = nullptr;
Button *scene_tile_add_button = nullptr;
Button *scene_tile_delete_button = nullptr;
EditorFileDialog *scene_select_dialog = nullptr;
void _tile_set_scenes_collection_source_changed();
void _scenes_collection_source_proxy_object_changed(const String &p_what);
void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_ud);
void _scenes_list_item_activated(int p_index);
void _source_add_pressed();
void _scene_file_selected(const String &p_path);
void _source_delete_pressed();
// Update methods.
void _update_source_inspector();
void _update_tile_inspector();
void _update_scenes_list();
void _update_action_buttons();
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
TileSetScenesCollectionSourceEditor();
~TileSetScenesCollectionSourceEditor();
};

View File

@@ -0,0 +1,555 @@
/**************************************************************************/
/* tiles_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "tiles_editor_plugin.h"
#include "tile_set_editor.h"
#include "core/os/mutex.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/inspector/multi_node_edit.h"
#include "editor/scene/canvas_item_editor_plugin.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/2d/tile_map.h"
#include "scene/2d/tile_map_layer.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/resources/2d/tile_set.h"
#include "scene/resources/image_texture.h"
TilesEditorUtils *TilesEditorUtils::singleton = nullptr;
TileMapEditorPlugin *tile_map_plugin_singleton = nullptr;
TileSetEditorPlugin *tile_set_plugin_singleton = nullptr;
void TilesEditorUtils::_preview_frame_started() {
RS::get_singleton()->request_frame_drawn_callback(callable_mp(this, &TilesEditorUtils::_pattern_preview_done));
}
void TilesEditorUtils::_pattern_preview_done() {
pattern_preview_done.post();
}
void TilesEditorUtils::_thread_func(void *ud) {
TilesEditorUtils *te = static_cast<TilesEditorUtils *>(ud);
set_current_thread_safe_for_nodes(true);
te->_thread();
}
void TilesEditorUtils::_thread() {
pattern_thread_exited.clear();
while (!pattern_thread_exit.is_set()) {
pattern_preview_sem.wait();
pattern_preview_mutex.lock();
if (pattern_preview_queue.is_empty()) {
pattern_preview_mutex.unlock();
} else {
QueueItem item = pattern_preview_queue.front()->get();
pattern_preview_queue.pop_front();
pattern_preview_mutex.unlock();
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
thumbnail_size *= EDSCALE;
Vector2 thumbnail_size2 = Vector2(thumbnail_size, thumbnail_size);
if (item.pattern.is_valid() && !item.pattern->is_empty()) {
// Generate the pattern preview
SubViewport *viewport = memnew(SubViewport);
viewport->set_size(thumbnail_size2);
viewport->set_disable_input(true);
viewport->set_transparent_background(true);
viewport->set_update_mode(SubViewport::UPDATE_ONCE);
TileMapLayer *tile_map_layer = memnew(TileMapLayer);
tile_map_layer->set_tile_set(item.tile_set);
tile_map_layer->set_pattern(Vector2(), item.pattern);
viewport->add_child(tile_map_layer);
Rect2 encompassing_rect;
encompassing_rect.set_position(tile_map_layer->map_to_local(tile_map_layer->get_tile_map_layer_data().begin()->key));
for (KeyValue<Vector2i, CellData> kv : tile_map_layer->get_tile_map_layer_data()) {
Vector2i cell = kv.key;
Vector2 world_pos = tile_map_layer->map_to_local(cell);
encompassing_rect.expand_to(world_pos);
// Texture.
Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map_layer->get_cell_source_id(cell));
if (atlas_source.is_valid()) {
Vector2i coords = tile_map_layer->get_cell_atlas_coords(cell);
int alternative = tile_map_layer->get_cell_alternative_tile(cell);
if (atlas_source->has_tile(coords) && atlas_source->has_alternative_tile(coords, alternative)) {
Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin();
encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2);
encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2);
}
}
}
Vector2 scale = thumbnail_size2 / MAX(encompassing_rect.size.x, encompassing_rect.size.y);
tile_map_layer->set_scale(scale);
tile_map_layer->set_position(-(scale * encompassing_rect.get_center()) + thumbnail_size2 / 2);
// Add the viewport at the last moment to avoid rendering too early.
callable_mp((Node *)EditorNode::get_singleton(), &Node::add_child).call_deferred(viewport, false, Node::INTERNAL_MODE_DISABLED);
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(this, &TilesEditorUtils::_preview_frame_started), Object::CONNECT_ONE_SHOT);
pattern_preview_done.wait();
Ref<Image> image = viewport->get_texture()->get_image();
// Find the index for the given pattern. TODO: optimize.
item.callback.call(item.pattern, ImageTexture::create_from_image(image));
viewport->queue_free();
}
}
}
pattern_thread_exited.set();
}
void TilesEditorUtils::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
ERR_FAIL_COND(p_tile_set.is_null());
ERR_FAIL_COND(p_pattern.is_null());
{
MutexLock lock(pattern_preview_mutex);
pattern_preview_queue.push_back({ p_tile_set, p_pattern, p_callback });
}
pattern_preview_sem.post();
}
void TilesEditorUtils::set_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
void TilesEditorUtils::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
ERR_FAIL_NULL(item_list);
ERR_FAIL_NULL(sorting_button);
if (sorting_button->is_visible_in_tree()) {
for (int i = 0; i != SOURCE_SORT_MAX; i++) {
sorting_button->get_popup()->set_item_checked(i, (i == (int)source_sort));
}
}
if (item_list->is_visible_in_tree()) {
// Make sure the selection is not overwritten after sorting.
int atlas_sources_lists_current_mem = atlas_sources_lists_current;
item_list->emit_signal(SNAME("sort_request"));
atlas_sources_lists_current = atlas_sources_lists_current_mem;
if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
item_list->deselect_all();
} else {
item_list->set_current(atlas_sources_lists_current);
item_list->ensure_current_is_visible();
item_list->emit_signal(SceneStringName(item_selected), atlas_sources_lists_current);
}
}
}
void TilesEditorUtils::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
atlas_view_zoom = p_zoom;
atlas_view_scroll = p_scroll;
}
void TilesEditorUtils::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
ERR_FAIL_NULL(tile_atlas_view);
if (tile_atlas_view->is_visible_in_tree()) {
tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll);
}
}
void TilesEditorUtils::set_sorting_option(int p_option) {
source_sort = p_option;
}
List<int> TilesEditorUtils::get_sorted_sources(const Ref<TileSet> p_tile_set) const {
SourceNameComparator::tile_set = p_tile_set;
List<int> source_ids;
for (int i = 0; i < p_tile_set->get_source_count(); i++) {
source_ids.push_back(p_tile_set->get_source_id(i));
}
switch (source_sort) {
case SOURCE_SORT_ID_REVERSE:
// Already sorted.
source_ids.reverse();
break;
case SOURCE_SORT_NAME:
source_ids.sort_custom<SourceNameComparator>();
break;
case SOURCE_SORT_NAME_REVERSE:
source_ids.sort_custom<SourceNameComparator>();
source_ids.reverse();
break;
default: // SOURCE_SORT_ID
break;
}
SourceNameComparator::tile_set.unref();
return source_ids;
}
Ref<TileSet> TilesEditorUtils::SourceNameComparator::tile_set;
bool TilesEditorUtils::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
String name_a;
String name_b;
{
TileSetSource *source = *tile_set->get_source(p_a);
if (!source->get_name().is_empty()) {
name_a = source->get_name();
}
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (name_a.is_empty() && texture.is_valid()) {
name_a = texture->get_path().get_file();
}
}
if (name_a.is_empty()) {
name_a = itos(p_a);
}
}
{
TileSetSource *source = *tile_set->get_source(p_b);
if (!source->get_name().is_empty()) {
name_b = source->get_name();
}
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (name_b.is_empty() && texture.is_valid()) {
name_b = texture->get_path().get_file();
}
}
if (name_b.is_empty()) {
name_b = itos(p_b);
}
}
return NaturalNoCaseComparator()(name_a, name_b);
}
void TilesEditorUtils::display_tile_set_editor_panel() {
tile_map_plugin_singleton->hide_editor();
tile_set_plugin_singleton->make_visible(true);
}
void TilesEditorUtils::draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color) {
Ref<Texture2D> selection_texture = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("TileSelection"), EditorStringName(EditorIcons));
real_t scale = p_ci->get_global_transform().get_scale().x * 0.5;
p_ci->draw_set_transform(p_rect.position, 0, Vector2(1, 1) / scale);
RS::get_singleton()->canvas_item_add_nine_patch(
p_ci->get_canvas_item(), Rect2(Vector2(), p_rect.size * scale), Rect2(), selection_texture->get_rid(),
Vector2(2, 2), Vector2(2, 2), RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, false, p_color);
p_ci->draw_set_transform_matrix(Transform2D());
}
TilesEditorUtils::TilesEditorUtils() {
singleton = this;
// Pattern preview generation thread.
pattern_preview_thread.start(_thread_func, this);
ED_SHORTCUT("tiles_editor/cut", TTRC("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X);
ED_SHORTCUT("tiles_editor/copy", TTRC("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C);
ED_SHORTCUT("tiles_editor/paste", TTRC("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V);
ED_SHORTCUT("tiles_editor/cancel", TTRC("Cancel"), Key::ESCAPE);
ED_SHORTCUT("tiles_editor/delete", TTRC("Delete"), Key::KEY_DELETE);
ED_SHORTCUT("tiles_editor/paint_tool", TTRC("Paint Tool"), Key::D);
ED_SHORTCUT("tiles_editor/line_tool", TTRC("Line Tool"), Key::L);
ED_SHORTCUT("tiles_editor/rect_tool", TTRC("Rect Tool"), Key::R);
ED_SHORTCUT("tiles_editor/bucket_tool", TTRC("Bucket Tool"), Key::B);
ED_SHORTCUT("tiles_editor/eraser", TTRC("Eraser Tool"), Key::E);
ED_SHORTCUT("tiles_editor/picker", TTRC("Picker Tool"), Key::P);
}
TilesEditorUtils::~TilesEditorUtils() {
if (pattern_preview_thread.is_started()) {
pattern_thread_exit.set();
pattern_preview_sem.post();
while (!pattern_thread_exited.is_set()) {
OS::get_singleton()->delay_usec(10000);
RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
}
pattern_preview_thread.wait_to_finish();
}
singleton = nullptr;
}
void TileMapEditorPlugin::_tile_map_layer_changed() {
if (tile_map_changed_needs_update) {
return;
}
tile_map_changed_needs_update = true;
callable_mp(this, &TileMapEditorPlugin::_update_tile_map).call_deferred();
}
void TileMapEditorPlugin::_tile_map_layer_removed() {
// Workaround for TileMap, making sure the editor stays open when you delete the currently edited layer.
TileMap *tile_map = ObjectDB::get_instance<TileMap>(tile_map_group_id);
if (tile_map) {
edit(tile_map);
}
}
void TileMapEditorPlugin::_update_tile_map() {
TileMapLayer *edited_layer = ObjectDB::get_instance<TileMapLayer>(tile_map_layer_id);
if (edited_layer) {
Ref<TileSet> tile_set = edited_layer->get_tile_set();
if (tile_set.is_valid() && tile_set_id != tile_set->get_instance_id()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_id = tile_set->get_instance_id();
} else if (tile_set.is_null()) {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
tile_set_id = ObjectID();
}
}
tile_map_changed_needs_update = false;
}
void TileMapEditorPlugin::_select_layer(const StringName &p_name) {
TileMapLayer *edited_layer = ObjectDB::get_instance<TileMapLayer>(tile_map_layer_id);
ERR_FAIL_NULL(edited_layer);
Node *parent = edited_layer->get_parent();
if (parent) {
TileMapLayer *new_layer = Object::cast_to<TileMapLayer>(parent->get_node_or_null(String(p_name)));
edit(new_layer);
}
}
void TileMapEditorPlugin::_edit_tile_map_layer(TileMapLayer *p_tile_map_layer, bool p_show_layer_selector) {
ERR_FAIL_NULL(p_tile_map_layer);
editor->edit(p_tile_map_layer);
editor->set_show_layer_selector(p_show_layer_selector);
// Update the object IDs.
tile_map_layer_id = p_tile_map_layer->get_instance_id();
p_tile_map_layer->connect(CoreStringName(changed), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
p_tile_map_layer->connect(SceneStringName(tree_exited), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
// Update the edited tileset.
Ref<TileSet> tile_set = p_tile_map_layer->get_tile_set();
if (tile_set.is_valid()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_id = tile_set->get_instance_id();
} else {
tile_set_plugin_singleton->edit(nullptr);
tile_set_plugin_singleton->make_visible(false);
}
}
void TileMapEditorPlugin::_edit_tile_map(TileMap *p_tile_map) {
ERR_FAIL_NULL(p_tile_map);
if (p_tile_map->get_layers_count() > 0) {
TileMapLayer *selected_layer = Object::cast_to<TileMapLayer>(p_tile_map->get_child(0));
_edit_tile_map_layer(selected_layer, true);
} else {
editor->edit(nullptr);
editor->set_show_layer_selector(false);
}
}
void TileMapEditorPlugin::_notification(int p_notification) {
if (p_notification == NOTIFICATION_EXIT_TREE) {
get_tree()->queue_delete(TilesEditorUtils::get_singleton());
}
}
void TileMapEditorPlugin::edit(Object *p_object) {
TileMapLayer *edited_layer = ObjectDB::get_instance<TileMapLayer>(tile_map_layer_id);
if (edited_layer) {
edited_layer->disconnect(CoreStringName(changed), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_changed));
edited_layer->disconnect(SceneStringName(tree_exited), callable_mp(this, &TileMapEditorPlugin::_tile_map_layer_removed));
}
tile_map_group_id = ObjectID();
tile_map_layer_id = ObjectID();
tile_set_id = ObjectID();
TileMap *tile_map = Object::cast_to<TileMap>(p_object);
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_object);
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(p_object);
if (tile_map) {
_edit_tile_map(tile_map);
} else if (tile_map_layer) {
_edit_tile_map_layer(tile_map_layer, false);
} else if (multi_node_edit) {
editor->edit(multi_node_edit);
} else {
editor->edit(nullptr);
}
}
bool TileMapEditorPlugin::handles(Object *p_object) const {
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(p_object);
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
if (multi_node_edit && edited_scene) {
bool only_tile_map_layers = true;
for (int i = 0; i < multi_node_edit->get_node_count(); i++) {
if (!Object::cast_to<TileMapLayer>(edited_scene->get_node(multi_node_edit->get_node(i)))) {
only_tile_map_layers = false;
break;
}
}
return only_tile_map_layers;
}
return Object::cast_to<TileMapLayer>(p_object) != nullptr || Object::cast_to<TileMap>(p_object) != nullptr;
}
void TileMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
EditorNode::get_bottom_panel()->make_item_visible(editor);
} else {
button->hide();
if (editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
bool TileMapEditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
return editor->forward_canvas_gui_input(p_event);
}
void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
editor->forward_canvas_draw_over_viewport(p_overlay);
}
void TileMapEditorPlugin::hide_editor() {
if (editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
bool TileMapEditorPlugin::is_editor_visible() const {
return editor->is_visible_in_tree();
}
TileMapEditorPlugin::TileMapEditorPlugin() {
if (!TilesEditorUtils::get_singleton()) {
memnew(TilesEditorUtils);
}
tile_map_plugin_singleton = this;
editor = memnew(TileMapLayerEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
editor->hide();
button = EditorNode::get_bottom_panel()->add_item(TTRC("TileMap"), editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_tile_map_bottom_panel", TTRC("Toggle TileMap Bottom Panel")));
button->hide();
}
TileMapEditorPlugin::~TileMapEditorPlugin() {
tile_map_plugin_singleton = nullptr;
}
void TileSetEditorPlugin::edit(Object *p_object) {
editor->edit(Ref<TileSet>(p_object));
if (p_object) {
edited_tileset = p_object->get_instance_id();
} else {
edited_tileset = ObjectID();
}
}
bool TileSetEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<TileSet>(p_object) != nullptr;
}
void TileSetEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
if (!tile_map_plugin_singleton->is_editor_visible()) {
EditorNode::get_bottom_panel()->make_item_visible(editor);
}
} else {
button->hide();
if (editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
ObjectID TileSetEditorPlugin::get_edited_tileset() const {
return edited_tileset;
}
TileSetEditorPlugin::TileSetEditorPlugin() {
if (!TilesEditorUtils::get_singleton()) {
memnew(TilesEditorUtils);
}
tile_set_plugin_singleton = this;
editor = memnew(TileSetEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
editor->hide();
button = EditorNode::get_bottom_panel()->add_item(TTRC("TileSet"), editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_tile_set_bottom_panel", TTRC("Toggle TileSet Bottom Panel")));
button->hide();
}
TileSetEditorPlugin::~TileSetEditorPlugin() {
tile_set_plugin_singleton = nullptr;
}

View File

@@ -0,0 +1,164 @@
/**************************************************************************/
/* tiles_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "tile_map_layer_editor.h"
#include "tile_set_editor.h"
class TilesEditorUtils : public Object {
GDCLASS(TilesEditorUtils, Object);
static TilesEditorUtils *singleton;
public:
enum SourceSortOption {
SOURCE_SORT_ID = 0,
SOURCE_SORT_ID_REVERSE,
SOURCE_SORT_NAME,
SOURCE_SORT_NAME_REVERSE,
SOURCE_SORT_MAX
};
private:
// For synchronization.
int atlas_sources_lists_current = 0;
float atlas_view_zoom = 1.0;
Vector2 atlas_view_scroll;
// Source sorting.
int source_sort = SOURCE_SORT_ID;
struct SourceNameComparator {
static Ref<TileSet> tile_set;
bool operator()(const int &p_a, const int &p_b) const;
};
// Patterns preview generation.
struct QueueItem {
Ref<TileSet> tile_set;
Ref<TileMapPattern> pattern;
Callable callback;
};
List<QueueItem> pattern_preview_queue;
Mutex pattern_preview_mutex;
Semaphore pattern_preview_sem;
Thread pattern_preview_thread;
SafeFlag pattern_thread_exit;
SafeFlag pattern_thread_exited;
Semaphore pattern_preview_done;
void _preview_frame_started();
void _pattern_preview_done();
static void _thread_func(void *ud);
void _thread();
public:
_FORCE_INLINE_ static TilesEditorUtils *get_singleton() { return singleton; }
// Pattern preview API.
void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
// To synchronize the atlas sources lists.
void set_sources_lists_current(int p_current);
void synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button);
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
void synchronize_atlas_view(Object *p_current);
// Sorting.
void set_sorting_option(int p_option);
List<int> get_sorted_sources(const Ref<TileSet> p_tile_set) const;
// Misc.
void display_tile_set_editor_panel();
static void draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color = Color(1.0, 1.0, 1.0));
TilesEditorUtils();
~TilesEditorUtils();
};
class TileMapEditorPlugin : public EditorPlugin {
GDCLASS(TileMapEditorPlugin, EditorPlugin);
TileMapLayerEditor *editor = nullptr;
Button *button = nullptr;
ObjectID tile_map_layer_id;
ObjectID tile_map_group_id; // Allow keeping the layer selector up to date.
bool tile_map_changed_needs_update = false;
ObjectID tile_set_id; // The TileSet associated with the TileMap.
void _tile_map_layer_changed();
void _tile_map_layer_removed();
void _update_tile_map();
void _select_layer(const StringName &p_name);
void _edit_tile_map_layer(TileMapLayer *p_tile_map_layer, bool p_show_layer_selector);
void _edit_tile_map(TileMap *p_tile_map);
protected:
void _notification(int p_notification);
public:
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
void hide_editor();
bool is_editor_visible() const;
TileMapEditorPlugin();
~TileMapEditorPlugin();
};
class TileSetEditorPlugin : public EditorPlugin {
GDCLASS(TileSetEditorPlugin, EditorPlugin);
TileSetEditor *editor = nullptr;
Button *button = nullptr;
ObjectID edited_tileset;
public:
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
ObjectID get_edited_tileset() const;
TileSetEditorPlugin();
~TileSetEditorPlugin();
};