diff --git a/Console.tscn b/Console.tscn
index 96cb19c..8198605 100644
--- a/Console.tscn
+++ b/Console.tscn
@@ -93,5 +93,5 @@ theme_override_fonts/font = ExtResource("4_jqfsc")
theme_override_font_sizes/font_size = 18
placeholder_text = "Enter a command here"
-[connection signal="pressed" from="Bar/CloseButton" to="." method="close_console"]
+[connection signal="pressed" from="Bar/CloseButton" to="." method="toggle_console"]
[connection signal="text_changed" from="Shell/Input" to="." method="input_changed"]
diff --git a/Loader.tscn b/Loader.tscn
index 360ce55..2e7ab2f 100644
--- a/Loader.tscn
+++ b/Loader.tscn
@@ -8,43 +8,3 @@ offset_bottom = 1440.0
size_flags_horizontal = 4
color = Color(0, 0, 0, 1)
script = ExtResource("1_dpffy")
-
-[node name="WindowSizeSupport" type="Control" parent="."]
-anchors_preset = 0
-offset_right = 40.0
-offset_bottom = 40.0
-
-[node name="EndPixel" type="ColorRect" parent="WindowSizeSupport"]
-layout_mode = 0
-offset_left = 1920.0
-offset_top = 1080.0
-offset_right = 1921.0
-offset_bottom = 1081.0
-
-[node name="1920+" type="ColorRect" parent="WindowSizeSupport"]
-layout_mode = 0
-offset_right = 9999.0
-offset_bottom = 30.0
-
-[node name="1920" type="ColorRect" parent="WindowSizeSupport"]
-layout_mode = 0
-offset_right = 1920.0
-offset_bottom = 30.0
-color = Color(0, 0, 1, 1)
-
-[node name="1441" type="ColorRect" parent="WindowSizeSupport"]
-layout_mode = 0
-offset_right = 30.0
-offset_bottom = 9999.0
-
-[node name="1440" type="ColorRect" parent="WindowSizeSupport"]
-layout_mode = 0
-offset_right = 30.0
-offset_bottom = 1440.0
-color = Color(1, 1, 0, 1)
-
-[node name="1080" type="ColorRect" parent="WindowSizeSupport"]
-layout_mode = 0
-offset_right = 30.0
-offset_bottom = 1080.0
-color = Color(1, 0, 1, 1)
diff --git a/docs/docs/reference/api/loader.md b/docs/docs/reference/api/loader.md
new file mode 100644
index 0000000..d360b12
--- /dev/null
+++ b/docs/docs/reference/api/loader.md
@@ -0,0 +1,32 @@
+---
+sidebar_position: 1
+---
+
+# Loader
+Initializes Presencode and holds the viewport while presenting.
+
+## Enums
+### `VersionType`
+- values
+ - `RELEASE`
+ - `RELEASECANDIDATE`
+ - `BETA`
+ - `ALPHA`
+## Constants
+### `version_release`
+- type `int`
+- description `Contains the current release number (e.g. 1)`
+### `version_type`
+- type `VersionType`
+- description `Contains the current release type (e.g. VersionType.BETA)`
+### `version_typerelease`
+- type `int`
+- description `Contains the current release type version (e.g. 3)`
+### `format_version`
+- return type `String`
+- description `Replaces the variables %release%, %type_raw%, %type%, %type_short%, %type_technical% and %typerelease%.`
+- arguments
+ - `format`
+ - type `string`
+ - mandatory `yes`
+ - description `The string that you want to format`
diff --git a/docs/docs/reference/api/logger.md b/docs/docs/reference/api/logger.md
index 68e2f0e..d3b3156 100644
--- a/docs/docs/reference/api/logger.md
+++ b/docs/docs/reference/api/logger.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 1
+sidebar_position: 2
---
# Logger
diff --git a/docs/docs/reference/api/misc.md b/docs/docs/reference/api/misc.md
index 1031f69..2d182c6 100644
--- a/docs/docs/reference/api/misc.md
+++ b/docs/docs/reference/api/misc.md
@@ -1,27 +1,45 @@
---
-sidebar_position: 2
+sidebar_position: 3
---
# Miscellaneous (misc)
Miscellaneous functions that do not fit into other scripts.
+## Enums
+### `BooleanState`
+- values
+ - `TRUE`
+ - `FALSE`
+ - `INVALID`
## Variables
### `config_shutdown_invisible`
- type `bool`
- description `Hides the main window on shutdown if true, displays a white texture on shutdown if false`
## Functions
-### `shutdown`
-- return type `void`
-- description `Registers a new presentation controller`
+### `get_bool`
+- return type `BooleanState`
+- description `Retrieves a bool from a String`
- arguments
- - `exitcode`
- - type `int`
- - mandatory `no`
- - description `DUDE DO YOU DON'T KNOW WHAT A EXITCODE IS?!`
-### `get_temporary_dir`
-- return type `String`
-- description `Return a temporary directory`
-- arguments `none`
+ - `string`
+ - type `String`
+ - mandatory `yes`
+ - description `The string that you want to convert into a bool`
+### `get_int`
+- return type `Vector2i`
+- description `Retrieves the next valid integer from an Array. Returns Vector2i(0, NUMBER) if successful, Vector2i(1, 0) if not`
+- arguments
+ - `array`
+ - type `Array`
+ - mandatory `yes`
+ - description `The array that you want to get an integer out of`
+### `get_int_direct`
+- return type `Vector2i`
+- description `Converts a String into an int. Returns Vector2i(0, NUMBER) if successful, Vector2i(1, 0) if not`
+- arguments
+ - `string`
+ - type `String`
+ - mandatory `yes`
+ - description `The string that you want to convert to an integer`
### `get_center`
- return type `Vector2i`
- description `Calculates the center of a child inside its parent (Vector2i edition)`
@@ -46,18 +64,28 @@ Miscellaneous functions that do not fit into other scripts.
- type `Vector2`
- mandatory `yes`
- description `The size of the child object`
-### `clear_viewport`
-- return type `void`
-- description `Clears the presentation viewport`
-- arguments `none`
-### `hide_log`
-- return type `void`
-- description `Hides log output`
-- arguments `none`
-### `show_log`
-- return type `void`
-- description `Unhides log output`
+### `get_shortened_array`
+- return type `Array`
+- description `Removes n items from an array (starting from the beginning)`
+- arguments
+ - `array`
+ - type `Array`
+ - mandatory `yes`
+ - description `The array that you want to shorten`
+ - `skip`
+ - type `int`
+ - mandatory `yes`
+ - description `The amount of items that you want to remove from the beginning.`
+ - warning `This function automatically adds 1 to the skip variable. To only skip one element, use 0 (for example)`
+### `get_temporary_dir`
+- return type `String`
+- description `Return a temporary directory`
- arguments `none`
### `shutdown`
- return type `void`
-- description `Ends the presentation and shuts Presencode down`
\ No newline at end of file
+- description `Registers a new presentation controller`
+- arguments
+ - `exitcode`
+ - type `int`
+ - mandatory `no`
+ - description `DUDE DO YOU DON'T KNOW WHAT A EXITCODE IS?!`
diff --git a/docs/docs/reference/api/pmana.md b/docs/docs/reference/api/pmana.md
index e621203..81caddf 100644
--- a/docs/docs/reference/api/pmana.md
+++ b/docs/docs/reference/api/pmana.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 3
+sidebar_position: 4
---
# Presentation Manager (pmana)
diff --git a/docs/docs/reference/api/preader.md b/docs/docs/reference/api/preader.md
index 9d2c330..32d7ff6 100644
--- a/docs/docs/reference/api/preader.md
+++ b/docs/docs/reference/api/preader.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 4
+sidebar_position: 5
---
# Presentation Reader (preader)
diff --git a/export_presets.cfg b/export_presets.cfg
index e3e64d2..b50698f 100644
--- a/export_presets.cfg
+++ b/export_presets.cfg
@@ -18,7 +18,7 @@ encrypt_directory=false
custom_template/debug=""
custom_template/release=""
-debug/export_console_wrapper=1
+debug/export_console_wrapper=2
binary_format/embed_pck=true
texture_format/bptc=true
texture_format/s3tc=true
@@ -81,7 +81,7 @@ encrypt_directory=false
custom_template/debug=""
custom_template/release=""
-debug/export_console_wrapper=1
+debug/export_console_wrapper=2
binary_format/embed_pck=true
texture_format/bptc=true
texture_format/s3tc=true
diff --git a/project.godot b/project.godot
index 808da6a..fbec78b 100644
--- a/project.godot
+++ b/project.godot
@@ -26,6 +26,7 @@ misc="*res://src/misc.gd"
preader="*res://src/preader.gd"
pmana="*res://src/pmana.gd"
ui_engine="*res://src/ui_engine.gd"
+processor="*res://src/processor.gd"
[display]
@@ -73,8 +74,8 @@ content_scale_switch={
}
console={
"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":96,"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":96,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
+"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":4194334,"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":4194334,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
editor_switchres={
diff --git a/src/clickoverlay.gd b/src/clickoverlay.gd
index 47d6ba4..936d76a 100644
--- a/src/clickoverlay.gd
+++ b/src/clickoverlay.gd
@@ -23,9 +23,10 @@
##############################################################################
extends Button
-# Setup clickoverlay
+# Initialize clickoverlay
func _ready() -> void:
- # Set properties
+ logger.diag("Initializing ClickOverlay")
+ # Set button properties
mouse_filter = Control.MOUSE_FILTER_STOP
position = Vector2i(0, 0)
# Hide button
@@ -34,17 +35,13 @@ func _ready() -> void:
add_theme_stylebox_override("pressed", StyleBoxEmpty.new())
add_theme_stylebox_override("disabled", StyleBoxEmpty.new())
add_theme_stylebox_override("focus", StyleBoxEmpty.new())
- # Connect "pressed" signal to function "clicked"
+ # Connect pressed signal
connect("pressed", Callable(self, "clicked"))
# Simple fix for a bug
await get_tree().process_frame
clicked()
+# Clicked event
func clicked() -> void:
- # Increase current_slide by one
logger.info("Navigating one slide forwards (click)")
pmana.change_slide(pmana.current_slide+1, true)
-
-func _process(_delta: float) -> void:
- # Scale clickoverlay to window size
- size = DisplayServer.window_get_size()
diff --git a/src/console.gd b/src/console.gd
index 7528f8d..2c96f5b 100644
--- a/src/console.gd
+++ b/src/console.gd
@@ -23,13 +23,6 @@
##############################################################################
extends ColorRect
-# Enums
-enum BooleanState {
- TRUE,
- FALSE,
- INVALID
-}
-
# Nodes
@onready var input: TextEdit = get_node("Shell/Input")
@onready var output: RichTextLabel = get_node("Shell/Output")
@@ -43,20 +36,24 @@ var dragging: bool = false
var drag_area: Vector2 = Vector2(0, 0)
var cursor_origin: Vector2 = Vector2(0, 0)
+# Initialize debug console
func _ready() -> void:
logger.info("Initializing debug console")
+ # Set properties
visible = false
position = Vector2(30, 30)
- calculate_drag_area()
+ # Calculate console drag area
+ processor.update_dragging_area(processor.DragNode.CONSOLE)
# Setup window dragging
- $Bar/DragButton.connect("button_down", func():
+ $Bar/DragButton.connect("button_down", func() -> void:
dragging = true
cursor_origin = get_tree().root.get_viewport().get_mouse_position()
)
- $Bar/DragButton.connect("button_up", func():
+ $Bar/DragButton.connect("button_up", func() -> void:
dragging = false
cursor_origin = Vector2(0,0)
)
+ # Setup ExpressionScript (in which arbitrary expressions are run)
expressionscript.name = "ExpressionScript"
expressionscript.set_script(ResourceLoader.load("res://src/console_expressionscript.gd"))
expressionscript.logger = get_node("/root/logger")
@@ -67,60 +64,18 @@ func _ready() -> void:
expressionscript.console = self
expressionscript.loader = get_node("/root/Presencode")
add_child(expressionscript)
+ # Reset console session
await process_command(PackedStringArray(["exit"]))
logger.info("Debug console initialized")
-func _process(_delta: float) -> void:
- # Move to top
- get_tree().root.move_child(self, get_tree().root.get_child_count(true))
- # Visibility toggle key
- if Input.is_action_just_pressed("console"):
- logger.info("Toggling console visibility")
- visible = !visible
- if dragging:
- # Get new cursor position
- var cursor_position = get_tree().root.get_viewport().get_mouse_position()
- # If nothing changed, don't do anything
- if cursor_origin != cursor_position:
- # Calculate cursor offset
- var cursor_offset: Vector2 = Vector2(0, 0)
- cursor_offset.x = cursor_origin.x-cursor_position.x
- cursor_offset.y = cursor_origin.y-cursor_position.y
- # Change console position
- position.x = position.x-cursor_offset.x
- position.y = position.y-cursor_offset.y
- # 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:
- var new_position: Vector2 = position
- if position.x <= 0:
- new_position.x = 0
- elif position.x >= drag_area.x:
- new_position.x = drag_area.x
- if position.y <= 0:
- new_position.y = 0
- elif position.y >= drag_area.y:
- new_position.y = drag_area.y
- return new_position
-
-# Calculates a new drag_area
-func calculate_drag_area(area: Vector2i = DisplayServer.window_get_size()) -> void:
- drag_area.x = area.x-size.x
- drag_area.y = area.y-size.y
-
-# Close console on close button press
-func close_console() -> void:
- logger.info("Closing console")
- visible = false
+# Toggle console visibility
+func toggle_console() -> void:
+ logger.info("Toggling console visibility")
+ visible = !visible
# Input changed
func input_changed() -> void:
+ # Check for newline/enter
if input.text.contains("\n"):
input.text = input.text.replace("\n", "")
var input2: String = input.text
@@ -128,13 +83,30 @@ func input_changed() -> void:
input.editable = false
await process_command(input2.split(" ", false))
input.editable = true
+ return
+ # Highlight wrong/correct arbitrary gdscript expressions
+ if input.text.begins_with("arbitrary ") and input.text != "arbitrary ":
+ var expression_raw: String = " ".join(PackedStringArray(misc.get_shortened_array(input.text.split(" "), 0)))
+ var expression: Expression = Expression.new()
+ if expression.parse(expression_raw) == Error.OK: input.add_theme_color_override("font_color", Color.LIGHT_GREEN)
+ else: input.add_theme_color_override("font_color", Color.LIGHT_CORAL)
+ else: input.add_theme_color_override("font_color", Color8(255, 255, 255, 255))
+
+# Write to console output
+func append_output(text: String, newline: bool = true) -> void:
+ if newline: output.text = output.text + text + "\n"
+ else: output.text = output.text + text
# Process commands
+## This function is completely undocumented, looking a bit through the function and
+## reading the append_output() calls should give you a rough understanding though.
func process_command(command: PackedStringArray) -> void:
logger.diag("Processing command [" + " ".join(command) + "]")
await get_tree().process_frame
- append_output("[color=gray]$ " + " ".join(command) + "[color=white]")
- if command.size() == 0: return
+ if command.size() == 0:
+ append_output("[color=gray]$ *nothing*[color=white]\nI don't think that entering nothing does anything. Try running [b]help[/b].")
+ return
+ else: append_output("[color=gray]$ " + " ".join(command) + "[color=white]")
match(command[0]):
"clear":
match(command.size()):
@@ -154,7 +126,7 @@ To get started, enter \"help\". To close the console, press the X button.[color=
1: exitcode = 0
2: exitcode = int(command[1])
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_MANY_ARGUMENTS))
- pmana.shutdown(exitcode)
+ misc.shutdown(exitcode)
"help":
match(command.size()):
1: append_output(info.get_help_topic(ConsoleInfo.HelpTopic.INDEX))
@@ -215,59 +187,59 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
_:
match(command[2]):
"LOGGER_ENABLED":
- match(get_boolean(" ".join(get_last(command, 2)))):
- BooleanState.TRUE:
+ match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
+ misc.BooleanState.TRUE:
logger.config_enabled = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b].")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
logger.config_enabled = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_DIAGNOSTIC":
- match(get_boolean(" ".join(get_last(command, 2)))):
- BooleanState.TRUE:
+ match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
+ misc.BooleanState.TRUE:
logger.config_diagnostic = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
logger.config_diagnostic = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_COLORED":
- match(get_boolean(" ".join(get_last(command, 2)))):
- BooleanState.TRUE:
+ match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
+ misc.BooleanState.TRUE:
logger.config_colored = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
logger.config_colored = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_HARDFAIL":
- match(get_boolean(" ".join(get_last(command, 2)))):
- BooleanState.TRUE:
+ match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
+ misc.BooleanState.TRUE:
logger.config_hardfail = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
logger.config_hardfail = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_LOGSTRING":
- logger.config_logstring = " ".join(PackedStringArray(get_last(command, 2)))
+ logger.config_logstring = " ".join(PackedStringArray(misc.get_shortened_array(command, 2)))
append_output("Set \"" + str(command[2]) + "\" to [b]\"" + logger.config_logstring + "\"[/b]")
"PMANA_ALLOW_FULLSCREEN":
- match(get_boolean(" ".join(get_last(command, 2)))):
- BooleanState.TRUE:
+ match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
+ misc.BooleanState.TRUE:
pmana.config_allow_fullscreen = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
pmana.config_allow_fullscreen = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"MISC_SHUTDOWN_INVISIBLE":
- match(get_boolean(" ".join(get_last(command, 2)))):
- BooleanState.TRUE:
+ match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
+ misc.BooleanState.TRUE:
misc.config_shutdown_invisible = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
misc.config_shutdown_invisible = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
@@ -301,11 +273,11 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
5: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS))
6: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS))
7:
- var version: Vector2 = get_int_direct(command[2])
- var slides: Vector2 = get_int_direct(command[3])
- var animations: BooleanState = get_boolean(command[4])
+ var version: Vector2 = misc.get_int_direct(command[2])
+ var slides: Vector2 = misc.get_int_direct(command[3])
+ var animations: misc.BooleanState = misc.get_bool(command[4])
var animations_bool: bool = false
- var quit_last_slide: BooleanState = get_boolean(command[5])
+ var quit_last_slide: misc.BooleanState = misc.get_bool(command[5])
var quit_last_slide_bool: bool = false
var controller: NodePath = NodePath(command[6])
if version.x != 0:
@@ -315,14 +287,14 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "int"}))
return
match(animations):
- BooleanState.TRUE: animations_bool = true
- BooleanState.FALSE: animations_bool = false
+ misc.BooleanState.TRUE: animations_bool = true
+ misc.BooleanState.FALSE: animations_bool = false
_:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
return
match(quit_last_slide):
- BooleanState.TRUE: quit_last_slide_bool = true
- BooleanState.FALSE: quit_last_slide_bool = false
+ misc.BooleanState.TRUE: quit_last_slide_bool = true
+ misc.BooleanState.FALSE: quit_last_slide_bool = false
_:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
return
@@ -346,31 +318,31 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
3:
if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED))
else:
- var slide: Vector2i = get_int_direct(command[2])
+ var slide: Vector2i = misc.get_int_direct(command[2])
match(slide.x):
0:
pmana.change_slide(slide.y)
append_output("Switched to slide [b]" + str(slide.y) + "[/b]")
1: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "int"}))
- _: append_output(info.generate_internal_error("Invalid get_int() status number"))
+ _: append_output(info.generate_internal_error("Invalid misc.get_int() status number"))
4:
if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED))
else:
- var slide: Vector2i = get_int_direct(command[2])
+ var slide: Vector2i = misc.get_int_direct(command[2])
match(slide.x):
0:
- var no_animations: BooleanState = get_boolean(command[3])
+ var no_animations: misc.BooleanState = misc.get_bool(command[3])
match(no_animations):
- BooleanState.TRUE:
+ misc.BooleanState.TRUE:
pmana.change_slide(slide.y, true)
append_output("Switched to slide [b]" + str(slide.y) + "[/b] without animations")
- BooleanState.FALSE:
+ misc.BooleanState.FALSE:
pmana.change_slide(slide.y, false)
append_output("Switched to slide [b]" + str(slide.y) + "[/b]")
- BooleanState.INVALID:
+ misc.BooleanState.INVALID:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
1: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "int"}))
- _: append_output(info.generate_internal_error("Invalid get_int() status number"))
+ _: append_output(info.generate_internal_error("Invalid misc.get_int() status number"))
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_MANY_ARGUMENTS))
"get_slide":
if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED))
@@ -423,7 +395,7 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
match(command.size()):
1: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS))
_:
- var expression_raw: String = " ".join(PackedStringArray(get_last(command, 0)))
+ var expression_raw: String = " ".join(PackedStringArray(misc.get_shortened_array(command, 0)))
var expression: Expression = Expression.new()
if expression.parse(expression_raw) != Error.OK:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.EXPRESSION_PARSING_FAILED, {"error": expression.get_error_text()}))
@@ -435,48 +407,3 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
append_output("""Executed arbitrary expression successfully.
Returned: """ + str(returned))
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_COMMAND))
-
-# Helper function. I'm bad at explaining and naming things, just look at the code... please.
-func get_last(array: Array, n: int) -> Array:
- # Makes it easier
- n = n+1
- # Create new array
- var new_array: Array = []
- if n >= array.size():
- logger.error("n is bigger than array.size()")
- return []
- for value in array:
- if n == 0: # Append to new_array
- new_array.append(value)
- else: # Count down
- n = n-1
- return new_array
-
-# Helper function. Tries to get a bool from an Array.
-func get_boolean(string: String) -> BooleanState:
- match(string):
- "true": return BooleanState.TRUE
- "false": return BooleanState.FALSE
- _: return BooleanState.INVALID
-
-# Helper function. Tries to get a int from an Array.
-## Returns Vector2i(0, NUMBER) if successful
-## Returns Vector2i(1, 0) if not successful
-func get_int(array: Array) -> Vector2i:
- for value in array:
- if str(value).is_valid_int(): return Vector2i(0, int(value))
- return Vector2i(1, 0)
-
-# Helper function. Tries to get a int from a String.
-## Returns Vector2i(0, NUMBER) if successful
-## Returns Vector2i(1, 0) if not successful
-func get_int_direct(string: String) -> Vector2i:
- if string.is_valid_int(): return Vector2i(0, int(string))
- else: return Vector2i(1, 0)
-
-# Helper function. Appends text to output.text
-func append_output(text: String, newline: bool = true) -> void:
- if newline:
- output.text = output.text + text + "\n"
- else:
- output.text = output.text + text
diff --git a/src/console_info.gd b/src/console_info.gd
index f566e74..5af7f46 100644
--- a/src/console_info.gd
+++ b/src/console_info.gd
@@ -24,6 +24,7 @@
extends Node
class_name ConsoleInfo
+# Enums
enum ConsoleError {
OK,
UNIMPLEMENTED,
@@ -55,57 +56,38 @@ enum HelpTopic {
ARBITRARY
}
+# Variables
var error_color: String = "[color=red]"
var internal_error_color: String = "[b][color=red]"
-func _init() -> void:
- logger.diag("ConsoleInfo initialized")
-
+# Generates an internal error
func generate_internal_error(error: String) -> String:
var origin = misc.get_origin()
logger.warn("Debug console (" + origin["file"] + ":" + origin["function"] + ":" + str(origin["line"]) + ") experienced an internal error: " + error)
return internal_error_color + "INTERNAL ERROR (" + error + ") [" + origin["file"] + ":" + origin["function"] + ":" + str(origin["line"]) + "]"
+# Converts ConsoleError into a String
func get_error_string(error: ConsoleError, context: Dictionary = {}) -> String:
match(error):
- ConsoleError.OK:
- return generate_internal_error("ConsoleError.OK is not a valid error")
- ConsoleError.UNIMPLEMENTED:
- return error_color + "Function unimplemented, aborting."
- ConsoleError.INVALID_COMMAND:
- return error_color + "Invalid command. Execute \"help\" for a list of all available commands."
- ConsoleError.TOO_MANY_ARGUMENTS:
- return error_color + "Too many arguments."
- ConsoleError.TOO_FEW_ARGUMENTS:
- return error_color + "Too few arguments."
- ConsoleError.INVALID_ARGUMENT:
- return error_color + "Invalid argument."
- ConsoleError.INVALID_TYPE:
- return error_color + "Invalid type. Command expected type " + str(context["expected_type"]) + "."
- ConsoleError.NOT_AN_OBJECT:
- return error_color + "Invalid type. Command expected a valid NodePath."
- ConsoleError.NO_SCRIPT_ATTACHED:
- return error_color + "Invalid object. Command expected a script-attached object."
- ConsoleError.INVALID_HELP_TOPIC:
- return error_color + "Invalid help topic."
- ConsoleError.INVALID_CONFIG_KEY:
- return error_color + "Invalid config key \"" + str(context["key"]) + "\". Execute \"config list\" for a list of all config keys."
- ConsoleError.NO_PRESENTATION_OPEN:
- return error_color + "No presentation is currently open."
- ConsoleError.CONTROLLER_ALREADY_REGISTERED:
- return error_color + "A presentation controller has been registered already."
- ConsoleError.NO_CONTROLLER_REGISTERED:
- return error_color + "No presentation controller has been registered yet."
- ConsoleError.EXPRESSION_PARSING_FAILED:
- return error_color + """Could not parse arbitrary expression successfully.
-Error: """ + str(context["error"])
- ConsoleError.EXPRESSION_EXECUTION_FAILED:
- return error_color + """Could not execute arbitrary expression successfully.
-Error: """ + str(context["error"]) + """
-Returned: """ + str(context["returned"])
- _:
- return generate_internal_error("Invalid ConsoleError \"" + str(error) + "\", context: " + str(context))
+ ConsoleError.OK: return generate_internal_error("ConsoleError.OK is not a valid error")
+ ConsoleError.UNIMPLEMENTED: return error_color + "Function unimplemented, aborting."
+ ConsoleError.INVALID_COMMAND: return error_color + "Invalid command. Execute \"help\" for a list of all available commands."
+ ConsoleError.TOO_MANY_ARGUMENTS: return error_color + "Too many arguments."
+ ConsoleError.TOO_FEW_ARGUMENTS: return error_color + "Too few arguments."
+ ConsoleError.INVALID_ARGUMENT: return error_color + "Invalid argument."
+ ConsoleError.INVALID_TYPE: return error_color + "Invalid type. Command expected type " + str(context["expected_type"]) + "."
+ ConsoleError.NOT_AN_OBJECT: return error_color + "Invalid type. Command expected a valid NodePath."
+ ConsoleError.NO_SCRIPT_ATTACHED: return error_color + "Invalid object. Command expected a script-attached object."
+ ConsoleError.INVALID_HELP_TOPIC: return error_color + "Invalid help topic."
+ ConsoleError.INVALID_CONFIG_KEY: return error_color + "Invalid config key \"" + str(context["key"]) + "\". Execute \"config list\" for a list of all config keys."
+ ConsoleError.NO_PRESENTATION_OPEN: return error_color + "No presentation is currently open."
+ ConsoleError.CONTROLLER_ALREADY_REGISTERED: return error_color + "A presentation controller has been registered already."
+ ConsoleError.NO_CONTROLLER_REGISTERED: return error_color + "No presentation controller has been registered yet."
+ ConsoleError.EXPRESSION_PARSING_FAILED: return error_color + "Could not parse arbitrary expression successfully.\nError: " + str(context["error"])
+ ConsoleError.EXPRESSION_EXECUTION_FAILED: return error_color + "Could not execute arbitrary expression successfully.\nError: " + str(context["error"]) + "\nReturned: " + str(context["returned"])
+ _: return generate_internal_error("Invalid ConsoleError \"" + str(error) + "\", context: " + str(context))
+# Converts HelpTopic into a String
func get_help_topic(topic: HelpTopic) -> String:
match(topic):
HelpTopic.INDEX:
@@ -158,5 +140,4 @@ Calls functions belonging to the Presentation Reader"""
Executes arbitrary GDScript expressions.
-> EXPERIMENTAL"""
- _:
- return generate_internal_error("Invalid HelpTopic \"" + str(topic) + "\"")
+ _: return generate_internal_error("Invalid HelpTopic \"" + str(topic) + "\"")
diff --git a/src/loader.gd b/src/loader.gd
index 3ab5456..eeaf519 100644
--- a/src/loader.gd
+++ b/src/loader.gd
@@ -23,21 +23,26 @@
##############################################################################
extends Control
-# Loader configuration
-## Window size support
-### This is/was used for configuring scaling
-var config_window_size_support: bool = false
-## Skip malicious scripts warning if running as debug build
-var config_skipwarning: bool = true
-## Slow init
-### I don't know why I put this here lol
-var config_slow_init: bool = false
+# Enums
+enum VersionType {
+ RELEASE,
+ RELEASECANDIDATE,
+ BETA,
+ ALPHA
+}
+
+# Constants
+const version_release: int = 1
+const version_type: VersionType = VersionType.ALPHA
+const version_typerelease: int = 2
# Nodes
var logrtl: RichTextLabel = null
var console: Control = null
func _ready() -> void:
+ #while true: await get_tree().create_timer(0.05).timeout
+ misc.set_main_window_visibility(false)
logger.info("Updating loader scene")
# Rename loader scene
name = "Presencode"
@@ -62,7 +67,7 @@ func _ready() -> void:
logrtl.add_theme_font_size_override("normal_font_size", 14)
logrtl.add_theme_font_size_override("bold_font_size", 14)
## Connect to logger
- logger.connect("log_event", Callable(self, "append_log"))
+ logger.connect("log_event", func(_type: logger.Types, _message: String, logstring: String) -> void: logrtl.text = get_node("/root/Presencode/Log").text + logstring + "\n")
## Add to SceneTree
add_child(logrtl)
## Remove VScrollBar
@@ -74,14 +79,6 @@ func _ready() -> void:
vsbar.add_theme_stylebox_override("grabber", StyleBoxEmpty.new())
vsbar.add_theme_stylebox_override("grabber_highlight", StyleBoxEmpty.new())
vsbar.add_theme_stylebox_override("grabber_pressed", StyleBoxEmpty.new())
- ## Window size support
- if config_window_size_support:
- $WindowSizeSupport.modulate = Color8(255, 255, 255, 255)
- else:
- $WindowSizeSupport.modulate = Color8(255, 255, 255, 0)
- ## Update process_mode
- logger.process_mode = Node.PROCESS_MODE_ALWAYS
- misc.process_mode = Node.PROCESS_MODE_ALWAYS
## Initialize Presencode
initialize()
@@ -98,24 +95,77 @@ func initialize() -> void:
Copyright (c) 2024 JeremyStarTM & Contributers
Licensed under the GNU General Public License version 3
""")
- if config_slow_init: await get_tree().create_timer(randf_range(0.2, 0.6)).timeout
# Check for platform
misc.check_platform()
# Create temporary directory
DirAccess.make_dir_recursive_absolute(misc.get_temporary_dir())
+ # Load debug console
logger.info("Injecting console")
console = ResourceLoader.load("res://Console.tscn").instantiate()
get_tree().root.add_child.call_deferred(console)
- if config_slow_init: await get_tree().create_timer(randf_range(0.1, 0.15)).timeout
- # Check for presentation path in commandline arguments
- # Open presentation archive/directory
- var path: String = " ".join(OS.get_cmdline_user_args())
- if OS.get_cmdline_user_args().size() == 0:
+ # Initialize processor
+ processor.initialize.call_deferred()
+ # Process arguments
+ var path: String = parse_arguments()
+ # Update window properties
+ misc.set_main_window_visibility(true)
+ # Check path variable
+ if path == "":
ui_engine.initialize(self)
else:
- await print_warning()
await load_presentation(path)
+# Process commandline arguments
+func parse_arguments() -> String:
+ var path: String = ""
+ var next_argument_value: String = ""
+ for arg in OS.get_cmdline_user_args():
+ match(next_argument_value):
+ "logger_enabled":
+ match(misc.get_bool(arg)):
+ misc.BooleanState.TRUE: logger.config_enabled = true
+ misc.BooleanState.FALSE: logger.config_enabled = false
+ _: logger.error("Value \"" + arg + "\" does not match \"true\" or \"false\"")
+ "logger_diagnostic":
+ match(misc.get_bool(arg)):
+ misc.BooleanState.TRUE: logger.config_diagnostic = true
+ misc.BooleanState.FALSE: logger.config_diagnostic = false
+ _: logger.error("Value \"" + arg + "\" does not match \"true\" or \"false\"")
+ "logger_colored":
+ match(misc.get_bool(arg)):
+ misc.BooleanState.TRUE: logger.config_colored = true
+ misc.BooleanState.FALSE: logger.config_colored = false
+ _: logger.error("Value \"" + arg + "\" does not match \"true\" or \"false\"")
+ "logger_hardfail":
+ match(misc.get_bool(arg)):
+ misc.BooleanState.TRUE: logger.config_hardfail = true
+ misc.BooleanState.FALSE: logger.config_hardfail = false
+ _: logger.error("Value \"" + arg + "\" does not match \"true\" or \"false\"")
+ "logger_logstring": logger.config_logstring = arg
+ "misc_shutdown_invisible":
+ match(misc.get_bool(arg)):
+ misc.BooleanState.TRUE: misc.config_shutdown_invisible = true
+ misc.BooleanState.FALSE: misc.config_shutdown_invisible = false
+ _: logger.error("Value \"" + arg + "\" does not match \"true\" or \"false\"")
+ if next_argument_value != "":
+ next_argument_value = ""
+ continue
+ match(arg):
+ "--help": continue
+ "--logger-enabled": next_argument_value = "logger_enabled"
+ "--logger-diagnostic": next_argument_value = "logger_diagnostic"
+ "--logger-colored": next_argument_value = "logger_colored"
+ "--logger-hardfail": next_argument_value = "logger_hardfail"
+ "--logger-logstring": next_argument_value = "logger_logstring"
+ "--shutdown-invisible": next_argument_value = "misc_shutdown_invisible"
+ _:
+ # doesn't match any of the above, append to path instead
+ ## this causes an interesting side effect, which is best explained by an example:
+ ## C:/Users/Example User/Documents/Important --shutdown-invisible false Documents/Example --logger-colored false Presentation.pcpa
+ if path == "": path = arg
+ else: path = path + " " + arg
+ return path
+
# Load a presentation from commandline arguments
func load_presentation(path: String) -> void:
if FileAccess.file_exists(path) and path.ends_with(".zip") or path.ends_with(".pcpa"): # .pcpa = presencode presentation archive
@@ -126,81 +176,52 @@ func load_presentation(path: String) -> void:
preader.open_presentation(path, false)
else:
await logger.error("Presentation file/directory \"" + path + "\" not found")
- if config_slow_init: await get_tree().create_timer(randf_range(0.6, 1)).timeout
+ return
# Read manifest & entrypoint files
preader.read_manifest()
preader.read_entrypoint()
# Update window properties
- console.calculate_drag_area(preader.get_ratio_resolution())
+ processor.update_dragging_area(processor.DragNode.CONSOLE, 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 config_slow_init: await get_tree().create_timer(randf_range(0.1, 0.3)).timeout
- if config_window_size_support: $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()))
- if config_slow_init: await get_tree().create_timer(randf_range(1, 1.5)).timeout
# Add entrypoint to SceneTree
get_tree().root.add_child(preader.get_entrypoint())
-# seeeeeeeeeeeeelf explanitory
-func _process(_delta: float) -> void:
- # Move to top
- get_tree().root.move_child(self, get_tree().root.get_child_count(true)-1)
- # Change sizes
- size = DisplayServer.window_get_size()
- if typeof(logrtl) == TYPE_OBJECT:
- logrtl.size = DisplayServer.window_get_size()
- if config_window_size_support: move_child($WindowSizeSupport, get_child_count(true))
- # Fullscreen key combo
- if pmana.config_allow_fullscreen and Input.is_action_just_pressed("fullscreen"):
- if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_WINDOWED:
- # window is windowed, set to fullscreen mode
- logger.info("Switched to fullscreen mode")
- DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
- else:
- # window is in fullscreen (or something else) mode, set to windowed mode
- logger.info("Switched to windowed mode")
- DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
- # Content scale switch combo
- if Input.is_action_just_pressed("content_scale_switch"):
- match(get_tree().root.content_scale_stretch):
- Window.CONTENT_SCALE_STRETCH_FRACTIONAL:
- logger.info("Switching to fractional scaling")
- get_tree().root.content_scale_stretch = Window.CONTENT_SCALE_STRETCH_INTEGER
- Window.CONTENT_SCALE_STRETCH_INTEGER:
- logger.info("Switching to integer scaling")
- get_tree().root.content_scale_stretch = Window.CONTENT_SCALE_STRETCH_FRACTIONAL
- _:
- logger.error("Invalid content_scale_stretch value")
-
-# Append log output to logrtl.text
-func append_log(_type: logger.Types, _message: String, log_str: String) -> void:
- logrtl.text = $Log.text + log_str + "\n"
-
-func print_warning() -> void:
- if !config_skipwarning and OS.is_debug_build():
- logger.warn("""Displaying warning
-###################################################
-##### !!! WARNING !!! WARNING !!! WARNING !!! #####
-###################################################
- Presentations made with Presencode perform
- malicious actions such as collect your passwords,
- encrypt your files, display you unwanted ads,
- install other malware, etc..
-
-ONLY VIEW PRESENCODE PRESENTATIONS IF YOU TRUST THE
- AUTHOR AND HAVE VERIFIED THE PRESENTATION SCRIPT!
- YOU HAVE 5 SECONDS TO EXIT PRESENCODE
-
- YOU HAVE BEEN WARNED.
-###################################################
-##### !!! WARNING !!! WARNING !!! WARNING !!! #####
-###################################################
-""")
- await get_tree().create_timer(5).timeout
+func format_version(format: String) -> String:
+ var version: String = format
+ var version_type_normal: String = ""
+ var version_type_short: String = ""
+ var version_type_technical: String = ""
+ match(version_type):
+ VersionType.RELEASE:
+ version_type_normal = "Release"
+ version_type_short = "Release"
+ version_type_technical = "r"
+ VersionType.RELEASECANDIDATE:
+ version_type_normal = "Releasecandidate"
+ version_type_short = "RC"
+ version_type_technical = "rc"
+ VersionType.BETA:
+ version_type_normal = "Beta"
+ version_type_short = "Beta"
+ version_type_technical = "b"
+ VersionType.ALPHA:
+ version_type_normal = "Alpha"
+ version_type_short = "Alpha"
+ version_type_technical = "a"
+ _: logger.info("Invalid version type \"" + str(version_type) + "\"")
+ version = version.replace("%release%", str(version_release))
+ version = version.replace("%type_raw%", str(version_type))
+ version = version.replace("%type%", version_type_normal)
+ version = version.replace("%type_short%", version_type_short)
+ version = version.replace("%type_technical%", version_type_technical)
+ version = version.replace("%typerelease%", str(version_typerelease))
+ return version
diff --git a/src/logger.gd b/src/logger.gd
index 2f9e2f2..1f8ed02 100644
--- a/src/logger.gd
+++ b/src/logger.gd
@@ -35,7 +35,9 @@ var config_enabled: bool = true
var config_diagnostic: bool = true
## Toggle colored output
var config_colored: bool = true
-## Exit Presencode on error? (don't set this to false)
+## Makes errors fail hard
+### Shuts Presencode down if error() is called
+### HIGHLY RECOMMENDED TO LEAVE ENABLED
var config_hardfail: bool = true
## Logging template
### Variables (begin and end with '%'): runtime, time, file, function, line, color, type, message
diff --git a/src/misc.gd b/src/misc.gd
index 029f8fd..d4ae747 100644
--- a/src/misc.gd
+++ b/src/misc.gd
@@ -35,8 +35,13 @@ enum Error {
MANIFEST_INVALID_PROGRAM,
MANIFEST_INVALID_RATIO
}
+enum BooleanState {
+ TRUE,
+ FALSE,
+ INVALID
+}
-# Manifest specification
+# Constants
const manifest_version: int = 1
const manifest_program: String = "Presencode"
@@ -44,45 +49,46 @@ const manifest_program: String = "Presencode"
## Make window invisible on shutdown
### Hides the main window on shutdown if true
### Displays RenderingServer.get_white_texture() if false instead
-var config_shutdown_invisible: bool = true
+var config_shutdown_invisible: bool = false
-# Get call origin from stacktrace
-func get_origin(n: int = 0) -> Dictionary:
- var stack: Dictionary = get_stack()[2+n]
- return {"file": stack["source"].replace("user://", "").replace("res://", ""), "line": stack["line"], "function": stack["function"]}
+# Helper function to retrieve a bool from a String
+func get_bool(string: String) -> BooleanState:
+ match(string):
+ "true": return BooleanState.TRUE
+ "false": return BooleanState.FALSE
+ _: return BooleanState.INVALID
-# Shutdown Presencode safely
-func shutdown(exitcode: int = 0) -> void:
- logger.info("Shutting down (code " + str(exitcode) + ")")
- get_tree().paused = true
- # Display white texture
- var npr: NinePatchRect = NinePatchRect.new()
- npr.name = "OverlayTexture"
- npr.texture = ImageTexture.create_from_image(RenderingServer.texture_2d_get(RenderingServer.get_white_texture()))
- npr.size = Vector2(100000, 100000)
- npr.position = Vector2(-50000, -50000)
- get_tree().root.add_child(npr)
- get_tree().root.move_child(npr, get_tree().root.get_child_count(true))
- # Window stuff
- if config_shutdown_invisible:
- # Make window invisible
- DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
- DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)
- DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_RESIZE_DISABLED, true)
- DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_NO_FOCUS, true)
- DisplayServer.window_set_min_size(Vector2i(1, 1))
- DisplayServer.window_set_size(Vector2i(1, 1))
- DisplayServer.window_set_position(Vector2i(0, 0))
- logger.diag("Removing temporary directory")
- # Remove tempdir
- DirAccess.remove_absolute(get_temporary_dir())
- # Wait for all log messages to be printed to console/log
- logger.diag("Waiting for log messages")
- await get_tree().create_timer(0.25, true).timeout
- print("Exiting!")
- get_tree().quit(exitcode)
- # Insanely long timer to prevent Godot from executing code during exit
- await get_tree().create_timer(999, true).timeout
+# Helper function to retrieve the next valid int from an Array
+## Returns Vector2i(0, NUMBER) if successful
+## Returns Vector2i(1, 0) if not successful
+func get_int(array: Array) -> Vector2i:
+ for value in array:
+ if str(value).is_valid_int(): return Vector2i(0, int(value))
+ return Vector2i(1, 0)
+
+# Helper function to retrieve a int from a String
+## Returns Vector2i(0, NUMBER) if successful
+## Returns Vector2i(1, 0) if not successful
+func get_int_direct(string: String) -> Vector2i:
+ if string.is_valid_int(): return Vector2i(0, int(string))
+ else: return Vector2i(1, 0)
+
+# Returns a shortened array, by skipping "skip" Items
+func get_shortened_array(array: Array, skip: int) -> Array:
+ # We want to remove at least one item
+ skip = skip+1
+ # Create new array
+ var new_array: Array = []
+ if skip >= array.size():
+ logger.error("n is bigger than array.size()")
+ return []
+ # Loop through array
+ for value in array:
+ if skip == 0: # Append to new_array
+ new_array.append(value)
+ else: # Count down
+ skip = skip-1
+ return new_array
# Calculate the center of a child inside its parent (Vector2i)
func get_center(parent_size: Vector2i, child_size: Vector2i) -> Vector2i:
@@ -105,26 +111,10 @@ func get_sign_float(number: float) -> float:
elif number < 0: return -1
else: return 0
-# Return path to temporary directory
-## This function tries to utilize the operating system's temporary directory.
-## If all of them can't be used, it falls back to "user://temp" instead.
-func get_temporary_dir() -> String:
- match(OS.get_name()):
- "Linux": # Use "/tmp" or fallback
- if DirAccess.dir_exists_absolute("/tmp"):
- return "/tmp/presencode"
- else:
- return "user://temp"
- "Windows": # Use "%USERPROFILE%/AppData/Local/Temp/Presencode" or "C:/Users/%USERNAME%/AppData/Local/Temp/Presencode" or fallback
- if OS.get_environment("USERPROFILE") != "" and DirAccess.dir_exists_absolute(OS.get_environment("USERPROFILE").replace("\\", "/")):
- return OS.get_environment("USERPROFILE").replace("\\", "/") + "/AppData/Local/Temp/Presencode"
- elif OS.get_environment("USERNAME") != "" and DirAccess.dir_exists_absolute("C:/Users/" + OS.get_environment("USERNAME")):
- return "C:/Users/" + OS.get_environment("USERNAME") + "/AppData/Local/Temp/Presencode"
- else:
- return "user://temp"
- _: # Platform not supported
- logger.warn("The " + OS.get_name() + " platform is not supported by Presencode. You can add support for that platform to Presencode yourself, if you want.")
- return ""
+# Get call origin from stacktrace
+func get_origin(n: int = 0) -> Dictionary:
+ var stack: Dictionary = get_stack()[2+n]
+ return {"file": stack["source"].replace("user://", "").replace("res://", ""), "line": stack["line"], "function": stack["function"]}
# Check platform support
func check_platform() -> void:
@@ -149,7 +139,70 @@ func get_best_resolution() -> Vector2i:
if closest_resolution == Vector2(NAN, NAN): return Vector2i(960, 540)
else: return closest_resolution
-# Check for manifest consistency
+# Return path to temporary directory
+## This function tries to utilize the operating system's temporary directory.
+## If all of them can't be used, it falls back to "user://temp" instead.
+func get_temporary_dir() -> String:
+ match(OS.get_name()):
+ "Linux": # Use "/tmp" or fallback
+ if DirAccess.dir_exists_absolute("/tmp"):
+ return "/tmp/presencode"
+ else:
+ return "user://temp"
+ "Windows": # Use "%USERPROFILE%/AppData/Local/Temp/Presencode" or "C:/Users/%USERNAME%/AppData/Local/Temp/Presencode" or fallback
+ if OS.get_environment("USERPROFILE") != "" and DirAccess.dir_exists_absolute(OS.get_environment("USERPROFILE").replace("\\", "/")):
+ return OS.get_environment("USERPROFILE").replace("\\", "/") + "/AppData/Local/Temp/Presencode"
+ elif OS.get_environment("USERNAME") != "" and DirAccess.dir_exists_absolute("C:/Users/" + OS.get_environment("USERNAME")):
+ return "C:/Users/" + OS.get_environment("USERNAME") + "/AppData/Local/Temp/Presencode"
+ else:
+ return "user://temp"
+ _: # Platform not supported
+ logger.warn("The " + OS.get_name() + " platform is not supported by Presencode. You can add support for that platform to Presencode yourself, if you want.")
+ return ""
+
+# Shutdown Presencode safely
+func shutdown(exitcode: int = 0) -> void:
+ logger.info("Shutting down (code " + str(exitcode) + ")")
+ get_tree().paused = true
+ # Call controller's presentation_end() function if a controller is registered
+ if pmana.registered: await pmana.controller.presentation_end()
+ # Display white texture
+ var npr: NinePatchRect = NinePatchRect.new()
+ npr.name = "OverlayTexture"
+ npr.texture = ImageTexture.create_from_image(RenderingServer.texture_2d_get(RenderingServer.get_white_texture()))
+ npr.size = Vector2(100000, 100000)
+ npr.position = Vector2(-50000, -50000)
+ get_tree().root.add_child(npr)
+ get_tree().root.move_child(npr, get_tree().root.get_child_count(true))
+ # Set window properties
+ if config_shutdown_invisible: set_main_window_visibility(false)
+ # Remove temporary directory
+ logger.diag("Removing temporary directory")
+ DirAccess.remove_absolute(get_temporary_dir())
+ # Wait for all log messages to be printed to console/log
+ logger.diag("Waiting for log messages")
+ await get_tree().create_timer(0.25, true).timeout
+ print("Exiting!")
+ get_tree().quit(exitcode)
+ # Insanely long timer to prevent Godot from executing code during exit
+ await get_tree().create_timer(999, true).timeout
+
+# Makes the window invisible/visible
+func set_main_window_visibility(visible: bool) -> void:
+ if DisplayServer.window_get_mode() != DisplayServer.WINDOW_MODE_WINDOWED: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED, true)
+ DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, !visible)
+ DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_RESIZE_DISABLED, !visible)
+ DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_NO_FOCUS, !visible)
+ if visible:
+ DisplayServer.window_set_min_size(Vector2i(960, 540))
+ DisplayServer.window_set_size(Vector2i(960, 540))
+ DisplayServer.window_set_position(get_center(DisplayServer.screen_get_size(), DisplayServer.window_get_size()))
+ else:
+ DisplayServer.window_set_min_size(Vector2i(1, 1))
+ DisplayServer.window_set_size(Vector2i(1, 1))
+ DisplayServer.window_set_position(Vector2i(0, 0))
+
+# Checks for manifest consistency
func check_manifest_consistency(manifest: Dictionary) -> misc.Error:
logger.diag("Checking manifest for consistency")
diff --git a/src/pmana.gd b/src/pmana.gd
index 3bbd2b1..aad4faf 100644
--- a/src/pmana.gd
+++ b/src/pmana.gd
@@ -26,6 +26,10 @@ extends Node
# Constants
const entrypoint_version: int = 1
+# Nodes
+var clickoverlay: Button = null
+var viewport: Control = null
+
# Configuration
var config_allow_fullscreen: bool = true
@@ -39,8 +43,7 @@ var quit_last_slide: bool = true
var registered: bool = false
var current_slide: int = -1
var animation_active: bool = false
-var clickoverlay: Button = null
-var viewport: Control = null
+var action_running: bool = false
# Register a controller
func register(version: int, slides_: int, animations_: bool, quit_last_slide_: bool, controller_: NodePath) -> void:
@@ -55,11 +58,13 @@ func register(version: int, slides_: int, animations_: bool, quit_last_slide_: b
if get_node(controller_) == null:
await logger.error("The presentation controller could not be located.")
# Set controller information
+ logger.diag("Setting controller information")
slides = slides_
animations = animations_
quit_last_slide = quit_last_slide_
controller = get_node(controller_)
- # Check for essential functions
+ # Check for required functions
+ logger.diag("Check for required functions")
var lacking_functions: Array = []
var check_functions: Array = ["change_slide", "presentation_start", "presentation_end"] ## Base functions
## "display_end_slide" function
@@ -80,19 +85,21 @@ func register(version: int, slides_: int, animations_: bool, quit_last_slide_: b
logger.warn("Consult the documentation or take a look at the example presentation if you need help.")
await misc.shutdown(1)
# Create presentation viewport
+ logger.diag("Creating presentation viewport")
viewport = Control.new()
viewport.name = "Viewport"
viewport.size = preader.get_ratio_resolution()
viewport.position = Vector2(0, 0)
get_node("/root/Presencode").add_child(viewport)
# Create clickoverlay
+ logger.diag("Creating clickoverlay")
clickoverlay = Button.new()
clickoverlay.name = "ClickOverlay"
clickoverlay.set_script(ResourceLoader.load("res://src/clickoverlay.gd"))
get_node("/root/Presencode").add_child(clickoverlay)
# Did you know that every big galaxy contains a massive black hole in it's center?
registered = true
- # Invoke presentation_start()
+ # Invoke controller's presentation_start()
controller.presentation_start(viewport)
# Unregister a controller
@@ -101,17 +108,23 @@ func unregister() -> void:
logger.warn("No presentation controller has been registered yet. Please call register() first.")
return
logger.info("Unregistering presentation controller")
- # Reset to default values
+ while action_running:
+ logger.warn("Waiting for action to finish")
+ await get_tree().create_timer(1).timeout
+ # Reset variables to default values
+ logger.diag("Resetting variables")
registered = false
current_slide = -1
animation_active = false
config_allow_fullscreen = true
- # Remove clickoverlay
+ # Remove nodes
+ logger.diag("Removing node")
+ ## Remove clickoverlay
get_node("/root/Presencode/").remove_child(clickoverlay)
clickoverlay = null
+ ## Remove viewport
get_node("/root/Presencode/").remove_child(viewport)
viewport = null
- await get_tree().process_frame
# Change slide
func change_slide(slide: int, no_animations: bool = false) -> void:
@@ -131,7 +144,7 @@ func change_slide(slide: int, no_animations: bool = false) -> void:
# Quit after last slide?
if quit_last_slide:
logger.info("Ending presentation")
- await shutdown()
+ await misc.shutdown(0)
return
else:
logger.warn("Over max slides by " + str(slide-slides))
@@ -142,33 +155,30 @@ func change_slide(slide: int, no_animations: bool = false) -> void:
return
else: # End presentation (slide is over max slides by two or more)
logger.info("Ending presentation")
- await shutdown()
+ await misc.shutdown(0)
return
# Slide is not over max slides, continue slide change
+ action_running = true
# Play switch away animation
if animations and !no_animations:
await controller.animation_switch_away(current_slide, slide)
# Check if controller has unregistered during animation
- if !registered:
- logger.warn("The current presentation controller has been unregistered during execution, aborting")
- return
+ if !registered: logger.error("The current presentation controller has been unregistered during execution")
# Change slide
await controller.change_slide(slide)
- # Check if controller has unregistered during animation
- if !registered:
- logger.warn("The current presentation controller has been unregistered during execution, aborting")
- return
+ # Check if controller has unregistered during slide change
+ if !registered: logger.error("The current presentation controller has been unregistered during execution")
# Set current_slide to new slide
current_slide = slide
# Play switch to animation
- if animations and !no_animations:
- await controller.animation_switch_to(current_slide, slide)
+ if animations and !no_animations: await controller.animation_switch_to(current_slide, slide)
# Clear the viewport
func clear_viewport() -> void:
if !registered:
logger.warn("No presentation controller has been registered yet. Please call register() first.")
return
+ logger.diag("Clearing viewport")
if viewport.get_child_count(true) == 0:
logger.warn("No children found in viewport")
return
@@ -180,6 +190,7 @@ func hide_log() -> void:
if !registered:
logger.warn("No presentation controller has been registered yet. Please call register() first.")
return
+ logger.diag("Hiding log")
get_node("/root/Presencode/Log").modulate = Color8(255, 255, 255, 0)
# Show log output
@@ -187,27 +198,5 @@ func show_log() -> void:
if !registered:
logger.warn("No presentation controller has been registered yet. Please call register() first.")
return
+ logger.diag("Showing log")
get_node("/root/Presencode/Log").modulate = Color8(255, 255, 255, 255)
-
-# Helper function. Shutdown Presencode
-func shutdown(exitcode: int = 0) -> void:
- logger.diag("Shutting down Presencode from Presentation Manager")
- if registered: await controller.presentation_end()
- await misc.shutdown(exitcode)
-
-# Runs every frame (duh)
-func _process(_delta: float) -> void:
- if !registered: return
- # Navigation key combos
- if Input.is_action_pressed("slide_forwards") and Input.is_action_pressed("slide_backwards"): return
- else:
- if get_node_or_null("/root/Console") == null or !get_node("/root/Console").visible:
- if Input.is_action_just_pressed("slide_forwards"):
- # Increase current_slide by one
- logger.info("Navigating one slide forwards (key)")
- change_slide(current_slide+1)
- elif Input.is_action_just_pressed("slide_backwards"):
- # Decrease current_slide by one
- logger.info("Navigating one slide backwards (key)")
-
- change_slide(current_slide-1)
diff --git a/src/preader.gd b/src/preader.gd
index 6fc639d..b923110 100644
--- a/src/preader.gd
+++ b/src/preader.gd
@@ -25,7 +25,10 @@
##############################################################################
extends Node
-# Reading
+# States
+var is_open: bool = false
+
+# Reading support
var directorypath: String = ""
var ziphandler: ZIPReader = ZIPReader.new()
@@ -33,10 +36,7 @@ var ziphandler: ZIPReader = ZIPReader.new()
var manifest: Dictionary = {}
var entrypoint: Node = null
-# States
-var is_open: bool = false
-
-# Opening a presentation
+# Open a presentation archive/directory
func open_presentation(path: String, zip: bool) -> Error:
if is_open:
logger.warn("Another presentation is still in memory")
@@ -53,7 +53,7 @@ func open_presentation(path: String, zip: bool) -> Error:
is_open = true
return Error.OK
-# Close the currently open presentation
+# Close opened presentation
func close_presentation() -> Error:
if !is_open:
logger.warn("No presentation is currently opened")
@@ -120,14 +120,11 @@ func read_resource(path: String) -> Resource:
file.store_buffer(resource_bytes)
file.close()
var resource: Resource = null
- if path.ends_with(".ttf") or path.ends_with(".otf") or path.ends_with(".woff") or path.ends_with(".woff2"):
- resource = FontFile.new()
- resource.load_dynamic_font(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1]))
- elif path.ends_with(".png") or path.ends_with(".jpg") or path.ends_with(".svg") or path.ends_with(".ktx") or path.ends_with(".tga") or path.ends_with(".webp"):
- resource = Image.load_from_file(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1]))
- else:
+ # Attempt workaround to mitigate loading issues
+ resource = _read_resource_workaround(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1]))
+ if resource == null: # Workaround did not work, load resource normally
resource = ResourceLoader.load(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1]))
- if resource == null:
+ if resource == null: # Resource could not be read
logger.warn("Resource could not be read, resource is null")
return null
return resource
@@ -135,13 +132,29 @@ func read_resource(path: String) -> Resource:
if !FileAccess.file_exists(directorypath + "/" + path):
logger.error("Requested resource is missing in presentation directory")
return null
- var resource: Resource = ResourceLoader.load(directorypath + "/" + path)
- if resource == null:
+ var resource: Resource = null
+ # Attempt workaround to mitigate loading issues
+ resource = _read_resource_workaround(directorypath + "/" + path)
+ if resource == null: # Workaround did not work, load resource normally
+ resource = ResourceLoader.load(directorypath + "/" + path)
+ if resource == null: # Resource could not be read
logger.warn("Resource could not be read, resource is null")
return null
return resource
-# Read a resource from the presentation archive/directory (unsafe)
+# Workaround to make loading certain resources possible, even if outside res:// and user://
+func _read_resource_workaround(path: String) -> Resource:
+ var resource: Resource = null
+ if path.ends_with(".ttf") or path.ends_with(".otf") or path.ends_with(".woff") or path.ends_with(".woff2"):
+ # Font workaround
+ resource = FontFile.new()
+ resource.load_dynamic_font(path)
+ elif path.ends_with(".png") or path.ends_with(".jpg") or path.ends_with(".svg") or path.ends_with(".ktx") or path.ends_with(".tga") or path.ends_with(".webp"):
+ # Image workaround
+ resource = Image.load_from_file(path)
+ return resource
+
+# Read a resource from the presentation archive/directory (unsafe, without workaround)
func read_resource_unsafe(path: String) -> Resource:
if !is_open:
logger.warn("No presentation is currently opened")
@@ -173,7 +186,7 @@ func read_resource_unsafe(path: String) -> Resource:
return null
return resource
-# Check if a file exists in presentation archive/direcoty
+# Check if file exists in presentation archive/directory
func file_exists(path: String) -> bool:
if !is_open:
logger.warn("No presentation is currently opened")
@@ -232,7 +245,7 @@ func get_entrypoint() -> Node:
return null
return entrypoint
-# Return presentation topic
+# Return the presentation topic
func get_topic() -> String:
if !is_open:
logger.warn("No presentation is currently opened")
@@ -243,7 +256,7 @@ func get_topic() -> String:
return ""
return manifest["topic"]
-# Return presentation authors
+# Return the presentation authors
func get_authors() -> String:
if !is_open:
logger.warn("No presentation is currently opened")
@@ -258,7 +271,7 @@ func get_authors() -> String:
else: authors = authors + ", " + author
return authors
-# Return display ratio
+# Return the presentation display ratio
func get_ratio() -> String:
if !is_open:
logger.warn("No presentation is currently opened")
@@ -269,7 +282,7 @@ func get_ratio() -> String:
return ""
return manifest["ratio"]
-# Return display resolution from display ratio
+# Return the display resolution (derived from display ratio)
func get_ratio_resolution() -> Vector2i:
if !is_open:
logger.warn("No presentation is currently opened")
diff --git a/src/processor.gd b/src/processor.gd
new file mode 100644
index 0000000..a135eb7
--- /dev/null
+++ b/src/processor.gd
@@ -0,0 +1,172 @@
+##############################################################################
+### PRESENCODE SOURCE FILE ###
+### Copyright (c) 2024 JeremyStarTM & Contributors ###
+### Licensed under the GNU General Public License v3 ###
+### ###
+### This program is free software: you can redistribute it and/or modify ###
+### it under the terms of the GNU General Public License as published by ###
+### the Free Software Foundation, either version 3 of the License, or ###
+### (at your option) any later version. ###
+### ###
+### This program is distributed in the hope that it will be useful, ###
+### but WITHOUT ANY WARRANTY; without even the implied warranty of ###
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ###
+### GNU General Public License for more details. ###
+### ###
+### You should have received a copy of the GNU General Public License ###
+### along with this program. If not, see . ###
+##############################################################################
+### src/processor.gd (Processor) ###
+### ###
+### This source file is solely used for doing things every frame. This ###
+### includes (for example) input handling, updating sizes, etc.. ###
+##############################################################################
+extends Node
+
+# Enums
+enum DragNode {
+ CONSOLE
+}
+
+# Nodes
+var nodes: Array[Node] = [null, null]
+var loader: Node = null
+var console: Node = null
+
+# States
+var initialized: bool = false
+
+# Initialize processor
+func initialize() -> void:
+ logger.diag("Initializing processor")
+ loader = get_node("/root/Presencode")
+ console = get_node("/root/Console")
+ nodes = [
+ loader,
+ console
+ ]
+ initialized = true
+
+# What did you expect?
+func _process(_delta: float) -> void:
+ if !initialized: return
+ move_to_top()
+ handle_keys()
+ change_sizes()
+ update_dragging()
+
+# Move elements to top
+func move_to_top() -> void:
+ if !initialized: return
+ var index: int = get_tree().root.get_child_count(true)
+ for node in nodes:
+ get_tree().root.move_child(node, index)
+ index = index-1
+
+# Handles key inputs
+func handle_keys() -> void:
+ if !initialized: return
+ # Global
+ ## Scaling method
+ if Input.is_action_just_pressed("content_scale_switch"):
+ match(get_tree().root.content_scale_stretch):
+ Window.CONTENT_SCALE_STRETCH_FRACTIONAL: # Current scaling mode is integer scaling
+ logger.info("Switched to integer scaling")
+ get_tree().root.content_scale_stretch = Window.CONTENT_SCALE_STRETCH_INTEGER
+ Window.CONTENT_SCALE_STRETCH_INTEGER: # Current scaling mode is fractional scaling
+ logger.info("Switched to fractional scaling")
+ get_tree().root.content_scale_stretch = Window.CONTENT_SCALE_STRETCH_FRACTIONAL
+ _: # Invalid value
+ logger.error("Invalid content_scale_stretch value")
+ # Presentation Manager
+ ## Fullscreen
+ if pmana.config_allow_fullscreen and Input.is_action_just_pressed("fullscreen"):
+ if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_WINDOWED: # Window is windowed, set to fullscreen mode
+ logger.info("Switched to fullscreen mode")
+ DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
+ else: # Window is in fullscreen (or something else) mode, set to windowed mode
+ logger.info("Switched to windowed mode")
+ DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
+ ## Navigation
+ if pmana.registered and Input.is_action_pressed("slide_forwards") and Input.is_action_pressed("slide_backwards"): return
+ else:
+ if get_node_or_null("/root/Console") == null or !get_node("/root/Console").visible:
+ if Input.is_action_just_pressed("slide_forwards"): # Increase current_slide by one
+ logger.info("Navigated one slide forwards")
+ pmana.change_slide(pmana.current_slide+1)
+ elif Input.is_action_just_pressed("slide_backwards"): # Decrease current_slide by one
+ logger.info("Navigated one slide backwards")
+ pmana.change_slide(pmana.current_slide-1)
+ # Console
+ ## Visibility toggle
+ if Input.is_action_just_pressed("console"): console.toggle_console()
+ # UI Engine
+ ## Switch resolution
+ if ui_engine.initialized and Input.is_action_just_pressed("editor_switchres"):
+ match(ui_engine.resolution):
+ "960x540": ui_engine.update_resolution(Vector2i(1920, 1080))
+ "1920x1080": ui_engine.update_resolution(Vector2i(2560, 1440))
+ "2560x1440": ui_engine.update_resolution(Vector2i(3840, 2160))
+ "3840x2160": ui_engine.update_resolution(Vector2i(960, 540))
+
+# Change sizes
+func change_sizes() -> void:
+ if !initialized: return
+ # Loader
+ loader.size = DisplayServer.window_get_size()
+ if typeof(loader.logrtl) == TYPE_OBJECT:
+ loader.logrtl.size = DisplayServer.window_get_size()
+ # ClickOverlay
+ if get_node_or_null("/root/Presencode/ClickOverlay") != null: get_node("/root/Presencode/ClickOverlay").size = DisplayServer.window_get_size()
+
+# Makes dragging windows possible
+func update_dragging() -> void:
+ if !initialized: return
+ # Console
+ if console.dragging:
+ # Get new cursor position
+ var cursor_position = get_tree().root.get_viewport().get_mouse_position()
+ if console.cursor_origin != cursor_position: # If mouse didn't move, don't execute further
+ # Calculate cursor offset
+ var cursor_offset: Vector2 = Vector2(0, 0)
+ cursor_offset.x = console.cursor_origin.x-cursor_position.x
+ cursor_offset.y = console.cursor_origin.y-cursor_position.y
+ # Change console position
+ console.position.x = console.position.x-cursor_offset.x
+ console.position.y = console.position.y-cursor_offset.y
+ # Set new console.cursor_origin
+ console.cursor_origin = cursor_position
+ # Prevent console from going out of scope
+ var position_new: Vector2 = await check_dragging_area_violation(DragNode.CONSOLE)
+ if console.position != position_new:
+ console.position = position_new
+
+# Calculate drag area
+func update_dragging_area(dragnode: DragNode, area: Vector2i = DisplayServer.window_get_size()) -> void:
+ while !initialized:
+ logger.diag("Waiting for Processor to initialize")
+ await get_tree().create_timer(0.25).timeout
+ match(dragnode):
+ DragNode.CONSOLE:
+ logger.diag("Updating drag area for CONSOLE")
+ console.drag_area.x = area.x-console.size.x
+ console.drag_area.y = area.y-console.size.y
+ _: await logger.error("Invalid DragNode \"" + str(dragnode) + "\"")
+
+# Check against drag area violations
+func check_dragging_area_violation(dragnode: DragNode) -> Vector2:
+ if !initialized: await logger.error("Processor is not initialized yet.")
+ var new_position: Vector2 = Vector2(0, 0)
+ match(dragnode):
+ DragNode.CONSOLE:
+ new_position = console.position
+ if console.position.x <= 0:
+ new_position.x = 0
+ elif console.position.x >= console.drag_area.x:
+ new_position.x = console.drag_area.x
+ if console.position.y <= 0:
+ new_position.y = 0
+ elif console.position.y >= console.drag_area.y:
+ new_position.y = console.drag_area.y
+ _: await logger.error("Invalid DragNode \"" + str(dragnode) + "\"")
+ return new_position
diff --git a/src/ui/welcome.gd b/src/ui/welcome.gd
index aad28cd..0010215 100644
--- a/src/ui/welcome.gd
+++ b/src/ui/welcome.gd
@@ -1,3 +1,25 @@
+##############################################################################
+### PRESENCODE SOURCE FILE ###
+### Copyright (c) 2024 JeremyStarTM & Contributors ###
+### Licensed under the GNU General Public License v3 ###
+### ###
+### This program is free software: you can redistribute it and/or modify ###
+### it under the terms of the GNU General Public License as published by ###
+### the Free Software Foundation, either version 3 of the License, or ###
+### (at your option) any later version. ###
+### ###
+### This program is distributed in the hope that it will be useful, ###
+### but WITHOUT ANY WARRANTY; without even the implied warranty of ###
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ###
+### GNU General Public License for more details. ###
+### ###
+### You should have received a copy of the GNU General Public License ###
+### along with this program. If not, see . ###
+##############################################################################
+### src/ui/welcome.gd (Welcome UI) ###
+### ###
+### This source file controls the welcome user interface scene. ###
+##############################################################################
extends Node
func switch_mode() -> void:
@@ -43,10 +65,12 @@ func update_resolution(new_resolution: Vector2i) -> void:
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("OpenArchiveButton").size = Vector2(new_resolution.x/4.403669724770642, new_resolution.y/14.21052631578947)
+ base.get_node("OpenArchiveButton").position = Vector2(base.size.x/16, base.size.y/2.410714285714286)
+ base.get_node("OpenDirButton").size = Vector2(new_resolution.x/4.403669724770642, new_resolution.y/14.21052631578947)
+ base.get_node("OpenDirButton").position = Vector2(base.size.x/16, base.get_node("OpenArchiveButton").position.y+base.get_node("OpenArchiveButton").size.y+base.size.y/108)
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("EditorButton").position = Vector2(base.size.x-base.get_node("OpenArchiveButton").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)
@@ -60,7 +84,7 @@ func get_stylebox_blur(resolution_: Vector2i) -> StyleBoxFlat:
stylebox.corner_radius_bottom_right = resolution_.x/30
return stylebox
-func open_presentation_picker() -> void:
+func open_presentation_archive_picker() -> void:
logger.info("Opening presentation")
var fd: FileDialog = FileDialog.new()
fd.access = FileDialog.ACCESS_FILESYSTEM
@@ -70,12 +94,28 @@ func open_presentation_picker() -> void:
fd.add_filter("*.zip", "")
fd.mode_overrides_title = false
fd.show_hidden_files = true
- fd.title = "Open a Presencode presentation file"
+ fd.title = "Open a Presencode presentation archive"
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))
+ fd.connect("file_selected", func(path: String) -> void: open_presentation(path))
+ add_child(fd)
+
+func open_presentation_dir_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_DIR
+ fd.mode_overrides_title = false
+ fd.show_hidden_files = true
+ fd.title = "Open a Presencode presentation directory"
+ fd.use_native_dialog = true
+ fd.cancel_button_text = "Cancel"
+ fd.ok_button_text = "Present"
+ fd.visible = true
+ fd.connect("dir_selected", func(path: String) -> void: open_presentation(path))
add_child(fd)
func open_presentation(path: String) -> void:
@@ -91,12 +131,11 @@ func open_presentation(path: String) -> void:
preader.read_manifest()
preader.read_entrypoint()
# Update window properties
- get_node("/root/Console").calculate_drag_area(preader.get_ratio_resolution())
+ processor.update_dragging_area(processor.DragNode.CONSOLE, 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
@@ -109,31 +148,38 @@ func open_presentation(path: String) -> void:
ui_engine.uninitialize()
func update_splash() -> void:
- logger.diag("Updating splash text")
- get_node("Wallpaper/Welcome/Splash").text = "[center]" + [
- "The first native code-based presentation viewer.", # Tagline
- "Presenting your presentations since 2023!", # Inspired by LulzSec's "Laughing at your security since 2011!"
- "Made using Godot", # FACT
- "nice", # nice :)
- "[b]your mom[/b]", # idk why i put this in
- "Also try LibreOffice!", # Inspired by Minecraft's "Also play Terraria" and Terraria's "Also play Minecraft"
- "... exists.", # We finished the sentence.
- "making unprofessional presentations", # Indicating that Presencode shouldn't be used for professional presentations
- "hates you!", # why not lol
- "tysm <3", # thank you for using
- "I'd like to interject for a moment...", # Linux interjection
- "cock", # no comment.
- "sus ඞ", # Among Us reference
- "Crashing in 3... 2... 1...", # Crashing
- "Not by StarOpenSource!", # This software doesn't have any involvement with the StarOpenSource Project
- "Extra spicy!", # Where did I leave my chilli?
- "--- Debugging process stopped ---", # Godot message after stopping a debug build
- "/effect @s 16 99999 255 true", # Infinite (or rather, veeeery long) night vision command for Minecraft 1.12.2
- "This text was inspired by Minecraft", # It is!
- "In this update we made the app much better for you.", # Parody of mobile game/application developers simply putting "We made the app better for you" into their changelog without mentioning any of the changes they've made.
- "NVIDIA fuck you", # Reference to Linus Torvalds
- "100% open source", # FACT
- "is made by idiots.", # if you're working on Presencode or using it you're probably not the smartest person on earth
- "'s icons were made with figlet", # FACT
- "https://billgates.sex is real" # Reference to MattKC
- ].pick_random() + "[/center]"
+ var splash: String = ""
+ var splash_ok: bool = false
+ while !splash_ok:
+ splash = [
+ "The first native code-based presentation viewer.", # Tagline
+ "Presenting your presentations since 2023!", # Inspired by LulzSec's "Laughing at your security since 2011!"
+ "Made using Godot", # FACT
+ "100% open source", # FACT
+ "'s icons were made with figlet", # FACT
+ "This text was inspired by Minecraft", # It is!
+ "nice", # nice
+ "[b]your mom[/b]", # idk why i put this in
+ "Also try LibreOffice!", # Inspired by Minecraft's "Also play Terraria" and Terraria's "Also play Minecraft"
+ "... exists.", # We finished the sentence.
+ "making unprofessional presentations", # Indicating that Presencode shouldn't be used for professional presentations
+ "hates you!", # why not lol
+ "tysm <3", # thank you for using
+ "I'd like to interject for a moment...", # Linux interjection
+ "cock", # no comment.
+ "sus ඞ", # Among Us reference
+ "Crashing in 3... 2... 1...", # Crashing
+ "Not by StarOpenSource!", # This software doesn't have any involvement with the StarOpenSource Project
+ "Extra spicy!", # Where did I leave my chilli?
+ "--- Debugging process stopped ---", # Godot message after stopping a debug build
+ "/effect @s 16 99999 255 true", # Infinite (or rather, veeeery long) night vision command for Minecraft 1.12.2
+ "In this update we made the app much better for you.", # Parody of mobile game/application developers simply putting "We made the app better for you" into their changelog without mentioning any of the changes they've made.
+ "NVIDIA fuck you", # Reference to Linus Torvalds
+ "is made by idiots.", # if you're working on Presencode or using it you're probably not the smartest person on earth
+ "https://billgates.sex is real", # Reference to MattKC
+ "happy :)", # happy :)
+ "E" # E
+ ].pick_random()
+ if splash == get_node("Wallpaper/Welcome/Splash").text.replace("[center]", "").replace("[/center]", ""): logger.warn("Got same splash text, picking another one")
+ else: splash_ok = true
+ get_node("Wallpaper/Welcome/Splash").text = "[center]" + splash + "[/center]"
diff --git a/src/ui_engine.gd b/src/ui_engine.gd
index e12f088..e60c3e6 100644
--- a/src/ui_engine.gd
+++ b/src/ui_engine.gd
@@ -15,16 +15,22 @@ var initialized: bool = false
var ui_mode: UIMode = UIMode.UNKNOWN
var resolution: String = "960x540"
+# Initialize engine
func initialize(loader_: Control) -> void:
if initialized:
logger.error("The UI Engine been initialized already")
return
logger.info("Initializing UI Engine")
+ # Set variables
loader = loader_
initialized = true
+ # Switch mode
logger.diag("Switching to welcome mode")
switch_mode(UIMode.WELCOME)
+ # Update resolution to next best
+ logger.diag("Updating resolution to next best resolution")
update_resolution(misc.get_best_resolution())
+ # Update window properties
logger.diag("Updating window properties")
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
DisplayServer.window_set_min_size(Vector2i(960, 540))
@@ -32,50 +38,54 @@ func initialize(loader_: Control) -> void:
get_tree().root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP
get_tree().root.content_scale_factor = 1.0
+# Uninitialize engine
func uninitialize() -> void:
if !initialized:
logger.error("The UI Engine is not initialized.")
return
logger.info("Uninitializing UI Engine")
+ # Reset variables
loader = null
initialized = false
resolution = "960x540"
ui_mode = UIMode.UNKNOWN
logger.info("Unloading UI mode")
+ # Unload current 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))
-
+# Switch UI mode
func switch_mode(mode: UIMode) -> void:
if !initialized:
logger.error("The UI Engine hasn't been initialized yet")
return
- if mode == UIMode.UNKNOWN: return
+ if mode == UIMode.UNKNOWN: await logger.error("Invalid UI mode \"" + str(mode) + "\"")
logger.info("Switching to mode " + str(mode))
+ # Remove current UI mode node
if ui_mode_node != null: loader.remove_child(ui_mode_node)
+ # Load UI node into ui_mode_node and set ui_mode
match(mode):
UIMode.WELCOME:
ui_mode_node = ResourceLoader.load("res://ui/Welcome.tscn").instantiate()
ui_mode = UIMode.WELCOME
+ # Add UI mode node to Loader
loader.add_child(ui_mode_node)
+ # UI mode node's switch_mode() takes over
ui_mode_node.switch_mode()
+# Updates the resolution
func update_resolution(new_resolution: Vector2i) -> void:
if !initialized:
logger.error("The UI Engine hasn't been initialized yet")
return
+ # Update window properties
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)
+ # Calculate new console drag area
+ processor.update_dragging_area(processor.DragNode.CONSOLE)
+ # Update resolution variable
resolution = str(new_resolution.x) + "x" + str(new_resolution.y)
+ # Call current UI mode's update_resolution() function
match(ui_mode):
UIMode.UNKNOWN: return
UIMode.WELCOME: ui_mode_node.update_resolution(new_resolution)
diff --git a/ui/Welcome.tscn b/ui/Welcome.tscn
index bc90bdf..86e07e4 100644
--- a/ui/Welcome.tscn
+++ b/ui/Welcome.tscn
@@ -94,7 +94,7 @@ 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"]]
+[node name="OpenArchiveButton" type="Button" parent="Wallpaper/Welcome" groups=["font_one", "font_one_normal"]]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
@@ -104,7 +104,19 @@ offset_top = -19.0
offset_right = 278.0
offset_bottom = 19.0
grow_vertical = 2
-text = "Open presentation"
+text = "Open archive"
+
+[node name="OpenDirButton" 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 = 24.0
+offset_right = 278.0
+offset_bottom = 62.0
+grow_vertical = 2
+text = "Open directory"
[node name="EditorButton" type="Button" parent="Wallpaper/Welcome" groups=["font_one", "font_one_normal"]]
layout_mode = 1
@@ -122,4 +134,5 @@ 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"]
+[connection signal="pressed" from="Wallpaper/Welcome/OpenArchiveButton" to="." method="open_presentation_archive_picker"]
+[connection signal="pressed" from="Wallpaper/Welcome/OpenDirButton" to="." method="open_presentation_dir_picker"]