From b82b06d07002b62662ab9150d8dcb1e17e29257e Mon Sep 17 00:00:00 2001 From: eitanbariboa Date: Fri, 26 Apr 2024 12:23:48 +0300 Subject: [PATCH 1/5] Working UI --- demo/addons/anthonyec.camera_preview | 1 - .../GuiResizerTopLeft.svg | 1 + .../GuiResizerTopLeft.svg.import | 37 ++ .../GuiResizerTopRight.svg | 1 + .../GuiResizerTopRight.svg.import | 37 ++ demo/addons/anthonyec.camera_preview/Pin.svg | 1 + .../anthonyec.camera_preview/Pin.svg.import | 37 ++ .../anthonyec.camera_preview/plugin.cfg | 7 + .../addons/anthonyec.camera_preview/plugin.gd | 87 ++++ .../anthonyec.camera_preview/preview.gd | 448 ++++++++++++++++++ .../anthonyec.camera_preview/preview.tscn | 204 ++++++++ demo/main_2d.tscn | 11 +- 12 files changed, 870 insertions(+), 2 deletions(-) delete mode 120000 demo/addons/anthonyec.camera_preview create mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg create mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import create mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg create mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import create mode 100644 demo/addons/anthonyec.camera_preview/Pin.svg create mode 100644 demo/addons/anthonyec.camera_preview/Pin.svg.import create mode 100644 demo/addons/anthonyec.camera_preview/plugin.cfg create mode 100644 demo/addons/anthonyec.camera_preview/plugin.gd create mode 100644 demo/addons/anthonyec.camera_preview/preview.gd create mode 100644 demo/addons/anthonyec.camera_preview/preview.tscn diff --git a/demo/addons/anthonyec.camera_preview b/demo/addons/anthonyec.camera_preview deleted file mode 120000 index a939673..0000000 --- a/demo/addons/anthonyec.camera_preview +++ /dev/null @@ -1 +0,0 @@ -/Users/anthony/workspace/misc/240101_godot_camera_preview/addons/anthonyec.camera_preview \ No newline at end of file diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg b/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg new file mode 100644 index 0000000..fe4dbf5 --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg @@ -0,0 +1 @@ + diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import b/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import new file mode 100644 index 0000000..9584d3b --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://btc01wc11tiid" +path="res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" +dest_files=["res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg b/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg new file mode 100644 index 0000000..dd00953 --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg @@ -0,0 +1 @@ + diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import b/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import new file mode 100644 index 0000000..4a1fa5d --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://04l05jxuyt7k" +path="res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" +dest_files=["res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/demo/addons/anthonyec.camera_preview/Pin.svg b/demo/addons/anthonyec.camera_preview/Pin.svg new file mode 100644 index 0000000..8e5935c --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/Pin.svg @@ -0,0 +1 @@ + diff --git a/demo/addons/anthonyec.camera_preview/Pin.svg.import b/demo/addons/anthonyec.camera_preview/Pin.svg.import new file mode 100644 index 0000000..27d274f --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/Pin.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://do6d60od41vmg" +path="res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/anthonyec.camera_preview/Pin.svg" +dest_files=["res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/demo/addons/anthonyec.camera_preview/plugin.cfg b/demo/addons/anthonyec.camera_preview/plugin.cfg new file mode 100644 index 0000000..4ad0d4c --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Little Camera Preview" +description="Shows a picture-in-picture preview of the selected 2D or 3D camera" +author="Anthony Cossins" +version="1.3" +script="plugin.gd" diff --git a/demo/addons/anthonyec.camera_preview/plugin.gd b/demo/addons/anthonyec.camera_preview/plugin.gd new file mode 100644 index 0000000..4e74dd8 --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/plugin.gd @@ -0,0 +1,87 @@ +@tool +extends EditorPlugin + +const preview_scene = preload("res://addons/anthonyec.camera_preview/preview.tscn") + +var preview: CameraPreview +var current_main_screen_name: String + +func _enter_tree() -> void: + main_screen_changed.connect(_on_main_screen_changed) + EditorInterface.get_selection().selection_changed.connect(_on_editor_selection_changed) + + # Initialise preview panel and add to main screen. + preview = preview_scene.instantiate() as CameraPreview + preview.request_hide() + + var main_screen = EditorInterface.get_editor_main_screen() + main_screen.add_child(preview) + +func _exit_tree() -> void: + if preview: + preview.queue_free() + +func _ready() -> void: + # TODO: Currently there is no API to get the main screen name without + # listening to the `EditorPlugin.main_screen_changed` signal: + # https://github.com/godotengine/godot-proposals/issues/2081 + EditorInterface.set_main_screen_editor("Script") + EditorInterface.set_main_screen_editor("3D") + +func _on_main_screen_changed(screen_name: String) -> void: + current_main_screen_name = screen_name + + # TODO: Bit of a hack to prevent pinned staying between view changes on the same scene. + preview.unlink_camera() + _on_editor_selection_changed() + +func _on_editor_selection_changed() -> void: + if not is_main_screen_viewport(): + # This hides the preview "container" and not the preview itself, allowing + # any locked previews to remain visible once switching back to 3D tab. + preview.visible = false + return + + preview.visible = true + + var selected_nodes = EditorInterface.get_selection().get_selected_nodes() + + var selected_camera_3d: Camera3D = find_camera_3d_or_null(selected_nodes) + var selected_camera_2d: Camera2D = find_camera_2d_or_null(selected_nodes) + + if selected_camera_3d and current_main_screen_name == "3D": + preview.link_with_camera_3d(selected_camera_3d) + preview.request_show() + + elif selected_camera_2d and current_main_screen_name == "2D": + preview.link_with_camera_2d(selected_camera_2d) + preview.request_show() + + else: + preview.request_hide() + +func is_main_screen_viewport() -> bool: + return current_main_screen_name == "3D" or current_main_screen_name == "2D" + +func find_camera_3d_or_null(nodes: Array[Node]) -> Camera3D: + var camera: Camera3D + + for node in nodes: + if node is Camera3D: + camera = node as Camera3D + break + + return camera + +func find_camera_2d_or_null(nodes: Array[Node]) -> Camera2D: + var camera: Camera2D + + for node in nodes: + if node is Camera2D: + camera = node as Camera2D + break + + return camera + +func _on_selected_camera_3d_tree_exiting() -> void: + preview.unlink_camera() diff --git a/demo/addons/anthonyec.camera_preview/preview.gd b/demo/addons/anthonyec.camera_preview/preview.gd new file mode 100644 index 0000000..e94cc06 --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/preview.gd @@ -0,0 +1,448 @@ +@tool + +class_name CameraPreview +extends Control + +enum CameraType { + CAMERA_2D, + CAMERA_3D +} + +enum PinnedPosition { + LEFT, + RIGHT, +} + +enum InteractionState { + NONE, + RESIZE, + DRAG, + + # Animation is split into 2 seperate states so that the tween is only + # invoked once in the "start" state. + START_ANIMATE_INTO_PLACE, + ANIMATE_INTO_PLACE, +} + +const margin_3d: Vector2 = Vector2(10, 10) +const margin_2d: Vector2 = Vector2(20, 15) +const panel_margin: float = 2 +const min_panel_size: float = 250 + +@onready var panel: Panel = %Panel +@onready var placeholder: Panel = %Placeholder +@onready var preview_camera_3d: Camera3D = %Camera3D +@onready var preview_camera_2d: Camera2D = %Camera2D +@onready var sub_viewport: SubViewport = %SubViewport +@onready var sub_viewport_text_rect: TextureRect = %TextureRect +@onready var resize_left_handle: Button = %ResizeLeftHandle +@onready var resize_right_handle: Button = %ResizeRightHandle +@onready var lock_button: Button = %LockButton +@onready var gradient: TextureRect = %Gradient +@onready var viewport_margin_container: MarginContainer = %ViewportMarginContainer +@onready var overlay_margin_container: MarginContainer = %OverlayMarginContainer +@onready var overlay_container: Control = %OverlayContainer +@onready var ui_draw: CanvasLayer = $Panel/SubViewport/UIDraw + +var camera_type: CameraType = CameraType.CAMERA_3D +var pinned_position: PinnedPosition = PinnedPosition.RIGHT +var viewport_ratio: float = 1 +var editor_scale: float = EditorInterface.get_editor_scale() +var is_locked: bool +var show_controls: bool +var selected_camera_3d: Camera3D +var selected_camera_2d: Camera2D + +var state: InteractionState = InteractionState.NONE +var initial_mouse_position: Vector2 +var initial_panel_size: Vector2 +var initial_panel_position: Vector2 + +func _ready() -> void: + # Set initial width. + panel.size.x = min_panel_size * editor_scale + + # Setting texture to viewport in code instead of directly in the editor + # because otherwise an error "Path to node is invalid: Panel/SubViewport" + # on first load. This is harmless but doesn't look great. + # + # This is a known issue: + # https://github.com/godotengine/godot/issues/27790#issuecomment-499740220 + sub_viewport_text_rect.texture = sub_viewport.get_texture() + + # From what I can tell there's something wrong with how an editor theme + # scales when used within a plugin. It seems to ignore the screen scale. + # For instance, a 30x30px button will appear tiny on a retina display. + # + # Someone else had the issue with no luck: + # https://forum.godotengine.org/t/how-to-scale-plugin-controls-to-look-the-same-in-4k-as-1080p/36151 + # + # And seems Dialogic also scales buttons manually: + # https://github.com/dialogic-godot/dialogic/blob/master/addons/dialogic/Editor/Common/sidebar.gd#L25C6-L38 + # + # Maybe I don't know the correct way to do it, so for now the workaround is + # to set the correct size in code using screen scale. + var button_size = Vector2(30, 30) * editor_scale + var margin_size: float = panel_margin * editor_scale + + resize_left_handle.size = button_size + resize_left_handle.pivot_offset = Vector2(0, 0) * editor_scale + + resize_right_handle.size = button_size + resize_right_handle.pivot_offset = Vector2(30, 30) * editor_scale + + lock_button.size = button_size + lock_button.pivot_offset = Vector2(0, 30) * editor_scale + + viewport_margin_container.add_theme_constant_override("margin_left", margin_size) + viewport_margin_container.add_theme_constant_override("margin_top", margin_size) + viewport_margin_container.add_theme_constant_override("margin_right", margin_size) + viewport_margin_container.add_theme_constant_override("margin_bottom", margin_size) + + overlay_margin_container.add_theme_constant_override("margin_left", margin_size) + overlay_margin_container.add_theme_constant_override("margin_top", margin_size) + overlay_margin_container.add_theme_constant_override("margin_right", margin_size) + overlay_margin_container.add_theme_constant_override("margin_bottom", margin_size) + + # Parent node overlay size is not available on first ready, need to wait a + # frame for it to be drawn. + await get_tree().process_frame + + # Anchors are set in code because setting them in the editor UI doesn't take + # editor scale into account. + resize_left_handle.position = Vector2(0, 0) + resize_right_handle.set_anchors_preset(Control.PRESET_TOP_LEFT) + + resize_right_handle.position = Vector2(overlay_container.size.x - button_size.x, 0) + resize_right_handle.set_anchors_preset(Control.PRESET_TOP_RIGHT) + + lock_button.position = Vector2(0, overlay_container.size.y - button_size.y) + lock_button.set_anchors_preset(Control.PRESET_BOTTOM_LEFT) + +func _process(_delta: float) -> void: + if not visible: return + + match state: + InteractionState.NONE: + panel.size = get_clamped_size(panel.size) + panel.position = get_pinned_position(pinned_position) + + InteractionState.RESIZE: + var delta_mouse_position = initial_mouse_position - get_global_mouse_position() + var resized_size = panel.size + + if pinned_position == PinnedPosition.LEFT: + resized_size = initial_panel_size - delta_mouse_position + + if pinned_position == PinnedPosition.RIGHT: + resized_size = initial_panel_size + delta_mouse_position + + panel.size = get_clamped_size(resized_size) + panel.position = get_pinned_position(pinned_position) + + InteractionState.DRAG: + placeholder.size = panel.size + + var global_mouse_position = get_global_mouse_position() + var offset = initial_mouse_position - initial_panel_position + + panel.global_position = global_mouse_position - offset + + if global_mouse_position.x < global_position.x + size.x / 2: + pinned_position = PinnedPosition.LEFT + else: + pinned_position = PinnedPosition.RIGHT + + placeholder.position = get_pinned_position(pinned_position) + + InteractionState.START_ANIMATE_INTO_PLACE: + var final_position: Vector2 = get_pinned_position(pinned_position) + var tween = get_tree().create_tween() + + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_CUBIC) + tween.tween_property(panel, "position", final_position, 0.3) + + tween.finished.connect(func(): + panel.position = final_position + state = InteractionState.NONE + ) + + state = InteractionState.ANIMATE_INTO_PLACE + + # I couldn't get `mouse_entered` and `mouse_exited` events to work + # nicely, so I use rect method instead. Plus using this method it's easy to + # grow the hit area size. + var panel_hover_rect = Rect2(panel.global_position, panel.size) + panel_hover_rect = panel_hover_rect.grow(40) + + var mouse_position = get_global_mouse_position() + + show_controls = state != InteractionState.NONE or panel_hover_rect.has_point(mouse_position) + + # UI visibility. + resize_left_handle.visible = show_controls and pinned_position == PinnedPosition.RIGHT + resize_right_handle.visible = show_controls and pinned_position == PinnedPosition.LEFT + lock_button.visible = show_controls or is_locked + placeholder.visible = state == InteractionState.DRAG or state == InteractionState.ANIMATE_INTO_PLACE + gradient.visible = show_controls + + # Sync camera settings. + if camera_type == CameraType.CAMERA_3D and selected_camera_3d: + sub_viewport.size = panel.size + + # Sync position and rotation without using a `RemoteTransform` node + # because if you save a camera as a scene, the remote transform node will + # be stored within the scene. Also it's harder to keep the remote + # transform `remote_path` up-to-date with scene changes, which causes + # many errors. + preview_camera_3d.global_position = selected_camera_3d.global_position + preview_camera_3d.global_rotation = selected_camera_3d.global_rotation + + preview_camera_3d.fov = selected_camera_3d.fov + preview_camera_3d.projection = selected_camera_3d.projection + preview_camera_3d.size = selected_camera_3d.size + preview_camera_3d.cull_mask = selected_camera_3d.cull_mask + preview_camera_3d.keep_aspect = selected_camera_3d.keep_aspect + preview_camera_3d.near = selected_camera_3d.near + preview_camera_3d.far = selected_camera_3d.far + preview_camera_3d.h_offset = selected_camera_3d.h_offset + preview_camera_3d.v_offset = selected_camera_3d.v_offset + preview_camera_3d.attributes = selected_camera_3d.attributes + preview_camera_3d.environment = selected_camera_3d.environment + + if camera_type == CameraType.CAMERA_2D and selected_camera_2d: + var project_window_size = get_project_window_size() + var ratio = project_window_size.x / panel.size.x + + # TODO: Is there a better way to fix this? + # The camera border is visible sometimes due to pixel rounding. + # Subtract 1px from right and bottom to hide this. + var hide_camera_border_fix = Vector2(1, 1) + + sub_viewport.size = panel.size + sub_viewport.size_2d_override = (panel.size - hide_camera_border_fix) * ratio + sub_viewport.size_2d_override_stretch = true + + preview_camera_2d.global_position = selected_camera_2d.global_position + preview_camera_2d.global_rotation = selected_camera_2d.global_rotation + + preview_camera_2d.offset = selected_camera_2d.offset + preview_camera_2d.zoom = selected_camera_2d.zoom + preview_camera_2d.ignore_rotation = selected_camera_2d.ignore_rotation + preview_camera_2d.anchor_mode = selected_camera_2d.anchor_mode + preview_camera_2d.limit_left = selected_camera_2d.limit_left + preview_camera_2d.limit_right = selected_camera_2d.limit_right + preview_camera_2d.limit_top = selected_camera_2d.limit_top + preview_camera_2d.limit_bottom = selected_camera_2d.limit_bottom + +func link_with_camera_3d(camera_3d: Camera3D) -> void: + # TODO: Camera may not be ready since this method is called in `_enter_tree` + # in the plugin because of a workaround for: + # https://github.com/godotengine/godot-proposals/issues/2081 + if not preview_camera_3d: + return request_hide() + + var is_different_camera = camera_3d != preview_camera_3d + + # TODO: A bit messy. + if is_different_camera: + if preview_camera_3d.tree_exiting.is_connected(unlink_camera): + preview_camera_3d.tree_exiting.disconnect(unlink_camera) + + if not camera_3d.tree_exiting.is_connected(unlink_camera): + camera_3d.tree_exiting.connect(unlink_camera) + + sub_viewport.disable_3d = false + sub_viewport.world_3d = camera_3d.get_world_3d() + + selected_camera_3d = camera_3d + camera_type = CameraType.CAMERA_3D + +func link_with_camera_2d(camera_2d: Camera2D) -> void: + if not preview_camera_2d: + return request_hide() + + var is_different_camera = camera_2d != preview_camera_2d + # TODO: A bit messy. + if is_different_camera: + + if preview_camera_2d.tree_exiting.is_connected(unlink_camera): + preview_camera_2d.tree_exiting.disconnect(unlink_camera) + + if not camera_2d.tree_exiting.is_connected(unlink_camera): + camera_2d.tree_exiting.connect(unlink_camera) + + + sub_viewport.disable_3d = true + sub_viewport.world_2d = camera_2d.get_world_2d() + selected_camera_2d = camera_2d + redraw_ui() + + camera_type = CameraType.CAMERA_2D + +func redraw_ui() -> void: + var obj = get_main_scene(selected_camera_2d) + print(obj) + if obj: + var collectionfound = findByType(obj,CanvasLayer) + if(ui_draw.get_children()): + for child in ui_draw.get_children(): + ui_draw.remove_child(child) + child.queue_free() + for child in collectionfound: + var newObj = child.duplicate() + ui_draw.add_child(newObj) + +func unlink_camera() -> void: + if selected_camera_3d: + selected_camera_3d = null + + if selected_camera_2d: + selected_camera_2d = null + + is_locked = false + lock_button.button_pressed = false + +func request_hide() -> void: + if is_locked: return + visible = false + +func request_show() -> void: + visible = true + +func get_pinned_position(pinned_position: PinnedPosition) -> Vector2: + var margin: Vector2 = margin_3d * editor_scale + + if camera_type == CameraType.CAMERA_2D: + margin = margin_2d * editor_scale + + match pinned_position: + PinnedPosition.LEFT: + return Vector2.ZERO - Vector2(0, panel.size.y) - Vector2(-margin.x, margin.y) + PinnedPosition.RIGHT: + return size - panel.size - margin + _: + assert(false, "Unknown pinned position %s" % str(pinned_position)) + + return Vector2.ZERO + +func get_clamped_size(desired_size: Vector2) -> Vector2: + var viewport_ratio = get_project_window_ratio() + var editor_viewport_size = get_editor_viewport_size() + + var max_bounds = Vector2( + editor_viewport_size.x * 0.6, + editor_viewport_size.y * 0.8 + ) + + var clamped_size = desired_size + + # Apply aspect ratio. + clamped_size = Vector2(clamped_size.x, clamped_size.x * viewport_ratio) + + # Clamp the max size while respecting the aspect ratio. + if clamped_size.y >= max_bounds.y: + clamped_size.x = max_bounds.y / viewport_ratio + clamped_size.y = max_bounds.y + + if clamped_size.x >= max_bounds.x: + clamped_size.x = max_bounds.x + clamped_size.y = max_bounds.x * viewport_ratio + + # Clamp the min size based on if it's portrait or landscape. Portrait min + # size should be based on it's height. Landscape min size is based on it's + # width instead. Applying min width to a portrait size would make it too big. + var is_portrait = viewport_ratio > 1 + + if is_portrait and clamped_size.y <= min_panel_size * editor_scale: + clamped_size.x = min_panel_size / viewport_ratio + clamped_size.y = min_panel_size + clamped_size = clamped_size * editor_scale + + if not is_portrait and clamped_size.x <= min_panel_size * editor_scale: + clamped_size.x = min_panel_size + clamped_size.y = min_panel_size * viewport_ratio + clamped_size = clamped_size * editor_scale + + # Round down to avoid sub-pixel artifacts, mainly seen around the margins. + return clamped_size.floor() + +func get_project_window_size() -> Vector2: + var window_width = float(ProjectSettings.get_setting("display/window/size/viewport_width")) + var window_height = float(ProjectSettings.get_setting("display/window/size/viewport_height")) + + return Vector2(window_width, window_height) + +func get_project_window_ratio() -> float: + var project_window_size = get_project_window_size() + + return project_window_size.y / project_window_size.x + +func get_editor_viewport_size() -> Vector2: + var fallback_size = EditorInterface.get_editor_main_screen().size + + # There isn't an API for getting the viewport node. Instead it has to be + # found by checking the parent's parent of the subviewport and find + # the correct node based on name and class. + var editor_sub_viewport_3d = EditorInterface.get_editor_viewport_3d(0) + var editor_viewport_container = editor_sub_viewport_3d.get_parent().get_parent().get_parent() + + # Early return incase editor tree structure has changed. + if editor_viewport_container.get_class() != "Node3DEditorViewportContainer": + return fallback_size + + return editor_viewport_container.size + +func _on_resize_handle_button_down() -> void: + if state != InteractionState.NONE: return + + state = InteractionState.RESIZE + initial_mouse_position = get_global_mouse_position() + initial_panel_size = panel.size + +func _on_resize_handle_button_up() -> void: + state = InteractionState.NONE + +func _on_drag_handle_button_down() -> void: + if state != InteractionState.NONE: return + + state = InteractionState.DRAG + initial_mouse_position = get_global_mouse_position() + initial_panel_position = panel.global_position + +func _on_drag_handle_button_up() -> void: + if state != InteractionState.DRAG: return + + state = InteractionState.START_ANIMATE_INTO_PLACE + +func _on_lock_button_pressed() -> void: + is_locked = !is_locked + + +var listOfAllNodesInTree = [] +func findByType(parent, type,all:bool = false): + listOfAllNodesInTree.clear() + if not all: + for child in parent.get_children(): + if is_instance_of(child, type): + listOfAllNodesInTree.append(child) + else: + find(parent, type) + return listOfAllNodesInTree + +func find(parent, type): + for child in parent.get_children(): + if is_instance_of(child, type): + print(child) + listOfAllNodesInTree.append(child) + find(child, type) + + +func get_main_scene(obj): + if is_instance_of( obj ,SubViewport): + return null + if is_instance_of(obj.get_parent(),SubViewport): + return obj + else: + return get_main_scene(obj.get_parent()) diff --git a/demo/addons/anthonyec.camera_preview/preview.tscn b/demo/addons/anthonyec.camera_preview/preview.tscn new file mode 100644 index 0000000..4c8754c --- /dev/null +++ b/demo/addons/anthonyec.camera_preview/preview.tscn @@ -0,0 +1,204 @@ +[gd_scene load_steps=8 format=3 uid="uid://xybmfvufjuv"] + +[ext_resource type="Script" path="res://addons/anthonyec.camera_preview/preview.gd" id="1_6b32r"] +[ext_resource type="Texture2D" uid="uid://do6d60od41vmg" path="res://addons/anthonyec.camera_preview/Pin.svg" id="2_p0pa8"] +[ext_resource type="Texture2D" uid="uid://btc01wc11tiid" path="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" id="2_t64ej"] +[ext_resource type="Texture2D" uid="uid://04l05jxuyt7k" path="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" id="3_6yuab"] + +[sub_resource type="ViewportTexture" id="ViewportTexture_k8axx"] +viewport_path = NodePath("Panel/SubViewport") + +[sub_resource type="Gradient" id="Gradient_11p6r"] +offsets = PackedFloat32Array(0, 0.3, 0.6, 1) +colors = PackedColorArray(0, 0, 0, 0.235294, 0, 0, 0, 0.0784314, 0, 0, 0, 0.0784314, 0, 0, 0, 0.235294) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_4dkve"] +gradient = SubResource("Gradient_11p6r") +width = 256 +height = 256 +fill_to = Vector2(2.08165e-12, 1) + +[node name="Preview" type="Control"] +z_index = 999 +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_6b32r") + +[node name="Placeholder" type="Panel" parent="."] +unique_name_in_owner = true +visible = false +modulate = Color(1, 1, 1, 0.705882) +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -40.0 +offset_right = 410.0 +offset_bottom = 410.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="Panel" type="Panel" parent="."] +unique_name_in_owner = true +clip_contents = true +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -260.0 +offset_top = -150.0 +offset_right = -10.0 +offset_bottom = -10.0 +grow_horizontal = 0 +grow_vertical = 0 +pivot_offset = Vector2(450, 300) + +[node name="SubViewport" type="SubViewport" parent="Panel"] +unique_name_in_owner = true +handle_input_locally = false +gui_disable_input = true +size_2d_override_stretch = true + +[node name="Camera3D" type="Camera3D" parent="Panel/SubViewport"] +unique_name_in_owner = true +current = true + +[node name="Camera2D" type="Camera2D" parent="Panel/SubViewport"] +unique_name_in_owner = true +ignore_rotation = false + +[node name="UIDraw" type="CanvasLayer" parent="Panel/SubViewport"] + +[node name="ViewportMarginContainer" type="MarginContainer" parent="Panel"] +unique_name_in_owner = true +clip_contents = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 + +[node name="TextureRect" type="TextureRect" parent="Panel/ViewportMarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +texture = SubResource("ViewportTexture_k8axx") +expand_mode = 1 + +[node name="Gradient" type="TextureRect" parent="Panel"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +texture = SubResource("GradientTexture2D_4dkve") + +[node name="OverlayMarginContainer" type="MarginContainer" parent="Panel"] +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 +mouse_filter = 2 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 + +[node name="OverlayContainer" type="Control" parent="Panel/OverlayMarginContainer"] +unique_name_in_owner = true +clip_contents = true +layout_mode = 2 +mouse_filter = 2 + +[node name="DragHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_mode = 0 +flat = true + +[node name="ResizeLeftHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +offset_right = 30.0 +offset_bottom = 30.0 +size_flags_horizontal = 0 +size_flags_vertical = 0 +mouse_default_cursor_shape = 12 +icon = ExtResource("2_t64ej") +flat = true +icon_alignment = 1 +expand_icon = true + +[node name="ResizeRightHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -30.0 +offset_bottom = 30.0 +grow_horizontal = 0 +pivot_offset = Vector2(30, 30) +size_flags_horizontal = 8 +size_flags_vertical = 0 +mouse_default_cursor_shape = 11 +icon = ExtResource("3_6yuab") +flat = true +icon_alignment = 1 +expand_icon = true + +[node name="LockButton" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -30.0 +offset_right = 30.0 +grow_vertical = 0 +pivot_offset = Vector2(0, 30) +size_flags_horizontal = 0 +size_flags_vertical = 8 +tooltip_text = "Always Show Preview" +toggle_mode = true +icon = ExtResource("2_p0pa8") +flat = true +icon_alignment = 1 +expand_icon = true + +[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_down"] +[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_up"] +[connection signal="renamed" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_renamed"] +[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_down"] +[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_up"] +[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_down"] +[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_up"] +[connection signal="pressed" from="Panel/OverlayMarginContainer/OverlayContainer/LockButton" to="." method="_on_lock_button_pressed"] diff --git a/demo/main_2d.tscn b/demo/main_2d.tscn index 96ff74a..d3521e5 100644 --- a/demo/main_2d.tscn +++ b/demo/main_2d.tscn @@ -15,7 +15,16 @@ scale = Vector2(0.124943, 0.124943) texture = ExtResource("2_fk71s") [node name="Camera2D2" type="Camera2D" parent="."] -position = Vector2(162, 227) +position = Vector2(280, 133) rotation = -0.268458 ignore_rotation = false zoom = Vector2(2, 2) + +[node name="test" type="CanvasLayer" parent="."] + +[node name="Label" type="Label" parent="test"] +offset_right = 40.0 +offset_bottom = 23.0 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_font_sizes/font_size = 121 +text = "sasdfasdfsd" From 495b79f5e2efaa69338ee9bfe4ac4c174d2852d8 Mon Sep 17 00:00:00 2001 From: eitanbariboa Date: Fri, 26 Apr 2024 12:34:05 +0300 Subject: [PATCH 2/5] Working UI --- demo/addons/anthonyec.camera_preview/preview.gd | 14 ++++++++------ demo/main_2d.tscn | 9 +++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/demo/addons/anthonyec.camera_preview/preview.gd b/demo/addons/anthonyec.camera_preview/preview.gd index e94cc06..73f50c0 100644 --- a/demo/addons/anthonyec.camera_preview/preview.gd +++ b/demo/addons/anthonyec.camera_preview/preview.gd @@ -283,14 +283,17 @@ func link_with_camera_2d(camera_2d: Camera2D) -> void: func redraw_ui() -> void: var obj = get_main_scene(selected_camera_2d) - print(obj) if obj: - var collectionfound = findByType(obj,CanvasLayer) + var canvasfound = findByType(obj,CanvasLayer,true) + var controlfound = findByType(obj,Control,true) if(ui_draw.get_children()): for child in ui_draw.get_children(): ui_draw.remove_child(child) child.queue_free() - for child in collectionfound: + for child in canvasfound: + var newObj = child.duplicate() + ui_draw.add_child(newObj) + for child in controlfound: var newObj = child.duplicate() ui_draw.add_child(newObj) @@ -421,9 +424,9 @@ func _on_lock_button_pressed() -> void: var listOfAllNodesInTree = [] -func findByType(parent, type,all:bool = false): +func findByType(parent, type,isParent:bool = false): listOfAllNodesInTree.clear() - if not all: + if not isParent: for child in parent.get_children(): if is_instance_of(child, type): listOfAllNodesInTree.append(child) @@ -434,7 +437,6 @@ func findByType(parent, type,all:bool = false): func find(parent, type): for child in parent.get_children(): if is_instance_of(child, type): - print(child) listOfAllNodesInTree.append(child) find(child, type) diff --git a/demo/main_2d.tscn b/demo/main_2d.tscn index d3521e5..1e17a40 100644 --- a/demo/main_2d.tscn +++ b/demo/main_2d.tscn @@ -28,3 +28,12 @@ offset_bottom = 23.0 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_font_sizes/font_size = 121 text = "sasdfasdfsd" + +[node name="Label" type="Label" parent="."] +offset_left = 438.0 +offset_top = 378.0 +offset_right = 655.0 +offset_bottom = 401.0 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_font_sizes/font_size = 231 +text = "asdasdasdasdasdasdasdasd" From 4f2eb587897e0c7a1cd4056d250b4884c61bae7c Mon Sep 17 00:00:00 2001 From: eitanbariboa Date: Fri, 26 Apr 2024 12:48:17 +0300 Subject: [PATCH 3/5] Fixed rendered under Node2D --- .../anthonyec.camera_preview/preview.gd | 28 ++++++------------- demo/main_2d.tscn | 20 +++++++------ demo/main_3d.tscn | 11 ++++++++ 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/demo/addons/anthonyec.camera_preview/preview.gd b/demo/addons/anthonyec.camera_preview/preview.gd index 73f50c0..6bf17a8 100644 --- a/demo/addons/anthonyec.camera_preview/preview.gd +++ b/demo/addons/anthonyec.camera_preview/preview.gd @@ -258,6 +258,7 @@ func link_with_camera_3d(camera_3d: Camera3D) -> void: selected_camera_3d = camera_3d camera_type = CameraType.CAMERA_3D + redraw_ui(selected_camera_3d) func link_with_camera_2d(camera_2d: Camera2D) -> void: if not preview_camera_2d: @@ -277,23 +278,20 @@ func link_with_camera_2d(camera_2d: Camera2D) -> void: sub_viewport.disable_3d = true sub_viewport.world_2d = camera_2d.get_world_2d() selected_camera_2d = camera_2d - redraw_ui() - camera_type = CameraType.CAMERA_2D + redraw_ui(selected_camera_2d) -func redraw_ui() -> void: - var obj = get_main_scene(selected_camera_2d) +func redraw_ui(cameraSelected) -> void: + var obj = get_main_scene(cameraSelected) if obj: - var canvasfound = findByType(obj,CanvasLayer,true) - var controlfound = findByType(obj,Control,true) if(ui_draw.get_children()): for child in ui_draw.get_children(): ui_draw.remove_child(child) child.queue_free() - for child in canvasfound: + for child in findByType(obj,CanvasLayer): var newObj = child.duplicate() ui_draw.add_child(newObj) - for child in controlfound: + for child in findByType(obj,Control): var newObj = child.duplicate() ui_draw.add_child(newObj) @@ -424,22 +422,12 @@ func _on_lock_button_pressed() -> void: var listOfAllNodesInTree = [] -func findByType(parent, type,isParent:bool = false): +func findByType(parent, type): listOfAllNodesInTree.clear() - if not isParent: - for child in parent.get_children(): - if is_instance_of(child, type): - listOfAllNodesInTree.append(child) - else: - find(parent, type) - return listOfAllNodesInTree - -func find(parent, type): for child in parent.get_children(): if is_instance_of(child, type): listOfAllNodesInTree.append(child) - find(child, type) - + return listOfAllNodesInTree func get_main_scene(obj): if is_instance_of( obj ,SubViewport): diff --git a/demo/main_2d.tscn b/demo/main_2d.tscn index 1e17a40..95c3cd7 100644 --- a/demo/main_2d.tscn +++ b/demo/main_2d.tscn @@ -15,7 +15,7 @@ scale = Vector2(0.124943, 0.124943) texture = ExtResource("2_fk71s") [node name="Camera2D2" type="Camera2D" parent="."] -position = Vector2(280, 133) +position = Vector2(248, 146) rotation = -0.268458 ignore_rotation = false zoom = Vector2(2, 2) @@ -23,17 +23,21 @@ zoom = Vector2(2, 2) [node name="test" type="CanvasLayer" parent="."] [node name="Label" type="Label" parent="test"] -offset_right = 40.0 -offset_bottom = 23.0 +offset_left = 123.0 +offset_top = 40.0 +offset_right = 1220.0 +offset_bottom = 206.0 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_font_sizes/font_size = 121 -text = "sasdfasdfsd" +text = "Hello 2D" -[node name="Label" type="Label" parent="."] +[node name="Node2D" type="Node2D" parent="."] + +[node name="Label" type="Label" parent="Node2D"] offset_left = 438.0 offset_top = 378.0 -offset_right = 655.0 -offset_bottom = 401.0 +offset_right = 3558.0 +offset_bottom = 693.0 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_font_sizes/font_size = 231 -text = "asdasdasdasdasdasdasdasd" +text = "Hello Not to render" diff --git a/demo/main_3d.tscn b/demo/main_3d.tscn index e8ed806..d6ae9d4 100644 --- a/demo/main_3d.tscn +++ b/demo/main_3d.tscn @@ -63,3 +63,14 @@ fov = 69.4097 [node name="Camera3D3" type="Camera3D" parent="."] transform = Transform3D(0.716911, 0.00464808, 0.69715, 1.1914e-10, 0.999978, -0.00666711, -0.697165, 0.00477972, 0.716895, 8.21847, 1.01507, 7.42971) fov = 19.3989 + +[node name="test" type="CanvasLayer" parent="."] + +[node name="Label" type="Label" parent="test"] +offset_left = 123.0 +offset_top = 40.0 +offset_right = 1220.0 +offset_bottom = 206.0 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_font_sizes/font_size = 121 +text = "hello3D" From ffb9484ae9cab03d2f655bd67c64764b1dc7e94d Mon Sep 17 00:00:00 2001 From: eitanbariboa Date: Fri, 26 Apr 2024 12:54:09 +0300 Subject: [PATCH 4/5] working UI and organized folders --- addons/anthonyec.camera_preview/preview.gd | 38 +- addons/anthonyec.camera_preview/preview.tscn | 48 +- .../GuiResizerTopLeft.svg | 1 - .../GuiResizerTopLeft.svg.import | 37 -- .../GuiResizerTopRight.svg | 1 - .../GuiResizerTopRight.svg.import | 37 -- demo/addons/anthonyec.camera_preview/Pin.svg | 1 - .../anthonyec.camera_preview/Pin.svg.import | 37 -- .../anthonyec.camera_preview/plugin.cfg | 7 - .../addons/anthonyec.camera_preview/plugin.gd | 87 ---- .../anthonyec.camera_preview/preview.gd | 438 ------------------ .../anthonyec.camera_preview/preview.tscn | 204 -------- demo/main_2d.tscn | 2 +- 13 files changed, 63 insertions(+), 875 deletions(-) delete mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg delete mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import delete mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg delete mode 100644 demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import delete mode 100644 demo/addons/anthonyec.camera_preview/Pin.svg delete mode 100644 demo/addons/anthonyec.camera_preview/Pin.svg.import delete mode 100644 demo/addons/anthonyec.camera_preview/plugin.cfg delete mode 100644 demo/addons/anthonyec.camera_preview/plugin.gd delete mode 100644 demo/addons/anthonyec.camera_preview/preview.gd delete mode 100644 demo/addons/anthonyec.camera_preview/preview.tscn diff --git a/addons/anthonyec.camera_preview/preview.gd b/addons/anthonyec.camera_preview/preview.gd index 3c07d04..6bf17a8 100644 --- a/addons/anthonyec.camera_preview/preview.gd +++ b/addons/anthonyec.camera_preview/preview.gd @@ -42,6 +42,7 @@ const min_panel_size: float = 250 @onready var viewport_margin_container: MarginContainer = %ViewportMarginContainer @onready var overlay_margin_container: MarginContainer = %OverlayMarginContainer @onready var overlay_container: Control = %OverlayContainer +@onready var ui_draw: CanvasLayer = $Panel/SubViewport/UIDraw var camera_type: CameraType = CameraType.CAMERA_3D var pinned_position: PinnedPosition = PinnedPosition.RIGHT @@ -257,27 +258,43 @@ func link_with_camera_3d(camera_3d: Camera3D) -> void: selected_camera_3d = camera_3d camera_type = CameraType.CAMERA_3D + redraw_ui(selected_camera_3d) func link_with_camera_2d(camera_2d: Camera2D) -> void: if not preview_camera_2d: return request_hide() var is_different_camera = camera_2d != preview_camera_2d - # TODO: A bit messy. if is_different_camera: + if preview_camera_2d.tree_exiting.is_connected(unlink_camera): preview_camera_2d.tree_exiting.disconnect(unlink_camera) if not camera_2d.tree_exiting.is_connected(unlink_camera): camera_2d.tree_exiting.connect(unlink_camera) + sub_viewport.disable_3d = true sub_viewport.world_2d = camera_2d.get_world_2d() - selected_camera_2d = camera_2d camera_type = CameraType.CAMERA_2D + redraw_ui(selected_camera_2d) +func redraw_ui(cameraSelected) -> void: + var obj = get_main_scene(cameraSelected) + if obj: + if(ui_draw.get_children()): + for child in ui_draw.get_children(): + ui_draw.remove_child(child) + child.queue_free() + for child in findByType(obj,CanvasLayer): + var newObj = child.duplicate() + ui_draw.add_child(newObj) + for child in findByType(obj,Control): + var newObj = child.duplicate() + ui_draw.add_child(newObj) + func unlink_camera() -> void: if selected_camera_3d: selected_camera_3d = null @@ -402,3 +419,20 @@ func _on_drag_handle_button_up() -> void: func _on_lock_button_pressed() -> void: is_locked = !is_locked + + +var listOfAllNodesInTree = [] +func findByType(parent, type): + listOfAllNodesInTree.clear() + for child in parent.get_children(): + if is_instance_of(child, type): + listOfAllNodesInTree.append(child) + return listOfAllNodesInTree + +func get_main_scene(obj): + if is_instance_of( obj ,SubViewport): + return null + if is_instance_of(obj.get_parent(),SubViewport): + return obj + else: + return get_main_scene(obj.get_parent()) diff --git a/addons/anthonyec.camera_preview/preview.tscn b/addons/anthonyec.camera_preview/preview.tscn index 77b3ced..4c8754c 100644 --- a/addons/anthonyec.camera_preview/preview.tscn +++ b/addons/anthonyec.camera_preview/preview.tscn @@ -5,7 +5,7 @@ [ext_resource type="Texture2D" uid="uid://btc01wc11tiid" path="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" id="2_t64ej"] [ext_resource type="Texture2D" uid="uid://04l05jxuyt7k" path="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" id="3_6yuab"] -[sub_resource type="ViewportTexture" id="ViewportTexture_hchdq"] +[sub_resource type="ViewportTexture" id="ViewportTexture_k8axx"] viewport_path = NodePath("Panel/SubViewport") [sub_resource type="Gradient" id="Gradient_11p6r"] @@ -54,10 +54,10 @@ anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = -520.0 -offset_top = -908.889 -offset_right = -20.0 -offset_bottom = -20.0 +offset_left = -260.0 +offset_top = -150.0 +offset_right = -10.0 +offset_bottom = -10.0 grow_horizontal = 0 grow_vertical = 0 pivot_offset = Vector2(450, 300) @@ -76,6 +76,8 @@ current = true unique_name_in_owner = true ignore_rotation = false +[node name="UIDraw" type="CanvasLayer" parent="Panel/SubViewport"] + [node name="ViewportMarginContainer" type="MarginContainer" parent="Panel"] unique_name_in_owner = true clip_contents = true @@ -86,15 +88,15 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 -theme_override_constants/margin_left = 4 -theme_override_constants/margin_top = 4 -theme_override_constants/margin_right = 4 -theme_override_constants/margin_bottom = 4 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 [node name="TextureRect" type="TextureRect" parent="Panel/ViewportMarginContainer"] unique_name_in_owner = true layout_mode = 2 -texture = SubResource("ViewportTexture_hchdq") +texture = SubResource("ViewportTexture_k8axx") expand_mode = 1 [node name="Gradient" type="TextureRect" parent="Panel"] @@ -118,10 +120,10 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 -theme_override_constants/margin_left = 4 -theme_override_constants/margin_top = 4 -theme_override_constants/margin_right = 4 -theme_override_constants/margin_bottom = 4 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 [node name="OverlayContainer" type="Control" parent="Panel/OverlayMarginContainer"] unique_name_in_owner = true @@ -143,8 +145,8 @@ flat = true unique_name_in_owner = true visible = false layout_mode = 1 -offset_right = 60.0 -offset_bottom = 60.0 +offset_right = 30.0 +offset_bottom = 30.0 size_flags_horizontal = 0 size_flags_vertical = 0 mouse_default_cursor_shape = 12 @@ -160,9 +162,10 @@ layout_mode = 1 anchors_preset = 1 anchor_left = 1.0 anchor_right = 1.0 -offset_left = -60.0 -offset_bottom = 60.0 -pivot_offset = Vector2(60, 60) +offset_left = -30.0 +offset_bottom = 30.0 +grow_horizontal = 0 +pivot_offset = Vector2(30, 30) size_flags_horizontal = 8 size_flags_vertical = 0 mouse_default_cursor_shape = 11 @@ -178,9 +181,10 @@ layout_mode = 1 anchors_preset = 2 anchor_top = 1.0 anchor_bottom = 1.0 -offset_top = -60.0 -offset_right = 60.0 -pivot_offset = Vector2(0, 60) +offset_top = -30.0 +offset_right = 30.0 +grow_vertical = 0 +pivot_offset = Vector2(0, 30) size_flags_horizontal = 0 size_flags_vertical = 8 tooltip_text = "Always Show Preview" diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg b/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg deleted file mode 100644 index fe4dbf5..0000000 --- a/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import b/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import deleted file mode 100644 index 9584d3b..0000000 --- a/demo/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://btc01wc11tiid" -path="res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" -dest_files=["res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg b/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg deleted file mode 100644 index dd00953..0000000 --- a/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import b/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import deleted file mode 100644 index 4a1fa5d..0000000 --- a/demo/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://04l05jxuyt7k" -path="res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" -dest_files=["res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/demo/addons/anthonyec.camera_preview/Pin.svg b/demo/addons/anthonyec.camera_preview/Pin.svg deleted file mode 100644 index 8e5935c..0000000 --- a/demo/addons/anthonyec.camera_preview/Pin.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/demo/addons/anthonyec.camera_preview/Pin.svg.import b/demo/addons/anthonyec.camera_preview/Pin.svg.import deleted file mode 100644 index 27d274f..0000000 --- a/demo/addons/anthonyec.camera_preview/Pin.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://do6d60od41vmg" -path="res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/anthonyec.camera_preview/Pin.svg" -dest_files=["res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/demo/addons/anthonyec.camera_preview/plugin.cfg b/demo/addons/anthonyec.camera_preview/plugin.cfg deleted file mode 100644 index 4ad0d4c..0000000 --- a/demo/addons/anthonyec.camera_preview/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Little Camera Preview" -description="Shows a picture-in-picture preview of the selected 2D or 3D camera" -author="Anthony Cossins" -version="1.3" -script="plugin.gd" diff --git a/demo/addons/anthonyec.camera_preview/plugin.gd b/demo/addons/anthonyec.camera_preview/plugin.gd deleted file mode 100644 index 4e74dd8..0000000 --- a/demo/addons/anthonyec.camera_preview/plugin.gd +++ /dev/null @@ -1,87 +0,0 @@ -@tool -extends EditorPlugin - -const preview_scene = preload("res://addons/anthonyec.camera_preview/preview.tscn") - -var preview: CameraPreview -var current_main_screen_name: String - -func _enter_tree() -> void: - main_screen_changed.connect(_on_main_screen_changed) - EditorInterface.get_selection().selection_changed.connect(_on_editor_selection_changed) - - # Initialise preview panel and add to main screen. - preview = preview_scene.instantiate() as CameraPreview - preview.request_hide() - - var main_screen = EditorInterface.get_editor_main_screen() - main_screen.add_child(preview) - -func _exit_tree() -> void: - if preview: - preview.queue_free() - -func _ready() -> void: - # TODO: Currently there is no API to get the main screen name without - # listening to the `EditorPlugin.main_screen_changed` signal: - # https://github.com/godotengine/godot-proposals/issues/2081 - EditorInterface.set_main_screen_editor("Script") - EditorInterface.set_main_screen_editor("3D") - -func _on_main_screen_changed(screen_name: String) -> void: - current_main_screen_name = screen_name - - # TODO: Bit of a hack to prevent pinned staying between view changes on the same scene. - preview.unlink_camera() - _on_editor_selection_changed() - -func _on_editor_selection_changed() -> void: - if not is_main_screen_viewport(): - # This hides the preview "container" and not the preview itself, allowing - # any locked previews to remain visible once switching back to 3D tab. - preview.visible = false - return - - preview.visible = true - - var selected_nodes = EditorInterface.get_selection().get_selected_nodes() - - var selected_camera_3d: Camera3D = find_camera_3d_or_null(selected_nodes) - var selected_camera_2d: Camera2D = find_camera_2d_or_null(selected_nodes) - - if selected_camera_3d and current_main_screen_name == "3D": - preview.link_with_camera_3d(selected_camera_3d) - preview.request_show() - - elif selected_camera_2d and current_main_screen_name == "2D": - preview.link_with_camera_2d(selected_camera_2d) - preview.request_show() - - else: - preview.request_hide() - -func is_main_screen_viewport() -> bool: - return current_main_screen_name == "3D" or current_main_screen_name == "2D" - -func find_camera_3d_or_null(nodes: Array[Node]) -> Camera3D: - var camera: Camera3D - - for node in nodes: - if node is Camera3D: - camera = node as Camera3D - break - - return camera - -func find_camera_2d_or_null(nodes: Array[Node]) -> Camera2D: - var camera: Camera2D - - for node in nodes: - if node is Camera2D: - camera = node as Camera2D - break - - return camera - -func _on_selected_camera_3d_tree_exiting() -> void: - preview.unlink_camera() diff --git a/demo/addons/anthonyec.camera_preview/preview.gd b/demo/addons/anthonyec.camera_preview/preview.gd deleted file mode 100644 index 6bf17a8..0000000 --- a/demo/addons/anthonyec.camera_preview/preview.gd +++ /dev/null @@ -1,438 +0,0 @@ -@tool - -class_name CameraPreview -extends Control - -enum CameraType { - CAMERA_2D, - CAMERA_3D -} - -enum PinnedPosition { - LEFT, - RIGHT, -} - -enum InteractionState { - NONE, - RESIZE, - DRAG, - - # Animation is split into 2 seperate states so that the tween is only - # invoked once in the "start" state. - START_ANIMATE_INTO_PLACE, - ANIMATE_INTO_PLACE, -} - -const margin_3d: Vector2 = Vector2(10, 10) -const margin_2d: Vector2 = Vector2(20, 15) -const panel_margin: float = 2 -const min_panel_size: float = 250 - -@onready var panel: Panel = %Panel -@onready var placeholder: Panel = %Placeholder -@onready var preview_camera_3d: Camera3D = %Camera3D -@onready var preview_camera_2d: Camera2D = %Camera2D -@onready var sub_viewport: SubViewport = %SubViewport -@onready var sub_viewport_text_rect: TextureRect = %TextureRect -@onready var resize_left_handle: Button = %ResizeLeftHandle -@onready var resize_right_handle: Button = %ResizeRightHandle -@onready var lock_button: Button = %LockButton -@onready var gradient: TextureRect = %Gradient -@onready var viewport_margin_container: MarginContainer = %ViewportMarginContainer -@onready var overlay_margin_container: MarginContainer = %OverlayMarginContainer -@onready var overlay_container: Control = %OverlayContainer -@onready var ui_draw: CanvasLayer = $Panel/SubViewport/UIDraw - -var camera_type: CameraType = CameraType.CAMERA_3D -var pinned_position: PinnedPosition = PinnedPosition.RIGHT -var viewport_ratio: float = 1 -var editor_scale: float = EditorInterface.get_editor_scale() -var is_locked: bool -var show_controls: bool -var selected_camera_3d: Camera3D -var selected_camera_2d: Camera2D - -var state: InteractionState = InteractionState.NONE -var initial_mouse_position: Vector2 -var initial_panel_size: Vector2 -var initial_panel_position: Vector2 - -func _ready() -> void: - # Set initial width. - panel.size.x = min_panel_size * editor_scale - - # Setting texture to viewport in code instead of directly in the editor - # because otherwise an error "Path to node is invalid: Panel/SubViewport" - # on first load. This is harmless but doesn't look great. - # - # This is a known issue: - # https://github.com/godotengine/godot/issues/27790#issuecomment-499740220 - sub_viewport_text_rect.texture = sub_viewport.get_texture() - - # From what I can tell there's something wrong with how an editor theme - # scales when used within a plugin. It seems to ignore the screen scale. - # For instance, a 30x30px button will appear tiny on a retina display. - # - # Someone else had the issue with no luck: - # https://forum.godotengine.org/t/how-to-scale-plugin-controls-to-look-the-same-in-4k-as-1080p/36151 - # - # And seems Dialogic also scales buttons manually: - # https://github.com/dialogic-godot/dialogic/blob/master/addons/dialogic/Editor/Common/sidebar.gd#L25C6-L38 - # - # Maybe I don't know the correct way to do it, so for now the workaround is - # to set the correct size in code using screen scale. - var button_size = Vector2(30, 30) * editor_scale - var margin_size: float = panel_margin * editor_scale - - resize_left_handle.size = button_size - resize_left_handle.pivot_offset = Vector2(0, 0) * editor_scale - - resize_right_handle.size = button_size - resize_right_handle.pivot_offset = Vector2(30, 30) * editor_scale - - lock_button.size = button_size - lock_button.pivot_offset = Vector2(0, 30) * editor_scale - - viewport_margin_container.add_theme_constant_override("margin_left", margin_size) - viewport_margin_container.add_theme_constant_override("margin_top", margin_size) - viewport_margin_container.add_theme_constant_override("margin_right", margin_size) - viewport_margin_container.add_theme_constant_override("margin_bottom", margin_size) - - overlay_margin_container.add_theme_constant_override("margin_left", margin_size) - overlay_margin_container.add_theme_constant_override("margin_top", margin_size) - overlay_margin_container.add_theme_constant_override("margin_right", margin_size) - overlay_margin_container.add_theme_constant_override("margin_bottom", margin_size) - - # Parent node overlay size is not available on first ready, need to wait a - # frame for it to be drawn. - await get_tree().process_frame - - # Anchors are set in code because setting them in the editor UI doesn't take - # editor scale into account. - resize_left_handle.position = Vector2(0, 0) - resize_right_handle.set_anchors_preset(Control.PRESET_TOP_LEFT) - - resize_right_handle.position = Vector2(overlay_container.size.x - button_size.x, 0) - resize_right_handle.set_anchors_preset(Control.PRESET_TOP_RIGHT) - - lock_button.position = Vector2(0, overlay_container.size.y - button_size.y) - lock_button.set_anchors_preset(Control.PRESET_BOTTOM_LEFT) - -func _process(_delta: float) -> void: - if not visible: return - - match state: - InteractionState.NONE: - panel.size = get_clamped_size(panel.size) - panel.position = get_pinned_position(pinned_position) - - InteractionState.RESIZE: - var delta_mouse_position = initial_mouse_position - get_global_mouse_position() - var resized_size = panel.size - - if pinned_position == PinnedPosition.LEFT: - resized_size = initial_panel_size - delta_mouse_position - - if pinned_position == PinnedPosition.RIGHT: - resized_size = initial_panel_size + delta_mouse_position - - panel.size = get_clamped_size(resized_size) - panel.position = get_pinned_position(pinned_position) - - InteractionState.DRAG: - placeholder.size = panel.size - - var global_mouse_position = get_global_mouse_position() - var offset = initial_mouse_position - initial_panel_position - - panel.global_position = global_mouse_position - offset - - if global_mouse_position.x < global_position.x + size.x / 2: - pinned_position = PinnedPosition.LEFT - else: - pinned_position = PinnedPosition.RIGHT - - placeholder.position = get_pinned_position(pinned_position) - - InteractionState.START_ANIMATE_INTO_PLACE: - var final_position: Vector2 = get_pinned_position(pinned_position) - var tween = get_tree().create_tween() - - tween.set_ease(Tween.EASE_OUT) - tween.set_trans(Tween.TRANS_CUBIC) - tween.tween_property(panel, "position", final_position, 0.3) - - tween.finished.connect(func(): - panel.position = final_position - state = InteractionState.NONE - ) - - state = InteractionState.ANIMATE_INTO_PLACE - - # I couldn't get `mouse_entered` and `mouse_exited` events to work - # nicely, so I use rect method instead. Plus using this method it's easy to - # grow the hit area size. - var panel_hover_rect = Rect2(panel.global_position, panel.size) - panel_hover_rect = panel_hover_rect.grow(40) - - var mouse_position = get_global_mouse_position() - - show_controls = state != InteractionState.NONE or panel_hover_rect.has_point(mouse_position) - - # UI visibility. - resize_left_handle.visible = show_controls and pinned_position == PinnedPosition.RIGHT - resize_right_handle.visible = show_controls and pinned_position == PinnedPosition.LEFT - lock_button.visible = show_controls or is_locked - placeholder.visible = state == InteractionState.DRAG or state == InteractionState.ANIMATE_INTO_PLACE - gradient.visible = show_controls - - # Sync camera settings. - if camera_type == CameraType.CAMERA_3D and selected_camera_3d: - sub_viewport.size = panel.size - - # Sync position and rotation without using a `RemoteTransform` node - # because if you save a camera as a scene, the remote transform node will - # be stored within the scene. Also it's harder to keep the remote - # transform `remote_path` up-to-date with scene changes, which causes - # many errors. - preview_camera_3d.global_position = selected_camera_3d.global_position - preview_camera_3d.global_rotation = selected_camera_3d.global_rotation - - preview_camera_3d.fov = selected_camera_3d.fov - preview_camera_3d.projection = selected_camera_3d.projection - preview_camera_3d.size = selected_camera_3d.size - preview_camera_3d.cull_mask = selected_camera_3d.cull_mask - preview_camera_3d.keep_aspect = selected_camera_3d.keep_aspect - preview_camera_3d.near = selected_camera_3d.near - preview_camera_3d.far = selected_camera_3d.far - preview_camera_3d.h_offset = selected_camera_3d.h_offset - preview_camera_3d.v_offset = selected_camera_3d.v_offset - preview_camera_3d.attributes = selected_camera_3d.attributes - preview_camera_3d.environment = selected_camera_3d.environment - - if camera_type == CameraType.CAMERA_2D and selected_camera_2d: - var project_window_size = get_project_window_size() - var ratio = project_window_size.x / panel.size.x - - # TODO: Is there a better way to fix this? - # The camera border is visible sometimes due to pixel rounding. - # Subtract 1px from right and bottom to hide this. - var hide_camera_border_fix = Vector2(1, 1) - - sub_viewport.size = panel.size - sub_viewport.size_2d_override = (panel.size - hide_camera_border_fix) * ratio - sub_viewport.size_2d_override_stretch = true - - preview_camera_2d.global_position = selected_camera_2d.global_position - preview_camera_2d.global_rotation = selected_camera_2d.global_rotation - - preview_camera_2d.offset = selected_camera_2d.offset - preview_camera_2d.zoom = selected_camera_2d.zoom - preview_camera_2d.ignore_rotation = selected_camera_2d.ignore_rotation - preview_camera_2d.anchor_mode = selected_camera_2d.anchor_mode - preview_camera_2d.limit_left = selected_camera_2d.limit_left - preview_camera_2d.limit_right = selected_camera_2d.limit_right - preview_camera_2d.limit_top = selected_camera_2d.limit_top - preview_camera_2d.limit_bottom = selected_camera_2d.limit_bottom - -func link_with_camera_3d(camera_3d: Camera3D) -> void: - # TODO: Camera may not be ready since this method is called in `_enter_tree` - # in the plugin because of a workaround for: - # https://github.com/godotengine/godot-proposals/issues/2081 - if not preview_camera_3d: - return request_hide() - - var is_different_camera = camera_3d != preview_camera_3d - - # TODO: A bit messy. - if is_different_camera: - if preview_camera_3d.tree_exiting.is_connected(unlink_camera): - preview_camera_3d.tree_exiting.disconnect(unlink_camera) - - if not camera_3d.tree_exiting.is_connected(unlink_camera): - camera_3d.tree_exiting.connect(unlink_camera) - - sub_viewport.disable_3d = false - sub_viewport.world_3d = camera_3d.get_world_3d() - - selected_camera_3d = camera_3d - camera_type = CameraType.CAMERA_3D - redraw_ui(selected_camera_3d) - -func link_with_camera_2d(camera_2d: Camera2D) -> void: - if not preview_camera_2d: - return request_hide() - - var is_different_camera = camera_2d != preview_camera_2d - # TODO: A bit messy. - if is_different_camera: - - if preview_camera_2d.tree_exiting.is_connected(unlink_camera): - preview_camera_2d.tree_exiting.disconnect(unlink_camera) - - if not camera_2d.tree_exiting.is_connected(unlink_camera): - camera_2d.tree_exiting.connect(unlink_camera) - - - sub_viewport.disable_3d = true - sub_viewport.world_2d = camera_2d.get_world_2d() - selected_camera_2d = camera_2d - camera_type = CameraType.CAMERA_2D - redraw_ui(selected_camera_2d) - -func redraw_ui(cameraSelected) -> void: - var obj = get_main_scene(cameraSelected) - if obj: - if(ui_draw.get_children()): - for child in ui_draw.get_children(): - ui_draw.remove_child(child) - child.queue_free() - for child in findByType(obj,CanvasLayer): - var newObj = child.duplicate() - ui_draw.add_child(newObj) - for child in findByType(obj,Control): - var newObj = child.duplicate() - ui_draw.add_child(newObj) - -func unlink_camera() -> void: - if selected_camera_3d: - selected_camera_3d = null - - if selected_camera_2d: - selected_camera_2d = null - - is_locked = false - lock_button.button_pressed = false - -func request_hide() -> void: - if is_locked: return - visible = false - -func request_show() -> void: - visible = true - -func get_pinned_position(pinned_position: PinnedPosition) -> Vector2: - var margin: Vector2 = margin_3d * editor_scale - - if camera_type == CameraType.CAMERA_2D: - margin = margin_2d * editor_scale - - match pinned_position: - PinnedPosition.LEFT: - return Vector2.ZERO - Vector2(0, panel.size.y) - Vector2(-margin.x, margin.y) - PinnedPosition.RIGHT: - return size - panel.size - margin - _: - assert(false, "Unknown pinned position %s" % str(pinned_position)) - - return Vector2.ZERO - -func get_clamped_size(desired_size: Vector2) -> Vector2: - var viewport_ratio = get_project_window_ratio() - var editor_viewport_size = get_editor_viewport_size() - - var max_bounds = Vector2( - editor_viewport_size.x * 0.6, - editor_viewport_size.y * 0.8 - ) - - var clamped_size = desired_size - - # Apply aspect ratio. - clamped_size = Vector2(clamped_size.x, clamped_size.x * viewport_ratio) - - # Clamp the max size while respecting the aspect ratio. - if clamped_size.y >= max_bounds.y: - clamped_size.x = max_bounds.y / viewport_ratio - clamped_size.y = max_bounds.y - - if clamped_size.x >= max_bounds.x: - clamped_size.x = max_bounds.x - clamped_size.y = max_bounds.x * viewport_ratio - - # Clamp the min size based on if it's portrait or landscape. Portrait min - # size should be based on it's height. Landscape min size is based on it's - # width instead. Applying min width to a portrait size would make it too big. - var is_portrait = viewport_ratio > 1 - - if is_portrait and clamped_size.y <= min_panel_size * editor_scale: - clamped_size.x = min_panel_size / viewport_ratio - clamped_size.y = min_panel_size - clamped_size = clamped_size * editor_scale - - if not is_portrait and clamped_size.x <= min_panel_size * editor_scale: - clamped_size.x = min_panel_size - clamped_size.y = min_panel_size * viewport_ratio - clamped_size = clamped_size * editor_scale - - # Round down to avoid sub-pixel artifacts, mainly seen around the margins. - return clamped_size.floor() - -func get_project_window_size() -> Vector2: - var window_width = float(ProjectSettings.get_setting("display/window/size/viewport_width")) - var window_height = float(ProjectSettings.get_setting("display/window/size/viewport_height")) - - return Vector2(window_width, window_height) - -func get_project_window_ratio() -> float: - var project_window_size = get_project_window_size() - - return project_window_size.y / project_window_size.x - -func get_editor_viewport_size() -> Vector2: - var fallback_size = EditorInterface.get_editor_main_screen().size - - # There isn't an API for getting the viewport node. Instead it has to be - # found by checking the parent's parent of the subviewport and find - # the correct node based on name and class. - var editor_sub_viewport_3d = EditorInterface.get_editor_viewport_3d(0) - var editor_viewport_container = editor_sub_viewport_3d.get_parent().get_parent().get_parent() - - # Early return incase editor tree structure has changed. - if editor_viewport_container.get_class() != "Node3DEditorViewportContainer": - return fallback_size - - return editor_viewport_container.size - -func _on_resize_handle_button_down() -> void: - if state != InteractionState.NONE: return - - state = InteractionState.RESIZE - initial_mouse_position = get_global_mouse_position() - initial_panel_size = panel.size - -func _on_resize_handle_button_up() -> void: - state = InteractionState.NONE - -func _on_drag_handle_button_down() -> void: - if state != InteractionState.NONE: return - - state = InteractionState.DRAG - initial_mouse_position = get_global_mouse_position() - initial_panel_position = panel.global_position - -func _on_drag_handle_button_up() -> void: - if state != InteractionState.DRAG: return - - state = InteractionState.START_ANIMATE_INTO_PLACE - -func _on_lock_button_pressed() -> void: - is_locked = !is_locked - - -var listOfAllNodesInTree = [] -func findByType(parent, type): - listOfAllNodesInTree.clear() - for child in parent.get_children(): - if is_instance_of(child, type): - listOfAllNodesInTree.append(child) - return listOfAllNodesInTree - -func get_main_scene(obj): - if is_instance_of( obj ,SubViewport): - return null - if is_instance_of(obj.get_parent(),SubViewport): - return obj - else: - return get_main_scene(obj.get_parent()) diff --git a/demo/addons/anthonyec.camera_preview/preview.tscn b/demo/addons/anthonyec.camera_preview/preview.tscn deleted file mode 100644 index 4c8754c..0000000 --- a/demo/addons/anthonyec.camera_preview/preview.tscn +++ /dev/null @@ -1,204 +0,0 @@ -[gd_scene load_steps=8 format=3 uid="uid://xybmfvufjuv"] - -[ext_resource type="Script" path="res://addons/anthonyec.camera_preview/preview.gd" id="1_6b32r"] -[ext_resource type="Texture2D" uid="uid://do6d60od41vmg" path="res://addons/anthonyec.camera_preview/Pin.svg" id="2_p0pa8"] -[ext_resource type="Texture2D" uid="uid://btc01wc11tiid" path="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" id="2_t64ej"] -[ext_resource type="Texture2D" uid="uid://04l05jxuyt7k" path="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" id="3_6yuab"] - -[sub_resource type="ViewportTexture" id="ViewportTexture_k8axx"] -viewport_path = NodePath("Panel/SubViewport") - -[sub_resource type="Gradient" id="Gradient_11p6r"] -offsets = PackedFloat32Array(0, 0.3, 0.6, 1) -colors = PackedColorArray(0, 0, 0, 0.235294, 0, 0, 0, 0.0784314, 0, 0, 0, 0.0784314, 0, 0, 0, 0.235294) - -[sub_resource type="GradientTexture2D" id="GradientTexture2D_4dkve"] -gradient = SubResource("Gradient_11p6r") -width = 256 -height = 256 -fill_to = Vector2(2.08165e-12, 1) - -[node name="Preview" type="Control"] -z_index = 999 -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_6b32r") - -[node name="Placeholder" type="Panel" parent="."] -unique_name_in_owner = true -visible = false -modulate = Color(1, 1, 1, 0.705882) -layout_mode = 1 -anchors_preset = 3 -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -40.0 -offset_top = -40.0 -offset_right = 410.0 -offset_bottom = 410.0 -grow_horizontal = 0 -grow_vertical = 0 - -[node name="Panel" type="Panel" parent="."] -unique_name_in_owner = true -clip_contents = true -layout_mode = 1 -anchors_preset = 3 -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -260.0 -offset_top = -150.0 -offset_right = -10.0 -offset_bottom = -10.0 -grow_horizontal = 0 -grow_vertical = 0 -pivot_offset = Vector2(450, 300) - -[node name="SubViewport" type="SubViewport" parent="Panel"] -unique_name_in_owner = true -handle_input_locally = false -gui_disable_input = true -size_2d_override_stretch = true - -[node name="Camera3D" type="Camera3D" parent="Panel/SubViewport"] -unique_name_in_owner = true -current = true - -[node name="Camera2D" type="Camera2D" parent="Panel/SubViewport"] -unique_name_in_owner = true -ignore_rotation = false - -[node name="UIDraw" type="CanvasLayer" parent="Panel/SubViewport"] - -[node name="ViewportMarginContainer" type="MarginContainer" parent="Panel"] -unique_name_in_owner = true -clip_contents = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -theme_override_constants/margin_left = 2 -theme_override_constants/margin_top = 2 -theme_override_constants/margin_right = 2 -theme_override_constants/margin_bottom = 2 - -[node name="TextureRect" type="TextureRect" parent="Panel/ViewportMarginContainer"] -unique_name_in_owner = true -layout_mode = 2 -texture = SubResource("ViewportTexture_k8axx") -expand_mode = 1 - -[node name="Gradient" type="TextureRect" parent="Panel"] -unique_name_in_owner = true -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -texture = SubResource("GradientTexture2D_4dkve") - -[node name="OverlayMarginContainer" type="MarginContainer" parent="Panel"] -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 -mouse_filter = 2 -theme_override_constants/margin_left = 2 -theme_override_constants/margin_top = 2 -theme_override_constants/margin_right = 2 -theme_override_constants/margin_bottom = 2 - -[node name="OverlayContainer" type="Control" parent="Panel/OverlayMarginContainer"] -unique_name_in_owner = true -clip_contents = true -layout_mode = 2 -mouse_filter = 2 - -[node name="DragHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -focus_mode = 0 -flat = true - -[node name="ResizeLeftHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 1 -offset_right = 30.0 -offset_bottom = 30.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -mouse_default_cursor_shape = 12 -icon = ExtResource("2_t64ej") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="ResizeRightHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -30.0 -offset_bottom = 30.0 -grow_horizontal = 0 -pivot_offset = Vector2(30, 30) -size_flags_horizontal = 8 -size_flags_vertical = 0 -mouse_default_cursor_shape = 11 -icon = ExtResource("3_6yuab") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="LockButton" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 1 -anchors_preset = 2 -anchor_top = 1.0 -anchor_bottom = 1.0 -offset_top = -30.0 -offset_right = 30.0 -grow_vertical = 0 -pivot_offset = Vector2(0, 30) -size_flags_horizontal = 0 -size_flags_vertical = 8 -tooltip_text = "Always Show Preview" -toggle_mode = true -icon = ExtResource("2_p0pa8") -flat = true -icon_alignment = 1 -expand_icon = true - -[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_down"] -[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_up"] -[connection signal="renamed" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_renamed"] -[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_down"] -[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_up"] -[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_down"] -[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_up"] -[connection signal="pressed" from="Panel/OverlayMarginContainer/OverlayContainer/LockButton" to="." method="_on_lock_button_pressed"] diff --git a/demo/main_2d.tscn b/demo/main_2d.tscn index 95c3cd7..1e723e1 100644 --- a/demo/main_2d.tscn +++ b/demo/main_2d.tscn @@ -15,7 +15,7 @@ scale = Vector2(0.124943, 0.124943) texture = ExtResource("2_fk71s") [node name="Camera2D2" type="Camera2D" parent="."] -position = Vector2(248, 146) +position = Vector2(119, 166) rotation = -0.268458 ignore_rotation = false zoom = Vector2(2, 2) From c71c70a3b9a809e44963fa7ba6738cab1c1cd1aa Mon Sep 17 00:00:00 2001 From: eitanbariboa Date: Fri, 26 Apr 2024 14:43:05 +0300 Subject: [PATCH 5/5] Fixed not redrawing after UI changed --- addons/anthonyec.camera_preview/preview.gd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/anthonyec.camera_preview/preview.gd b/addons/anthonyec.camera_preview/preview.gd index 6bf17a8..d3cc6cd 100644 --- a/addons/anthonyec.camera_preview/preview.gd +++ b/addons/anthonyec.camera_preview/preview.gd @@ -210,6 +210,7 @@ func _process(_delta: float) -> void: preview_camera_3d.v_offset = selected_camera_3d.v_offset preview_camera_3d.attributes = selected_camera_3d.attributes preview_camera_3d.environment = selected_camera_3d.environment + redraw_ui(selected_camera_3d) if camera_type == CameraType.CAMERA_2D and selected_camera_2d: var project_window_size = get_project_window_size() @@ -235,6 +236,7 @@ func _process(_delta: float) -> void: preview_camera_2d.limit_right = selected_camera_2d.limit_right preview_camera_2d.limit_top = selected_camera_2d.limit_top preview_camera_2d.limit_bottom = selected_camera_2d.limit_bottom + redraw_ui(selected_camera_2d) func link_with_camera_3d(camera_3d: Camera3D) -> void: # TODO: Camera may not be ready since this method is called in `_enter_tree`