diff --git a/bytecode/gdscript_v2_tokenizer_buffer.cpp b/bytecode/gdscript_v2_tokenizer_buffer.cpp index 56277df11..82a19fe44 100644 --- a/bytecode/gdscript_v2_tokenizer_buffer.cpp +++ b/bytecode/gdscript_v2_tokenizer_buffer.cpp @@ -496,12 +496,16 @@ GDScriptV2TokenizerCompat::Token GDScriptV2TokenizerBufferCompat::scan() { } Token eof; eof.type = Token::Type::G_TK_EOF; + eof.start_line = current_line; + eof.end_line = current_line; return eof; }; if (!last_token_was_newline && token_lines.has(current)) { current_line = token_lines[current]; - uint32_t current_column = token_columns[current]; + auto it = token_columns.find(current); + uint32_t current_column = it->value(); + uint32_t current_indent = current_column - 1; // Check if there's a need to indent/dedent. if (!multiline_mode) { @@ -509,11 +513,30 @@ GDScriptV2TokenizerCompat::Token GDScriptV2TokenizerBufferCompat::scan() { if (!indent_stack.is_empty()) { previous_indent = indent_stack.back()->get(); } - if (current_column - 1 > previous_indent) { + if (current_indent > previous_indent) { + // find the next column that is less than the current column + Vector detents; + for (it = it->next(); it != nullptr; it = it->next()) { + uint32_t next = it->value() - 1; + if (next <= previous_indent) { + break; + } + if (next < current_indent && next > previous_indent) { + // the next dedent is bigger than the previous indent, we need to indent once more + if (!detents.has(next)) { + detents.push_back(next); + } + } + } + detents.sort(); + for (uint32_t dedent : detents) { + pending_indents++; + indent_stack.push_back(dedent); + } pending_indents++; - indent_stack.push_back(current_column - 1); + indent_stack.push_back(current_indent); } else { - while (current_column - 1 < previous_indent) { + while (current_indent < previous_indent) { pending_indents--; indent_stack.pop_back(); if (indent_stack.is_empty()) { diff --git a/gui/texture_previewer.cpp b/gui/texture_previewer.cpp new file mode 100644 index 000000000..9f55d51a8 --- /dev/null +++ b/gui/texture_previewer.cpp @@ -0,0 +1,430 @@ +/**************************************************************************/ +/* texture_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 "texture_previewer.h" + +#include "core/object/callable_mp.h" +#include "scene/gui/aspect_ratio_container.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/label.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/texture_rect.h" +#include "scene/resources/atlas_texture.h" +#include "scene/resources/material.h" +#include "scene/resources/texture_rd.h" +#include "servers/rendering/rendering_device.h" + +#include "utility/gdre_settings.h" + +#include "gui/gdre_color_channel_selector.h" +#include "gui/gui_icons.h" +constexpr const char *texture_2d_shader_code = R"( +shader_type canvas_item; +render_mode blend_mix; + +instance uniform vec4 u_channel_factors = vec4(1.0); +instance uniform float lod = 0.0; + +vec4 filter_preview_colors(vec4 input_color, vec4 factors) { + // Filter RGB. + vec4 output_color = input_color * vec4(factors.rgb, input_color.a); + + // Remove transparency when alpha is not enabled. + output_color.a = mix(1.0, output_color.a, factors.a); + + // Switch to opaque grayscale when visualizing only one channel. + float csum = factors.r + factors.g + factors.b + factors.a; + float single = clamp(2.0 - csum, 0.0, 1.0); + for (int i = 0; i < 4; i++) { + float c = input_color[i]; + output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single); + } + + return output_color; +} + +void fragment() { + COLOR = filter_preview_colors(textureLod(TEXTURE, UV, lod), u_channel_factors); +} +)"; + +void TexturePreviewer::init_shaders() { + texture_material.instantiate(); + + Ref texture_shader; + texture_shader.instantiate(); + texture_shader->set_code(texture_2d_shader_code); + + texture_material->set_shader(texture_shader); +} + +void TexturePreviewer::finish_shaders() { + texture_material.unref(); +} + +TextureRect *TexturePreviewer::get_texture_display() { + return texture_display; +} + +void TexturePreviewer::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + if (!is_inside_tree()) { + // TODO: This is a workaround because `NOTIFICATION_THEME_CHANGED` + // is getting called for some reason when the `TexturePreviewer` is + // getting destroyed, which causes `get_theme_font()` to return `nullptr`. + // See https://github.com/godotengine/godot/issues/50743. + break; + } + + if (metadata_label) { + Ref metadata_label_font = get_theme_font(SNAME("expression"), SNAME("Editor")); + metadata_label->add_theme_font_override(SceneStringName(font), metadata_label_font); + } + + bg_rect->set_color(get_theme_color(SNAME("dark_color_2"), SNAME("Editor"))); + checkerboard->set_texture(GDREGuiIcons::get_icon(SNAME("Checkerboard"))); + theme_cache.outline_color = get_theme_color(SNAME("extra_border_color_1"), SNAME("Editor")); + } break; + } +} + +void TexturePreviewer::_draw_outline() { + const float outline_width = Math::round(GDRESettings::get_auto_display_scale()); + const Rect2 outline_rect = Rect2(Vector2(), outline_overlay->get_size()).grow(outline_width * 0.5); + outline_overlay->draw_rect(outline_rect, theme_cache.outline_color, false, outline_width); +} + +void TexturePreviewer::_update_texture_display_ratio() { + if (texture_display->get_texture().is_valid()) { + centering_container->set_ratio(texture_display->get_texture()->get_size().aspect()); + } +} + +static Image::Format get_texture_2d_format(const Ref &p_texture) { + const Ref rd_texture = p_texture; + if (rd_texture.is_valid() && RD::get_singleton() && RD::get_singleton()->texture_is_valid(rd_texture->get_texture_rd_rid())) { + return rd_texture->get_image()->get_format(); + } + + return p_texture->get_format(); +} + +static int get_texture_mipmaps_count(const Ref &p_texture) { + ERR_FAIL_COND_V(p_texture.is_null(), -1); + + // We are having to download the image only to get its mipmaps count. It would be nice if we didn't have to. + Ref image; + Ref at = p_texture; + Ref rd_texture = p_texture; + + if (at.is_valid()) { + // The AtlasTexture tries to obtain the region from the atlas as an image, + // which will fail if it is a compressed format. + Ref atlas = at->get_atlas(); + if (atlas.is_valid()) { + image = atlas->get_image(); + } + } else if (rd_texture.is_valid()) { + if (RD::get_singleton() && RD::get_singleton()->texture_is_valid(rd_texture->get_texture_rd_rid())) { + return -1; + } + image = p_texture->get_image(); + } else { + image = p_texture->get_image(); + } + + if (image.is_valid()) { + return image->get_mipmap_count(); + } + return -1; +} + +void TexturePreviewer::gui_input(const Ref &p_event) { + // TODO: This isn't currently working, figure out a way to properly do zooming and panning + // ERR_FAIL_COND(p_event.is_null()); + + // Ref mm = p_event; + // if (mm.is_valid()) { + // if ((mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { + // position_x -= mm->get_relative().x; + // position_y -= mm->get_relative().y; + + // _update_position_and_scale(); + // } + // } + + // Ref mg = p_event; + // if (mg.is_valid()) { + // scale *= mg->get_factor(); + // _update_position_and_scale(); + // } + + // Ref mb = p_event; + // if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { + // scale *= 1.1; + // _update_position_and_scale(); + // } + // if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) { + // scale *= 0.9; + // _update_position_and_scale(); + // } + // Ref key = p_event; + // if (key.is_valid() && key->is_pressed() && (key->get_keycode() == Key::EQUAL || key->get_keycode() == Key::PLUS)) { + // scale *= 1.1; + // _update_position_and_scale(); + // } else if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::MINUS) { + // scale *= 0.9; + // _update_position_and_scale(); + // } +} + +void TexturePreviewer::_update_position_and_scale() { + // texture_display->set_position(Vector2(position_x, position_y)); + // texture_display->set_scale(Vector2(scale, scale)); +} + +void TexturePreviewer::_update_metadata_label_text() { + const Ref texture = texture_display->get_texture(); + ERR_FAIL_COND(texture.is_null()); + + Image::Format format; + int mipmaps; + + Ref image = texture->get_image(); + if (image.is_valid()) { + format = image->get_format(); + mipmaps = image->get_mipmap_count(); + } else { + format = get_texture_2d_format(texture.ptr()); + mipmaps = get_texture_mipmaps_count(texture); + } + + const String format_name = format != Image::FORMAT_MAX ? Image::get_format_name(format) : texture->get_class(); + + const Vector2i resolution = texture->get_size(); + + if (format != Image::FORMAT_MAX) { + // Avoid signed integer overflow that could occur with huge texture sizes by casting everything to uint64_t. + uint64_t memory = uint64_t(resolution.x) * uint64_t(resolution.y) * uint64_t(Image::get_format_pixel_size(format)); + // Handle VRAM-compressed formats that are stored with 4 bpp. + memory >>= Image::get_format_pixel_rshift(format); + + float mipmaps_multiplier = 1.0; + float mipmap_increase = 0.25; + for (int i = 0; i < mipmaps; i++) { + // Each mip adds 25% memory usage of the previous one. + // With a complete mipmap chain, memory usage increases by ~33%. + mipmaps_multiplier += mipmap_increase; + mipmap_increase *= 0.25; + } + memory *= mipmaps_multiplier; + + if (mipmaps >= 1) { + metadata_label->set_text( + vformat(String::utf8("%d×%d %s\n") + RTR("%s Mipmaps") + "\n" + RTR("Memory: %s"), + texture->get_width(), + texture->get_height(), + format_name, + mipmaps, + String::humanize_size(memory))); + } else { + // "No Mipmaps" is easier to distinguish than "0 Mipmaps", + // especially since 0, 6, and 8 look quite close with the default code font. + metadata_label->set_text( + vformat(String::utf8("%d×%d %s\n") + RTR("No Mipmaps") + "\n" + RTR("Memory: %s"), + texture->get_width(), + texture->get_height(), + format_name, + String::humanize_size(memory))); + } + } else { + metadata_label->set_text( + vformat(String::utf8("%d×%d %s"), + texture->get_width(), + texture->get_height(), + format_name)); + } +} + +void TexturePreviewer::on_selected_channels_changed() { + texture_display->set_instance_shader_parameter("u_channel_factors", channel_selector->get_selected_channel_factors()); +} + +void TexturePreviewer::on_selected_mipmap_changed(double p_value) { + texture_display->set_instance_shader_parameter("lod", mipmap_spinbox->get_value()); +} + +void TexturePreviewer::edit(Ref p_texture, bool p_show_metadata) { + texture_display->set_texture(p_texture); + if (p_texture.is_valid()) { + _update_texture_display_ratio(); + p_texture->connect_changed(callable_mp(this, &TexturePreviewer::_update_texture_display_ratio)); + } + + // Null can be passed by `Camera3DPreview` (which immediately after sets a texture anyways). + const Image::Format format = p_texture.is_valid() ? get_texture_2d_format(p_texture.ptr()) : Image::FORMAT_MAX; + const uint32_t components_mask = format != Image::FORMAT_MAX ? Image::get_format_component_mask(format) : 0xf; + + // Setup Mipmap selector. + const int mipmaps = get_texture_mipmaps_count(p_texture); + if (mipmaps > 0) { + mipmap_spinbox->set_max(mipmaps); + mipmap_spinbox->set_value(0); + mipmap_spinbox->set_visible(true); + } else { + mipmap_spinbox->set_visible(false); + } + + // Add color channel selector at the bottom left if more than 1 channel is available. + if (p_show_metadata && !Math::is_power_of_2(components_mask)) { + channel_selector->set_available_channels_mask(components_mask); + channel_selector->set_visible(true); + } else { + channel_selector->set_visible(false); + } + + if (p_show_metadata) { + if (p_texture.is_valid()) { + _update_metadata_label_text(); + p_texture->connect_changed(callable_mp(this, &TexturePreviewer::_update_metadata_label_text)); + metadata_label->set_visible(true); + } + } else { + metadata_label->set_visible(false); + } + _update_position_and_scale(); +} + +String TexturePreviewer::get_edited_resource_path() const { + auto texture = texture_display->get_texture(); + if (texture.is_valid()) { + return texture->get_path(); + } + return ""; +} + +void TexturePreviewer::reset() { + auto texture = texture_display->get_texture(); + if (texture.is_valid()) { + texture->disconnect_changed(callable_mp(this, &TexturePreviewer::_update_texture_display_ratio)); + if (texture->is_connected(CoreStringName(changed), callable_mp(this, &TexturePreviewer::_update_metadata_label_text))) { + texture->disconnect_changed(callable_mp(this, &TexturePreviewer::_update_metadata_label_text)); + } + } + texture_display->set_texture(nullptr); + mipmap_spinbox->set_value(0); + channel_selector->set_available_channels_mask(0xf); + channel_selector->set_visible(false); + metadata_label->set_text(""); + metadata_label->set_visible(false); + mipmap_spinbox->set_visible(false); + position_x = 0.0; + position_y = 0.0; + scale = 1.0; +} + +TexturePreviewer::TexturePreviewer() { + set_custom_minimum_size(Size2(0.0, 256.0) * GDRESettings::get_auto_display_scale()); + bg_rect = memnew(ColorRect); + + add_child(bg_rect); + + margin_container = memnew(MarginContainer); + const float outline_width = Math::round(GDRESettings::get_auto_display_scale()); + margin_container->add_theme_constant_override("margin_right", outline_width); + margin_container->add_theme_constant_override("margin_top", outline_width); + margin_container->add_theme_constant_override("margin_left", outline_width); + margin_container->add_theme_constant_override("margin_bottom", outline_width); + add_child(margin_container); + + centering_container = memnew(AspectRatioContainer); + centering_container->set_clip_children_mode(ClipChildrenMode::CLIP_CHILDREN_AND_DRAW); + margin_container->add_child(centering_container); + + checkerboard = memnew(TextureRect); + checkerboard->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); + checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE); + checkerboard->set_texture_repeat(CanvasItem::TEXTURE_REPEAT_ENABLED); + centering_container->add_child(checkerboard); + + texture_display = memnew(TextureRect); + texture_display->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); + // texture_display->set_texture(p_texture); + texture_display->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); + texture_display->set_material(texture_material); + texture_display->set_instance_shader_parameter("u_channel_factors", Vector4(1, 1, 1, 1)); + centering_container->add_child(texture_display); + + // Creating a separate control so it is not affected by the filtering shader. + outline_overlay = memnew(Control); + centering_container->add_child(outline_overlay); + + outline_overlay->connect(SceneStringName(draw), callable_mp(this, &TexturePreviewer::_draw_outline)); + + // Setup Mipmap selector. + mipmap_spinbox = memnew(SpinBox); + mipmap_spinbox->set_tooltip_text(TTRC("Mipmap level index selector.")); + mipmap_spinbox->set_max(0); + mipmap_spinbox->set_modulate(Color(1, 1, 1, 0.8)); + mipmap_spinbox->set_h_grow_direction(GROW_DIRECTION_BEGIN); + mipmap_spinbox->set_h_size_flags(Control::SIZE_SHRINK_END); + mipmap_spinbox->set_v_size_flags(Control::SIZE_SHRINK_BEGIN); + mipmap_spinbox->set_anchors_preset(Control::PRESET_TOP_RIGHT); + mipmap_spinbox->connect(SceneStringName(value_changed), callable_mp(this, &TexturePreviewer::on_selected_mipmap_changed)); + add_child(mipmap_spinbox); + + channel_selector = memnew(GDREColorChannelSelector); + channel_selector->connect("selected_channels_changed", callable_mp(this, &TexturePreviewer::on_selected_channels_changed)); + channel_selector->set_h_size_flags(Control::SIZE_SHRINK_BEGIN); + channel_selector->set_v_size_flags(Control::SIZE_SHRINK_BEGIN); + add_child(channel_selector); + + metadata_label = memnew(Label); + metadata_label->set_focus_mode(FOCUS_ACCESSIBILITY); + + // It's okay that these colors are static since the grid color is static too. + metadata_label->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1)); + metadata_label->add_theme_color_override("font_shadow_color", Color(0, 0, 0)); + + metadata_label->add_theme_font_size_override(SceneStringName(font_size), 14 * GDRESettings::get_auto_display_scale()); + metadata_label->add_theme_color_override("font_outline_color", Color(0, 0, 0)); + metadata_label->add_theme_constant_override("outline_size", 8 * GDRESettings::get_auto_display_scale()); + + metadata_label->set_h_size_flags(Control::SIZE_SHRINK_END); + metadata_label->set_v_size_flags(Control::SIZE_SHRINK_END); + add_child(metadata_label); +} + +void TexturePreviewer::_bind_methods() { + ClassDB::bind_method(D_METHOD("edit", "texture", "show_metadata"), &TexturePreviewer::edit, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("reset"), &TexturePreviewer::reset); + ClassDB::bind_method(D_METHOD("get_edited_resource_path"), &TexturePreviewer::get_edited_resource_path); +} diff --git a/gui/texture_previewer.h b/gui/texture_previewer.h new file mode 100644 index 000000000..5844730d1 --- /dev/null +++ b/gui/texture_previewer.h @@ -0,0 +1,58 @@ +#pragma once +#include "scene/gui/margin_container.h" +#include "scene/resources/texture.h" + +class AspectRatioContainer; +class ColorRect; +class TextureRect; +class ShaderMaterial; +class GDREColorChannelSelector; +class SpinBox; + +class TexturePreviewer : public MarginContainer { + GDCLASS(TexturePreviewer, MarginContainer); + +private: + struct ThemeCache { + Color outline_color; + } theme_cache; + + TextureRect *texture_display = nullptr; + + MarginContainer *margin_container = nullptr; + Control *outline_overlay = nullptr; + AspectRatioContainer *centering_container = nullptr; + ColorRect *bg_rect = nullptr; + TextureRect *checkerboard = nullptr; + Label *metadata_label = nullptr; + float scale = 1.0; + float position_x = 0.0; + float position_y = 0.0; + + static inline Ref texture_material; + + GDREColorChannelSelector *channel_selector = nullptr; + SpinBox *mipmap_spinbox = nullptr; + + void _draw_outline(); + void _update_metadata_label_text(); + void _update_position_and_scale(); + +protected: + void _notification(int p_what); + void _update_texture_display_ratio(); + void on_selected_channels_changed(); + void on_selected_mipmap_changed(double p_value); + static void _bind_methods(); + void gui_input(const Ref &p_event) override; + +public: + static void init_shaders(); + static void finish_shaders(); + + TextureRect *get_texture_display(); + void edit(Ref p_texture, bool p_show_metadata = true); + void reset(); + String get_edited_resource_path() const; + TexturePreviewer(); +}; diff --git a/register_types.cpp b/register_types.cpp index afcfaa9ab..ff35871f8 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -13,6 +13,7 @@ #include "gui/gdre_progress.h" #include "gui/gdre_standalone.h" #include "gui/texture_layered_previewer.h" +#include "gui/texture_previewer.h" #include "modules/regex/regex.h" #include "modules/register_module_types.h" #include "utility/app_version_getter.h" @@ -577,6 +578,7 @@ void initialize_gdsdecomp_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -617,6 +619,7 @@ void initialize_gdsdecomp_module(ModuleInitializationLevel p_level) { ico_loader.instantiate(); ImageLoader::add_image_format_loader(ico_loader); TextureLayeredPreviewer::init_shaders(); + TexturePreviewer::init_shaders(); } void uninitialize_gdsdecomp_module(ModuleInitializationLevel p_level) { @@ -624,6 +627,7 @@ void uninitialize_gdsdecomp_module(ModuleInitializationLevel p_level) { return; } TextureLayeredPreviewer::finish_shaders(); + TexturePreviewer::finish_shaders(); if (ico_loader.is_valid()) { ImageLoader::remove_image_format_loader(ico_loader); ico_loader.unref(); diff --git a/standalone/gdre_config_dialog.gd b/standalone/gdre_config_dialog.gd index 4834236e5..2629492e3 100644 --- a/standalone/gdre_config_dialog.gd +++ b/standalone/gdre_config_dialog.gd @@ -96,12 +96,6 @@ func make_button_label(text: String) -> Label: func set_setting_value(setting: GDREConfigSetting, value: Variant): setting_value_map[setting] = value -func popup_error(message: String): - %ErrorDialog.dialog_text = message - %ErrorDialog.popup_centered() - - - func setting_callback(setting: GDREConfigSetting, value: Variant, control: Control): print('setting "' + setting.get_brief_description() + '" set to ', value) var reset = false @@ -123,7 +117,7 @@ func setting_callback(setting: GDREConfigSetting, value: Variant, control: Contr force_change = true setting_value_map[s] = s.get_value() else: - popup_error(error_message) + popup_error_box(error_message) setting.clear_error_message() else: @@ -387,7 +381,6 @@ func _close_requested(): # so we have to re-show ourselves when a close is requested self.show() unclose = true - print("has unsaved changes") %ConfirmClose.popup_centered() return #print("closing") diff --git a/standalone/gdre_config_dialog.tscn b/standalone/gdre_config_dialog.tscn index c497e9cbb..82d438db0 100644 --- a/standalone/gdre_config_dialog.tscn +++ b/standalone/gdre_config_dialog.tscn @@ -215,10 +215,6 @@ unique_name_in_owner = true access = 2 use_native_dialog = true -[node name="ErrorDialog" type="GDREAcceptDialogBase" parent="." unique_id=419260906] -unique_name_in_owner = true -title = "Error" - [connection signal="close_requested" from="." to="." method="_close_requested"] [connection signal="canceled" from="ConfirmClose" to="." method="_on_confirm_close_canceled"] [connection signal="confirmed" from="ConfirmClose" to="." method="_on_confirm_close_confirmed"] diff --git a/standalone/gdre_patch_pck.tscn b/standalone/gdre_patch_pck.tscn index abd104d00..ba67394ef 100644 --- a/standalone/gdre_patch_pck.tscn +++ b/standalone/gdre_patch_pck.tscn @@ -17,9 +17,6 @@ dialog_hide_on_ok = false script = ExtResource("1_p07vp") metadata/_custom_type_script = "uid://cbggqwojxui00" -[node name="ErrorDialog" type="GDREAcceptDialogBase" parent="." unique_id=1566817302] -unique_name_in_owner = true - [node name="SelectPckDialog" type="GDREFileDialog" parent="." unique_id=1540086483] unique_name_in_owner = true title = "Open a File" diff --git a/standalone/gdre_resource_preview.gd b/standalone/gdre_resource_preview.gd index 1eedf8e8d..2529a72c6 100644 --- a/standalone/gdre_resource_preview.gd +++ b/standalone/gdre_resource_preview.gd @@ -79,8 +79,7 @@ func _reset(): _make_all_views_invisible() %MediaPlayer.reset() %TextView.reset() - %TextureInfo.text = "" - %TextureRect.texture = null + %TextureView.reset() %ResourceInfo.text = "" %MeshPreviewer.reset() %ScenePreviewer3D.reset() @@ -91,25 +90,16 @@ var previous_res_info_size = Vector2(0, 0) func load_texture(path): var ext = path.get_extension().to_lower() + var texture = null if (ext == "image"): - %TextureRect.texture = ImageTexture.create_from_image(ResourceCompatLoader.real_load(path, "", ResourceCompatLoader.CACHE_MODE_IGNORE_DEEP)) + texture = ImageTexture.create_from_image(ResourceCompatLoader.real_load(path, "", ResourceCompatLoader.CACHE_MODE_IGNORE_DEEP)) elif (is_image(ext)): - %TextureRect.texture = ImageTexture.create_from_image(GDRECommon.load_image_from_file(path)) + texture = ImageTexture.create_from_image(GDRECommon.load_image_from_file(path)) else: - %TextureRect.texture = ResourceCompatLoader.real_load(path, "", ResourceCompatLoader.CACHE_MODE_IGNORE_DEEP) # TODO: handle other texture types - if (%TextureRect.texture == null): + texture = ResourceCompatLoader.real_load(path, "", ResourceCompatLoader.CACHE_MODE_IGNORE_DEEP) # TODO: handle other texture types + if (texture == null): return false - %TextureRect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE - %TextureRect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - var image = %TextureRect.texture.get_image() - var info_text = str(%TextureRect.texture.get_width()) + "x" + str(%TextureRect.texture.get_height()) + " " + IMAGE_FORMAT_NAME[image.get_format()] - if image.has_mipmaps(): - - info_text += "\n" + str(image.get_mipmap_count()) + " Mipmaps" + "\n" + "Memory: " + String.humanize_size(image.get_data_size()) - else: - info_text += "\n" + "No Mipmaps" + "\n" + "Memory: " + String.humanize_size(image.get_data_size()) - - %TextureInfo.text = info_text + %TextureView.edit(texture) %TextureView.visible = true return true diff --git a/standalone/gdre_resource_preview.tscn b/standalone/gdre_resource_preview.tscn index 1fe4a0bea..6416c8692 100644 --- a/standalone/gdre_resource_preview.tscn +++ b/standalone/gdre_resource_preview.tscn @@ -60,7 +60,7 @@ custom_minimum_size = Vector2(64, 64) layout_mode = 2 theme_override_styles/panel = SubResource("StyleBoxFlat_gp5ss") -[node name="TextureView" type="Control" parent="VBoxContainer/RootView/ResourceView" unique_id=494726531] +[node name="TextureView" type="TexturePreviewer" parent="VBoxContainer/RootView/ResourceView" unique_id=494726531] unique_name_in_owner = true visible = false layout_mode = 1 @@ -70,39 +70,6 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="TextureRect" type="TextureRect" parent="VBoxContainer/RootView/ResourceView/TextureView" unique_id=1503017689] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -texture = ExtResource("3_gp5ss") -expand_mode = 1 -stretch_mode = 5 - -[node name="TextureInfo" type="Label" parent="VBoxContainer/RootView/ResourceView/TextureView" unique_id=1618224773] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = -1 -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -117.0 -offset_top = -74.0 -grow_horizontal = 0 -grow_vertical = 0 -layout_direction = 2 -theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) -theme_override_colors/font_outline_color = Color(0, 0, 0, 1) -theme_override_constants/outline_size = 8 -theme_override_font_sizes/font_size = 14 -text = "16x16 RGBA8 -No Mipmaps -Memory: 1024 B" - [node name="MediaPlayer" parent="VBoxContainer/RootView/ResourceView" unique_id=326216806 instance=ExtResource("4_crn14")] unique_name_in_owner = true visible = false diff --git a/utility/common.cpp b/utility/common.cpp index 745e1ee0d..7b76103a7 100644 --- a/utility/common.cpp +++ b/utility/common.cpp @@ -2,18 +2,16 @@ #include "bytecode/bytecode_base.h" #include "compat/file_access_encrypted_v3.h" #include "compat/variant_decoder_compat.h" -#include "utility/file_access_buffer.h" #include "utility/glob.h" #include "core/error/error_list.h" #include "core/error/error_macros.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" -#include "core/io/http_client.h" +#include "core/io/image_loader.h" #include "core/object/class_db.h" #include "modules/regex/regex.h" #include "modules/zip/zip_reader.h" -#include "utility/gdre_logger.h" #include "utility/task_manager.h" namespace { @@ -1155,13 +1153,13 @@ String gdre::get_safe_dir_name(const String &p_dir_name, bool p_allow_paths) { } Ref gdre::load_image_from_file(const String &p_path) { -#ifdef DEBUG_ENABLED - GDRELogger::set_thread_local_silent_errors(true); -#endif - Ref image = Image::load_from_file(p_path); -#ifdef DEBUG_ENABLED - GDRELogger::set_thread_local_silent_errors(false); -#endif + Ref image; + image.instantiate(); + Error err = ImageLoader::load_image(p_path, image); + ERR_FAIL_COND_V_MSG(err == ERR_FILE_UNRECOGNIZED, Ref(), vformat("Image has unsupported format: %s", p_path)); + if (err != OK) { + return Ref(); + } return image; } diff --git a/utility/file_access_apk.cpp b/utility/file_access_apk.cpp index b8c102500..b05266413 100644 --- a/utility/file_access_apk.cpp +++ b/utility/file_access_apk.cpp @@ -315,7 +315,7 @@ bool APKArchive::handle_xapk(const String &pack_path, ManifestInfo &r_manifest_i bool APKArchive::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { // load with offset feature only supported for PCK files ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with ZIP archives."); - String pack_path = p_path.replace("_GDRE_a_really_dumb_hack", ""); + String pack_path = p_path; String ext = pack_path.get_extension().to_lower(); // This handles zip files, too bool is_xapk = ext == "xapk"; diff --git a/utility/gdre_config.cpp b/utility/gdre_config.cpp index 11e64ba77..57cbd4d13 100644 --- a/utility/gdre_config.cpp +++ b/utility/gdre_config.cpp @@ -9,6 +9,7 @@ #include "core/os/os.h" #include "gdre_logger.h" #include "gdre_settings.h" +#include "gdre_version.gen.h" #include "godot_mono_decomp_wrapper.h" GDREConfig *GDREConfig::singleton = nullptr; @@ -206,8 +207,15 @@ class GDREConfigSetting_DefaultParentFolderForRecovery : public GDREConfigSettin virtual bool is_dirpicker() const override { return true; } }; -Vector> GDREConfig::_init_default_settings() { - return { +HashMap> GDREConfig::_init_default_settings() { + Vector> settings = { + memnew(GDREConfigSetting( + "last_gdre_version_used", + "Last GDRE version used", + "", + "", + true, + false)), memnew(GDREConfigSetting( "download_plugins", "Download plugins", @@ -394,6 +402,11 @@ Vector> GDREConfig::_init_default_settings() { false, true)), }; + HashMap> default_settings; + for (const auto &setting : settings) { + default_settings[setting->full_name] = setting; + } + return default_settings; } // static const HashMap deprecated_setting_mappings = { @@ -415,13 +428,20 @@ GDREConfig::~GDREConfig() { } TypedArray GDREConfig::get_all_settings() const { - return gdre::vector_to_typed_array(default_settings); + TypedArray settings; + settings.resize(default_settings.size()); + int i = 0; + for (const auto &[key, setting] : default_settings) { + settings.set(i, setting); + i++; + } + return settings; } void GDREConfig::load_config() { settings.clear(); - for (const auto &setting : default_settings) { + for (const auto &[key, setting] : default_settings) { if (setting->is_virtual_setting()) { continue; } @@ -451,8 +471,12 @@ void GDREConfig::save_config() { auto cfg_path = get_config_path(); Ref config = memnew(ConfigFile); for (const auto &[key, value] : settings) { - config->set_value(get_section_from_key(key), get_name_from_key(key), value); + String name = get_name_from_key(key); + if (!default_settings.has(key) || get_default_value(key) != value) { + config->set_value(get_section_from_key(key), name, value); + } } + config->set_value("General", "last_gdre_version_used", GDRE_VERSION); gdre::ensure_dir(cfg_path.get_base_dir()); Error err = config->save(cfg_path); if (err != OK) { @@ -461,7 +485,7 @@ void GDREConfig::save_config() { } String get_full_name(const String &p_setting) { - if (!p_setting.contains("/")) { + if (!p_setting.contains_char('/')) { return "General/" + p_setting; } return p_setting; @@ -489,11 +513,10 @@ Variant GDREConfig::get_setting(const String &p_setting, const Variant &p_defaul } String GDREConfig::get_section_from_key(const String &p_setting) { - auto parts = p_setting.split("/", true, 1); - if (parts.size() == 1) { + if (!p_setting.contains_char('/')) { return "General"; } - return parts[0]; + return p_setting.get_slice("/", 0); } String GDREConfig::get_name_from_key(const String &p_setting) { @@ -506,17 +529,15 @@ String GDREConfig::get_name_from_key(const String &p_setting) { Variant GDREConfig::get_default_value(const String &p_setting) const { String full_name = get_full_name(p_setting); - for (const auto &setting : default_settings) { - if (setting->get_full_name() == full_name) { - return setting->get_default_value(); - } + if (default_settings.has(full_name)) { + return default_settings[full_name]->get_default_value(); } return Variant(); } void GDREConfig::reset_ephemeral_settings() { ephemeral_settings.clear(); - for (const auto &setting : default_settings) { + for (const auto &[key, setting] : default_settings) { if (setting->is_ephemeral()) { Variant default_value = setting->get_default_value(); settings.if_contains(setting->get_full_name(), [&](const auto &v) { default_value = v.second; }); diff --git a/utility/gdre_config.h b/utility/gdre_config.h index bf689e832..a438c1c6a 100644 --- a/utility/gdre_config.h +++ b/utility/gdre_config.h @@ -59,10 +59,10 @@ class GDREConfig : public Object { ParallelFlatHashMap settings; static GDREConfig *singleton; - Vector> default_settings; + HashMap> default_settings; ParallelFlatHashMap ephemeral_settings; - static Vector> _init_default_settings(); + static HashMap> _init_default_settings(); static String get_config_path(); diff --git a/utility/gdre_packed_source.cpp b/utility/gdre_packed_source.cpp index 1fe963ff7..35d419d0e 100644 --- a/utility/gdre_packed_source.cpp +++ b/utility/gdre_packed_source.cpp @@ -316,7 +316,7 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, if (ext == "apk" || ext == "zip") { return false; } - String pck_path = p_path.replace("_GDRE_a_really_dumb_hack", ""); + String pck_path = p_path; Ref f = FileAccess::open(pck_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), false, "Failed to open pack file: " + pck_path);