You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1062 lines
32 KiB

tool
extends HSplitContainer
var editor_reference
var timeline_name: String = ''
var timeline_file: String = ''
var current_timeline: Dictionary = {}
var TimelineUndoRedo := UndoRedo.new()
onready var master_tree = get_node('../MasterTreeContainer/MasterTree')
onready var timeline = $TimelineArea/TimeLine
onready var events_warning = $ScrollContainer/EventContainer/EventsWarning
onready var custom_events_container = $ScrollContainer/EventContainer/CustomEventsContainer
var hovered_item = null
var selected_style : StyleBoxFlat = load("res://addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres")
var selected_style_text : StyleBoxFlat = load("res://addons/dialogic/Editor/Events/styles/selected_styleboxflat_text_event.tres")
var selected_style_template : StyleBoxFlat = load("res://addons/dialogic/Editor/Events/styles/selected_styleboxflat_template.tres")
var saved_style : StyleBoxFlat
var selected_items : Array = []
var event_scenes : Dictionary = {}
var currently_draged_event_type = null
var move_start_position = null
var moving_piece = null
var piece_was_dragged = false
var custom_events = {}
var id_to_scene_name = {
#Main events
'dialogic_001':'TextEvent',
'dialogic_002':'CharacterJoin',
'dialogic_003':'CharacterLeave',
#Logic
'dialogic_010':'Question',
'dialogic_011':'Choice',
'dialogic_012':'Condition',
'dialogic_013':'EndBranch',
'dialogic_014':'SetValue',
#Timeline
'dialogic_020':'ChangeTimeline',
'dialogic_021':'ChangeBackground',
'dialogic_022':'CloseDialog',
'dialogic_023':'WaitSeconds',
'dialogic_024':'SetTheme',
'dialogic_025':'SetGlossary',
'dialogic_026':'SaveEvent',
#Audio
'dialogic_030':'AudioEvent',
'dialogic_031':'BackgroundMusic',
#Godot
'dialogic_040':'EmitSignal',
'dialogic_041':'ChangeScene',
'dialogic_042':'CallNode',
}
var batches = []
var building_timeline = true
signal selection_updated
signal batch_loaded
func _ready():
editor_reference = find_parent('EditorView')
connect("batch_loaded", self, '_on_batch_loaded')
var modifier = ''
var _scale = get_constant("inspector_margin", "Editor")
_scale = _scale * 0.125
$ScrollContainer.rect_min_size.x = 180
if _scale == 1.25:
modifier = '-1.25'
$ScrollContainer.rect_min_size.x = 200
if _scale == 1.5:
modifier = '-1.25'
$ScrollContainer.rect_min_size.x = 200
if _scale == 1.75:
modifier = '-1.25'
$ScrollContainer.rect_min_size.x = 390
if _scale == 2:
modifier = '-2'
$ScrollContainer.rect_min_size.x = 390
# We connect all the event buttons to the event creation functions
for c in range(0, 5):
for button in get_node("ScrollContainer/EventContainer/Grid"+str(c+1)).get_children():
# Question
if button.event_id == 'dialogic_010':
button.connect('pressed', self, "_on_ButtonQuestion_pressed", [])
# Condition
elif button.event_id == 'dialogic_012':
button.connect('pressed', self, "_on_ButtonCondition_pressed", [])
else:
button.connect('pressed', self, "_create_event_button_pressed", [button.event_id])
var style = $TimelineArea.get('custom_styles/bg')
style.set('bg_color', get_color("dark_color_1", "Editor"))
update_custom_events()
$TimelineArea.connect('resized', self, 'add_extra_scroll_area_to_timeline', [])
# handles dragging/moving of events
func _process(delta):
if moving_piece != null:
var current_position = get_global_mouse_position()
var node_position = moving_piece.rect_global_position.y
var height = get_block_height(moving_piece)
var up_offset = get_block_height(get_block_above(moving_piece))
var down_offset = get_block_height(get_block_below(moving_piece))
if up_offset != null:
up_offset = (up_offset / 2) + 5
if current_position.y < node_position - up_offset:
move_block(moving_piece, 'up')
piece_was_dragged = true
if down_offset != null:
down_offset = height + (down_offset / 2) + 5
if current_position.y > node_position + down_offset:
move_block(moving_piece, 'down')
piece_was_dragged = true
# SIGNAL handles input on the events mainly for selection and moving events
func _on_event_block_gui_input(event, item: Node):
if event is InputEventMouseButton and event.button_index == 1:
if (not event.is_pressed()):
if (piece_was_dragged and moving_piece != null and move_start_position):
var to_position = moving_piece.get_index()
if move_start_position != to_position:
# move it back so the DO action works. (Kinda stupid but whatever)
move_block_to_index(to_position, move_start_position)
TimelineUndoRedo.create_action("[D] Moved event (type '"+moving_piece.event_data.event_id+"').")
TimelineUndoRedo.add_do_method(self, "move_block_to_index", move_start_position, to_position)
TimelineUndoRedo.add_undo_method(self, "move_block_to_index", to_position, move_start_position)
TimelineUndoRedo.commit_action()
move_start_position = null
if (moving_piece != null):
indent_events()
moving_piece = null
elif event.is_pressed():
moving_piece = item
move_start_position = moving_piece.get_index()
if not _is_item_selected(item):
pass#piece_was_dragged = true
else:
piece_was_dragged = false
select_item(item)
## *****************************************************************************
## SHORTCUTS
## *****************************************************************************
func _input(event):
# some shortcuts need to get handled in the common input event
# especially CTRL-based
# because certain godot controls swallow events (like textedit)
# we protect this with is_visible_in_tree to not
# invoke a shortcut by accident
if get_focus_owner() is TextEdit:
return
if (event is InputEventKey and event is InputEventWithModifiers and is_visible_in_tree()):
# CTRL Z # UNDO
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_Z
and event.echo == false
):
TimelineUndoRedo.undo()
indent_events()
get_tree().set_input_as_handled()
if (event is InputEventKey and event is InputEventWithModifiers and is_visible_in_tree()):
# CTRL +SHIFT+ Z # REDO
if (event.pressed
and event.alt == false
and event.shift == true
and event.control == true
and event.scancode == KEY_Z
and event.echo == false
) or (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_Y
and event.echo == false):
TimelineUndoRedo.redo()
indent_events()
get_tree().set_input_as_handled()
if (event is InputEventKey and event is InputEventWithModifiers and is_visible_in_tree()):
# CTRL UP
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == false
and event.scancode == KEY_UP
and event.echo == false
):
# select previous
if (len(selected_items) == 1):
var prev = max(0, selected_items[0].get_index() - 1)
var prev_node = timeline.get_child(prev)
if (prev_node != selected_items[0]):
selected_items = []
select_item(prev_node)
get_tree().set_input_as_handled()
# CTRL DOWN
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == false
and event.scancode == KEY_DOWN
and event.echo == false
):
# select next
if (len(selected_items) == 1):
var next = min(timeline.get_child_count() - 1, selected_items[0].get_index() + 1)
var next_node = timeline.get_child(next)
if (next_node != selected_items[0]):
selected_items = []
select_item(next_node)
get_tree().set_input_as_handled()
# CTRL DELETE
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == false
and event.scancode == KEY_DELETE
and event.echo == false
):
if (len(selected_items) != 0):
var events_indexed = get_events_indexed(selected_items)
TimelineUndoRedo.create_action("[D] Deleting "+str(len(selected_items))+" event(s).")
TimelineUndoRedo.add_do_method(self, "delete_events_indexed", events_indexed)
TimelineUndoRedo.add_undo_method(self, "add_events_indexed", events_indexed)
TimelineUndoRedo.commit_action()
get_tree().set_input_as_handled()
# CTRL T
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_T
and event.echo == false
):
var at_index = -1
if selected_items:
at_index = selected_items[-1].get_index()+1
else:
at_index = timeline.get_child_count()
TimelineUndoRedo.create_action("[D] Add Text event.")
TimelineUndoRedo.add_do_method(self, "create_event", "dialogic_000", {'no-data': true}, true, at_index, true)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", at_index, 1)
TimelineUndoRedo.commit_action()
get_tree().set_input_as_handled()
# CTRL A
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_A
and event.echo == false
):
if (len(selected_items) != 0):
select_all_items()
get_tree().set_input_as_handled()
# CTRL SHIFT A
if (event.pressed
and event.alt == false
and event.shift == true
and event.control == true
and event.scancode == KEY_A
and event.echo == false
):
if (len(selected_items) != 0):
deselect_all_items()
get_tree().set_input_as_handled()
# CTRL C
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_C
and event.echo == false
):
copy_selected_events()
get_tree().set_input_as_handled()
# CTRL V
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_V
and event.echo == false
):
var events_list = paste_check()
var paste_position = -1
if selected_items:
paste_position = selected_items[-1].get_index()
else:
paste_position = timeline.get_child_count()-1
TimelineUndoRedo.create_action("[D] Pasting "+str(len(events_list))+" event(s).")
TimelineUndoRedo.add_do_method(self, "add_events_at_index", events_list, paste_position)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", paste_position+1, len(events_list))
TimelineUndoRedo.commit_action()
get_tree().set_input_as_handled()
# CTRL X
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_X
and event.echo == false
):
var events_indexed = get_events_indexed(selected_items)
TimelineUndoRedo.create_action("[D] Cut "+str(len(selected_items))+" event(s).")
TimelineUndoRedo.add_do_method(self, "cut_events_indexed", events_indexed)
TimelineUndoRedo.add_undo_method(self, "add_events_indexed", events_indexed)
TimelineUndoRedo.commit_action()
get_tree().set_input_as_handled()
# CTRL D
if (event.pressed
and event.alt == false
and event.shift == false
and event.control == true
and event.scancode == KEY_D
and event.echo == false
):
if len(selected_items) > 0:
var events = get_events_indexed(selected_items).values()
var at_index = selected_items[-1].get_index()
TimelineUndoRedo.create_action("[D] Duplicate "+str(len(events))+" event(s).")
TimelineUndoRedo.add_do_method(self, "add_events_at_index", events, at_index)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", at_index, len(events))
TimelineUndoRedo.commit_action()
get_tree().set_input_as_handled()
func _unhandled_key_input(event):
if (event is InputEventWithModifiers):
# ALT UP
if (event.pressed
and event.alt == true
and event.shift == false
and event.control == false
and event.scancode == KEY_UP
and event.echo == false
):
# move selected up
if (len(selected_items) == 1):
move_block(selected_items[0], "up")
indent_events()
get_tree().set_input_as_handled()
# ALT DOWN
if (event.pressed
and event.alt == true
and event.shift == false
and event.control == false
and event.scancode == KEY_DOWN
and event.echo == false
):
# move selected down
if (len(selected_items) == 1):
move_block(selected_items[0], "down")
indent_events()
get_tree().set_input_as_handled()
## *****************************************************************************
## DELETING, COPY, PASTE
## *****************************************************************************
func get_events_indexed(events:Array) -> Dictionary:
var indexed_dict = {}
for event in events:
indexed_dict[event.get_index()] = event.event_data.duplicate()
return indexed_dict
func select_indexed_events(indexed_events:Dictionary) -> void:
selected_items = []
for event_index in indexed_events.keys():
selected_items.append(timeline.get_child(event_index))
func add_events_indexed(indexed_events:Dictionary) -> void:
var indexes = indexed_events.keys()
indexes.sort()
var events = []
for event_idx in indexes:
deselect_all_items()
events.append(create_event(indexed_events[event_idx].event_id, indexed_events[event_idx]))
timeline.move_child(events[-1], event_idx)
selected_items = events
visual_update_selection()
func delete_events_indexed(indexed_events:Dictionary) -> void:
select_indexed_events(indexed_events)
delete_selected_events()
func delete_selected_events():
if len(selected_items) == 0:
return
# get next element
var next = min(timeline.get_child_count() - 1, selected_items[-1].get_index() + 1)
var next_node = timeline.get_child(next)
if _is_item_selected(next_node):
next_node = null
for event in selected_items:
event.get_parent().remove_child(event)
event.queue_free()
# select next
if (next_node != null):
select_item(next_node, false)
else:
if (timeline.get_child_count() > 0):
next_node = timeline.get_child(max(0, timeline.get_child_count() - 1))
if (next_node != null):
select_item(next_node, false)
else:
deselect_all_items()
indent_events()
func cut_selected_events():
copy_selected_events()
delete_selected_events()
func cut_events_indexed(indexed_events:Dictionary) -> void:
select_indexed_events(indexed_events)
cut_selected_events()
func copy_selected_events():
if len(selected_items) == 0:
return
var event_copy_array = []
for item in selected_items:
event_copy_array.append(item.event_data)
OS.clipboard = JSON.print(
{
"events":event_copy_array,
"dialogic_version": editor_reference.version_string,
"project_name": ProjectSettings.get_setting("application/config/name")
})
func paste_check():
var clipboard_parse = JSON.parse(OS.clipboard).result
if typeof(clipboard_parse) == TYPE_DICTIONARY:
if clipboard_parse.has("dialogic_version"):
if clipboard_parse['dialogic_version'] != editor_reference.version_string:
print("[D] Be careful when copying from older versions!")
if clipboard_parse.has("project_name"):
if clipboard_parse['project_name'] != ProjectSettings.get_setting("application/config/name"):
print("[D] Be careful when copying from another project!")
if clipboard_parse.has('events'):
return clipboard_parse['events']
func remove_events_at_index(at_index:int, amount:int = 1)-> void:
selected_items = []
for i in range(0, amount):
selected_items.append(timeline.get_child(at_index + i))
delete_selected_events()
func add_events_at_index(event_list:Array, at_index:int) -> void:
if at_index != -1:
event_list.invert()
selected_items = [timeline.get_child(at_index)]
else:
selected_items = []
var new_items = []
for item in event_list:
if typeof(item) == TYPE_DICTIONARY and item.has('event_id'):
new_items.append(create_event(item['event_id'], item))
selected_items = new_items
sort_selection()
visual_update_selection()
indent_events()
func paste_events_indexed(indexed_events):
pass
func duplicate_events_indexed(indexed_events):
pass
## *****************************************************************************
## BLOCK SELECTION
## *****************************************************************************
func _is_item_selected(item: Node):
return item in selected_items
func select_item(item: Node, multi_possible:bool = true):
if item == null:
return
if Input.is_key_pressed(KEY_CONTROL) and multi_possible:
# deselect the item if it is selected
if _is_item_selected(item):
selected_items.erase(item)
else:
selected_items.append(item)
elif Input.is_key_pressed(KEY_SHIFT) and multi_possible:
if len(selected_items) == 0:
selected_items = [item]
else:
var index = selected_items[-1].get_index()
var goal_idx = item.get_index()
while true:
if index < goal_idx: index += 1
else: index -= 1
if not timeline.get_child(index) in selected_items:
selected_items.append(timeline.get_child(index))
if index == goal_idx:
break
else:
if len(selected_items) == 1:
if _is_item_selected(item):
selected_items.erase(item)
else:
selected_items = [item]
else:
selected_items = [item]
sort_selection()
visual_update_selection()
# checks all the events and sets their styles (selected/deselected)
func visual_update_selection():
for item in timeline.get_children():
item.visual_deselect()
for item in selected_items:
item.visual_select()
## Sorts the selection using 'custom_sort_selection'
func sort_selection():
selected_items.sort_custom(self, 'custom_sort_selection')
## Compares two event blocks based on their position in the timeline
func custom_sort_selection(item1, item2):
return item1.get_index() < item2.get_index()
## Helpers
func select_all_items():
selected_items = []
for event in timeline.get_children():
selected_items.append(event)
visual_update_selection()
func deselect_all_items():
selected_items = []
visual_update_selection()
## *****************************************************************************
## SPECIAL BLOCK OPERATIONS
## *****************************************************************************
# SIGNAL handles the actions of the small menu on the right
func _on_event_options_action(action: String, item: Node):
### WORK TODO
if action == "remove":
if len(selected_items) != 1 or (len(selected_items) == 1 and selected_items[0] != item):
select_item(item, false)
delete_selected_events()
else:
move_block(item, action)
indent_events()
func delete_event(event):
event.get_parent().remove_child(event)
event.queue_free()
## *****************************************************************************
## CREATING NEW EVENTS USING THE BUTTONS
## *****************************************************************************
# Event Creation signal for buttons
func _create_event_button_pressed(event_id):
var at_index = -1
if selected_items:
at_index = selected_items[-1].get_index()+1
else:
at_index = timeline.get_child_count()
TimelineUndoRedo.create_action("[D] Add event.")
TimelineUndoRedo.add_do_method(self, "create_event", event_id, {'no-data': true}, true, at_index, true)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", at_index, 1)
TimelineUndoRedo.commit_action()
scroll_to_piece(at_index)
indent_events()
# the Question button adds multiple blocks
func _on_ButtonQuestion_pressed() -> void:
var at_index = -1
if selected_items:
at_index = selected_items[-1].get_index()+1
else:
at_index = timeline.get_child_count()
TimelineUndoRedo.create_action("[D] Add question events.")
TimelineUndoRedo.add_do_method(self, "create_question", at_index)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", at_index, 4)
TimelineUndoRedo.commit_action()
func create_question(at_position):
if at_position == 0: selected_items = []
else: selected_items = [timeline.get_child(at_position-1)]
if len(selected_items) != 0:
# Events are added bellow the selected node
# So we must reverse the adding order
create_event("dialogic_013", {'no-data': true}, true)
create_event("dialogic_011", {'no-data': true}, true)
create_event("dialogic_011", {'no-data': true}, true)
create_event("dialogic_010", {'no-data': true}, true)
else:
create_event("dialogic_010", {'no-data': true}, true)
create_event("dialogic_011", {'no-data': true}, true)
create_event("dialogic_011", {'no-data': true}, true)
create_event("dialogic_013", {'no-data': true}, true)
# the Condition button adds multiple blocks
func _on_ButtonCondition_pressed() -> void:
var at_index = -1
if selected_items:
at_index = selected_items[-1].get_index()+1
else:
at_index = timeline.get_child_count()
TimelineUndoRedo.create_action("[D] Add condition events.")
TimelineUndoRedo.add_do_method(self, "create_condition", at_index)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", at_index, 2)
TimelineUndoRedo.commit_action()
func create_condition(at_position):
if at_position == 0: selected_items = []
else: selected_items = [timeline.get_child(at_position-1)]
if len(selected_items) != 0:
# Events are added bellow the selected node
# So we must reverse the adding order
create_event("dialogic_013", {'no-data': true}, true)
create_event("dialogic_012", {'no-data': true}, true)
else:
create_event("dialogic_012", {'no-data': true}, true)
create_event("dialogic_013", {'no-data': true}, true)
func update_custom_events() -> void:
## CLEANUP
custom_events = {}
# cleaning the 'old' buttons
for child in custom_events_container.get_children():
child.queue_free()
var path:String = "res://dialogic/custom-events"
var dir = Directory.new()
if dir.open(path) == OK:
dir.list_dir_begin()
var file_name = dir.get_next()
# goes through all the folders in the custom events folder
while file_name != "":
# if it found a folder
if dir.current_is_dir() and not file_name in ['.', '..']:
# look through that folder
#print("Found custom event folder: " + file_name)
var event = load(path.plus_file(file_name).plus_file('EventBlock.tscn')).instance()
if event:
custom_events[event.event_data['event_id']] = {
'event_block_scene' :path.plus_file(file_name).plus_file('EventBlock.tscn'),
'event_name' : event.event_name,
'event_icon' : event.event_icon
}
event.queue_free()
else:
print("[D] An error occurred when trying to access a custom event.")
else:
pass # files in the directory are ignored
file_name = dir.get_next()
# After we finishing checking, if any events exist, show the panel
if custom_events.size() == 0:
custom_events_container.hide()
$ScrollContainer/EventContainer/CustomEventsHeadline.hide()
else:
custom_events_container.show()
$ScrollContainer/EventContainer/CustomEventsHeadline.show()
else:
print("[D] An error occurred when trying to access the custom events folder.")
## VISUAL UPDATE
# adding new ones
for custom_event_id in custom_events.keys():
var button = load('res://addons/dialogic/Editor/TimelineEditor/SmallEventButton.tscn').instance()
#button.set_script(preload("EventButton.gd"))
button.event_id = custom_event_id
button.visible_name = custom_events[custom_event_id]['event_name']
button.self_modulate = Color('#494d58')
button.hint_tooltip = custom_events[custom_event_id]['event_name']
if custom_events[custom_event_id]['event_icon']:
button.event_icon = custom_events[custom_event_id]['event_icon']
button.connect("pressed", self, "_create_event_button_pressed", [custom_event_id])
custom_events_container.add_child(button)
## *****************************************************************************
## DRAG AND DROP
## *****************************************************************************
# Creates a ghost event for drag and drop
func create_drag_and_drop_event(event_id: String):
var index = get_index_under_cursor()
var piece = create_event(event_id)
currently_draged_event_type = event_id
timeline.move_child(piece, index)
moving_piece = piece
piece_was_dragged = true
set_event_ignore_save(piece, true)
select_item(piece)
return piece
func drop_event():
if moving_piece != null:
var at_index = moving_piece.get_index()
moving_piece.queue_free()
TimelineUndoRedo.create_action("[D] Add event.")
TimelineUndoRedo.add_do_method(self, "create_event", currently_draged_event_type, {'no-data': true}, true, at_index, true)
TimelineUndoRedo.add_undo_method(self, "remove_events_at_index", at_index, 1)
TimelineUndoRedo.commit_action()
moving_piece = null
piece_was_dragged = false
indent_events()
add_extra_scroll_area_to_timeline()
func cancel_drop_event():
if moving_piece != null:
moving_piece = null
piece_was_dragged = false
delete_selected_events()
deselect_all_items()
## *****************************************************************************
## CREATING THE TIMELINE
## *****************************************************************************
# Adding an event to the timeline
func create_event(event_id: String, data: Dictionary = {'no-data': true} , indent: bool = false, at_index: int = -1, auto_select: bool = false):
var piece = null
# check if it's a custom event
if event_id in custom_events.keys():
piece = load(custom_events[event_id]['event_block_scene']).instance()
# check if it's a builtin event
elif event_id in id_to_scene_name.keys():
piece = load("res://addons/dialogic/Editor/Events/" + id_to_scene_name[event_id] + ".tscn").instance()
# else use dummy event
else:
piece = load("res://addons/dialogic/Editor/Events/DummyEvent.tscn").instance()
# load the piece with data
piece.editor_reference = editor_reference
if data.has('no-data') == false:
piece.event_data = data
if at_index == -1:
if len(selected_items) != 0:
timeline.add_child_below_node(selected_items[0], piece)
else:
timeline.add_child(piece)
else:
timeline.add_child(piece)
timeline.move_child(piece, at_index)
piece.connect("option_action", self, '_on_event_options_action', [piece])
piece.connect("gui_input", self, '_on_event_block_gui_input', [piece])
events_warning.visible = false
if auto_select:
select_item(piece, false)
# Spacing
add_extra_scroll_area_to_timeline()
# Indent on create
if indent:
indent_events()
return piece
func load_timeline(filename: String):
clear_timeline()
update_custom_events()
if timeline_file != filename:
TimelineUndoRedo.clear_history()
building_timeline = true
timeline_file = filename
var data = DialogicResources.get_timeline_json(filename)
if data['metadata'].has('name'):
timeline_name = data['metadata']['name']
else:
timeline_name = data['metadata']['file']
data = data['events']
var page = 1
var batch_size = 12
while batch_events(data, batch_size, page).size() != 0:
batches.append(batch_events(data, batch_size, page))
page += 1
load_batch(batches)
# Reset the scroll position
$TimelineArea.scroll_vertical = 0
func batch_events(array, size, batch_number):
return array.slice((batch_number - 1) * size, batch_number * size - 1)
func load_batch(data):
#print('[D] Loading batch')
var current_batch = batches.pop_front()
if current_batch:
for i in current_batch:
create_event(i['event_id'], i)
emit_signal("batch_loaded")
func _on_batch_loaded():
if batches.size() > 0:
yield(get_tree().create_timer(0.01), "timeout")
load_batch(batches)
else:
events_warning.visible = false
indent_events()
building_timeline = false
add_extra_scroll_area_to_timeline()
func clear_timeline():
deselect_all_items()
for event in timeline.get_children():
event.free()
## *****************************************************************************
## BLOCK GETTERS
## *****************************************************************************
func get_block_above(block):
var block_index = block.get_index()
var item = null
if block_index > 0:
item = timeline.get_child(block_index - 1)
return item
func get_block_below(block):
var block_index = block.get_index()
var item = null
if block_index < timeline.get_child_count() - 1:
item = timeline.get_child(block_index + 1)
return item
func get_block_height(block):
if block != null:
return block.rect_size.y
else:
return null
func get_index_under_cursor():
var current_position = get_global_mouse_position()
var top_pos = 0
for i in range(timeline.get_child_count()):
var c = timeline.get_child(i)
if c.rect_global_position.y < current_position.y:
top_pos = i
return top_pos
# ordering blocks in timeline
func move_block(block, direction):
var block_index = block.get_index()
if direction == 'up':
if block_index > 0:
timeline.move_child(block, block_index - 1)
return true
if direction == 'down':
timeline.move_child(block, block_index + 1)
return true
return false
func move_block_to_index(block_index, index):
timeline.move_child(timeline.get_child(block_index), index)
## *****************************************************************************
## TIMELINE CREATION AND SAVING
## *****************************************************************************
func create_timeline():
timeline_file = 'timeline-' + str(OS.get_unix_time()) + '.json'
var timeline = {
"events": [],
"metadata":{
"dialogic-version": editor_reference.version_string,
"file": timeline_file
}
}
DialogicResources.set_timeline(timeline)
return timeline
# Saving
func generate_save_data():
var info_to_save = {
'metadata': {
'dialogic-version': editor_reference.version_string,
'name': timeline_name,
'file': timeline_file
},
'events': []
}
for event in timeline.get_children():
# Checking that the event is not waiting to be removed
# or that it is not a drag and drop placeholder
if not get_event_ignore_save(event) and event.is_queued_for_deletion() == false:
info_to_save['events'].append(event.event_data)
return info_to_save
func set_event_ignore_save(event: Node, ignore: bool):
event.ignore_save = ignore
func get_event_ignore_save(event: Node) -> bool:
return event.ignore_save
func save_timeline() -> void:
if timeline_file != '' and building_timeline == false:
var info_to_save = generate_save_data()
DialogicResources.set_timeline(info_to_save)
#print('[+] Saving: ' , timeline_file)
## *****************************************************************************
## UTILITIES/HELPERS
## *****************************************************************************
# Scrolling
func scroll_to_piece(piece_index) -> void:
var height = 0
for i in range(0, piece_index):
height += $TimelineArea/TimeLine.get_child(i).rect_size.y
$TimelineArea.scroll_vertical = height
# Event Indenting
func indent_events() -> void:
# Now indenting
var indent: int = 0
var starter: bool = false
var event_list: Array = timeline.get_children()
var question_index: int = 0
var question_indent = {}
if event_list.size() < 2:
return
# Resetting all the indents
for event in event_list:
var indent_node
event.set_indent(0)
# Adding new indents
for event in event_list:
# since there are indicators now, not all elements
# in this list have an event_data property
if (not "event_data" in event):
continue
if event.event_data['event_id'] == 'dialogic_011':
if question_index > 0:
indent = question_indent[question_index] + 1
starter = true
elif event.event_data['event_id'] == 'dialogic_010' or event.event_data['event_id'] == 'dialogic_012':
indent += 1
starter = true
question_index += 1
question_indent[question_index] = indent
elif event.event_data['event_id'] == 'dialogic_013':
if question_indent.has(question_index):
indent = question_indent[question_index]
indent -= 1
question_index -= 1
if indent < 0:
indent = 0
if indent > 0:
# Keep old behavior for items without template
if starter:
event.set_indent(indent - 1)
else:
event.set_indent(indent)
starter = false
# called from the toolbar
func fold_all_nodes():
for event in timeline.get_children():
event.set_expanded(false)
add_extra_scroll_area_to_timeline()
# called from the toolbar
func unfold_all_nodes():
for event in timeline.get_children():
event.set_expanded(true)
add_extra_scroll_area_to_timeline()
func add_extra_scroll_area_to_timeline():
if timeline.get_children().size() > 4:
timeline.rect_min_size.y = 0
timeline.rect_size.y = 0
if timeline.rect_size.y + 200 > $TimelineArea.rect_size.y:
timeline.rect_min_size = Vector2(0, timeline.rect_size.y + 200)