Changed to vehicle sensor instead of single sensor types
@@ -14,15 +14,6 @@ var WGS84_AB = WGS84_A_M * WGS84_B_M
|
||||
var g = 9.80665
|
||||
|
||||
|
||||
func calc_time_diff(timestamp_1, timestamp_2):
|
||||
if timestamp_2 < 3600 and timestamp_1 > 82800:
|
||||
return (timestamp_2 + 86400) - timestamp_1
|
||||
return timestamp_2 - timestamp_1
|
||||
|
||||
func is_timestamp_newer(old_timestamp: float, new_timestamp: float) -> bool:
|
||||
if old_timestamp > 82800. and new_timestamp < 3600: # earlier than 23 hours
|
||||
return true
|
||||
return old_timestamp < new_timestamp
|
||||
|
||||
func calc_wgs84_altitude(latitude_rad: float):
|
||||
var theta_rad = abs(latitude_rad)
|
||||
|
||||
|
Before Width: | Height: | Size: 167 B |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ivppb5cqrsak"
|
||||
path="res://.godot/imported/NinePatchWindow-InnerRecess.png-07bd09e8d845ac941af5cf12e9f19c54.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie-console/assets/NinePatchWindow-InnerRecess.png"
|
||||
dest_files=["res://.godot/imported/NinePatchWindow-InnerRecess.png-07bd09e8d845ac941af5cf12e9f19c54.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 167 B |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d1w4w4yi6p3w3"
|
||||
path="res://.godot/imported/NinePatchWindowInner.png-47f4d96fc18c0a2a90855797a8cd0e05.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie-console/assets/NinePatchWindowInner.png"
|
||||
dest_files=["res://.godot/imported/NinePatchWindowInner.png-47f4d96fc18c0a2a90855797a8cd0e05.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 166 B |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cs676riq457do"
|
||||
path="res://.godot/imported/NinePatchWindowOuter.png-bca6314f960490a8a5e8b837d843d94e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie-console/assets/NinePatchWindowOuter.png"
|
||||
dest_files=["res://.godot/imported/NinePatchWindowOuter.png-bca6314f960490a8a5e8b837d843d94e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bhps6csrl6lef"
|
||||
path="res://.godot/imported/close.png-9f5b597652eecfc041a6acfb8a79a5f3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie-console/assets/close.png"
|
||||
dest_files=["res://.godot/imported/close.png-9f5b597652eecfc041a6acfb8a79a5f3.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,34 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ckoblpvpivjma"
|
||||
path="res://.godot/imported/icon.png-b025e725a72f01c7e881efaf61058c1e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie-console/assets/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-b025e725a72f01c7e881efaf61058c1e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,17 +0,0 @@
|
||||
[gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://b7k8yo7jfhpue"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://d1w4w4yi6p3w3" path="res://addons/loggie-console/assets/NinePatchWindowInner.png" id="1_vngi6"]
|
||||
|
||||
[resource]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
texture = ExtResource("1_vngi6")
|
||||
texture_margin_left = 2.0
|
||||
texture_margin_top = 2.0
|
||||
texture_margin_right = 2.0
|
||||
texture_margin_bottom = 2.0
|
||||
axis_stretch_horizontal = 1
|
||||
axis_stretch_vertical = 1
|
||||
region_rect = Rect2(0, 0, 36, 36)
|
||||
@@ -1,16 +0,0 @@
|
||||
[gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://dolgdjpxlvt8f"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://ivppb5cqrsak" path="res://addons/loggie-console/assets/NinePatchWindow-InnerRecess.png" id="1_lfme6"]
|
||||
|
||||
[resource]
|
||||
content_margin_left = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
texture = ExtResource("1_lfme6")
|
||||
texture_margin_left = 2.0
|
||||
texture_margin_top = 2.0
|
||||
texture_margin_right = 2.0
|
||||
texture_margin_bottom = 2.0
|
||||
axis_stretch_horizontal = 1
|
||||
axis_stretch_vertical = 1
|
||||
region_rect = Rect2(0, 0, 36, 36)
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://2ie83nlho48h"
|
||||
path="res://.godot/imported/minimize.png-3f9dff399b51d5e6e7a64266986b96cf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie-console/assets/minimize.png"
|
||||
dest_files=["res://.godot/imported/minimize.png-3f9dff399b51d5e6e7a64266986b96cf.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,13 +0,0 @@
|
||||
[gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://bpkqcbx15ejak"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://cs676riq457do" path="res://sprites/ui/NinePatchWindowBrown.png" id="1_4h7p8"]
|
||||
|
||||
[resource]
|
||||
texture = ExtResource("1_4h7p8")
|
||||
texture_margin_left = 2.0
|
||||
texture_margin_top = 2.0
|
||||
texture_margin_right = 2.0
|
||||
texture_margin_bottom = 2.0
|
||||
axis_stretch_horizontal = 1
|
||||
axis_stretch_vertical = 1
|
||||
region_rect = Rect2(0, 0, 36, 36)
|
||||
@@ -1,21 +0,0 @@
|
||||
[gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://cfafk3ybc15uo"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://cs676riq457do" path="res://sprites/ui/NinePatchWindowBrown.png" id="1_8qfda"]
|
||||
|
||||
[resource]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
texture = ExtResource("1_8qfda")
|
||||
texture_margin_left = 2.0
|
||||
texture_margin_top = 2.0
|
||||
texture_margin_right = 2.0
|
||||
texture_margin_bottom = 2.0
|
||||
expand_margin_left = 4.0
|
||||
expand_margin_top = 24.0
|
||||
expand_margin_right = 4.0
|
||||
expand_margin_bottom = 4.0
|
||||
axis_stretch_horizontal = 1
|
||||
axis_stretch_vertical = 1
|
||||
region_rect = Rect2(0, 0, 36, 36)
|
||||
@@ -1,7 +0,0 @@
|
||||
[plugin]
|
||||
|
||||
name="Loggie Console"
|
||||
description="Interactive debug console built upon the Loggie logging framework"
|
||||
author="Smitner Studio"
|
||||
version="1.0"
|
||||
script="plugin.gd"
|
||||
@@ -1,16 +0,0 @@
|
||||
@tool
|
||||
class_name LoggieConsoleEditorPlugin extends EditorPlugin
|
||||
|
||||
const AUTOLOAD_NAME = &"LoggieConsoleAutoload"
|
||||
|
||||
func _enter_tree():
|
||||
add_autoload_singleton(AUTOLOAD_NAME, "res://addons/loggie-console/scenes/console.tscn")
|
||||
|
||||
func _exit_tree():
|
||||
remove_autoload_singleton(AUTOLOAD_NAME)
|
||||
|
||||
func _enable_plugin() -> void:
|
||||
print("%s enabled" % AUTOLOAD_NAME)
|
||||
|
||||
func _disable_plugin() -> void:
|
||||
print("%s disabled" % AUTOLOAD_NAME)
|
||||
@@ -1 +0,0 @@
|
||||
uid://b8ul8pbaeqx2l
|
||||
@@ -1,55 +0,0 @@
|
||||
@tool
|
||||
class_name LoggieConsoleFilterState extends Resource
|
||||
|
||||
## Persistent filter state resource for LoggieConsole
|
||||
##
|
||||
## Stores filtering criteria (domains, log levels, search terms) in a format compatible
|
||||
## with Godot's Resource system while maintaining compatibility with LogBuffer.FilterState.
|
||||
## This enables console filter settings to persist between application sessions.
|
||||
|
||||
|
||||
## List of domain names that are currently enabled for display
|
||||
@export var enabled_domains: Array[String] = []
|
||||
|
||||
## Minimum log level to display (stored as int for persistence compatibility)
|
||||
@export var min_log_level: int = LoggieEnums.LogLevel.DEBUG
|
||||
|
||||
## Text search filter - only messages containing this text will be shown
|
||||
@export var text_search: String = ""
|
||||
|
||||
## Whether to show only messages that include stack traces
|
||||
@export var show_stack_only: bool = false
|
||||
|
||||
## Converts this persistent resource to runtime FilterState object
|
||||
## Returns: A new LogBuffer.FilterState instance with matching filter criteria
|
||||
func to_filter_state() -> LogBuffer.FilterState:
|
||||
var filter_state: LogBuffer.FilterState = LogBuffer.FilterState.new()
|
||||
filter_state.enabled_domains = enabled_domains.duplicate()
|
||||
filter_state.min_log_level = min_log_level as LoggieEnums.LogLevel
|
||||
filter_state.text_search = text_search
|
||||
filter_state.show_stack_only = show_stack_only
|
||||
return filter_state
|
||||
|
||||
## Populates this resource from runtime FilterState object
|
||||
## Param filter_state: The LogBuffer.FilterState to copy data from
|
||||
func from_filter_state(filter_state: LogBuffer.FilterState) -> void:
|
||||
enabled_domains = filter_state.enabled_domains.duplicate()
|
||||
min_log_level = filter_state.min_log_level as int
|
||||
text_search = filter_state.text_search
|
||||
show_stack_only = filter_state.show_stack_only
|
||||
|
||||
## Validates and sanitizes all data after loading from disk
|
||||
## Ensures all fields have valid values even if the saved file was corrupted
|
||||
func validate() -> void:
|
||||
# Ensure domain array exists
|
||||
if enabled_domains == null:
|
||||
enabled_domains = []
|
||||
|
||||
# Validate log level is within valid enum range
|
||||
if min_log_level < 0 or min_log_level >= LoggieEnums.LogLevel.size():
|
||||
min_log_level = LoggieEnums.LogLevel.DEBUG
|
||||
Loggie.msg("Invalid log level, reset to DEBUG").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
|
||||
# Ensure search text is never null
|
||||
if text_search == null:
|
||||
text_search = ""
|
||||
@@ -1 +0,0 @@
|
||||
uid://b6iuus8kvu3y3
|
||||
@@ -1,294 +0,0 @@
|
||||
@tool
|
||||
class_name LoggieConsoleSettings extends Resource
|
||||
|
||||
## Complete persistent settings for LoggieConsole
|
||||
##
|
||||
## This resource stores all console state including window properties, filter settings,
|
||||
## component states, and discovered domains. It automatically saves when the console
|
||||
## exits and loads on startup to restore the user's previous console configuration.
|
||||
|
||||
const LoggieConsoleFilterState = preload("res://addons/loggie-console/resources/loggie_console_filter_state.gd")
|
||||
|
||||
# Configuration constants
|
||||
const MIN_WINDOW_WIDTH: int = 400
|
||||
const MIN_WINDOW_HEIGHT: int = 300
|
||||
const DEFAULT_WINDOW_WIDTH: int = 800
|
||||
const DEFAULT_WINDOW_HEIGHT: int = 400
|
||||
const SCREEN_MARGIN_PIXELS: int = 100 # 50px on each side
|
||||
const WINDOW_EDGE_MARGIN: int = 20
|
||||
const WINDOW_MIN_MARGIN: int = 10
|
||||
|
||||
|
||||
## Filter state for log message display (domains, levels, search terms)
|
||||
@export var filter_state: LoggieConsoleFilterState
|
||||
|
||||
## Currently enabled domain names for filtering
|
||||
@export var enabled_domains: Array[String] = []
|
||||
|
||||
## All domain names discovered during runtime (persisted for next session)
|
||||
@export var all_known_domains: Array[String] = []
|
||||
|
||||
## Console window screen position (-1, -1 means use default centering)
|
||||
@export var window_position: Vector2i = Vector2i(-1, -1)
|
||||
|
||||
## Console window size in pixels
|
||||
@export var window_size: Vector2i = Vector2i(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)
|
||||
|
||||
## Whether console is currently minimized
|
||||
@export var is_minimized: bool = false
|
||||
|
||||
## Whether console window is visible
|
||||
@export var is_visible: bool = true
|
||||
|
||||
## Display/screen index where console was last positioned (-1 means primary display)
|
||||
@export var display_index: int = -1
|
||||
|
||||
## Whether subwindows were embedded when settings were last saved
|
||||
@export var was_embedded_subwindows: bool = false
|
||||
|
||||
## Current log level filter (stored as int for persistence)
|
||||
@export var log_level: int = LoggieEnums.LogLevel.DEBUG
|
||||
|
||||
## Whether to auto-scroll to newest messages
|
||||
@export var scroll_follow_enabled: bool = true
|
||||
|
||||
## Current text search filter term
|
||||
@export var search_text: String = ""
|
||||
|
||||
## Whether to show only messages with stack traces
|
||||
@export var stack_filter_enabled: bool = false
|
||||
|
||||
## Maximum number of messages to keep in buffer before cleanup
|
||||
@export var max_messages: int = 5000
|
||||
|
||||
## Text size multiplier for console fonts (0.5 to 2.0, default 1.0)
|
||||
@export var text_size_multiplier: float = 1.0
|
||||
|
||||
## Restore button alignment position (0=TOP_LEFT, 1=TOP_RIGHT, 2=BOTTOM_LEFT, 3=BOTTOM_RIGHT)
|
||||
@export var restore_button_alignment: int = 1 # Default to TOP_RIGHT
|
||||
|
||||
## Settings file format version for future migration compatibility
|
||||
@export var version: int = 1
|
||||
|
||||
## Unix timestamp when settings were last saved
|
||||
@export var last_saved: float = 0.0
|
||||
|
||||
## Initializes default settings values
|
||||
##
|
||||
## Creates the filter state resource if it doesn't exist. This ensures that
|
||||
## all required sub-resources are properly initialized even when creating
|
||||
## settings from scratch.
|
||||
func _init() -> void:
|
||||
if not filter_state:
|
||||
filter_state = LoggieConsoleFilterState.new()
|
||||
|
||||
## Validates and sanitizes all data after loading from disk
|
||||
## Ensures all fields have valid values even if the saved file was corrupted
|
||||
func validate() -> void:
|
||||
# Ensure filter state exists and is valid
|
||||
if not filter_state:
|
||||
filter_state = LoggieConsoleFilterState.new()
|
||||
filter_state.validate()
|
||||
|
||||
# Ensure domain arrays exist
|
||||
if enabled_domains == null:
|
||||
enabled_domains = []
|
||||
if all_known_domains == null:
|
||||
all_known_domains = []
|
||||
|
||||
# Validate log level is within enum range
|
||||
if log_level < 0 or log_level >= LoggieEnums.LogLevel.size():
|
||||
log_level = LoggieEnums.LogLevel.DEBUG
|
||||
Loggie.msg("Invalid log level, reset to DEBUG").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
|
||||
# Enforce minimum and maximum window size for usability
|
||||
_validate_window_size()
|
||||
|
||||
# Ensure search text is never null
|
||||
if search_text == null:
|
||||
search_text = ""
|
||||
|
||||
# Validate text size multiplier is within reasonable bounds
|
||||
if text_size_multiplier < 0.5 or text_size_multiplier > 2.0:
|
||||
text_size_multiplier = 1.0
|
||||
Loggie.msg("Invalid text size multiplier, reset to 1.0").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
|
||||
# Validate restore button alignment is within enum range (0-3)
|
||||
if restore_button_alignment < 0 or restore_button_alignment > 3:
|
||||
restore_button_alignment = 1 # Default to TOP_RIGHT
|
||||
Loggie.msg("Invalid restore button alignment, reset to TOP_RIGHT").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
|
||||
# Update save timestamp
|
||||
last_saved = Time.get_unix_time_from_system()
|
||||
|
||||
## Validates and ensures window size fits within available screen space
|
||||
##
|
||||
## Enforces minimum size for usability (400x300) and maximum size based on
|
||||
## screen dimensions. Checks all connected displays to find the largest
|
||||
## available space and clamps the window size accordingly.
|
||||
func _validate_window_size() -> void:
|
||||
# Get the largest available screen to determine maximum constraints
|
||||
var max_screen_size: Vector2i = Vector2i.ZERO
|
||||
var screens: int = DisplayServer.get_screen_count()
|
||||
|
||||
for i: int in screens:
|
||||
var screen_rect: Rect2i = DisplayServer.screen_get_usable_rect(i)
|
||||
max_screen_size.x = maxi(max_screen_size.x, screen_rect.size.x)
|
||||
max_screen_size.y = maxi(max_screen_size.y, screen_rect.size.y)
|
||||
|
||||
# Fallback if no screens detected
|
||||
if max_screen_size == Vector2i.ZERO:
|
||||
max_screen_size = Vector2i(1920, 1080)
|
||||
|
||||
# Enforce minimum window size for usability
|
||||
window_size.x = maxi(window_size.x, MIN_WINDOW_WIDTH)
|
||||
window_size.y = maxi(window_size.y, MIN_WINDOW_HEIGHT)
|
||||
|
||||
# Enforce maximum window size to fit on screen (with margins)
|
||||
var max_width: int = max_screen_size.x - SCREEN_MARGIN_PIXELS
|
||||
var max_height: int = max_screen_size.y - SCREEN_MARGIN_PIXELS
|
||||
|
||||
if window_size.x > max_width:
|
||||
window_size.x = max_width
|
||||
Loggie.msg("Window width too large, clamped to %d" % max_width).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
|
||||
if window_size.y > max_height:
|
||||
window_size.y = max_height
|
||||
Loggie.msg("Window height too large, clamped to %d" % max_height).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
|
||||
## Detects if embed_subwindows setting has changed since last save
|
||||
## Returns: true if setting has changed and window positioning needs reset
|
||||
func _has_embed_setting_changed() -> bool:
|
||||
var current_embedded = ProjectSettings.get_setting("display/window/subwindows/embed_subwindows", true)
|
||||
return current_embedded != was_embedded_subwindows
|
||||
|
||||
## Updates the stored embed_subwindows setting to current project setting
|
||||
##
|
||||
## This should be called whenever settings are saved to track the current
|
||||
## embed_subwindows state for future change detection.
|
||||
func update_embed_setting() -> void:
|
||||
was_embedded_subwindows = ProjectSettings.get_setting("display/window/subwindows/embed_subwindows", true)
|
||||
|
||||
## Validates and corrects window position for current display setup
|
||||
## Returns: A valid window position that will be visible on screen
|
||||
func validate_window_position() -> Vector2i:
|
||||
# Check if embed_subwindows setting has changed
|
||||
var embed_changed = _has_embed_setting_changed()
|
||||
var current_embedded = ProjectSettings.get_setting("display/window/subwindows/embed_subwindows", true)
|
||||
|
||||
# If embed setting changed and window was previously invisible, reset position
|
||||
if embed_changed and not is_visible:
|
||||
Loggie.msg("embed_subwindows changed from %s to %s, resetting window position" % [was_embedded_subwindows, current_embedded]).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
update_embed_setting()
|
||||
# Reset to default centering
|
||||
var target_display = _get_target_display()
|
||||
var target_rect: Rect2i = DisplayServer.screen_get_usable_rect(target_display)
|
||||
window_position = Vector2i(-1, -1) # Mark for centering
|
||||
display_index = target_display
|
||||
return _center_window_on_screen(target_rect)
|
||||
|
||||
# Update embed setting for future comparisons
|
||||
update_embed_setting()
|
||||
|
||||
# Use default centering if position is not set
|
||||
if window_position == Vector2i(-1, -1):
|
||||
var target_display = _get_target_display()
|
||||
var target_rect: Rect2i = DisplayServer.screen_get_usable_rect(target_display)
|
||||
return _center_window_on_screen(target_rect)
|
||||
|
||||
# If we have a specific display preference, validate against it first
|
||||
if display_index >= 0 and display_index < DisplayServer.get_screen_count():
|
||||
var preferred_rect: Rect2i = DisplayServer.screen_get_usable_rect(display_index)
|
||||
var validated_pos = _clamp_window_to_screen(window_position, preferred_rect)
|
||||
if validated_pos != window_position:
|
||||
Loggie.msg("Window position adjusted to fit display %d" % display_index).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return validated_pos
|
||||
|
||||
# Check if window can fit properly on any connected display
|
||||
var screens: int = DisplayServer.get_screen_count()
|
||||
var best_display: int = -1
|
||||
var best_pos: Vector2i = window_position
|
||||
|
||||
for i: int in screens:
|
||||
var screen_rect: Rect2i = DisplayServer.screen_get_usable_rect(i)
|
||||
var window_rect: Rect2i = Rect2i(window_position, window_size)
|
||||
|
||||
# Check if window intersects with this screen
|
||||
if screen_rect.intersects(window_rect):
|
||||
best_display = i
|
||||
best_pos = _clamp_window_to_screen(window_position, screen_rect)
|
||||
# If the window fits perfectly on this screen, use it
|
||||
if best_pos == window_position:
|
||||
display_index = i
|
||||
return window_position
|
||||
break
|
||||
|
||||
# If we found a suitable display, use the clamped position
|
||||
if best_display >= 0:
|
||||
display_index = best_display
|
||||
if best_pos != window_position:
|
||||
Loggie.msg("Window position clamped to fit display %d" % best_display).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return best_pos
|
||||
|
||||
# Window is completely off-screen, center it on target display
|
||||
var target_display = _get_target_display()
|
||||
var target_rect: Rect2i = DisplayServer.screen_get_usable_rect(target_display)
|
||||
display_index = target_display
|
||||
Loggie.msg("Window position off-screen, centering on display %d" % target_display).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return _center_window_on_screen(target_rect)
|
||||
|
||||
## Centers the window on the given screen rectangle with margins
|
||||
##
|
||||
## Calculates a centered position that keeps the window fully visible with
|
||||
## appropriate margins from screen edges.
|
||||
## @param screen_rect: The target screen's usable rectangle
|
||||
## @return: Position that centers the window with 20px margins
|
||||
func _center_window_on_screen(screen_rect: Rect2i) -> Vector2i:
|
||||
var margin_vector: Vector2i = Vector2i(WINDOW_EDGE_MARGIN, WINDOW_EDGE_MARGIN)
|
||||
var available_size: Vector2i = screen_rect.size - margin_vector * 2
|
||||
var centered_offset: Vector2i = (available_size - window_size) / 2
|
||||
return screen_rect.position + margin_vector + centered_offset
|
||||
|
||||
## Clamps window position to ensure it fits entirely within screen bounds
|
||||
##
|
||||
## Adjusts the position to keep the entire window visible on the screen,
|
||||
## respecting minimum margins from edges.
|
||||
## @param pos: The desired window position
|
||||
## @param screen_rect: The target screen's rectangle
|
||||
## @return: Adjusted position that keeps the entire window visible
|
||||
func _clamp_window_to_screen(pos: Vector2i, screen_rect: Rect2i) -> Vector2i:
|
||||
var margin: Vector2i = Vector2i(WINDOW_MIN_MARGIN, WINDOW_MIN_MARGIN)
|
||||
var usable_rect: Rect2i = Rect2i(
|
||||
screen_rect.position + margin,
|
||||
screen_rect.size - margin * 2
|
||||
)
|
||||
|
||||
# Ensure window fits within usable area
|
||||
var max_pos: Vector2i = usable_rect.position + usable_rect.size - window_size
|
||||
|
||||
var clamped_pos: Vector2i = Vector2i(
|
||||
clampi(pos.x, usable_rect.position.x, max_pos.x),
|
||||
clampi(pos.y, usable_rect.position.y, max_pos.y)
|
||||
)
|
||||
|
||||
return clamped_pos
|
||||
|
||||
## Gets the target display index for window positioning
|
||||
## Returns: Valid display index (0 if saved display no longer exists)
|
||||
func _get_target_display() -> int:
|
||||
# Use saved display if it still exists
|
||||
if display_index >= 0 and display_index < DisplayServer.get_screen_count():
|
||||
return display_index
|
||||
|
||||
# Fall back to primary display (index 0)
|
||||
return 0
|
||||
|
||||
## Gets the log level as a proper enum value
|
||||
## Returns: The current log level as LoggieEnums.LogLevel
|
||||
func get_log_level() -> LoggieEnums.LogLevel:
|
||||
return log_level as LoggieEnums.LogLevel
|
||||
|
||||
## Sets the log level from an enum value
|
||||
## @param level: The new log level to store
|
||||
func set_log_level(level: LoggieEnums.LogLevel) -> void:
|
||||
log_level = level as int
|
||||
@@ -1 +0,0 @@
|
||||
uid://w2lnu7heu47v
|
||||
@@ -1,563 +0,0 @@
|
||||
## Enhanced LoggieConsole with persistence, minimize/restore, and multi-display support
|
||||
##
|
||||
## This console window provides an advanced debugging interface for the Loggie logging system.
|
||||
## Features include:
|
||||
## - Persistent window positioning and settings across sessions
|
||||
## - Multi-display awareness for non-embedded subwindows
|
||||
## - Domain-based filtering with dynamic discovery
|
||||
## - Minimize/restore functionality with floating restore button
|
||||
## - Complete component-based architecture for extensibility
|
||||
##
|
||||
## The console automatically saves all settings when closed or minimized, and restores
|
||||
## the exact previous state on next startup, including window position, size, filters,
|
||||
## and discovered domains.
|
||||
class_name LoggieConsole extends Window
|
||||
|
||||
# Constants
|
||||
const LoggieConsoleChannel = preload("res://addons/loggie-console/scripts/loggie_console_channel.gd")
|
||||
const ConsoleSettingsManager = preload("res://addons/loggie-console/scripts/managers/console_settings_manager.gd")
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
const RestoreButtonScene := preload("res://addons/loggie-console/scenes/restore_button.tscn")
|
||||
const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd")
|
||||
|
||||
|
||||
# UI Constants
|
||||
const WINDOW_MARGIN_PIXELS: int = 5
|
||||
|
||||
# Display detection constants
|
||||
const DEFAULT_DOMAIN_OFFSET: int = 1 # +1 for default domain in total domain count
|
||||
|
||||
|
||||
# Exported component references - automatically assigned from scene
|
||||
@export var buffer: LogBuffer ## Log message display and filtering component
|
||||
@export var output_level: LogLevelFilter ## Log level selection component
|
||||
@export var domain_filter: PanelDomainSelector ## Domain multi-selection component
|
||||
@export var controls: ConsoleControls ## Control buttons and settings component
|
||||
@export var status_display: StatusDisplay ## Status and statistics display component
|
||||
|
||||
# Settings UI references
|
||||
var _settings_button: Button ## Settings button in controls
|
||||
var _settings_panel: LoggieConsoleSettingsPanel ## Settings panel component
|
||||
|
||||
|
||||
# Minimize/restore system state
|
||||
var _restore_button_component: RestoreButton ## Restore button component instance
|
||||
var _is_minimized: bool = false ## Current minimized state
|
||||
|
||||
# Custom Loggie channel for receiving log messages
|
||||
var _console_channel: LoggieConsoleChannel ## Direct channel integration with Loggie
|
||||
|
||||
# Component coordination
|
||||
var _filter_state: LogBuffer.FilterState ## Shared filter state between components
|
||||
|
||||
# Persistence system
|
||||
var _settings_manager: ConsoleSettingsManager ## Handles save/load operations
|
||||
var _current_settings: LoggieConsoleSettings ## Current settings instance
|
||||
|
||||
# Domain color management
|
||||
var _domain_color_manager: DomainColorManager ## Centralized color management for all components
|
||||
|
||||
## Initializes the console window and all its components
|
||||
##
|
||||
## This is the main initialization sequence that:
|
||||
## 1. Loads persistent settings from disk
|
||||
## 2. Applies window positioning and visibility settings
|
||||
## 3. Initializes all components with loaded state
|
||||
## 4. Sets up the custom Loggie channel for message reception
|
||||
## 5. Connects all necessary signals for runtime operation
|
||||
##
|
||||
## The order is critical - settings must be loaded and applied before
|
||||
## components are initialized to prevent unwanted signal emissions during startup.
|
||||
func _ready() -> void:
|
||||
# Initialize persistence system FIRST
|
||||
_settings_manager = ConsoleSettingsManager.new()
|
||||
_current_settings = _settings_manager.load_settings()
|
||||
|
||||
_restore_button_component = RestoreButtonScene.instantiate()
|
||||
_restore_button_component.restore_requested.connect(_on_restore_requested)
|
||||
|
||||
# Apply window settings before component initialization
|
||||
_apply_window_settings()
|
||||
|
||||
# Connect window signals for persistence
|
||||
close_requested.connect(_on_close_requested)
|
||||
|
||||
# Initialize components with loaded settings
|
||||
_initialize_components()
|
||||
|
||||
# Setup save triggers after initialization
|
||||
_setup_save_triggers()
|
||||
|
||||
# Setup custom channel
|
||||
_setup_console_channel()
|
||||
|
||||
# Apply loaded filters after components are ready
|
||||
_apply_loaded_settings()
|
||||
|
||||
## Called by our custom LoggieConsoleChannel with complete message data
|
||||
##
|
||||
## This is the main message reception callback that receives log messages directly
|
||||
## from the Loggie system through our custom channel. Unlike signal-based approaches,
|
||||
## this provides complete type information including LogLevel and MsgType enums.
|
||||
##
|
||||
## @param msg: The original LoggieMsg with all metadata
|
||||
## @param preprocessed_content: Fully formatted message text from Loggie
|
||||
## @param msg_type: Message type enum (ERROR, WARNING, DEBUG, STANDARD)
|
||||
## @param log_level: Log level enum (ERROR, WARN, NOTICE, INFO, DEBUG)
|
||||
func receive_log_message(msg: LoggieMsg, preprocessed_content: String, msg_type: LoggieEnums.MsgType, log_level: LoggieEnums.LogLevel) -> void:
|
||||
# Check if we need to add a new domain to our list
|
||||
if not msg.domain_name.is_empty():
|
||||
domain_filter.add_domain(msg.domain_name)
|
||||
|
||||
# Add message to buffer (buffer handles filtering internally)
|
||||
buffer.add_message(msg, preprocessed_content, msg_type, log_level)
|
||||
|
||||
## Sets up the custom Loggie channel for direct message reception
|
||||
##
|
||||
## Creates and registers a LoggieConsoleChannel that provides the console with
|
||||
## direct access to log messages including complete type information. The channel
|
||||
## is automatically added to the default channels list so it receives all messages
|
||||
## by default, without requiring explicit channel targeting in log calls.
|
||||
func _setup_console_channel() -> void:
|
||||
# Create and register our custom channel
|
||||
_console_channel = LoggieConsoleChannel.new()
|
||||
_console_channel.set_console_window(self)
|
||||
Loggie.add_channel(_console_channel)
|
||||
|
||||
# Automatically add our channel to the default channels so console receives all messages
|
||||
var current_defaults: Array = Loggie.settings.default_channels
|
||||
if not current_defaults.has("loggie_console"):
|
||||
var new_defaults: Array = current_defaults.duplicate()
|
||||
new_defaults.append("loggie_console")
|
||||
Loggie.settings.default_channels = new_defaults
|
||||
|
||||
func _setup_domain_filter() -> void:
|
||||
# Get all domains dynamically from Loggie
|
||||
var initial_domains: Array[String] = [""] # Always include default domain
|
||||
|
||||
# Get all registered domains from Loggie
|
||||
for domain_name: String in Loggie.domains.keys():
|
||||
if not initial_domains.has(domain_name):
|
||||
initial_domains.append(domain_name)
|
||||
|
||||
# Add LoggieConsole internal domain (will appear unselected by default)
|
||||
if not initial_domains.has(LoggieConsoleConstants.DOMAIN):
|
||||
var all_domains: Array[String] = initial_domains.duplicate()
|
||||
all_domains.append(LoggieConsoleConstants.DOMAIN)
|
||||
|
||||
# Initialize the domain selector with all domains (including console domain)
|
||||
domain_filter.set_domains_with_console_unselected(all_domains, initial_domains, [LoggieConsoleConstants.DOMAIN])
|
||||
else:
|
||||
# Console domain already exists, use standard initialization
|
||||
domain_filter.set_domains(initial_domains)
|
||||
domain_filter.domains_changed.connect(_on_domains_changed)
|
||||
|
||||
# Apply initial filter state to buffer (only enabled domains, not internal ones)
|
||||
_filter_state.enabled_domains = initial_domains
|
||||
buffer.set_filters(_filter_state)
|
||||
|
||||
func _on_domains_changed(enabled_domains: Array[String]) -> void:
|
||||
# Update filter state and apply to buffer
|
||||
_filter_state.enabled_domains = enabled_domains
|
||||
buffer.set_filters(_filter_state)
|
||||
|
||||
|
||||
|
||||
|
||||
## New component event handlers
|
||||
|
||||
func _on_log_level_changed(new_level: LoggieEnums.LogLevel) -> void:
|
||||
# Update filter state and apply to buffer
|
||||
_filter_state.min_log_level = new_level
|
||||
buffer.set_filters(_filter_state)
|
||||
|
||||
func _on_clear_requested() -> void:
|
||||
# Clear the buffer
|
||||
buffer.clear_all()
|
||||
|
||||
func _on_scroll_follow_changed(enabled: bool) -> void:
|
||||
# Update buffer scroll following
|
||||
buffer.scroll_following = enabled
|
||||
|
||||
func _on_text_search_changed(search_text: String) -> void:
|
||||
# Update filter state and apply to buffer
|
||||
_filter_state.text_search = search_text
|
||||
buffer.set_filters(_filter_state)
|
||||
|
||||
func _on_stack_filter_changed(enabled: bool) -> void:
|
||||
# Update filter state and apply to buffer
|
||||
_filter_state.show_stack_only = enabled
|
||||
buffer.set_filters(_filter_state)
|
||||
|
||||
func _on_buffer_filter_changed(visible_count: int, total_count: int) -> void:
|
||||
# Update status display with current counts
|
||||
var enabled_domains: Array[String] = domain_filter.get_enabled_domains()
|
||||
var total_domains: int = enabled_domains.size()
|
||||
if total_domains == 0:
|
||||
total_domains = Loggie.domains.keys().size() + DEFAULT_DOMAIN_OFFSET
|
||||
|
||||
status_display.update_status(total_count, visible_count, enabled_domains, total_domains)
|
||||
|
||||
|
||||
## Minimize/Restore System
|
||||
|
||||
|
||||
## Minimizes the console window and shows a floating restore button
|
||||
##
|
||||
## This hides the console window and creates a small floating "LOG" button in the
|
||||
## top-right corner of the main viewport. The button allows restoring the console
|
||||
## to its previous position and size. All settings are saved before minimizing
|
||||
## to ensure state persistence.
|
||||
func minimize_console() -> void:
|
||||
if _is_minimized:
|
||||
Loggie.msg("Already minimized, ignoring request").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return
|
||||
|
||||
# Save current settings including window state before minimizing
|
||||
_capture_current_state()
|
||||
_settings_manager.save_settings(_current_settings)
|
||||
|
||||
_is_minimized = true
|
||||
hide() # Hide the console window
|
||||
_show_restore_button()
|
||||
|
||||
## Creates and shows the restore button component
|
||||
##
|
||||
## Instantiates the RestoreButton scene and displays it in the root viewport.
|
||||
## Connects the restore_requested signal to handle button clicks and restore the console.
|
||||
func _show_restore_button() -> void:
|
||||
_restore_button_component.show_restore_button(get_tree().root)
|
||||
Loggie.msg("RestoreButton show_restore_button() called").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
## Handles restore button click events from the RestoreButton component
|
||||
##
|
||||
## Called when the RestoreButton component emits its restore_requested signal.
|
||||
## Triggers the console restoration process.
|
||||
func _on_restore_requested() -> void:
|
||||
restore_console()
|
||||
|
||||
## Restores the console window from minimized state
|
||||
##
|
||||
## This shows the console window at its previously saved position and size,
|
||||
## removes the floating restore button, and updates the minimized state.
|
||||
## The window is restored exactly as it was when minimized.
|
||||
func restore_console() -> void:
|
||||
if not _is_minimized:
|
||||
Loggie.msg("Not minimized, ignoring restore request").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return
|
||||
|
||||
# Restore to saved position and size from settings
|
||||
position = _current_settings.window_position
|
||||
size = _current_settings.window_size
|
||||
|
||||
_is_minimized = false
|
||||
show() # Show the console window
|
||||
_hide_restore_button()
|
||||
|
||||
## Hides and cleans up the restore button component
|
||||
##
|
||||
## Removes the RestoreButton component from the scene tree and frees its resources.
|
||||
## Ensures proper cleanup when the console is restored from minimized state.
|
||||
func _hide_restore_button() -> void:
|
||||
if _restore_button_component and is_instance_valid(_restore_button_component):
|
||||
_restore_button_component.hide_restore_button()
|
||||
Loggie.msg("Restore button component cleaned up").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
## Applies saved window properties to the console window
|
||||
## Validates position to ensure window remains visible on current display setup
|
||||
func _apply_window_settings() -> void:
|
||||
# Check if we're using embedded subwindows
|
||||
var subwindows_embedded: bool = ProjectSettings.get_setting("display/window/subwindows/embed_subwindows", true)
|
||||
|
||||
var validated_pos: Vector2i
|
||||
if subwindows_embedded and _is_outside_viewport():
|
||||
validated_pos = _center_window()
|
||||
else:
|
||||
# Use existing validation for separate windows or valid embedded positions
|
||||
validated_pos = _current_settings.validate_window_position()
|
||||
|
||||
position = validated_pos
|
||||
|
||||
# Apply saved window dimensions
|
||||
size = _current_settings.window_size
|
||||
|
||||
# Apply visibility state
|
||||
visible = _current_settings.is_visible
|
||||
|
||||
# Apply minimized state (defer restore button creation)
|
||||
_is_minimized = _current_settings.is_minimized
|
||||
if _is_minimized:
|
||||
hide()
|
||||
# Defer restore button creation until scene tree is ready
|
||||
_show_restore_button.call_deferred()
|
||||
|
||||
## Checks if console window is outside the main viewport (for embedded subwindows)
|
||||
func _is_outside_viewport() -> bool:
|
||||
var viewport_size: Vector2i = Vector2i(get_tree().root.get_visible_rect().size)
|
||||
var window_rect: Rect2i = Rect2i(_current_settings.window_position, _current_settings.window_size)
|
||||
var viewport_rect: Rect2i = Rect2i(Vector2i.ZERO, viewport_size)
|
||||
|
||||
# Return true if window is not completely within viewport or has invalid position
|
||||
return _current_settings.window_position == Vector2i(-1, -1) or not viewport_rect.encloses(window_rect)
|
||||
|
||||
## Centers the console window in the main viewport
|
||||
func _center_window() -> Vector2i:
|
||||
var viewport_size: Vector2i = Vector2i(get_tree().root.get_visible_rect().size)
|
||||
const MARGIN = 50
|
||||
var centered_pos: Vector2i = (viewport_size - _current_settings.window_size) / 2
|
||||
return Vector2i(maxi(MARGIN, centered_pos.x), maxi(MARGIN, centered_pos.y))
|
||||
|
||||
## Initializes all console components with loaded settings before connecting signals
|
||||
## This prevents unwanted signal emissions during the initialization process
|
||||
func _initialize_components() -> void:
|
||||
# Initialize base filter state
|
||||
_filter_state = LogBuffer.FilterState.new()
|
||||
|
||||
# Create domain color manager
|
||||
_domain_color_manager = DomainColorManager.new()
|
||||
|
||||
# Pass color manager to components that need it
|
||||
buffer.set_color_manager(_domain_color_manager)
|
||||
domain_filter.set_color_manager(_domain_color_manager)
|
||||
|
||||
# Load persistent settings into each component
|
||||
buffer.load_settings_from_resource(_current_settings)
|
||||
domain_filter.load_settings_from_resource(_current_settings)
|
||||
controls.load_settings_from_resource(_current_settings)
|
||||
output_level.load_settings_from_resource(_current_settings)
|
||||
|
||||
# Connect component signals for runtime interaction
|
||||
output_level.level_changed.connect(_on_log_level_changed)
|
||||
controls.clear_requested.connect(_on_clear_requested)
|
||||
controls.scroll_follow_changed.connect(_on_scroll_follow_changed)
|
||||
controls.text_search_changed.connect(_on_text_search_changed)
|
||||
controls.stack_filter_changed.connect(_on_stack_filter_changed)
|
||||
|
||||
# Configure buffer with loaded scroll following setting
|
||||
buffer.filter_changed.connect(_on_buffer_filter_changed)
|
||||
buffer.scroll_following = controls.is_scroll_follow_enabled()
|
||||
|
||||
# Set up remaining console systems
|
||||
_setup_domain_filter()
|
||||
|
||||
# Initialize settings UI
|
||||
_setup_settings_ui()
|
||||
status_display.reset()
|
||||
|
||||
## Finalizes the loading process by applying loaded filter settings
|
||||
## Called after component initialization to ensure filters work with existing messages
|
||||
func _apply_loaded_settings() -> void:
|
||||
# Apply the loaded filter state to any existing messages
|
||||
buffer.apply_loaded_filters()
|
||||
|
||||
# Sync domain filter state to ensure consistency
|
||||
domain_filter.domains_changed.emit(domain_filter.get_enabled_domains())
|
||||
|
||||
# Apply text size setting to theme
|
||||
_apply_text_size_to_theme(_current_settings.text_size_multiplier)
|
||||
|
||||
# Apply restore button alignment
|
||||
_apply_restore_button_alignment(_current_settings.restore_button_alignment)
|
||||
|
||||
## Placeholder for save triggers (not used in exit-tree persistence model)
|
||||
func _setup_save_triggers() -> void:
|
||||
pass # Settings saved only on exit tree
|
||||
|
||||
## Captures current state from window and all components into the settings resource
|
||||
## This is called before each save operation to ensure all current state is preserved
|
||||
func _capture_current_state() -> void:
|
||||
# Capture window properties
|
||||
_current_settings.window_position = position
|
||||
_current_settings.window_size = size
|
||||
_current_settings.is_visible = visible
|
||||
_current_settings.is_minimized = _is_minimized
|
||||
|
||||
# Capture current display index (important when embed_subwindows=false)
|
||||
_current_settings.display_index = _get_current_display_index()
|
||||
|
||||
# Capture current embed_subwindows setting for change detection
|
||||
_current_settings.update_embed_setting()
|
||||
|
||||
# Let each component save its own state
|
||||
buffer.save_settings_to_resource(_current_settings)
|
||||
domain_filter.save_settings_to_resource(_current_settings)
|
||||
controls.save_settings_to_resource(_current_settings)
|
||||
output_level.save_settings_to_resource(_current_settings)
|
||||
|
||||
## Manually saves current console settings to disk
|
||||
## Useful for forcing a save before potentially destructive operations
|
||||
func save_settings() -> void:
|
||||
_capture_current_state()
|
||||
_settings_manager.save_settings(_current_settings)
|
||||
|
||||
## Handles window close requests by saving settings first, then minimizing
|
||||
## This ensures settings are preserved even if the console is closed unexpectedly
|
||||
func _on_close_requested() -> void:
|
||||
# Preserve settings before minimizing
|
||||
_capture_current_state()
|
||||
_settings_manager.save_settings(_current_settings)
|
||||
|
||||
# Continue with normal minimize behavior
|
||||
minimize_console()
|
||||
|
||||
## Automatically saves settings when console exits the scene tree
|
||||
## This is the primary save mechanism ensuring settings persist between sessions
|
||||
func _exit_tree() -> void:
|
||||
if _settings_manager and _current_settings:
|
||||
_capture_current_state()
|
||||
_settings_manager.save_settings(_current_settings)
|
||||
|
||||
## Handles system-level window destruction notifications
|
||||
## Provides additional safety net for settings preservation
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_WM_CLOSE_REQUEST:
|
||||
if _settings_manager and _current_settings:
|
||||
_capture_current_state()
|
||||
_settings_manager.save_settings(_current_settings)
|
||||
|
||||
## Determines which display the console window is currently on
|
||||
## Returns: Display index (0-based) where the window center is located
|
||||
func _get_current_display_index() -> int:
|
||||
# Calculate window center point
|
||||
var window_center: Vector2i = position + size / 2
|
||||
|
||||
# Check which display contains the window center
|
||||
var screens: int = DisplayServer.get_screen_count()
|
||||
for i: int in screens:
|
||||
var screen_rect: Rect2i = DisplayServer.screen_get_usable_rect(i)
|
||||
if screen_rect.has_point(window_center):
|
||||
return i
|
||||
|
||||
# Fallback: find display with most window overlap
|
||||
var max_overlap: int = 0
|
||||
var best_display: int = 0
|
||||
var window_rect: Rect2i = Rect2i(position, size)
|
||||
|
||||
for i: int in screens:
|
||||
var screen_rect: Rect2i = DisplayServer.screen_get_usable_rect(i)
|
||||
var intersection: Rect2i = screen_rect.intersection(window_rect)
|
||||
var overlap_area: int = intersection.size.x * intersection.size.y
|
||||
if overlap_area > max_overlap:
|
||||
max_overlap = overlap_area
|
||||
best_display = i
|
||||
|
||||
return best_display
|
||||
|
||||
## Sets up the settings UI components and connects their signals
|
||||
##
|
||||
## Initializes the settings button and panel component, loads current settings,
|
||||
## and connects the component's signals for handling settings changes.
|
||||
func _setup_settings_ui() -> void:
|
||||
# Get UI component references
|
||||
_settings_button = get_node("PanelContainer/VBoxContainer/Controls/Settings")
|
||||
_settings_panel = get_node("SettingsPanel")
|
||||
|
||||
# Connect signals
|
||||
_settings_button.pressed.connect(_on_settings_button_pressed)
|
||||
_settings_panel.settings_applied.connect(_on_settings_applied)
|
||||
_settings_panel.settings_reset.connect(_on_settings_reset)
|
||||
|
||||
# Load current settings into the panel
|
||||
_settings_panel.set_text_size_multiplier(_current_settings.text_size_multiplier)
|
||||
_settings_panel.set_restore_button_alignment(_current_settings.restore_button_alignment)
|
||||
|
||||
## Called when the Settings button is pressed
|
||||
##
|
||||
## Shows the settings panel dialog with current values loaded.
|
||||
func _on_settings_button_pressed() -> void:
|
||||
_settings_panel.show_settings()
|
||||
|
||||
## Called when settings are applied via the settings panel
|
||||
##
|
||||
## Saves the new settings and applies them dynamically.
|
||||
## @param text_size_multiplier: The new text size multiplier value
|
||||
## @param restore_button_alignment: The new restore button alignment
|
||||
func _on_settings_applied(text_size_multiplier: float, restore_button_alignment: RestoreButton.Alignment) -> void:
|
||||
# Update settings
|
||||
_current_settings.text_size_multiplier = text_size_multiplier
|
||||
_current_settings.restore_button_alignment = restore_button_alignment as int
|
||||
_settings_manager.save_settings(_current_settings)
|
||||
|
||||
# Apply text size change to theme
|
||||
_apply_text_size_to_theme(text_size_multiplier)
|
||||
|
||||
# Apply restore button alignment
|
||||
_apply_restore_button_alignment(restore_button_alignment as int)
|
||||
|
||||
Loggie.msg("Settings updated: text size %.1fx, alignment %s" % [text_size_multiplier, _get_alignment_name(restore_button_alignment)]).domain(LoggieConsoleConstants.DOMAIN).info()
|
||||
|
||||
## Called when the settings are reset to defaults
|
||||
##
|
||||
## Logs the reset action for user feedback.
|
||||
func _on_settings_reset() -> void:
|
||||
Loggie.msg("Settings reset to defaults").domain(LoggieConsoleConstants.DOMAIN).info()
|
||||
|
||||
## Dynamically applies text size multiplier to the current theme
|
||||
##
|
||||
## Modifies font sizes in the theme resource at runtime to immediately
|
||||
## reflect the new text size setting without requiring a restart.
|
||||
## @param multiplier: Text size multiplier (0.5 to 2.0)
|
||||
func _apply_text_size_to_theme(multiplier: float) -> void:
|
||||
var console_theme: Theme = get_theme()
|
||||
if not console_theme:
|
||||
Loggie.msg("No theme found, cannot apply text size").domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return
|
||||
|
||||
# Define base font sizes (from theme_compact.tres)
|
||||
var base_font_sizes: Dictionary = {
|
||||
"Button/font_size": 11,
|
||||
"CheckBox/font_size": 11,
|
||||
"LineEdit/font_size": 12,
|
||||
"MenuButton/font_size": 11,
|
||||
"OptionButton/font_size": 11,
|
||||
"PopupMenu/font_size": 11,
|
||||
"RichTextLabel/bold_font_size": 10,
|
||||
"RichTextLabel/bold_italics_font_size": 10,
|
||||
"RichTextLabel/italics_font_size": 10,
|
||||
"RichTextLabel/mono_font_size": 9,
|
||||
"RichTextLabel/normal_font_size": 10,
|
||||
"Window/title_font_size": 16
|
||||
}
|
||||
|
||||
# Apply multiplier to each font size
|
||||
for font_path: String in base_font_sizes:
|
||||
var parts: PackedStringArray = font_path.split("/")
|
||||
var control_type: String = parts[0]
|
||||
var property: String = parts[1]
|
||||
var base_size: int = base_font_sizes[font_path]
|
||||
var new_size: int = int(base_size * multiplier)
|
||||
|
||||
console_theme.set_font_size(property, control_type, new_size)
|
||||
|
||||
# Theme changes are applied automatically
|
||||
|
||||
## Applies the restore button alignment setting
|
||||
##
|
||||
## Updates the restore button position based on the alignment preference.
|
||||
## @param alignment: Alignment enum value (0=TOP_LEFT, 1=TOP_RIGHT, 2=BOTTOM_LEFT, 3=BOTTOM_RIGHT)
|
||||
func _apply_restore_button_alignment(alignment: int) -> void:
|
||||
if not _restore_button_component:
|
||||
return
|
||||
|
||||
# Set position based on alignment using the RestoreButton's enum
|
||||
var restore_alignment: RestoreButton.Alignment = alignment as RestoreButton.Alignment
|
||||
_restore_button_component.set_alignment(restore_alignment)
|
||||
|
||||
## Gets a human-readable name for the alignment enum
|
||||
##
|
||||
## @param alignment: The alignment enum value
|
||||
## @return: String name of the alignment
|
||||
func _get_alignment_name(alignment: RestoreButton.Alignment) -> String:
|
||||
match alignment:
|
||||
RestoreButton.Alignment.TOP_LEFT:
|
||||
return "Top Left"
|
||||
RestoreButton.Alignment.TOP_RIGHT:
|
||||
return "Top Right"
|
||||
RestoreButton.Alignment.BOTTOM_LEFT:
|
||||
return "Bottom Left"
|
||||
RestoreButton.Alignment.BOTTOM_RIGHT:
|
||||
return "Bottom Right"
|
||||
_:
|
||||
return "Unknown"
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
uid://64r2in67soep
|
||||
@@ -1,164 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://d0sxs0is80byx"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://64r2in67soep" path="res://addons/loggie-console/scenes/console.gd" id="1_yp485"]
|
||||
[ext_resource type="PackedScene" uid="uid://dgok5l3frx53m" path="res://addons/loggie-console/scenes/panel_domain_selector.tscn" id="2_domain"]
|
||||
[ext_resource type="StyleBox" uid="uid://b7k8yo7jfhpue" path="res://addons/loggie-console/assets/inner_panel.tres" id="3_fxnpl"]
|
||||
[ext_resource type="Script" uid="uid://wah8efffydnu" path="res://addons/loggie-console/scripts/components/log_buffer.gd" id="3_logbuffer"]
|
||||
[ext_resource type="Theme" uid="uid://c1lnnysnspc6v" path="res://addons/loggie-console/theme_compact.tres" id="4_2cgp1"]
|
||||
[ext_resource type="Script" uid="uid://d3l2tmf5gym1d" path="res://addons/loggie-console/scripts/components/log_level_filter.gd" id="4_loglevel"]
|
||||
[ext_resource type="Script" uid="uid://c4f6c3gs8venj" path="res://addons/loggie-console/scripts/components/console_controls.gd" id="5_controls"]
|
||||
[ext_resource type="Script" uid="uid://whrim6lbiako" path="res://addons/loggie-console/scripts/components/status_display.gd" id="6_status"]
|
||||
[ext_resource type="Texture2D" uid="uid://ivppb5cqrsak" path="res://addons/loggie-console/assets/NinePatchWindow-InnerRecess.png" id="7_56hvo"]
|
||||
[ext_resource type="PackedScene" uid="uid://bw8k3yap2n4qv" path="res://addons/loggie-console/scenes/settings_panel.tscn" id="7_settings_panel"]
|
||||
|
||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_20rq3"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
texture = ExtResource("7_56hvo")
|
||||
texture_margin_left = 2.0
|
||||
texture_margin_top = 2.0
|
||||
texture_margin_right = 2.0
|
||||
texture_margin_bottom = 2.0
|
||||
axis_stretch_horizontal = 1
|
||||
axis_stretch_vertical = 1
|
||||
region_rect = Rect2(0, 0, 36, 36)
|
||||
modulate_color = Color(0.60933, 0.60933, 0.60933, 1)
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_njst7"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ibivl"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_0yrjc"]
|
||||
|
||||
[node name="LoggieConsoleWindow" type="Window" unique_id=1024795111 node_paths=PackedStringArray("buffer", "output_level", "domain_filter", "controls", "status_display")]
|
||||
oversampling_override = 1.0
|
||||
title = "Loggie Console"
|
||||
initial_position = 1
|
||||
size = Vector2i(889, 905)
|
||||
wrap_controls = true
|
||||
sharp_corners = true
|
||||
min_size = Vector2i(600, 300)
|
||||
keep_title_visible = true
|
||||
theme = ExtResource("4_2cgp1")
|
||||
script = ExtResource("1_yp485")
|
||||
buffer = NodePath("PanelContainer/VBoxContainer/PanelContainer/Buffer")
|
||||
output_level = NodePath("PanelContainer/VBoxContainer/Controls/OutputLevel")
|
||||
domain_filter = NodePath("PanelContainer/VBoxContainer/Controls/DomainFilter")
|
||||
controls = NodePath("PanelContainer/VBoxContainer/Controls")
|
||||
status_display = NodePath("PanelContainer/VBoxContainer/StatusContainer/HBoxContainer/StatusLabel")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1949067581]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = ExtResource("3_fxnpl")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=1611268808]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Controls" type="HBoxContainer" parent="PanelContainer/VBoxContainer" unique_id=251197975 node_paths=PackedStringArray("clear_button", "scroll_follow_checkbox", "search_input", "stack_filter_checkbox")]
|
||||
layout_mode = 2
|
||||
script = ExtResource("5_controls")
|
||||
clear_button = NodePath("Clear")
|
||||
scroll_follow_checkbox = NodePath("ScrollFollow")
|
||||
search_input = NodePath("../StatusContainer/HBoxContainer/SearchInput")
|
||||
stack_filter_checkbox = NodePath("StackFilter")
|
||||
|
||||
[node name="OutputLevel" type="OptionButton" parent="PanelContainer/VBoxContainer/Controls" unique_id=751332603]
|
||||
custom_minimum_size = Vector2(60, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Filter by log level"
|
||||
selected = 3
|
||||
item_count = 5
|
||||
popup/item_0/text = "ERROR"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "WARN"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "NOTICE"
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "INFO"
|
||||
popup/item_3/id = 3
|
||||
popup/item_4/text = "DEBUG"
|
||||
popup/item_4/id = 4
|
||||
script = ExtResource("4_loglevel")
|
||||
|
||||
[node name="ScrollFollow" type="CheckBox" parent="PanelContainer/VBoxContainer/Controls" unique_id=569696074]
|
||||
custom_minimum_size = Vector2(50, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Auto-scroll to newest messages"
|
||||
button_pressed = true
|
||||
text = "Follow"
|
||||
|
||||
[node name="StackFilter" type="CheckBox" parent="PanelContainer/VBoxContainer/Controls" unique_id=719019705]
|
||||
custom_minimum_size = Vector2(80, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Show only messages with stack traces"
|
||||
text = "Stack Only"
|
||||
|
||||
[node name="Clear" type="Button" parent="PanelContainer/VBoxContainer/Controls" unique_id=1133245115]
|
||||
custom_minimum_size = Vector2(45, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Clear console buffer"
|
||||
text = "Clear"
|
||||
|
||||
[node name="Settings" type="Button" parent="PanelContainer/VBoxContainer/Controls" unique_id=671578664]
|
||||
custom_minimum_size = Vector2(60, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Console settings"
|
||||
text = "Settings"
|
||||
|
||||
[node name="DomainFilter" parent="PanelContainer/VBoxContainer/Controls" unique_id=582666238 instance=ExtResource("2_domain")]
|
||||
custom_minimum_size = Vector2(300, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 10
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="PanelContainer/VBoxContainer" unique_id=2068430600]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxTexture_20rq3")
|
||||
|
||||
[node name="Buffer" type="RichTextLabel" parent="PanelContainer/VBoxContainer/PanelContainer" unique_id=1261116881]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_njst7")
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_ibivl")
|
||||
bbcode_enabled = true
|
||||
scroll_following = true
|
||||
context_menu_enabled = true
|
||||
selection_enabled = true
|
||||
script = ExtResource("3_logbuffer")
|
||||
|
||||
[node name="StatusContainer" type="PanelContainer" parent="PanelContainer/VBoxContainer" unique_id=592277225]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_0yrjc")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/StatusContainer" unique_id=958031751]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SearchInput" type="LineEdit" parent="PanelContainer/VBoxContainer/StatusContainer/HBoxContainer" unique_id=526298685]
|
||||
custom_minimum_size = Vector2(200, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 2
|
||||
placeholder_text = "Filter messages..."
|
||||
clear_button_enabled = true
|
||||
|
||||
[node name="StatusLabel" type="Label" parent="PanelContainer/VBoxContainer/StatusContainer/HBoxContainer" unique_id=848734114]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Ready"
|
||||
horizontal_alignment = 2
|
||||
vertical_alignment = 1
|
||||
script = ExtResource("6_status")
|
||||
|
||||
[node name="SettingsPanel" parent="." unique_id=1146821245 instance=ExtResource("7_settings_panel")]
|
||||
@@ -1,55 +0,0 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cjy8b6h1xrq2x"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dgkxvw0v5n6hq" path="res://addons/loggie-console/scripts/components/domain_item.gd" id="1_domain_item"]
|
||||
[ext_resource type="Theme" uid="uid://c1lnnysnspc6v" path="res://addons/loggie-console/theme_compact.tres" id="2_theme"]
|
||||
|
||||
[node name="DomainItem" type="Control" node_paths=PackedStringArray("select_checkbox", "only_button", "color_indicator")]
|
||||
custom_minimum_size = Vector2(280, 32)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("2_theme")
|
||||
script = ExtResource("1_domain_item")
|
||||
select_checkbox = NodePath("HBoxContainer/SelectCheckBox")
|
||||
only_button = NodePath("OnlyButton")
|
||||
color_indicator = NodePath("HBoxContainer/ColorIndicator")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="ColorIndicator" type="ColorRect" parent="HBoxContainer"]
|
||||
custom_minimum_size = Vector2(12, 12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SelectCheckBox" type="CheckBox" parent="HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
tooltip_text = "Select/deselect this domain"
|
||||
theme_override_constants/h_separation = 20
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="OnlyButton" type="Button" parent="."]
|
||||
modulate = Color(1, 1, 1, 0)
|
||||
custom_minimum_size = Vector2(45, 24)
|
||||
layout_mode = 1
|
||||
anchors_preset = 6
|
||||
anchor_left = 1.0
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -45.0
|
||||
offset_top = -12.0
|
||||
offset_bottom = 12.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 2
|
||||
tooltip_text = "Select only this domain"
|
||||
mouse_filter = 2
|
||||
text = "Only"
|
||||
@@ -1,86 +0,0 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://dgok5l3frx53m"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bfmpjs4x3fhte" path="res://addons/loggie-console/scripts/components/panel_domain_selector.gd" id="1_panel_selector"]
|
||||
[ext_resource type="Theme" uid="uid://c1lnnysnspc6v" path="res://addons/loggie-console/theme_compact.tres" id="2_theme"]
|
||||
[ext_resource type="StyleBox" uid="uid://b7k8yo7jfhpue" path="res://addons/loggie-console/assets/inner_panel.tres" id="3_panel_style"]
|
||||
|
||||
[node name="PanelDomainSelector" type="Control" node_paths=PackedStringArray("trigger_button", "domain_panel", "select_all_button", "select_none_button", "scroll_container", "domain_list")]
|
||||
custom_minimum_size = Vector2(100, 20)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("2_theme")
|
||||
script = ExtResource("1_panel_selector")
|
||||
trigger_button = NodePath("TriggerButton")
|
||||
domain_panel = NodePath("DomainPanel")
|
||||
select_all_button = NodePath("DomainPanel/PanelContainer/VBoxContainer/SelectionControls/SelectAllButton")
|
||||
select_none_button = NodePath("DomainPanel/PanelContainer/VBoxContainer/SelectionControls/SelectNoneButton")
|
||||
scroll_container = NodePath("DomainPanel/PanelContainer/VBoxContainer/ScrollContainer")
|
||||
domain_list = NodePath("DomainPanel/PanelContainer/VBoxContainer/ScrollContainer/DomainList")
|
||||
|
||||
[node name="TriggerButton" type="Button" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
tooltip_text = "All domains selected (1)"
|
||||
text = "Domains: All"
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="DomainPanel" type="PopupPanel" parent="."]
|
||||
initial_position = 1
|
||||
size = Vector2i(300, 160)
|
||||
visible = true
|
||||
min_size = Vector2i(300, 150)
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="DomainPanel"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 2.0
|
||||
offset_top = 2.0
|
||||
offset_right = -2.0
|
||||
offset_bottom = -2.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = ExtResource("3_panel_style")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="DomainPanel/PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SelectionControls" type="HBoxContainer" parent="DomainPanel/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
alignment = 2
|
||||
|
||||
[node name="SelectAllButton" type="Button" parent="DomainPanel/PanelContainer/VBoxContainer/SelectionControls"]
|
||||
custom_minimum_size = Vector2(60, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Select all domains"
|
||||
text = "Select All"
|
||||
|
||||
[node name="SelectNoneButton" type="Button" parent="DomainPanel/PanelContainer/VBoxContainer/SelectionControls"]
|
||||
custom_minimum_size = Vector2(60, 20)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
tooltip_text = "Deselect all domains"
|
||||
text = "None"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="DomainPanel/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="DomainPanel/PanelContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="DomainList" type="VBoxContainer" parent="DomainPanel/PanelContainer/VBoxContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cb5mkvrnm3hxn"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dmyd1gquxy42u" path="res://addons/loggie-console/scripts/components/restore_button.gd" id="1_2h8k5"]
|
||||
[ext_resource type="Theme" uid="uid://c1lnnysnspc6v" path="res://addons/loggie-console/theme_compact.tres" id="1_o01pp"]
|
||||
|
||||
[node name="LoggieConsoleOverlay" type="CanvasLayer" node_paths=PackedStringArray("restore_button")]
|
||||
layer = 2147483647
|
||||
script = ExtResource("1_2h8k5")
|
||||
restore_button = NodePath("MarginContainer/Button")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="Button" type="Button" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 0
|
||||
tooltip_text = "Show Loggie Console"
|
||||
theme = ExtResource("1_o01pp")
|
||||
text = " Loggie Console "
|
||||
@@ -1,90 +0,0 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://bw8k3yap2n4qv"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://c1lnnysnspc6v" path="res://addons/loggie-console/theme_compact.tres" id="1_08jdj"]
|
||||
[ext_resource type="Script" uid="uid://giuyu6vhhgam" path="res://addons/loggie-console/scripts/components/settings_panel.gd" id="1_settings_panel"]
|
||||
[ext_resource type="StyleBox" uid="uid://b7k8yo7jfhpue" path="res://addons/loggie-console/assets/inner_panel.tres" id="2_08jdj"]
|
||||
|
||||
[node name="SettingsPanel" type="AcceptDialog" node_paths=PackedStringArray("_text_size_slider", "_text_size_value_label", "_reset_button", "_alignment_option")]
|
||||
title = "Console Settings"
|
||||
initial_position = 1
|
||||
size = Vector2i(320, 210)
|
||||
transient = false
|
||||
sharp_corners = true
|
||||
theme = ExtResource("1_08jdj")
|
||||
theme_override_styles/panel = ExtResource("2_08jdj")
|
||||
ok_button_text = " Apply Changes "
|
||||
script = ExtResource("1_settings_panel")
|
||||
_text_size_slider = NodePath("VBoxContainer/TextSizeContainer/TextSizeSlider")
|
||||
_text_size_value_label = NodePath("VBoxContainer/TextSizeContainer/TextSizeValueLabel")
|
||||
_reset_button = NodePath("VBoxContainer/ResetContainer/ResetButton")
|
||||
_alignment_option = NodePath("VBoxContainer/AlignmentContainer/AlignmentOption")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -38.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="TextSizeContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TextSizeLabel" type="Label" parent="VBoxContainer/TextSizeContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Text Size:"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="TextSizeSlider" type="HSlider" parent="VBoxContainer/TextSizeContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 0.5
|
||||
max_value = 2.0
|
||||
step = 0.1
|
||||
value = 1.0
|
||||
tick_count = 16
|
||||
ticks_on_borders = true
|
||||
|
||||
[node name="TextSizeValueLabel" type="Label" parent="VBoxContainer/TextSizeContainer"]
|
||||
custom_minimum_size = Vector2(40, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "1.0x"
|
||||
horizontal_alignment = 2
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AlignmentContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AlignmentLabel" type="Label" parent="VBoxContainer/AlignmentContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Restore Button:"
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AlignmentOption" type="OptionButton" parent="VBoxContainer/AlignmentContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
selected = 1
|
||||
item_count = 4
|
||||
popup/item_0/text = "Top Left"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Top Right"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Bottom Left"
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "Bottom Right"
|
||||
popup/item_3/id = 3
|
||||
|
||||
[node name="ResetContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="ResetButton" type="Button" parent="VBoxContainer/ResetContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Reset to Default"
|
||||
@@ -1,104 +0,0 @@
|
||||
class_name ConsoleControls extends HBoxContainer
|
||||
|
||||
## Console controls toolbar component
|
||||
## Manages clear button, scroll follow, and other control elements
|
||||
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
|
||||
# Signals
|
||||
signal clear_requested()
|
||||
signal scroll_follow_changed(enabled: bool)
|
||||
signal text_search_changed(search_text: String)
|
||||
signal stack_filter_changed(enabled: bool)
|
||||
|
||||
# Exported references to child controls
|
||||
@export var clear_button: Button
|
||||
@export var scroll_follow_checkbox: CheckBox
|
||||
@export var search_input: LineEdit
|
||||
@export var stack_filter_checkbox: CheckBox
|
||||
|
||||
func _ready() -> void:
|
||||
_setup_controls()
|
||||
|
||||
## Initialize the controls with default states
|
||||
func initialize() -> void:
|
||||
if scroll_follow_checkbox:
|
||||
scroll_follow_checkbox.button_pressed = true
|
||||
_on_scroll_follow_toggled(true)
|
||||
|
||||
## Get current scroll follow state
|
||||
func is_scroll_follow_enabled() -> bool:
|
||||
return scroll_follow_checkbox.button_pressed if scroll_follow_checkbox else false
|
||||
|
||||
## Set scroll follow state programmatically
|
||||
func set_scroll_follow(enabled: bool) -> void:
|
||||
if scroll_follow_checkbox:
|
||||
scroll_follow_checkbox.button_pressed = enabled
|
||||
|
||||
## Get current text search
|
||||
func get_search_text() -> String:
|
||||
return search_input.text if search_input else ""
|
||||
|
||||
## Set search text programmatically
|
||||
func set_search_text(text: String) -> void:
|
||||
if search_input:
|
||||
search_input.text = text
|
||||
|
||||
## Get current stack filter state
|
||||
func is_stack_filter_enabled() -> bool:
|
||||
return stack_filter_checkbox.button_pressed if stack_filter_checkbox else false
|
||||
|
||||
## Set stack filter state programmatically
|
||||
func set_stack_filter(enabled: bool) -> void:
|
||||
if stack_filter_checkbox:
|
||||
stack_filter_checkbox.button_pressed = enabled
|
||||
|
||||
## Private methods
|
||||
|
||||
func _setup_controls() -> void:
|
||||
# Connect clear button if available
|
||||
if clear_button:
|
||||
clear_button.pressed.connect(_on_clear_pressed)
|
||||
clear_button.tooltip_text = "Clear console buffer"
|
||||
|
||||
# Connect scroll follow checkbox if available
|
||||
if scroll_follow_checkbox:
|
||||
scroll_follow_checkbox.toggled.connect(_on_scroll_follow_toggled)
|
||||
scroll_follow_checkbox.tooltip_text = "Auto-scroll to newest messages"
|
||||
|
||||
# Connect search input if available
|
||||
if search_input:
|
||||
search_input.text_changed.connect(_on_search_text_changed)
|
||||
search_input.placeholder_text = "Filter messages..."
|
||||
|
||||
# Connect stack filter checkbox if available
|
||||
if stack_filter_checkbox:
|
||||
stack_filter_checkbox.toggled.connect(_on_stack_filter_toggled)
|
||||
stack_filter_checkbox.tooltip_text = "Show only messages with stack traces"
|
||||
|
||||
func _on_clear_pressed() -> void:
|
||||
clear_requested.emit()
|
||||
|
||||
func _on_scroll_follow_toggled(enabled: bool) -> void:
|
||||
scroll_follow_changed.emit(enabled)
|
||||
|
||||
func _on_search_text_changed(new_text: String) -> void:
|
||||
text_search_changed.emit(new_text)
|
||||
|
||||
func _on_stack_filter_toggled(enabled: bool) -> void:
|
||||
stack_filter_changed.emit(enabled)
|
||||
|
||||
## Saves current toolbar control states to persistent settings
|
||||
## Param settings: The settings resource to populate with current control states
|
||||
func save_settings_to_resource(settings: LoggieConsoleSettings) -> void:
|
||||
settings.scroll_follow_enabled = is_scroll_follow_enabled()
|
||||
settings.search_text = get_search_text()
|
||||
settings.stack_filter_enabled = is_stack_filter_enabled()
|
||||
|
||||
## Loads toolbar control states from persistent settings and applies them
|
||||
## Updates all UI controls to reflect the loaded state without emitting signals
|
||||
## Param settings: The settings resource containing saved control states
|
||||
func load_settings_from_resource(settings: LoggieConsoleSettings) -> void:
|
||||
set_scroll_follow(settings.scroll_follow_enabled)
|
||||
set_search_text(settings.search_text)
|
||||
set_stack_filter(settings.stack_filter_enabled)
|
||||
@@ -1 +0,0 @@
|
||||
uid://c4f6c3gs8venj
|
||||
@@ -1,166 +0,0 @@
|
||||
class_name DomainItem extends Control
|
||||
|
||||
## Custom control for individual domain rows in PanelDomainSelector
|
||||
## Provides checkbox selection, domain label, and hover-revealed "Only" button
|
||||
|
||||
const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd")
|
||||
|
||||
# Signals
|
||||
signal selection_changed(domain_name: String, is_selected: bool)
|
||||
signal only_button_pressed(domain_name: String)
|
||||
|
||||
# UI Components - exported for scene tree assignment
|
||||
@export var select_checkbox: CheckBox
|
||||
@export var only_button: Button
|
||||
@export var color_indicator: ColorRect
|
||||
|
||||
# Domain state
|
||||
var _domain_name: String = ""
|
||||
var _is_hovered: bool = false
|
||||
var _color_manager: DomainColorManager
|
||||
|
||||
# UI Layout constants
|
||||
const ITEM_MIN_WIDTH: int = 280
|
||||
const ITEM_MIN_HEIGHT: int = 32
|
||||
|
||||
# Visibility constants for only button hover effect
|
||||
const ONLY_BUTTON_ALPHA_HIDDEN: float = 0.0
|
||||
const ONLY_BUTTON_ALPHA_VISIBLE: float = 1.0
|
||||
|
||||
## Initializes the domain item control with UI connections and hover behavior
|
||||
##
|
||||
## Sets up consistent sizing, signal connections for checkbox and button interactions,
|
||||
## and configures mouse hover detection for the "Only" button visibility toggle.
|
||||
## The only button starts hidden and becomes visible on hover with smooth alpha transition.
|
||||
## Requires select_checkbox and only_button to be properly assigned via @export.
|
||||
func _ready() -> void:
|
||||
if not select_checkbox:
|
||||
return
|
||||
|
||||
# Set minimum size for consistent layout
|
||||
custom_minimum_size = Vector2(ITEM_MIN_WIDTH, ITEM_MIN_HEIGHT)
|
||||
|
||||
# Connect signals
|
||||
select_checkbox.toggled.connect(_on_checkbox_toggled)
|
||||
only_button.pressed.connect(_on_only_button_pressed)
|
||||
|
||||
# Setup hover detection on checkbox (which now contains the text)
|
||||
select_checkbox.mouse_entered.connect(_on_mouse_entered)
|
||||
select_checkbox.mouse_exited.connect(_on_mouse_exited)
|
||||
|
||||
# Also setup hover detection on the main control for complete coverage
|
||||
mouse_entered.connect(_on_mouse_entered)
|
||||
mouse_exited.connect(_on_mouse_exited)
|
||||
|
||||
|
||||
# Initialize only button as hidden
|
||||
only_button.modulate.a = ONLY_BUTTON_ALPHA_HIDDEN
|
||||
only_button.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
## Set the domain color manager instance
|
||||
func set_color_manager(color_manager: DomainColorManager) -> void:
|
||||
_color_manager = color_manager
|
||||
|
||||
## Configures the domain item with a specific domain name and selection state
|
||||
##
|
||||
## Sets up the checkbox display text (shows "(default)" for empty domain names),
|
||||
## configures tooltips for both checkbox and only button, and applies the initial
|
||||
## selection state. This method should be called after the item is added to the scene tree.
|
||||
##
|
||||
## @param domain_name: The domain name to display and track
|
||||
## @param is_selected: Whether the domain should start as selected
|
||||
func setup_domain(domain_name: String, is_selected: bool = false) -> void:
|
||||
_domain_name = domain_name
|
||||
|
||||
if select_checkbox:
|
||||
var display_name = domain_name if not domain_name.is_empty() else "(default)"
|
||||
|
||||
# Update color indicator instead of modulating checkbox
|
||||
if _color_manager and color_indicator:
|
||||
var domain_color = _color_manager.get_domain_color(domain_name)
|
||||
color_indicator.color = domain_color
|
||||
|
||||
select_checkbox.text = display_name
|
||||
select_checkbox.tooltip_text = "Select/deselect domain: " + domain_name
|
||||
select_checkbox.button_pressed = is_selected
|
||||
|
||||
if only_button:
|
||||
only_button.text = "Only"
|
||||
only_button.tooltip_text = "Select only this domain: " + domain_name
|
||||
|
||||
## Get the domain name for this item
|
||||
func get_domain_name() -> String:
|
||||
return _domain_name
|
||||
|
||||
## Updates the checkbox selection state without emitting signals
|
||||
##
|
||||
## Useful for programmatically setting the selection state during bulk operations
|
||||
## like "Select All" or when loading from saved settings. Uses set_pressed_no_signal
|
||||
## to avoid triggering selection_changed signals during these operations.
|
||||
##
|
||||
## @param is_selected: The new selection state for the checkbox
|
||||
func set_selected(is_selected: bool) -> void:
|
||||
if select_checkbox:
|
||||
select_checkbox.set_pressed_no_signal(is_selected)
|
||||
|
||||
## Get current selection state
|
||||
func is_selected() -> bool:
|
||||
return select_checkbox.button_pressed if select_checkbox else false
|
||||
|
||||
## Private methods
|
||||
|
||||
func _on_checkbox_toggled(pressed: bool) -> void:
|
||||
selection_changed.emit(_domain_name, pressed)
|
||||
|
||||
func _on_only_button_pressed() -> void:
|
||||
only_button_pressed.emit(_domain_name)
|
||||
|
||||
|
||||
func _on_mouse_entered() -> void:
|
||||
_is_hovered = true
|
||||
_set_only_button_visibility(true)
|
||||
|
||||
func _on_mouse_exited() -> void:
|
||||
_is_hovered = false
|
||||
_set_only_button_visibility(false)
|
||||
|
||||
## Controls the visibility and interaction of the "Only" button based on hover state
|
||||
##
|
||||
## Instantly toggles the button's alpha transparency and mouse filter to create
|
||||
## a hover-reveal effect. When hidden, the button becomes completely transparent
|
||||
## and ignores mouse input. When visible, it becomes fully opaque and interactive.
|
||||
##
|
||||
## @param show: Whether to show (true) or hide (false) the only button
|
||||
func _set_only_button_visibility(show: bool) -> void:
|
||||
if not only_button:
|
||||
return
|
||||
|
||||
# Set visibility instantly - no animation
|
||||
var target_alpha = ONLY_BUTTON_ALPHA_VISIBLE if show else ONLY_BUTTON_ALPHA_HIDDEN
|
||||
var target_mouse_filter = Control.MOUSE_FILTER_PASS if show else Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
only_button.modulate.a = target_alpha
|
||||
only_button.mouse_filter = target_mouse_filter
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
# Handle keyboard navigation
|
||||
if event is InputEventKey and event.pressed:
|
||||
match event.keycode:
|
||||
KEY_SPACE, KEY_ENTER:
|
||||
if select_checkbox:
|
||||
select_checkbox.button_pressed = not select_checkbox.button_pressed
|
||||
_on_checkbox_toggled(select_checkbox.button_pressed)
|
||||
accept_event()
|
||||
KEY_O:
|
||||
if event.ctrl_pressed: # Ctrl+O for "Only"
|
||||
_on_only_button_pressed()
|
||||
accept_event()
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_FOCUS_ENTER:
|
||||
# Add visual focus indication if needed
|
||||
pass
|
||||
NOTIFICATION_FOCUS_EXIT:
|
||||
# Remove visual focus indication if needed
|
||||
pass
|
||||
@@ -1 +0,0 @@
|
||||
uid://dgkxvw0v5n6hq
|
||||
@@ -1,256 +0,0 @@
|
||||
class_name DomainSelector extends MenuButton
|
||||
|
||||
## Standalone domain selector component for filtering log messages by domain
|
||||
## Provides clean interface with proper signal-based communication
|
||||
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd")
|
||||
|
||||
# Signals
|
||||
signal domains_changed(enabled_domains: Array[String])
|
||||
|
||||
# Properties
|
||||
var _enabled_domains: Array[String] = []
|
||||
var _all_domains: Array[String] = []
|
||||
var _domain_popup: PopupMenu
|
||||
var _color_manager: DomainColorManager
|
||||
|
||||
# Constants
|
||||
const ALL_ITEM_ID = 0
|
||||
const SEPARATOR_ID = 1
|
||||
const DOMAIN_ID_OFFSET = 2
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_domain_popup = get_popup()
|
||||
_domain_popup.id_pressed.connect(_on_domain_selected)
|
||||
_initialize_empty_state()
|
||||
|
||||
## Set the domain color manager instance
|
||||
func set_color_manager(color_manager: DomainColorManager) -> void:
|
||||
_color_manager = color_manager
|
||||
|
||||
## Initialize the selector with available domains
|
||||
func set_domains(domains: Array[String]) -> void:
|
||||
_all_domains = domains.duplicate()
|
||||
_enabled_domains = domains.duplicate()
|
||||
_rebuild_popup()
|
||||
_update_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Initialize the selector with all domains, but only enable selected ones
|
||||
## Console domains appear normally but are unselected by default
|
||||
func set_domains_with_console_unselected(all_domains: Array[String], enabled_domains: Array[String], console_domains: Array[String]) -> void:
|
||||
_all_domains = all_domains.duplicate()
|
||||
_enabled_domains = enabled_domains.duplicate()
|
||||
_rebuild_popup_with_console_unselected(console_domains)
|
||||
_update_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Add a new domain to the selector (for dynamic discovery)
|
||||
func add_domain(domain_name: String) -> void:
|
||||
if domain_name.is_empty() or _all_domains.has(domain_name):
|
||||
return
|
||||
|
||||
_all_domains.append(domain_name)
|
||||
_enabled_domains.append(domain_name)
|
||||
|
||||
# Add new item to popup with color icon
|
||||
var display_name = _get_domain_display_name(domain_name)
|
||||
var color_icon = _create_domain_color_icon(domain_name)
|
||||
var item_id = _get_domain_item_id(_all_domains.size() - 1)
|
||||
|
||||
_domain_popup.add_check_item(display_name, item_id)
|
||||
_domain_popup.set_item_icon(item_id, color_icon)
|
||||
_domain_popup.set_item_checked(item_id, true)
|
||||
|
||||
_update_all_checkbox()
|
||||
_update_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Get currently enabled domains
|
||||
func get_enabled_domains() -> Array[String]:
|
||||
return _enabled_domains.duplicate()
|
||||
|
||||
## Check if a domain is enabled
|
||||
func is_domain_enabled(domain_name: String) -> bool:
|
||||
return _enabled_domains.has(domain_name)
|
||||
|
||||
## Private methods
|
||||
|
||||
## Create colored icon for domain display in popup
|
||||
func _create_domain_color_icon(domain_name: String, size: int = 12) -> ImageTexture:
|
||||
var color: Color = Color.WHITE # Default color
|
||||
if _color_manager:
|
||||
color = _color_manager.get_domain_color(domain_name)
|
||||
|
||||
# Create a small colored square image
|
||||
var image = Image.create(size, size, false, Image.FORMAT_RGBA8)
|
||||
image.fill(color)
|
||||
|
||||
# Convert to texture
|
||||
var texture: ImageTexture = ImageTexture.create_from_image(image)
|
||||
return texture
|
||||
|
||||
## Get display name for domain (without color formatting)
|
||||
func _get_domain_display_name(domain_name: String) -> String:
|
||||
return domain_name if not domain_name.is_empty() else "(default)"
|
||||
|
||||
func _initialize_empty_state() -> void:
|
||||
_domain_popup.clear()
|
||||
_domain_popup.add_item("No domains available")
|
||||
_domain_popup.set_item_disabled(0, true)
|
||||
text = "Domains: None"
|
||||
|
||||
func _rebuild_popup() -> void:
|
||||
_domain_popup.clear()
|
||||
|
||||
# Add "All" toggle
|
||||
_domain_popup.add_check_item("All", ALL_ITEM_ID)
|
||||
_domain_popup.set_item_checked(ALL_ITEM_ID, _enabled_domains.size() == _all_domains.size())
|
||||
|
||||
# Add separator
|
||||
_domain_popup.add_separator()
|
||||
|
||||
# Add individual domain items with color icons
|
||||
for i in range(_all_domains.size()):
|
||||
var domain_name = _all_domains[i]
|
||||
var display_name = _get_domain_display_name(domain_name)
|
||||
var color_icon = _create_domain_color_icon(domain_name)
|
||||
var item_id = _get_domain_item_id(i)
|
||||
|
||||
_domain_popup.add_check_item(display_name, item_id)
|
||||
_domain_popup.set_item_icon(item_id, color_icon)
|
||||
_domain_popup.set_item_checked(item_id, _enabled_domains.has(domain_name))
|
||||
|
||||
## Builds popup with console domain unselected by default (no separator)
|
||||
func _rebuild_popup_with_console_unselected(console_domains: Array[String]) -> void:
|
||||
_domain_popup.clear()
|
||||
|
||||
# Add "All" toggle (only for non-console domains)
|
||||
_domain_popup.add_check_item("All", ALL_ITEM_ID)
|
||||
var non_console_domains = []
|
||||
for domain in _all_domains:
|
||||
if not console_domains.has(domain):
|
||||
non_console_domains.append(domain)
|
||||
_domain_popup.set_item_checked(ALL_ITEM_ID, _enabled_domains.size() == non_console_domains.size())
|
||||
|
||||
# Add separator
|
||||
_domain_popup.add_separator()
|
||||
|
||||
# Add all domain items in order with color icons
|
||||
for i in range(_all_domains.size()):
|
||||
var domain_name = _all_domains[i]
|
||||
var display_name: String
|
||||
|
||||
# Use friendly name for console domain
|
||||
if console_domains.has(domain_name):
|
||||
display_name = "LoggieConsole" # Simplified name for console domain
|
||||
else:
|
||||
display_name = domain_name if not domain_name.is_empty() else "(default)"
|
||||
|
||||
# Create color icon for the domain
|
||||
var color_icon = _create_domain_color_icon(domain_name)
|
||||
|
||||
var item_id = _get_domain_item_id(i)
|
||||
_domain_popup.add_check_item(display_name, item_id)
|
||||
_domain_popup.set_item_icon(item_id, color_icon)
|
||||
_domain_popup.set_item_checked(item_id, _enabled_domains.has(domain_name))
|
||||
|
||||
func _on_domain_selected(id: int) -> void:
|
||||
if id == ALL_ITEM_ID:
|
||||
_toggle_all_domains()
|
||||
else:
|
||||
_toggle_individual_domain(id)
|
||||
|
||||
_update_all_checkbox()
|
||||
_update_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
func _toggle_all_domains() -> void:
|
||||
var current_all_state = _domain_popup.is_item_checked(ALL_ITEM_ID)
|
||||
var new_all_state = not current_all_state
|
||||
|
||||
# Update all checkbox
|
||||
_domain_popup.set_item_checked(ALL_ITEM_ID, new_all_state)
|
||||
|
||||
# Update all domain checkboxes and enabled list
|
||||
_enabled_domains.clear()
|
||||
for i in range(_all_domains.size()):
|
||||
var item_id = _get_domain_item_id(i)
|
||||
_domain_popup.set_item_checked(item_id, new_all_state)
|
||||
if new_all_state:
|
||||
_enabled_domains.append(_all_domains[i])
|
||||
|
||||
func _toggle_individual_domain(id: int) -> void:
|
||||
var domain_index = _get_domain_index_from_id(id)
|
||||
if domain_index < 0 or domain_index >= _all_domains.size():
|
||||
Loggie.msg("Invalid domain index %d for ID %d" % [domain_index, id]).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return
|
||||
|
||||
var domain_name = _all_domains[domain_index]
|
||||
var current_state = _domain_popup.is_item_checked(id)
|
||||
var new_state = not current_state
|
||||
|
||||
# Update checkbox
|
||||
_domain_popup.set_item_checked(id, new_state)
|
||||
|
||||
# Update enabled domains list
|
||||
if new_state and not _enabled_domains.has(domain_name):
|
||||
_enabled_domains.append(domain_name)
|
||||
elif not new_state and _enabled_domains.has(domain_name):
|
||||
_enabled_domains.erase(domain_name)
|
||||
|
||||
func _update_all_checkbox() -> void:
|
||||
var all_enabled = _enabled_domains.size() == _all_domains.size()
|
||||
_domain_popup.set_item_checked(ALL_ITEM_ID, all_enabled)
|
||||
|
||||
func _update_button_text() -> void:
|
||||
var enabled_count = _enabled_domains.size()
|
||||
var total_count = _all_domains.size()
|
||||
|
||||
if total_count == 0:
|
||||
text = "Domains: None"
|
||||
elif enabled_count == total_count:
|
||||
text = "Domains: All"
|
||||
elif enabled_count == 0:
|
||||
text = "Domains: None"
|
||||
elif enabled_count == 1:
|
||||
var domain_name = _enabled_domains[0]
|
||||
var display_name = _get_domain_display_name(domain_name)
|
||||
text = "Domains: " + display_name
|
||||
else:
|
||||
text = "Domains: %d selected" % enabled_count
|
||||
|
||||
func _get_domain_item_id(domain_index: int) -> int:
|
||||
return DOMAIN_ID_OFFSET + domain_index
|
||||
|
||||
func _get_domain_index_from_id(item_id: int) -> int:
|
||||
return item_id - DOMAIN_ID_OFFSET
|
||||
|
||||
## Saves current domain selection state to persistent settings
|
||||
## Param settings: The settings resource to populate with current domain state
|
||||
func save_settings_to_resource(settings: LoggieConsoleSettings) -> void:
|
||||
settings.enabled_domains = _enabled_domains.duplicate()
|
||||
settings.all_known_domains = _all_domains.duplicate()
|
||||
|
||||
## Loads domain selection state from persistent settings and rebuilds UI
|
||||
## Automatically validates that enabled domains still exist in the known domains list
|
||||
## Param settings: The settings resource containing saved domain state
|
||||
func load_settings_from_resource(settings: LoggieConsoleSettings) -> void:
|
||||
_all_domains = settings.all_known_domains.duplicate()
|
||||
_enabled_domains = settings.enabled_domains.duplicate()
|
||||
|
||||
# Remove any enabled domains that no longer exist in all_domains
|
||||
var valid_enabled_domains: Array[String] = []
|
||||
for domain in _enabled_domains:
|
||||
if _all_domains.has(domain):
|
||||
valid_enabled_domains.append(domain)
|
||||
_enabled_domains = valid_enabled_domains
|
||||
|
||||
# Rebuild UI to reflect loaded state
|
||||
if _all_domains.size() > 0:
|
||||
_rebuild_popup()
|
||||
_update_button_text()
|
||||
else:
|
||||
_initialize_empty_state()
|
||||
@@ -1 +0,0 @@
|
||||
uid://bq6xlpjg4qoca
|
||||
@@ -1,209 +0,0 @@
|
||||
class_name LogBuffer extends RichTextLabel
|
||||
|
||||
## Enhanced message buffer with retroactive filtering capabilities
|
||||
## Stores all messages and applies filters dynamically without losing data
|
||||
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd")
|
||||
|
||||
# Signals
|
||||
signal filter_changed(visible_count: int, total_count: int)
|
||||
|
||||
# Internal message storage
|
||||
var _all_messages: Array[LogMessage] = []
|
||||
var _filtered_messages: Array[LogMessage] = []
|
||||
var _current_filters: FilterState
|
||||
|
||||
# Domain color manager reference
|
||||
var _color_manager: DomainColorManager
|
||||
|
||||
# Memory management constants
|
||||
const DEFAULT_MAX_MESSAGES: int = 5000
|
||||
const CLEANUP_PERCENTAGE: float = 0.2 # Remove 20% of messages when limit reached
|
||||
|
||||
# Memory management
|
||||
@export var max_messages: int = DEFAULT_MAX_MESSAGES
|
||||
|
||||
## Internal LogMessage class for storing complete message data
|
||||
class LogMessage:
|
||||
var msg: LoggieMsg
|
||||
var preprocessed_content: String
|
||||
var msg_type: LoggieEnums.MsgType
|
||||
var log_level: LoggieEnums.LogLevel
|
||||
var timestamp: float
|
||||
var enhanced_content: String
|
||||
|
||||
func _init(p_msg: LoggieMsg, p_content: String, p_type: LoggieEnums.MsgType, p_level: LoggieEnums.LogLevel) -> void:
|
||||
msg = p_msg
|
||||
preprocessed_content = p_content
|
||||
msg_type = p_type
|
||||
log_level = p_level
|
||||
timestamp = Time.get_unix_time_from_system()
|
||||
|
||||
## Filter state for retroactive filtering
|
||||
class FilterState:
|
||||
var enabled_domains: Array[String] = []
|
||||
var min_log_level: LoggieEnums.LogLevel = LoggieEnums.LogLevel.DEBUG
|
||||
var text_search: String = ""
|
||||
var show_stack_only: bool = false
|
||||
|
||||
func _init() -> void:
|
||||
# Initialize to show ERROR and above by default (lower numbers = higher priority)
|
||||
min_log_level = LoggieEnums.LogLevel.DEBUG
|
||||
|
||||
func _ready() -> void:
|
||||
_current_filters = FilterState.new()
|
||||
bbcode_enabled = true
|
||||
|
||||
## Set the domain color manager instance
|
||||
func set_color_manager(color_manager: DomainColorManager) -> void:
|
||||
_color_manager = color_manager
|
||||
|
||||
## Add a new log message to the buffer
|
||||
func add_message(msg: LoggieMsg, content: String, msg_type: LoggieEnums.MsgType, log_level: LoggieEnums.LogLevel) -> void:
|
||||
# Create and store the message
|
||||
var log_msg: LogMessage = LogMessage.new(msg, content, msg_type, log_level)
|
||||
log_msg.enhanced_content = _enhance_with_metadata(log_msg)
|
||||
|
||||
_all_messages.append(log_msg)
|
||||
|
||||
# Manage memory if needed
|
||||
_manage_memory()
|
||||
|
||||
# Apply current filters and rebuild display
|
||||
_reapply_filters()
|
||||
|
||||
## Set new filter state and reapply filters
|
||||
func set_filters(filter_state: FilterState) -> void:
|
||||
_current_filters = filter_state
|
||||
_reapply_filters()
|
||||
|
||||
## Set text search filter
|
||||
func set_text_search(search: String) -> void:
|
||||
if _current_filters.text_search == search:
|
||||
return
|
||||
|
||||
_current_filters.text_search = search
|
||||
_reapply_filters()
|
||||
|
||||
## Clear all messages
|
||||
func clear_all() -> void:
|
||||
_all_messages.clear()
|
||||
_filtered_messages.clear()
|
||||
text = ""
|
||||
clear()
|
||||
|
||||
# Emit updated counts
|
||||
filter_changed.emit(0, 0)
|
||||
|
||||
## Get current message counts
|
||||
func get_message_counts() -> Dictionary:
|
||||
return {
|
||||
"total": _all_messages.size(),
|
||||
"filtered": _filtered_messages.size()
|
||||
}
|
||||
|
||||
## Saves current buffer state to persistent settings resource
|
||||
## Param settings: The settings resource to populate with current state
|
||||
func save_settings_to_resource(settings: LoggieConsoleSettings) -> void:
|
||||
settings.filter_state.from_filter_state(_current_filters)
|
||||
settings.max_messages = max_messages
|
||||
|
||||
## Loads buffer state from persistent settings resource
|
||||
## Note: Does not immediately apply filters to avoid triggering reapply before messages exist
|
||||
## Param settings: The settings resource containing saved state
|
||||
func load_settings_from_resource(settings: LoggieConsoleSettings) -> void:
|
||||
if settings.filter_state:
|
||||
_current_filters = settings.filter_state.to_filter_state()
|
||||
max_messages = settings.max_messages
|
||||
|
||||
## Applies previously loaded filter state to existing messages
|
||||
## Call this after load_settings_from_resource() and message loading is complete
|
||||
func apply_loaded_filters() -> void:
|
||||
_reapply_filters()
|
||||
|
||||
|
||||
## Private methods
|
||||
|
||||
func _reapply_filters() -> void:
|
||||
_filtered_messages.clear()
|
||||
|
||||
for msg in _all_messages:
|
||||
if msg != null and _message_passes_filters(msg, _current_filters):
|
||||
_filtered_messages.append(msg)
|
||||
|
||||
_rebuild_display()
|
||||
|
||||
# Emit filter change signal
|
||||
filter_changed.emit(_filtered_messages.size(), _all_messages.size())
|
||||
|
||||
func _message_passes_filters(msg: LogMessage, filters: FilterState) -> bool:
|
||||
# Domain check - if no domains are enabled, show no messages
|
||||
if filters.enabled_domains.size() == 0:
|
||||
return false
|
||||
|
||||
# If domains are enabled, check if this message's domain is in the enabled list
|
||||
if not filters.enabled_domains.has(msg.msg.domain_name):
|
||||
return false
|
||||
|
||||
# Level check - show messages at or above the minimum level (lower numbers = higher priority)
|
||||
if msg.log_level > filters.min_log_level:
|
||||
return false
|
||||
|
||||
# Text search
|
||||
if not filters.text_search.is_empty():
|
||||
var search_lower: String = filters.text_search.to_lower()
|
||||
if not msg.preprocessed_content.to_lower().contains(search_lower):
|
||||
return false
|
||||
|
||||
# Stack filter
|
||||
if filters.show_stack_only and not msg.msg.appends_stack:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
func _rebuild_display() -> void:
|
||||
# Clear current display
|
||||
text = ""
|
||||
clear()
|
||||
|
||||
# Add all filtered messages
|
||||
for msg in _filtered_messages:
|
||||
text += msg.enhanced_content + "\n"
|
||||
|
||||
func _enhance_with_metadata(msg: LogMessage) -> String:
|
||||
var metadata_parts: Array[String] = []
|
||||
|
||||
metadata_parts.append("[color=%s]%s [/color]" % [Color.DIM_GRAY.to_html(false), Time.get_time_string_from_unix_time(int(msg.timestamp))])
|
||||
|
||||
if msg.msg.domain_name and not msg.msg.domain_name.is_empty():
|
||||
var domain_color_html: String = _color_manager.get_domain_color_html(msg.msg.domain_name)
|
||||
metadata_parts.append("[color=%s]%s[/color]" % [domain_color_html, msg.msg.domain_name])
|
||||
|
||||
if msg.msg.domain_name and not msg.msg.domain_name.is_empty():
|
||||
metadata_parts.append("[color=%s][%s] [/color]" % [_get_color_for_log_level(msg.log_level).to_html(false), LoggieEnums.LogLevel.keys()[msg.log_level]])
|
||||
|
||||
return "%s %s" % [" ".join(metadata_parts), msg.msg.content[0]]
|
||||
|
||||
func _get_color_for_log_level(level: LoggieEnums.LogLevel) -> Color:
|
||||
match level:
|
||||
LoggieEnums.LogLevel.ERROR:
|
||||
return Color.RED
|
||||
LoggieEnums.LogLevel.WARN:
|
||||
return Color.ORANGE
|
||||
LoggieEnums.LogLevel.NOTICE:
|
||||
return Color.GREEN
|
||||
LoggieEnums.LogLevel.INFO:
|
||||
return Color.WHITE
|
||||
LoggieEnums.LogLevel.DEBUG, _:
|
||||
return Color.AQUA
|
||||
|
||||
## Manages memory by removing oldest messages when buffer limit exceeded
|
||||
## Uses CLEANUP_PERCENTAGE constant to determine how many messages to remove
|
||||
func _manage_memory() -> void:
|
||||
if _all_messages.size() > max_messages:
|
||||
# Remove oldest messages based on cleanup percentage
|
||||
var remove_count: int = int(max_messages * CLEANUP_PERCENTAGE)
|
||||
_all_messages = _all_messages.slice(remove_count)
|
||||
# Note: This will cause filtered_messages indices to be invalid,
|
||||
# but _reapply_filters() will be called which rebuilds it
|
||||
@@ -1 +0,0 @@
|
||||
uid://wah8efffydnu
|
||||
@@ -1,62 +0,0 @@
|
||||
class_name LogLevelFilter extends OptionButton
|
||||
|
||||
## Standalone log level filter component
|
||||
## Manages log level selection and syncs with Loggie settings
|
||||
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
|
||||
# Signals
|
||||
signal level_changed(new_level: LoggieEnums.LogLevel)
|
||||
|
||||
func _ready() -> void:
|
||||
_setup_level_options()
|
||||
item_selected.connect(_on_level_selected)
|
||||
tooltip_text = "Filter by log level"
|
||||
|
||||
## Initialize with current Loggie settings
|
||||
func initialize() -> void:
|
||||
selected = Loggie.settings.log_level
|
||||
level_changed.emit(Loggie.settings.log_level)
|
||||
|
||||
## Get currently selected log level
|
||||
func get_current_level() -> LoggieEnums.LogLevel:
|
||||
if selected >= 0 and selected < get_item_count():
|
||||
return get_item_id(selected) as LoggieEnums.LogLevel
|
||||
return LoggieEnums.LogLevel.DEBUG
|
||||
|
||||
## Set log level programmatically
|
||||
func set_log_level(level: LoggieEnums.LogLevel) -> void:
|
||||
for i in range(get_item_count()):
|
||||
if get_item_id(i) == level:
|
||||
selected = i
|
||||
break
|
||||
|
||||
## Private methods
|
||||
|
||||
func _setup_level_options() -> void:
|
||||
clear()
|
||||
|
||||
# Add all log levels as options
|
||||
for value in LoggieEnums.LogLevel.values():
|
||||
var label: String = LoggieEnums.LogLevel.keys()[value]
|
||||
add_item(label, value)
|
||||
|
||||
func _on_level_selected(index: int) -> void:
|
||||
var new_level = get_item_id(index) as LoggieEnums.LogLevel
|
||||
|
||||
# Update Loggie settings
|
||||
Loggie.settings.log_level = new_level
|
||||
|
||||
# Emit change signal
|
||||
level_changed.emit(new_level)
|
||||
|
||||
## Saves current log level selection to persistent settings
|
||||
## Param settings: The settings resource to populate with current log level
|
||||
func save_settings_to_resource(settings: LoggieConsoleSettings) -> void:
|
||||
settings.set_log_level(get_current_level())
|
||||
|
||||
## Loads log level selection from persistent settings and applies it
|
||||
## Updates the UI control to reflect the loaded level without emitting change signals
|
||||
## Param settings: The settings resource containing saved log level
|
||||
func load_settings_from_resource(settings: LoggieConsoleSettings) -> void:
|
||||
set_log_level(settings.get_log_level())
|
||||
@@ -1 +0,0 @@
|
||||
uid://d3l2tmf5gym1d
|
||||
@@ -1,381 +0,0 @@
|
||||
class_name PanelDomainSelector extends Control
|
||||
|
||||
## Panel-based domain selector component for filtering log messages by domain
|
||||
## Provides enhanced UI with vertical list, hover "Only" buttons, and Apply/Cancel workflow
|
||||
## Drop-in replacement for MenuButton-based DomainSelector with identical API
|
||||
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
const LoggieConsoleConstants = preload("res://addons/loggie-console/scripts/loggie_console_constants.gd")
|
||||
const DomainItemScene = preload("res://addons/loggie-console/scenes/domain_item.tscn")
|
||||
const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd")
|
||||
|
||||
# Signals - maintains API compatibility with existing DomainSelector
|
||||
signal domains_changed(enabled_domains: Array[String])
|
||||
|
||||
# UI Components - exported for scene tree assignment
|
||||
@export var trigger_button: Button
|
||||
@export var domain_panel: PopupPanel
|
||||
@export var select_all_button: Button
|
||||
@export var select_none_button: Button
|
||||
@export var scroll_container: ScrollContainer
|
||||
@export var domain_list: VBoxContainer
|
||||
|
||||
# Domain management
|
||||
var _all_domains: Array[String] = []
|
||||
var _enabled_domains: Array[String] = []
|
||||
|
||||
# Domain item tracking
|
||||
var _domain_items: Array[DomainItem] = []
|
||||
|
||||
# Panel state tracking
|
||||
var _panel_is_open: bool = false
|
||||
|
||||
# Color management
|
||||
var _color_manager: DomainColorManager
|
||||
|
||||
# Panel sizing constants
|
||||
const PANEL_WIDTH: int = 300
|
||||
const PANEL_MAX_HEIGHT: int = 400
|
||||
const PANEL_MIN_HEIGHT: int = 150
|
||||
|
||||
# Panel positioning constants
|
||||
const PANEL_VERTICAL_GAP: int = 2
|
||||
const PANEL_HORIZONTAL_MARGIN: int = 10
|
||||
|
||||
# Domain item layout constants
|
||||
const DOMAIN_ITEM_HEIGHT: int = 32
|
||||
const PANEL_HEADER_HEIGHT: int = 40
|
||||
const PANEL_SELECTION_CONTROLS_HEIGHT: int = 28
|
||||
const PANEL_FOOTER_HEIGHT: int = 40
|
||||
const PANEL_PADDING: int = 20
|
||||
|
||||
|
||||
## Initializes the panel domain selector component
|
||||
##
|
||||
## Sets up UI connections, panel sizing, and attempts to load domains from Loggie.
|
||||
## If no domains are found, initializes an empty state. All UI components must be
|
||||
## properly assigned via @export variables in the scene for initialization to succeed.
|
||||
func _ready() -> void:
|
||||
if not trigger_button:
|
||||
_initialize_empty_state()
|
||||
return
|
||||
|
||||
domain_panel.hide()
|
||||
|
||||
# Connect UI signals
|
||||
trigger_button.pressed.connect(_on_trigger_button_pressed)
|
||||
select_all_button.pressed.connect(_on_select_all_button_pressed)
|
||||
select_none_button.pressed.connect(_on_select_none_button_pressed)
|
||||
|
||||
# Connect popup signals to track state
|
||||
domain_panel.popup_hide.connect(_on_panel_hidden)
|
||||
domain_panel.about_to_popup.connect(_on_panel_about_to_show)
|
||||
|
||||
# Set initial panel size
|
||||
domain_panel.size = Vector2(PANEL_WIDTH, PANEL_MIN_HEIGHT)
|
||||
|
||||
# Try to initialize with real Loggie domains first
|
||||
_initialize_domains_from_loggie()
|
||||
|
||||
# If no domains found, show empty state
|
||||
if _all_domains.is_empty():
|
||||
_initialize_empty_state()
|
||||
|
||||
## Set the domain color manager instance
|
||||
func set_color_manager(color_manager: DomainColorManager) -> void:
|
||||
_color_manager = color_manager
|
||||
|
||||
## Initialize the selector with available domains
|
||||
func set_domains(domains: Array[String]) -> void:
|
||||
_all_domains = domains.duplicate()
|
||||
_enabled_domains = domains.duplicate()
|
||||
_rebuild_domain_list()
|
||||
_update_trigger_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Initialize the selector with all domains, but only enable selected ones
|
||||
## Console domains appear normally but are unselected by default
|
||||
func set_domains_with_console_unselected(all_domains: Array[String], enabled_domains: Array[String], _console_domains: Array[String]) -> void:
|
||||
_all_domains = all_domains.duplicate()
|
||||
_enabled_domains = enabled_domains.duplicate()
|
||||
_rebuild_domain_list()
|
||||
_update_trigger_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Add a new domain to the selector (for dynamic discovery)
|
||||
func add_domain(domain_name: String) -> void:
|
||||
if domain_name.is_empty() or _all_domains.has(domain_name):
|
||||
return
|
||||
|
||||
_all_domains.append(domain_name)
|
||||
_enabled_domains.append(domain_name)
|
||||
|
||||
_add_domain_item(domain_name, true)
|
||||
_update_trigger_button_text()
|
||||
_update_panel_size()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Get currently enabled domains
|
||||
func get_enabled_domains() -> Array[String]:
|
||||
return _enabled_domains.duplicate()
|
||||
|
||||
## Check if a domain is enabled
|
||||
func is_domain_enabled(domain_name: String) -> bool:
|
||||
return _enabled_domains.has(domain_name)
|
||||
|
||||
## Saves current domain selection state to persistent settings
|
||||
func save_settings_to_resource(settings: LoggieConsoleSettings) -> void:
|
||||
settings.enabled_domains = _enabled_domains.duplicate()
|
||||
settings.all_known_domains = _all_domains.duplicate()
|
||||
|
||||
## Loads domain selection state from persistent settings and rebuilds UI
|
||||
func load_settings_from_resource(settings: LoggieConsoleSettings) -> void:
|
||||
_all_domains = settings.all_known_domains.duplicate()
|
||||
_enabled_domains = settings.enabled_domains.duplicate()
|
||||
|
||||
# Remove any enabled domains that no longer exist in all_domains
|
||||
var valid_enabled_domains: Array[String] = []
|
||||
for domain in _enabled_domains:
|
||||
if _all_domains.has(domain):
|
||||
valid_enabled_domains.append(domain)
|
||||
_enabled_domains = valid_enabled_domains
|
||||
|
||||
# Rebuild UI to reflect loaded state
|
||||
if _all_domains.size() > 0:
|
||||
_rebuild_domain_list()
|
||||
_update_trigger_button_text()
|
||||
else:
|
||||
_initialize_empty_state()
|
||||
|
||||
## Private methods
|
||||
|
||||
func _initialize_empty_state() -> void:
|
||||
if trigger_button:
|
||||
trigger_button.text = "Domains: None"
|
||||
trigger_button.tooltip_text = "No domains available"
|
||||
|
||||
func _initialize_domains_from_loggie() -> void:
|
||||
# Query Loggie for registered domains
|
||||
if not Loggie:
|
||||
return
|
||||
|
||||
var loggie_domains: Array[String] = []
|
||||
|
||||
# Get domains from Loggie's registered domains
|
||||
for domain_name: String in Loggie.domains.keys():
|
||||
if not domain_name.is_empty():
|
||||
loggie_domains.append(domain_name)
|
||||
|
||||
# If we found domains, initialize with them
|
||||
if loggie_domains.size() > 0:
|
||||
set_domains(loggie_domains)
|
||||
Loggie.msg("Initialized domain selector with %d domains from Loggie" % loggie_domains.size()).domain(LoggieConsoleConstants.DOMAIN).info()
|
||||
else:
|
||||
_initialize_empty_state()
|
||||
|
||||
## Refresh domains from Loggie (useful for dynamic discovery)
|
||||
func refresh_domains_from_loggie() -> void:
|
||||
if not Loggie:
|
||||
return
|
||||
|
||||
var _current_domains: Array[String] = _all_domains.duplicate()
|
||||
var loggie_domains: Array[String] = []
|
||||
|
||||
# Get current domains from Loggie
|
||||
for domain_name: String in Loggie.domains.keys():
|
||||
if not domain_name.is_empty():
|
||||
loggie_domains.append(domain_name)
|
||||
|
||||
# Add any new domains we discovered
|
||||
var new_domains_added: bool = false
|
||||
for domain_name in loggie_domains:
|
||||
if not _all_domains.has(domain_name):
|
||||
add_domain(domain_name)
|
||||
new_domains_added = true
|
||||
|
||||
if new_domains_added:
|
||||
Loggie.msg("Discovered new domains from Loggie").domain(LoggieConsoleConstants.DOMAIN).info()
|
||||
|
||||
func _on_trigger_button_pressed() -> void:
|
||||
if _all_domains.is_empty():
|
||||
return
|
||||
|
||||
# Only open panel if not already open (button disabled when open)
|
||||
if not _panel_is_open:
|
||||
# Position and show panel
|
||||
domain_panel.popup()
|
||||
_position_panel()
|
||||
|
||||
|
||||
func _on_select_all_button_pressed() -> void:
|
||||
# Select all domains instantly
|
||||
_enabled_domains = _all_domains.duplicate()
|
||||
_update_domain_items_state()
|
||||
_update_trigger_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
func _on_select_none_button_pressed() -> void:
|
||||
# Deselect all domains instantly
|
||||
_enabled_domains.clear()
|
||||
_update_domain_items_state()
|
||||
_update_trigger_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
## Intelligently positions the domain selection panel relative to the trigger button
|
||||
##
|
||||
## Calculates optimal panel placement considering viewport boundaries and trigger button
|
||||
## position. Prefers positioning below and left-aligned to the trigger button, but will
|
||||
## adjust to above or right-aligned if necessary to keep the panel fully visible.
|
||||
## Ensures minimum margins from viewport edges for accessibility.
|
||||
func _position_panel() -> void:
|
||||
if not trigger_button:
|
||||
return
|
||||
|
||||
# Get trigger button global position and size
|
||||
var button_global_pos: Vector2 = trigger_button.global_position
|
||||
|
||||
var button_size: Vector2 = trigger_button.size
|
||||
await get_tree().process_frame
|
||||
var panel_size: Vector2 = domain_panel.size
|
||||
|
||||
# Get viewport rect for boundary checking
|
||||
var viewport: Viewport = get_viewport()
|
||||
var viewport_rect: Rect2 = viewport.get_visible_rect()
|
||||
|
||||
# Position directly below button, aligned to left edge
|
||||
var panel_pos: Vector2i = Vector2i(
|
||||
button_global_pos.x,
|
||||
button_global_pos.y + button_size.y + PANEL_VERTICAL_GAP
|
||||
)
|
||||
|
||||
# Check if panel would go off-screen horizontally
|
||||
if panel_pos.x + panel_size.x > viewport_rect.size.x:
|
||||
# Align to right edge of button instead
|
||||
panel_pos.x = button_global_pos.x + button_size.x - panel_size.x
|
||||
# Ensure still within bounds
|
||||
panel_pos.x = max(PANEL_HORIZONTAL_MARGIN, panel_pos.x)
|
||||
|
||||
# Check if panel would go off-screen vertically
|
||||
if panel_pos.y + panel_size.y > viewport_rect.size.y:
|
||||
# Position above button instead
|
||||
panel_pos.y = button_global_pos.y - panel_size.y - PANEL_VERTICAL_GAP
|
||||
|
||||
# Final boundary check
|
||||
panel_pos.x = max(PANEL_HORIZONTAL_MARGIN, min(panel_pos.x, viewport_rect.size.x - panel_size.x - PANEL_HORIZONTAL_MARGIN))
|
||||
panel_pos.y = max(PANEL_HORIZONTAL_MARGIN, min(panel_pos.y, viewport_rect.size.y - panel_size.y - PANEL_HORIZONTAL_MARGIN))
|
||||
|
||||
domain_panel.position = get_window().position + panel_pos
|
||||
|
||||
func _rebuild_domain_list() -> void:
|
||||
# Clear existing domain items
|
||||
_clear_domain_items()
|
||||
|
||||
# Create new domain items
|
||||
for domain_name in _all_domains:
|
||||
var is_enabled: bool = _enabled_domains.has(domain_name)
|
||||
_add_domain_item(domain_name, is_enabled)
|
||||
|
||||
_update_panel_size()
|
||||
|
||||
func _clear_domain_items() -> void:
|
||||
for item in _domain_items:
|
||||
if is_instance_valid(item):
|
||||
item.queue_free()
|
||||
_domain_items.clear()
|
||||
|
||||
# Clear any remaining children
|
||||
for child in domain_list.get_children():
|
||||
child.queue_free()
|
||||
|
||||
func _add_domain_item(domain_name: String, is_enabled: bool) -> void:
|
||||
var item: DomainItem = DomainItemScene.instantiate()
|
||||
|
||||
# Add to scene first
|
||||
domain_list.add_child(item)
|
||||
_domain_items.append(item)
|
||||
|
||||
# Set color manager if available
|
||||
if _color_manager:
|
||||
item.set_color_manager(_color_manager)
|
||||
|
||||
# Setup domain after adding to scene tree (ensures _ready is called first)
|
||||
item.setup_domain(domain_name, is_enabled)
|
||||
|
||||
# Connect signals
|
||||
item.selection_changed.connect(_on_domain_item_selection_changed)
|
||||
item.only_button_pressed.connect(_on_domain_item_only_pressed)
|
||||
|
||||
## Dynamically adjusts panel size based on the number of domain items
|
||||
##
|
||||
## Calculates the optimal panel height by summing all component heights (header,
|
||||
## selection controls, domain items, footer, and padding). Clamps the result between
|
||||
## minimum and maximum height constraints to ensure usability and prevent overflow.
|
||||
func _update_panel_size() -> void:
|
||||
if not domain_panel or _domain_items.is_empty():
|
||||
return
|
||||
|
||||
# Calculate required height based on content
|
||||
var required_height: int = PANEL_HEADER_HEIGHT + PANEL_SELECTION_CONTROLS_HEIGHT + (_domain_items.size() * DOMAIN_ITEM_HEIGHT) + PANEL_FOOTER_HEIGHT + PANEL_PADDING
|
||||
var final_height: int = clamp(required_height, PANEL_MIN_HEIGHT, PANEL_MAX_HEIGHT)
|
||||
|
||||
domain_panel.size = Vector2(PANEL_WIDTH, final_height)
|
||||
|
||||
func _update_trigger_button_text() -> void:
|
||||
if not trigger_button:
|
||||
return
|
||||
|
||||
var enabled_count: int = _enabled_domains.size()
|
||||
var total_count: int = _all_domains.size()
|
||||
|
||||
if total_count == 0:
|
||||
trigger_button.text = "Domains: None"
|
||||
trigger_button.tooltip_text = "No domains available"
|
||||
elif enabled_count == total_count:
|
||||
trigger_button.text = "Domains: All"
|
||||
trigger_button.tooltip_text = "All domains selected (%d)" % total_count
|
||||
elif enabled_count == 0:
|
||||
trigger_button.text = "Domains: None"
|
||||
trigger_button.tooltip_text = "No domains selected"
|
||||
elif enabled_count == 1:
|
||||
var domain_name: String = _enabled_domains[0]
|
||||
var display_name: String = domain_name if not domain_name.is_empty() else "(default)"
|
||||
trigger_button.text = "Domains: " + display_name
|
||||
trigger_button.tooltip_text = "Selected: " + display_name
|
||||
else:
|
||||
trigger_button.text = "Domains: %d selected" % enabled_count
|
||||
trigger_button.tooltip_text = "%d of %d domains selected" % [enabled_count, total_count]
|
||||
|
||||
func _update_domain_items_state() -> void:
|
||||
for item in _domain_items:
|
||||
if is_instance_valid(item):
|
||||
var domain_name: String = item.get_domain_name()
|
||||
var is_enabled: bool = _enabled_domains.has(domain_name)
|
||||
item.set_selected(is_enabled)
|
||||
|
||||
func _on_domain_item_selection_changed(domain_name: String, is_selected: bool) -> void:
|
||||
if is_selected and not _enabled_domains.has(domain_name):
|
||||
_enabled_domains.append(domain_name)
|
||||
elif not is_selected and _enabled_domains.has(domain_name):
|
||||
_enabled_domains.erase(domain_name)
|
||||
|
||||
# Update UI and emit change immediately
|
||||
_update_trigger_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
func _on_domain_item_only_pressed(domain_name: String) -> void:
|
||||
# Clear all selections and select only this domain instantly
|
||||
_enabled_domains.clear()
|
||||
_enabled_domains.append(domain_name)
|
||||
_update_domain_items_state()
|
||||
_update_trigger_button_text()
|
||||
domains_changed.emit(_enabled_domains)
|
||||
|
||||
func _on_panel_about_to_show() -> void:
|
||||
_panel_is_open = true
|
||||
if trigger_button:
|
||||
trigger_button.disabled = true
|
||||
|
||||
func _on_panel_hidden() -> void:
|
||||
_panel_is_open = false
|
||||
if trigger_button:
|
||||
trigger_button.disabled = false
|
||||
@@ -1 +0,0 @@
|
||||
uid://bfmpjs4x3fhte
|
||||
@@ -1,123 +0,0 @@
|
||||
class_name RestoreButton extends CanvasLayer
|
||||
|
||||
## Floating restore button component for minimized LoggieConsole
|
||||
##
|
||||
## Self-contained overlay button that appears when the console is minimized.
|
||||
## Handles its own positioning, styling, and lifecycle management with proper
|
||||
## cleanup when the console is restored. Designed to be added to the root viewport
|
||||
## for maximum visibility across all scenes.
|
||||
|
||||
const LoggieConsoleConstants = preload("res://addons/loggie-console/scripts/loggie_console_constants.gd")
|
||||
|
||||
# Alignment constants
|
||||
enum Alignment {
|
||||
TOP_LEFT = 0,
|
||||
TOP_RIGHT = 1,
|
||||
BOTTOM_LEFT = 2,
|
||||
BOTTOM_RIGHT = 3
|
||||
}
|
||||
|
||||
# Signals
|
||||
signal restore_requested() ## Emitted when user clicks the restore button
|
||||
@export var restore_button: Button
|
||||
|
||||
## Initializes the restore button component and sets up styling
|
||||
##
|
||||
## Configures the canvas layer for maximum rendering priority, sets up the
|
||||
## container to fill the viewport, and applies professional styling to the
|
||||
## restore button. All UI components must be properly assigned via @export.
|
||||
func _ready() -> void:
|
||||
Loggie.msg("RestoreButton _ready() called").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
# Connect button signal
|
||||
restore_button.pressed.connect(_on_restore_button_pressed)
|
||||
|
||||
Loggie.msg("RestoreButton initialization complete").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
## Shows the restore button by adding it to the provided scene tree root
|
||||
##
|
||||
## This method expects to be called from a parent that has access to the scene tree.
|
||||
## The parent should pass the root viewport so this component can add itself properly.
|
||||
##
|
||||
## @param root_viewport: The root viewport where the button should be added
|
||||
func show_restore_button(root_viewport: Window = null) -> void:
|
||||
Loggie.msg("RestoreButton show_restore_button() method called").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
if not root_viewport:
|
||||
Loggie.msg("No root viewport available for restore button").domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return
|
||||
|
||||
root_viewport.add_child(self)
|
||||
Loggie.msg("Restore button added to root viewport: %s" % root_viewport.name).domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
await get_tree().process_frame
|
||||
# Ensure visibility
|
||||
visible = true
|
||||
Loggie.msg("Restore button visibility set to true").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
# Force positioning update and debug info
|
||||
await get_tree().process_frame
|
||||
|
||||
## Hides and removes the restore button from the scene tree
|
||||
##
|
||||
## Safely removes the button from its parent and hides it. Handles cleanup
|
||||
## and ensures proper resource management when the console is restored.
|
||||
func hide_restore_button() -> void:
|
||||
Loggie.msg("Hiding restore button").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
visible = false
|
||||
|
||||
# Remove from parent if attached
|
||||
if get_parent():
|
||||
get_parent().remove_child(self)
|
||||
Loggie.msg("Restore button removed from parent").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
## Sets the alignment position of the restore button
|
||||
##
|
||||
## Updates the button's position flags to align it to the specified corner of the screen.
|
||||
## @param alignment: The alignment enum value (TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT)
|
||||
func set_alignment(alignment: Alignment) -> void:
|
||||
if not restore_button:
|
||||
Loggie.msg("RestoreButton not found, cannot set alignment").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return
|
||||
|
||||
match alignment:
|
||||
Alignment.TOP_LEFT:
|
||||
restore_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN # 0 = left align
|
||||
restore_button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN # 0 = top align
|
||||
Alignment.TOP_RIGHT:
|
||||
restore_button.size_flags_horizontal = Control.SIZE_SHRINK_END # 8 = right align
|
||||
restore_button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN # 0 = top align
|
||||
Alignment.BOTTOM_LEFT:
|
||||
restore_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN # 0 = left align
|
||||
restore_button.size_flags_vertical = Control.SIZE_SHRINK_END # 8 = bottom align
|
||||
Alignment.BOTTOM_RIGHT:
|
||||
restore_button.size_flags_horizontal = Control.SIZE_SHRINK_END # 8 = right align
|
||||
restore_button.size_flags_vertical = Control.SIZE_SHRINK_END # 8 = bottom align
|
||||
|
||||
Loggie.msg("Restore button alignment set to: %s" % _get_alignment_name(alignment)).domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
|
||||
## Gets a human-readable name for the alignment enum
|
||||
##
|
||||
## @param alignment: The alignment enum value
|
||||
## @return: String name of the alignment
|
||||
func _get_alignment_name(alignment: Alignment) -> String:
|
||||
match alignment:
|
||||
Alignment.TOP_LEFT:
|
||||
return "Top Left"
|
||||
Alignment.TOP_RIGHT:
|
||||
return "Top Right"
|
||||
Alignment.BOTTOM_LEFT:
|
||||
return "Bottom Left"
|
||||
Alignment.BOTTOM_RIGHT:
|
||||
return "Bottom Right"
|
||||
_:
|
||||
return "Unknown"
|
||||
|
||||
## Handles restore button click events
|
||||
##
|
||||
## Emits the restore_requested signal when the button is pressed, allowing
|
||||
## the parent console to handle the actual restore logic while keeping
|
||||
## this component focused solely on UI concerns.
|
||||
func _on_restore_button_pressed() -> void:
|
||||
Loggie.msg("Restore button pressed").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
restore_requested.emit()
|
||||
@@ -1 +0,0 @@
|
||||
uid://dmyd1gquxy42u
|
||||
@@ -1,125 +0,0 @@
|
||||
class_name LoggieConsoleSettingsPanel extends AcceptDialog
|
||||
|
||||
## Standalone settings panel component for LoggieConsole
|
||||
##
|
||||
## This component provides a clean, reusable interface for adjusting console settings
|
||||
## such as text size multiplier. It emits signals when settings are changed and provides
|
||||
## methods to get/set current values.
|
||||
##
|
||||
## Signals:
|
||||
## - settings_applied(text_size_multiplier: float): Emitted when Apply is clicked
|
||||
## - settings_reset(): Emitted when Reset to Default is clicked
|
||||
##
|
||||
## Public Methods:
|
||||
## - set_text_size_multiplier(value: float): Sets the current text size value
|
||||
## - get_text_size_multiplier() -> float: Gets the current text size value
|
||||
## - reset_to_defaults(): Resets all settings to default values
|
||||
|
||||
## Emitted when the user clicks Apply with current settings
|
||||
signal settings_applied(text_size_multiplier: float, restore_button_alignment: RestoreButton.Alignment)
|
||||
|
||||
## Emitted when the user clicks Reset to Default
|
||||
signal settings_reset()
|
||||
|
||||
# UI component references - assigned via editor
|
||||
@export var _text_size_slider: HSlider
|
||||
@export var _text_size_value_label: Label
|
||||
@export var _reset_button: Button
|
||||
@export var _alignment_option: OptionButton ## Optional - may be null in simplified versions
|
||||
|
||||
# Constants
|
||||
const DEFAULT_TEXT_SIZE_MULTIPLIER: float = 1.0
|
||||
const MIN_TEXT_SIZE_MULTIPLIER: float = 0.5
|
||||
const MAX_TEXT_SIZE_MULTIPLIER: float = 2.0
|
||||
const DEFAULT_RESTORE_BUTTON_ALIGNMENT: RestoreButton.Alignment = RestoreButton.Alignment.TOP_RIGHT
|
||||
|
||||
## Initializes the settings panel and connects internal signals
|
||||
func _ready() -> void:
|
||||
# Connect internal UI signals
|
||||
_text_size_slider.value_changed.connect(_on_text_size_slider_changed)
|
||||
_reset_button.pressed.connect(_on_reset_button_pressed)
|
||||
confirmed.connect(_on_apply_pressed)
|
||||
|
||||
# Initialize display
|
||||
_update_text_size_label(_text_size_slider.value)
|
||||
|
||||
## Sets the text size multiplier value
|
||||
##
|
||||
## Updates the slider position and display label to reflect the new value.
|
||||
## The value is automatically clamped to the valid range (0.5 to 2.0).
|
||||
##
|
||||
## @param value: The text size multiplier (will be clamped to valid range)
|
||||
func set_text_size_multiplier(value: float) -> void:
|
||||
var clamped_value: float = clampf(value, MIN_TEXT_SIZE_MULTIPLIER, MAX_TEXT_SIZE_MULTIPLIER)
|
||||
_text_size_slider.value = clamped_value
|
||||
_update_text_size_label(clamped_value)
|
||||
|
||||
## Gets the current text size multiplier value
|
||||
##
|
||||
## @return: The current text size multiplier value from the slider
|
||||
func get_text_size_multiplier() -> float:
|
||||
return _text_size_slider.value
|
||||
|
||||
## Sets the restore button alignment
|
||||
##
|
||||
## Updates the alignment option button to reflect the new value.
|
||||
## If the alignment option doesn't exist, the setting is ignored.
|
||||
## @param alignment: The restore button alignment enum value
|
||||
func set_restore_button_alignment(alignment: RestoreButton.Alignment) -> void:
|
||||
if _alignment_option:
|
||||
_alignment_option.selected = alignment as int
|
||||
|
||||
## Gets the current restore button alignment
|
||||
##
|
||||
## @return: The current restore button alignment as an enum value, or default if option doesn't exist
|
||||
func get_restore_button_alignment() -> RestoreButton.Alignment:
|
||||
if _alignment_option:
|
||||
return _alignment_option.selected as RestoreButton.Alignment
|
||||
else:
|
||||
return DEFAULT_RESTORE_BUTTON_ALIGNMENT
|
||||
|
||||
## Resets all settings to their default values
|
||||
##
|
||||
## This method resets both text size multiplier and alignment to defaults and emits the settings_reset signal.
|
||||
func reset_to_defaults() -> void:
|
||||
set_text_size_multiplier(DEFAULT_TEXT_SIZE_MULTIPLIER)
|
||||
set_restore_button_alignment(DEFAULT_RESTORE_BUTTON_ALIGNMENT)
|
||||
settings_reset.emit()
|
||||
|
||||
## Shows the settings panel centered on screen
|
||||
##
|
||||
## Convenience method that wraps popup_centered() for easier API usage.
|
||||
func show_settings() -> void:
|
||||
popup_centered()
|
||||
|
||||
## Called when the text size slider value changes
|
||||
##
|
||||
## Updates the value display label to show the current multiplier.
|
||||
## @param value: The new slider value
|
||||
func _on_text_size_slider_changed(value: float) -> void:
|
||||
_update_text_size_label(value)
|
||||
|
||||
## Called when the Reset to Default button is pressed
|
||||
##
|
||||
## Resets settings to defaults and emits the settings_reset signal.
|
||||
func _on_reset_button_pressed() -> void:
|
||||
reset_to_defaults()
|
||||
|
||||
## Called when the Apply button is pressed
|
||||
##
|
||||
## Emits the settings_applied signal with the current settings values.
|
||||
func _on_apply_pressed() -> void:
|
||||
settings_applied.emit(get_text_size_multiplier(), get_restore_button_alignment())
|
||||
|
||||
## Updates the text size value label to show the current multiplier
|
||||
##
|
||||
## @param value: The text size multiplier to display
|
||||
func _update_text_size_label(value: float) -> void:
|
||||
_text_size_value_label.text = "%.1fx" % value
|
||||
|
||||
## Validates that a text size multiplier value is within acceptable bounds
|
||||
##
|
||||
## @param value: The value to validate
|
||||
## @return: True if the value is valid, false otherwise
|
||||
func is_valid_text_size_multiplier(value: float) -> bool:
|
||||
return value >= MIN_TEXT_SIZE_MULTIPLIER and value <= MAX_TEXT_SIZE_MULTIPLIER
|
||||
@@ -1 +0,0 @@
|
||||
uid://giuyu6vhhgam
|
||||
@@ -1,55 +0,0 @@
|
||||
class_name StatusDisplay extends Label
|
||||
|
||||
## Standalone status display component for console information
|
||||
## Shows message counts, filter status, and real-time feedback
|
||||
|
||||
func _ready() -> void:
|
||||
text = "Ready"
|
||||
horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
|
||||
## Update status with message and domain information
|
||||
func update_status(total_messages: int, filtered_messages: int, enabled_domains: Array[String], total_domains: int) -> void:
|
||||
var status_text = ""
|
||||
|
||||
if total_messages == 0:
|
||||
status_text = "Ready"
|
||||
else:
|
||||
# Message count display
|
||||
if filtered_messages == total_messages:
|
||||
status_text = "%d msgs" % total_messages
|
||||
else:
|
||||
status_text = "%d/%d msgs" % [filtered_messages, total_messages]
|
||||
|
||||
# Domain filter info
|
||||
if total_domains > 0 and enabled_domains.size() < total_domains:
|
||||
status_text += " | %d/%d domains" % [enabled_domains.size(), total_domains]
|
||||
|
||||
text = status_text
|
||||
|
||||
## Update status when only message counts change (simpler version)
|
||||
func update_message_counts(total_messages: int, filtered_messages: int) -> void:
|
||||
if total_messages == 0:
|
||||
text = "Ready"
|
||||
elif filtered_messages == total_messages:
|
||||
text = "%d msgs" % total_messages
|
||||
else:
|
||||
text = "%d/%d msgs" % [filtered_messages, total_messages]
|
||||
|
||||
## Show search status when text filtering is active
|
||||
## @param search_term: Current search filter text
|
||||
## @param results_count: Number of messages matching the search
|
||||
## @param total_count: Total number of messages in buffer
|
||||
func update_search_status(search_term: String, results_count: int, total_count: int) -> void:
|
||||
if search_term.is_empty():
|
||||
update_message_counts(total_count, results_count)
|
||||
else:
|
||||
text = "Search: \"%s\" - %d/%d msgs" % [search_term, results_count, total_count]
|
||||
|
||||
## Show loading/processing status with operation name
|
||||
## @param operation: Name of the operation being performed
|
||||
func show_processing_status(operation: String) -> void:
|
||||
text = "%s..." % operation
|
||||
|
||||
## Reset to ready state
|
||||
func reset() -> void:
|
||||
text = "Ready"
|
||||
@@ -1 +0,0 @@
|
||||
uid://whrim6lbiako
|
||||
@@ -1,167 +0,0 @@
|
||||
class_name DomainColorManager extends RefCounted
|
||||
|
||||
## Centralized domain color management for LoggieConsole
|
||||
## Provides consistent color mapping across all console components
|
||||
|
||||
## Domain color mapping information
|
||||
class DomainColorInfo:
|
||||
var domain_name: String
|
||||
var color: Color
|
||||
var palette_index: int
|
||||
var cycle_count: int
|
||||
var is_variation: bool
|
||||
|
||||
func _init(p_domain_name: String, p_color: Color, p_palette_index: int, p_cycle_count: int) -> void:
|
||||
domain_name = p_domain_name
|
||||
color = p_color
|
||||
palette_index = p_palette_index
|
||||
cycle_count = p_cycle_count
|
||||
is_variation = p_cycle_count > 0
|
||||
|
||||
## Color usage statistics and diagnostic information
|
||||
class ColorUsageInfo:
|
||||
var total_domains: int
|
||||
var palette_size: int
|
||||
var current_cycle: int
|
||||
var next_color_index: int
|
||||
var remaining_in_current_cycle: int
|
||||
var has_color_variations: bool
|
||||
|
||||
func _init(p_total_domains: int, p_palette_size: int, p_current_cycle: int, p_next_color_index: int, p_remaining_in_current_cycle: int, p_has_color_variations: bool) -> void:
|
||||
total_domains = p_total_domains
|
||||
palette_size = p_palette_size
|
||||
current_cycle = p_current_cycle
|
||||
next_color_index = p_next_color_index
|
||||
remaining_in_current_cycle = p_remaining_in_current_cycle
|
||||
has_color_variations = p_has_color_variations
|
||||
|
||||
# Domain color mapping storage
|
||||
var _domain_colors: Dictionary = {} # String -> DomainColorInfo
|
||||
var _color_index: int = 0
|
||||
|
||||
# Predefined color palette for domains
|
||||
const DOMAIN_COLOR_PALETTE: Array[Color] = [
|
||||
Color("#FF6B6B"), # Red
|
||||
Color("#4ECDC4"), # Teal
|
||||
Color("#45B7D1"), # Blue
|
||||
Color("#96CEB4"), # Green
|
||||
Color("#FECA57"), # Yellow
|
||||
Color("#FF9FF3"), # Pink
|
||||
Color("#54A0FF"), # Light Blue
|
||||
Color("#5F27CD"), # Purple
|
||||
Color("#00D2D3"), # Cyan
|
||||
Color("#FF9F43"), # Orange
|
||||
Color("#10AC84"), # Mint
|
||||
Color("#EE5A24"), # Dark Orange
|
||||
Color("#0984E3"), # Royal Blue
|
||||
Color("#6C5CE7"), # Violet
|
||||
Color("#A29BFE"), # Light Purple
|
||||
Color("#FD79A8"), # Rose
|
||||
Color("#FDCB6E"), # Light Yellow
|
||||
Color("#6C5CE7"), # Indigo
|
||||
Color("#00B894"), # Emerald
|
||||
Color("#E84393") # Magenta
|
||||
]
|
||||
|
||||
|
||||
## Get or assign a color for a domain
|
||||
func get_domain_color(domain: String) -> Color:
|
||||
if domain.is_empty():
|
||||
return Color.WHITE
|
||||
|
||||
if not _domain_colors.has(domain):
|
||||
# Assign a new color from the palette with potential variations
|
||||
var palette_index: int = _color_index % DOMAIN_COLOR_PALETTE.size()
|
||||
var cycle_count: int = _color_index / DOMAIN_COLOR_PALETTE.size()
|
||||
|
||||
var base_color: Color = DOMAIN_COLOR_PALETTE[palette_index]
|
||||
var color: Color = _get_color_variation(base_color, cycle_count)
|
||||
|
||||
var new_color_info: DomainColorInfo = DomainColorInfo.new(domain, color, palette_index, cycle_count)
|
||||
_domain_colors[domain] = new_color_info
|
||||
_color_index += 1
|
||||
|
||||
var existing_color_info: DomainColorInfo = _domain_colors[domain] as DomainColorInfo
|
||||
return existing_color_info.color
|
||||
|
||||
## Get HTML color string for domain (for BBCode)
|
||||
func get_domain_color_html(domain: String) -> String:
|
||||
return get_domain_color(domain).to_html(false)
|
||||
|
||||
## Check if domain has assigned color
|
||||
func has_domain_color(domain: String) -> bool:
|
||||
return _domain_colors.has(domain)
|
||||
|
||||
## Get domain color information for a specific domain
|
||||
func get_domain_color_info(domain: String) -> DomainColorInfo:
|
||||
if not _domain_colors.has(domain):
|
||||
# Trigger color assignment by calling get_domain_color
|
||||
get_domain_color(domain)
|
||||
|
||||
return _domain_colors[domain] as DomainColorInfo
|
||||
|
||||
## Get all registered domains with their color information
|
||||
func get_all_domain_color_infos() -> Array[DomainColorInfo]:
|
||||
var infos: Array[DomainColorInfo] = []
|
||||
for domain_color_info: DomainColorInfo in _domain_colors.values():
|
||||
infos.append(domain_color_info)
|
||||
return infos
|
||||
|
||||
## Get diagnostic information about color usage and cycles
|
||||
func get_color_usage_info() -> ColorUsageInfo:
|
||||
var current_cycle: int = _color_index / DOMAIN_COLOR_PALETTE.size()
|
||||
var remaining_in_cycle: int = DOMAIN_COLOR_PALETTE.size() - (_color_index % DOMAIN_COLOR_PALETTE.size())
|
||||
|
||||
return ColorUsageInfo.new(
|
||||
_domain_colors.size(),
|
||||
DOMAIN_COLOR_PALETTE.size(),
|
||||
current_cycle,
|
||||
_color_index % DOMAIN_COLOR_PALETTE.size(),
|
||||
remaining_in_cycle,
|
||||
current_cycle > 0
|
||||
)
|
||||
|
||||
## Clear all color assignments (for testing/reset)
|
||||
func clear_all_colors() -> void:
|
||||
_domain_colors.clear()
|
||||
_color_index = 0
|
||||
|
||||
## Get the color palette for reference
|
||||
func get_color_palette() -> Array[Color]:
|
||||
return DOMAIN_COLOR_PALETTE
|
||||
|
||||
## Create color variations for domains beyond the initial palette
|
||||
## Uses brightness and saturation adjustments to create distinguishable variations
|
||||
func _get_color_variation(base_color: Color, cycle_count: int) -> Color:
|
||||
if cycle_count == 0:
|
||||
# First cycle - use original colors
|
||||
return base_color
|
||||
|
||||
# Get HSV components directly from Color
|
||||
var hue: float = base_color.h
|
||||
var saturation: float = base_color.s
|
||||
var value: float = base_color.v
|
||||
|
||||
match cycle_count:
|
||||
1:
|
||||
# Second cycle - Increase brightness
|
||||
value = minf(value * 1.3, 1.0)
|
||||
2:
|
||||
# Third cycle - Decrease saturation (more pastel)
|
||||
saturation = maxf(saturation * 0.6, 0.3)
|
||||
3:
|
||||
# Fourth cycle - Decrease brightness
|
||||
value = maxf(value * 0.7, 0.4)
|
||||
4:
|
||||
# Fifth cycle - High saturation, medium brightness
|
||||
saturation = minf(saturation * 1.2, 1.0)
|
||||
value = maxf(minf(value * 0.85, 1.0), 0.5)
|
||||
_:
|
||||
# Beyond fifth cycle - Use mathematical variations
|
||||
var brightness_mod: float = 1.0 + (cycle_count % 3) * 0.25 - 0.25 # -0.25, 0, +0.25
|
||||
var saturation_mod: float = 1.0 + ((cycle_count + 1) % 3) * 0.2 - 0.2 # -0.2, 0, +0.2
|
||||
|
||||
value = maxf(minf(value * brightness_mod, 1.0), 0.3)
|
||||
saturation = maxf(minf(saturation * saturation_mod, 1.0), 0.2)
|
||||
|
||||
return Color.from_hsv(hue, saturation, value)
|
||||
@@ -1 +0,0 @@
|
||||
uid://x0ut5e0gpyu1
|
||||
@@ -1,66 +0,0 @@
|
||||
class_name LoggieConsoleChannel extends LoggieMsgChannel
|
||||
|
||||
## Custom Loggie channel that feeds messages directly to the LoggieConsole
|
||||
## This channel captures all message metadata (level, type, domain, channels, stack info)
|
||||
## without requiring string parsing
|
||||
|
||||
|
||||
# Reference to the console window that will display messages
|
||||
var _console_window: LoggieConsole
|
||||
|
||||
func _init() -> void:
|
||||
ID = "loggie_console"
|
||||
# Enable all preprocessing steps to get complete information
|
||||
preprocess_flags = (
|
||||
LoggieEnums.PreprocessStep.APPEND_TIMESTAMPS |
|
||||
LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME |
|
||||
LoggieEnums.PreprocessStep.APPEND_CLASS_NAME
|
||||
)
|
||||
|
||||
## Sets the console window that will receive messages from this channel
|
||||
## @param window: The console window instance, must not be null
|
||||
func set_console_window(window: LoggieConsole) -> void:
|
||||
if window == null:
|
||||
Loggie.msg("Cannot set null console window").domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return
|
||||
_console_window = window
|
||||
|
||||
## Override send method to pass complete message data to console
|
||||
## @param msg: The Loggie message object containing all metadata
|
||||
## @param msg_type: The message type enum from Loggie
|
||||
func send(msg: LoggieMsg, msg_type: LoggieEnums.MsgType) -> void:
|
||||
if _console_window == null:
|
||||
Loggie.msg("No console window set, message dropped").domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return
|
||||
|
||||
if msg == null:
|
||||
Loggie.msg("Received null message").domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return
|
||||
|
||||
# Get the preprocessed text (already formatted by Loggie)
|
||||
var preprocessed_text = msg.last_preprocess_result
|
||||
|
||||
# Determine log level from the message content patterns or message type
|
||||
var log_level = _determine_log_level(msg, msg_type, preprocessed_text)
|
||||
|
||||
# Send complete message data to console
|
||||
_console_window.receive_log_message(msg, preprocessed_text, msg_type, log_level)
|
||||
|
||||
## Determine the log level based on message type and content
|
||||
func _determine_log_level(_msg: LoggieMsg, msg_type: LoggieEnums.MsgType, content: String) -> LoggieEnums.LogLevel:
|
||||
# Use message type to infer log level
|
||||
match msg_type:
|
||||
LoggieEnums.MsgType.ERROR:
|
||||
return LoggieEnums.LogLevel.ERROR
|
||||
LoggieEnums.MsgType.WARN:
|
||||
return LoggieEnums.LogLevel.WARN
|
||||
LoggieEnums.MsgType.DEBUG:
|
||||
return LoggieEnums.LogLevel.DEBUG
|
||||
LoggieEnums.MsgType.INFO:
|
||||
# For standard messages, check content patterns to determine level
|
||||
if content.contains("[NOTICE]"):
|
||||
return LoggieEnums.LogLevel.NOTICE
|
||||
else:
|
||||
return LoggieEnums.LogLevel.INFO
|
||||
_:
|
||||
return LoggieEnums.LogLevel.INFO
|
||||
@@ -1 +0,0 @@
|
||||
uid://bcehgenffbmr3
|
||||
@@ -1,9 +0,0 @@
|
||||
class_name LoggieConsoleConstants extends RefCounted
|
||||
|
||||
## Centralized constants for the LoggieConsole addon
|
||||
##
|
||||
## Contains all shared constants used across LoggieConsole components to ensure
|
||||
## consistency and avoid duplication. Use StringName for efficient string operations.
|
||||
|
||||
## Domain for all LoggieConsole logging messages
|
||||
const DOMAIN: StringName = &"LOGGIE_CONSOLE"
|
||||
@@ -1 +0,0 @@
|
||||
uid://7iexr4yxwqfg
|
||||
@@ -1,96 +0,0 @@
|
||||
class_name ConsoleSettingsManager extends RefCounted
|
||||
|
||||
## Manages persistent storage of LoggieConsole settings
|
||||
##
|
||||
## Provides robust save/load functionality with automatic backup creation and
|
||||
## comprehensive error handling. Settings are saved immediately when requested
|
||||
## (no debouncing) and loaded with fallback to backup file or defaults.
|
||||
|
||||
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
|
||||
|
||||
# File paths and configuration
|
||||
const SETTINGS_PATH = "user://loggie_console_settings.tres"
|
||||
const BACKUP_PATH = "user://loggie_console_settings_backup.tres"
|
||||
const SETTINGS_RESOURCE_TYPE = "LoggieConsoleSettings"
|
||||
|
||||
## Loads console settings with comprehensive error handling and fallback chain
|
||||
##
|
||||
## Load order: primary file → backup file → create defaults
|
||||
## All loaded settings are validated and sanitized before returning.
|
||||
## Returns: A valid LoggieConsoleSettings instance (never null)
|
||||
func load_settings() -> LoggieConsoleSettings:
|
||||
var settings: LoggieConsoleSettings = null
|
||||
|
||||
# Attempt to load primary settings file
|
||||
settings = _try_load_file(SETTINGS_PATH, "main settings")
|
||||
|
||||
# Fall back to backup file if primary failed
|
||||
if settings == null:
|
||||
settings = _try_load_file(BACKUP_PATH, "backup settings")
|
||||
|
||||
# Create default settings if both files failed
|
||||
if settings == null:
|
||||
Loggie.msg("Creating default console settings").domain(LoggieConsoleConstants.DOMAIN).info()
|
||||
settings = LoggieConsoleSettings.new()
|
||||
|
||||
# Always validate to ensure data integrity
|
||||
settings.validate()
|
||||
|
||||
Loggie.msg("Console settings loaded successfully").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
return settings
|
||||
|
||||
## Saves console settings to disk immediately with backup creation
|
||||
##
|
||||
## Creates a backup of the existing file before saving, then validates
|
||||
## the settings and writes them using Godot's ResourceSaver.
|
||||
## Param settings: The settings to save (must not be null)
|
||||
## Returns: true if save succeeded, false if it failed
|
||||
func save_settings(settings: LoggieConsoleSettings) -> bool:
|
||||
return _save_with_backup(settings)
|
||||
|
||||
## Attempts to load a settings file with error handling
|
||||
## Param path: File path to load from
|
||||
## Param description: Human-readable description for logging
|
||||
## Returns: Loaded settings or null if loading failed
|
||||
func _try_load_file(path: String, description: String) -> LoggieConsoleSettings:
|
||||
if not FileAccess.file_exists(path):
|
||||
Loggie.msg("Settings file not found: %s" % path).domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
return null
|
||||
|
||||
var resource: Resource = ResourceLoader.load(path, SETTINGS_RESOURCE_TYPE)
|
||||
if resource == null:
|
||||
Loggie.msg("Failed to load %s from: %s" % [description, path]).domain(LoggieConsoleConstants.DOMAIN).warn()
|
||||
return null
|
||||
|
||||
if not resource is LoggieConsoleSettings:
|
||||
Loggie.msg("Invalid resource type in %s: %s" % [description, path]).domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return null
|
||||
|
||||
Loggie.msg("Loaded %s from: %s" % [description, path]).domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
return resource as LoggieConsoleSettings
|
||||
|
||||
## Saves settings with automatic backup creation
|
||||
## Param settings: The settings to save
|
||||
## Returns: true if save succeeded, false otherwise
|
||||
func _save_with_backup(settings: LoggieConsoleSettings) -> bool:
|
||||
if not settings:
|
||||
Loggie.msg("Cannot save null settings").domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return false
|
||||
|
||||
# Validate settings before saving
|
||||
settings.validate()
|
||||
|
||||
# Create backup of existing file before overwriting
|
||||
if FileAccess.file_exists(SETTINGS_PATH):
|
||||
var dir: DirAccess = DirAccess.open("user://")
|
||||
if dir:
|
||||
dir.copy(SETTINGS_PATH, BACKUP_PATH)
|
||||
|
||||
# Save the validated settings
|
||||
var error: Error = ResourceSaver.save(settings, SETTINGS_PATH)
|
||||
if error != OK:
|
||||
Loggie.msg("Failed to save console settings: %s" % error_string(error)).domain(LoggieConsoleConstants.DOMAIN).error()
|
||||
return false
|
||||
|
||||
Loggie.msg("Console settings saved successfully").domain(LoggieConsoleConstants.DOMAIN).debug()
|
||||
return true
|
||||
@@ -1 +0,0 @@
|
||||
uid://bntgmwyauc4h6
|
||||
@@ -1,141 +0,0 @@
|
||||
[gd_resource type="Theme" format=3 uid="uid://c1lnnysnspc6v"]
|
||||
|
||||
[ext_resource type="StyleBox" uid="uid://dolgdjpxlvt8f" path="res://addons/loggie-console/assets/inner_panel_recess.tres" id="1_nubd1"]
|
||||
[ext_resource type="Texture2D" uid="uid://bhps6csrl6lef" path="res://addons/loggie-console/assets/close.png" id="1_rapoa"]
|
||||
[ext_resource type="StyleBox" uid="uid://bpkqcbx15ejak" path="res://addons/loggie-console/assets/outer_panel.tres" id="2_rapoa"]
|
||||
[ext_resource type="StyleBox" uid="uid://cfafk3ybc15uo" path="res://addons/loggie-console/assets/outer_window_panel.tres" id="3_f200k"]
|
||||
[ext_resource type="StyleBox" uid="uid://b7k8yo7jfhpue" path="res://addons/loggie-console/assets/inner_panel.tres" id="3_p21dw"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_button_normal"]
|
||||
content_margin_left = 3.0
|
||||
content_margin_top = 1.0
|
||||
content_margin_right = 3.0
|
||||
content_margin_bottom = 1.0
|
||||
bg_color = Color(0.25, 0.25, 0.25, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.4, 0.4, 0.4, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_checkbox_normal"]
|
||||
bg_color = Color(0.15, 0.15, 0.15, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.4, 0.4, 0.4, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"]
|
||||
bg_color = Color(0.15, 0.15, 0.15, 0.95)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.3, 0.3, 0.3, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_richtextlabel"]
|
||||
content_margin_left = 1.0
|
||||
content_margin_top = 1.0
|
||||
content_margin_right = 1.0
|
||||
content_margin_bottom = 1.0
|
||||
bg_color = Color(0.1, 0.1, 0.1, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.3, 0.3, 0.3, 1)
|
||||
|
||||
[sub_resource type="SystemFont" id="SystemFont_f200k"]
|
||||
font_names = PackedStringArray("Monospace")
|
||||
font_weight = 800
|
||||
generate_mipmaps = true
|
||||
multichannel_signed_distance_field = true
|
||||
|
||||
[resource]
|
||||
Button/colors/font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
Button/colors/font_disabled_color = Color(0.5, 0.5, 0.5, 1)
|
||||
Button/colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
Button/colors/font_pressed_color = Color(0.8, 0.8, 0.8, 1)
|
||||
Button/font_sizes/font_size = 11
|
||||
Button/styles/disabled = SubResource("StyleBoxFlat_button_normal")
|
||||
Button/styles/hover = ExtResource("2_rapoa")
|
||||
Button/styles/normal = ExtResource("2_rapoa")
|
||||
Button/styles/pressed = ExtResource("1_nubd1")
|
||||
CheckBox/colors/font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
CheckBox/colors/font_disabled_color = Color(0.5, 0.5, 0.5, 1)
|
||||
CheckBox/colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
CheckBox/colors/font_pressed_color = Color(0.8, 0.8, 0.8, 1)
|
||||
CheckBox/font_sizes/font_size = 11
|
||||
CheckBox/styles/disabled = SubResource("StyleBoxFlat_checkbox_normal")
|
||||
CheckBox/styles/hover_pressed = ExtResource("2_rapoa")
|
||||
CheckBox/styles/normal = ExtResource("2_rapoa")
|
||||
CheckBox/styles/pressed = ExtResource("2_rapoa")
|
||||
HBoxContainer/constants/separation = 2
|
||||
LineEdit/colors/caret_color = Color(1, 1, 1, 1)
|
||||
LineEdit/colors/clear_button_color = Color(0, 0, 0, 1)
|
||||
LineEdit/colors/clear_button_color_pressed = Color(0, 0, 0, 1)
|
||||
LineEdit/colors/font_color = Color(1, 1, 1, 1)
|
||||
LineEdit/colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
LineEdit/colors/font_placeholder_color = Color(0.447904, 0.509153, 0.407339, 1)
|
||||
LineEdit/colors/font_selected_color = Color(0.792157, 0.901961, 0.85098, 1)
|
||||
LineEdit/colors/font_uneditable_color = Color(0, 0, 0, 1)
|
||||
LineEdit/colors/selection_color = Color(0.188235, 0.219608, 0.262745, 1)
|
||||
LineEdit/constants/caret_width = 0
|
||||
LineEdit/constants/minimum_character_width = 0
|
||||
LineEdit/constants/outline_size = 0
|
||||
LineEdit/font_sizes/font_size = 12
|
||||
LineEdit/fonts/font = null
|
||||
LineEdit/icons/clear = null
|
||||
LineEdit/styles/focus = ExtResource("1_nubd1")
|
||||
LineEdit/styles/normal = ExtResource("1_nubd1")
|
||||
LineEdit/styles/read_only = ExtResource("3_p21dw")
|
||||
MarginContainer/constants/margin_bottom = 1
|
||||
MarginContainer/constants/margin_left = 1
|
||||
MarginContainer/constants/margin_right = 1
|
||||
MarginContainer/constants/margin_top = 1
|
||||
MenuButton/colors/font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
MenuButton/colors/font_disabled_color = Color(0.5, 0.5, 0.5, 1)
|
||||
MenuButton/colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
MenuButton/colors/font_pressed_color = Color(0.8, 0.8, 0.8, 1)
|
||||
MenuButton/font_sizes/font_size = 11
|
||||
MenuButton/styles/hover = ExtResource("1_nubd1")
|
||||
MenuButton/styles/normal = ExtResource("2_rapoa")
|
||||
MenuButton/styles/pressed = ExtResource("2_rapoa")
|
||||
OptionButton/colors/font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
OptionButton/colors/font_disabled_color = Color(0.5, 0.5, 0.5, 1)
|
||||
OptionButton/colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
OptionButton/colors/font_pressed_color = Color(0.8, 0.8, 0.8, 1)
|
||||
OptionButton/font_sizes/font_size = 11
|
||||
OptionButton/styles/disabled = ExtResource("3_f200k")
|
||||
OptionButton/styles/hover = ExtResource("1_nubd1")
|
||||
OptionButton/styles/normal = ExtResource("1_nubd1")
|
||||
OptionButton/styles/pressed = ExtResource("1_nubd1")
|
||||
Panel/styles/panel = SubResource("StyleBoxFlat_panel")
|
||||
PopupMenu/colors/font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
PopupMenu/colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
PopupMenu/colors/font_separator_color = Color(0.5, 0.5, 0.5, 1)
|
||||
PopupMenu/font_sizes/font_size = 11
|
||||
PopupMenu/styles/hover = ExtResource("1_nubd1")
|
||||
PopupMenu/styles/normal = ExtResource("1_nubd1")
|
||||
PopupMenu/styles/panel = ExtResource("2_rapoa")
|
||||
PopupPanel/styles/panel = ExtResource("2_rapoa")
|
||||
RichTextLabel/colors/default_color = Color(0.9, 0.9, 0.9, 1)
|
||||
RichTextLabel/colors/selection_color = Color(0.4, 0.6, 0.8, 0.4)
|
||||
RichTextLabel/font_sizes/bold_font_size = 10
|
||||
RichTextLabel/font_sizes/bold_italics_font_size = 10
|
||||
RichTextLabel/font_sizes/italics_font_size = 10
|
||||
RichTextLabel/font_sizes/mono_font_size = 9
|
||||
RichTextLabel/font_sizes/normal_font_size = 10
|
||||
RichTextLabel/styles/normal = SubResource("StyleBoxFlat_richtextlabel")
|
||||
VBoxContainer/constants/separation = 2
|
||||
Window/colors/title_color = Color(0.175119, 0.187776, 0.176406, 1)
|
||||
Window/constants/close_h_offset = 18
|
||||
Window/constants/close_v_offset = 20
|
||||
Window/constants/title_height = 22
|
||||
Window/font_sizes/title_font_size = 16
|
||||
Window/fonts/title_font = SubResource("SystemFont_f200k")
|
||||
Window/icons/close = ExtResource("1_rapoa")
|
||||
Window/icons/close_pressed = ExtResource("1_rapoa")
|
||||
Window/styles/embedded_border = ExtResource("3_f200k")
|
||||
Window/styles/embedded_unfocused_border = ExtResource("3_f200k")
|
||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://2fr6et0qni2y"
|
||||
path="res://.godot/imported/icon.png-57313fc4d67f18c33a83a3388ad36531.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-57313fc4d67f18c33a83a3388ad36531.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 58 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://duuj0ibyreygv"
|
||||
path="res://.godot/imported/logo.png-2771abf7e361d7f10d5859133bc43562.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/logo.png"
|
||||
dest_files=["res://.godot/imported/logo.png-2771abf7e361d7f10d5859133bc43562.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bfnp2a0sbhi2x"
|
||||
path="res://.godot/imported/checkbox_checked.png-a77d9d083c4ebd2bf01389a1bba7091b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/theme/checkbox/checkbox_checked.png"
|
||||
dest_files=["res://.godot/imported/checkbox_checked.png-a77d9d083c4ebd2bf01389a1bba7091b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 4.8 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dqf5cye21gyw8"
|
||||
path="res://.godot/imported/checkbox_checked_disabled.png-0752d58bb0616edc7e127ccd992cc02f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/theme/checkbox/checkbox_checked_disabled.png"
|
||||
dest_files=["res://.godot/imported/checkbox_checked_disabled.png-0752d58bb0616edc7e127ccd992cc02f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bib1lwikra5kr"
|
||||
path="res://.godot/imported/checkbox_unchecked.png-4c82347fbe6c6b60f40739ece2ad0dba.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/theme/checkbox/checkbox_unchecked.png"
|
||||
dest_files=["res://.godot/imported/checkbox_unchecked.png-4c82347fbe6c6b60f40739ece2ad0dba.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cloe7vx2ej0nf"
|
||||
path="res://.godot/imported/checkbox_unchecked_disabled.png-aa159b17a13862a51b7ba58c26a64745.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/theme/checkbox/checkbox_unchecked_disabled.png"
|
||||
dest_files=["res://.godot/imported/checkbox_unchecked_disabled.png-aa159b17a13862a51b7ba58c26a64745.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,45 +0,0 @@
|
||||
Font: Patrick Hand SC
|
||||
|
||||
Source: https://fonts.google.com/specimen/Patrick+Hand+SC/license
|
||||
|
||||
Designed by Patrick Wagesreiter
|
||||
License
|
||||
Copyright (c) 2010-2012 Patrick Wagesreiter (mail@patrickwagesreiter.at)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1 . This license is copied below, and is also available with a FAQ at: https://openfontlicense.org
|
||||
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
|
||||
PREAMBLE
|
||||
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
|
||||
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
||||
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||
|
||||
Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||
Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||
No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||
The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||
The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
|
||||
This license becomes null and void if any of the above conditions are not met.
|
||||
DISCLAIMER
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -1,36 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://btuvtbhws7p8s"
|
||||
path="res://.godot/imported/PatrickHandSC-Regular.ttf-a768b75713759378d46297f0740f6da5.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/theme/fonts/PatrickHandSC-Regular.ttf"
|
||||
dest_files=["res://.godot/imported/PatrickHandSC-Regular.ttf-a768b75713759378d46297f0740f6da5.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
disable_embedded_bitmaps=true
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
modulate_color_glyphs=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
keep_rounding_remainders=true
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
@@ -1,30 +0,0 @@
|
||||
Font: Coffee Soda
|
||||
Source: https://www.creativefabrica.com/product/coffee-soda/ref/53/
|
||||
|
||||
--------- AUTHOR'S NOTE ---------
|
||||
|
||||
First of All,
|
||||
|
||||
Thank you for download my product.
|
||||
This product 100% free for personal use & commercial use
|
||||
|
||||
File include :
|
||||
|
||||
1. Coffee Soda.ttf
|
||||
2. Coffee Soda.otf
|
||||
|
||||
More Info :
|
||||
khurasantype@gmail.com
|
||||
|
||||
For Donation:
|
||||
paypal.me/khurasantype
|
||||
|
||||
My OnlineShop:
|
||||
https://www.creativefabrica.com/ref/53/
|
||||
|
||||
My Portofolio:
|
||||
https://www.behance.net/khurasan
|
||||
|
||||
My web:
|
||||
www.khurasanstudio.com
|
||||
www.mbkaos.com
|
||||
@@ -1,36 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://e3rpni7mpu0p"
|
||||
path="res://.godot/imported/coffee_soda.ttf-1c45a1f5c42cb2ffa335471f7a4924a3.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/theme/fonts/coffee_soda.ttf"
|
||||
dest_files=["res://.godot/imported/coffee_soda.ttf-1c45a1f5c42cb2ffa335471f7a4924a3.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
disable_embedded_bitmaps=true
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
modulate_color_glyphs=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
keep_rounding_remainders=true
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
@@ -1,14 +0,0 @@
|
||||
[gd_resource type="StyleBoxFlat" format=3 uid="uid://ckw36egxdynxc"]
|
||||
|
||||
[resource]
|
||||
draw_center = false
|
||||
border_width_left = 3
|
||||
border_width_top = 3
|
||||
border_width_right = 3
|
||||
border_width_bottom = 3
|
||||
border_color = Color(0.71, 0.52114, 0.1704, 0.772549)
|
||||
border_blend = true
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
corner_radius_bottom_right = 10
|
||||
corner_radius_bottom_left = 10
|
||||
@@ -1,95 +0,0 @@
|
||||
[gd_resource type="Theme" load_steps=10 format=3 uid="uid://bntkg3oi4b314"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://btuvtbhws7p8s" path="res://addons/loggie/assets/theme/fonts/PatrickHandSC-Regular.ttf" id="1_ucfms"]
|
||||
[ext_resource type="Texture2D" uid="uid://bfnp2a0sbhi2x" path="res://addons/loggie/assets/theme/checkbox/checkbox_checked.png" id="2_tqabx"]
|
||||
[ext_resource type="Texture2D" uid="uid://dqf5cye21gyw8" path="res://addons/loggie/assets/theme/checkbox/checkbox_checked_disabled.png" id="3_plx1a"]
|
||||
[ext_resource type="Texture2D" uid="uid://bib1lwikra5kr" path="res://addons/loggie/assets/theme/checkbox/checkbox_unchecked.png" id="4_yp55b"]
|
||||
[ext_resource type="Texture2D" uid="uid://cloe7vx2ej0nf" path="res://addons/loggie/assets/theme/checkbox/checkbox_unchecked_disabled.png" id="5_0424s"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gb03k"]
|
||||
bg_color = Color(0.349691, 0.30101, 0.201282, 1)
|
||||
border_width_left = 4
|
||||
border_width_top = 4
|
||||
border_width_right = 4
|
||||
border_width_bottom = 4
|
||||
border_color = Color(0.361244, 0.323458, 0.213348, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
expand_margin_left = 2.0
|
||||
expand_margin_top = 1.0
|
||||
expand_margin_right = 2.0
|
||||
expand_margin_bottom = 1.0
|
||||
anti_aliasing_size = 0.537
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_73pt3"]
|
||||
bg_color = Color(0.767332, 0.598222, 0.330864, 1)
|
||||
border_width_left = 4
|
||||
border_width_top = 4
|
||||
border_width_right = 4
|
||||
border_width_bottom = 4
|
||||
border_color = Color(0.943861, 0.775452, 0.522251, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
expand_margin_left = 2.0
|
||||
expand_margin_top = 1.0
|
||||
expand_margin_right = 2.0
|
||||
expand_margin_bottom = 1.0
|
||||
anti_aliasing_size = 0.537
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xxk7l"]
|
||||
bg_color = Color(0.661808, 0.507874, 0.261375, 1)
|
||||
border_width_left = 4
|
||||
border_width_top = 4
|
||||
border_width_right = 4
|
||||
border_width_bottom = 4
|
||||
border_color = Color(0.83, 0.629555, 0.3071, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
expand_margin_left = 2.0
|
||||
expand_margin_top = 1.0
|
||||
expand_margin_right = 2.0
|
||||
expand_margin_bottom = 1.0
|
||||
anti_aliasing_size = 0.537
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_i1fiu"]
|
||||
bg_color = Color(0.6, 0.4574, 0.228, 1)
|
||||
border_width_left = 4
|
||||
border_width_top = 4
|
||||
border_width_right = 4
|
||||
border_width_bottom = 4
|
||||
border_color = Color(0.83, 0.629555, 0.3071, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
expand_margin_left = 2.0
|
||||
expand_margin_top = 1.0
|
||||
expand_margin_right = 2.0
|
||||
expand_margin_bottom = 1.0
|
||||
anti_aliasing_size = 0.537
|
||||
|
||||
[resource]
|
||||
Button/constants/outline_size = 6
|
||||
Button/font_sizes/font_size = 30
|
||||
Button/fonts/font = ExtResource("1_ucfms")
|
||||
Button/styles/disabled = SubResource("StyleBoxFlat_gb03k")
|
||||
Button/styles/focus = SubResource("StyleBoxFlat_73pt3")
|
||||
Button/styles/hover = SubResource("StyleBoxFlat_xxk7l")
|
||||
Button/styles/normal = SubResource("StyleBoxFlat_i1fiu")
|
||||
CheckBox/icons/checked = ExtResource("2_tqabx")
|
||||
CheckBox/icons/checked_disabled = ExtResource("3_plx1a")
|
||||
CheckBox/icons/radio_checked = ExtResource("2_tqabx")
|
||||
CheckBox/icons/radio_checked_disabled = ExtResource("3_plx1a")
|
||||
CheckBox/icons/radio_unchecked = ExtResource("4_yp55b")
|
||||
CheckBox/icons/radio_unchecked_disabled = ExtResource("5_0424s")
|
||||
CheckBox/icons/unchecked = ExtResource("4_yp55b")
|
||||
CheckBox/icons/unchecked_disabled = ExtResource("5_0424s")
|
||||
Label/colors/font_color = Color(0.980392, 0.843137, 0.619608, 1)
|
||||
Label/constants/outline_size = 3
|
||||
Label/font_sizes/font_size = 21
|
||||
|
Before Width: | Height: | Size: 685 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cgh6hd3u8nlpj"
|
||||
path="res://.godot/imported/updater_bg.png-c90bec38cc1681b029907f694c893180.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/loggie/assets/updater_bg.png"
|
||||
dest_files=["res://.godot/imported/updater_bg.png-c90bec38cc1681b029907f694c893180.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,28 +0,0 @@
|
||||
extends LoggieMsgChannel
|
||||
|
||||
func _init() -> void:
|
||||
self.ID = "<your-custom-id>"
|
||||
|
||||
# Example - Customize preprocess flags:
|
||||
# self.preprocess_flags = LoggieEnums.PreprocessStep.APPEND_TIMESTAMPS | LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME
|
||||
|
||||
func send(msg : LoggieMsg, msg_type : LoggieEnums.MsgType):
|
||||
# Validate that the message is coming from a valid logger if you are going to be requiring access to a [Loggie] instance.
|
||||
var loggie = msg.get_logger() # Access a [Loggie] instance directly from the message.
|
||||
if loggie == null:
|
||||
push_error("Attempt to send a message that's coming from an invalid logger.")
|
||||
return
|
||||
|
||||
# To access the most recently preprocessed version of the message - use:
|
||||
# msg.last_preprocess_result
|
||||
|
||||
# To access a string version of the message (no preprocessing) - use:
|
||||
var msg_text = msg.string()
|
||||
|
||||
# Optionally
|
||||
# We can use `LoggieTools.convert_string_to_format_mode` to apply one of the Loggie format modes to it.
|
||||
# Explore more functions in [LoggieTools] and [LoggieMsg].
|
||||
# var converted = LoggieTools.convert_string_to_format_mode(msg.last_preprocess_result, loggie.settings.msg_format_mode)
|
||||
|
||||
# Do something with the message.
|
||||
print(msg_text)
|
||||
@@ -1,75 +0,0 @@
|
||||
class_name DiscordLoggieMsgChannel extends LoggieMsgChannel
|
||||
|
||||
const discord_msg_character_limit = 2000 # The max. amount of characters the content of the message can contain before discord refuses to post it.
|
||||
var debug_domain = "_d_loggie_discord"
|
||||
var debug_enabled = false
|
||||
|
||||
func _init() -> void:
|
||||
self.ID = "discord"
|
||||
self.preprocess_flags = 0 # For this type of channel, this will be applied dynamically by Loggie after it loads LoggieSettings.
|
||||
|
||||
func send(msg : LoggieMsg, msg_type : LoggieEnums.MsgType):
|
||||
# Validate variables.
|
||||
var loggie = msg.get_logger()
|
||||
if loggie == null:
|
||||
push_error("Attempt to send a message that's coming from an invalid logger.")
|
||||
return
|
||||
|
||||
# Wait until loggie is inside tree so that we can use add_child(http) on it without errors.
|
||||
if !loggie.is_inside_tree():
|
||||
loggie.tree_entered.connect(func():
|
||||
send(msg, msg_type)
|
||||
, CONNECT_ONE_SHOT)
|
||||
return
|
||||
|
||||
var webhook_url = loggie.settings.discord_webhook_url_live if loggie.is_in_production() else loggie.settings.discord_webhook_url_dev
|
||||
if webhook_url == null or (webhook_url is String and webhook_url.is_empty()):
|
||||
push_error("Attempt to send a message to the Discord channel with an invalid webhook_url.")
|
||||
return
|
||||
|
||||
var output_text = LoggieTools.convert_string_to_format_mode(msg.last_preprocess_result, LoggieEnums.MsgFormatMode.MARKDOWN)
|
||||
|
||||
# Chunk the given string into chunks of maximum supported size by Discord, so we don't end up hitting the character limit
|
||||
# which would prevent the message from getting posted.
|
||||
var chunks = LoggieTools.chunk_string(output_text, discord_msg_character_limit)
|
||||
if chunks.size() > 1:
|
||||
loggie.debug("Chunking a long (", output_text.length(), "length ) message while sending to Discord into:", chunks.size(), "chunks.")
|
||||
for chunk : String in chunks:
|
||||
call_deferred("send_post_request", loggie, chunk, webhook_url)
|
||||
|
||||
func send_post_request(logger : Variant, output_text : String, webhook_url : String):
|
||||
# Enable debug messages if configured.
|
||||
logger.set_domain_enabled(debug_domain, debug_enabled)
|
||||
|
||||
# Create a new HTTPRequest POST request that will be sent to Discord and add it into the scenetree.
|
||||
var http = HTTPRequest.new()
|
||||
logger.add_child(http)
|
||||
|
||||
# When the request is completed, destroy it.
|
||||
http.request_completed.connect(func(result, response_code, headers, body):
|
||||
var debug_msg = logger.msg("HTTP Request Completed:").color(Color.ORANGE).header().domain(debug_domain).channel("terminal")
|
||||
debug_msg.nl().msg("Result:").color(Color.ORANGE).bold().space().msg(result).nl()
|
||||
debug_msg.msg("Response Code:").color(Color.ORANGE).bold().space().msg(response_code).nl()
|
||||
debug_msg.msg("Headers:").color(Color.ORANGE).bold().space().msg(headers).nl()
|
||||
debug_msg.msg("Body:").color(Color.ORANGE).bold().space().msg(body)
|
||||
debug_msg.debug()
|
||||
|
||||
## Inform the user about a received non-success response code.
|
||||
if response_code < 200 or response_code > 299:
|
||||
logger.msg("Discord responded with a non-success code: ").bold().msg(response_code, " - This is an indicator that something about the message you tried to send to Discord does not comply with their request body standards (e.g. content is too long, invalid format, etc.)").channel("terminal").warn()
|
||||
|
||||
http.queue_free()
|
||||
)
|
||||
|
||||
# Convert the [LoggieMsg]'s contents into markdown and post that to the target webhook url.
|
||||
var json = JSON.stringify({"content": output_text})
|
||||
var header = ["Content-Type: application/json"]
|
||||
|
||||
# Construct debug message.
|
||||
if debug_enabled:
|
||||
var debug_msg_post = logger.msg("Sending POST Request:").color(Color.CORNFLOWER_BLUE).header().channel("terminal").domain(debug_domain).nl()
|
||||
debug_msg_post.msg("JSON stringified (length {size}):".format({"size": output_text.length()})).color(Color.LIGHT_SLATE_GRAY).bold().space().msg(json).color(Color.SLATE_GRAY)
|
||||
debug_msg_post.debug()
|
||||
|
||||
# Send the request.
|
||||
http.request(webhook_url, header, HTTPClient.METHOD_POST, json)
|
||||
@@ -1 +0,0 @@
|
||||
uid://byj005gxsloio
|
||||
@@ -1,65 +0,0 @@
|
||||
class_name SlackLoggieMsgChannel extends LoggieMsgChannel
|
||||
|
||||
var debug_domain = "_d_loggie_slack"
|
||||
var debug_enabled = false
|
||||
|
||||
func _init() -> void:
|
||||
self.ID = "slack"
|
||||
self.preprocess_flags = 0 # For this type of channel, this will be applied dynamically by Loggie after it loads LoggieSettings.
|
||||
|
||||
func send(msg : LoggieMsg, msg_type : LoggieEnums.MsgType):
|
||||
# Validate variables.
|
||||
var loggie = msg.get_logger()
|
||||
if loggie == null:
|
||||
push_error("Attempt to send a message that's coming from an invalid logger.")
|
||||
return
|
||||
|
||||
# Wait until loggie is inside tree so that we can use add_child(http) on it without errors.
|
||||
if !loggie.is_inside_tree():
|
||||
loggie.tree_entered.connect(func():
|
||||
send(msg, msg_type)
|
||||
, CONNECT_ONE_SHOT)
|
||||
return
|
||||
|
||||
var webhook = loggie.settings.slack_webhook_url_live if loggie.is_in_production() else loggie.settings.slack_webhook_url_dev
|
||||
if webhook == null or (webhook is String and webhook.is_empty()):
|
||||
push_error("Attempt to send a message to the Slack channel with an invalid webhook.")
|
||||
return
|
||||
|
||||
# Enable debug messages if configured.
|
||||
loggie.set_domain_enabled(debug_domain, debug_enabled)
|
||||
|
||||
# Create a new HTTPRequest POST request that will be sent to Slack and add it into the scenetree.
|
||||
var http = HTTPRequest.new()
|
||||
loggie.add_child(http)
|
||||
|
||||
# When the request is completed, destroy it.
|
||||
http.request_completed.connect(func(result, response_code, headers, body):
|
||||
var debug_msg = loggie.msg("HTTP Request Completed:").color(Color.ORANGE).header().domain(debug_domain)
|
||||
debug_msg.nl().msg("Result:").color(Color.ORANGE).bold().space().msg(result).nl()
|
||||
debug_msg.msg("Response Code:").color(Color.ORANGE).bold().space().msg(response_code).nl()
|
||||
debug_msg.msg("Headers:").color(Color.ORANGE).bold().space().msg(headers).nl()
|
||||
debug_msg.msg("Body:").color(Color.ORANGE).bold().space().msg(body)
|
||||
debug_msg.debug()
|
||||
|
||||
## Inform the user about a received non-success response code.
|
||||
if response_code < 200 or response_code > 299:
|
||||
loggie.msg("Slack responded with a non-success code: ").bold().msg(response_code, " - This is an indicator that something about the message you tried to send to Slack does not comply with their request body standards (e.g. content is too long, invalid format, etc.)").channel("terminal").warn()
|
||||
|
||||
http.queue_free()
|
||||
)
|
||||
|
||||
# Convert the [LoggieMsg]'s contents into markdown and post that to the target webhook url.
|
||||
var md_text = LoggieTools.convert_string_to_format_mode(msg.last_preprocess_result, LoggieEnums.MsgFormatMode.PLAIN)
|
||||
var json = JSON.stringify({"text": md_text})
|
||||
var header = ["Content-Type: application/json"]
|
||||
|
||||
# Construct debug message.
|
||||
if debug_enabled:
|
||||
var debug_msg_post = loggie.msg("Sending POST Request:").color(Color.ORANGE).header().domain(debug_domain).nl()
|
||||
debug_msg_post.msg("Preprocessed message:").color(Color.ORANGE).bold().space().msg(msg.last_preprocess_result).nl()
|
||||
debug_msg_post.msg("JSON stringified:").color(Color.ORANGE).bold().space().msg(json)
|
||||
debug_msg_post.debug()
|
||||
|
||||
# Send the request.
|
||||
http.request(webhook, header, HTTPClient.METHOD_POST, json)
|
||||
@@ -1 +0,0 @@
|
||||
uid://dj1jdugf244ho
|
||||
@@ -1,21 +0,0 @@
|
||||
class_name TerminalLoggieMsgChannel extends LoggieMsgChannel
|
||||
|
||||
func _init() -> void:
|
||||
self.ID = "terminal"
|
||||
self.preprocess_flags = 0 # For this type of channel, this will be applied dynamically by Loggie after it loads LoggieSettings.
|
||||
|
||||
func send(msg : LoggieMsg, msg_type : LoggieEnums.MsgType):
|
||||
var loggie = msg.get_logger()
|
||||
var text = LoggieTools.convert_string_to_format_mode(msg.last_preprocess_result, loggie.settings.msg_format_mode)
|
||||
|
||||
match loggie.settings.msg_format_mode:
|
||||
LoggieEnums.MsgFormatMode.ANSI, LoggieEnums.MsgFormatMode.BBCODE:
|
||||
print_rich(text)
|
||||
LoggieEnums.MsgFormatMode.PLAIN, _:
|
||||
print(text)
|
||||
|
||||
# Dump a non-preprocessed terminal-ready version of the message in additional ways if that has been configured.
|
||||
if msg_type == LoggieEnums.MsgType.ERROR and loggie.settings.print_errors_to_console:
|
||||
push_error(LoggieTools.convert_string_to_format_mode(msg.string(), LoggieEnums.MsgFormatMode.PLAIN))
|
||||
if msg_type == LoggieEnums.MsgType.WARN and loggie.settings.print_warnings_to_console:
|
||||
push_warning(LoggieTools.convert_string_to_format_mode(msg.string(), LoggieEnums.MsgFormatMode.PLAIN))
|
||||
@@ -1 +0,0 @@
|
||||
uid://cd82p0vffosco
|
||||
@@ -1,52 +0,0 @@
|
||||
class_name CustomLoggieSettings extends LoggieSettings
|
||||
|
||||
func load():
|
||||
# Omit settings from here to have them use their default value instead.
|
||||
# Otherwise, directly set the value of the setting to your liking.
|
||||
# Any variable in [LoggieSettings] except `project_settings` is a valid target to alter.
|
||||
# You could also have them loaded here in some custom way, for example, from a .json or .ini file.
|
||||
# See the documentation of the [LoggieSettings] class to see all available options and their descriptions.
|
||||
# This template doesn't contain *all* the available options.
|
||||
|
||||
# Example: Customize Loggie preferences.
|
||||
self.update_check_mode = LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_UPDATER_WINDOW
|
||||
self.msg_format_mode = LoggieEnums.MsgFormatMode.BBCODE
|
||||
self.log_level = LoggieEnums.LogLevel.INFO
|
||||
self.show_loggie_specs = LoggieEnums.ShowLoggieSpecsMode.ESSENTIAL
|
||||
self.show_system_specs = true
|
||||
self.enforce_optimal_settings_in_release_build = true
|
||||
|
||||
# Example: Customize logging settings.
|
||||
self.print_errors_to_console = true
|
||||
self.print_warnings_to_console = true
|
||||
self.nameless_class_name_proxy = LoggieEnums.NamelessClassExtensionNameProxy.BASE_TYPE
|
||||
self.timestamps_use_utc = true
|
||||
|
||||
# Example: Customize formats.
|
||||
self.format_info_msg = "{msg}"
|
||||
self.format_notice_msg = "[b][color=cyan][NOTICE]:[/color][/b] {msg}"
|
||||
self.format_warning_msg = "[b][color=orange][WARN]:[/color][/b] {msg}"
|
||||
self.format_error_msg = "[b][color=red][ERROR]:[/color][/b] {msg}"
|
||||
self.format_debug_msg = "[b][color=pink][DEBUG]:[/color][/b] {msg}"
|
||||
|
||||
# Example: Customize boxes.
|
||||
self.h_separator_symbol = "-"
|
||||
self.box_characters_mode = LoggieEnums.BoxCharactersMode.COMPATIBLE
|
||||
|
||||
self.box_symbols_compatible = {
|
||||
"top_left" : "-",
|
||||
"top_right" : "-",
|
||||
"bottom_left" : "-",
|
||||
"bottom_right" : "-",
|
||||
"h_line" : "-",
|
||||
"v_line" : ":",
|
||||
}
|
||||
|
||||
self.box_symbols_pretty = {
|
||||
"top_left" : "┌",
|
||||
"top_right" : "┐",
|
||||
"bottom_left" : "└",
|
||||
"bottom_right" : "┘",
|
||||
"h_line" : "─",
|
||||
"v_line" : "│",
|
||||
}
|
||||
@@ -1,310 +0,0 @@
|
||||
@tool
|
||||
|
||||
## Loggie is a basic logging utility for those who need common minor improvements and helpers around the basic [method print], [method print_rich]
|
||||
## and other default Godot printing functions. Loggie creates instances of [LoggieMsg], which are a wrapper around a string that needs to manipulated,
|
||||
## then uses them to properly format, arrange and present them in the console and .log files. Loggie uses the default Godot logging mechanism under the hood.
|
||||
extends Node
|
||||
|
||||
## The current version of Loggie.
|
||||
## Needs to be updated manually when changing the version.
|
||||
var version : LoggieVersion = LoggieVersion.new(3,0)
|
||||
|
||||
## Emitted any time Loggie attempts to log a message.
|
||||
## Useful for capturing the messages that pass through Loggie.
|
||||
## [br][param msg] is the message Loggie attempted to log (before any preprocessing).
|
||||
## [br][param msg_string] is a shortcut providing the string content of that message.
|
||||
## [br][param result] describes the final result of the attempt to log that message.
|
||||
signal log_attempted(msg : LoggieMsg, msg_string : String, result : LoggieEnums.LogAttemptResult)
|
||||
|
||||
## A reference to the settings of this Loggie. Read more about [LoggieSettings].
|
||||
var settings : LoggieSettings
|
||||
|
||||
## Holds a mapping between all registered domains (string keys) and bool values representing whether
|
||||
## those domains are currently enabled. Enable domains with [method set_domain_enabled].
|
||||
## You can then place [LoggieMsg] messages into a domain by calling [method LoggieMsg.domain].
|
||||
## Messages belonging to a disabled domain will never be outputted.
|
||||
var domains : Dictionary = {}
|
||||
|
||||
## Holds a mapping between script paths and the names of the classes defined in those scripts.
|
||||
var class_names : Dictionary = {}
|
||||
|
||||
## Holds a mapping between channel IDs (string) and the
|
||||
## [LoggieMsgChannel] objects those IDs are representing.
|
||||
var available_channels = {}
|
||||
|
||||
## Stores a reference to a [LoggieVersionManager] that will be used to manage the
|
||||
## version of this instance.
|
||||
var version_manager : LoggieVersionManager = LoggieVersionManager.new()
|
||||
|
||||
## Stores a reference to each [LoggiePreset] that was created.
|
||||
## The key is the ID (string) of the preset, and the value is the [LoggieMsg] that was saved as a preset.
|
||||
var presets : Dictionary = {}
|
||||
|
||||
func _init() -> void:
|
||||
# Connect the version manager to this logger.
|
||||
version_manager.connect_logger(self)
|
||||
|
||||
# Load and initialize the settings.
|
||||
var uses_original_settings_file = true
|
||||
var default_settings_path = get_script().get_path().get_base_dir().path_join("loggie_settings.gd")
|
||||
var custom_settings_path = get_script().get_path().get_base_dir().path_join("custom_settings.gd")
|
||||
|
||||
if self.settings == null:
|
||||
if custom_settings_path != null and custom_settings_path != "" and ResourceLoader.exists(custom_settings_path):
|
||||
var loaded_successfully = load_settings_from_path(custom_settings_path)
|
||||
if loaded_successfully:
|
||||
uses_original_settings_file = false
|
||||
|
||||
if uses_original_settings_file:
|
||||
var _settings = ResourceLoader.load(default_settings_path)
|
||||
if _settings != null:
|
||||
self.settings = _settings.new()
|
||||
self.settings.load()
|
||||
else:
|
||||
push_error("Loggie loaded neither a custom nor a default settings file. This will break the plugin. Make sure that a valid loggie_settings.gd is in the same directory where loggie.gd is.")
|
||||
return
|
||||
|
||||
# Enforce certain settings if configured to do so.
|
||||
if self.settings.enforce_optimal_settings_in_release_build == true and is_in_production():
|
||||
apply_production_optimal_settings()
|
||||
|
||||
# Set the default custom string converter.
|
||||
self.settings.custom_string_converter = LoggieTools.convert_to_string
|
||||
|
||||
# Install all the built-in channels.
|
||||
var terminal_channel : TerminalLoggieMsgChannel = load("res://addons/loggie/channels/terminal.gd").new()
|
||||
terminal_channel.preprocess_flags = self.settings.preprocess_flags_terminal_channel
|
||||
add_channel(terminal_channel)
|
||||
var discord_channel : DiscordLoggieMsgChannel = load("res://addons/loggie/channels/discord.gd").new()
|
||||
discord_channel.preprocess_flags = self.settings.preprocess_flags_discord_channel
|
||||
add_channel(discord_channel)
|
||||
var slack_channel : SlackLoggieMsgChannel = load("res://addons/loggie/channels/slack.gd").new()
|
||||
slack_channel.preprocess_flags = self.settings.preprocess_flags_slack_channel
|
||||
add_channel(slack_channel)
|
||||
|
||||
# Already cache the name of the singleton found at loggie's script path.
|
||||
class_names[self.get_script().resource_path] = LoggieSettings.loggie_singleton_name
|
||||
|
||||
# Prepopulate class data from ProjectSettings to avoid needing to read files.
|
||||
if OS.has_feature("debug"):
|
||||
for class_data: Dictionary in ProjectSettings.get_global_class_list():
|
||||
class_names[class_data.path] = class_data.class
|
||||
|
||||
for autoload_setting: String in ProjectSettings.get_property_list().map(func(prop): return prop.name).filter(func(prop): return prop.begins_with("autoload/") and ProjectSettings.has_setting(prop)):
|
||||
var autoload_class: String = autoload_setting.trim_prefix("autoload/")
|
||||
var class_path: String = ProjectSettings.get_setting(autoload_setting)
|
||||
class_path = class_path.trim_prefix("*")
|
||||
if not class_names.has(class_path):
|
||||
class_names[class_path] = autoload_class
|
||||
|
||||
# And don't proceed further if we're in Editor mode, since we don't need to show loggie boot messages in that case.
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
# Print the Loggie boot messages.
|
||||
if self.settings.show_loggie_specs != LoggieEnums.ShowLoggieSpecsMode.DISABLED:
|
||||
msg("👀 Loggie {version}{isproxy} booted.".format({
|
||||
"version" : self.version_manager.version,
|
||||
"isproxy" : " (proxy for {original})".format({"original": self.version_manager.version.proxy_for}) if self.version_manager.version.proxy_for != null else ""
|
||||
})).color(Color.ORANGE).header().nl().info()
|
||||
var loggie_specs_msg = LoggieSystemSpecsMsg.new().use_logger(self)
|
||||
loggie_specs_msg.add(msg("|\t Using Custom Settings File: ").bold(), !uses_original_settings_file).nl().add("|\t ").hseparator(35).nl()
|
||||
|
||||
match self.settings.show_loggie_specs:
|
||||
LoggieEnums.ShowLoggieSpecsMode.ESSENTIAL:
|
||||
loggie_specs_msg.embed_essential_logger_specs()
|
||||
LoggieEnums.ShowLoggieSpecsMode.ADVANCED:
|
||||
loggie_specs_msg.embed_advanced_logger_specs()
|
||||
|
||||
loggie_specs_msg.preprocessed(false).info()
|
||||
|
||||
if self.settings.show_system_specs:
|
||||
var system_specs_msg = LoggieSystemSpecsMsg.new().use_logger(self)
|
||||
system_specs_msg.embed_specs().preprocessed(false).info()
|
||||
|
||||
## Attempts to instantiate and use a LoggieSettings object from the script at the given [param path].
|
||||
## Returns true if successful, otherwise false and prints an error.
|
||||
func load_settings_from_path(path : String) -> bool:
|
||||
var settings_resource = ResourceLoader.load(path)
|
||||
var settings_instance
|
||||
|
||||
if settings_resource != null:
|
||||
settings_instance = settings_resource.new()
|
||||
|
||||
if (settings_instance is LoggieSettings):
|
||||
self.settings = settings_instance
|
||||
self.settings.load()
|
||||
return true
|
||||
else:
|
||||
push_error("Unable to instantiate a LoggieSettings object from the script at path {path}. Check that loggie.gd -> custom_settings_path is pointing to a valid .gd script that contains the class definition of a class that either extends LoggieSettings, or is LoggieSettings.".format({"path": path}))
|
||||
return false
|
||||
|
||||
## Checks if Loggie is running in production (release) mode of the game.
|
||||
## While it is, every [LoggieMsg] will have plain output.
|
||||
## Uses a sensible default check for most projects, but
|
||||
## you can rewrite this function to your needs if necessary.
|
||||
## TODO: Port this out of Loggie into LoggieSettings so users can override it easier.
|
||||
func is_in_production() -> bool:
|
||||
return OS.has_feature("release")
|
||||
|
||||
## Returns a custom list of channels that messages from the given [param domain_name] will be sent to.
|
||||
## This list can be set through the [method set_domain_enabled] method.
|
||||
## If the list is empty, default channels will be used.
|
||||
func get_domain_custom_target_channels(domain_name : String) -> Array:
|
||||
if domains.has(domain_name):
|
||||
return domains[domain_name].custom_target_channels
|
||||
return []
|
||||
|
||||
## Sets whether the domain with the given name is enabled.
|
||||
## If [param custom_target_channels] is provided, it will be used as the list of channels that messages from the given domain will be sent to.
|
||||
## It can be provided as a string (if only one channel is used), or an array of strings (if multiple channels are used).
|
||||
## Otherwise, the default channels will be used.
|
||||
func set_domain_enabled(domain_name : String, enabled : bool, custom_target_channels : Variant = []) -> void:
|
||||
var pruned_target_channels = []
|
||||
|
||||
if custom_target_channels is String:
|
||||
custom_target_channels = [custom_target_channels]
|
||||
|
||||
# Prune the array to ensure only string content is used.
|
||||
if custom_target_channels is Array:
|
||||
for entry in custom_target_channels:
|
||||
if entry is String or entry is StringName:
|
||||
pruned_target_channels.push_back(entry)
|
||||
else:
|
||||
push_error("Attempt to set a custom target channel for domain {domain_name} with an invalid value: {custom_target_channels}. The value must be a string or an array of strings. Default channels will be used instead.".format({
|
||||
"domain_name": domain_name,
|
||||
"custom_target_channels": custom_target_channels
|
||||
}))
|
||||
|
||||
domains[domain_name] = {"enabled": enabled, "custom_target_channels": pruned_target_channels}
|
||||
|
||||
## Checks whether the domain with the given name is enabled.
|
||||
## The domain name "" (empty string) is the default one for all newly created messages,
|
||||
## and is designed to always be enabled.
|
||||
func is_domain_enabled(domain_name : String) -> bool:
|
||||
if domain_name == "":
|
||||
return true
|
||||
|
||||
if domains.has(domain_name) and domains[domain_name].enabled == true:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
## Returns an available channel with the given ID (if one exists), otherwise null.
|
||||
func get_channel(channel_id : String) -> LoggieMsgChannel:
|
||||
if available_channels.has(channel_id):
|
||||
return available_channels[channel_id]
|
||||
return null
|
||||
|
||||
## Adds a new channel for sending messages to.
|
||||
## Multiple channels with the same ID can not be added, so make sure your ID
|
||||
## does not clash with one of the existing channels' IDs, which are:
|
||||
## [param terminal], [param discord], [param slack].
|
||||
func add_channel(channel : LoggieMsgChannel):
|
||||
if not available_channels.has(channel.ID):
|
||||
available_channels[channel.ID] = channel
|
||||
else:
|
||||
push_error("Attempt to add a channel with ID {ID} failed, a channel with that ID already exists in Loggie.".format({
|
||||
"ID": channel.ID
|
||||
}))
|
||||
|
||||
## Gets a [LoggiePreset] message with the given ID, or creates one if it doesn't exist and returns it.
|
||||
## The ID cannot be an empty string.
|
||||
func preset(id : String) -> LoggiePreset:
|
||||
if id.is_empty():
|
||||
push_error("Attempt to create or get a LoggiePreset with an empty ID - not allowed.")
|
||||
return null
|
||||
|
||||
if presets.has(id):
|
||||
return presets[id]
|
||||
else:
|
||||
var newPreset : LoggiePreset = LoggiePreset.new()
|
||||
newPreset.use_logger(self)
|
||||
presets[id] = newPreset
|
||||
return newPreset
|
||||
|
||||
## Creates a new [LoggieMsg] out of the given [param msg] and extra arguments (by converting them to strings and concatenating them to the msg).
|
||||
## You may continue to modify the [LoggieMsg] with additional functions from that class, then when you are ready to output it, use methods like:
|
||||
## [method LoggieMsg.info], [method LoggieMsg.warn], etc.
|
||||
func msg(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
var loggieMsg = LoggieMsg.new(message, arg1, arg2, arg3, arg4, arg5)
|
||||
loggieMsg.use_logger(self)
|
||||
return loggieMsg
|
||||
|
||||
## A shortcut method that instantly creates a [LoggieMsg] with the given arguments and outputs it at the info level.
|
||||
## Can be used when you have no intention of customizing a LoggieMsg in any way using helper methods.
|
||||
## For customization, use [method msg] instead.
|
||||
func info(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
return msg(message, arg1, arg2, arg3, arg4, arg5).info()
|
||||
|
||||
## A shortcut method that instantly creates a [LoggieMsg] with the given arguments and outputs it at the warn level.
|
||||
## Can be used when you have no intention of customizing a LoggieMsg in any way using helper methods.
|
||||
## For customization, use [method msg] instead.
|
||||
func warn(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
return msg(message, arg1, arg2, arg3, arg4, arg5).warn()
|
||||
|
||||
## A shortcut method that instantly creates a [LoggieMsg] with the given arguments and outputs it at the error level.
|
||||
## Can be used when you have no intention of customizing a LoggieMsg in any way using helper methods.
|
||||
## For customization, use [method msg] instead.
|
||||
func error(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
return msg(message, arg1, arg2, arg3, arg4, arg5).error()
|
||||
|
||||
## A shortcut method that instantly creates a [LoggieMsg] with the given arguments and outputs it at the debug level.
|
||||
## Can be used when you have no intention of customizing a LoggieMsg in any way using helper methods.
|
||||
## For customization, use [method msg] instead.
|
||||
func debug(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
return msg(message, arg1, arg2, arg3, arg4, arg5).debug()
|
||||
|
||||
## A shortcut method that instantly creates a [LoggieMsg] with the given arguments and outputs it at the notice level.
|
||||
## Can be used when you have no intention of customizing a LoggieMsg in any way using helper methods.
|
||||
## For customization, use [method msg] instead.
|
||||
func notice(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
return msg(message, arg1, arg2, arg3, arg4, arg5).notice()
|
||||
|
||||
## Returns the path to the directory from which within this script is running.
|
||||
func get_directory_path() -> String:
|
||||
return get_script().resource_path.get_base_dir()
|
||||
|
||||
## Returns a [LoggieMsg] that comes inserted with stylized content describing the stack trace obtained via [method get_stack].
|
||||
## This function only works in debug builds, and on the main thread, because it uses [method get_stack].
|
||||
## Read more about why in that function's documentation.
|
||||
func stack() -> LoggieMsg:
|
||||
if !OS.has_feature("debug"):
|
||||
return msg()
|
||||
|
||||
const FALLBACK_TXT_TO_FORMAT = "{index}: {fn_name}:{line} (in {source_path})"
|
||||
var stack = get_stack()
|
||||
var stack_msg = msg()
|
||||
|
||||
var text_to_format = settings.format_stacktrace_entry if is_instance_valid(settings) else FALLBACK_TXT_TO_FORMAT
|
||||
|
||||
stack.reverse()
|
||||
|
||||
for index in stack.size():
|
||||
var file_name = stack[index].source.get_file().get_basename()
|
||||
|
||||
if settings.skipped_filenames_in_stack_trace.has(file_name):
|
||||
continue
|
||||
|
||||
var entry_msg = LoggieMsg.new()
|
||||
entry_msg.add(text_to_format.format({
|
||||
"index": index,
|
||||
"source_path": stack[index].source,
|
||||
"fn_name": stack[index].function,
|
||||
"line": stack[index].line
|
||||
}))
|
||||
|
||||
if index == 0 or index < stack.size():
|
||||
entry_msg.prefix("\n ")
|
||||
entry_msg.endseg()
|
||||
|
||||
stack_msg.add(entry_msg)
|
||||
|
||||
return stack_msg
|
||||
|
||||
## Changes the values of certain members of [member settings] to values which are optimal for
|
||||
## use in production builds. Used when [member LoggieSettings.enforce_optimal_settings_in_release_build]
|
||||
## needs to be enforced.
|
||||
func apply_production_optimal_settings():
|
||||
self.settings.msg_format_mode = LoggieEnums.MsgFormatMode.PLAIN
|
||||
self.settings.box_characters_mode = LoggieEnums.BoxCharactersMode.COMPATIBLE
|
||||
@@ -1 +0,0 @@
|
||||
uid://bmh2auo806k0k
|
||||
@@ -1,566 +0,0 @@
|
||||
@tool
|
||||
|
||||
## LoggieMsg represents a mutable object that holds an array of strings ([member content]) [i](referred to as 'content segments')[/i], and
|
||||
## a bunch of helper methods that make it easy to manipulate these segments and chain together additions and changes to them.
|
||||
## [br][br]For example:
|
||||
## [codeblock]
|
||||
### Prints: "Hello world!" at the INFO debug level.
|
||||
##var msg = LoggieMsg.new("Hello world").color(Color.RED).suffix("!").info()
|
||||
##[/codeblock]
|
||||
## [br] You can also use [method Loggie.msg] to quickly construct a message.
|
||||
## [br] Example of usage:
|
||||
## [codeblock]Loggie.msg("Hello world").color(Color("#ffffff")).suffix("!").info()[/codeblock]
|
||||
class_name LoggieMsg extends RefCounted
|
||||
|
||||
## The full content of this message. By calling various helper methods in this class, this content is further altered.
|
||||
## The content is an array of strings which represents segments of the message which are ultimately appended together
|
||||
## to form the final message. You can start a new segment by calling [method msg] on this class.
|
||||
## You can then output the whole message with methods like [method info], [method debug], etc.
|
||||
var content : Array = [""]
|
||||
|
||||
## The segment of [member content] that is currently being edited.
|
||||
var current_segment_index : int = 0
|
||||
|
||||
## The (key string) domain this message belongs to.
|
||||
## "" is the default domain which is always enabled.
|
||||
## If this message attempts to be outputted, but belongs to a disabled domain, it will not be outputted.
|
||||
## You can change which domains are enabled in Loggie at any time with [Loggie.set_domain_enabled].
|
||||
## This is useful for creating blocks of debugging output that you can simply turn off/on with a boolean when you actually need them.
|
||||
var domain_name : String = ""
|
||||
|
||||
## Stores a reference to the logger that generated this message, from which we need to read settings and other data.
|
||||
## This variable should be set with [method use_logger] before an attempt is made to log this message out.
|
||||
var _logger : Variant
|
||||
|
||||
## Stores an array of IDs of all channels this message should be sent to when being outputted.
|
||||
var used_channels : Array = ["terminal"]
|
||||
|
||||
## Whether this message should be preprocessed and modified during [method output].
|
||||
var preprocess : bool = true
|
||||
|
||||
## Usually, the [LoggieMsgChannel] this message gets outputted on sets the preprocessing steps this message should use.
|
||||
## But sometimes we may want to use a specific set of preprocessing steps on this message,
|
||||
## overriding the channel's set of rules.
|
||||
## In that case, set this variable to the value of the [LoggieEnums.PreprocessStep] flags you want this message to use with
|
||||
## [method preprocess].
|
||||
var custom_preprocess_flags : int = -1
|
||||
|
||||
## Stores the string which was obtained the last time the [method get_preprocessed] was called on this message.
|
||||
## You need to call it at least once for this to have any results.
|
||||
var last_preprocess_result : String = ""
|
||||
|
||||
## Stores the integer value of a [enum LoggieEnums.LogLevel], matching the log level
|
||||
## this message was most recently outputted at. If value is `-1`, the message was never
|
||||
## outputted yet.
|
||||
var last_outputted_at_log_level : int = -1
|
||||
|
||||
## Whether this message should append the stack trace during preprocessing.
|
||||
var appends_stack : bool = false
|
||||
|
||||
## Controls whether the [enum LoggieEnums.MsgType] of this message, during [method output],
|
||||
## will be decided dynamically, based on the [enum LoggieEnums.LogLevel] the message is being output at.
|
||||
## If disabled, the [member strict_type] will be used instead.
|
||||
var dynamic_type : bool = true
|
||||
|
||||
## The [LoggieEnums.MsgType] this message will be considered typed as, if [member dynamic_type] is set
|
||||
## to [param false].
|
||||
var strict_type : LoggieEnums.MsgType = LoggieEnums.MsgType.INFO
|
||||
|
||||
## The environment in which this message should be outputted based on the context the output is being requested from.
|
||||
## (e.g. only in-engine (during tool scripts), or only ingame, or both).
|
||||
var environment_mode : LoggieEnums.MsgEnvironment = LoggieEnums.MsgEnvironment.BOTH
|
||||
|
||||
## If set to true, the [signal Loggie.log_attempted] signal will not be emitted when this message attempts to be outputted.
|
||||
var dont_emit_log_attempted_signal : bool = false
|
||||
|
||||
func _init(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> void:
|
||||
var args = [message, arg1, arg2, arg3, arg4, arg5]
|
||||
self.content[0] = LoggieTools.concatenate_args(args)
|
||||
self.set_meta("initial_args", args)
|
||||
|
||||
## Returns a reference to the logger object that created this message.
|
||||
func get_logger() -> Variant:
|
||||
return self._logger
|
||||
|
||||
## Sets this message to use the given [param logger] as the logger from which it will be reading
|
||||
## settings. The given logger should be of class [Loggie] or an extension of it.
|
||||
func use_logger(logger_to_use : Variant) -> LoggieMsg:
|
||||
self._logger = logger_to_use
|
||||
self.used_channels = self._logger.settings.default_channels
|
||||
|
||||
# Now that a logger is connected, we can re-format the first segment
|
||||
# using that Logger's converter function if it has a custom one defined.
|
||||
if self.has_meta("initial_args"):
|
||||
var initial_args = self.get_meta("initial_args")
|
||||
if initial_args is Array:
|
||||
var converter_fn = self._logger.settings.custom_string_converter if is_instance_valid(self._logger) and is_instance_valid(self._logger.settings) else null
|
||||
if converter_fn != LoggieTools.convert_to_string:
|
||||
self.content[0] = LoggieTools.concatenate_args(initial_args, converter_fn)
|
||||
self.remove_meta("initial_args") # Clean up.
|
||||
|
||||
return self
|
||||
|
||||
## Sets the list of channels this message should be sent to when outputted.
|
||||
## [param channels] should either be provided as a single channel ID (String), or
|
||||
## as an array of channel IDs (Array of strings).
|
||||
func channel(channels : Variant) -> LoggieMsg:
|
||||
if typeof(channels) != TYPE_ARRAY and typeof(channels) != TYPE_PACKED_STRING_ARRAY:
|
||||
channels = [str(channels)]
|
||||
self.used_channels = channels
|
||||
return self
|
||||
|
||||
## Returns a processed version of the content of this message, which has modifications applied to
|
||||
## it based on the requested [param type] and other settings defined by the provided preprocess [param flags].
|
||||
## Available preprocess flags are found in [enum LoggieEnums.PreprocessStep].
|
||||
func get_preprocessed(flags : int, _level : LoggieEnums.LogLevel, type : LoggieEnums.MsgType) -> String:
|
||||
var loggie = get_logger()
|
||||
var message = self.string()
|
||||
|
||||
match type:
|
||||
LoggieEnums.MsgType.ERROR:
|
||||
message = loggie.settings.format_error_msg.format({"msg": message})
|
||||
LoggieEnums.MsgType.WARN:
|
||||
message = loggie.settings.format_warning_msg.format({"msg": message})
|
||||
LoggieEnums.MsgType.NOTICE:
|
||||
message = loggie.settings.format_notice_msg.format({"msg": message})
|
||||
LoggieEnums.MsgType.INFO:
|
||||
message = loggie.settings.format_info_msg.format({"msg": message})
|
||||
LoggieEnums.MsgType.DEBUG:
|
||||
message = loggie.settings.format_debug_msg.format({"msg": message})
|
||||
|
||||
if (flags & LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME != 0) and !self.domain_name.is_empty():
|
||||
message = _apply_format_domain(message)
|
||||
|
||||
if (flags & LoggieEnums.PreprocessStep.APPEND_CLASS_NAME != 0) and OS.has_feature("debug"):
|
||||
message = _apply_format_class_name(message)
|
||||
|
||||
if (flags & LoggieEnums.PreprocessStep.APPEND_TIMESTAMPS != 0):
|
||||
message = _apply_format_timestamp(message)
|
||||
|
||||
if self.appends_stack or (loggie.settings.debug_msgs_print_stack_trace and type == LoggieEnums.MsgType.DEBUG):
|
||||
message = _apply_format_stack(message)
|
||||
|
||||
return message
|
||||
|
||||
## Outputs the given string [param message] at the given output [param level] to the standard output using either [method print_rich] or [method print].
|
||||
## The domain from which the message is considered to be coming can be provided via [param target_domain].
|
||||
## The message type can be provided via [param msg_type], but it will be ignored if [member dynamic_type] is false,
|
||||
## in which case, the [member strict_type] will be used.
|
||||
## It also does a number of changes to the given [param msg] based on various Loggie settings.
|
||||
## Designed to be called internally. You should consider using [method info], [method error], [method warn], [method notice], [method debug] instead.
|
||||
func output(level : LoggieEnums.LogLevel, msg_type : LoggieEnums.MsgType = LoggieEnums.MsgType.INFO) -> void:
|
||||
var loggie = get_logger()
|
||||
var message = self.string()
|
||||
var target_domain = self.domain_name
|
||||
var target_channels = self.used_channels
|
||||
|
||||
if loggie == null:
|
||||
push_error("Attempt to log output with an invalid _logger. Make sure to call LoggieMsg.use_logger to set the appropriate logger before working with the message.")
|
||||
return
|
||||
|
||||
if loggie.settings == null:
|
||||
push_error("Attempt to use a _logger with invalid settings to print: {msg}".format({"msg": message}))
|
||||
return
|
||||
|
||||
# We don't output the message if the settings dictate that messages of that level shouldn't be outputted.
|
||||
if level > loggie.settings.log_level:
|
||||
_emit_log_attempted_signal(LoggieEnums.LogAttemptResult.LOG_LEVEL_INSUFFICIENT)
|
||||
return
|
||||
|
||||
# We don't output the message if the domain from which it comes is not enabled.
|
||||
if not loggie.is_domain_enabled(target_domain):
|
||||
_emit_log_attempted_signal(LoggieEnums.LogAttemptResult.DOMAIN_DISABLED)
|
||||
return
|
||||
|
||||
# We don't output the message if the environment the output is being requested from is not compatible with
|
||||
# the environment this message is configured to be outputted in.
|
||||
match environment_mode:
|
||||
LoggieEnums.MsgEnvironment.ENGINE:
|
||||
if not Engine.is_editor_hint():
|
||||
_emit_log_attempted_signal(LoggieEnums.LogAttemptResult.WRONG_ENVIRONMENT)
|
||||
return
|
||||
LoggieEnums.MsgEnvironment.RUNTIME:
|
||||
if Engine.is_editor_hint():
|
||||
_emit_log_attempted_signal(LoggieEnums.LogAttemptResult.WRONG_ENVIRONMENT)
|
||||
return
|
||||
|
||||
# Enforce the strict type of this message if it is configured not to allow dynamic type.
|
||||
if !dynamic_type:
|
||||
msg_type = strict_type
|
||||
|
||||
# Send the message on all configured channels.
|
||||
var custom_target_channels = loggie.get_domain_custom_target_channels(target_domain)
|
||||
if custom_target_channels.size() > 0:
|
||||
target_channels = custom_target_channels
|
||||
|
||||
for channel_id : String in target_channels:
|
||||
var channel : LoggieMsgChannel = loggie.get_channel(channel_id)
|
||||
|
||||
if channel == null:
|
||||
_emit_log_attempted_signal(LoggieEnums.LogAttemptResult.INVALID_CHANNEL)
|
||||
continue
|
||||
|
||||
# Preprocessing Stage:
|
||||
# Apply full preprocessing only if explicitly enabled.
|
||||
# Otherwise, simply concatenate together all the [member content].
|
||||
if self.preprocess:
|
||||
var flags = self.custom_preprocess_flags if self.custom_preprocess_flags != -1 else channel.preprocess_flags
|
||||
self.last_preprocess_result = get_preprocessed(flags, level, msg_type)
|
||||
else:
|
||||
self.last_preprocess_result = self.string()
|
||||
|
||||
self.last_outputted_at_log_level = int(level)
|
||||
channel.send(self, msg_type)
|
||||
|
||||
# Emit signal deferred so if this is called from a thread, it doesn't cry about it.
|
||||
_emit_log_attempted_signal(LoggieEnums.LogAttemptResult.SUCCESS, true)
|
||||
|
||||
## Outputs this message from Loggie as an Error type message.
|
||||
## The [Loggie.settings.log_level] must be equal to or higher to the ERROR level for this to work.
|
||||
func error() -> LoggieMsg:
|
||||
output(LoggieEnums.LogLevel.ERROR, LoggieEnums.MsgType.ERROR)
|
||||
return self
|
||||
|
||||
## Outputs this message from Loggie as an Warning type message.
|
||||
## The [Loggie.settings.log_level] must be equal to or higher to the WARN level for this to work.
|
||||
func warn() -> LoggieMsg:
|
||||
output(LoggieEnums.LogLevel.WARN, LoggieEnums.MsgType.WARN)
|
||||
return self
|
||||
|
||||
## Outputs this message from Loggie as an Notice type message.
|
||||
## The [Loggie.settings.log_level] must be equal to or higher to the NOTICE level for this to work.
|
||||
func notice() -> LoggieMsg:
|
||||
output(LoggieEnums.LogLevel.NOTICE, LoggieEnums.MsgType.NOTICE)
|
||||
return self
|
||||
|
||||
## Outputs this message from Loggie as an Info type message.
|
||||
## The [Loggie.settings.log_level] must be equal to or higher to the INFO level for this to work.
|
||||
func info() -> LoggieMsg:
|
||||
output(LoggieEnums.LogLevel.INFO, LoggieEnums.MsgType.INFO)
|
||||
return self
|
||||
|
||||
## Outputs this message from Loggie as a Debug type message.
|
||||
## The [Loggie.settings.log_level] must be equal to or higher to the DEBUG level for this to work.
|
||||
func debug() -> LoggieMsg:
|
||||
output(LoggieEnums.LogLevel.DEBUG, LoggieEnums.MsgType.DEBUG)
|
||||
return self
|
||||
|
||||
## Returns the string content of this message.
|
||||
## If [param segment] is provided, it should be an integer indicating which segment of the message to return.
|
||||
## If its value is -1, all segments are concatenated together and returned.
|
||||
func string(segment : int = -1) -> String:
|
||||
if segment == -1:
|
||||
return "".join(self.content)
|
||||
else:
|
||||
if segment < self.content.size():
|
||||
return self.content[segment]
|
||||
else:
|
||||
push_error("Attempt to access a non-existent segment of a LoggieMsg. Make sure to use a valid segment index.")
|
||||
return ""
|
||||
|
||||
## Converts the current content of this message to an ANSI compatible form.
|
||||
func to_ANSI() -> LoggieMsg:
|
||||
var new_content : Array = []
|
||||
for segment in self.content:
|
||||
new_content.append(LoggieTools.rich_to_ANSI(segment))
|
||||
self.content = new_content
|
||||
return self
|
||||
|
||||
## Strips all the BBCode in the current content of this message.
|
||||
func strip_BBCode() -> LoggieMsg:
|
||||
var new_content : Array = []
|
||||
for segment in self.content:
|
||||
new_content.append(LoggieTools.remove_BBCode(segment))
|
||||
self.content = new_content
|
||||
return self
|
||||
|
||||
## Wraps the content of the current segment of this message in the given color.
|
||||
## The [param color] can be provided as a [Color], a recognized Godot color name (String, e.g. "red"), or a color hex code (String, e.g. "#ff0000").
|
||||
func color(_color : Variant) -> LoggieMsg:
|
||||
if _color is Color:
|
||||
_color = _color.to_html()
|
||||
|
||||
self.content[current_segment_index] = "[color={colorstr}]{msg}[/color]".format({
|
||||
"colorstr": _color,
|
||||
"msg": self.content[current_segment_index]
|
||||
})
|
||||
|
||||
return self
|
||||
|
||||
## Stylizes the current segment of this message to be bold.
|
||||
func bold() -> LoggieMsg:
|
||||
self.content[current_segment_index] = "[b]{msg}[/b]".format({"msg": self.content[current_segment_index]})
|
||||
return self
|
||||
|
||||
## Stylizes the current segment of this message to be italic.
|
||||
func italic() -> LoggieMsg:
|
||||
self.content[current_segment_index] = "[i]{msg}[/i]".format({"msg": self.content[current_segment_index]})
|
||||
return self
|
||||
|
||||
## Makes the current segment of this message a hyperlink to the given [param url].
|
||||
## Optionally, you can provide a [param color], as the same type of parameter that [method color] uses,
|
||||
## to colorize the link. This is a shortcut for additionally calling [method color] on the link.
|
||||
## [br][br]If the link doesn't work when rendered inside of a [RichTextLabel], you may need to connect to the [signal RichTextLabel.meta_clicked] signal,
|
||||
## and handle the opening of the link there. Read the docs of that signal for more info.
|
||||
## [br][br][WARNING]: Appending URLs to messages is potentially dangerous.
|
||||
## Please read this article for more info: https://docs.godotengine.org/en/latest/tutorials/ui/bbcode_in_richtextlabel.html#handling-user-input-safely
|
||||
func link(url : String, _color : Variant = null) -> LoggieMsg:
|
||||
self.content[current_segment_index] = "[url={url}]{msg}[/url]".format({"url": url, "msg": self.content[current_segment_index]})
|
||||
if _color:
|
||||
self.color(_color)
|
||||
return self
|
||||
|
||||
## Stylizes the current segment of this message as a header.
|
||||
func header() -> LoggieMsg:
|
||||
var loggie = get_logger()
|
||||
self.content[current_segment_index] = loggie.settings.format_header.format({"msg": self.content[current_segment_index]})
|
||||
return self
|
||||
|
||||
## Sets whether this message should append the stack trace during preprocessing.
|
||||
## If used in a different thread, it doesn't work, because it relies on [method get_stack] and
|
||||
## that method doesn't work within threads.
|
||||
func stack(enabled : bool = true) -> LoggieMsg:
|
||||
self.appends_stack = enabled
|
||||
return self
|
||||
|
||||
## Constructs a decorative box with the given horizontal padding around the current segment
|
||||
## of this message. Messages containing a box are not going to be preprocessed, so they are best
|
||||
## used only as a special header or decoration.
|
||||
func box(h_padding : int = 4) -> LoggieMsg:
|
||||
var loggie = get_logger()
|
||||
var stripped_content = LoggieTools.remove_BBCode(self.content[current_segment_index]).strip_edges(true, true)
|
||||
var content_length = stripped_content.length()
|
||||
var h_fill_length = content_length + (h_padding * 2)
|
||||
var box_character_source = loggie.settings.box_symbols_compatible if loggie.settings.box_characters_mode == LoggieEnums.BoxCharactersMode.COMPATIBLE else loggie.settings.box_symbols_pretty
|
||||
|
||||
var top_row_design = "{top_left_corner}{h_fill}{top_right_corner}".format({
|
||||
"top_left_corner" : box_character_source.top_left,
|
||||
"h_fill" : box_character_source.h_line.repeat(h_fill_length),
|
||||
"top_right_corner" : box_character_source.top_right
|
||||
})
|
||||
|
||||
var middle_row_design = "{vert_line}{padding}{content}{space_fill}{padding}{vert_line}".format({
|
||||
"vert_line" : box_character_source.v_line,
|
||||
"content" : self.content[current_segment_index],
|
||||
"padding" : " ".repeat(h_padding),
|
||||
"space_fill" : " ".repeat(h_fill_length - stripped_content.length() - h_padding*2)
|
||||
})
|
||||
|
||||
var bottom_row_design = "{bottom_left_corner}{h_fill}{bottom_right_corner}".format({
|
||||
"bottom_left_corner" : box_character_source.bottom_left,
|
||||
"h_fill" : box_character_source.h_line.repeat(h_fill_length),
|
||||
"bottom_right_corner" : box_character_source.bottom_right
|
||||
})
|
||||
|
||||
self.content[current_segment_index] = "{top_row}\n{middle_row}\n{bottom_row}\n".format({
|
||||
"top_row" : top_row_design,
|
||||
"middle_row" : middle_row_design,
|
||||
"bottom_row" : bottom_row_design
|
||||
})
|
||||
|
||||
self.preprocessed(false)
|
||||
return self
|
||||
|
||||
## Appends additional content to this message at the end of the current content and its stylings.
|
||||
## This does not create a new message segment, just appends to the current one.
|
||||
func add(message : Variant = null, arg1 : Variant = null, arg2 : Variant = null, arg3 : Variant = null, arg4 : Variant = null, arg5 : Variant = null) -> LoggieMsg:
|
||||
var converter_fn = self._logger.settings.custom_string_converter if is_instance_valid(self._logger) and is_instance_valid(self._logger.settings) else null
|
||||
self.content[current_segment_index] = self.content[current_segment_index] + LoggieTools.concatenate_args([message, arg1, arg2, arg3, arg4, arg5], converter_fn)
|
||||
return self
|
||||
|
||||
## Adds a specified amount of newlines to the end of the current segment of this message.
|
||||
func nl(amount : int = 1) -> LoggieMsg:
|
||||
self.content[current_segment_index] += "\n".repeat(amount)
|
||||
return self
|
||||
|
||||
## Adds a specified amount of spaces to the end of the current segment of this message.
|
||||
func space(amount : int = 1) -> LoggieMsg:
|
||||
self.content[current_segment_index] += " ".repeat(amount)
|
||||
return self
|
||||
|
||||
## Adds a specified amount of tabs to the end of the current segment of this message.
|
||||
func tab(amount : int = 1) -> LoggieMsg:
|
||||
self.content[current_segment_index] += "\t".repeat(amount)
|
||||
return self
|
||||
|
||||
## Sets this message to belong to the domain with the given name.
|
||||
## If it attempts to be outputted, but the domain is disabled, it won't be outputted.
|
||||
func domain(_domain_name : String) -> LoggieMsg:
|
||||
self.domain_name = _domain_name
|
||||
return self
|
||||
|
||||
## Prepends the given prefix string to the start of the message (first segment) with the provided separator.
|
||||
func prefix(str_prefix : String, separator : String = "") -> LoggieMsg:
|
||||
self.content[0] = "{prefix}{separator}{content}".format({
|
||||
"prefix" : str_prefix,
|
||||
"separator" : separator,
|
||||
"content" : self.content[0]
|
||||
})
|
||||
return self
|
||||
|
||||
## Appends the given suffix string to the end of the message (last segment) with the provided separator.
|
||||
func suffix(str_suffix : String, separator : String = "") -> LoggieMsg:
|
||||
self.content[self.content.size() - 1] = "{content}{separator}{suffix}".format({
|
||||
"suffix" : str_suffix,
|
||||
"separator" : separator,
|
||||
"content" : self.content[self.content.size() - 1]
|
||||
})
|
||||
return self
|
||||
|
||||
## Appends a horizontal separator with the given length to the current segment of this message.
|
||||
## If [param alternative_symbol] is provided, it should be a String, and it will be used as the symbol for the separator instead of the default one.
|
||||
func hseparator(size : int = 16, alternative_symbol : Variant = null) -> LoggieMsg:
|
||||
var loggie = get_logger()
|
||||
var symbol = loggie.settings.h_separator_symbol if alternative_symbol == null else str(alternative_symbol)
|
||||
self.content[current_segment_index] = self.content[current_segment_index] + (symbol.repeat(size))
|
||||
return self
|
||||
|
||||
## Ends the current segment of the message and starts a new one.
|
||||
func endseg() -> LoggieMsg:
|
||||
self.content.push_back("")
|
||||
self.current_segment_index = self.content.size() - 1
|
||||
return self
|
||||
|
||||
## Sets the environment in which this message should be outputted based on the context the output is being requested from.
|
||||
## (e.g. only in-engine (during tool scripts), or only ingame, or both).
|
||||
func env(mode : LoggieEnums.MsgEnvironment) -> LoggieMsg:
|
||||
self.environment_mode = mode
|
||||
return self
|
||||
|
||||
## Creates a new segment in this message and sets its content to the given message.
|
||||
## Acts as a shortcut for calling [method endseg] + [method add].
|
||||
func msg(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
self.endseg()
|
||||
var converter_fn = self._logger.settings.custom_string_converter if is_instance_valid(self._logger) and is_instance_valid(self._logger.settings) else null
|
||||
self.content[current_segment_index] = LoggieTools.concatenate_args([message, arg1, arg2, arg3, arg4, arg5], converter_fn)
|
||||
return self
|
||||
|
||||
## Sets whether this message should be preprocessed and potentially modified with prefixes and suffixes during [method output].
|
||||
## If turned off, while outputting this message, Loggie will skip the steps where it appends the messaage domain, class name, timestamp, etc.
|
||||
## Whether preprocess is set to true doesn't affect the final conversion from RICH to ANSI or PLAIN, which always happens
|
||||
## under some circumstances that are based on other settings.
|
||||
func preprocessed(shouldPreprocess : bool) -> LoggieMsg:
|
||||
self.preprocess = shouldPreprocess
|
||||
return self
|
||||
|
||||
## Sets this message's [LoggieEnums.MsgType] to be strictly the provided value,
|
||||
## instead of dynamically decided by the log level at which it is being outputted.
|
||||
## [br][param loggie_enums_msgtype_key_or_value] should either be provided as a direct value of the enum,
|
||||
## (e.g. [enum LoggieEnum.MsgType.INFO]) or as a string matching one of the keys in that enum (e.g.
|
||||
## `"info"` or `"INFO"`, case-insensitive).
|
||||
func type(loggie_enums_msgtype_key_or_value : Variant) -> LoggieMsg:
|
||||
var isValid = false
|
||||
var _type : LoggieEnums.MsgType
|
||||
|
||||
if loggie_enums_msgtype_key_or_value is LoggieEnums.MsgType:
|
||||
_type = loggie_enums_msgtype_key_or_value
|
||||
isValid = true
|
||||
elif loggie_enums_msgtype_key_or_value is String:
|
||||
var uppercase_key = loggie_enums_msgtype_key_or_value.to_upper()
|
||||
if LoggieEnums.MsgType.keys().has(uppercase_key):
|
||||
_type = LoggieEnums.MsgType[uppercase_key]
|
||||
isValid = true
|
||||
|
||||
if isValid:
|
||||
dynamic_type = false
|
||||
strict_type = _type
|
||||
else:
|
||||
push_error("Attempt to set LoggieMsg type to {value} - could not be converted to a proper [LoggieEnums.MsgType]. Either provide a [LoggieEnums.MsgType], or a string that matches a key in that enum (case-insensitive).".format({"value": str(loggie_enums_msgtype_key_or_value)}))
|
||||
|
||||
return self
|
||||
|
||||
## If [param enabled], the [signal Loggie.log_attempted] signal will not be emitted when this message attempts to be outputted.
|
||||
func no_signal(enabled : bool = true) -> LoggieMsg:
|
||||
dont_emit_log_attempted_signal = enabled
|
||||
return self
|
||||
|
||||
## Applies the [LoggiePreset] with the given [param id] to this message.
|
||||
## [br]If [param apply_only_to_current_segment] is [param true], the styles from the preset will only be
|
||||
## applied to the current content segment of this message. Otherwise, the entire content of this message
|
||||
## will be collapsed into a single segment, and the styles will be applied to that.
|
||||
func preset(id : String, apply_only_to_current_segment : bool = false) -> LoggieMsg:
|
||||
var loggie = get_logger()
|
||||
var preset_to_use : LoggiePreset = loggie.preset(id)
|
||||
if preset_to_use:
|
||||
preset_to_use.apply_to(self, apply_only_to_current_segment)
|
||||
else:
|
||||
push_error("Attempt to obtain LoggiePreset with ID {id} returned null. Something went terribly wrong, as this should usually be impossible.")
|
||||
return self
|
||||
|
||||
## Internal method. Emits the [signal Loggie.log_attempted] signal (unless that feature is disabled).
|
||||
## Used during [method output]. If [param call_deferred] is true, the string of the message's content will
|
||||
## be prepared immediately, but the emission of the signal will be deferred.
|
||||
func _emit_log_attempted_signal(result : LoggieEnums.LogAttemptResult, call_deferred : bool = false) -> void:
|
||||
if dont_emit_log_attempted_signal:
|
||||
return
|
||||
|
||||
var loggie = get_logger()
|
||||
var string_content = self.string()
|
||||
|
||||
if call_deferred:
|
||||
loggie.call_deferred("emit_signal", "log_attempted", self, string_content, result)
|
||||
else:
|
||||
loggie.log_attempted.emit(self, string_content, result)
|
||||
|
||||
## Adds this message's configured domain to the start of the given [param message] and returns the modifier version of it.
|
||||
func _apply_format_domain(message : String) -> String:
|
||||
var loggie = get_logger()
|
||||
message = loggie.settings.format_domain_prefix.format({"domain" : self.domain_name, "msg" : message})
|
||||
return message
|
||||
|
||||
## Adds a formatted class name to the given [param message] and returns the modified version of it.
|
||||
func _apply_format_class_name(message : String) -> String:
|
||||
var loggie = get_logger()
|
||||
var stack_frame : Dictionary = LoggieTools.get_current_stack_frame_data()
|
||||
var _class_name : String
|
||||
|
||||
var scriptPath = stack_frame.source
|
||||
if loggie.class_names.has(scriptPath):
|
||||
_class_name = loggie.class_names[scriptPath]
|
||||
else:
|
||||
_class_name = LoggieTools.get_class_name_from_script(scriptPath, loggie.settings.nameless_class_name_proxy)
|
||||
loggie.class_names[scriptPath] = _class_name
|
||||
|
||||
if _class_name != "":
|
||||
message = "[b]({class_name})[/b] {msg}".format({
|
||||
"class_name" : _class_name,
|
||||
"msg" : message
|
||||
})
|
||||
return message
|
||||
|
||||
## Adds a formatted timestamp to the given [param message] and returns the modified version of it.
|
||||
func _apply_format_timestamp(message : String) -> String:
|
||||
var loggie = get_logger()
|
||||
var format_dict : Dictionary = Time.get_datetime_dict_from_system(loggie.settings.timestamps_use_utc)
|
||||
for field in ["month", "day", "hour", "minute", "second"]:
|
||||
format_dict[field] = "%02d" % format_dict[field]
|
||||
|
||||
# Add the millisecond
|
||||
var unix_time: float = Time.get_unix_time_from_system()
|
||||
var millisecond: int = int((unix_time - int(unix_time)) * 1000.0)
|
||||
format_dict["millisecond"] = "%03d" % millisecond
|
||||
|
||||
# Add the startup time to the format dictionary.
|
||||
var elapsed_millisecond: int = Time.get_ticks_msec()
|
||||
var startup_hour: int = elapsed_millisecond / 3_600_000
|
||||
var startup_minute: int = (elapsed_millisecond % 3_600_000) / 60_000
|
||||
var startup_second: int = (elapsed_millisecond % 60_000) / 1000
|
||||
var startup_millisecond: int = elapsed_millisecond % 1000
|
||||
format_dict["startup_hour"] = "%02d" % startup_hour
|
||||
format_dict["startup_minute"] = "%02d" % startup_minute
|
||||
format_dict["startup_second"] = "%02d" % startup_second
|
||||
format_dict["startup_millisecond"] = "%03d" % startup_millisecond
|
||||
|
||||
message = "{formatted_time} {msg}".format({
|
||||
"formatted_time" : loggie.settings.format_timestamp.format(format_dict),
|
||||
"msg" : message
|
||||
})
|
||||
|
||||
return message
|
||||
|
||||
## Adds the stack trace to the given [param message] and returns the modified version of it.
|
||||
func _apply_format_stack(message : String) -> String:
|
||||
var loggie = get_logger()
|
||||
var stack_msg = loggie.stack()
|
||||
message = message + stack_msg.string()
|
||||
return message
|
||||
@@ -1 +0,0 @@
|
||||
uid://bd3hgca3nslui
|
||||
@@ -1,24 +0,0 @@
|
||||
@tool
|
||||
|
||||
## A class that describes a channel that can be used to output Loggie Messages.
|
||||
class_name LoggieMsgChannel extends RefCounted
|
||||
|
||||
## The ID of the channel.
|
||||
var ID : String = ""
|
||||
|
||||
## The preprocessing steps a [method LoggieMsg] that's about to be
|
||||
## sent to this channel has to go through. See: [LoggieEnums.PreprocessStep] for
|
||||
## the list of flags that can be used.
|
||||
var preprocess_flags : int = 0
|
||||
|
||||
## Defines what happens when some [LoggieMsg] wants to be sent with this channel.
|
||||
## [br]If you're implementing your own channel, override this function to define
|
||||
## how your channel outputs the message.
|
||||
##
|
||||
## You can access the last known preprocessed version of the message
|
||||
## in [LoggieMsg.last_preprocess_result].
|
||||
##
|
||||
## If your channel requires extra data, the data can be embedded into a message
|
||||
## with [method LoggieMsg.set_meta] and read here with [method LoggieMsg.get_meta].
|
||||
func send(msg : LoggieMsg, type : LoggieEnums.MsgType):
|
||||
pass
|
||||
@@ -1 +0,0 @@
|
||||
uid://bunns8okgi81c
|
||||
@@ -1,83 +0,0 @@
|
||||
## An extension of the [LoggieMsg] class, meant to clearly differentiate between a message
|
||||
## that was created with the sole purpose of being used as a Preset.
|
||||
## Future behaviors of the message may diverge from regular behaviors of LoggieMsg, hence
|
||||
## being separated into a different class.
|
||||
class_name LoggiePreset extends LoggieMsg
|
||||
|
||||
## A placeholder that will be placed into the content of this preset as soon as it's initialized.
|
||||
## This placeholder then gets wrapped with various styles as you call methods (e.g. [method bold],
|
||||
## [method italic], etc.) on the preset. Later, only this string part of the content
|
||||
## gets replaced by the actual content of a message this preset is being applied to, keeping the
|
||||
## wrapped styles around it.
|
||||
const content_placeholder = "{content}"
|
||||
|
||||
func _init(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> void:
|
||||
self.content = [content_placeholder]
|
||||
|
||||
## Applies the settings and styles from this preset onto the given [LoggieMsg], modifying that message
|
||||
## (does not create a duplicate) and returning that same message after it has been modified.
|
||||
##
|
||||
## [br][br]If any styles are applied to this [LoggiePreset], the entire content of the given [LoggieMsg] will
|
||||
## be collapsed into a single segment, then have this preset's styles wrapped around that segment.
|
||||
## [b]This directly resizes the [member LoggieMsg.content] array on the modified message.[/b]
|
||||
##
|
||||
## [br][br]However, if [param only_to_current_segment] is true, only the [member LoggieMsg.content] of
|
||||
## the message found at its [member LoggieMsg.current_segment_index] will be wrapped in this preset's styles and modified,
|
||||
## hence no resizing of the [member LoggieMsg.content] array will occur.
|
||||
##
|
||||
## [br]Neither of the above described actions will occur if this preset's content was not modified by any styles in any way.
|
||||
func apply_to(msg : LoggieMsg, only_to_current_segment : bool = false) -> LoggieMsg:
|
||||
if msg is LoggiePreset:
|
||||
push_error("Attempt to apply a [LoggiePreset] to another [LoggiePreset] - unsupported behavior.")
|
||||
return
|
||||
|
||||
# Apply the settings from this setting on the given message.
|
||||
msg.domain_name = self.domain_name
|
||||
msg.used_channels = self.used_channels.duplicate()
|
||||
msg.preprocess = self.preprocess
|
||||
msg.custom_preprocess_flags = self.custom_preprocess_flags
|
||||
msg.appends_stack = self.appends_stack
|
||||
msg.dynamic_type = self.dynamic_type
|
||||
msg.strict_type = self.strict_type
|
||||
msg.environment_mode = self.environment_mode
|
||||
msg.dont_emit_log_attempted_signal = self.dont_emit_log_attempted_signal
|
||||
|
||||
# If no modifications were done to this preset's content (no styles applied),
|
||||
# we don't need to go further.
|
||||
if self.content[0] == content_placeholder:
|
||||
return msg
|
||||
|
||||
# Otherwise,
|
||||
# Based on the value of [param only_to_current_segment], either:
|
||||
# 1. (if true): Replace the content of the [param msg] on its current segment to be that same content, but wrapped in the styles of this preset.
|
||||
# 2. (if false): Smush the entire content of the [param msg] into a single segment, then wrap that segment in the styles of this preset.
|
||||
var content_to_apply_preset_to : String
|
||||
|
||||
if only_to_current_segment:
|
||||
content_to_apply_preset_to = msg.string(msg.current_segment_index)
|
||||
var new_segment_content = self.content[0].format({"content": content_to_apply_preset_to})
|
||||
msg.content[msg.current_segment_index] = new_segment_content
|
||||
else:
|
||||
content_to_apply_preset_to = msg.string()
|
||||
var new_message_content = self.content[0].format({"content": content_to_apply_preset_to})
|
||||
msg.content = [new_message_content]
|
||||
|
||||
return msg
|
||||
|
||||
## Overrides the parent class method by the same name to disable its functionality.
|
||||
## Presets cannot have additional segments added.
|
||||
func endseg() -> LoggieMsg:
|
||||
push_warning("Attempt to call LoggieMsg.endseg on a LoggiePreset. This is not allowed. Presets shouldn't have more than 1 string content segment.")
|
||||
return self
|
||||
|
||||
## Overrides the parent class method by the same name to disable its functionality.
|
||||
## Presets cannot have their content modified.
|
||||
func msg(message = "", arg1 = null, arg2 = null, arg3 = null, arg4 = null, arg5 = null) -> LoggieMsg:
|
||||
push_warning("Attempt to call LoggieMsg.msg on a LoggiePreset. This is not allowed. Presets cannot have string content.")
|
||||
return self
|
||||
|
||||
## Overrides the parent class method by the same name to disable its functionality.
|
||||
## Presets cannot be outputted, they are only meant to be applied to other [LoggieMsg]s.
|
||||
func output(level : LoggieEnums.LogLevel, msg_type : LoggieEnums.MsgType = LoggieEnums.MsgType.INFO) -> void:
|
||||
push_warning("Attempt to call LoggieMsg.output on a LoggiePreset. This is not allowed. Presets should only be applied to other [LoggieMsg] instances, not used directly.")
|
||||
return
|
||||
@@ -1 +0,0 @@
|
||||
uid://b2oby5841g2bw
|
||||
@@ -1,521 +0,0 @@
|
||||
@tool
|
||||
|
||||
## Defines a set of variables through which all the relevant settings of Loggie can have their
|
||||
## values set, read and documented. An instance of this class is found in [member Loggie.settings], and that's where Loggie
|
||||
## ultimately reads from when it's asking for the value of a setting. For user convenience, settings are (by default) exported
|
||||
## as custom Godot project settings and are loaded from there into these variables during [method load], however,
|
||||
## you can extend or overwrite this class' [method load] method to define a different way of loading these settings if you prefer.
|
||||
## [i](e.g. loading from a config.ini file, or a .json file, etc.)[/i].[br][br]
|
||||
##
|
||||
## Loggie calls [method load] on this class during its [method _ready] function.
|
||||
class_name LoggieSettings extends Resource
|
||||
|
||||
## The name that will be used for the singleton referring to Loggie.
|
||||
## [br][br][i][b]Note:[/b] You may change this to something you're more used to, such as "log" or "logger".[/i]
|
||||
## When doing so, make sure to either do it while the Plugin is enabled, then disable and re-enable the plugin,
|
||||
## or that you manually clear out the previously created autoload (should be called "Loggie") in Project Settings -> Autoloads.
|
||||
static var loggie_singleton_name = "Loggie"
|
||||
|
||||
# ----------------------------------------------- #
|
||||
#region Project Settings
|
||||
# ----------------------------------------------- #
|
||||
## The dictionary which is used to grab the defaults and other values associated with each setting
|
||||
## relevant to Loggie, particularly important for the default way of loading [LoggieSettings] and
|
||||
## setting up Godot Project Settings related to Loggie.
|
||||
const project_settings = {
|
||||
"update_check_mode" = {
|
||||
"path": "loggie/general/check_for_updates",
|
||||
"default_value" : LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_UPDATER_WINDOW,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_ENUM,
|
||||
"hint_string" : "Never:0,Only print notice if available:1,Print notice and auto-install:2,Yes and show updater window:3",
|
||||
"doc" : "Sets which behavior Loggie should use when checking for updates.",
|
||||
},
|
||||
"remove_settings_if_plugin_disabled" = {
|
||||
"path": "loggie/general/remove_settings_if_plugin_disabled",
|
||||
"default_value" : true,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "Choose whether you want Loggie project settings to be wiped from ProjectSettings if the Loggie plugin is disabled.",
|
||||
},
|
||||
"msg_format_mode" = {
|
||||
"path": "loggie/general/msg_format_mode",
|
||||
"default_value" : LoggieEnums.MsgFormatMode.BBCODE,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_ENUM,
|
||||
"hint_string" : "Plain:0,ANSI:1,BBCode:2,Markdown:3",
|
||||
"doc" : "Choose the format for which loggie should preprocess the output so that it displays correctly on the intended output medium.[br][br]Use BBCode for Godot console.[br]Use ANSI for Powershell, Bash, etc.[br]Use MARKDOWN for Discord.[br]Use PLAIN for log files.",
|
||||
},
|
||||
"log_level" = {
|
||||
"path": "loggie/general/log_level",
|
||||
"default_value" : LoggieEnums.LogLevel.INFO,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_ENUM,
|
||||
"hint_string" : "Error:0,Warn:1,Notice:2,Info:3,Debug:4",
|
||||
"doc" : "Choose the level of messages which should be displayed. Loggie displays all messages that are outputted at the currently set level (or any lower level).",
|
||||
},
|
||||
"show_system_specs" = {
|
||||
"path": "loggie/general/show_system_specs",
|
||||
"default_value" : true,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "Should Loggie log the system and device specs of the user as soon as it is booted?",
|
||||
},
|
||||
"show_loggie_specs" = {
|
||||
"path": "loggie/general/show_loggie_specs",
|
||||
"default_value" : LoggieEnums.ShowLoggieSpecsMode.ESSENTIAL,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_ENUM,
|
||||
"hint_string" : "Disabled:0,Essential:1,Advanced:2",
|
||||
"doc" : "Defines which way Loggie should print its own specs when it is booted.",
|
||||
},
|
||||
"enforce_optimal_settings_in_release_build" = {
|
||||
"path": "loggie/general/enforce_optimal_settings_in_release_build",
|
||||
"default_value" : true,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "Should Loggie enforce certain settings to automatically change to optimal values in production/release builds?",
|
||||
},
|
||||
"default_channels" = {
|
||||
"path": "loggie/general/default_channels",
|
||||
"default_value" : ["terminal"],
|
||||
"type" : TYPE_PACKED_STRING_ARRAY,
|
||||
"hint" : PROPERTY_HINT_TYPE_STRING,
|
||||
"hint_string" : "",
|
||||
"doc" : "The channels messages outputted from Loggie will be sent to by default (if not modified with LoggieMsg.channel method).",
|
||||
},
|
||||
"skipped_filenames_in_stack_trace" = {
|
||||
"path": "loggie/general/skipped_filenames_in_stack_trace",
|
||||
"default_value" : ["loggie", "loggie_message"],
|
||||
"type" : TYPE_PACKED_STRING_ARRAY,
|
||||
"hint" : PROPERTY_HINT_TYPE_STRING,
|
||||
"hint_string" : "",
|
||||
"doc" : "The file names, which, when appearing in a stack trace, should be omitted from the output.",
|
||||
},
|
||||
"discord_webhook_url_live" = {
|
||||
"path": "loggie/general/discord/live_webhook",
|
||||
"default_value" : "",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_MULTILINE_TEXT,
|
||||
"hint_string" : "",
|
||||
"doc" : "The endpoint URL for the Discord webhook used when Loggie is running in a production build.",
|
||||
},
|
||||
"discord_webhook_url_dev" = {
|
||||
"path": "loggie/general/discord/dev_webhook",
|
||||
"default_value" : "",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_MULTILINE_TEXT,
|
||||
"hint_string" : "",
|
||||
"doc" : "The endpoint URL for the Discord webhook used when Loggie is not running in a production build.",
|
||||
},
|
||||
"slack_webhook_url_live" = {
|
||||
"path": "loggie/general/slack/live_webhook",
|
||||
"default_value" : "",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_MULTILINE_TEXT,
|
||||
"hint_string" : "",
|
||||
"doc" : "The endpoint URL for the Slack webhook used when Loggie is running in a production build.",
|
||||
},
|
||||
"slack_webhook_url_dev" = {
|
||||
"path": "loggie/general/slack/dev_webhook",
|
||||
"default_value" : "",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_MULTILINE_TEXT,
|
||||
"hint_string" : "",
|
||||
"doc" : "The endpoint URL for the Slack webhook used when Loggie is not running in a production build.",
|
||||
},
|
||||
"timestamps_use_utc" = {
|
||||
"path": "loggie/preprocessing/timestamps_use_utc",
|
||||
"default_value" : true,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "If 'Output Timestamps' is true, should those timestamps use the UTC time. If not, local system time is used instead.",
|
||||
},
|
||||
"output_errors_to_console" = {
|
||||
"path": "loggie/preprocessing/terminal/output_errors_also_to_console",
|
||||
"default_value" : true,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "If true, errors printed by Loggie will also be visible through an additional print in the main output.",
|
||||
},
|
||||
"output_warnings_to_console" = {
|
||||
"path": "loggie/preprocessing/terminal/output_warnings_also_to_console",
|
||||
"default_value" : true,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "If true, warnings printed by Loggie will also be visible through an additional print in the main output.",
|
||||
},
|
||||
"debug_msgs_print_stack_trace" = {
|
||||
"path": "loggie/preprocessing/terminal/debug_msgs_print_stack_trace",
|
||||
"default_value" : false,
|
||||
"type" : TYPE_BOOL,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "If true, 'debug' level messages outputted by Loggie will also print the stack trace.",
|
||||
},
|
||||
"nameless_class_name_proxy" = {
|
||||
"path": "loggie/preprocessing/nameless_class_name_proxy",
|
||||
"default_value" : LoggieEnums.NamelessClassExtensionNameProxy.BASE_TYPE,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_ENUM,
|
||||
"hint_string" : "Nothing:0,ScriptName:1,BaseType:2",
|
||||
"doc" : "If 'Derive and Display Class Names From Scripts' is enabled, and a script doesn't have a 'class_name', which text should we use as a substitute?",
|
||||
},
|
||||
"preprocess_flags_terminal_channel" = {
|
||||
"path": "loggie/preprocessing/terminal/preprocess_flags",
|
||||
"default_value" : LoggieEnums.PreprocessStep.APPEND_TIMESTAMPS | LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_FLAGS,
|
||||
"hint_string" : "Append Timestamp:1,Append Domain Name:2,Append Class Name:4",
|
||||
"doc" : "Defines the flags which LoggieMessages sent to the terminal channel will use during preprocessing.",
|
||||
},
|
||||
"preprocess_flags_discord_channel" = {
|
||||
"path": "loggie/preprocessing/discord/preprocess_flags",
|
||||
"default_value" : LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_FLAGS,
|
||||
"hint_string" : "Append Timestamp:1,Append Domain Name:2,Append Class Name:4",
|
||||
"doc" : "Defines the flags which LoggieMessages sent to the Discord channel will use during preprocessing.",
|
||||
},
|
||||
"preprocess_flags_slack_channel" = {
|
||||
"path": "loggie/preprocessing/slack/preprocess_flags",
|
||||
"default_value" : LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_FLAGS,
|
||||
"hint_string" : "Append Timestamp:1,Append Domain Name:2,Append Class Name:4",
|
||||
"doc" : "Defines the flags which LoggieMessages sent to the Slack channel will use during preprocessing.",
|
||||
},
|
||||
"format_timestamp" = {
|
||||
"path": "loggie/formats/timestamp",
|
||||
"default_value" : "[{day}.{month}.{year} {hour}:{minute}:{second}]",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for timestamps which are prepended to the message when the appending of timestamps is enabled.",
|
||||
},
|
||||
"format_stacktrace_entry" = {
|
||||
"path": "loggie/formats/stacktrace_entry",
|
||||
"default_value" : "{index}: [color=#ff7085]func[/color] [color=#53b1c3][b]{fn_name}[/b]:{line}[/color] [color=slate_gray][i](in {source_path})[/i][/color]",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for stack trace entries when trace logging is enabled.",
|
||||
},
|
||||
"format_debug_msg" = {
|
||||
"path": "loggie/formats/debug_message",
|
||||
"default_value" : "[b][color=pink][DEBUG]:[/color][/b] {msg}",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for debug messages.",
|
||||
},
|
||||
"format_info_msg" = {
|
||||
"path": "loggie/formats/info_message",
|
||||
"default_value" : "{msg}",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for info messages.",
|
||||
},
|
||||
"format_notice_msg" = {
|
||||
"path": "loggie/formats/notice_message",
|
||||
"default_value" : "[b][color=cyan][NOTICE]:[/color][/b] {msg}",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for notice messages.",
|
||||
},
|
||||
"format_warning_msg" = {
|
||||
"path": "loggie/formats/warning_message",
|
||||
"default_value" : "[b][color=orange][WARN]:[/color][/b] {msg}",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for warning messages.",
|
||||
},
|
||||
"format_error_msg" = {
|
||||
"path": "loggie/formats/error_message",
|
||||
"default_value" : "[b][color=red][ERROR]:[/color][/b] {msg}",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for error messages.",
|
||||
},
|
||||
"format_domain_prefix" = {
|
||||
"path": "loggie/formats/domain_prefix",
|
||||
"default_value" : "[b]({domain})[/b] {msg}",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for domain prefixes.",
|
||||
},
|
||||
"format_header" = {
|
||||
"path": "loggie/formats/header",
|
||||
"default_value" : "[b][i]{msg}[/i][/b]",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The format used for headers.",
|
||||
},
|
||||
"h_separator_symbol" = {
|
||||
"path": "loggie/formats/h_separator_symbol",
|
||||
"default_value" : "-",
|
||||
"type" : TYPE_STRING,
|
||||
"hint" : PROPERTY_HINT_NONE,
|
||||
"hint_string" : "",
|
||||
"doc" : "The symbol used for the horizontal separator.",
|
||||
},
|
||||
"box_characters_mode" = {
|
||||
"path": "loggie/formats/box_characters_mode",
|
||||
"default_value" : LoggieEnums.BoxCharactersMode.COMPATIBLE,
|
||||
"type" : TYPE_INT,
|
||||
"hint" : PROPERTY_HINT_ENUM,
|
||||
"hint_string" : "Compatible:0,Pretty:1",
|
||||
"doc" : "There are two sets of box characters defined in LoggieSettings - one set contains prettier characters that produce a nicer looking box, but may not render correctly in the context of various terminals. The other set contains characters that produce a less pretty box, but are compatible with being shown in most terminals.",
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
# ----------------------------------------------- #
|
||||
#region Variables
|
||||
# ----------------------------------------------- #
|
||||
|
||||
## The current behavior Loggie uses to check for updates.
|
||||
var update_check_mode : LoggieEnums.UpdateCheckType = LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_UPDATER_WINDOW
|
||||
|
||||
## The current Message Format Mode of Loggie.
|
||||
## Message Format Mode determines whether BBCode, ANSI or some other type of
|
||||
## formatting is used to convey text effects, such as bold, italic, colors, etc.
|
||||
## [br][br]BBCode is compatible with the Godot console.
|
||||
## [br]ANSI is compatible with consoles like Powershell and Windows CMD.
|
||||
## [br]PLAIN is used to strip any effects and use plain text instead, which is good for saving raw logs into log files.
|
||||
var msg_format_mode : LoggieEnums.MsgFormatMode = LoggieEnums.MsgFormatMode.BBCODE
|
||||
|
||||
## The current log level of Loggie.
|
||||
## It determines which types of messages are allowed to be logged.
|
||||
## Set this using [method setLogLevel].
|
||||
var log_level : LoggieEnums.LogLevel = LoggieEnums.LogLevel.INFO
|
||||
|
||||
## Whether or not Loggie should log the loggie specs on ready.
|
||||
var show_loggie_specs : LoggieEnums.ShowLoggieSpecsMode = LoggieEnums.ShowLoggieSpecsMode.ESSENTIAL
|
||||
|
||||
## Whether or not Loggie should log the system specs on ready.
|
||||
var show_system_specs : bool = true
|
||||
|
||||
## Whether to, in addition to logging errors with [method push_error],
|
||||
## Loggie should also print the error as a message in the standard output.
|
||||
var print_errors_to_console : bool = true
|
||||
|
||||
## Whether to, in addition to logging errors with [method push_warning],
|
||||
## Loggie should also print the error as a message in the standard output.
|
||||
var print_warnings_to_console : bool = true
|
||||
|
||||
## Defines which text will be used as a substitute for the 'class_name' of scripts that do not have a 'class_name'.
|
||||
## Relevant only during the [member LoggieEnums.PreprocessStep.APPEND_CLASS_NAME] step of preprocessing.
|
||||
var nameless_class_name_proxy : LoggieEnums.NamelessClassExtensionNameProxy
|
||||
|
||||
## Whether the outputted timestamps use UTC or local machine time.
|
||||
var timestamps_use_utc : bool = true
|
||||
|
||||
## If true, when outputting Debug level messages, the stack trace will also be appended.
|
||||
var debug_msgs_print_stack_trace : bool = false
|
||||
|
||||
## Whether Loggie should enforce optimal values for certain settings when in a Release/Production build.
|
||||
## [br]If true, Loggie will enforce:
|
||||
## [br] * [member msg_format_mode] to [member LoggieEnums.MsgFormatMode.PLAIN]
|
||||
## [br] * [member box_characters_mode] to [member LoggieEnums.BoxCharactersMode.COMPATIBLE]
|
||||
var enforce_optimal_settings_in_release_build : bool = true
|
||||
|
||||
## Endpoint URL for the Discord webhook (used in dev environment)
|
||||
## [br][b]NEVER[/b] distribute your webhook in your project's repository, source code, or built game, where it can be accessed by other people.
|
||||
## This is meant to be used only in controlled circumstances.
|
||||
var discord_webhook_url_dev : String = ""
|
||||
|
||||
## Endpoint URL for the Discord webhook (used in production/release environment)
|
||||
## [br][b]NEVER[/b] distribute your webhook in your project's repository, source code, or built game, where it can be accessed by other people.
|
||||
## This is meant to be used only in controlled circumstances.
|
||||
var discord_webhook_url_live : String = ""
|
||||
|
||||
## Endpoint URL for the Slack webhook (used in dev environment)
|
||||
## [br][b]NEVER[/b] distribute your webhook in your project's repository, source code, or built game, where it can be accessed by other people.
|
||||
## This is meant to be used only in controlled circumstances.
|
||||
var slack_webhook_url_dev : String = ""
|
||||
|
||||
## Endpoint URL for the Slack webhook (used in production/release environment)
|
||||
## [br][b]NEVER[/b] distribute your webhook in your project's repository, source code, or built game, where it can be accessed by other people.
|
||||
## This is meant to be used only in controlled circumstances.
|
||||
var slack_webhook_url_live : String = ""
|
||||
|
||||
## Defines the flags which LoggieMessages sent to the terminal channel will use during preprocessing.
|
||||
var preprocess_flags_terminal_channel = LoggieEnums.PreprocessStep.APPEND_TIMESTAMPS | LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME
|
||||
|
||||
## Defines the flags which LoggieMessages sent to the Discord channel output will use during preprocessing.
|
||||
var preprocess_flags_discord_channel = LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME
|
||||
|
||||
## Defines the flags which LoggieMessages sent to the Slack channel output will use during preprocessing.
|
||||
var preprocess_flags_slack_channel = LoggieEnums.PreprocessStep.APPEND_DOMAIN_NAME | LoggieEnums.PreprocessStep.APPEND_CLASS_NAME
|
||||
|
||||
## The list of channels a message outputted from Loggie should be sent to by default.
|
||||
var default_channels : PackedStringArray = ["terminal"]
|
||||
|
||||
## The list of file names, which, when appearing in a stack trace, should be omitted from the output..
|
||||
var skipped_filenames_in_stack_trace : PackedStringArray = ["loggie", "loggie_message"]
|
||||
|
||||
#endregion
|
||||
# ----------------------------------------------- #
|
||||
#region Formats for prints
|
||||
# ----------------------------------------------- #
|
||||
# As per the `print_rich` documentation, supported colors are: black, red, green, yellow, blue, magenta, pink, purple, cyan, white, orange, gray.
|
||||
# Any other color will be displayed in the Godot console or an ANSI based console, but the color tag (in case of BBCode) won't be properly stripped
|
||||
# when written to the .log file, resulting in BBCode visible in .log files.
|
||||
|
||||
## The format used to decorate a message as a header when using [method LoggieMsg.header].[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
var format_header = "[b][i]{msg}[/i][/b]"
|
||||
|
||||
## The format used when appending a domain to a message.[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
## The [param {domain}] is a variable that will be replaced with the domain key.[br]
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_domain_prefix = "[b]({domain})[/b] {msg}"
|
||||
|
||||
## The format used when outputting error messages.[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_error_msg = "[b][color=red][ERROR]:[/color][/b] {msg}"
|
||||
|
||||
## The format used when outputting warning messages.[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_warning_msg = "[b][color=orange][WARN]:[/color][/b] {msg}"
|
||||
|
||||
## The format used when outputting notice messages.[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_notice_msg = "[b][color=cyan][NOTICE]:[/color][/b] {msg}"
|
||||
|
||||
## The format used when outputting info messages.[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_info_msg = "{msg}"
|
||||
|
||||
## The format used when outputting debug messages.[br]
|
||||
## The [param {msg}] is a variable that will be replaced with the contents of the message.[br]
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_debug_msg = "[b][color=pink][DEBUG]:[/color][/b] {msg}"
|
||||
|
||||
## The format used for timestamps when they are prepended to the output.[br]
|
||||
## The variables [param {day}], [param {month}], [param {year}], [param {hour}], [param {minute}], [param {second}], [param {weekday}], and [param {dst}] are supported.
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_timestamp = "[{day}.{month}.{year} {hour}:{minute}:{second}]"
|
||||
|
||||
## The format used for each entry in a stack trace that is obtained through [method Loggie.stack].
|
||||
## The variables [param {fn_name}], [param {index}], [param {source_path}], [param {line}] are supported.
|
||||
## You can customize this in your ProjectSettings, or custom_settings.gd (if using it).[br]
|
||||
var format_stacktrace_entry = "{index}: [color=#ff7085]func[/color] [color=#53b1c3][b]{fn_name}[/b]:{line}[/color] [color=slate_gray][i](in {source_path})[/i][/color]"
|
||||
|
||||
## The symbol which will be used for the HSeparator.
|
||||
var h_separator_symbol = "-"
|
||||
|
||||
## The mode used for drawing boxes.
|
||||
var box_characters_mode : LoggieEnums.BoxCharactersMode
|
||||
|
||||
## The symbols which will be used to construct a box decoration that will properly
|
||||
## display on any kind of terminal or text reader.
|
||||
## For a prettier but potentially incompatible box, use [member box_symbols_pretty] instead.
|
||||
var box_symbols_compatible = {
|
||||
# ANSI and .log compatible box characters:
|
||||
"top_left" : "-",
|
||||
"top_right" : "-",
|
||||
"bottom_left" : "-",
|
||||
"bottom_right" : "-",
|
||||
"h_line" : "-",
|
||||
"v_line" : ":",
|
||||
}
|
||||
|
||||
## The symbols which will be used to construct pretty box decoration.
|
||||
## These may not be compatible with some terminals or text readers.
|
||||
## Use the [member box_symbols_compatible] instead as an alternative.
|
||||
var box_symbols_pretty = {
|
||||
"top_left" : "┌",
|
||||
"top_right" : "┐",
|
||||
"bottom_left" : "└",
|
||||
"bottom_right" : "┘",
|
||||
"h_line" : "─",
|
||||
"v_line" : "│",
|
||||
}
|
||||
|
||||
#endregion
|
||||
# ----------------------------------------------- #
|
||||
|
||||
## A [Callable] function that takes 1 parameter [param something] (Variant),
|
||||
## and returns a [String] which represents the given [param something] in text.
|
||||
## By default, Loggie sets this to `LoggieTools.convert_to_string` when initialized.
|
||||
## [br][br]
|
||||
## You can, however, override that by changing this value to a valid replacement [Callable],
|
||||
## after Loggie has initialized.
|
||||
var custom_string_converter : Callable
|
||||
|
||||
## Loads the initial (default) values for all of the LoggieSettings variables.
|
||||
## (By default, loads them from ProjectSettings (if any modifications there exist),
|
||||
## or looks in [LoggieEditorPlugin..project_settings] for default values).
|
||||
## [br][br]Extend this class and override this function to write your own logic for
|
||||
## how loggie should obtain these settings if you have a need for a different approach.
|
||||
func load():
|
||||
update_check_mode = ProjectSettings.get_setting(project_settings.update_check_mode.path, project_settings.update_check_mode.default_value)
|
||||
msg_format_mode = ProjectSettings.get_setting(project_settings.msg_format_mode.path, project_settings.msg_format_mode.default_value)
|
||||
log_level = ProjectSettings.get_setting(project_settings.log_level.path, project_settings.log_level.default_value)
|
||||
show_loggie_specs = ProjectSettings.get_setting(project_settings.show_loggie_specs.path, project_settings.show_loggie_specs.default_value)
|
||||
show_system_specs = ProjectSettings.get_setting(project_settings.show_system_specs.path, project_settings.show_system_specs.default_value)
|
||||
timestamps_use_utc = ProjectSettings.get_setting(project_settings.timestamps_use_utc.path, project_settings.timestamps_use_utc.default_value)
|
||||
enforce_optimal_settings_in_release_build = ProjectSettings.get_setting(project_settings.enforce_optimal_settings_in_release_build.path, project_settings.enforce_optimal_settings_in_release_build.default_value)
|
||||
default_channels = ProjectSettings.get_setting(project_settings.default_channels.path, project_settings.default_channels.default_value)
|
||||
skipped_filenames_in_stack_trace = ProjectSettings.get_setting(project_settings.skipped_filenames_in_stack_trace.path, project_settings.skipped_filenames_in_stack_trace.default_value)
|
||||
|
||||
print_errors_to_console = ProjectSettings.get_setting(project_settings.output_errors_to_console.path, project_settings.output_errors_to_console.default_value)
|
||||
print_warnings_to_console = ProjectSettings.get_setting(project_settings.output_warnings_to_console.path, project_settings.output_warnings_to_console.default_value)
|
||||
debug_msgs_print_stack_trace = ProjectSettings.get_setting(project_settings.debug_msgs_print_stack_trace.path, project_settings.debug_msgs_print_stack_trace.default_value)
|
||||
|
||||
nameless_class_name_proxy = ProjectSettings.get_setting(project_settings.nameless_class_name_proxy.path, project_settings.nameless_class_name_proxy.default_value)
|
||||
box_characters_mode = ProjectSettings.get_setting(project_settings.box_characters_mode.path, project_settings.box_characters_mode.default_value)
|
||||
|
||||
format_timestamp = ProjectSettings.get_setting(project_settings.format_timestamp.path, project_settings.format_timestamp.default_value)
|
||||
format_stacktrace_entry = ProjectSettings.get_setting(project_settings.format_stacktrace_entry.path, project_settings.format_stacktrace_entry.default_value)
|
||||
format_info_msg = ProjectSettings.get_setting(project_settings.format_info_msg.path, project_settings.format_info_msg.default_value)
|
||||
format_notice_msg = ProjectSettings.get_setting(project_settings.format_notice_msg.path, project_settings.format_notice_msg.default_value)
|
||||
format_warning_msg = ProjectSettings.get_setting(project_settings.format_warning_msg.path, project_settings.format_warning_msg.default_value)
|
||||
format_error_msg = ProjectSettings.get_setting(project_settings.format_error_msg.path, project_settings.format_error_msg.default_value)
|
||||
format_debug_msg = ProjectSettings.get_setting(project_settings.format_debug_msg.path, project_settings.format_debug_msg.default_value)
|
||||
h_separator_symbol = ProjectSettings.get_setting(project_settings.h_separator_symbol.path, project_settings.h_separator_symbol.default_value)
|
||||
|
||||
discord_webhook_url_live = ProjectSettings.get_setting(project_settings.discord_webhook_url_live.path, project_settings.discord_webhook_url_live.default_value)
|
||||
discord_webhook_url_dev = ProjectSettings.get_setting(project_settings.discord_webhook_url_dev.path, project_settings.discord_webhook_url_dev.default_value)
|
||||
preprocess_flags_discord_channel = ProjectSettings.get_setting(project_settings.preprocess_flags_discord_channel.path, project_settings.preprocess_flags_discord_channel.default_value)
|
||||
slack_webhook_url_live = ProjectSettings.get_setting(project_settings.slack_webhook_url_live.path, project_settings.slack_webhook_url_live.default_value)
|
||||
slack_webhook_url_dev = ProjectSettings.get_setting(project_settings.slack_webhook_url_dev.path, project_settings.slack_webhook_url_dev.default_value)
|
||||
preprocess_flags_slack_channel = ProjectSettings.get_setting(project_settings.preprocess_flags_slack_channel.path, project_settings.preprocess_flags_slack_channel.default_value)
|
||||
preprocess_flags_terminal_channel = ProjectSettings.get_setting(project_settings.preprocess_flags_terminal_channel.path, project_settings.preprocess_flags_terminal_channel.default_value)
|
||||
|
||||
## Returns a dictionary where the indices are names of relevant variables in the LoggieSettings class,
|
||||
## and the values are their current values.
|
||||
func to_dict() -> Dictionary:
|
||||
var dict = {}
|
||||
var included = [
|
||||
"preprocess_flags_discord_channel", "preprocess_flags_slack_channel", "preprocess_flags_terminal_channel",
|
||||
"default_channels", "skipped_filenames_in_stack_trace", "msg_format_mode", "log_level", "show_loggie_specs", "show_system_specs", "enforce_optimal_settings_in_release_build",
|
||||
"print_errors_to_console", "print_warnings_to_console",
|
||||
"debug_msgs_print_stack_trace", "nameless_class_name_proxy",
|
||||
"timestamps_use_utc", "format_header", "format_domain_prefix", "format_error_msg",
|
||||
"format_warning_msg", "format_notice_msg", "format_info_msg", "format_debug_msg", "format_timestamp",
|
||||
"h_separator_symbol", "box_characters_mode", "box_symbols_compatible", "box_symbols_pretty",
|
||||
]
|
||||
|
||||
for var_name in included:
|
||||
dict[var_name] = get(var_name)
|
||||
return dict
|
||||
@@ -1 +0,0 @@
|
||||
uid://cs4ybo1axktlw
|
||||
@@ -1,7 +0,0 @@
|
||||
[plugin]
|
||||
|
||||
name="Loggie"
|
||||
description="Simple functional stylish logger for your basic logging needs."
|
||||
author="Shiva Shadowsong"
|
||||
version="3.0"
|
||||
script="plugin.gd"
|
||||
@@ -1,50 +0,0 @@
|
||||
@tool
|
||||
class_name LoggieEditorPlugin extends EditorPlugin
|
||||
|
||||
func _enter_tree():
|
||||
add_loggie_project_settings()
|
||||
Engine.set_meta("LoggieEditorPlugin", self)
|
||||
if Engine.is_editor_hint():
|
||||
Engine.set_meta("LoggieEditorInterfaceBaseControl", EditorInterface.get_base_control())
|
||||
|
||||
func _enable_plugin() -> void:
|
||||
add_autoload_singleton(LoggieSettings.loggie_singleton_name, "res://addons/loggie/loggie.gd")
|
||||
add_loggie_project_settings()
|
||||
|
||||
func _disable_plugin() -> void:
|
||||
var wipe_setting_exists = ProjectSettings.has_setting(LoggieSettings.project_settings.remove_settings_if_plugin_disabled.path)
|
||||
if (not wipe_setting_exists) or (wipe_setting_exists and ProjectSettings.get_setting(LoggieSettings.project_settings.remove_settings_if_plugin_disabled.path, true)):
|
||||
push_warning("The Loggie plugin is being disabled, and all of its ProjectSettings are erased from Godot. If you wish to prevent this behavior, look for the 'Project Settings -> Loggie -> General -> Remove Settings if Plugin Disabled' option while the plugin is enabled.")
|
||||
remove_loggie_project_setings()
|
||||
else:
|
||||
push_warning("The Loggie plugin is being disabled, but its ProjectSettings have been prevented from being removed from Godot. If you wish to allow that behavior, look for the 'Project Settings -> Loggie -> General -> Remove Settings if Plugin Disabled' option while the plugin is enabled.")
|
||||
remove_autoload_singleton(LoggieSettings.loggie_singleton_name)
|
||||
|
||||
## Adds new Loggie related ProjectSettings to Godot.
|
||||
func add_loggie_project_settings():
|
||||
for setting in LoggieSettings.project_settings.values():
|
||||
add_project_setting(setting["path"], setting["default_value"], setting["type"], setting["hint"], setting["hint_string"], setting["doc"])
|
||||
|
||||
## Removes Loggie related ProjectSettings from Godot.
|
||||
func remove_loggie_project_setings():
|
||||
for setting in LoggieSettings.project_settings.values():
|
||||
ProjectSettings.set_setting(setting["path"], null)
|
||||
|
||||
var error: int = ProjectSettings.save()
|
||||
if error != OK:
|
||||
push_error("Loggie - Encountered error %d while saving project settings." % error)
|
||||
|
||||
## Adds a new project setting to Godot.
|
||||
## (WARNING): Unfortunately, as of Godot 4.3, it has been confirmed that it is not possible to set the
|
||||
## popup tooltip text for a project setting. This should be implemented here as soon as that becomes possible.
|
||||
func add_project_setting(setting_name: String, default_value : Variant, value_type: int, type_hint: int = PROPERTY_HINT_NONE, hint_string: String = "", documentation : String = ""):
|
||||
if !ProjectSettings.has_setting(setting_name):
|
||||
ProjectSettings.set_setting(setting_name, default_value)
|
||||
|
||||
ProjectSettings.set_initial_value(setting_name, default_value)
|
||||
ProjectSettings.add_property_info({ "name": setting_name, "type": value_type, "hint": type_hint, "hint_string": hint_string})
|
||||
ProjectSettings.set_as_basic(setting_name, true)
|
||||
|
||||
var error: int = ProjectSettings.save()
|
||||
if error:
|
||||
push_error("Loggie - Encountered error %d while saving project settings." % error)
|
||||
@@ -1 +0,0 @@
|
||||
uid://ct2hpdyg7gd81
|
||||