From 15d7905b5753657b95f698ce5012ef7da86a9db7 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 19:16:13 +0200 Subject: [PATCH 01/23] Create light Sentry theme --- .../sentry/user_feedback/sentry_theme.tres | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 project/addons/sentry/user_feedback/sentry_theme.tres diff --git a/project/addons/sentry/user_feedback/sentry_theme.tres b/project/addons/sentry/user_feedback/sentry_theme.tres new file mode 100644 index 00000000..0594f92a --- /dev/null +++ b/project/addons/sentry/user_feedback/sentry_theme.tres @@ -0,0 +1,281 @@ +[gd_resource type="Theme" load_steps=15 format=3 uid="uid://bw0anqwp7xj8t"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hffoe"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.954, 0.954, 0.954, 1) +draw_center = false +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.39607844, 0.34901962, 0.77254903, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e23mj"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.49787024, 0.43896988, 0.9101726, 1) +border_color = Color(0.98099995, 0.98099995, 0.98099995, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g6gn8"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.42352942, 0.37254903, 0.78039217, 1) +border_color = Color(0.98099995, 0.98099995, 0.98099995, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_63sqy"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.42352942, 0.37254903, 0.78039217, 1) +border_color = Color(0.98099995, 0.98099995, 0.98099995, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5o1s5"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.90999997, 0.90999997, 0.90999997, 1) +border_color = Color(0.93, 0.93, 0.93, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4sisp"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.954, 0.954, 0.954, 1) +draw_center = false +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.39607844, 0.34901962, 0.77254903, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gix2e"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.9607843, 0.9529412, 0.96862745, 1) +border_color = Color(0, 0, 0, 0.05) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7qvkq"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.96862745, 0.9647059, 0.9764706, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.98099995, 0.98099995, 0.98099995, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6xjsq"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.83475, 0.83475, 0.83475, 1) +border_color = Color(0.98099995, 0.98099995, 0.98099995, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_sfof1"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pmhvd"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.96862745, 0.9647059, 0.9764706, 1) +draw_center = false +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.42352942, 0.37254903, 0.78039217, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ij1qw"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.96862745, 0.9647059, 0.9764706, 1) +border_width_bottom = 4 +border_color = Color(0.8784314, 0.8627451, 0.8980392, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3wai3"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(0.9607843, 0.9529412, 0.96862745, 1) +border_width_bottom = 4 +border_color = Color(0.9411765, 0.9254902, 0.9529412, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qfyhw"] +content_margin_left = 12.0 +content_margin_top = 10.0 +content_margin_right = 12.0 +content_margin_bottom = 10.0 +bg_color = Color(1, 1, 1, 1) +border_color = Color(0.98099995, 0.98099995, 0.98099995, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +corner_detail = 5 +anti_aliasing = false + +[resource] +default_font_size = 24 +ActionButton/base_type = &"Button" +ActionButton/colors/font_color = Color(1, 1, 1, 1) +ActionButton/colors/font_focus_color = Color(1, 1, 1, 1) +ActionButton/colors/font_hover_color = Color(1, 1, 1, 1) +ActionButton/colors/font_pressed_color = Color(0.9607843, 0.9529412, 0.96862745, 1) +ActionButton/styles/focus = SubResource("StyleBoxFlat_hffoe") +ActionButton/styles/hover = SubResource("StyleBoxFlat_e23mj") +ActionButton/styles/normal = SubResource("StyleBoxFlat_g6gn8") +ActionButton/styles/pressed = SubResource("StyleBoxFlat_63sqy") +Button/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +Button/colors/font_disabled_color = Color(0.44313726, 0.3882353, 0.49411765, 1) +Button/colors/font_focus_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +Button/colors/font_hover_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +Button/colors/font_hover_pressed_color = Color(0.39607844, 0.34901962, 0.77254903, 1) +Button/colors/font_outline_color = Color(0, 0, 0, 0) +Button/colors/font_pressed_color = Color(0.39607844, 0.34901962, 0.77254903, 1) +Button/colors/icon_disabled_color = Color(1, 1, 1, 0.4) +Button/colors/icon_focus_color = Color(1.45, 1.45, 1.45, 1) +Button/colors/icon_hover_color = Color(1.45, 1.45, 1.45, 1) +Button/colors/icon_hover_pressed_color = Color(0.63, 1.75, 3.5, 1) +Button/colors/icon_normal_color = Color(1, 1, 1, 1) +Button/colors/icon_pressed_color = Color(0.63, 1.75, 3.5, 1) +Button/constants/align_to_largest_stylebox = 1 +Button/constants/h_separation = 8 +Button/constants/outline_size = 0 +Button/styles/disabled = SubResource("StyleBoxFlat_5o1s5") +Button/styles/focus = SubResource("StyleBoxFlat_4sisp") +Button/styles/hover = SubResource("StyleBoxFlat_gix2e") +Button/styles/normal = SubResource("StyleBoxFlat_7qvkq") +Button/styles/pressed = SubResource("StyleBoxFlat_6xjsq") +HeaderMedium/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +HeaderMedium/font_sizes/font_size = 30 +Label/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +Label/colors/font_outline_color = Color(0, 0, 0, 0) +Label/colors/font_shadow_color = Color(0, 0, 0, 0) +Label/constants/line_spacing = 6 +Label/constants/outline_size = 0 +Label/constants/shadow_offset_x = 2 +Label/constants/shadow_offset_y = 2 +Label/constants/shadow_outline_size = 2 +Label/styles/focus = SubResource("StyleBoxFlat_4sisp") +Label/styles/normal = SubResource("StyleBoxEmpty_sfof1") +LineEdit/colors/caret_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +LineEdit/colors/clear_button_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +LineEdit/colors/clear_button_color_pressed = Color(0.39607844, 0.34901962, 0.77254903, 1) +LineEdit/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +LineEdit/colors/font_outline_color = Color(0, 0, 0, 0) +LineEdit/colors/font_placeholder_color = Color(0.44313726, 0.3882353, 0.49411765, 1) +LineEdit/colors/font_selected_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +LineEdit/colors/font_uneditable_color = Color(0, 0, 0, 0.65) +LineEdit/colors/selection_color = Color(0.39607844, 0.34901962, 0.77254903, 0.4) +LineEdit/constants/caret_width = 1 +LineEdit/constants/minimum_character_width = 4 +LineEdit/constants/outline_size = 0 +LineEdit/styles/focus = SubResource("StyleBoxFlat_pmhvd") +LineEdit/styles/normal = SubResource("StyleBoxFlat_ij1qw") +LineEdit/styles/read_only = SubResource("StyleBoxFlat_3wai3") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_qfyhw") +TextEdit/colors/background_color = Color(0, 0, 0, 0) +TextEdit/colors/caret_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +TextEdit/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +TextEdit/colors/font_outline_color = Color(0, 0, 0, 0) +TextEdit/colors/font_placeholder_color = Color(0.44313726, 0.3882353, 0.49411765, 1) +TextEdit/colors/font_readonly_color = Color(0, 0, 0, 0.65) +TextEdit/colors/font_selected_color = Color(0.24313726, 0.20392157, 0.27450982, 1) +TextEdit/colors/selection_color = Color(0.39607844, 0.34901962, 0.77254903, 0.4) +TextEdit/constants/caret_width = 1 +TextEdit/constants/line_spacing = 8 +TextEdit/constants/outline_size = 0 +TextEdit/styles/focus = SubResource("StyleBoxFlat_pmhvd") +TextEdit/styles/normal = SubResource("StyleBoxFlat_ij1qw") +TextEdit/styles/read_only = SubResource("StyleBoxFlat_3wai3") From b9c9db79f61855ef325fdb61c6230590e481a28d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 19:16:43 +0200 Subject: [PATCH 02/23] Add logo --- project/addons/sentry/user_feedback/logo.svg | 1 + .../sentry/user_feedback/logo.svg.import | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 project/addons/sentry/user_feedback/logo.svg create mode 100644 project/addons/sentry/user_feedback/logo.svg.import diff --git a/project/addons/sentry/user_feedback/logo.svg b/project/addons/sentry/user_feedback/logo.svg new file mode 100644 index 00000000..8234a3ab --- /dev/null +++ b/project/addons/sentry/user_feedback/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/project/addons/sentry/user_feedback/logo.svg.import b/project/addons/sentry/user_feedback/logo.svg.import new file mode 100644 index 00000000..46238288 --- /dev/null +++ b/project/addons/sentry/user_feedback/logo.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://d0o3nt85ac67i" +path="res://.godot/imported/logo.svg-25820d7157ee760c1db8b8ba461e2e2f.dpitex" + +[deps] + +source_file="res://addons/sentry/user_feedback/logo.svg" +dest_files=["res://.godot/imported/logo.svg-25820d7157ee760c1db8b8ba461e2e2f.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true From 8bfa75a42fca43a60884731d2bbeee8ebc67169b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 19:17:11 +0200 Subject: [PATCH 03/23] Update .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a5a8cdc8..6b023087 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # Project-related -project/addons/ +project/addons/gdUnit4 +project/addons/sentry/* +!project/addons/sentry/user_feedback/ project/export_presets.cfg project/.vscode/ project/reports/ From 3238b80fae9f90c9d126ee4f50af58c515aaaaf4 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 19:21:17 +0200 Subject: [PATCH 04/23] Add reference User Feedback form --- .../user_feedback/user_feedback_form.gd | 60 ++++++++++ .../user_feedback/user_feedback_form.gd.uid | 1 + .../user_feedback/user_feedback_form.tscn | 110 ++++++++++++++++++ .../sentry/user_feedback/user_feedback_gui.gd | 9 ++ .../user_feedback/user_feedback_gui.gd.uid | 1 + .../user_feedback/user_feedback_gui.tscn | 48 ++++++++ project/views/capture_events.gd | 16 ++- project/views/capture_events.tscn | 21 +++- 8 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 project/addons/sentry/user_feedback/user_feedback_form.gd create mode 100644 project/addons/sentry/user_feedback/user_feedback_form.gd.uid create mode 100644 project/addons/sentry/user_feedback/user_feedback_form.tscn create mode 100644 project/addons/sentry/user_feedback/user_feedback_gui.gd create mode 100644 project/addons/sentry/user_feedback/user_feedback_gui.gd.uid create mode 100644 project/addons/sentry/user_feedback/user_feedback_gui.tscn diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd new file mode 100644 index 00000000..b28c0306 --- /dev/null +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -0,0 +1,60 @@ +extends PanelContainer +## User Feedback Form +## +## This is a reference user feedback form implementation. +## +## Tip: Copy folder with this feedback form into your project and customize it to your needs. + + +signal feedback_submitted(feedback: SentryFeedback) +signal feedback_cancelled() + +@export var show_logo: bool = true: + set(value): + show_logo = value + _update_logo() + +@onready var message_edit: TextEdit = %MessageEdit +@onready var name_edit: LineEdit = %NameEdit +@onready var email_edit: LineEdit = %EmailEdit +@onready var submit_button: Button = %SubmitButton + + +func _ready() -> void: + _update_logo() + + +func _update_logo() -> void: + %Logo.visible = show_logo + + +func _on_submit_button_pressed() -> void: + var feedback := SentryFeedback.new() + feedback.message = message_edit.text + feedback.name = name_edit.text + feedback.contact_email = email_edit.text + + SentrySDK.capture_feedback(feedback) + + feedback_submitted.emit(feedback) + + # Reset feedback message + message_edit.text = "" + + +func _on_message_edit_text_changed() -> void: + var message: String = message_edit.text + submit_button.disabled = _count_words(message) < 2 + + +func _count_words(text: String) -> int: + var words: PackedStringArray = text.strip_edges().split(" ", false) + var clean_words: PackedStringArray = [] + for word in words: + if word.strip_edges() != "": + clean_words.append(word) + return clean_words.size() + + +func _on_cancel_button_pressed() -> void: + feedback_cancelled.emit() diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd.uid b/project/addons/sentry/user_feedback/user_feedback_form.gd.uid new file mode 100644 index 00000000..81c677c9 --- /dev/null +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd.uid @@ -0,0 +1 @@ +uid://dmtkflr7lmh5h diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn new file mode 100644 index 00000000..273d0d5a --- /dev/null +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -0,0 +1,110 @@ +[gd_scene load_steps=3 format=3 uid="uid://bdn5fqm81rhy6"] + +[ext_resource type="Script" uid="uid://dmtkflr7lmh5h" path="res://addons/sentry/user_feedback/user_feedback_form.gd" id="1_0vncv"] +[ext_resource type="Texture2D" uid="uid://d0o3nt85ac67i" path="res://addons/sentry/user_feedback/logo.svg" id="2_useou"] + +[node name="UserFeedbackForm" type="PanelContainer"] +offset_right = 387.0 +offset_bottom = 462.0 +script = ExtResource("1_0vncv") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 16 +theme_override_constants/margin_bottom = 16 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="Title" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer/Title"] +layout_mode = 2 +theme_type_variation = &"HeaderMedium" +text = "Give Feedback" + +[node name="Logo" type="TextureRect" parent="MarginContainer/VBoxContainer/Title"] +unique_name_in_owner = true +texture_filter = 3 +layout_mode = 2 +size_flags_horizontal = 10 +texture = ExtResource("2_useou") +expand_mode = 3 + +[node name="Spacer0" type="Control" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 12) +layout_mode = 2 + +[node name="MessageLabel" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Message" + +[node name="MessageEdit" type="TextEdit" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 100) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +placeholder_text = "Tell us about your issue" +wrap_mode = 1 +tab_input_mode = false + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Spacer1" type="Control" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 12) +layout_mode = 2 + +[node name="NameLabel" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Name" + +[node name="NameEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Your name (optional)" + +[node name="Spacer2" type="Control" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 12) +layout_mode = 2 + +[node name="EmailLabel" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Email" + +[node name="EmailEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Contact email (optional)" + +[node name="Spacer3" type="Control" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 16) +layout_mode = 2 + +[node name="Actions" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="CancelButton" type="Button" parent="MarginContainer/VBoxContainer/Actions"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +size_flags_horizontal = 6 +text = "Cancel" + +[node name="SubmitButton" type="Button" parent="MarginContainer/VBoxContainer/Actions"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +size_flags_horizontal = 6 +disabled = true +text = "Submit" + +[connection signal="text_changed" from="MarginContainer/VBoxContainer/MessageEdit" to="." method="_on_message_edit_text_changed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/Actions/CancelButton" to="." method="_on_cancel_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/Actions/SubmitButton" to="." method="_on_submit_button_pressed"] diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd new file mode 100644 index 00000000..8931db2c --- /dev/null +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -0,0 +1,9 @@ +extends Control + + +func _on_user_feedback_form_feedback_submitted(feedback: SentryFeedback) -> void: + hide() + + +func _on_user_feedback_form_feedback_cancelled() -> void: + hide() diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd.uid b/project/addons/sentry/user_feedback/user_feedback_gui.gd.uid new file mode 100644 index 00000000..3bc2fde9 --- /dev/null +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd.uid @@ -0,0 +1 @@ +uid://b7c0cq2oneiwv diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.tscn b/project/addons/sentry/user_feedback/user_feedback_gui.tscn new file mode 100644 index 00000000..0dbd1661 --- /dev/null +++ b/project/addons/sentry/user_feedback/user_feedback_gui.tscn @@ -0,0 +1,48 @@ +[gd_scene load_steps=4 format=3 uid="uid://d3cll30toja8f"] + +[ext_resource type="Script" uid="uid://b7c0cq2oneiwv" path="res://addons/sentry/user_feedback/user_feedback_gui.gd" id="1_jugy2"] +[ext_resource type="PackedScene" uid="uid://bdn5fqm81rhy6" path="res://addons/sentry/user_feedback/user_feedback_form.tscn" id="1_t8jgq"] +[ext_resource type="Theme" uid="uid://bw0anqwp7xj8t" path="res://addons/sentry/user_feedback/sentry_theme.tres" id="1_u3uht"] + +[node name="UserFeedbackGUI" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_u3uht") +script = ExtResource("1_jugy2") + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.22745098) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="UserFeedbackForm" parent="VBoxContainer" instance=ExtResource("1_t8jgq")] +custom_minimum_size = Vector2(600, 800) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 6 +show_logo = null + +[node name="SubmitButton" parent="VBoxContainer/UserFeedbackForm/MarginContainer/VBoxContainer/Actions" index="1"] +theme_type_variation = &"ActionButton" +disabled = false + +[connection signal="feedback_cancelled" from="VBoxContainer/UserFeedbackForm" to="." method="_on_user_feedback_form_feedback_cancelled"] +[connection signal="feedback_submitted" from="VBoxContainer/UserFeedbackForm" to="." method="_on_user_feedback_form_feedback_submitted"] + +[editable path="VBoxContainer/UserFeedbackForm"] diff --git a/project/views/capture_events.gd b/project/views/capture_events.gd index cc7cefef..c8517dc9 100644 --- a/project/views/capture_events.gd +++ b/project/views/capture_events.gd @@ -9,13 +9,22 @@ extends VBoxContainer var _event_level: SentrySDK.Level +var _user_feedback_gui: Control func _ready() -> void: - level_choice.get_popup().id_pressed.connect(_on_level_choice_id_pressed) + _init_user_feedback_popup() _init_level_choice_popup() _init_user_info() +## Initialize User Feedback popup +func _init_user_feedback_popup() -> void: + _user_feedback_gui = load("res://addons/sentry/user_feedback/user_feedback_gui.tscn").instantiate() + _user_feedback_gui.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + get_owner().add_child.call_deferred(_user_feedback_gui) + _user_feedback_gui.hide() + + func _init_level_choice_popup() -> void: var popup: PopupMenu = level_choice.get_popup() popup.add_item("DEBUG", SentrySDK.LEVEL_DEBUG) @@ -24,6 +33,7 @@ func _init_level_choice_popup() -> void: popup.add_item("ERROR", SentrySDK.LEVEL_ERROR) popup.add_item("FATAL", SentrySDK.LEVEL_FATAL) + popup.id_pressed.connect(_on_level_choice_id_pressed) _on_level_choice_id_pressed(SentrySDK.LEVEL_INFO) @@ -84,3 +94,7 @@ func _on_gen_script_error_pressed() -> void: func _on_gen_native_error_pressed() -> void: DemoOutput.print_info("Generating native Godot error (in C++ unit)...") load("res://file_does_not_exist") + + +func _on_user_feedback_button_pressed() -> void: + _user_feedback_gui.show() diff --git a/project/views/capture_events.tscn b/project/views/capture_events.tscn index d5283440..67aea3ab 100644 --- a/project/views/capture_events.tscn +++ b/project/views/capture_events.tscn @@ -83,6 +83,18 @@ text = "CAPTURE EVENTS" horizontal_alignment = 1 vertical_alignment = 2 +[node name="UserFeedback" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Label" type="Label" parent="UserFeedback"] +layout_mode = 2 +text = "User feedback:" + +[node name="UserFeedbackButton" type="Button" parent="UserFeedback"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Show User Feedback Form" + [node name="MessageEvent" type="HBoxContainer" parent="."] layout_mode = 2 @@ -107,14 +119,14 @@ flat = false layout_mode = 2 text = "Capture Event" -[node name="Crash2" type="HBoxContainer" parent="."] +[node name="Crash" type="HBoxContainer" parent="."] layout_mode = 2 -[node name="Label" type="Label" parent="Crash2"] +[node name="Label" type="Label" parent="Crash"] layout_mode = 2 text = "Crash program: " -[node name="CrashButton" type="Button" parent="Crash2"] +[node name="CrashButton" type="Button" parent="Crash"] layout_mode = 2 size_flags_horizontal = 3 text = "CRASH!" @@ -122,5 +134,6 @@ text = "CRASH!" [connection signal="pressed" from="SetUserButton" to="." method="_on_set_user_button_pressed"] [connection signal="pressed" from="GenerateErrors/GenScriptError" to="." method="_on_gen_script_error_pressed"] [connection signal="pressed" from="GenerateErrors/GenNativeError" to="." method="_on_gen_native_error_pressed"] +[connection signal="pressed" from="UserFeedback/UserFeedbackButton" to="." method="_on_user_feedback_button_pressed"] [connection signal="pressed" from="MessageEvent/CaptureButton" to="." method="_on_capture_button_pressed"] -[connection signal="pressed" from="Crash2/CrashButton" to="." method="_on_crash_button_pressed"] +[connection signal="pressed" from="Crash/CrashButton" to="." method="_on_crash_button_pressed"] From 1e7f7c729dd7f92a72bd69f534344f035d034e13 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 20:26:28 +0200 Subject: [PATCH 05/23] Refine, and add character limit --- .../user_feedback/user_feedback_form.gd | 49 ++++++++++++++----- .../user_feedback/user_feedback_form.tscn | 15 ++++-- .../sentry/user_feedback/user_feedback_gui.gd | 17 +++++++ .../user_feedback/user_feedback_gui.tscn | 1 + project/views/capture_events.gd | 7 +-- 5 files changed, 70 insertions(+), 19 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index b28c0306..6d09b9b8 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -1,23 +1,41 @@ extends PanelContainer ## User Feedback Form ## -## This is a reference user feedback form implementation. +## A customizable user feedback panel for use with Sentry SDK. +## The feedback form automatically handles message validation, character limits, and +## integrates with SentrySDK for feedback submission. ## -## Tip: Copy folder with this feedback form into your project and customize it to your needs. +## Usage: +## 1. Copy the folder with this feedback form into your project to customize it. +## 2. Customize the form as needed, you can tweak the theme file to change the looks. +## 3. Integrate the panel into your existing UI hierarchy. +## +## Files: +## - user_feedback_form.tscn: Feedback form for integrating into existing UI. +## - user_feedback_gui.tscn: Ready to use integration example. +## - sentry_theme.tres: Reference UI theme file. +## Emitted when feedback is successfully submitted after the user clicks the "Submit" button. signal feedback_submitted(feedback: SentryFeedback) +## Emitted when feedback is cancelled after the user clicks the "Cancel" button. signal feedback_cancelled() +## Whether to display Sentry logo in the top right corner. @export var show_logo: bool = true: set(value): show_logo = value _update_logo() -@onready var message_edit: TextEdit = %MessageEdit -@onready var name_edit: LineEdit = %NameEdit -@onready var email_edit: LineEdit = %EmailEdit -@onready var submit_button: Button = %SubmitButton +## Minimum number of words required in the feedback message before the feedback can be submitted. +@export var minimum_words: int = 2 + + +@onready var _message_edit: TextEdit = %MessageEdit +@onready var _name_edit: LineEdit = %NameEdit +@onready var _email_edit: LineEdit = %EmailEdit +@onready var _submit_button: Button = %SubmitButton +@onready var _character_counter: Label = %CharacterCounter func _ready() -> void: @@ -30,21 +48,28 @@ func _update_logo() -> void: func _on_submit_button_pressed() -> void: var feedback := SentryFeedback.new() - feedback.message = message_edit.text - feedback.name = name_edit.text - feedback.contact_email = email_edit.text + feedback.message = _message_edit.text + feedback.name = _name_edit.text + feedback.contact_email = _email_edit.text SentrySDK.capture_feedback(feedback) feedback_submitted.emit(feedback) # Reset feedback message - message_edit.text = "" + _message_edit.text = "" func _on_message_edit_text_changed() -> void: - var message: String = message_edit.text - submit_button.disabled = _count_words(message) < 2 + var message: String = _message_edit.text + if message.length() > 4096: + var col: int = _message_edit.get_caret_column() + message = message.substr(0, 4096) + _message_edit.text = message + _message_edit.set_caret_column(col) + _submit_button.disabled = _count_words(message) < minimum_words + _character_counter.text = str(message.length()) + "/4096" + _character_counter.visible = message.length() > 3000 func _count_words(text: String) -> int: diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn index 273d0d5a..59f3dcc0 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -38,10 +38,20 @@ expand_mode = 3 custom_minimum_size = Vector2(0, 12) layout_mode = 2 -[node name="MessageLabel" type="Label" parent="MarginContainer/VBoxContainer"] +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="MessageLabel" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"] layout_mode = 2 text = "Message" +[node name="CharacterCounter" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 10 +text = "0/4096" + [node name="MessageEdit" type="TextEdit" parent="MarginContainer/VBoxContainer"] unique_name_in_owner = true custom_minimum_size = Vector2(0, 100) @@ -52,9 +62,6 @@ placeholder_text = "Tell us about your issue" wrap_mode = 1 tab_input_mode = false -[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] -layout_mode = 2 - [node name="Spacer1" type="Control" parent="MarginContainer/VBoxContainer"] custom_minimum_size = Vector2(0, 12) layout_mode = 2 diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 8931db2c..29934fb2 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -1,4 +1,21 @@ extends Control +## User feedback GUI. +## +## Complete example of User Feedback integration. +## +## Tip: Add "user_feedback_gui.tscn" to your UI CanvasLayer and call show() when needed. +## +## See "user_feedback_form.gd" for more details. + + +@export var show_logo: bool = true: + set(value): + show_logo = value + %UserFeedbackForm.show_logo = show_logo + + +func _ready() -> void: + %UserFeedbackForm.show_logo = show_logo func _on_user_feedback_form_feedback_submitted(feedback: SentryFeedback) -> void: diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.tscn b/project/addons/sentry/user_feedback/user_feedback_gui.tscn index 0dbd1661..fb546f62 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_gui.tscn @@ -32,6 +32,7 @@ grow_horizontal = 2 grow_vertical = 2 [node name="UserFeedbackForm" parent="VBoxContainer" instance=ExtResource("1_t8jgq")] +unique_name_in_owner = true custom_minimum_size = Vector2(600, 800) layout_mode = 2 size_flags_horizontal = 4 diff --git a/project/views/capture_events.gd b/project/views/capture_events.gd index c8517dc9..e6e2518b 100644 --- a/project/views/capture_events.gd +++ b/project/views/capture_events.gd @@ -11,14 +11,15 @@ var _event_level: SentrySDK.Level var _user_feedback_gui: Control + func _ready() -> void: - _init_user_feedback_popup() + _init_user_feedback_gui() _init_level_choice_popup() _init_user_info() -## Initialize User Feedback popup -func _init_user_feedback_popup() -> void: +## Initialize User Feedback UI +func _init_user_feedback_gui() -> void: _user_feedback_gui = load("res://addons/sentry/user_feedback/user_feedback_gui.tscn").instantiate() _user_feedback_gui.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) get_owner().add_child.call_deferred(_user_feedback_gui) From e01a0007c3eda8502820132e777d01259277b890 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 20:39:36 +0200 Subject: [PATCH 06/23] Fix issues with properties --- .../user_feedback/user_feedback_form.gd | 3 ++- .../sentry/user_feedback/user_feedback_gui.gd | 20 ++++++++++++++++--- .../user_feedback/user_feedback_gui.tscn | 1 - 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index 6d09b9b8..7b84a185 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -43,7 +43,8 @@ func _ready() -> void: func _update_logo() -> void: - %Logo.visible = show_logo + if is_node_ready(): + %Logo.visible = show_logo func _on_submit_button_pressed() -> void: diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 29934fb2..28e2ad52 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -8,14 +8,28 @@ extends Control ## See "user_feedback_form.gd" for more details. +## Whether to display Sentry logo in the top right corner. @export var show_logo: bool = true: set(value): show_logo = value - %UserFeedbackForm.show_logo = show_logo + _update_form() + + +## Minimum number of words required in the feedback message before the feedback can be submitted. +@export var minimum_words: int = 2: + set(value): + minimum_words = value + _update_form() -func _ready() -> void: - %UserFeedbackForm.show_logo = show_logo +func _ready(): + _update_form() + + +func _update_form(): + if is_node_ready(): + %UserFeedbackForm.show_logo = show_logo + %UserFeedbackForm.minimum_words = minimum_words func _on_user_feedback_form_feedback_submitted(feedback: SentryFeedback) -> void: diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.tscn b/project/addons/sentry/user_feedback/user_feedback_gui.tscn index fb546f62..56cbc9d1 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_gui.tscn @@ -37,7 +37,6 @@ custom_minimum_size = Vector2(600, 800) layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 6 -show_logo = null [node name="SubmitButton" parent="VBoxContainer/UserFeedbackForm/MarginContainer/VBoxContainer/Actions" index="1"] theme_type_variation = &"ActionButton" From 202983836ede0c4009586c0caa101c840897b5ff Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 21:24:29 +0200 Subject: [PATCH 07/23] Update user_feedback_gui.gd --- project/addons/sentry/user_feedback/user_feedback_gui.gd | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 28e2ad52..dd24d716 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -1,9 +1,7 @@ extends Control -## User feedback GUI. -## ## Complete example of User Feedback integration. ## -## Tip: Add "user_feedback_gui.tscn" to your UI CanvasLayer and call show() when needed. +## Usage: Add "user_feedback_gui.tscn" to your UI scene and call show() when needed. ## ## See "user_feedback_form.gd" for more details. From 8b3ebd472e2cbfe063a84a6ee25689356208ed63 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 23:10:22 +0200 Subject: [PATCH 08/23] Decrese theme fonts --- project/addons/sentry/user_feedback/sentry_theme.tres | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/addons/sentry/user_feedback/sentry_theme.tres b/project/addons/sentry/user_feedback/sentry_theme.tres index 0594f92a..0f67cb72 100644 --- a/project/addons/sentry/user_feedback/sentry_theme.tres +++ b/project/addons/sentry/user_feedback/sentry_theme.tres @@ -206,7 +206,7 @@ corner_detail = 5 anti_aliasing = false [resource] -default_font_size = 24 +default_font_size = 20 ActionButton/base_type = &"Button" ActionButton/colors/font_color = Color(1, 1, 1, 1) ActionButton/colors/font_focus_color = Color(1, 1, 1, 1) @@ -238,7 +238,7 @@ Button/styles/hover = SubResource("StyleBoxFlat_gix2e") Button/styles/normal = SubResource("StyleBoxFlat_7qvkq") Button/styles/pressed = SubResource("StyleBoxFlat_6xjsq") HeaderMedium/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) -HeaderMedium/font_sizes/font_size = 30 +HeaderMedium/font_sizes/font_size = 26 Label/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) Label/colors/font_outline_color = Color(0, 0, 0, 0) Label/colors/font_shadow_color = Color(0, 0, 0, 0) From 75d87e712293f55d978abfc8bcd961384221919b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 23:10:46 +0200 Subject: [PATCH 09/23] Fix minimum_words = 0 --- project/addons/sentry/user_feedback/user_feedback_form.gd | 1 + 1 file changed, 1 insertion(+) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index 7b84a185..664fa2ab 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -40,6 +40,7 @@ signal feedback_cancelled() func _ready() -> void: _update_logo() + _on_message_edit_text_changed() func _update_logo() -> void: From acfdb4c10f401c7551031c4cebf878ebbcd7694d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 23:11:10 +0200 Subject: [PATCH 10/23] Use VBox sections --- .../user_feedback/user_feedback_form.tscn | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn index 59f3dcc0..ac37a161 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -4,12 +4,13 @@ [ext_resource type="Texture2D" uid="uid://d0o3nt85ac67i" path="res://addons/sentry/user_feedback/logo.svg" id="2_useou"] [node name="UserFeedbackForm" type="PanelContainer"] -offset_right = 387.0 -offset_bottom = 462.0 +offset_right = 376.0 +offset_bottom = 446.0 script = ExtResource("1_0vncv") [node name="MarginContainer" type="MarginContainer" parent="."] layout_mode = 2 +size_flags_horizontal = 3 theme_override_constants/margin_left = 16 theme_override_constants/margin_top = 16 theme_override_constants/margin_right = 16 @@ -17,6 +18,7 @@ theme_override_constants/margin_bottom = 16 [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] layout_mode = 2 +theme_override_constants/separation = 10 [node name="Title" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] layout_mode = 2 @@ -34,25 +36,25 @@ size_flags_horizontal = 10 texture = ExtResource("2_useou") expand_mode = 3 -[node name="Spacer0" type="Control" parent="MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(0, 12) +[node name="MessageSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] layout_mode = 2 +size_flags_vertical = 3 -[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MessageSection"] layout_mode = 2 -[node name="MessageLabel" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"] +[node name="MessageLabel" type="Label" parent="MarginContainer/VBoxContainer/MessageSection/HBoxContainer"] layout_mode = 2 text = "Message" -[node name="CharacterCounter" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"] +[node name="CharacterCounter" type="Label" parent="MarginContainer/VBoxContainer/MessageSection/HBoxContainer"] unique_name_in_owner = true visible = false layout_mode = 2 size_flags_horizontal = 10 text = "0/4096" -[node name="MessageEdit" type="TextEdit" parent="MarginContainer/VBoxContainer"] +[node name="MessageEdit" type="TextEdit" parent="MarginContainer/VBoxContainer/MessageSection"] unique_name_in_owner = true custom_minimum_size = Vector2(0, 100) layout_mode = 2 @@ -62,36 +64,34 @@ placeholder_text = "Tell us about your issue" wrap_mode = 1 tab_input_mode = false -[node name="Spacer1" type="Control" parent="MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(0, 12) +[node name="NameSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] layout_mode = 2 -[node name="NameLabel" type="Label" parent="MarginContainer/VBoxContainer"] +[node name="NameLabel" type="Label" parent="MarginContainer/VBoxContainer/NameSection"] layout_mode = 2 text = "Name" -[node name="NameEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"] +[node name="NameEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/NameSection"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 placeholder_text = "Your name (optional)" -[node name="Spacer2" type="Control" parent="MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(0, 12) +[node name="EmailSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] layout_mode = 2 -[node name="EmailLabel" type="Label" parent="MarginContainer/VBoxContainer"] +[node name="EmailLabel" type="Label" parent="MarginContainer/VBoxContainer/EmailSection"] layout_mode = 2 text = "Email" -[node name="EmailEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"] +[node name="EmailEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/EmailSection"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 placeholder_text = "Contact email (optional)" [node name="Spacer3" type="Control" parent="MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(0, 16) +custom_minimum_size = Vector2(0, 6) layout_mode = 2 [node name="Actions" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] @@ -112,6 +112,6 @@ size_flags_horizontal = 6 disabled = true text = "Submit" -[connection signal="text_changed" from="MarginContainer/VBoxContainer/MessageEdit" to="." method="_on_message_edit_text_changed"] +[connection signal="text_changed" from="MarginContainer/VBoxContainer/MessageSection/MessageEdit" to="." method="_on_message_edit_text_changed"] [connection signal="pressed" from="MarginContainer/VBoxContainer/Actions/CancelButton" to="." method="_on_cancel_button_pressed"] [connection signal="pressed" from="MarginContainer/VBoxContainer/Actions/SubmitButton" to="." method="_on_submit_button_pressed"] From 73c99d9cd5c628d29dcd50b29f89588b6f6a3f4e Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 23:11:43 +0200 Subject: [PATCH 11/23] Impl custom container logic --- .../sentry/user_feedback/user_feedback_gui.gd | 41 +++++++++++++++++-- .../user_feedback/user_feedback_gui.tscn | 34 ++++----------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index dd24d716..b59770c0 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -1,4 +1,5 @@ -extends Control +@tool +extends Container ## Complete example of User Feedback integration. ## ## Usage: Add "user_feedback_gui.tscn" to your UI scene and call show() when needed. @@ -19,15 +20,49 @@ extends Control minimum_words = value _update_form() +## Limit the maximum size that the form can have. +@export var maximum_size := Vector2(600, 600) + +@export var top_offset: float = 40.0 func _ready(): _update_form() +func _notification(what: int) -> void: + if what == NOTIFICATION_SORT_CHILDREN: + _resize_children() + + func _update_form(): if is_node_ready(): - %UserFeedbackForm.show_logo = show_logo - %UserFeedbackForm.minimum_words = minimum_words + var form := %UserFeedbackForm + form.show_logo = show_logo + form.minimum_words = minimum_words + + +## Centers children horizontally at the top of the screen with an offset. +func _resize_children() -> void: + var sz := get_size(); + + for i in get_child_count(): + var c = get_child(i) + if c is not Control: + continue + + var new_sz := Vector2(minf(sz.x, maximum_size.x), minf(sz.y, maximum_size.y)) + var ofs: Vector2 = ((size - new_sz) / 2.0).floor() + ofs.y = top_offset + + if c.size_flags_vertical == SIZE_EXPAND_FILL: + new_sz.y = size.y + ofs.y = 0 + + if c.size_flags_horizontal == SIZE_EXPAND_FILL: + new_sz.x = size.x + ofs.x = 0 + + fit_child_in_rect(c, Rect2(ofs, new_sz)); func _on_user_feedback_form_feedback_submitted(feedback: SentryFeedback) -> void: diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.tscn b/project/addons/sentry/user_feedback/user_feedback_gui.tscn index 56cbc9d1..c6af8a77 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_gui.tscn @@ -4,8 +4,7 @@ [ext_resource type="PackedScene" uid="uid://bdn5fqm81rhy6" path="res://addons/sentry/user_feedback/user_feedback_form.tscn" id="1_t8jgq"] [ext_resource type="Theme" uid="uid://bw0anqwp7xj8t" path="res://addons/sentry/user_feedback/sentry_theme.tres" id="1_u3uht"] -[node name="UserFeedbackGUI" type="Control"] -layout_mode = 3 +[node name="UserFeedbackGUI" type="Container"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -15,34 +14,19 @@ theme = ExtResource("1_u3uht") script = ExtResource("1_jugy2") [node name="ColorRect" type="ColorRect" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 color = Color(0, 0, 0, 0.22745098) -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="UserFeedbackForm" parent="VBoxContainer" instance=ExtResource("1_t8jgq")] +[node name="UserFeedbackForm" parent="." instance=ExtResource("1_t8jgq")] unique_name_in_owner = true -custom_minimum_size = Vector2(600, 800) layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 6 -[node name="SubmitButton" parent="VBoxContainer/UserFeedbackForm/MarginContainer/VBoxContainer/Actions" index="1"] +[node name="SubmitButton" parent="UserFeedbackForm/MarginContainer/VBoxContainer/Actions" index="1"] theme_type_variation = &"ActionButton" -disabled = false -[connection signal="feedback_cancelled" from="VBoxContainer/UserFeedbackForm" to="." method="_on_user_feedback_form_feedback_cancelled"] -[connection signal="feedback_submitted" from="VBoxContainer/UserFeedbackForm" to="." method="_on_user_feedback_form_feedback_submitted"] +[connection signal="feedback_cancelled" from="UserFeedbackForm" to="." method="_on_user_feedback_form_feedback_cancelled"] +[connection signal="feedback_submitted" from="UserFeedbackForm" to="." method="_on_user_feedback_form_feedback_submitted"] -[editable path="VBoxContainer/UserFeedbackForm"] +[editable path="UserFeedbackForm"] From 959df21d405ff48d38bba3f718c665f0a5a2494a Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 23:22:45 +0200 Subject: [PATCH 12/23] Add enable_name_input, enable_email_input --- .../user_feedback/user_feedback_form.gd | 20 ++++++++++++++++--- .../user_feedback/user_feedback_form.tscn | 2 ++ .../sentry/user_feedback/user_feedback_gui.gd | 16 +++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index 664fa2ab..ed69d0c6 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -25,7 +25,19 @@ signal feedback_cancelled() @export var show_logo: bool = true: set(value): show_logo = value - _update_logo() + _update_controls() + +## Whether to display name input field. +@export var enable_name_input: bool = true: + set(value): + enable_name_input = value + _update_controls() + +## Whether to display email input field. +@export var enable_email_input: bool = true: + set(value): + enable_email_input = value + _update_controls() ## Minimum number of words required in the feedback message before the feedback can be submitted. @export var minimum_words: int = 2 @@ -39,13 +51,15 @@ signal feedback_cancelled() func _ready() -> void: - _update_logo() + _update_controls() _on_message_edit_text_changed() -func _update_logo() -> void: +func _update_controls() -> void: if is_node_ready(): %Logo.visible = show_logo + %EmailSection.visible = enable_email_input + %NameSection.visible = enable_name_input func _on_submit_button_pressed() -> void: diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn index ac37a161..301f2409 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -65,6 +65,7 @@ wrap_mode = 1 tab_input_mode = false [node name="NameSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true layout_mode = 2 [node name="NameLabel" type="Label" parent="MarginContainer/VBoxContainer/NameSection"] @@ -78,6 +79,7 @@ size_flags_horizontal = 3 placeholder_text = "Your name (optional)" [node name="EmailSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true layout_mode = 2 [node name="EmailLabel" type="Label" parent="MarginContainer/VBoxContainer/EmailSection"] diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index b59770c0..6a7eb910 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -14,6 +14,19 @@ extends Container _update_form() +## Whether to display name input field. +@export var enable_name_input: bool = true: + set(value): + enable_name_input = value + _update_form() + +## Whether to display email input field. +@export var enable_email_input: bool = true: + set(value): + enable_email_input = value + _update_form() + + ## Minimum number of words required in the feedback message before the feedback can be submitted. @export var minimum_words: int = 2: set(value): @@ -23,6 +36,7 @@ extends Container ## Limit the maximum size that the form can have. @export var maximum_size := Vector2(600, 600) +## Offset from the top to position the form. @export var top_offset: float = 40.0 func _ready(): @@ -39,6 +53,8 @@ func _update_form(): var form := %UserFeedbackForm form.show_logo = show_logo form.minimum_words = minimum_words + form.enable_email_input = enable_email_input + form.enable_name_input = enable_name_input ## Centers children horizontally at the top of the screen with an offset. From 8e3335284d3fdb9c0a58b8ffcb74f10b3dc00018 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 17 Oct 2025 23:36:12 +0200 Subject: [PATCH 13/23] Comments --- project/addons/sentry/user_feedback/user_feedback_gui.gd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 6a7eb910..3a7d2363 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -36,7 +36,7 @@ extends Container ## Limit the maximum size that the form can have. @export var maximum_size := Vector2(600, 600) -## Offset from the top to position the form. +## Vertical offset from the top edge of the container to position the form. @export var top_offset: float = 40.0 func _ready(): @@ -70,10 +70,10 @@ func _resize_children() -> void: var ofs: Vector2 = ((size - new_sz) / 2.0).floor() ofs.y = top_offset + # Override size constraints when expand & fill flags are set to use all available space. if c.size_flags_vertical == SIZE_EXPAND_FILL: new_sz.y = size.y ofs.y = 0 - if c.size_flags_horizontal == SIZE_EXPAND_FILL: new_sz.x = size.x ofs.x = 0 From d0a935def2fbff629789b5f712cb5af7cbb43c4d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 11:29:26 +0200 Subject: [PATCH 14/23] Resize and close virtual keyboard on outside tap --- .../addons/sentry/user_feedback/user_feedback_form.tscn | 4 ++-- project/addons/sentry/user_feedback/user_feedback_gui.gd | 8 +++++++- .../addons/sentry/user_feedback/user_feedback_gui.tscn | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn index 301f2409..cfc95b6f 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -5,7 +5,7 @@ [node name="UserFeedbackForm" type="PanelContainer"] offset_right = 376.0 -offset_bottom = 446.0 +offset_bottom = 327.0 script = ExtResource("1_0vncv") [node name="MarginContainer" type="MarginContainer" parent="."] @@ -56,7 +56,7 @@ text = "0/4096" [node name="MessageEdit" type="TextEdit" parent="MarginContainer/VBoxContainer/MessageSection"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 100) +custom_minimum_size = Vector2(0, 72) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 3a7d2363..e6780009 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -39,6 +39,7 @@ extends Container ## Vertical offset from the top edge of the container to position the form. @export var top_offset: float = 40.0 + func _ready(): _update_form() @@ -48,7 +49,12 @@ func _notification(what: int) -> void: _resize_children() -func _update_form(): +func _gui_input(event: InputEvent) -> void: + if event is InputEventScreenTouch: + DisplayServer.virtual_keyboard_hide() + + +func _update_form() -> void: if is_node_ready(): var form := %UserFeedbackForm form.show_logo = show_logo diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.tscn b/project/addons/sentry/user_feedback/user_feedback_gui.tscn index c6af8a77..baadd7a2 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_gui.tscn @@ -10,6 +10,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 0 theme = ExtResource("1_u3uht") script = ExtResource("1_jugy2") @@ -17,11 +18,13 @@ script = ExtResource("1_jugy2") layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 +mouse_filter = 1 color = Color(0, 0, 0, 0.22745098) [node name="UserFeedbackForm" parent="." instance=ExtResource("1_t8jgq")] unique_name_in_owner = true layout_mode = 2 +mouse_filter = 1 [node name="SubmitButton" parent="UserFeedbackForm/MarginContainer/VBoxContainer/Actions" index="1"] theme_type_variation = &"ActionButton" From e9e733775f7f7684e3dd68c4befc0f9e280a991a Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 13:32:36 +0200 Subject: [PATCH 15/23] Scale UI for different resolutions --- .../sentry/user_feedback/user_feedback_gui.gd | 96 +++++++++++++++++-- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index e6780009..2a2214ce 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -26,6 +26,9 @@ extends Container enable_email_input = value _update_form() +## Enabling this option allows feedback UI to scale for different resolutions. +## Note: The default theme is mastered for 1080p viewport resolution. +@export var auto_scale_ui: bool = true ## Minimum number of words required in the feedback message before the feedback can be submitted. @export var minimum_words: int = 2: @@ -33,27 +36,31 @@ extends Container minimum_words = value _update_form() -## Limit the maximum size that the form can have. -@export var maximum_size := Vector2(600, 600) +## Maximum size constraint for the feedback form (measured in 1080p reference resolution). +@export var maximum_reference_size := Vector2(600, 600) -## Vertical offset from the top edge of the container to position the form. +## Vertical offset from the top edge of the container to position the form (measured in 1080p reference resolution). @export var top_offset: float = 40.0 -func _ready(): - _update_form() +@onready var _original_theme: Theme = theme -func _notification(what: int) -> void: - if what == NOTIFICATION_SORT_CHILDREN: - _resize_children() +func _ready(): + _update_form() func _gui_input(event: InputEvent) -> void: if event is InputEventScreenTouch: + # Hide virtual keyboard when user taps outside the feedback UI. DisplayServer.virtual_keyboard_hide() +func _notification(what: int) -> void: + if what == NOTIFICATION_SORT_CHILDREN: + _resize_children() + + func _update_form() -> void: if is_node_ready(): var form := %UserFeedbackForm @@ -67,14 +74,25 @@ func _update_form() -> void: func _resize_children() -> void: var sz := get_size(); + # Calculate scale factor + var scale_xy: float = 1.0 + if auto_scale_ui: + var vp_size: Vector2 = get_viewport().get_visible_rect().size + scale_xy = vp_size.y / 1080.0 + _rescale_theme(scale_xy) + for i in get_child_count(): var c = get_child(i) if c is not Control: continue - var new_sz := Vector2(minf(sz.x, maximum_size.x), minf(sz.y, maximum_size.y)) + var new_sz := Vector2( + minf(sz.x, maximum_reference_size.x * scale_xy), + minf(sz.y - top_offset * scale_xy, maximum_reference_size.y * scale_xy)) + new_sz = new_sz.floor() + var ofs: Vector2 = ((size - new_sz) / 2.0).floor() - ofs.y = top_offset + ofs.y = floorf(top_offset * scale_xy) # Override size constraints when expand & fill flags are set to use all available space. if c.size_flags_vertical == SIZE_EXPAND_FILL: @@ -93,3 +111,61 @@ func _on_user_feedback_form_feedback_submitted(feedback: SentryFeedback) -> void func _on_user_feedback_form_feedback_cancelled() -> void: hide() + + +# *** SCALING FOR DIFFERENT RESOLUTIONS *** + +func _rescale_theme(scale_factor: float) -> void: + if Engine.is_editor_hint() or not _original_theme: + # Don't make changes to theme in the editor. + return + + var th: Theme = _original_theme.duplicate() + + th.default_font_size = floori(20 * scale_factor) + th.set_font_size("font_size", "HeaderMedium", floori(26 * scale_factor)) + + # Resize stylebox items + for theme_type in th.get_stylebox_type_list(): + for sb_name in th.get_stylebox_list(theme_type): + var sb: StyleBox = th.get_stylebox(sb_name, theme_type) + if sb is StyleBoxFlat: + th.set_stylebox(sb_name, theme_type, _scale_stylebox(sb, scale_factor)) + + # Resize constants + for theme_type in th.get_constant_type_list(): + for constant_name in th.get_constant_list(theme_type): + var c: int = th.get_constant(constant_name, theme_type) + c = floori(c * scale_factor) + th.set_constant(constant_name, theme_type, c) + + theme = th + + +func _scale_stylebox(sb: StyleBox, scale_factor: float) -> StyleBox: + if sb is StyleBoxFlat: + var new_sb: StyleBoxFlat = sb.duplicate() + + new_sb.content_margin_bottom = floorf(new_sb.content_margin_bottom * scale_factor) + new_sb.content_margin_right = floorf(new_sb.content_margin_right * scale_factor) + new_sb.content_margin_left = floorf(new_sb.content_margin_left * scale_factor) + new_sb.content_margin_top = floorf(new_sb.content_margin_top * scale_factor) + + new_sb.border_width_bottom = floorf(new_sb.border_width_bottom * scale_factor) + new_sb.border_width_top = floorf(new_sb.border_width_top * scale_factor) + new_sb.border_width_left = floorf(new_sb.border_width_left * scale_factor) + new_sb.border_width_right = floorf(new_sb.border_width_right * scale_factor) + + new_sb.corner_radius_bottom_left = floorf(new_sb.corner_radius_bottom_left * scale_factor) + new_sb.corner_radius_bottom_right = floorf(new_sb.corner_radius_bottom_right * scale_factor) + new_sb.corner_radius_top_left = floorf(new_sb.corner_radius_top_left * scale_factor) + new_sb.corner_radius_top_right = floorf(new_sb.corner_radius_top_right * scale_factor) + + new_sb.expand_margin_bottom = floorf(new_sb.expand_margin_bottom * scale_factor) + new_sb.expand_margin_left = floorf(new_sb.expand_margin_left * scale_factor) + new_sb.expand_margin_right = floorf(new_sb.expand_margin_right * scale_factor) + new_sb.expand_margin_top = floorf(new_sb.expand_margin_top * scale_factor) + + return new_sb + else: + return sb From 54ba7be3b07af56b0f89c5f906d65c49880b66a0 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 13:46:32 +0200 Subject: [PATCH 16/23] Organize --- .../sentry/user_feedback/user_feedback_gui.gd | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 2a2214ce..c1cb12af 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -26,23 +26,31 @@ extends Container enable_email_input = value _update_form() -## Enabling this option allows feedback UI to scale for different resolutions. -## Note: The default theme is mastered for 1080p viewport resolution. -@export var auto_scale_ui: bool = true - ## Minimum number of words required in the feedback message before the feedback can be submitted. @export var minimum_words: int = 2: set(value): minimum_words = value _update_form() -## Maximum size constraint for the feedback form (measured in 1080p reference resolution). -@export var maximum_reference_size := Vector2(600, 600) +## Maximum size constraint for the feedback form (measured in reference resolution). +@export var maximum_form_size := Vector2(600, 600) -## Vertical offset from the top edge of the container to position the form (measured in 1080p reference resolution). +## Vertical offset from the top edge of the container to position the form (measured in reference resolution). @export var top_offset: float = 40.0 +@export_group("Auto Scale UI", "auto_scale") + +## Enabling this option allows feedback UI to scale for different resolutions. +## Note: The default theme is mastered for 1080p viewport resolution. +@export var auto_scale_enable: bool = true + +## Master resolution used as reference for UI scaling calculations. +## When auto_scale_ui is enabled, the UI will scale proportionally based on +## the ratio between the current viewport height and this resolution. +@export var auto_scale_master_resolution: int = 1080 + + @onready var _original_theme: Theme = theme @@ -70,15 +78,16 @@ func _update_form() -> void: form.enable_name_input = enable_name_input -## Centers children horizontally at the top of the screen with an offset. +## Centers children horizontally at the top of the screen with an offset, +## and optionally rescales for actual viewport resolution. func _resize_children() -> void: var sz := get_size(); # Calculate scale factor var scale_xy: float = 1.0 - if auto_scale_ui: + if auto_scale_enable: var vp_size: Vector2 = get_viewport().get_visible_rect().size - scale_xy = vp_size.y / 1080.0 + scale_xy = vp_size.y / auto_scale_master_resolution _rescale_theme(scale_xy) for i in get_child_count(): @@ -87,8 +96,8 @@ func _resize_children() -> void: continue var new_sz := Vector2( - minf(sz.x, maximum_reference_size.x * scale_xy), - minf(sz.y - top_offset * scale_xy, maximum_reference_size.y * scale_xy)) + minf(sz.x, maximum_form_size.x * scale_xy), + minf(sz.y - top_offset * scale_xy, maximum_form_size.y * scale_xy)) new_sz = new_sz.floor() var ofs: Vector2 = ((size - new_sz) / 2.0).floor() From fdf04a26f214660821b925b98002b3434c826bce Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 13:50:46 +0200 Subject: [PATCH 17/23] Store VBoxContainer separation in the theme file, so we can scale it --- project/addons/sentry/user_feedback/sentry_theme.tres | 2 ++ project/addons/sentry/user_feedback/user_feedback_form.tscn | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/project/addons/sentry/user_feedback/sentry_theme.tres b/project/addons/sentry/user_feedback/sentry_theme.tres index 0f67cb72..5454fefa 100644 --- a/project/addons/sentry/user_feedback/sentry_theme.tres +++ b/project/addons/sentry/user_feedback/sentry_theme.tres @@ -216,6 +216,7 @@ ActionButton/styles/focus = SubResource("StyleBoxFlat_hffoe") ActionButton/styles/hover = SubResource("StyleBoxFlat_e23mj") ActionButton/styles/normal = SubResource("StyleBoxFlat_g6gn8") ActionButton/styles/pressed = SubResource("StyleBoxFlat_63sqy") +BoxContainer/constants/separation = 4 Button/colors/font_color = Color(0.24313726, 0.20392157, 0.27450982, 1) Button/colors/font_disabled_color = Color(0.44313726, 0.3882353, 0.49411765, 1) Button/colors/font_focus_color = Color(0.24313726, 0.20392157, 0.27450982, 1) @@ -264,6 +265,7 @@ LineEdit/constants/outline_size = 0 LineEdit/styles/focus = SubResource("StyleBoxFlat_pmhvd") LineEdit/styles/normal = SubResource("StyleBoxFlat_ij1qw") LineEdit/styles/read_only = SubResource("StyleBoxFlat_3wai3") +MainBoxContainer/constants/separation = 10 PanelContainer/styles/panel = SubResource("StyleBoxFlat_qfyhw") TextEdit/colors/background_color = Color(0, 0, 0, 0) TextEdit/colors/caret_color = Color(0.24313726, 0.20392157, 0.27450982, 1) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn index cfc95b6f..a08ad905 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -18,7 +18,7 @@ theme_override_constants/margin_bottom = 16 [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] layout_mode = 2 -theme_override_constants/separation = 10 +theme_type_variation = &"MainBoxContainer" [node name="Title" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] layout_mode = 2 From 360955bf07cda94bd1f38cd168b9031340796865 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 14:01:43 +0200 Subject: [PATCH 18/23] Dont hardcode font sizes --- project/addons/sentry/user_feedback/user_feedback_gui.gd | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index c1cb12af..49adef0e 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -46,7 +46,7 @@ extends Container @export var auto_scale_enable: bool = true ## Master resolution used as reference for UI scaling calculations. -## When auto_scale_ui is enabled, the UI will scale proportionally based on +## When auto_scale_enable is ON, the UI will scale proportionally based on ## the ratio between the current viewport height and this resolution. @export var auto_scale_master_resolution: int = 1080 @@ -131,8 +131,9 @@ func _rescale_theme(scale_factor: float) -> void: var th: Theme = _original_theme.duplicate() - th.default_font_size = floori(20 * scale_factor) - th.set_font_size("font_size", "HeaderMedium", floori(26 * scale_factor)) + var font_size: int = th.default_font_size + th.default_font_size = floori(font_size * scale_factor) + th.set_font_size("font_size", "HeaderMedium", floori(font_size * 1.3 * scale_factor)) # Resize stylebox items for theme_type in th.get_stylebox_type_list(): From 2618cd216bc25141bb68ddd7cd3ae220ea66bfe5 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 14:06:23 +0200 Subject: [PATCH 19/23] Ensure we dont steal UI events --- project/addons/sentry/user_feedback/user_feedback_gui.gd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 49adef0e..97f14837 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -59,6 +59,8 @@ func _ready(): func _gui_input(event: InputEvent) -> void: + if not visible: + return if event is InputEventScreenTouch: # Hide virtual keyboard when user taps outside the feedback UI. DisplayServer.virtual_keyboard_hide() From 2ecda4f6b7ad17928242266ddb27ce6d2450e681 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 14:10:26 +0200 Subject: [PATCH 20/23] Focus message input on visible --- project/addons/sentry/user_feedback/user_feedback_form.gd | 5 +++++ project/addons/sentry/user_feedback/user_feedback_form.tscn | 1 + 2 files changed, 6 insertions(+) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index ed69d0c6..163cdf20 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -99,3 +99,8 @@ func _count_words(text: String) -> int: func _on_cancel_button_pressed() -> void: feedback_cancelled.emit() + + +func _on_visibility_changed() -> void: + if visible: + _message_edit.grab_focus() diff --git a/project/addons/sentry/user_feedback/user_feedback_form.tscn b/project/addons/sentry/user_feedback/user_feedback_form.tscn index a08ad905..a6d06efb 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.tscn +++ b/project/addons/sentry/user_feedback/user_feedback_form.tscn @@ -114,6 +114,7 @@ size_flags_horizontal = 6 disabled = true text = "Submit" +[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] [connection signal="text_changed" from="MarginContainer/VBoxContainer/MessageSection/MessageEdit" to="." method="_on_message_edit_text_changed"] [connection signal="pressed" from="MarginContainer/VBoxContainer/Actions/CancelButton" to="." method="_on_cancel_button_pressed"] [connection signal="pressed" from="MarginContainer/VBoxContainer/Actions/SubmitButton" to="." method="_on_submit_button_pressed"] From 88844b43d6274c6b45bf7a1b20447fa31216eea1 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 14:13:34 +0200 Subject: [PATCH 21/23] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d86b8b1..303d50bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add user feedback API for collecting and sending user feedback to Sentry ([#418](https://github.com/getsentry/sentry-godot/pull/418)) +- Add customizable User Feedback form that can be used for feedback submission, and as an example for custom implementations ([#422](https://github.com/getsentry/sentry-godot/pull/422)) ### Improvements From 3485ae654756037f7340f212847dbc014949b41a Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 18 Oct 2025 14:37:03 +0200 Subject: [PATCH 22/23] Fix error --- project/addons/sentry/user_feedback/user_feedback_form.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index 163cdf20..c34a9c14 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -102,5 +102,5 @@ func _on_cancel_button_pressed() -> void: func _on_visibility_changed() -> void: - if visible: + if visible and _message_edit: _message_edit.grab_focus() From 5c6cd6d851c582f3eade9d0f6b2ab6388bd7c3ea Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Wed, 22 Oct 2025 15:37:53 +0200 Subject: [PATCH 23/23] Rename props to "x_visible" pattern --- .../sentry/user_feedback/user_feedback_form.gd | 18 +++++++++--------- .../sentry/user_feedback/user_feedback_gui.gd | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/project/addons/sentry/user_feedback/user_feedback_form.gd b/project/addons/sentry/user_feedback/user_feedback_form.gd index c34a9c14..1683ff8f 100644 --- a/project/addons/sentry/user_feedback/user_feedback_form.gd +++ b/project/addons/sentry/user_feedback/user_feedback_form.gd @@ -22,21 +22,21 @@ signal feedback_submitted(feedback: SentryFeedback) signal feedback_cancelled() ## Whether to display Sentry logo in the top right corner. -@export var show_logo: bool = true: +@export var logo_visible: bool = true: set(value): - show_logo = value + logo_visible = value _update_controls() ## Whether to display name input field. -@export var enable_name_input: bool = true: +@export var name_visible: bool = true: set(value): - enable_name_input = value + name_visible = value _update_controls() ## Whether to display email input field. -@export var enable_email_input: bool = true: +@export var email_visible: bool = true: set(value): - enable_email_input = value + email_visible = value _update_controls() ## Minimum number of words required in the feedback message before the feedback can be submitted. @@ -57,9 +57,9 @@ func _ready() -> void: func _update_controls() -> void: if is_node_ready(): - %Logo.visible = show_logo - %EmailSection.visible = enable_email_input - %NameSection.visible = enable_name_input + %Logo.visible = logo_visible + %EmailSection.visible = email_visible + %NameSection.visible = name_visible func _on_submit_button_pressed() -> void: diff --git a/project/addons/sentry/user_feedback/user_feedback_gui.gd b/project/addons/sentry/user_feedback/user_feedback_gui.gd index 97f14837..4a12073c 100644 --- a/project/addons/sentry/user_feedback/user_feedback_gui.gd +++ b/project/addons/sentry/user_feedback/user_feedback_gui.gd @@ -8,22 +8,22 @@ extends Container ## Whether to display Sentry logo in the top right corner. -@export var show_logo: bool = true: +@export var logo_visible: bool = true: set(value): - show_logo = value + logo_visible = value _update_form() ## Whether to display name input field. -@export var enable_name_input: bool = true: +@export var name_visible: bool = true: set(value): - enable_name_input = value + name_visible = value _update_form() ## Whether to display email input field. -@export var enable_email_input: bool = true: +@export var email_visible: bool = true: set(value): - enable_email_input = value + email_visible = value _update_form() ## Minimum number of words required in the feedback message before the feedback can be submitted. @@ -74,10 +74,10 @@ func _notification(what: int) -> void: func _update_form() -> void: if is_node_ready(): var form := %UserFeedbackForm - form.show_logo = show_logo + form.logo_visible = logo_visible + form.email_visible = email_visible + form.name_visible = name_visible form.minimum_words = minimum_words - form.enable_email_input = enable_email_input - form.enable_name_input = enable_name_input ## Centers children horizontally at the top of the screen with an offset,