Merge pull request #49997 from akien-mga/3.x-cherrypicks
This commit is contained in:
commit
48fe8da245
41 changed files with 327 additions and 116 deletions
|
@ -1740,6 +1740,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
|
|||
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
|
||||
ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
|
||||
ERR_FAIL_COND_MSG(write_lock.ptr(), "Cannot create image when it is locked.");
|
||||
ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum.");
|
||||
|
||||
int mm = 0;
|
||||
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
|
||||
|
@ -1760,6 +1761,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
|
|||
ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
|
||||
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
|
||||
ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
|
||||
ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum.");
|
||||
|
||||
int mm;
|
||||
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
|
||||
|
@ -2113,6 +2115,8 @@ Error Image::decompress() {
|
|||
}
|
||||
|
||||
Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) {
|
||||
ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode.");
|
||||
ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source.");
|
||||
switch (p_mode) {
|
||||
case COMPRESS_S3TC: {
|
||||
ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
|
||||
|
@ -2138,6 +2142,9 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss
|
|||
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
|
||||
_image_compress_bptc_func(this, p_lossy_quality, p_source);
|
||||
} break;
|
||||
case COMPRESS_MAX: {
|
||||
ERR_FAIL_V(ERR_INVALID_PARAMETER);
|
||||
} break;
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
|
|
@ -124,6 +124,7 @@ public:
|
|||
COMPRESS_SOURCE_SRGB,
|
||||
COMPRESS_SOURCE_NORMAL,
|
||||
COMPRESS_SOURCE_LAYERED,
|
||||
COMPRESS_SOURCE_MAX,
|
||||
};
|
||||
|
||||
//some functions provided by something else
|
||||
|
@ -304,7 +305,8 @@ public:
|
|||
COMPRESS_PVRTC4,
|
||||
COMPRESS_ETC,
|
||||
COMPRESS_ETC2,
|
||||
COMPRESS_BPTC
|
||||
COMPRESS_BPTC,
|
||||
COMPRESS_MAX,
|
||||
};
|
||||
|
||||
Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
Class representing a cylindrical [PrimitiveMesh].
|
||||
</brief_description>
|
||||
<description>
|
||||
Class representing a cylindrical [PrimitiveMesh]. This class can be used to create cones by setting either the [member top_radius] or [member bottom_radius] properties to 0.0.
|
||||
Class representing a cylindrical [PrimitiveMesh]. This class can be used to create cones by setting either the [member top_radius] or [member bottom_radius] properties to [code]0.0[/code].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -12,19 +12,19 @@
|
|||
</methods>
|
||||
<members>
|
||||
<member name="bottom_radius" type="float" setter="set_bottom_radius" getter="get_bottom_radius" default="1.0">
|
||||
Bottom radius of the cylinder.
|
||||
Bottom radius of the cylinder. If set to [code]0.0[/code], the bottom faces will not be generated, resulting in a conic shape.
|
||||
</member>
|
||||
<member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
|
||||
Full height of the cylinder.
|
||||
</member>
|
||||
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="64">
|
||||
Number of radial segments on the cylinder.
|
||||
Number of radial segments on the cylinder. Higher values result in a more detailed cylinder/cone at the cost of performance.
|
||||
</member>
|
||||
<member name="rings" type="int" setter="set_rings" getter="get_rings" default="4">
|
||||
Number of edge rings along the height of the cylinder.
|
||||
Number of edge rings along the height of the cylinder. Changing [member rings] does not have any visual impact unless a shader or procedural mesh tool is used to alter the vertex data. Higher values result in more subdivisions, which can be used to create smoother-looking effects with shaders or procedural mesh tools (at the cost of performance). When not altering the vertex data using a shader or procedural mesh tool, [member rings] should be kept to its default value.
|
||||
</member>
|
||||
<member name="top_radius" type="float" setter="set_top_radius" getter="get_top_radius" default="1.0">
|
||||
Top radius of the cylinder.
|
||||
Top radius of the cylinder. If set to [code]0.0[/code], the top faces will not be generated, resulting in a conic shape.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
|
|
|
@ -450,6 +450,7 @@
|
|||
</argument>
|
||||
<description>
|
||||
Stores any Variant value in the file. If [code]full_objects[/code] is [code]true[/code], encoding objects is allowed (and can potentially include code).
|
||||
[b]Note:[/b] Not all properties are included. Only properties that are configured with the [constant PROPERTY_USAGE_STORAGE] flag set will be serialized. You can add a new usage flag to a property by overriding the [method Object._get_property_list] method in your class. You can also check how property usage is configured by calling [method Object._get_property_list]. See [enum PropertyUsageFlags] for the possible usage flags.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
If [code]true[/code], wraps the text inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text.
|
||||
</member>
|
||||
<member name="clip_text" type="bool" setter="set_clip_text" getter="is_clipping_text" default="false">
|
||||
If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle. It also lets you scale the node down freely.
|
||||
If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle and will clip text horizontally.
|
||||
</member>
|
||||
<member name="lines_skipped" type="int" setter="set_lines_skipped" getter="get_lines_skipped" default="0">
|
||||
The node ignores the first [code]lines_skipped[/code] lines before it starts to display text.
|
||||
|
|
|
@ -245,10 +245,12 @@
|
|||
Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method OS.set_native_icon].
|
||||
</member>
|
||||
<member name="application/run/disable_stderr" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], disables printing to standard error in an exported build.
|
||||
If [code]true[/code], disables printing to standard error. If [code]true[/code], this also hides error and warning messages printed by [method @GDScript.push_error] and [method @GDScript.push_warning]. See also [member application/run/disable_stdout].
|
||||
Changes to this setting will only be applied upon restarting the application.
|
||||
</member>
|
||||
<member name="application/run/disable_stdout" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], disables printing to standard output in an exported build.
|
||||
If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] command line argument. See also [member application/run/disable_stderr].
|
||||
Changes to this setting will only be applied upon restarting the application.
|
||||
</member>
|
||||
<member name="application/run/flush_stdout_on_print" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], flushes the standard output stream every time a line is printed. This affects both terminal logging and file logging.
|
||||
|
|
|
@ -1175,10 +1175,7 @@ public:
|
|||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
if (!same_key_type) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (same_key_type) {
|
||||
Variant v = animation->track_get_key_value(first_track, first_key);
|
||||
|
||||
if (hint.type != Variant::NIL) {
|
||||
|
@ -1202,6 +1199,7 @@ public:
|
|||
p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
|
||||
} break;
|
||||
|
|
|
@ -609,6 +609,7 @@ void EditorNode::_notification(int p_what) {
|
|||
p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons"));
|
||||
p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons"));
|
||||
p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("Instance", "EditorIcons"));
|
||||
p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_icon("Instance", "EditorIcons"));
|
||||
p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_icon("Instance", "EditorIcons"));
|
||||
p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("Instance", "EditorIcons"));
|
||||
p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons"));
|
||||
|
@ -1516,15 +1517,6 @@ void EditorNode::_save_scene(String p_file, int idx) {
|
|||
return;
|
||||
}
|
||||
|
||||
// force creation of node path cache
|
||||
// (hacky but needed for the tree to update properly)
|
||||
Node *dummy_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
|
||||
if (!dummy_scene) {
|
||||
show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK"));
|
||||
return;
|
||||
}
|
||||
memdelete(dummy_scene);
|
||||
|
||||
int flg = 0;
|
||||
if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources")) {
|
||||
flg |= ResourceSaver::FLAG_COMPRESS;
|
||||
|
@ -2803,6 +2795,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
|
|||
case HELP_REPORT_A_BUG: {
|
||||
OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
|
||||
} break;
|
||||
case HELP_SUGGEST_A_FEATURE: {
|
||||
OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme");
|
||||
} break;
|
||||
case HELP_SEND_DOCS_FEEDBACK: {
|
||||
OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues");
|
||||
} break;
|
||||
|
@ -6364,6 +6359,7 @@ EditorNode::EditorNode() {
|
|||
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
|
||||
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA);
|
||||
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
|
||||
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
|
||||
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
|
||||
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
|
||||
p->add_separator();
|
||||
|
|
|
@ -200,6 +200,7 @@ private:
|
|||
HELP_DOCS,
|
||||
HELP_QA,
|
||||
HELP_REPORT_A_BUG,
|
||||
HELP_SUGGEST_A_FEATURE,
|
||||
HELP_SEND_DOCS_FEEDBACK,
|
||||
HELP_COMMUNITY,
|
||||
HELP_ABOUT,
|
||||
|
|
|
@ -5844,7 +5844,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
|
|||
|
||||
p = view_menu->get_popup();
|
||||
p->set_hide_on_checkable_item_selection(false);
|
||||
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), KEY_MASK_CTRL | KEY_G), SHOW_GRID);
|
||||
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), KEY_MASK_CMD | KEY_G), SHOW_GRID);
|
||||
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS);
|
||||
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers")), SHOW_RULERS);
|
||||
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES);
|
||||
|
|
|
@ -6345,7 +6345,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
|
|||
|
||||
p->add_separator();
|
||||
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
|
||||
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID);
|
||||
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), KEY_MASK_CMD + KEY_G), MENU_VIEW_GRID);
|
||||
|
||||
p->add_separator();
|
||||
p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS);
|
||||
|
|
|
@ -103,17 +103,16 @@ void SpriteFramesEditor::_sheet_preview_draw() {
|
|||
split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d Frame(s)"), frames_selected.size()));
|
||||
}
|
||||
void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
|
||||
const Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
|
||||
Size2i size = split_sheet_preview->get_size();
|
||||
int h = split_sheet_h->get_value();
|
||||
int v = split_sheet_v->get_value();
|
||||
const Size2i size = split_sheet_preview->get_size();
|
||||
const int h = split_sheet_h->get_value();
|
||||
const int v = split_sheet_v->get_value();
|
||||
|
||||
int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
|
||||
int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
|
||||
const int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
|
||||
const int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
|
||||
|
||||
int idx = h * y + x;
|
||||
const int idx = h * y + x;
|
||||
|
||||
if (mb->get_shift() && last_frame_selected >= 0) {
|
||||
//select multiple
|
||||
|
@ -124,6 +123,9 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
for (int i = from; i <= to; i++) {
|
||||
// Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
|
||||
frames_toggled_by_mouse_hover.insert(idx);
|
||||
|
||||
if (mb->get_control()) {
|
||||
frames_selected.erase(i);
|
||||
} else {
|
||||
|
@ -131,6 +133,9 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
|
||||
frames_toggled_by_mouse_hover.insert(idx);
|
||||
|
||||
if (frames_selected.has(idx)) {
|
||||
frames_selected.erase(idx);
|
||||
} else {
|
||||
|
@ -141,6 +146,39 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
|
|||
last_frame_selected = idx;
|
||||
split_sheet_preview->update();
|
||||
}
|
||||
|
||||
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
|
||||
frames_toggled_by_mouse_hover.clear();
|
||||
}
|
||||
|
||||
const Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
|
||||
// Select by holding down the mouse button on frames.
|
||||
const Size2i size = split_sheet_preview->get_size();
|
||||
const int h = split_sheet_h->get_value();
|
||||
const int v = split_sheet_v->get_value();
|
||||
|
||||
const int x = CLAMP(int(mm->get_position().x) * h / size.width, 0, h - 1);
|
||||
const int y = CLAMP(int(mm->get_position().y) * v / size.height, 0, v - 1);
|
||||
|
||||
const int idx = h * y + x;
|
||||
|
||||
if (!frames_toggled_by_mouse_hover.has(idx)) {
|
||||
// Only allow toggling each tile once per mouse hold.
|
||||
// Otherwise, the selection would constantly "flicker" in and out when moving the mouse cursor.
|
||||
// The mouse button must be released before it can be toggled again.
|
||||
frames_toggled_by_mouse_hover.insert(idx);
|
||||
|
||||
if (frames_selected.has(idx)) {
|
||||
frames_selected.erase(idx);
|
||||
} else {
|
||||
frames_selected.insert(idx);
|
||||
}
|
||||
|
||||
last_frame_selected = idx;
|
||||
split_sheet_preview->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {
|
||||
|
|
|
@ -86,6 +86,7 @@ class SpriteFramesEditor : public HSplitContainer {
|
|||
ToolButton *split_sheet_zoom_in;
|
||||
EditorFileDialog *file_split_sheet;
|
||||
Set<int> frames_selected;
|
||||
Set<int> frames_toggled_by_mouse_hover;
|
||||
int last_frame_selected;
|
||||
|
||||
float scale_ratio;
|
||||
|
|
|
@ -1848,9 +1848,6 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) {
|
|||
case KEY_ENTER: {
|
||||
_open_selected_projects_ask();
|
||||
} break;
|
||||
case KEY_DELETE: {
|
||||
_erase_project();
|
||||
} break;
|
||||
case KEY_HOME: {
|
||||
if (_project_list->get_project_count() > 0) {
|
||||
_project_list->select_project(0);
|
||||
|
@ -2491,12 +2488,14 @@ ProjectManager::ProjectManager() {
|
|||
|
||||
Button *open = memnew(Button);
|
||||
open->set_text(TTR("Edit"));
|
||||
open->set_shortcut(ED_SHORTCUT("project_manager/edit_project", TTR("Edit Project"), KEY_MASK_CMD | KEY_E));
|
||||
tree_vb->add_child(open);
|
||||
open->connect("pressed", this, "_open_selected_projects_ask");
|
||||
open_btn = open;
|
||||
|
||||
Button *run = memnew(Button);
|
||||
run->set_text(TTR("Run"));
|
||||
run->set_shortcut(ED_SHORTCUT("project_manager/run_project", TTR("Run Project"), KEY_MASK_CMD | KEY_R));
|
||||
tree_vb->add_child(run);
|
||||
run->connect("pressed", this, "_run_project");
|
||||
run_btn = run;
|
||||
|
@ -2505,6 +2504,7 @@ ProjectManager::ProjectManager() {
|
|||
|
||||
Button *scan = memnew(Button);
|
||||
scan->set_text(TTR("Scan"));
|
||||
scan->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KEY_MASK_CMD | KEY_S));
|
||||
tree_vb->add_child(scan);
|
||||
scan->connect("pressed", this, "_scan_projects");
|
||||
|
||||
|
@ -2520,22 +2520,26 @@ ProjectManager::ProjectManager() {
|
|||
|
||||
Button *create = memnew(Button);
|
||||
create->set_text(TTR("New Project"));
|
||||
create->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KEY_MASK_CMD | KEY_N));
|
||||
tree_vb->add_child(create);
|
||||
create->connect("pressed", this, "_new_project");
|
||||
|
||||
Button *import = memnew(Button);
|
||||
import->set_text(TTR("Import"));
|
||||
import->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KEY_MASK_CMD | KEY_I));
|
||||
tree_vb->add_child(import);
|
||||
import->connect("pressed", this, "_import_project");
|
||||
|
||||
Button *rename = memnew(Button);
|
||||
rename->set_text(TTR("Rename"));
|
||||
rename->set_shortcut(ED_SHORTCUT("project_manager/rename_project", TTR("Rename Project"), KEY_F2));
|
||||
tree_vb->add_child(rename);
|
||||
rename->connect("pressed", this, "_rename_project");
|
||||
rename_btn = rename;
|
||||
|
||||
Button *erase = memnew(Button);
|
||||
erase->set_text(TTR("Remove"));
|
||||
erase->set_shortcut(ED_SHORTCUT("project_manager/remove_project", TTR("Remove Project"), KEY_DELETE));
|
||||
tree_vb->add_child(erase);
|
||||
erase->connect("pressed", this, "_erase_project");
|
||||
erase_btn = erase;
|
||||
|
|
|
@ -635,8 +635,6 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
|
|||
r_normal = -p_camera->project_ray_normal(p_point);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (collision_segments.size()) {
|
||||
|
@ -687,8 +685,6 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
|
|||
r_normal = -p_camera->project_ray_normal(p_point);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (collision_mesh.is_valid()) {
|
||||
|
|
|
@ -187,7 +187,9 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
|
|||
E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
|
||||
for (int i = 0; i < script_symbols.size(); ++i) {
|
||||
if (query.is_subsequence_ofi(script_symbols[i].name)) {
|
||||
arr.push_back(script_symbols[i].to_json());
|
||||
lsp::DocumentedSymbolInformation symbol = script_symbols[i];
|
||||
symbol.location.uri = get_file_uri(symbol.location.uri);
|
||||
arr.push_back(symbol.to_json());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,10 @@ static void _compress_pvrtc4(Image *p_img) {
|
|||
if (!img->is_size_po2() || img->get_width() != img->get_height()) {
|
||||
make_mipmaps = img->has_mipmaps();
|
||||
img->resize_to_po2(true);
|
||||
// Resizing can fail for some formats
|
||||
if (!img->is_size_po2() || img->get_width() != img->get_height()) {
|
||||
ERR_FAIL_MSG("Failed to resize the image for compression.");
|
||||
}
|
||||
}
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
if (!img->has_mipmaps() && make_mipmaps) {
|
||||
|
|
|
@ -466,10 +466,11 @@ void VisualScriptPropertySelector::_item_selected() {
|
|||
|
||||
at_class = ClassDB::get_parent_class_nocheck(at_class);
|
||||
}
|
||||
Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(class_type);
|
||||
Vector<String> functions = name.rsplit("/", false);
|
||||
at_class = functions.size() > 3 ? functions[functions.size() - 2] : class_type;
|
||||
Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(at_class);
|
||||
if (T) {
|
||||
for (int i = 0; i < T->get().methods.size(); i++) {
|
||||
Vector<String> functions = name.rsplit("/", false, 1);
|
||||
if (T->get().methods[i].name == functions[functions.size() - 1]) {
|
||||
text = T->get().methods[i].description;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,9 @@
|
|||
<member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain">
|
||||
When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake.
|
||||
</member>
|
||||
<member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0">
|
||||
The time in seconds before a pending client (i.e. a client that has not yet finished the HTTP handshake) is considered stale and forcefully disconnected.
|
||||
</member>
|
||||
<member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key">
|
||||
When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol).
|
||||
</member>
|
||||
|
|
|
@ -43,9 +43,9 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
|
|||
_is_multiplayer = gd_mp_api;
|
||||
|
||||
String host = p_url;
|
||||
String path = "/";
|
||||
String scheme = "";
|
||||
int port = 80;
|
||||
String path;
|
||||
String scheme;
|
||||
int port = 0;
|
||||
Error err = p_url.parse_url(scheme, host, port, path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
|
||||
|
||||
|
@ -56,6 +56,9 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
|
|||
if (port == 0) {
|
||||
port = ssl ? 443 : 80;
|
||||
}
|
||||
if (path.empty()) {
|
||||
path = "/";
|
||||
}
|
||||
return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,10 @@ void WebSocketServer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ca_chain", "get_ca_chain");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout);
|
||||
ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));
|
||||
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close")));
|
||||
ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
|
||||
|
@ -108,6 +112,15 @@ void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) {
|
|||
ca_chain = p_ca_chain;
|
||||
}
|
||||
|
||||
float WebSocketServer::get_handshake_timeout() const {
|
||||
return handshake_timeout / 1000.0;
|
||||
}
|
||||
|
||||
void WebSocketServer::set_handshake_timeout(float p_timeout) {
|
||||
ERR_FAIL_COND(p_timeout <= 0.0);
|
||||
handshake_timeout = p_timeout * 1000;
|
||||
}
|
||||
|
||||
NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
|
||||
if (is_listening()) {
|
||||
return CONNECTION_CONNECTED;
|
||||
|
|
|
@ -48,6 +48,7 @@ protected:
|
|||
Ref<CryptoKey> private_key;
|
||||
Ref<X509Certificate> ssl_cert;
|
||||
Ref<X509Certificate> ca_chain;
|
||||
uint32_t handshake_timeout = 3000;
|
||||
|
||||
public:
|
||||
virtual void poll() = 0;
|
||||
|
@ -80,6 +81,9 @@ public:
|
|||
Ref<X509Certificate> get_ca_chain() const;
|
||||
void set_ca_chain(Ref<X509Certificate> p_ca_chain);
|
||||
|
||||
float get_handshake_timeout() const;
|
||||
void set_handshake_timeout(float p_timeout);
|
||||
|
||||
virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
|
||||
|
||||
WebSocketServer();
|
||||
|
|
|
@ -158,6 +158,7 @@ bool WSLClient::_verify_headers(String &r_protocol) {
|
|||
|
||||
Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
|
||||
ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(p_path.empty(), ERR_INVALID_PARAMETER);
|
||||
|
||||
_peer = Ref<WSLPeer>(memnew(WSLPeer));
|
||||
IP_Address addr;
|
||||
|
|
|
@ -104,8 +104,8 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {
|
|||
return true;
|
||||
}
|
||||
|
||||
Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) {
|
||||
if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) {
|
||||
Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout) {
|
||||
if (OS::get_singleton()->get_ticks_msec() - time > p_timeout) {
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
if (use_ssl) {
|
||||
|
@ -197,7 +197,7 @@ void WSLServer::poll() {
|
|||
List<Ref<PendingPeer>> remove_peers;
|
||||
for (List<Ref<PendingPeer>>::Element *E = _pending.front(); E; E = E->next()) {
|
||||
Ref<PendingPeer> ppeer = E->get();
|
||||
Error err = ppeer->do_handshake(_protocols);
|
||||
Error err = ppeer->do_handshake(_protocols, handshake_timeout);
|
||||
if (err == ERR_BUSY) {
|
||||
continue;
|
||||
} else if (err != OK) {
|
||||
|
|
|
@ -40,8 +40,6 @@
|
|||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
|
||||
#define WSL_SERVER_TIMEOUT 1000
|
||||
|
||||
class WSLServer : public WebSocketServer {
|
||||
GDCIIMPL(WSLServer, WebSocketServer);
|
||||
|
||||
|
@ -66,7 +64,7 @@ private:
|
|||
|
||||
PendingPeer();
|
||||
|
||||
Error do_handshake(const Vector<String> p_protocols);
|
||||
Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout);
|
||||
};
|
||||
|
||||
int _in_buf_size;
|
||||
|
|
|
@ -1910,7 +1910,7 @@ public:
|
|||
err = OS::get_singleton()->execute(adb, args, true, nullptr, &output, &rv, true);
|
||||
print_verbose(output);
|
||||
if (err || rv != 0) {
|
||||
EditorNode::add_io_error("Could not install to device.");
|
||||
EditorNode::add_io_error("Could not install to device: " + output);
|
||||
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ buildscript {
|
|||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
//CHUNK_BUILDSCRIPT_REPOSITORIES_BEGIN
|
||||
//CHUNK_BUILDSCRIPT_REPOSITORIES_END
|
||||
}
|
||||
|
@ -25,9 +25,8 @@ apply plugin: 'com.android.application'
|
|||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
//CHUNK_ALLPROJECTS_REPOSITORIES_BEGIN
|
||||
//CHUNK_ALLPROJECTS_REPOSITORIES_END
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
ext.versions = [
|
||||
androidGradlePlugin: '4.0.1',
|
||||
androidGradlePlugin: '4.2.1',
|
||||
compileSdk : 29,
|
||||
minSdk : 18,
|
||||
targetSdk : 29,
|
||||
buildTools : '30.0.3',
|
||||
supportCoreUtils : '1.0.0',
|
||||
kotlinVersion : '1.4.10',
|
||||
kotlinVersion : '1.5.10',
|
||||
v4Support : '1.0.0',
|
||||
javaVersion : 1.8,
|
||||
ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.
|
||||
|
|
|
@ -5,7 +5,7 @@ buildscript {
|
|||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath libraries.androidGradlePlugin
|
||||
|
@ -16,7 +16,6 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Mon Sep 02 02:44:30 PDT 2019
|
||||
#Wed Jun 23 23:42:22 PDT 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -136,8 +136,11 @@ public:
|
|||
// Wrong protocol
|
||||
ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
|
||||
|
||||
const String req_file = req[1].get_file();
|
||||
const String req_ext = req[1].get_extension();
|
||||
const int query_index = req[1].find_char('?');
|
||||
const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index);
|
||||
|
||||
const String req_file = path.get_file();
|
||||
const String req_ext = path.get_extension();
|
||||
const String cache_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("web");
|
||||
const String filepath = cache_path.plus_file(req_file);
|
||||
|
||||
|
@ -446,6 +449,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
|
|||
}
|
||||
config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy");
|
||||
config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard");
|
||||
config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start");
|
||||
config["gdnativeLibs"] = libs;
|
||||
config["executable"] = p_name;
|
||||
config["args"] = args;
|
||||
|
@ -648,6 +652,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
|
|||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));
|
||||
|
|
|
@ -90,6 +90,14 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
|
|||
* @default
|
||||
*/
|
||||
args: [],
|
||||
/**
|
||||
* When enabled, the game canvas will automatically grab the focus when the engine starts.
|
||||
*
|
||||
* @memberof EngineConfig
|
||||
* @type {boolean}
|
||||
* @default
|
||||
*/
|
||||
focusCanvas: true,
|
||||
/**
|
||||
* When enabled, this will turn on experimental virtual keyboard support on mobile.
|
||||
*
|
||||
|
@ -238,6 +246,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
|
|||
this.persistentPaths = parse('persistentPaths', this.persistentPaths);
|
||||
this.persistentDrops = parse('persistentDrops', this.persistentDrops);
|
||||
this.experimentalVK = parse('experimentalVK', this.experimentalVK);
|
||||
this.focusCanvas = parse('focusCanvas', this.focusCanvas);
|
||||
this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
|
||||
this.fileSizes = parse('fileSizes', this.fileSizes);
|
||||
this.args = parse('args', this.args);
|
||||
|
@ -324,6 +333,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
|
|||
'locale': locale,
|
||||
'persistentDrops': this.persistentDrops,
|
||||
'virtualKeyboard': this.experimentalVK,
|
||||
'focusCanvas': this.focusCanvas,
|
||||
'onExecute': this.onExecute,
|
||||
'onExit': function (p_code) {
|
||||
cleanup(); // We always need to call the cleanup callback to free memory.
|
||||
|
|
|
@ -72,6 +72,9 @@ const GodotConfig = {
|
|||
GodotConfig.persistent_drops = !!p_opts['persistentDrops'];
|
||||
GodotConfig.on_execute = p_opts['onExecute'];
|
||||
GodotConfig.on_exit = p_opts['onExit'];
|
||||
if (p_opts['focusCanvas']) {
|
||||
GodotConfig.canvas.focus();
|
||||
}
|
||||
},
|
||||
|
||||
locate_file: function (file) {
|
||||
|
|
|
@ -3295,6 +3295,12 @@ void OS_OSX::set_mouse_mode(MouseMode p_mode) {
|
|||
ignore_warp = true;
|
||||
warp_events.clear();
|
||||
mouse_mode = p_mode;
|
||||
|
||||
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
|
||||
CursorShape p_shape = cursor_shape;
|
||||
cursor_shape = OS::CURSOR_MAX;
|
||||
set_cursor_shape(p_shape);
|
||||
}
|
||||
}
|
||||
|
||||
OS::MouseMode OS_OSX::get_mouse_mode() const {
|
||||
|
|
|
@ -1056,19 +1056,19 @@ public:
|
|||
// Capabilities
|
||||
const char **basic = uwp_capabilities;
|
||||
while (*basic) {
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic).camelcase_to_underscore(false)), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false));
|
||||
basic++;
|
||||
}
|
||||
|
||||
const char **uap = uwp_uap_capabilities;
|
||||
while (*uap) {
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap).camelcase_to_underscore(false)), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false));
|
||||
uap++;
|
||||
}
|
||||
|
||||
const char **device = uwp_device_capabilities;
|
||||
while (*device) {
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device).camelcase_to_underscore(false)), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false));
|
||||
device++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,7 +310,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
|
|||
args.push_back(p_path);
|
||||
#ifndef WINDOWS_ENABLED
|
||||
args.push_back("-out");
|
||||
args.push_back(p_path);
|
||||
args.push_back(p_path + "_signed");
|
||||
#endif
|
||||
|
||||
String str;
|
||||
|
@ -326,6 +326,16 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
|
|||
return FAILED;
|
||||
}
|
||||
|
||||
#ifndef WINDOWS_ENABLED
|
||||
DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
|
||||
|
||||
err = tmp_dir->remove(p_path);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
|
||||
err = tmp_dir->rename(p_path + "_signed", p_path);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -575,7 +575,7 @@ void GraphNode::_connpos_update() {
|
|||
continue;
|
||||
}
|
||||
|
||||
Size2i size = c->get_combined_minimum_size();
|
||||
Size2i size = c->get_rect().size;
|
||||
|
||||
int y = sb->get_margin(MARGIN_TOP) + vofs;
|
||||
int h = size.y;
|
||||
|
|
|
@ -221,43 +221,87 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
|
|||
double width_texture = 0.0;
|
||||
double first_section_size = 0.0;
|
||||
double last_section_size = 0.0;
|
||||
switch (mode) {
|
||||
case FILL_LEFT_TO_RIGHT:
|
||||
case FILL_RIGHT_TO_LEFT: {
|
||||
switch (p_mode) {
|
||||
case FILL_LEFT_TO_RIGHT: {
|
||||
width_total = dst_rect.size.x;
|
||||
width_texture = texture_size.x;
|
||||
first_section_size = topleft.x;
|
||||
last_section_size = bottomright.x;
|
||||
} break;
|
||||
case FILL_TOP_TO_BOTTOM:
|
||||
case FILL_BOTTOM_TO_TOP: {
|
||||
case FILL_RIGHT_TO_LEFT: {
|
||||
width_total = dst_rect.size.x;
|
||||
width_texture = texture_size.x;
|
||||
// In contrast to `FILL_LEFT_TO_RIGHT`, `first_section_size` and `last_section_size` should switch value.
|
||||
first_section_size = bottomright.x;
|
||||
last_section_size = topleft.x;
|
||||
} break;
|
||||
case FILL_TOP_TO_BOTTOM: {
|
||||
width_total = dst_rect.size.y;
|
||||
width_texture = texture_size.y;
|
||||
first_section_size = topleft.y;
|
||||
last_section_size = bottomright.y;
|
||||
} break;
|
||||
case FILL_BOTTOM_TO_TOP: {
|
||||
width_total = dst_rect.size.y;
|
||||
width_texture = texture_size.y;
|
||||
// Similar to `FILL_RIGHT_TO_LEFT`.
|
||||
first_section_size = bottomright.y;
|
||||
last_section_size = topleft.y;
|
||||
} break;
|
||||
case FILL_BILINEAR_LEFT_AND_RIGHT: {
|
||||
// TODO: Implement
|
||||
width_total = dst_rect.size.x;
|
||||
width_texture = texture_size.x;
|
||||
first_section_size = topleft.x;
|
||||
last_section_size = bottomright.x;
|
||||
} break;
|
||||
case FILL_BILINEAR_TOP_AND_BOTTOM: {
|
||||
// TODO: Implement
|
||||
width_total = dst_rect.size.y;
|
||||
width_texture = texture_size.y;
|
||||
first_section_size = topleft.y;
|
||||
last_section_size = bottomright.y;
|
||||
} break;
|
||||
case FILL_CLOCKWISE:
|
||||
case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
|
||||
case FILL_COUNTER_CLOCKWISE: {
|
||||
// Those modes are circular, not relevant for nine patch
|
||||
// Those modes are circular, not relevant for nine patch.
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
}
|
||||
|
||||
double width_filled = width_total * p_ratio;
|
||||
double middle_section_size = MAX(0.0, width_texture - first_section_size - last_section_size);
|
||||
|
||||
// Maximum middle texture size.
|
||||
double max_middle_texture_size = middle_section_size;
|
||||
|
||||
// Maximum real middle texture size.
|
||||
double max_middle_real_size = MAX(0.0, width_total - (first_section_size + last_section_size));
|
||||
|
||||
switch (p_mode) {
|
||||
case FILL_BILINEAR_LEFT_AND_RIGHT:
|
||||
case FILL_BILINEAR_TOP_AND_BOTTOM: {
|
||||
last_section_size = MAX(0.0, last_section_size - (width_total - width_filled) * 0.5);
|
||||
first_section_size = MAX(0.0, first_section_size - (width_total - width_filled) * 0.5);
|
||||
|
||||
// When `width_filled` increases, `middle_section_size` only increases when either of `first_section_size` and `last_section_size` is zero.
|
||||
// Also, it should always be smaller than or equal to `(width_total - (first_section_size + last_section_size))`.
|
||||
double real_middle_size = width_filled - first_section_size - last_section_size;
|
||||
middle_section_size *= MIN(max_middle_real_size, real_middle_size) / max_middle_real_size;
|
||||
|
||||
width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default: {
|
||||
middle_section_size *= MIN(1.0, (MAX(0.0, width_filled - first_section_size) / MAX(1.0, width_total - first_section_size - last_section_size)));
|
||||
last_section_size = MAX(0.0, last_section_size - (width_total - width_filled));
|
||||
first_section_size = MIN(first_section_size, width_filled);
|
||||
width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
switch (p_mode) {
|
||||
case FILL_LEFT_TO_RIGHT: {
|
||||
src_rect.size.x = width_texture;
|
||||
dst_rect.size.x = width_filled;
|
||||
|
@ -287,16 +331,32 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
|
|||
bottomright.y = first_section_size;
|
||||
} break;
|
||||
case FILL_BILINEAR_LEFT_AND_RIGHT: {
|
||||
// TODO: Implement
|
||||
double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x;
|
||||
double drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x);
|
||||
src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
|
||||
src_rect.size.x = width_texture;
|
||||
dst_rect.position.x += (width_total - width_filled) * 0.5;
|
||||
dst_rect.size.x = width_filled;
|
||||
topleft.x = first_section_size;
|
||||
bottomright.x = last_section_size;
|
||||
} break;
|
||||
case FILL_BILINEAR_TOP_AND_BOTTOM: {
|
||||
// TODO: Implement
|
||||
double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y;
|
||||
double drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y);
|
||||
src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
|
||||
src_rect.size.y = width_texture;
|
||||
dst_rect.position.y += (width_total - width_filled) * 0.5;
|
||||
dst_rect.size.y = width_filled;
|
||||
topleft.y = first_section_size;
|
||||
bottomright.y = last_section_size;
|
||||
} break;
|
||||
case FILL_CLOCKWISE:
|
||||
case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
|
||||
case FILL_COUNTER_CLOCKWISE: {
|
||||
// Those modes are circular, not relevant for nine patch
|
||||
// Those modes are circular, not relevant for nine patch.
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,20 +370,35 @@ void TextureProgress::_notification(int p_what) {
|
|||
const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 };
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) {
|
||||
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) {
|
||||
if (under.is_valid()) {
|
||||
draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under);
|
||||
draw_nine_patch_stretched(under, mode, 1.0, tint_under);
|
||||
}
|
||||
if (progress.is_valid()) {
|
||||
draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
|
||||
}
|
||||
if (over.is_valid()) {
|
||||
draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over);
|
||||
draw_nine_patch_stretched(over, mode, 1.0, tint_over);
|
||||
}
|
||||
} else {
|
||||
if (under.is_valid()) {
|
||||
switch (mode) {
|
||||
case FILL_CLOCKWISE:
|
||||
case FILL_COUNTER_CLOCKWISE:
|
||||
case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
|
||||
if (nine_patch_stretch) {
|
||||
Rect2 region = Rect2(Point2(), get_size());
|
||||
draw_texture_rect(under, region, false, tint_under);
|
||||
} else {
|
||||
draw_texture(under, Point2(), tint_under);
|
||||
}
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default:
|
||||
draw_texture(under, Point2(), tint_under);
|
||||
}
|
||||
}
|
||||
if (progress.is_valid()) {
|
||||
Size2 s = progress->get_size();
|
||||
switch (mode) {
|
||||
|
@ -353,7 +428,7 @@ void TextureProgress::_notification(int p_what) {
|
|||
float val = get_as_ratio() * rad_max_degrees / 360;
|
||||
if (val == 1) {
|
||||
Rect2 region = Rect2(Point2(), s);
|
||||
draw_texture_rect_region(progress, region, region, tint_progress);
|
||||
draw_texture_rect(progress, region, false, tint_progress);
|
||||
} else if (val != 0) {
|
||||
Array pts;
|
||||
float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1;
|
||||
|
@ -416,13 +491,30 @@ void TextureProgress::_notification(int p_what) {
|
|||
Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio()));
|
||||
draw_texture_rect_region(progress, region, region, tint_progress);
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default:
|
||||
draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
|
||||
}
|
||||
}
|
||||
if (over.is_valid()) {
|
||||
switch (mode) {
|
||||
case FILL_CLOCKWISE:
|
||||
case FILL_COUNTER_CLOCKWISE:
|
||||
case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
|
||||
if (nine_patch_stretch) {
|
||||
Rect2 region = Rect2(Point2(), get_size());
|
||||
draw_texture_rect(over, region, false, tint_over);
|
||||
} else {
|
||||
draw_texture(over, Point2(), tint_over);
|
||||
}
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default:
|
||||
draw_texture(over, Point2(), tint_over);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
@ -430,7 +522,7 @@ void TextureProgress::_notification(int p_what) {
|
|||
}
|
||||
|
||||
void TextureProgress::set_fill_mode(int p_fill) {
|
||||
ERR_FAIL_INDEX(p_fill, 9);
|
||||
ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX);
|
||||
mode = (FillMode)p_fill;
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ public:
|
|||
FILL_COUNTER_CLOCKWISE,
|
||||
FILL_BILINEAR_LEFT_AND_RIGHT,
|
||||
FILL_BILINEAR_TOP_AND_BOTTOM,
|
||||
FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE
|
||||
FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE,
|
||||
FILL_MODE_MAX,
|
||||
};
|
||||
|
||||
void set_fill_mode(int p_fill);
|
||||
|
|
|
@ -471,7 +471,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
|
|||
StringName type = p_node->get_class();
|
||||
|
||||
Ref<Script> script = p_node->get_script();
|
||||
if (script.is_valid()) {
|
||||
if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
|
||||
// Should be called in the editor only and not at runtime,
|
||||
// otherwise it can cause problems because of missing instance state support.
|
||||
script->update_exports();
|
||||
}
|
||||
|
||||
|
@ -893,6 +895,13 @@ Error SceneState::pack(Node *p_scene) {
|
|||
node_paths.write[E->get()] = scene->get_path_to(E->key());
|
||||
}
|
||||
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
// Build node path cache
|
||||
for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) {
|
||||
node_path_cache[scene->get_path_to(E->key())] = E->get();
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -927,10 +936,12 @@ Ref<SceneState> SceneState::_get_base_scene_state() const {
|
|||
}
|
||||
|
||||
int SceneState::find_node_by_path(const NodePath &p_node) const {
|
||||
ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");
|
||||
|
||||
if (!node_path_cache.has(p_node)) {
|
||||
if (_get_base_scene_state().is_valid()) {
|
||||
int idx = _get_base_scene_state()->find_node_by_path(p_node);
|
||||
if (idx >= 0) {
|
||||
if (idx != -1) {
|
||||
int rkey = _find_base_scene_node_remap_key(idx);
|
||||
if (rkey == -1) {
|
||||
rkey = nodes.size() + base_scene_node_remap.size();
|
||||
|
|
|
@ -886,9 +886,9 @@ void CylinderMesh::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);
|
||||
ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_top_radius", "get_top_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_top_radius", "get_top_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_height", "get_height");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue