diff --git a/project.godot b/project.godot index 1b80571..9e7ce45 100644 --- a/project.godot +++ b/project.godot @@ -25,6 +25,7 @@ logger="*res://src/logger.gd" misc="*res://src/misc.gd" preader="*res://src/preader.gd" pmana="*res://src/pmana.gd" +ui_engine="*res://src/ui_engine.gd" [display] @@ -33,7 +34,7 @@ window/size/viewport_height=540 [editor] -run/main_run_args="%command% ++ example" +run/main_run_args="%command% ++ " export/convert_text_resources_to_binary=false [editor_plugins] @@ -76,6 +77,12 @@ console={ , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":96,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) ] } +editor_switchres={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":4194335,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194335,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} [rendering] diff --git a/src/console.gd b/src/console.gd index 34b39ec..7528f8d 100644 --- a/src/console.gd +++ b/src/console.gd @@ -63,6 +63,7 @@ func _ready() -> void: expressionscript.misc = get_node("/root/misc") expressionscript.preader = get_node("/root/preader") expressionscript.pmana = get_node("/root/pmana") + expressionscript.ui_engine = get_node("/root/ui_engine") expressionscript.console = self expressionscript.loader = get_node("/root/Presencode") add_child(expressionscript) @@ -88,12 +89,12 @@ func _process(_delta: float) -> void: # Change console position position.x = position.x-cursor_offset.x position.y = position.y-cursor_offset.y - # Prevent console from going out of scope - var position_new: Vector2 = check_violation() - if position != position_new: - position = position_new # Set new cursor_origin cursor_origin = cursor_position + # Prevent console from going out of scope + var position_new: Vector2 = check_violation() + if position != position_new: + position = position_new # Helper function, prevents console from going out of scope func check_violation() -> Vector2: diff --git a/src/console_expressionscript.gd b/src/console_expressionscript.gd index cb640b8..2d130f1 100644 --- a/src/console_expressionscript.gd +++ b/src/console_expressionscript.gd @@ -28,6 +28,7 @@ var logger: Node = null var misc: Node = null var preader: Node = null var pmana: Node = null +var ui_engine: Node = null # SceneTree var console: Node = null var loader: Node = null diff --git a/src/misc.gd b/src/misc.gd index a28ab47..029f8fd 100644 --- a/src/misc.gd +++ b/src/misc.gd @@ -134,6 +134,21 @@ func check_platform() -> void: _: # Platform not supported logger.error("The \"" + OS.get_name() + "\" operating system is not supported by Presencode. You can add support for that platform to Presencode yourself, if you want.") +# Returns the best common resolution +func get_best_resolution() -> Vector2i: + logger.info("Determining best resolution") + var screen_size: Vector2 = Vector2(DisplayServer.screen_get_size()) + var resolutions: Array[Vector2] = [Vector2(960, 540), Vector2(1920, 1080), Vector2(2560, 1440), Vector2(3840, 2160)] + var closest_resolution: Vector2 = Vector2(NAN, NAN) + var closest_distance: float = INF + for resolution in resolutions: + var distance = screen_size.distance_to(resolution) + if distance < closest_distance: + closest_distance = distance + closest_resolution = resolution + if closest_resolution == Vector2(NAN, NAN): return Vector2i(960, 540) + else: return closest_resolution + # Check for manifest consistency func check_manifest_consistency(manifest: Dictionary) -> misc.Error: logger.diag("Checking manifest for consistency") diff --git a/src/ui/welcome.gd b/src/ui/welcome.gd new file mode 100644 index 0000000..ff340eb --- /dev/null +++ b/src/ui/welcome.gd @@ -0,0 +1,113 @@ +extends Node + +func switch_mode() -> void: + DisplayServer.window_set_title("Presencode » Welcome") + update_splash() + +@warning_ignore("integer_division") +func update_resolution(new_resolution: Vector2i) -> void: + var base: Button = get_node("Wallpaper/Welcome") + var wallpaper_size: Vector2 = Vector2(7440, 4822) + base.get_parent().size = Vector2(wallpaper_size.x*(new_resolution.x/wallpaper_size.x), wallpaper_size.y*(new_resolution.y/wallpaper_size.y)) + base.size = Vector2(new_resolution.x*0.9, new_resolution.y*0.9) + base.position = Vector2(new_resolution.x*0.05, new_resolution.y*0.05) + base.add_theme_stylebox_override("normal", get_stylebox_blur(new_resolution)) + base.add_theme_stylebox_override("hover", get_stylebox_blur(new_resolution)) + base.add_theme_stylebox_override("pressed", get_stylebox_blur(new_resolution)) + base.add_theme_stylebox_override("disabled", get_stylebox_blur(new_resolution)) + for child in base.get_children(true): + for group in child.get_groups(): + match(group): + "font": + child.add_theme_font_override("normal_font", ResourceLoader.load("res://assets/fonts/Inter/Regular.ttf")) + child.add_theme_font_override("bold_font", ResourceLoader.load("res://assets/fonts/Inter/Bold.ttf")) + child.add_theme_font_override("italics_font", ResourceLoader.load("res://assets/fonts/Inter/Italic.ttf")) + child.add_theme_font_override("bold_italics_font", ResourceLoader.load("res://assets/fonts/Inter/BoldItalic.ttf")) + child.add_theme_font_override("mono_font", ResourceLoader.load("res://assets/fonts/FiraCode/Regular.ttf")) + "font_normal": + child.add_theme_font_size_override("normal_font_size", new_resolution.x/40) + child.add_theme_font_size_override("bold_font_size", new_resolution.x/40) + child.add_theme_font_size_override("italics_font_size", new_resolution.x/40) + child.add_theme_font_size_override("bold_italics_font_size", new_resolution.x/40) + child.add_theme_font_size_override("mono_font_size", new_resolution.x/41.73913043478261) + "font_small": + child.add_theme_font_size_override("normal_font_size", new_resolution.x/53.33333333333333) + child.add_theme_font_size_override("bold_font_size", new_resolution.x/53.33333333333333) + child.add_theme_font_size_override("italics_font_size", new_resolution.x/53.33333333333333) + child.add_theme_font_size_override("bold_italics_font_size", new_resolution.x/53.33333333333333) + child.add_theme_font_size_override("mono_font_size", new_resolution.x/53.33333333333333) + "font_one": child.add_theme_font_override("font", ResourceLoader.load("res://assets/fonts/Inter/Regular.ttf")) + "font_one_normal": child.add_theme_font_size_override("font_size", new_resolution.x/40) + "font_one_small": child.add_theme_font_size_override("font_size", new_resolution.x/53.33333333333333) + base.get_node("Icon").size = Vector2(new_resolution.x/1.5, new_resolution.y/4.5) + base.get_node("Icon").position = Vector2(base.size.x/2-base.get_node("Icon").size.x/2, base.size.y*0.05) + base.get_node("Splash").size = Vector2(new_resolution.x/1.5, new_resolution.y/15.88235294117647) + base.get_node("Splash").position = Vector2(base.size.x/2-base.get_node("Splash").size.x/2, base.get_node("Icon").position.y+11*(new_resolution.y/49.09090909090909)) + base.get_node("OpenButton").size = Vector2(new_resolution.x/4.403669724770642, new_resolution.y/14.21052631578947) + base.get_node("OpenButton").position = Vector2(base.size.x/16, base.size.y/2.410714285714286) + base.get_node("EditorButton").size = Vector2(new_resolution.x/4.403669724770642, new_resolution.y/14.21052631578947) + base.get_node("EditorButton").position = Vector2(base.size.x-base.get_node("OpenButton").position.x-base.get_node("EditorButton").size.x, base.size.y/2.410714285714286) + base.get_node("AboutText").size = Vector2(new_resolution.x/1.5, new_resolution.y/7.5) + base.get_node("AboutText").position = Vector2(base.size.x/2-base.get_node("AboutText").size.x/2, base.size.y-base.size.y/54-base.size.y/7.5) + +@warning_ignore("integer_division") +func get_stylebox_blur(resolution_: Vector2i) -> StyleBoxFlat: + var stylebox: StyleBoxFlat = StyleBoxFlat.new() + stylebox.bg_color = Color8(0, 0, 0, 180) + stylebox.corner_radius_top_left = resolution_.x/30 + stylebox.corner_radius_top_right = resolution_.x/30 + stylebox.corner_radius_bottom_left = resolution_.x/30 + stylebox.corner_radius_bottom_right = resolution_.x/30 + return stylebox + +func update_splash() -> void: + logger.diag("Updating splash text") + get_node("Wallpaper/Welcome/Splash").text = "[center]" + ["The first native code-based presentation viewer.", "Presenting your presentations since 2023", "Made using Godot", "nice", "[b]your mom[/b]", "Also try LibreOffice!", "... exists.", "making unprofessional presentations"].pick_random() + "[/center]" + +func open_presentation_picker() -> void: + logger.info("Opening presentation") + var fd: FileDialog = FileDialog.new() + fd.access = FileDialog.ACCESS_FILESYSTEM + fd.current_path = "user://" + fd.file_mode = FileDialog.FILE_MODE_OPEN_FILE + fd.add_filter("*.pcpa", "") + fd.add_filter("*.zip", "") + fd.mode_overrides_title = false + fd.show_hidden_files = true + fd.title = "Open a Presencode presentation file" + fd.use_native_dialog = true + fd.cancel_button_text = "Cancel" + fd.ok_button_text = "Present" + fd.visible = true + fd.connect("file_selected", func(path: String): open_presentation(path)) + add_child(fd) + +func open_presentation(path: String) -> void: + if FileAccess.file_exists(path) and path.ends_with(".zip") or path.ends_with(".pcpa"): # .pcpa = presencode presentation archive + logger.info("Opening presentation archive") + preader.open_presentation(path, true) + elif DirAccess.dir_exists_absolute(path): + logger.info("Opening presentation directory") + preader.open_presentation(path, false) + else: + await logger.error("Presentation file/directory \"" + path + "\" not found") + # Read manifest & entrypoint files + preader.read_manifest() + preader.read_entrypoint() + # Update window properties + get_node("/root/Console").calculate_drag_area(preader.get_ratio_resolution()) + DisplayServer.window_set_title("Presencode » Presenting \"" + preader.get_topic() + "\" by \"" + preader.get_authors() + "\"") + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + DisplayServer.window_set_size(preader.get_ratio_resolution()) + DisplayServer.window_set_min_size(preader.get_ratio_resolution()) + if get_node("/root/Presencode").config_window_size_support: get_node("/root/Presencode/WindowSizeSupport/EndPixel").position = Vector2(preader.get_ratio_resolution().x-1, preader.get_ratio_resolution().y-1) + get_tree().root.content_scale_size = preader.get_ratio_resolution() + get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_VIEWPORT + get_tree().root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP + get_tree().root.content_scale_factor = 1.0 + await get_tree().process_frame + DisplayServer.window_set_position(misc.get_center(DisplayServer.screen_get_size(), DisplayServer.window_get_size())) + # Add entrypoint to SceneTree + get_tree().root.add_child(preader.get_entrypoint()) + # Uninitialize UI Engine + ui_engine.uninitialize() diff --git a/src/ui_engine.gd b/src/ui_engine.gd new file mode 100644 index 0000000..e12f088 --- /dev/null +++ b/src/ui_engine.gd @@ -0,0 +1,82 @@ +extends Node + +# Enums +enum UIMode { + UNKNOWN, + WELCOME +} + +# Nodes +var loader: Control = null +var ui_mode_node: Control = null + +# States +var initialized: bool = false +var ui_mode: UIMode = UIMode.UNKNOWN +var resolution: String = "960x540" + +func initialize(loader_: Control) -> void: + if initialized: + logger.error("The UI Engine been initialized already") + return + logger.info("Initializing UI Engine") + loader = loader_ + initialized = true + logger.diag("Switching to welcome mode") + switch_mode(UIMode.WELCOME) + update_resolution(misc.get_best_resolution()) + logger.diag("Updating window properties") + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) + DisplayServer.window_set_min_size(Vector2i(960, 540)) + get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_VIEWPORT + get_tree().root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP + get_tree().root.content_scale_factor = 1.0 + +func uninitialize() -> void: + if !initialized: + logger.error("The UI Engine is not initialized.") + return + logger.info("Uninitializing UI Engine") + loader = null + initialized = false + resolution = "960x540" + ui_mode = UIMode.UNKNOWN + logger.info("Unloading UI mode") + ui_mode_node.queue_free() + ui_mode_node = null + +func _process(_delta: float) -> void: + if !initialized: return + if Input.is_action_just_pressed("editor_switchres"): + match(resolution): + "960x540": update_resolution(Vector2i(1920, 1080)) + "1920x1080": update_resolution(Vector2i(2560, 1440)) + "2560x1440": update_resolution(Vector2i(3840, 2160)) + "3840x2160": update_resolution(Vector2i(960, 540)) + +func switch_mode(mode: UIMode) -> void: + if !initialized: + logger.error("The UI Engine hasn't been initialized yet") + return + if mode == UIMode.UNKNOWN: return + logger.info("Switching to mode " + str(mode)) + if ui_mode_node != null: loader.remove_child(ui_mode_node) + match(mode): + UIMode.WELCOME: + ui_mode_node = ResourceLoader.load("res://ui/Welcome.tscn").instantiate() + ui_mode = UIMode.WELCOME + loader.add_child(ui_mode_node) + ui_mode_node.switch_mode() + +func update_resolution(new_resolution: Vector2i) -> void: + if !initialized: + logger.error("The UI Engine hasn't been initialized yet") + return + DisplayServer.window_set_size(new_resolution) + get_tree().root.content_scale_size = new_resolution + if get_node_or_null("/root/Console") != null: get_node("/root/Console").calculate_drag_area(new_resolution) + resolution = str(new_resolution.x) + "x" + str(new_resolution.y) + match(ui_mode): + UIMode.UNKNOWN: return + UIMode.WELCOME: ui_mode_node.update_resolution(new_resolution) + logger.diag("Set resolution to " + str(new_resolution.x) + "x" + str(new_resolution.y)) diff --git a/ui/Welcome.tscn b/ui/Welcome.tscn new file mode 100644 index 0000000..986536a --- /dev/null +++ b/ui/Welcome.tscn @@ -0,0 +1,125 @@ +[gd_scene load_steps=6 format=3 uid="uid://b8byh8tp1kc6b"] + +[ext_resource type="Texture2D" uid="uid://s10lveujhhqs" path="res://assets/images/Wallpaper.png" id="1_3cfcw"] +[ext_resource type="Script" path="res://src/ui/welcome.gd" id="1_rowat"] +[ext_resource type="Texture2D" uid="uid://41uwq5qahj23" path="res://assets/images/IconTextInverted.png" id="2_2w5c0"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0l23a"] +bg_color = Color(0, 0, 0, 0.705882) +corner_radius_top_left = 32 +corner_radius_top_right = 32 +corner_radius_bottom_right = 32 +corner_radius_bottom_left = 32 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_sor5s"] + +[node name="GUIWelcome" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_rowat") + +[node name="Wallpaper" type="NinePatchRect" parent="."] +layout_mode = 0 +offset_right = 960.0 +offset_bottom = 540.0 +texture = ExtResource("1_3cfcw") + +[node name="Welcome" type="Button" parent="Wallpaper"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -432.0 +offset_top = -243.0 +offset_right = 432.0 +offset_bottom = 243.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/normal = SubResource("StyleBoxFlat_0l23a") +theme_override_styles/hover = SubResource("StyleBoxFlat_0l23a") +theme_override_styles/pressed = SubResource("StyleBoxFlat_0l23a") +theme_override_styles/disabled = SubResource("StyleBoxFlat_0l23a") +theme_override_styles/focus = SubResource("StyleBoxEmpty_sor5s") + +[node name="Icon" type="NinePatchRect" parent="Wallpaper/Welcome"] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -320.0 +offset_top = 24.3 +offset_right = 320.0 +offset_bottom = 144.3 +grow_horizontal = 2 +texture = ExtResource("2_2w5c0") + +[node name="Splash" type="RichTextLabel" parent="Wallpaper/Welcome" groups=["font", "font_normal"]] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -320.0 +offset_top = 152.975 +offset_right = 320.0 +offset_bottom = 186.975 +grow_horizontal = 2 +mouse_filter = 2 +bbcode_enabled = true +text = "[center]splash text[/center]" +scroll_active = false + +[node name="AboutText" type="RichTextLabel" parent="Wallpaper/Welcome" groups=["font", "font_small"]] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -320.0 +offset_top = -92.0 +offset_right = 320.0 +offset_bottom = -20.0 +grow_horizontal = 2 +grow_vertical = 0 +mouse_filter = 2 +bbcode_enabled = true +text = "[center][i]Copyright (c) 2024[/i] [b]JeremyStar™ & Contributors[/b] +Licensed under the [b]GNU General Public License version 3[/b]. +Thank you for using Presencode <3[/center]" +scroll_active = false + +[node name="OpenButton" type="Button" parent="Wallpaper/Welcome" groups=["font_one", "font_one_normal"]] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 60.0 +offset_top = -19.0 +offset_right = 278.0 +offset_bottom = 19.0 +grow_vertical = 2 +text = "Open presentation" + +[node name="EditorButton" type="Button" parent="Wallpaper/Welcome" groups=["font_one", "font_one_normal"]] +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -278.0 +offset_top = -19.0 +offset_right = -60.0 +offset_bottom = 19.0 +grow_horizontal = 0 +grow_vertical = 2 +text = "Edit presentation" + +[connection signal="pressed" from="Wallpaper/Welcome" to="." method="update_splash"] +[connection signal="pressed" from="Wallpaper/Welcome/OpenButton" to="." method="open_presentation_picker"]