From 7a0c210f9b30c94dfe6509768442414bce7d781c Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Thu, 29 Jul 2021 15:26:07 -0700 Subject: [PATCH] Refactor layer property editor grid - Now able to display up to 32 layers in physics (still 20 for render) - Adjustable grid size to fit available space in dock - Expansion icon to display more layers vertically - Layer numbers in cells to help with selection --- doc/classes/ProjectSettings.xml | 144 ++++++++++++++++ editor/editor_properties.cpp | 292 ++++++++++++++++++++++++-------- scene/register_scene_types.cpp | 5 +- 3 files changed, 373 insertions(+), 68 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index c3ef9f242508..f500aa1f7f86 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -820,9 +820,45 @@ Optional name for the 2D navigation layer 2. If left empty, the layer will display as "Layer 2". + + Optional name for the 2D navigation layer 20. If left empty, the layer will display as "Layer 20". + + + Optional name for the 2D navigation layer 21. If left empty, the layer will display as "Layer 21". + + + Optional name for the 2D navigation layer 22. If left empty, the layer will display as "Layer 22". + + + Optional name for the 2D navigation layer 23. If left empty, the layer will display as "Layer 23". + + + Optional name for the 2D navigation layer 24. If left empty, the layer will display as "Layer 24". + + + Optional name for the 2D navigation layer 25. If left empty, the layer will display as "Layer 25". + + + Optional name for the 2D navigation layer 26. If left empty, the layer will display as "Layer 26". + + + Optional name for the 2D navigation layer 27. If left empty, the layer will display as "Layer 27". + + + Optional name for the 2D navigation layer 28. If left empty, the layer will display as "Layer 28". + + + Optional name for the 2D navigation layer 29. If left empty, the layer will display as "Layer 29". + Optional name for the 2D navigation layer 3. If left empty, the layer will display as "Layer 3". + + Optional name for the 2D navigation layer 30. If left empty, the layer will display as "Layer 30". + + + Optional name for the 2D navigation layer 31. If left empty, the layer will display as "Layer 31". + Optional name for the 2D navigation layer 4. If left empty, the layer will display as "Layer 4". @@ -880,9 +916,45 @@ Optional name for the 2D physics layer 2. If left empty, the layer will display as "Layer 2". + + Optional name for the 2D physics layer 20. If left empty, the layer will display as "Layer 20". + + + Optional name for the 2D physics layer 21. If left empty, the layer will display as "Layer 21". + + + Optional name for the 2D physics layer 22. If left empty, the layer will display as "Layer 22". + + + Optional name for the 2D physics layer 23. If left empty, the layer will display as "Layer 23". + + + Optional name for the 2D physics layer 24. If left empty, the layer will display as "Layer 24". + + + Optional name for the 2D physics layer 25. If left empty, the layer will display as "Layer 25". + + + Optional name for the 2D physics layer 26. If left empty, the layer will display as "Layer 26". + + + Optional name for the 2D physics layer 27. If left empty, the layer will display as "Layer 27". + + + Optional name for the 2D physics layer 28. If left empty, the layer will display as "Layer 28". + + + Optional name for the 2D physics layer 29. If left empty, the layer will display as "Layer 29". + Optional name for the 2D physics layer 3. If left empty, the layer will display as "Layer 3". + + Optional name for the 2D physics layer 30. If left empty, the layer will display as "Layer 30". + + + Optional name for the 2D physics layer 31. If left empty, the layer will display as "Layer 31". + Optional name for the 2D physics layer 4. If left empty, the layer will display as "Layer 4". @@ -1000,9 +1072,45 @@ Optional name for the 3D navigation layer 2. If left empty, the layer will display as "Layer 2". + + Optional name for the 3D navigation layer 20. If left empty, the layer will display as "Layer 20". + + + Optional name for the 3D navigation layer 21. If left empty, the layer will display as "Layer 21". + + + Optional name for the 3D navigation layer 22. If left empty, the layer will display as "Layer 22". + + + Optional name for the 3D navigation layer 23. If left empty, the layer will display as "Layer 23". + + + Optional name for the 3D navigation layer 24. If left empty, the layer will display as "Layer 24". + + + Optional name for the 3D navigation layer 25. If left empty, the layer will display as "Layer 25". + + + Optional name for the 3D navigation layer 26. If left empty, the layer will display as "Layer 26". + + + Optional name for the 3D navigation layer 27. If left empty, the layer will display as "Layer 27". + + + Optional name for the 3D navigation layer 28. If left empty, the layer will display as "Layer 28". + + + Optional name for the 3D navigation layer 29. If left empty, the layer will display as "Layer 29". + Optional name for the 3D navigation layer 3. If left empty, the layer will display as "Layer 3". + + Optional name for the 3D navigation layer 30. If left empty, the layer will display as "Layer 30". + + + Optional name for the 3D navigation layer 31. If left empty, the layer will display as "Layer 31". + Optional name for the 3D navigation layer 4. If left empty, the layer will display as "Layer 4". @@ -1060,9 +1168,45 @@ Optional name for the 3D physics layer 2. If left empty, the layer will display as "Layer 2". + + Optional name for the 3D physics layer 20. If left empty, the layer will display as "Layer 20". + + + Optional name for the 3D physics layer 21. If left empty, the layer will display as "Layer 21". + + + Optional name for the 3D physics layer 22. If left empty, the layer will display as "Layer 22". + + + Optional name for the 3D physics layer 23. If left empty, the layer will display as "Layer 23". + + + Optional name for the 3D physics layer 24. If left empty, the layer will display as "Layer 24". + + + Optional name for the 3D physics layer 25. If left empty, the layer will display as "Layer 25". + + + Optional name for the 3D physics layer 26. If left empty, the layer will display as "Layer 26". + + + Optional name for the 3D physics layer 27. If left empty, the layer will display as "Layer 27". + + + Optional name for the 3D physics layer 28. If left empty, the layer will display as "Layer 28". + + + Optional name for the 3D physics layer 29. If left empty, the layer will display as "Layer 29". + Optional name for the 3D physics layer 3. If left empty, the layer will display as "Layer 3". + + Optional name for the 3D physics layer 30. If left empty, the layer will display as "Layer 30". + + + Optional name for the 3D physics layer 31. If left empty, the layer will display as "Layer 31". + Optional name for the 3D physics layer 4. If left empty, the layer will display as "Layer 4". diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index df763d5bf0f4..fa03d281f268 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -691,17 +691,39 @@ EditorPropertyFlags::EditorPropertyFlags() { class EditorPropertyLayersGrid : public Control { GDCLASS(EditorPropertyLayersGrid, Control); -public: - uint32_t value; +private: Vector flag_rects; + Rect2 expand_rect; + bool expand_hovered = false; + bool expanded = false; + int expansion_rows = 0; + int hovered_index = -1; + + Size2 get_grid_size() const { + Ref font = get_theme_font(SNAME("font"), SNAME("Label")); + int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); + return Vector2(0, font->get_height(font_size) * 3); + } + +public: + uint32_t value = 0; + int layer_group_size = 0; + int layer_count = 0; Vector names; Vector tooltips; - int hovered_index; virtual Size2 get_minimum_size() const override { - Ref font = get_theme_font(SNAME("font"), SNAME("Label")); - int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); - return Vector2(0, font->get_height(font_size) * 2); + Size2 min_size = get_grid_size(); + + // Add extra rows when expanded. + if (expanded) { + const int bsize = (min_size.height * 80 / 100) / 2; + for (int i = 0; i < expansion_rows; ++i) { + min_size.y += 2 * (bsize + 1) + 3; + } + } + + return min_size; } virtual String get_tooltip(const Point2 &p_pos) const override { @@ -712,80 +734,192 @@ class EditorPropertyLayersGrid : public Control { } return String(); } + void _gui_input(const Ref &p_ev) { const Ref mm = p_ev; - if (mm.is_valid()) { - for (int i = 0; i < flag_rects.size(); i++) { - if (flag_rects[i].has_point(mm->get_position())) { - // Used to highlight the hovered flag in the layers grid. - hovered_index = i; - update(); - break; + bool expand_was_hovered = expand_hovered; + expand_hovered = expand_rect.has_point(mm->get_position()); + if (expand_hovered != expand_was_hovered) { + update(); + } + + if (!expand_hovered) { + for (int i = 0; i < flag_rects.size(); i++) { + if (flag_rects[i].has_point(mm->get_position())) { + // Used to highlight the hovered flag in the layers grid. + hovered_index = i; + update(); + return; + } } } + + // Remove highlight when no square is hovered. + if (hovered_index != -1) { + hovered_index = -1; + update(); + } + + return; } const Ref mb = p_ev; + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) { + if (hovered_index >= 0) { + // Toggle the flag. + // We base our choice on the hovered flag, so that it always matches the hovered flag. + if (value & (1 << hovered_index)) { + value &= ~(1 << hovered_index); + } else { + value |= (1 << hovered_index); + } - if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed() && hovered_index >= 0) { - // Toggle the flag. - // We base our choice on the hovered flag, so that it always matches the hovered flag. - if (value & (1 << hovered_index)) { - value &= ~(1 << hovered_index); - } else { - value |= (1 << hovered_index); + emit_signal(SNAME("flag_changed"), value); + update(); + } else if (expand_hovered) { + expanded = !expanded; + minimum_size_changed(); + update(); } - - emit_signal(SNAME("flag_changed"), value); - update(); } } void _notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Rect2 rect; - rect.size = get_size(); + Size2 grid_size = get_grid_size(); + grid_size.x = get_size().x; + flag_rects.clear(); - const int bsize = (rect.size.height * 80 / 100) / 2; + int prev_expansion_rows = expansion_rows; + expansion_rows = 0; + + const int bsize = (grid_size.height * 80 / 100) / 2; const int h = bsize * 2 + 1; - const int vofs = (rect.size.height - h) / 2; Color color = get_theme_color(SNAME("highlight_color"), SNAME("Editor")); - for (int i = 0; i < 2; i++) { - Point2 ofs(4, vofs); - if (i == 1) { + + Color text_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); + text_color.a *= 0.5; + + Color text_color_on = get_theme_color(SNAME("font_hover_color"), SNAME("Editor")); + text_color_on.a *= 0.7; + + const int vofs = (grid_size.height - h) / 2; + + int layer_index = 0; + int block_index = 0; + + Point2 arrow_pos; + + Point2 block_ofs(4, vofs); + + while (true) { + Point2 ofs = block_ofs; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < layer_group_size; j++) { + const bool on = value & (1 << layer_index); + Rect2 rect2 = Rect2(ofs, Size2(bsize, bsize)); + + color.a = on ? 0.6 : 0.2; + if (layer_index == hovered_index) { + // Add visual feedback when hovering a flag. + color.a += 0.15; + } + + draw_rect(rect2, color); + flag_rects.push_back(rect2); + + Ref font = get_theme_font(SNAME("font"), SNAME("Label")); + Vector2 offset; + offset.y = rect2.size.y * 0.75; + + draw_string(font, rect2.position + offset, itos(layer_index), HALIGN_CENTER, rect2.size.x, -1, on ? text_color_on : text_color); + + ofs.x += bsize + 1; + + ++layer_index; + } + + ofs.x = block_ofs.x; ofs.y += bsize + 1; } - ofs += rect.position; - for (int j = 0; j < 10; j++) { - Point2 o = ofs + Point2(j * (bsize + 1), 0); - if (j >= 5) { - o.x += 1; + if (layer_index >= layer_count) { + if (!flag_rects.is_empty() && (expansion_rows == 0)) { + const Rect2 &last_rect = flag_rects[flag_rects.size() - 1]; + arrow_pos = last_rect.position + last_rect.size; } + break; + } - const int idx = i * 10 + j; - const bool on = value & (1 << idx); - Rect2 rect2 = Rect2(o, Size2(bsize, bsize)); + int block_size_x = layer_group_size * (bsize + 1); + block_ofs.x += block_size_x + 3; - color.a = on ? 0.6 : 0.2; - if (idx == hovered_index) { - // Add visual feedback when hovering a flag. - color.a += 0.15; + if (block_ofs.x + block_size_x + 12 > grid_size.width) { + // Keep last valid cell position for the expansion icon. + if (!flag_rects.is_empty() && (expansion_rows == 0)) { + const Rect2 &last_rect = flag_rects[flag_rects.size() - 1]; + arrow_pos = last_rect.position + last_rect.size; + } + ++expansion_rows; + + if (expanded) { + // Expand grid to next line. + block_ofs.x = 4; + block_ofs.y += 2 * (bsize + 1) + 3; + } else { + // Skip remaining blocks. + break; } - - draw_rect(rect2, color); - flag_rects.push_back(rect2); } + + ++block_index; } + + if ((expansion_rows != prev_expansion_rows) && expanded) { + minimum_size_changed(); + } + + if ((expansion_rows == 0) && (layer_index == layer_count)) { + // Whole grid was drawn, no need for expansion icon. + break; + } + + Ref arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); + ERR_FAIL_COND(arrow.is_null()); + + Color arrow_color = get_theme_color(SNAME("highlight_color"), SNAME("Editor")); + arrow_color.a = expand_hovered ? 1.0 : 0.6; + + arrow_pos.x += 2.0; + arrow_pos.y -= arrow->get_height(); + + Rect2 arrow_draw_rect(arrow_pos, arrow->get_size()); + expand_rect = arrow_draw_rect; + if (expanded) { + arrow_draw_rect.size.y *= -1.0; // Flip arrow vertically when expanded. + } + + RID ci = get_canvas_item(); + arrow->draw_rect(ci, arrow_draw_rect, false, arrow_color); + } break; + case NOTIFICATION_MOUSE_EXIT: { - hovered_index = -1; - update(); + if (expand_hovered) { + expand_hovered = false; + update(); + } + if (hovered_index != -1) { + hovered_index = -1; + update(); + } } break; + default: break; } @@ -800,12 +934,8 @@ class EditorPropertyLayersGrid : public Control { ClassDB::bind_method(D_METHOD("_gui_input"), &EditorPropertyLayersGrid::_gui_input); ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag"))); } - - EditorPropertyLayersGrid() { - value = 0; - hovered_index = -1; // Nothing is hovered. - } }; + void EditorPropertyLayers::_grid_changed(uint32_t p_grid) { emit_changed(get_edited_property(), p_grid); } @@ -818,30 +948,49 @@ void EditorPropertyLayers::update_property() { void EditorPropertyLayers::setup(LayerType p_layer_type) { String basename; + int layer_group_size = 0; + int layer_count = 0; switch (p_layer_type) { - case LAYER_RENDER_2D: + case LAYER_RENDER_2D: { basename = "layer_names/2d_render"; - break; - case LAYER_PHYSICS_2D: + layer_group_size = 5; + layer_count = 20; + } break; + + case LAYER_PHYSICS_2D: { basename = "layer_names/2d_physics"; - break; - case LAYER_NAVIGATION_2D: + layer_group_size = 4; + layer_count = 32; + } break; + + case LAYER_NAVIGATION_2D: { basename = "layer_names/2d_navigation"; - break; - case LAYER_RENDER_3D: + layer_group_size = 4; + layer_count = 32; + } break; + + case LAYER_RENDER_3D: { basename = "layer_names/3d_render"; - break; - case LAYER_PHYSICS_3D: + layer_group_size = 5; + layer_count = 20; + } break; + + case LAYER_PHYSICS_3D: { basename = "layer_names/3d_physics"; - break; - case LAYER_NAVIGATION_3D: + layer_group_size = 4; + layer_count = 32; + } break; + + case LAYER_NAVIGATION_3D: { basename = "layer_names/3d_navigation"; - break; + layer_group_size = 4; + layer_count = 32; + } break; } Vector names; Vector tooltips; - for (int i = 0; i < 20; i++) { + for (int i = 0; i < layer_count; i++) { String name; if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i))) { @@ -858,12 +1007,17 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) { grid->names = names; grid->tooltips = tooltips; + grid->layer_group_size = layer_group_size; + grid->layer_count = layer_count; } void EditorPropertyLayers::_button_pressed() { + int layer_count = grid->layer_count; + int layer_group_size = grid->layer_group_size; + layers->clear(); - for (int i = 0; i < 20; i++) { - if (i == 5 || i == 10 || i == 15) { + for (int i = 0; i < layer_count; i++) { + if ((i != 0) && ((i % layer_group_size) == 0)) { layers->add_separator(); } layers->add_check_item(grid->names[i], i); @@ -894,17 +1048,21 @@ void EditorPropertyLayers::_bind_methods() { EditorPropertyLayers::EditorPropertyLayers() { HBoxContainer *hb = memnew(HBoxContainer); + hb->set_clip_contents(true); add_child(hb); grid = memnew(EditorPropertyLayersGrid); grid->connect("flag_changed", callable_mp(this, &EditorPropertyLayers::_grid_changed)); grid->set_h_size_flags(SIZE_EXPAND_FILL); hb->add_child(grid); + button = memnew(Button); button->set_toggle_mode(true); button->set_text("..."); button->connect("pressed", callable_mp(this, &EditorPropertyLayers::_button_pressed)); hb->add_child(button); + set_bottom_editor(hb); + layers = memnew(PopupMenu); add_child(layers); layers->set_hide_on_checkable_item_selection(false); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8ede8e9a0b66..4d3a4ea334e6 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -1000,9 +1000,12 @@ void register_scene_types() { for (int i = 0; i < 20; i++) { GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); + } + + for (int i = 0; i < 32; i++) { GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i), ""); }