extends Control class_name DeckHolderRenderer ## Renderer class for [DeckHolder] ## ## Entry point for the [GraphEdit] based Renderer ## Reference to the base scene for [DeckRendererGraphEdit] const DECK_SCENE := preload("res://graph_node_renderer/deck_renderer_graph_edit.tscn") const DEBUG_DECKS_LIST := preload("res://graph_node_renderer/debug_decks_list.tscn") ## Reference to the main windows [TabContainerCustom] @onready var tab_container: TabContainerCustom = %TabContainerCustom as TabContainerCustom ## Reference to the [FileDialog] used for File operations through the program. @onready var file_dialog: FileDialog = $FileDialog @export var new_deck_shortcut: Shortcut @export var open_deck_shortcut: Shortcut @export var save_deck_shortcut: Shortcut @export var save_deck_as_shortcut: Shortcut @export var close_deck_shortcut: Shortcut ## Enum for storing the Options in the "File" PopupMenu. enum FileMenuId { NEW, OPEN, SAVE = 3, SAVE_AS, CLOSE = 6, } @onready var file_popup_menu: PopupMenu = %File as PopupMenu enum ConnectionsMenuId { OBS, TWITCH, } ## Weak Reference to the Deck that is currently going to be saved. var _deck_to_save: WeakRef @onready var no_obsws: NoOBSWS = %NoOBSWS as NoOBSWS @onready var obs_setup_dialog: OBSWebsocketSetupDialog = $OBSWebsocketSetupDialog as OBSWebsocketSetupDialog @onready var twitch_setup_dialog : TwitchSetupDialog = $Twitch_Setup_Dialog as TwitchSetupDialog func _ready() -> void: tab_container.add_button_pressed.connect(add_empty_deck) tab_container.tab_close_requested.connect( func(tab: int): DeckHolder.close_deck(tab_container.get_tab_metadata(tab)) tab_container.close_tab(tab) ) file_dialog.canceled.connect(disconnect_file_dialog_signals) Connections.obs_websocket = no_obsws Connections.twitch = %Twitch_Connection Connections.twitch.chat_socket.chat_received_rich.connect(Connections._twitch_chat_received) file_popup_menu.set_item_shortcut(FileMenuId.NEW, new_deck_shortcut) file_popup_menu.set_item_shortcut(FileMenuId.OPEN, open_deck_shortcut) file_popup_menu.set_item_shortcut(FileMenuId.SAVE, save_deck_shortcut) file_popup_menu.set_item_shortcut(FileMenuId.SAVE_AS, save_deck_as_shortcut) file_popup_menu.set_item_shortcut(FileMenuId.CLOSE, close_deck_shortcut) ## Called when the File button in the [MenuBar] is pressed with the [param id] ## of the button within it that was pressed. func _on_file_id_pressed(id: int) -> void: match id: FileMenuId.NEW: add_empty_deck() FileMenuId.OPEN: open_open_dialog("res://") FileMenuId.SAVE: save_active_deck() FileMenuId.SAVE_AS: open_save_dialog("res://") FileMenuId.CLOSE: close_current_tab() ## Adds an empty [DeckRendererGraphEdit] with a corresponding [Deck] for it's data. func add_empty_deck() -> void: var deck := DeckHolder.add_empty_deck() var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate() inst.deck = deck tab_container.add_content(inst, "Deck %s" % (tab_container.get_tab_count() + 1)) tab_container.set_tab_metadata(tab_container.get_current_tab(), deck.id) inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) ## Closes the current tab in [member tab_container] func close_current_tab() -> void: tab_container.close_tab(tab_container.get_current_tab()) ## Opens [member file_dialog] with the mode [member FileDialog.FILE_MODE_SAVE_FILE] ## as well as getting a weakref to the active [Deck] func open_save_dialog(path: String) -> void: file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE file_dialog.title = "Save a Deck" file_dialog.current_path = path _deck_to_save = weakref(get_active_deck()) file_dialog.popup_centered() file_dialog.file_selected.connect(_on_file_dialog_save_file, CONNECT_ONE_SHOT) ## Opens [member file_dialog] with the mode [FileDialog.FILE_MODE_OPEN_FILES] ## with the supplied [param path] func open_open_dialog(path: String) -> void: file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILES file_dialog.title = "Open Deck(s)" file_dialog.current_path = path file_dialog.popup_centered() file_dialog.files_selected.connect(_on_file_dialog_open_files, CONNECT_ONE_SHOT) ## Connected to [signal FileDialog.save_file] on [member file_dialog]. ## Saves the selected [Deck] if it still exists. func _on_file_dialog_save_file(path: String) -> void: var deck: Deck = _deck_to_save.get_ref() as Deck if !deck: return deck.save_path = path var json := JSON.stringify(deck.to_dict(), "\t") var f := FileAccess.open(path, FileAccess.WRITE) f.store_string(json) ## Connected to [signal FileDialog.open_files] on [member file_dialog]. Opens ## the selected paths, instantiating [DeckRenderGraphEdit]s and [Deck]s for each. func _on_file_dialog_open_files(paths: PackedStringArray) -> void: for path in paths: var deck := DeckHolder.open_deck_from_file(path) var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate() inst.deck = deck tab_container.add_content(inst, "Deck %s" % (tab_container.get_tab_count() + 1)) tab_container.set_tab_metadata(tab_container.get_current_tab(), deck.id) inst.initialize_from_deck() inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) ## Gets the currently active [Deck] from [member tab_container] func get_active_deck() -> Deck: if tab_container.is_empty(): return null return (tab_container.get_content(tab_container.get_current_tab()) as DeckRendererGraphEdit).deck ## Saves the active [Deck] in [member tab_container] func save_active_deck() -> void: if get_active_deck().save_path.is_empty(): open_save_dialog("res://") else: var json := JSON.stringify(get_active_deck().to_dict(), "\t") var f := FileAccess.open(get_active_deck().save_path, FileAccess.WRITE) f.store_string(json) ## Disconnects the [FileDialog] signals if they are already connected. func disconnect_file_dialog_signals() -> void: if file_dialog.file_selected.is_connected(_on_file_dialog_save_file): file_dialog.file_selected.disconnect(_on_file_dialog_save_file) if file_dialog.files_selected.is_connected(_on_file_dialog_open_files): file_dialog.files_selected.disconnect(_on_file_dialog_open_files) ## Connected to [signal DeckRenderGraphEdit.group_entered_request] to allow entering ## groups based off the given [param group_id] and [param deck]. As well as adding ## a corresponding tab to [member tab_container] func _on_deck_renderer_group_enter_requested(group_id: String) -> void: #var group_deck := deck.get_group(group_id) var group_deck := DeckHolder.get_deck(group_id) var deck_renderer: DeckRendererGraphEdit = DECK_SCENE.instantiate() deck_renderer.deck = group_deck deck_renderer.initialize_from_deck() tab_container.add_content(deck_renderer, "Group %s" % (tab_container.get_tab_count() + 1)) tab_container.set_tab_metadata(tab_container.get_current_tab(), group_id) deck_renderer.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) func _on_connections_id_pressed(id: int) -> void: match id: ConnectionsMenuId.OBS: obs_setup_dialog.popup_centered() ConnectionsMenuId.TWITCH: twitch_setup_dialog.popup_centered() func _on_obs_websocket_setup_dialog_connect_button_pressed(state: OBSWebsocketSetupDialog.ConnectionState) -> void: match state: OBSWebsocketSetupDialog.ConnectionState.DISCONNECTED: obs_setup_dialog.set_button_state(OBSWebsocketSetupDialog.ConnectionState.CONNECTING) no_obsws.connect_to_obsws(obs_setup_dialog.get_port(), obs_setup_dialog.get_password()) await no_obsws.connection_ready obs_setup_dialog.set_button_state(OBSWebsocketSetupDialog.ConnectionState.CONNECTED) func _process(delta: float) -> void: DeckHolder.send_event(&"process", {"delta": delta}) func _on_debug_id_pressed(id: int) -> void: var d := AcceptDialog.new() var debug_decks: DebugDecksList = DEBUG_DECKS_LIST.instantiate() d.add_child(debug_decks) d.canceled.connect(d.queue_free) d.confirmed.connect(d.queue_free) debug_decks.item_pressed.connect(_on_debug_decks_viewer_item_pressed) add_child(d) d.popup_centered() func _on_debug_decks_viewer_item_pressed(deck_id: String, instance_id: String) -> void: if instance_id == "": var deck := DeckHolder.get_deck(deck_id) var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate() inst.deck = deck tab_container.add_content(inst, "" % [deck_id.left(8)]) tab_container.set_tab_metadata(tab_container.get_current_tab(), deck.id) inst.initialize_from_deck() inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) else: var deck := DeckHolder.get_group_instance(deck_id, instance_id) var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate() inst.deck = deck tab_container.add_content(inst, "" % [deck_id.left(8), instance_id.left(8)]) tab_container.set_tab_metadata(tab_container.get_current_tab(), deck.id) inst.initialize_from_deck() inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested)