Partial source code rewrite

- [src/*] Update comments
- [src/*] Improve code quality
- [export_presets.cfg] All exports use a console wrapper now
- [src/console.gd] Add highlighting for "arbitrary" command
- [src/console.gd] Add special message if nothing has been typed in
- [src/misc.gd, src/console.gd] Move BooleanState, get_last() (now called get_shortened_array), get_boolean(), get_int() and get_int_direct() into misc
- [src/console_info.gd] Remove useless _init() function
- [src/console_info.gd] Make get_error_string() function more compact
- [Loader.tscn, src/loader.gd] Remove window size support and slow initialization functionality
- [src/loader.gd] Remove warning on startup (will be reimplemented at some point, most likely graphically)
- [src/loader.gd] Replaced append_log() function with a lambda
- [src/loader.gd] Implement argument parser with configuration support
- [src/loader.gd] Add Presencode version information as constants and format_version() function
- [src/misc.gd] Reorganize functions
- [src/pmana.gd] Add checks to avoid slide change collisions
- [src/pmana.gd] Replace logger.warn() calls to "await logger.error()" calls
- [src/pmana.gd, src/misc.gd, src/console.gd] Remove pmana.shutdown() function in favour of misc.shutdown() which now does the same
- [src/preader.gd] Fix resource loading issues for presentation directories
- [src/preader.gd] Move resource loading workaround into seperate function _read_resource_workaround()
- [src/processor.gd, src/console.gd, src/loader.gd, src/pmana.gd, src/ui_engine.gd] Move all _process() functions into seperate file
- [src/processor.gd, src/console.gd, src/loader.gd, src/pmana.gd, src/ui_engine.gd, src/ui/welcome.gd] Move everything dragging related (except variables) into processor.gd
- [ui/Welcome.tscn, src/ui/welcome.gd] Add support for presentation directories
- [src/ui/welcome.gd] Update splash text
This commit is contained in:
JeremyStar™ 2024-01-19 20:45:37 +01:00
parent 19d3e8a0d0
commit b5acd36479
21 changed files with 767 additions and 522 deletions

View file

@ -93,5 +93,5 @@ theme_override_fonts/font = ExtResource("4_jqfsc")
theme_override_font_sizes/font_size = 18 theme_override_font_sizes/font_size = 18
placeholder_text = "Enter a command here" 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"] [connection signal="text_changed" from="Shell/Input" to="." method="input_changed"]

View file

@ -8,43 +8,3 @@ offset_bottom = 1440.0
size_flags_horizontal = 4 size_flags_horizontal = 4
color = Color(0, 0, 0, 1) color = Color(0, 0, 0, 1)
script = ExtResource("1_dpffy") 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)

View file

@ -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`

View file

@ -1,5 +1,5 @@
--- ---
sidebar_position: 1 sidebar_position: 2
--- ---
# Logger # Logger

View file

@ -1,27 +1,45 @@
--- ---
sidebar_position: 2 sidebar_position: 3
--- ---
# Miscellaneous (misc) # Miscellaneous (misc)
Miscellaneous functions that do not fit into other scripts. Miscellaneous functions that do not fit into other scripts.
## Enums
### `BooleanState`
- values
- `TRUE`
- `FALSE`
- `INVALID`
## Variables ## Variables
### `config_shutdown_invisible` ### `config_shutdown_invisible`
- type `bool` - type `bool`
- description `Hides the main window on shutdown if true, displays a white texture on shutdown if false` - description `Hides the main window on shutdown if true, displays a white texture on shutdown if false`
## Functions ## Functions
### `shutdown` ### `get_bool`
- return type `void` - return type `BooleanState`
- description `Registers a new presentation controller` - description `Retrieves a bool from a String`
- arguments - arguments
- `exitcode` - `string`
- type `int` - type `String`
- mandatory `no` - mandatory `yes`
- description `DUDE DO YOU DON'T KNOW WHAT A EXITCODE IS?!` - description `The string that you want to convert into a bool`
### `get_temporary_dir` ### `get_int`
- return type `String` - return type `Vector2i`
- description `Return a temporary directory` - description `Retrieves the next valid integer from an Array. Returns Vector2i(0, NUMBER) if successful, Vector2i(1, 0) if not`
- arguments `none` - 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` ### `get_center`
- return type `Vector2i` - return type `Vector2i`
- description `Calculates the center of a child inside its parent (Vector2i edition)` - 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` - type `Vector2`
- mandatory `yes` - mandatory `yes`
- description `The size of the child object` - description `The size of the child object`
### `clear_viewport` ### `get_shortened_array`
- return type `void` - return type `Array`
- description `Clears the presentation viewport` - description `Removes n items from an array (starting from the beginning)`
- arguments `none` - arguments
### `hide_log` - `array`
- return type `void` - type `Array`
- description `Hides log output` - mandatory `yes`
- arguments `none` - description `The array that you want to shorten`
### `show_log` - `skip`
- return type `void` - type `int`
- description `Unhides log output` - 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` - arguments `none`
### `shutdown` ### `shutdown`
- return type `void` - return type `void`
- description `Ends the presentation and shuts Presencode down` - description `Registers a new presentation controller`
- arguments
- `exitcode`
- type `int`
- mandatory `no`
- description `DUDE DO YOU DON'T KNOW WHAT A EXITCODE IS?!`

View file

@ -1,5 +1,5 @@
--- ---
sidebar_position: 3 sidebar_position: 4
--- ---
# Presentation Manager (pmana) # Presentation Manager (pmana)

View file

@ -1,5 +1,5 @@
--- ---
sidebar_position: 4 sidebar_position: 5
--- ---
# Presentation Reader (preader) # Presentation Reader (preader)

View file

@ -18,7 +18,7 @@ encrypt_directory=false
custom_template/debug="" custom_template/debug=""
custom_template/release="" custom_template/release=""
debug/export_console_wrapper=1 debug/export_console_wrapper=2
binary_format/embed_pck=true binary_format/embed_pck=true
texture_format/bptc=true texture_format/bptc=true
texture_format/s3tc=true texture_format/s3tc=true
@ -81,7 +81,7 @@ encrypt_directory=false
custom_template/debug="" custom_template/debug=""
custom_template/release="" custom_template/release=""
debug/export_console_wrapper=1 debug/export_console_wrapper=2
binary_format/embed_pck=true binary_format/embed_pck=true
texture_format/bptc=true texture_format/bptc=true
texture_format/s3tc=true texture_format/s3tc=true

View file

@ -26,6 +26,7 @@ misc="*res://src/misc.gd"
preader="*res://src/preader.gd" preader="*res://src/preader.gd"
pmana="*res://src/pmana.gd" pmana="*res://src/pmana.gd"
ui_engine="*res://src/ui_engine.gd" ui_engine="*res://src/ui_engine.gd"
processor="*res://src/processor.gd"
[display] [display]
@ -73,8 +74,8 @@ content_scale_switch={
} }
console={ console={
"deadzone": 0.5, "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) "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":96,"physical_keycode":0,"key_label":0,"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={ editor_switchres={

View file

@ -23,9 +23,10 @@
############################################################################## ##############################################################################
extends Button extends Button
# Setup clickoverlay # Initialize clickoverlay
func _ready() -> void: func _ready() -> void:
# Set properties logger.diag("Initializing ClickOverlay")
# Set button properties
mouse_filter = Control.MOUSE_FILTER_STOP mouse_filter = Control.MOUSE_FILTER_STOP
position = Vector2i(0, 0) position = Vector2i(0, 0)
# Hide button # Hide button
@ -34,17 +35,13 @@ func _ready() -> void:
add_theme_stylebox_override("pressed", StyleBoxEmpty.new()) add_theme_stylebox_override("pressed", StyleBoxEmpty.new())
add_theme_stylebox_override("disabled", StyleBoxEmpty.new()) add_theme_stylebox_override("disabled", StyleBoxEmpty.new())
add_theme_stylebox_override("focus", StyleBoxEmpty.new()) add_theme_stylebox_override("focus", StyleBoxEmpty.new())
# Connect "pressed" signal to function "clicked" # Connect pressed signal
connect("pressed", Callable(self, "clicked")) connect("pressed", Callable(self, "clicked"))
# Simple fix for a bug # Simple fix for a bug
await get_tree().process_frame await get_tree().process_frame
clicked() clicked()
# Clicked event
func clicked() -> void: func clicked() -> void:
# Increase current_slide by one
logger.info("Navigating one slide forwards (click)") logger.info("Navigating one slide forwards (click)")
pmana.change_slide(pmana.current_slide+1, true) pmana.change_slide(pmana.current_slide+1, true)
func _process(_delta: float) -> void:
# Scale clickoverlay to window size
size = DisplayServer.window_get_size()

View file

@ -23,13 +23,6 @@
############################################################################## ##############################################################################
extends ColorRect extends ColorRect
# Enums
enum BooleanState {
TRUE,
FALSE,
INVALID
}
# Nodes # Nodes
@onready var input: TextEdit = get_node("Shell/Input") @onready var input: TextEdit = get_node("Shell/Input")
@onready var output: RichTextLabel = get_node("Shell/Output") @onready var output: RichTextLabel = get_node("Shell/Output")
@ -43,20 +36,24 @@ var dragging: bool = false
var drag_area: Vector2 = Vector2(0, 0) var drag_area: Vector2 = Vector2(0, 0)
var cursor_origin: Vector2 = Vector2(0, 0) var cursor_origin: Vector2 = Vector2(0, 0)
# Initialize debug console
func _ready() -> void: func _ready() -> void:
logger.info("Initializing debug console") logger.info("Initializing debug console")
# Set properties
visible = false visible = false
position = Vector2(30, 30) position = Vector2(30, 30)
calculate_drag_area() # Calculate console drag area
processor.update_dragging_area(processor.DragNode.CONSOLE)
# Setup window dragging # Setup window dragging
$Bar/DragButton.connect("button_down", func(): $Bar/DragButton.connect("button_down", func() -> void:
dragging = true dragging = true
cursor_origin = get_tree().root.get_viewport().get_mouse_position() 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 dragging = false
cursor_origin = Vector2(0,0) cursor_origin = Vector2(0,0)
) )
# Setup ExpressionScript (in which arbitrary expressions are run)
expressionscript.name = "ExpressionScript" expressionscript.name = "ExpressionScript"
expressionscript.set_script(ResourceLoader.load("res://src/console_expressionscript.gd")) expressionscript.set_script(ResourceLoader.load("res://src/console_expressionscript.gd"))
expressionscript.logger = get_node("/root/logger") expressionscript.logger = get_node("/root/logger")
@ -67,60 +64,18 @@ func _ready() -> void:
expressionscript.console = self expressionscript.console = self
expressionscript.loader = get_node("/root/Presencode") expressionscript.loader = get_node("/root/Presencode")
add_child(expressionscript) add_child(expressionscript)
# Reset console session
await process_command(PackedStringArray(["exit"])) await process_command(PackedStringArray(["exit"]))
logger.info("Debug console initialized") logger.info("Debug console initialized")
func _process(_delta: float) -> void: # Toggle console visibility
# Move to top func toggle_console() -> void:
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") logger.info("Toggling console visibility")
visible = !visible 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
# Input changed # Input changed
func input_changed() -> void: func input_changed() -> void:
# Check for newline/enter
if input.text.contains("\n"): if input.text.contains("\n"):
input.text = input.text.replace("\n", "") input.text = input.text.replace("\n", "")
var input2: String = input.text var input2: String = input.text
@ -128,13 +83,30 @@ func input_changed() -> void:
input.editable = false input.editable = false
await process_command(input2.split(" ", false)) await process_command(input2.split(" ", false))
input.editable = true 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 # 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: func process_command(command: PackedStringArray) -> void:
logger.diag("Processing command [" + " ".join(command) + "]") logger.diag("Processing command [" + " ".join(command) + "]")
await get_tree().process_frame await get_tree().process_frame
append_output("[color=gray]$ " + " ".join(command) + "[color=white]") if command.size() == 0:
if command.size() == 0: return 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]): match(command[0]):
"clear": "clear":
match(command.size()): match(command.size()):
@ -154,7 +126,7 @@ To get started, enter \"help\". To close the console, press the X button.[color=
1: exitcode = 0 1: exitcode = 0
2: exitcode = int(command[1]) 2: exitcode = int(command[1])
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_MANY_ARGUMENTS)) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_MANY_ARGUMENTS))
pmana.shutdown(exitcode) misc.shutdown(exitcode)
"help": "help":
match(command.size()): match(command.size()):
1: append_output(info.get_help_topic(ConsoleInfo.HelpTopic.INDEX)) 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]): match(command[2]):
"LOGGER_ENABLED": "LOGGER_ENABLED":
match(get_boolean(" ".join(get_last(command, 2)))): match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
BooleanState.TRUE: misc.BooleanState.TRUE:
logger.config_enabled = true logger.config_enabled = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b].") append_output("Set \"" + str(command[2]) + "\" to [b]true[/b].")
BooleanState.FALSE: misc.BooleanState.FALSE:
logger.config_enabled = false logger.config_enabled = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_DIAGNOSTIC": "LOGGER_DIAGNOSTIC":
match(get_boolean(" ".join(get_last(command, 2)))): match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
BooleanState.TRUE: misc.BooleanState.TRUE:
logger.config_diagnostic = true logger.config_diagnostic = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
BooleanState.FALSE: misc.BooleanState.FALSE:
logger.config_diagnostic = false logger.config_diagnostic = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_COLORED": "LOGGER_COLORED":
match(get_boolean(" ".join(get_last(command, 2)))): match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
BooleanState.TRUE: misc.BooleanState.TRUE:
logger.config_colored = true logger.config_colored = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
BooleanState.FALSE: misc.BooleanState.FALSE:
logger.config_colored = false logger.config_colored = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_HARDFAIL": "LOGGER_HARDFAIL":
match(get_boolean(" ".join(get_last(command, 2)))): match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
BooleanState.TRUE: misc.BooleanState.TRUE:
logger.config_hardfail = true logger.config_hardfail = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
BooleanState.FALSE: misc.BooleanState.FALSE:
logger.config_hardfail = false logger.config_hardfail = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"LOGGER_LOGSTRING": "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]") append_output("Set \"" + str(command[2]) + "\" to [b]\"" + logger.config_logstring + "\"[/b]")
"PMANA_ALLOW_FULLSCREEN": "PMANA_ALLOW_FULLSCREEN":
match(get_boolean(" ".join(get_last(command, 2)))): match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
BooleanState.TRUE: misc.BooleanState.TRUE:
pmana.config_allow_fullscreen = true pmana.config_allow_fullscreen = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
BooleanState.FALSE: misc.BooleanState.FALSE:
pmana.config_allow_fullscreen = false pmana.config_allow_fullscreen = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
"MISC_SHUTDOWN_INVISIBLE": "MISC_SHUTDOWN_INVISIBLE":
match(get_boolean(" ".join(get_last(command, 2)))): match(misc.get_bool(" ".join(misc.get_shortened_array(command, 2)))):
BooleanState.TRUE: misc.BooleanState.TRUE:
misc.config_shutdown_invisible = true misc.config_shutdown_invisible = true
append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]true[/b]")
BooleanState.FALSE: misc.BooleanState.FALSE:
misc.config_shutdown_invisible = false misc.config_shutdown_invisible = false
append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]") append_output("Set \"" + str(command[2]) + "\" to [b]false[/b]")
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) _: 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)) 5: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS))
6: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS)) 6: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS))
7: 7:
var version: Vector2 = get_int_direct(command[2]) var version: Vector2 = misc.get_int_direct(command[2])
var slides: Vector2 = get_int_direct(command[3]) var slides: Vector2 = misc.get_int_direct(command[3])
var animations: BooleanState = get_boolean(command[4]) var animations: misc.BooleanState = misc.get_bool(command[4])
var animations_bool: bool = false 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 quit_last_slide_bool: bool = false
var controller: NodePath = NodePath(command[6]) var controller: NodePath = NodePath(command[6])
if version.x != 0: 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"})) append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "int"}))
return return
match(animations): match(animations):
BooleanState.TRUE: animations_bool = true misc.BooleanState.TRUE: animations_bool = true
BooleanState.FALSE: animations_bool = false misc.BooleanState.FALSE: animations_bool = false
_: _:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
return return
match(quit_last_slide): match(quit_last_slide):
BooleanState.TRUE: quit_last_slide_bool = true misc.BooleanState.TRUE: quit_last_slide_bool = true
BooleanState.FALSE: quit_last_slide_bool = false misc.BooleanState.FALSE: quit_last_slide_bool = false
_: _:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"})) append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "bool"}))
return return
@ -346,31 +318,31 @@ MISC_SHUTDOWN_INVISIBLE | bool | Toggles if the window should be made invisibl
3: 3:
if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED)) if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED))
else: else:
var slide: Vector2i = get_int_direct(command[2]) var slide: Vector2i = misc.get_int_direct(command[2])
match(slide.x): match(slide.x):
0: 0:
pmana.change_slide(slide.y) pmana.change_slide(slide.y)
append_output("Switched to slide [b]" + str(slide.y) + "[/b]") append_output("Switched to slide [b]" + str(slide.y) + "[/b]")
1: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_TYPE, {"expected_type": "int"})) 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: 4:
if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED)) if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED))
else: else:
var slide: Vector2i = get_int_direct(command[2]) var slide: Vector2i = misc.get_int_direct(command[2])
match(slide.x): match(slide.x):
0: 0:
var no_animations: BooleanState = get_boolean(command[3]) var no_animations: misc.BooleanState = misc.get_bool(command[3])
match(no_animations): match(no_animations):
BooleanState.TRUE: misc.BooleanState.TRUE:
pmana.change_slide(slide.y, true) pmana.change_slide(slide.y, true)
append_output("Switched to slide [b]" + str(slide.y) + "[/b] without animations") append_output("Switched to slide [b]" + str(slide.y) + "[/b] without animations")
BooleanState.FALSE: misc.BooleanState.FALSE:
pmana.change_slide(slide.y, false) pmana.change_slide(slide.y, false)
append_output("Switched to slide [b]" + str(slide.y) + "[/b]") 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"})) 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"})) 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)) _: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_MANY_ARGUMENTS))
"get_slide": "get_slide":
if !pmana.registered: append_output(info.get_error_string(ConsoleInfo.ConsoleError.NO_CONTROLLER_REGISTERED)) 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()): match(command.size()):
1: append_output(info.get_error_string(ConsoleInfo.ConsoleError.TOO_FEW_ARGUMENTS)) 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() var expression: Expression = Expression.new()
if expression.parse(expression_raw) != Error.OK: if expression.parse(expression_raw) != Error.OK:
append_output(info.get_error_string(ConsoleInfo.ConsoleError.EXPRESSION_PARSING_FAILED, {"error": expression.get_error_text()})) 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. append_output("""Executed arbitrary expression successfully.
Returned: """ + str(returned)) Returned: """ + str(returned))
_: append_output(info.get_error_string(ConsoleInfo.ConsoleError.INVALID_COMMAND)) _: 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

View file

@ -24,6 +24,7 @@
extends Node extends Node
class_name ConsoleInfo class_name ConsoleInfo
# Enums
enum ConsoleError { enum ConsoleError {
OK, OK,
UNIMPLEMENTED, UNIMPLEMENTED,
@ -55,57 +56,38 @@ enum HelpTopic {
ARBITRARY ARBITRARY
} }
# Variables
var error_color: String = "[color=red]" var error_color: String = "[color=red]"
var internal_error_color: String = "[b][color=red]" var internal_error_color: String = "[b][color=red]"
func _init() -> void: # Generates an internal error
logger.diag("ConsoleInfo initialized")
func generate_internal_error(error: String) -> String: func generate_internal_error(error: String) -> String:
var origin = misc.get_origin() var origin = misc.get_origin()
logger.warn("Debug console (" + origin["file"] + ":" + origin["function"] + ":" + str(origin["line"]) + ") experienced an internal error: " + error) 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"]) + "]" 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: func get_error_string(error: ConsoleError, context: Dictionary = {}) -> String:
match(error): match(error):
ConsoleError.OK: ConsoleError.OK: return generate_internal_error("ConsoleError.OK is not a valid error")
return generate_internal_error("ConsoleError.OK is not a valid error") ConsoleError.UNIMPLEMENTED: return error_color + "Function unimplemented, aborting."
ConsoleError.UNIMPLEMENTED: ConsoleError.INVALID_COMMAND: return error_color + "Invalid command. Execute \"help\" for a list of all available commands."
return error_color + "Function unimplemented, aborting." ConsoleError.TOO_MANY_ARGUMENTS: return error_color + "Too many arguments."
ConsoleError.INVALID_COMMAND: ConsoleError.TOO_FEW_ARGUMENTS: return error_color + "Too few arguments."
return error_color + "Invalid command. Execute \"help\" for a list of all available commands." ConsoleError.INVALID_ARGUMENT: return error_color + "Invalid argument."
ConsoleError.TOO_MANY_ARGUMENTS: ConsoleError.INVALID_TYPE: return error_color + "Invalid type. Command expected type " + str(context["expected_type"]) + "."
return error_color + "Too many arguments." ConsoleError.NOT_AN_OBJECT: return error_color + "Invalid type. Command expected a valid NodePath."
ConsoleError.TOO_FEW_ARGUMENTS: ConsoleError.NO_SCRIPT_ATTACHED: return error_color + "Invalid object. Command expected a script-attached object."
return error_color + "Too few arguments." ConsoleError.INVALID_HELP_TOPIC: return error_color + "Invalid help topic."
ConsoleError.INVALID_ARGUMENT: ConsoleError.INVALID_CONFIG_KEY: return error_color + "Invalid config key \"" + str(context["key"]) + "\". Execute \"config list\" for a list of all config keys."
return error_color + "Invalid argument." ConsoleError.NO_PRESENTATION_OPEN: return error_color + "No presentation is currently open."
ConsoleError.INVALID_TYPE: ConsoleError.CONTROLLER_ALREADY_REGISTERED: return error_color + "A presentation controller has been registered already."
return error_color + "Invalid type. Command expected type " + str(context["expected_type"]) + "." ConsoleError.NO_CONTROLLER_REGISTERED: return error_color + "No presentation controller has been registered yet."
ConsoleError.NOT_AN_OBJECT: ConsoleError.EXPRESSION_PARSING_FAILED: return error_color + "Could not parse arbitrary expression successfully.\nError: " + str(context["error"])
return error_color + "Invalid type. Command expected a valid NodePath." ConsoleError.EXPRESSION_EXECUTION_FAILED: return error_color + "Could not execute arbitrary expression successfully.\nError: " + str(context["error"]) + "\nReturned: " + str(context["returned"])
ConsoleError.NO_SCRIPT_ATTACHED: _: return generate_internal_error("Invalid ConsoleError \"" + str(error) + "\", context: " + str(context))
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))
# Converts HelpTopic into a String
func get_help_topic(topic: HelpTopic) -> String: func get_help_topic(topic: HelpTopic) -> String:
match(topic): match(topic):
HelpTopic.INDEX: HelpTopic.INDEX:
@ -158,5 +140,4 @@ Calls functions belonging to the Presentation Reader"""
Executes arbitrary GDScript expressions. Executes arbitrary GDScript expressions.
-> EXPERIMENTAL""" -> EXPERIMENTAL"""
_: _: return generate_internal_error("Invalid HelpTopic \"" + str(topic) + "\"")
return generate_internal_error("Invalid HelpTopic \"" + str(topic) + "\"")

View file

@ -23,21 +23,26 @@
############################################################################## ##############################################################################
extends Control extends Control
# Loader configuration # Enums
## Window size support enum VersionType {
### This is/was used for configuring scaling RELEASE,
var config_window_size_support: bool = false RELEASECANDIDATE,
## Skip malicious scripts warning if running as debug build BETA,
var config_skipwarning: bool = true ALPHA
## Slow init }
### I don't know why I put this here lol
var config_slow_init: bool = false # Constants
const version_release: int = 1
const version_type: VersionType = VersionType.ALPHA
const version_typerelease: int = 2
# Nodes # Nodes
var logrtl: RichTextLabel = null var logrtl: RichTextLabel = null
var console: Control = null var console: Control = null
func _ready() -> void: func _ready() -> void:
#while true: await get_tree().create_timer(0.05).timeout
misc.set_main_window_visibility(false)
logger.info("Updating loader scene") logger.info("Updating loader scene")
# Rename loader scene # Rename loader scene
name = "Presencode" 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("normal_font_size", 14)
logrtl.add_theme_font_size_override("bold_font_size", 14) logrtl.add_theme_font_size_override("bold_font_size", 14)
## Connect to logger ## 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 to SceneTree
add_child(logrtl) add_child(logrtl)
## Remove VScrollBar ## Remove VScrollBar
@ -74,14 +79,6 @@ func _ready() -> void:
vsbar.add_theme_stylebox_override("grabber", StyleBoxEmpty.new()) vsbar.add_theme_stylebox_override("grabber", StyleBoxEmpty.new())
vsbar.add_theme_stylebox_override("grabber_highlight", StyleBoxEmpty.new()) vsbar.add_theme_stylebox_override("grabber_highlight", StyleBoxEmpty.new())
vsbar.add_theme_stylebox_override("grabber_pressed", 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 Presencode
initialize() initialize()
@ -98,24 +95,77 @@ func initialize() -> void:
Copyright (c) 2024 JeremyStarTM & Contributers Copyright (c) 2024 JeremyStarTM & Contributers
Licensed under the GNU General Public License version 3 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 # Check for platform
misc.check_platform() misc.check_platform()
# Create temporary directory # Create temporary directory
DirAccess.make_dir_recursive_absolute(misc.get_temporary_dir()) DirAccess.make_dir_recursive_absolute(misc.get_temporary_dir())
# Load debug console
logger.info("Injecting console") logger.info("Injecting console")
console = ResourceLoader.load("res://Console.tscn").instantiate() console = ResourceLoader.load("res://Console.tscn").instantiate()
get_tree().root.add_child.call_deferred(console) get_tree().root.add_child.call_deferred(console)
if config_slow_init: await get_tree().create_timer(randf_range(0.1, 0.15)).timeout # Initialize processor
# Check for presentation path in commandline arguments processor.initialize.call_deferred()
# Open presentation archive/directory # Process arguments
var path: String = " ".join(OS.get_cmdline_user_args()) var path: String = parse_arguments()
if OS.get_cmdline_user_args().size() == 0: # Update window properties
misc.set_main_window_visibility(true)
# Check path variable
if path == "":
ui_engine.initialize(self) ui_engine.initialize(self)
else: else:
await print_warning()
await load_presentation(path) 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 # Load a presentation from commandline arguments
func load_presentation(path: String) -> void: 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 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) preader.open_presentation(path, false)
else: else:
await logger.error("Presentation file/directory \"" + path + "\" not found") 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 # Read manifest & entrypoint files
preader.read_manifest() preader.read_manifest()
preader.read_entrypoint() preader.read_entrypoint()
# Update window properties # 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_title("Presencode » Presenting \"" + preader.get_topic() + "\" by \"" + preader.get_authors() + "\"")
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
DisplayServer.window_set_size(preader.get_ratio_resolution()) DisplayServer.window_set_size(preader.get_ratio_resolution())
DisplayServer.window_set_min_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_size = preader.get_ratio_resolution()
get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_VIEWPORT 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_aspect = Window.CONTENT_SCALE_ASPECT_KEEP
get_tree().root.content_scale_factor = 1.0 get_tree().root.content_scale_factor = 1.0
await get_tree().process_frame await get_tree().process_frame
DisplayServer.window_set_position(misc.get_center(DisplayServer.screen_get_size(), DisplayServer.window_get_size())) 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 # Add entrypoint to SceneTree
get_tree().root.add_child(preader.get_entrypoint()) get_tree().root.add_child(preader.get_entrypoint())
# seeeeeeeeeeeeelf explanitory func format_version(format: String) -> String:
func _process(_delta: float) -> void: var version: String = format
# Move to top var version_type_normal: String = ""
get_tree().root.move_child(self, get_tree().root.get_child_count(true)-1) var version_type_short: String = ""
# Change sizes var version_type_technical: String = ""
size = DisplayServer.window_get_size() match(version_type):
if typeof(logrtl) == TYPE_OBJECT: VersionType.RELEASE:
logrtl.size = DisplayServer.window_get_size() version_type_normal = "Release"
if config_window_size_support: move_child($WindowSizeSupport, get_child_count(true)) version_type_short = "Release"
# Fullscreen key combo version_type_technical = "r"
if pmana.config_allow_fullscreen and Input.is_action_just_pressed("fullscreen"): VersionType.RELEASECANDIDATE:
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_WINDOWED: version_type_normal = "Releasecandidate"
# window is windowed, set to fullscreen mode version_type_short = "RC"
logger.info("Switched to fullscreen mode") version_type_technical = "rc"
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) VersionType.BETA:
else: version_type_normal = "Beta"
# window is in fullscreen (or something else) mode, set to windowed mode version_type_short = "Beta"
logger.info("Switched to windowed mode") version_type_technical = "b"
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) VersionType.ALPHA:
# Content scale switch combo version_type_normal = "Alpha"
if Input.is_action_just_pressed("content_scale_switch"): version_type_short = "Alpha"
match(get_tree().root.content_scale_stretch): version_type_technical = "a"
Window.CONTENT_SCALE_STRETCH_FRACTIONAL: _: logger.info("Invalid version type \"" + str(version_type) + "\"")
logger.info("Switching to fractional scaling") version = version.replace("%release%", str(version_release))
get_tree().root.content_scale_stretch = Window.CONTENT_SCALE_STRETCH_INTEGER version = version.replace("%type_raw%", str(version_type))
Window.CONTENT_SCALE_STRETCH_INTEGER: version = version.replace("%type%", version_type_normal)
logger.info("Switching to integer scaling") version = version.replace("%type_short%", version_type_short)
get_tree().root.content_scale_stretch = Window.CONTENT_SCALE_STRETCH_FRACTIONAL version = version.replace("%type_technical%", version_type_technical)
_: version = version.replace("%typerelease%", str(version_typerelease))
logger.error("Invalid content_scale_stretch value") return version
# 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

View file

@ -35,7 +35,9 @@ var config_enabled: bool = true
var config_diagnostic: bool = true var config_diagnostic: bool = true
## Toggle colored output ## Toggle colored output
var config_colored: bool = true 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 var config_hardfail: bool = true
## Logging template ## Logging template
### Variables (begin and end with '%'): runtime, time, file, function, line, color, type, message ### Variables (begin and end with '%'): runtime, time, file, function, line, color, type, message

View file

@ -35,8 +35,13 @@ enum Error {
MANIFEST_INVALID_PROGRAM, MANIFEST_INVALID_PROGRAM,
MANIFEST_INVALID_RATIO MANIFEST_INVALID_RATIO
} }
enum BooleanState {
TRUE,
FALSE,
INVALID
}
# Manifest specification # Constants
const manifest_version: int = 1 const manifest_version: int = 1
const manifest_program: String = "Presencode" const manifest_program: String = "Presencode"
@ -44,45 +49,46 @@ const manifest_program: String = "Presencode"
## Make window invisible on shutdown ## Make window invisible on shutdown
### Hides the main window on shutdown if true ### Hides the main window on shutdown if true
### Displays RenderingServer.get_white_texture() if false instead ### 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 # Helper function to retrieve a bool from a String
func get_origin(n: int = 0) -> Dictionary: func get_bool(string: String) -> BooleanState:
var stack: Dictionary = get_stack()[2+n] match(string):
return {"file": stack["source"].replace("user://", "").replace("res://", ""), "line": stack["line"], "function": stack["function"]} "true": return BooleanState.TRUE
"false": return BooleanState.FALSE
_: return BooleanState.INVALID
# Shutdown Presencode safely # Helper function to retrieve the next valid int from an Array
func shutdown(exitcode: int = 0) -> void: ## Returns Vector2i(0, NUMBER) if successful
logger.info("Shutting down (code " + str(exitcode) + ")") ## Returns Vector2i(1, 0) if not successful
get_tree().paused = true func get_int(array: Array) -> Vector2i:
# Display white texture for value in array:
var npr: NinePatchRect = NinePatchRect.new() if str(value).is_valid_int(): return Vector2i(0, int(value))
npr.name = "OverlayTexture" return Vector2i(1, 0)
npr.texture = ImageTexture.create_from_image(RenderingServer.texture_2d_get(RenderingServer.get_white_texture()))
npr.size = Vector2(100000, 100000) # Helper function to retrieve a int from a String
npr.position = Vector2(-50000, -50000) ## Returns Vector2i(0, NUMBER) if successful
get_tree().root.add_child(npr) ## Returns Vector2i(1, 0) if not successful
get_tree().root.move_child(npr, get_tree().root.get_child_count(true)) func get_int_direct(string: String) -> Vector2i:
# Window stuff if string.is_valid_int(): return Vector2i(0, int(string))
if config_shutdown_invisible: else: return Vector2i(1, 0)
# Make window invisible
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) # Returns a shortened array, by skipping "skip" Items
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true) func get_shortened_array(array: Array, skip: int) -> Array:
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_RESIZE_DISABLED, true) # We want to remove at least one item
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_NO_FOCUS, true) skip = skip+1
DisplayServer.window_set_min_size(Vector2i(1, 1)) # Create new array
DisplayServer.window_set_size(Vector2i(1, 1)) var new_array: Array = []
DisplayServer.window_set_position(Vector2i(0, 0)) if skip >= array.size():
logger.diag("Removing temporary directory") logger.error("n is bigger than array.size()")
# Remove tempdir return []
DirAccess.remove_absolute(get_temporary_dir()) # Loop through array
# Wait for all log messages to be printed to console/log for value in array:
logger.diag("Waiting for log messages") if skip == 0: # Append to new_array
await get_tree().create_timer(0.25, true).timeout new_array.append(value)
print("Exiting!") else: # Count down
get_tree().quit(exitcode) skip = skip-1
# Insanely long timer to prevent Godot from executing code during exit return new_array
await get_tree().create_timer(999, true).timeout
# Calculate the center of a child inside its parent (Vector2i) # Calculate the center of a child inside its parent (Vector2i)
func get_center(parent_size: Vector2i, child_size: Vector2i) -> 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 elif number < 0: return -1
else: return 0 else: return 0
# Return path to temporary directory # Get call origin from stacktrace
## This function tries to utilize the operating system's temporary directory. func get_origin(n: int = 0) -> Dictionary:
## If all of them can't be used, it falls back to "user://temp" instead. var stack: Dictionary = get_stack()[2+n]
func get_temporary_dir() -> String: return {"file": stack["source"].replace("user://", "").replace("res://", ""), "line": stack["line"], "function": stack["function"]}
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 ""
# Check platform support # Check platform support
func check_platform() -> void: func check_platform() -> void:
@ -149,7 +139,70 @@ func get_best_resolution() -> Vector2i:
if closest_resolution == Vector2(NAN, NAN): return Vector2i(960, 540) if closest_resolution == Vector2(NAN, NAN): return Vector2i(960, 540)
else: return closest_resolution 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: func check_manifest_consistency(manifest: Dictionary) -> misc.Error:
logger.diag("Checking manifest for consistency") logger.diag("Checking manifest for consistency")

View file

@ -26,6 +26,10 @@ extends Node
# Constants # Constants
const entrypoint_version: int = 1 const entrypoint_version: int = 1
# Nodes
var clickoverlay: Button = null
var viewport: Control = null
# Configuration # Configuration
var config_allow_fullscreen: bool = true var config_allow_fullscreen: bool = true
@ -39,8 +43,7 @@ var quit_last_slide: bool = true
var registered: bool = false var registered: bool = false
var current_slide: int = -1 var current_slide: int = -1
var animation_active: bool = false var animation_active: bool = false
var clickoverlay: Button = null var action_running: bool = false
var viewport: Control = null
# Register a controller # Register a controller
func register(version: int, slides_: int, animations_: bool, quit_last_slide_: bool, controller_: NodePath) -> void: 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: if get_node(controller_) == null:
await logger.error("The presentation controller could not be located.") await logger.error("The presentation controller could not be located.")
# Set controller information # Set controller information
logger.diag("Setting controller information")
slides = slides_ slides = slides_
animations = animations_ animations = animations_
quit_last_slide = quit_last_slide_ quit_last_slide = quit_last_slide_
controller = get_node(controller_) controller = get_node(controller_)
# Check for essential functions # Check for required functions
logger.diag("Check for required functions")
var lacking_functions: Array = [] var lacking_functions: Array = []
var check_functions: Array = ["change_slide", "presentation_start", "presentation_end"] ## Base functions var check_functions: Array = ["change_slide", "presentation_start", "presentation_end"] ## Base functions
## "display_end_slide" function ## "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.") logger.warn("Consult the documentation or take a look at the example presentation if you need help.")
await misc.shutdown(1) await misc.shutdown(1)
# Create presentation viewport # Create presentation viewport
logger.diag("Creating presentation viewport")
viewport = Control.new() viewport = Control.new()
viewport.name = "Viewport" viewport.name = "Viewport"
viewport.size = preader.get_ratio_resolution() viewport.size = preader.get_ratio_resolution()
viewport.position = Vector2(0, 0) viewport.position = Vector2(0, 0)
get_node("/root/Presencode").add_child(viewport) get_node("/root/Presencode").add_child(viewport)
# Create clickoverlay # Create clickoverlay
logger.diag("Creating clickoverlay")
clickoverlay = Button.new() clickoverlay = Button.new()
clickoverlay.name = "ClickOverlay" clickoverlay.name = "ClickOverlay"
clickoverlay.set_script(ResourceLoader.load("res://src/clickoverlay.gd")) clickoverlay.set_script(ResourceLoader.load("res://src/clickoverlay.gd"))
get_node("/root/Presencode").add_child(clickoverlay) get_node("/root/Presencode").add_child(clickoverlay)
# Did you know that every big galaxy contains a massive black hole in it's center? # Did you know that every big galaxy contains a massive black hole in it's center?
registered = true registered = true
# Invoke presentation_start() # Invoke controller's presentation_start()
controller.presentation_start(viewport) controller.presentation_start(viewport)
# Unregister a controller # Unregister a controller
@ -101,17 +108,23 @@ func unregister() -> void:
logger.warn("No presentation controller has been registered yet. Please call register() first.") logger.warn("No presentation controller has been registered yet. Please call register() first.")
return return
logger.info("Unregistering presentation controller") 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 registered = false
current_slide = -1 current_slide = -1
animation_active = false animation_active = false
config_allow_fullscreen = true config_allow_fullscreen = true
# Remove clickoverlay # Remove nodes
logger.diag("Removing node")
## Remove clickoverlay
get_node("/root/Presencode/").remove_child(clickoverlay) get_node("/root/Presencode/").remove_child(clickoverlay)
clickoverlay = null clickoverlay = null
## Remove viewport
get_node("/root/Presencode/").remove_child(viewport) get_node("/root/Presencode/").remove_child(viewport)
viewport = null viewport = null
await get_tree().process_frame
# Change slide # Change slide
func change_slide(slide: int, no_animations: bool = false) -> void: 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? # Quit after last slide?
if quit_last_slide: if quit_last_slide:
logger.info("Ending presentation") logger.info("Ending presentation")
await shutdown() await misc.shutdown(0)
return return
else: else:
logger.warn("Over max slides by " + str(slide-slides)) logger.warn("Over max slides by " + str(slide-slides))
@ -142,33 +155,30 @@ func change_slide(slide: int, no_animations: bool = false) -> void:
return return
else: # End presentation (slide is over max slides by two or more) else: # End presentation (slide is over max slides by two or more)
logger.info("Ending presentation") logger.info("Ending presentation")
await shutdown() await misc.shutdown(0)
return return
# Slide is not over max slides, continue slide change # Slide is not over max slides, continue slide change
action_running = true
# Play switch away animation # Play switch away animation
if animations and !no_animations: if animations and !no_animations:
await controller.animation_switch_away(current_slide, slide) await controller.animation_switch_away(current_slide, slide)
# Check if controller has unregistered during animation # Check if controller has unregistered during animation
if !registered: if !registered: logger.error("The current presentation controller has been unregistered during execution")
logger.warn("The current presentation controller has been unregistered during execution, aborting")
return
# Change slide # Change slide
await controller.change_slide(slide) await controller.change_slide(slide)
# Check if controller has unregistered during animation # Check if controller has unregistered during slide change
if !registered: if !registered: logger.error("The current presentation controller has been unregistered during execution")
logger.warn("The current presentation controller has been unregistered during execution, aborting")
return
# Set current_slide to new slide # Set current_slide to new slide
current_slide = slide current_slide = slide
# Play switch to animation # Play switch to animation
if animations and !no_animations: if animations and !no_animations: await controller.animation_switch_to(current_slide, slide)
await controller.animation_switch_to(current_slide, slide)
# Clear the viewport # Clear the viewport
func clear_viewport() -> void: func clear_viewport() -> void:
if !registered: if !registered:
logger.warn("No presentation controller has been registered yet. Please call register() first.") logger.warn("No presentation controller has been registered yet. Please call register() first.")
return return
logger.diag("Clearing viewport")
if viewport.get_child_count(true) == 0: if viewport.get_child_count(true) == 0:
logger.warn("No children found in viewport") logger.warn("No children found in viewport")
return return
@ -180,6 +190,7 @@ func hide_log() -> void:
if !registered: if !registered:
logger.warn("No presentation controller has been registered yet. Please call register() first.") logger.warn("No presentation controller has been registered yet. Please call register() first.")
return return
logger.diag("Hiding log")
get_node("/root/Presencode/Log").modulate = Color8(255, 255, 255, 0) get_node("/root/Presencode/Log").modulate = Color8(255, 255, 255, 0)
# Show log output # Show log output
@ -187,27 +198,5 @@ func show_log() -> void:
if !registered: if !registered:
logger.warn("No presentation controller has been registered yet. Please call register() first.") logger.warn("No presentation controller has been registered yet. Please call register() first.")
return return
logger.diag("Showing log")
get_node("/root/Presencode/Log").modulate = Color8(255, 255, 255, 255) 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)

View file

@ -25,7 +25,10 @@
############################################################################## ##############################################################################
extends Node extends Node
# Reading # States
var is_open: bool = false
# Reading support
var directorypath: String = "" var directorypath: String = ""
var ziphandler: ZIPReader = ZIPReader.new() var ziphandler: ZIPReader = ZIPReader.new()
@ -33,10 +36,7 @@ var ziphandler: ZIPReader = ZIPReader.new()
var manifest: Dictionary = {} var manifest: Dictionary = {}
var entrypoint: Node = null var entrypoint: Node = null
# States # Open a presentation archive/directory
var is_open: bool = false
# Opening a presentation
func open_presentation(path: String, zip: bool) -> Error: func open_presentation(path: String, zip: bool) -> Error:
if is_open: if is_open:
logger.warn("Another presentation is still in memory") logger.warn("Another presentation is still in memory")
@ -53,7 +53,7 @@ func open_presentation(path: String, zip: bool) -> Error:
is_open = true is_open = true
return Error.OK return Error.OK
# Close the currently open presentation # Close opened presentation
func close_presentation() -> Error: func close_presentation() -> Error:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")
@ -120,14 +120,11 @@ func read_resource(path: String) -> Resource:
file.store_buffer(resource_bytes) file.store_buffer(resource_bytes)
file.close() file.close()
var resource: Resource = null var resource: Resource = null
if path.ends_with(".ttf") or path.ends_with(".otf") or path.ends_with(".woff") or path.ends_with(".woff2"): # Attempt workaround to mitigate loading issues
resource = FontFile.new() resource = _read_resource_workaround(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1]))
resource.load_dynamic_font(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1])) if resource == null: # Workaround did not work, load resource normally
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:
resource = ResourceLoader.load(misc.get_temporary_dir() + "/" + str(split_path[split_path.size()-1])) 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") logger.warn("Resource could not be read, resource is null")
return null return null
return resource return resource
@ -135,13 +132,29 @@ func read_resource(path: String) -> Resource:
if !FileAccess.file_exists(directorypath + "/" + path): if !FileAccess.file_exists(directorypath + "/" + path):
logger.error("Requested resource is missing in presentation directory") logger.error("Requested resource is missing in presentation directory")
return null return null
var resource: Resource = ResourceLoader.load(directorypath + "/" + path) var resource: Resource = null
if 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") logger.warn("Resource could not be read, resource is null")
return null return null
return resource 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: func read_resource_unsafe(path: String) -> Resource:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")
@ -173,7 +186,7 @@ func read_resource_unsafe(path: String) -> Resource:
return null return null
return resource 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: func file_exists(path: String) -> bool:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")
@ -232,7 +245,7 @@ func get_entrypoint() -> Node:
return null return null
return entrypoint return entrypoint
# Return presentation topic # Return the presentation topic
func get_topic() -> String: func get_topic() -> String:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")
@ -243,7 +256,7 @@ func get_topic() -> String:
return "" return ""
return manifest["topic"] return manifest["topic"]
# Return presentation authors # Return the presentation authors
func get_authors() -> String: func get_authors() -> String:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")
@ -258,7 +271,7 @@ func get_authors() -> String:
else: authors = authors + ", " + author else: authors = authors + ", " + author
return authors return authors
# Return display ratio # Return the presentation display ratio
func get_ratio() -> String: func get_ratio() -> String:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")
@ -269,7 +282,7 @@ func get_ratio() -> String:
return "" return ""
return manifest["ratio"] return manifest["ratio"]
# Return display resolution from display ratio # Return the display resolution (derived from display ratio)
func get_ratio_resolution() -> Vector2i: func get_ratio_resolution() -> Vector2i:
if !is_open: if !is_open:
logger.warn("No presentation is currently opened") logger.warn("No presentation is currently opened")

172
src/processor.gd Normal file
View file

@ -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 <https://www.gnu.org/licenses/>. ###
##############################################################################
### 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

View file

@ -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 <https://www.gnu.org/licenses/>. ###
##############################################################################
### src/ui/welcome.gd (Welcome UI) ###
### ###
### This source file controls the welcome user interface scene. ###
##############################################################################
extends Node extends Node
func switch_mode() -> void: 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("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").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("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("OpenArchiveButton").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").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").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").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) 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 stylebox.corner_radius_bottom_right = resolution_.x/30
return stylebox return stylebox
func open_presentation_picker() -> void: func open_presentation_archive_picker() -> void:
logger.info("Opening presentation") logger.info("Opening presentation")
var fd: FileDialog = FileDialog.new() var fd: FileDialog = FileDialog.new()
fd.access = FileDialog.ACCESS_FILESYSTEM fd.access = FileDialog.ACCESS_FILESYSTEM
@ -70,12 +94,28 @@ func open_presentation_picker() -> void:
fd.add_filter("*.zip", "") fd.add_filter("*.zip", "")
fd.mode_overrides_title = false fd.mode_overrides_title = false
fd.show_hidden_files = true 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.use_native_dialog = true
fd.cancel_button_text = "Cancel" fd.cancel_button_text = "Cancel"
fd.ok_button_text = "Present" fd.ok_button_text = "Present"
fd.visible = true 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) add_child(fd)
func open_presentation(path: String) -> void: func open_presentation(path: String) -> void:
@ -91,12 +131,11 @@ func open_presentation(path: String) -> void:
preader.read_manifest() preader.read_manifest()
preader.read_entrypoint() preader.read_entrypoint()
# Update window properties # 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_title("Presencode » Presenting \"" + preader.get_topic() + "\" by \"" + preader.get_authors() + "\"")
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
DisplayServer.window_set_size(preader.get_ratio_resolution()) DisplayServer.window_set_size(preader.get_ratio_resolution())
DisplayServer.window_set_min_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_size = preader.get_ratio_resolution()
get_tree().root.content_scale_mode = Window.CONTENT_SCALE_MODE_VIEWPORT 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_aspect = Window.CONTENT_SCALE_ASPECT_KEEP
@ -109,12 +148,17 @@ func open_presentation(path: String) -> void:
ui_engine.uninitialize() ui_engine.uninitialize()
func update_splash() -> void: func update_splash() -> void:
logger.diag("Updating splash text") var splash: String = ""
get_node("Wallpaper/Welcome/Splash").text = "[center]" + [ var splash_ok: bool = false
while !splash_ok:
splash = [
"The first native code-based presentation viewer.", # Tagline "The first native code-based presentation viewer.", # Tagline
"Presenting your presentations since 2023!", # Inspired by LulzSec's "Laughing at your security since 2011!" "Presenting your presentations since 2023!", # Inspired by LulzSec's "Laughing at your security since 2011!"
"Made using Godot", # FACT "Made using Godot", # FACT
"nice", # nice :) "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 "[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" "Also try LibreOffice!", # Inspired by Minecraft's "Also play Terraria" and Terraria's "Also play Minecraft"
"... exists.", # We finished the sentence. "... exists.", # We finished the sentence.
@ -129,11 +173,13 @@ func update_splash() -> void:
"Extra spicy!", # Where did I leave my chilli? "Extra spicy!", # Where did I leave my chilli?
"--- Debugging process stopped ---", # Godot message after stopping a debug build "--- 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 "/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. "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 "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 "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
"https://billgates.sex is real" # Reference to MattKC "happy :)", # happy :)
].pick_random() + "[/center]" "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]"

View file

@ -15,16 +15,22 @@ var initialized: bool = false
var ui_mode: UIMode = UIMode.UNKNOWN var ui_mode: UIMode = UIMode.UNKNOWN
var resolution: String = "960x540" var resolution: String = "960x540"
# Initialize engine
func initialize(loader_: Control) -> void: func initialize(loader_: Control) -> void:
if initialized: if initialized:
logger.error("The UI Engine been initialized already") logger.error("The UI Engine been initialized already")
return return
logger.info("Initializing UI Engine") logger.info("Initializing UI Engine")
# Set variables
loader = loader_ loader = loader_
initialized = true initialized = true
# Switch mode
logger.diag("Switching to welcome mode") logger.diag("Switching to welcome mode")
switch_mode(UIMode.WELCOME) switch_mode(UIMode.WELCOME)
# Update resolution to next best
logger.diag("Updating resolution to next best resolution")
update_resolution(misc.get_best_resolution()) update_resolution(misc.get_best_resolution())
# Update window properties
logger.diag("Updating window properties") logger.diag("Updating window properties")
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
DisplayServer.window_set_min_size(Vector2i(960, 540)) 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_aspect = Window.CONTENT_SCALE_ASPECT_KEEP
get_tree().root.content_scale_factor = 1.0 get_tree().root.content_scale_factor = 1.0
# Uninitialize engine
func uninitialize() -> void: func uninitialize() -> void:
if !initialized: if !initialized:
logger.error("The UI Engine is not initialized.") logger.error("The UI Engine is not initialized.")
return return
logger.info("Uninitializing UI Engine") logger.info("Uninitializing UI Engine")
# Reset variables
loader = null loader = null
initialized = false initialized = false
resolution = "960x540" resolution = "960x540"
ui_mode = UIMode.UNKNOWN ui_mode = UIMode.UNKNOWN
logger.info("Unloading UI mode") logger.info("Unloading UI mode")
# Unload current UI mode
ui_mode_node.queue_free() ui_mode_node.queue_free()
ui_mode_node = null ui_mode_node = null
func _process(_delta: float) -> void: # Switch UI mode
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: func switch_mode(mode: UIMode) -> void:
if !initialized: if !initialized:
logger.error("The UI Engine hasn't been initialized yet") logger.error("The UI Engine hasn't been initialized yet")
return 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)) logger.info("Switching to mode " + str(mode))
# Remove current UI mode node
if ui_mode_node != null: loader.remove_child(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): match(mode):
UIMode.WELCOME: UIMode.WELCOME:
ui_mode_node = ResourceLoader.load("res://ui/Welcome.tscn").instantiate() ui_mode_node = ResourceLoader.load("res://ui/Welcome.tscn").instantiate()
ui_mode = UIMode.WELCOME ui_mode = UIMode.WELCOME
# Add UI mode node to Loader
loader.add_child(ui_mode_node) loader.add_child(ui_mode_node)
# UI mode node's switch_mode() takes over
ui_mode_node.switch_mode() ui_mode_node.switch_mode()
# Updates the resolution
func update_resolution(new_resolution: Vector2i) -> void: func update_resolution(new_resolution: Vector2i) -> void:
if !initialized: if !initialized:
logger.error("The UI Engine hasn't been initialized yet") logger.error("The UI Engine hasn't been initialized yet")
return return
# Update window properties
DisplayServer.window_set_size(new_resolution) DisplayServer.window_set_size(new_resolution)
get_tree().root.content_scale_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) resolution = str(new_resolution.x) + "x" + str(new_resolution.y)
# Call current UI mode's update_resolution() function
match(ui_mode): match(ui_mode):
UIMode.UNKNOWN: return UIMode.UNKNOWN: return
UIMode.WELCOME: ui_mode_node.update_resolution(new_resolution) UIMode.WELCOME: ui_mode_node.update_resolution(new_resolution)

View file

@ -94,7 +94,7 @@ Licensed under the [b]GNU General Public License version 3[/b].
Thank you for using Presencode <3[/center]" Thank you for using Presencode <3[/center]"
scroll_active = false 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 layout_mode = 1
anchors_preset = 4 anchors_preset = 4
anchor_top = 0.5 anchor_top = 0.5
@ -104,7 +104,19 @@ offset_top = -19.0
offset_right = 278.0 offset_right = 278.0
offset_bottom = 19.0 offset_bottom = 19.0
grow_vertical = 2 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"]] [node name="EditorButton" type="Button" parent="Wallpaper/Welcome" groups=["font_one", "font_one_normal"]]
layout_mode = 1 layout_mode = 1
@ -122,4 +134,5 @@ grow_vertical = 2
text = "Edit presentation" text = "Edit presentation"
[connection signal="pressed" from="Wallpaper/Welcome" to="." method="update_splash"] [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"]