diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 4199a53de652..a61fa6ede8e5 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -58,6 +58,7 @@ #include "scene/gui/separator.h" #include "scene/gui/split_container.h" #include "scene/gui/subviewport_container.h" +#include "scene/gui/texture_rect.h" #include "scene/gui/view_panner.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" @@ -606,6 +607,21 @@ Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) { return rect; } +Vector2 CanvasItemEditor::_get_screen_size() const { + if (!_is_viewport_overridden()) { + return Vector2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + } + SubViewport *svp = Object::cast_to(current_viewport); + if (svp) { + return svp->get_size(); + } + Window *w = Object::cast_to(current_viewport); + if (w) { + return w->get_size(); + } + return Vector2(); +} + void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) { return; @@ -618,7 +634,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no if (CanvasLayer *cl = Object::cast_to(p_node)) { xform = cl->get_transform(); } else if (Viewport *vp = Object::cast_to(p_node)) { - if (!vp->is_visible_subviewport()) { + if (!_is_usable_viewport(vp)) { return; } xform = vp->get_popup_base_transform(); @@ -639,6 +655,10 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no } } + if (_is_viewport_overridden() && p_node->get_viewport() != current_viewport) { + return; + } + if (ci && ci->is_visible_in_tree()) { if (!ci->is_set_as_top_level()) { xform *= p_parent_xform; @@ -721,7 +741,7 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n if (CanvasLayer *cl = Object::cast_to(p_node)) { xform = cl->get_transform(); } else if (Viewport *vp = Object::cast_to(p_node)) { - if (!vp->is_visible_subviewport()) { + if (!_is_usable_viewport(vp)) { return; } xform = vp->get_popup_base_transform(); @@ -804,7 +824,7 @@ List CanvasItemEditor::_get_edited_canvas_items(bool p_retrieve_lo if (ci) { if (ci->is_visible_in_tree() && (p_retrieve_locked || !_is_node_locked(ci))) { Viewport *vp = ci->get_viewport(); - if (vp && !vp->is_visible_subviewport()) { + if (!_is_usable_viewport(vp)) { continue; } CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(ci); @@ -1066,6 +1086,47 @@ void CanvasItemEditor::_on_grid_menu_id_pressed(int p_id) { viewport->queue_redraw(); } +void CanvasItemEditor::_set_viewport_override(Viewport *p_viewport) { + if (p_viewport && p_viewport == current_viewport) { + return; + } + + const Callable update_size = callable_mp(this, &CanvasItemEditor::_update_viewport_size); + const Callable redraw = callable_mp((CanvasItem *)viewport, &CanvasItem::queue_redraw); + + if (current_viewport && _is_viewport_overridden()) { + current_viewport->disconnect("size_changed", update_size); + current_viewport->disconnect("size_changed", redraw); + } + + if (p_viewport) { + p_viewport->connect("size_changed", update_size); + p_viewport->connect("size_changed", redraw); + scene_view_display->set_global_canvas_transform(EditorNode::get_singleton()->get_scene_root()->get_global_canvas_transform()); + } else { + p_viewport = EditorNode::get_singleton()->get_scene_root(); + p_viewport->set_global_canvas_transform(scene_view_display->get_global_canvas_transform()); + scene_view_display->set_global_canvas_transform(Transform2D()); + } + + current_viewport = p_viewport; + scene_view->set_texture(p_viewport->get_texture()); + _update_viewport_size(); + viewport->queue_redraw(); +} + +void CanvasItemEditor::_update_viewport_size() { + EditorNode::get_singleton()->get_scene_root()->set_size(scene_tree->get_size()); +} + +bool CanvasItemEditor::_is_usable_viewport(const Viewport *p_viewport) const { + return p_viewport && p_viewport == current_viewport && p_viewport->is_visible_subviewport(); +} + +bool CanvasItemEditor::_is_viewport_overridden() const { + return current_viewport != EditorNode::get_singleton()->get_scene_root(); +} + void CanvasItemEditor::_switch_theme_preview(int p_mode) { view_menu->get_popup()->hide(); @@ -3793,13 +3854,13 @@ void CanvasItemEditor::_draw_axis() { Color area_axis_color = EDITOR_GET("editors/2d/viewport_border_color"); - Size2 screen_size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + Size2 viewport_size = _get_screen_size(); Vector2 screen_endpoints[4] = { transform.xform(Vector2(0, 0)), - transform.xform(Vector2(screen_size.width, 0)), - transform.xform(Vector2(screen_size.width, screen_size.height)), - transform.xform(Vector2(0, screen_size.height)) + transform.xform(Vector2(viewport_size.width, 0)), + transform.xform(Vector2(viewport_size.width, viewport_size.height)), + transform.xform(Vector2(0, viewport_size.height)) }; for (int i = 0; i < 4; i++) { @@ -3829,7 +3890,7 @@ void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Trans parent_xform = Transform2D(); canvas_xform = cl->get_transform(); } else if (Viewport *vp = Object::cast_to(p_node)) { - if (!vp->is_visible_subviewport()) { + if (_is_usable_viewport(vp)) { return; } parent_xform = Transform2D(); @@ -3964,7 +4025,7 @@ void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p parent_xform = Transform2D(); canvas_xform = cl->get_transform(); } else if (Viewport *vp = Object::cast_to(p_node)) { - if (!vp->is_visible_subviewport()) { + if (!_is_usable_viewport(vp)) { return; } parent_xform = Transform2D(); @@ -3998,7 +4059,11 @@ void CanvasItemEditor::_draw_viewport() { transform = Transform2D(); transform.scale_basis(Size2(zoom, zoom)); transform.columns[2] = -view_offset * zoom; - EditorNode::get_singleton()->get_scene_root()->set_global_canvas_transform(transform); + if (_is_viewport_overridden()) { + scene_view_display->set_global_canvas_transform(transform); + } else { + current_viewport->set_global_canvas_transform(transform); + } _draw_grid(); _draw_ruler_tool(); @@ -4054,6 +4119,7 @@ void CanvasItemEditor::_update_editor_settings() { grid_snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); snap_config_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); skeleton_menu->set_button_icon(get_editor_theme_icon(SNAME("Bone"))); + override_viewport_button->set_button_icon(get_editor_theme_icon(SNAME("Viewport"))); pan_button->set_button_icon(get_editor_theme_icon(SNAME("ToolPan"))); ruler_button->set_button_icon(get_editor_theme_icon(SNAME("Ruler"))); pivot_button->set_button_icon(get_editor_theme_icon(SNAME("EditPivot"))); @@ -4084,7 +4150,7 @@ void CanvasItemEditor::_update_editor_settings() { } void CanvasItemEditor::_project_settings_changed() { - EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels")); + current_viewport->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels")); } void CanvasItemEditor::_notification(int p_what) { @@ -4224,6 +4290,10 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { } } +void CanvasItemEditor::edit_viewport(Viewport *p_viewport) { + _update_override_viewport_button(p_viewport); +} + void CanvasItemEditor::_update_scrollbars() { updating_scroll = true; @@ -4236,7 +4306,7 @@ void CanvasItemEditor::_update_scrollbars() { Size2 vmin = v_scroll->get_minimum_size(); // Get the visible frame. - Size2 screen_rect = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + Size2 screen_rect = _get_screen_size(); Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height)); // Calculate scrollable area. @@ -4347,6 +4417,17 @@ void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) { viewport->queue_redraw(); } +void CanvasItemEditor::_button_override_viewport(bool p_pressed) { + if (p_pressed) { + Viewport *vp = Object::cast_to(override_viewport_button->get_meta("viewport")); + _set_viewport_override(vp); + override_viewport_button->set_tooltip_text(TTR("Editor Viewport Override\nEditor viewport is overridden. Press to disable override.")); + } else { + _set_viewport_override(nullptr); + override_viewport_button->set_tooltip_text(TTR("Editor Viewport Override\nMakes editor use selected SubViewport instead of the main viewport, allowing to edit nodes inside it.")); + } +} + void CanvasItemEditor::_button_tool_select(int p_index) { Button *tb[TOOL_MAX] = { select_button, list_select_button, move_button, scale_button, rotate_button, pivot_button, pan_button, ruler_button }; for (int i = 0; i < TOOL_MAX; i++) { @@ -4384,7 +4465,7 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -4461,6 +4542,32 @@ void CanvasItemEditor::_prepare_view_menu() { popup->set_item_disabled(popup->get_item_index(CLEAR_GUIDES), !has_guides); } +void CanvasItemEditor::_update_override_viewport_button(Viewport *p_viewport) { + if (p_viewport) { + override_viewport_button->set_disabled(false); + if (p_viewport == current_viewport) { + override_viewport_button->set_pressed_no_signal(true); + override_viewport_button->set_tooltip_text(TTR("Editor Viewport Override\nEditor viewport is overridden. Press to disable override.")); + } else { + override_viewport_button->set_pressed_no_signal(false); + override_viewport_button->set_meta(SNAME("viewport"), p_viewport); + override_viewport_button->set_tooltip_text(TTR("Editor Viewport Override\nMakes editor use selected SubViewport instead of the main viewport, allowing to edit nodes inside it.")); + } + + } else { + if (_is_viewport_overridden()) { + override_viewport_button->set_disabled(false); + override_viewport_button->set_pressed_no_signal(true); + override_viewport_button->set_tooltip_text(TTR("Editor Viewport Override\nEditor viewport is overridden. Press to disable override.")); + } else { + override_viewport_button->set_disabled(true); + override_viewport_button->set_pressed_no_signal(false); + override_viewport_button->set_tooltip_text(TTR("Editor Viewport Override\nNo SubViewport node selected. Select one to allow overriding editor viewport.")); + } + override_viewport_button->set_meta(SNAME("viewport"), Variant()); + } +} + void CanvasItemEditor::_popup_callback(int p_op) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); last_option = MenuOption(p_op); @@ -4598,7 +4705,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!ci || !ci->is_inside_tree()) { continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -4620,7 +4727,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!ci || !ci->is_inside_tree()) { continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -4642,7 +4749,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!ci || !ci->is_inside_tree()) { continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -4664,7 +4771,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!ci || !ci->is_inside_tree()) { continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -4705,7 +4812,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -4751,7 +4858,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; } - if (ci->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { + if (ci->get_viewport() != current_viewport) { continue; } @@ -5170,6 +5277,8 @@ void CanvasItemEditor::clear() { snap_rotation_step = EditorSettings::get_singleton()->get_project_metadata("2d_editor", "snap_rotation_step", Math::deg_to_rad(15.0)); snap_rotation_offset = EditorSettings::get_singleton()->get_project_metadata("2d_editor", "snap_rotation_offset", 0.0); snap_scale_step = EditorSettings::get_singleton()->get_project_metadata("2d_editor", "snap_scale_step", 0.1); + + edit_viewport(nullptr); } void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { @@ -5249,7 +5358,7 @@ void CanvasItemEditor::focus_selection() { } void CanvasItemEditor::center_at(const Point2 &p_pos) { - Vector2 offset = viewport->get_size() / 2 - EditorNode::get_singleton()->get_scene_root()->get_global_canvas_transform().xform(p_pos); + Vector2 offset = viewport->get_size() / 2 - current_viewport->get_global_canvas_transform().xform(p_pos); view_offset -= (offset / zoom).round(); update_viewport(); } @@ -5303,11 +5412,19 @@ CanvasItemEditor::CanvasItemEditor() { viewport_scrollable->set_h_size_flags(Control::SIZE_EXPAND_FILL); viewport_scrollable->connect(SceneStringName(draw), callable_mp(this, &CanvasItemEditor::_update_scrollbars)); - SubViewportContainer *scene_tree = memnew(SubViewportContainer); + scene_tree = memnew(SubViewportContainer); viewport_scrollable->add_child(scene_tree); scene_tree->set_stretch(true); scene_tree->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - scene_tree->add_child(EditorNode::get_singleton()->get_scene_root()); + scene_tree->connect("item_rect_changed", callable_mp(this, &CanvasItemEditor::_update_viewport_size)); + + scene_view_display = memnew(SubViewport); + scene_tree->add_child(scene_view_display); + + scene_view = memnew(TextureRect); + scene_view_display->add_child(scene_view); + scene_view->set_clip_contents(true); + scene_view->add_child(EditorNode::get_singleton()->get_scene_root()); controls_vb = memnew(VBoxContainer); controls_vb->set_begin(Point2(5, 5)); @@ -5373,6 +5490,7 @@ CanvasItemEditor::CanvasItemEditor() { viewport->connect(SceneStringName(draw), callable_mp(this, &CanvasItemEditor::_draw_viewport)); viewport->connect(SceneStringName(gui_input), callable_mp(this, &CanvasItemEditor::_gui_input_viewport)); viewport->connect(SceneStringName(focus_exited), callable_mp(panner.ptr(), &ViewPanner::release_pan_key)); + _set_viewport_override(nullptr); h_scroll = memnew(HScrollBar); viewport->add_child(h_scroll); @@ -5564,6 +5682,16 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(memnew(VSeparator)); + override_viewport_button = memnew(Button); + override_viewport_button->set_theme_type_variation("FlatButton"); + override_viewport_button->set_toggle_mode(true); + override_viewport_button->set_disabled(true); + main_menu_hbox->add_child(override_viewport_button); + override_viewport_button->connect(SceneStringName(toggled), callable_mp(this, &CanvasItemEditor::_button_override_viewport)); + _update_override_viewport_button(nullptr); + + main_menu_hbox->add_child(memnew(VSeparator)); + view_menu = memnew(MenuButton); view_menu->set_flat(false); view_menu->set_theme_type_variation("FlatMenuButton"); @@ -5736,10 +5864,11 @@ CanvasItemEditor *CanvasItemEditor::singleton = nullptr; void CanvasItemEditorPlugin::edit(Object *p_object) { canvas_item_editor->edit(Object::cast_to(p_object)); + canvas_item_editor->edit_viewport(Object::cast_to(p_object)); } bool CanvasItemEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("CanvasItem"); + return Object::cast_to(p_object) || Object::cast_to(p_object) || Object::cast_to(p_object); } void CanvasItemEditorPlugin::make_visible(bool p_visible) { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 1581db93f8c6..e075e6d7d6f9 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -47,7 +47,11 @@ class HSplitContainer; class MenuButton; class PanelContainer; class StyleBoxTexture; +class SubViewport; +class SubViewportContainer; +class TextureRect; class ViewPanner; +class Viewport; class VScrollBar; class VSeparator; class VSplitContainer; @@ -195,6 +199,10 @@ class CanvasItemEditor : public VBoxContainer { Tool tool = TOOL_SELECT; Control *viewport = nullptr; Control *viewport_scrollable = nullptr; + SubViewportContainer *scene_tree = nullptr; + SubViewport *scene_view_display = nullptr; + TextureRect *scene_view = nullptr; + Viewport *current_viewport = nullptr; HScrollBar *h_scroll = nullptr; VScrollBar *v_scroll = nullptr; @@ -335,6 +343,7 @@ class CanvasItemEditor : public VBoxContainer { Button *group_button = nullptr; Button *ungroup_button = nullptr; + Button *override_viewport_button = nullptr; MenuButton *view_menu = nullptr; PopupMenu *grid_menu = nullptr; PopupMenu *theme_menu = nullptr; @@ -418,6 +427,12 @@ class CanvasItemEditor : public VBoxContainer { void _prepare_grid_menu(); void _on_grid_menu_id_pressed(int p_id); + void _set_viewport_override(Viewport *p_viewport); + void _update_viewport_size(); + bool _is_usable_viewport(const Viewport *p_viewport) const; + bool _is_viewport_overridden() const; + Vector2 _get_screen_size() const; + public: enum ThemePreviewMode { THEME_PREVIEW_PROJECT, @@ -517,8 +532,11 @@ class CanvasItemEditor : public VBoxContainer { void _zoom_on_position(real_t p_zoom, Point2 p_position = Point2()); void _button_toggle_smart_snap(bool p_status); void _button_toggle_grid_snap(bool p_status); + void _button_override_viewport(bool p_pressed); void _button_tool_select(int p_index); + void _update_override_viewport_button(Viewport *p_viewport); + HSplitContainer *left_panel_split = nullptr; HSplitContainer *right_panel_split = nullptr; VSplitContainer *bottom_split = nullptr; @@ -581,6 +599,7 @@ class CanvasItemEditor : public VBoxContainer { void set_current_tool(Tool p_tool); void edit(CanvasItem *p_canvas_item); + void edit_viewport(Viewport *p_viewport); void focus_selection(); void center_at(const Point2 &p_pos);