extends Container class_name TopicDraggable const SCROLL_SPEED = 25.0 onready var topic_feed: VBoxContainer = get_node("..") onready var scroll_container: ScrollContainer = get_node("../..") onready var v_scroll: VScrollBar = get_node("../../_v_scroll") onready var editable = get_node("TopicEditable") onready var tween = get_node("Tween") var global_position setget _set_global_position var local_position setget _set_local_position var _in_fit_in_animation = false var _previous_global_position var _previous_scroll var _offset var _something_passed = false func _ready(): pass func _gui_input(event): if event is InputEventMouseButton and event.button_index == BUTTON_LEFT: if event.is_pressed(): if event.shift: topic_feed._toggle_selection_result = _set_selection(null) topic_feed._shift_hold = true else: _deselect_all() if not editable.edit_mode: if event.doubleclick: editable._enable_edit_mode() editable.text_edit.grab_focus() yield(get_tree(), "idle_frame") editable.text_edit.select_all() elif !_in_fit_in_animation: _set_as_grabbed() func _input(event): if event is InputEventMouseButton and event.button_index == BUTTON_LEFT: if event.is_pressed(): if !_is_mouse_on_topic() and editable.edit_mode: editable.text_edit._confirm() else: if _is_grabbed(): _fit_in_animation() topic_feed._shift_hold = false if event is InputEventMouseMotion: if _is_grabbed(): _move_block(event) topic_feed.check_all_passings() if topic_feed._shift_hold and _is_mouse_on_topic(): # warning-ignore:return_value_discarded _set_selection(topic_feed._toggle_selection_result) if event is InputEventMouseButton: if _is_grabbed(): var scroll_value = 0.0 if event.button_index == BUTTON_WHEEL_UP: scroll_value = -SCROLL_SPEED elif event.button_index == BUTTON_WHEEL_DOWN: scroll_value = SCROLL_SPEED if scroll_value != 0.0: scroll_container.scroll_vertical += scroll_value _previous_scroll = scroll_container.scroll_vertical topic_feed.check_all_passings() func _set_selection(to) -> bool: # If 'to' is null, selection is toggled. if topic_feed.selected_topics.has(self) and to != true: topic_feed.selected_topics.erase(self) editable._update_color() return false elif !topic_feed.selected_topics.has(self) and to != false: topic_feed.selected_topics.append(self) editable._update_color() return true else: return topic_feed.selected_topics.has(self) func _deselect_all(): topic_feed.selected_topics.clear() topic_feed.update_all_colors() func _set_as_grabbed(): var rect = editable.get_global_rect() rect_min_size.y = editable.rect_size.y editable.set_as_toplevel(true) editable.mouse_filter = MOUSE_FILTER_IGNORE editable.rect_position = rect.position topic_feed.currently_grabbed_topic = self _previous_global_position = editable.rect_global_position _previous_scroll = scroll_container.scroll_vertical _offset = 0 _move_block(InputEventMouseMotion.new()) func _fit_in_animation(): _in_fit_in_animation = true topic_feed.currently_grabbed_topic = null var overhead = _topic_overhead(rect_global_position.y) var from = editable.rect_global_position var to if overhead > 0.0: var sc_rect = scroll_container.get_global_rect() to = Vector2( sc_rect.position.x, sc_rect.end.y - rect_size.y ) elif overhead < 0.0: to = scroll_container.rect_global_position else: to = rect_global_position - Vector2(0.0, overhead) tween.stop_all() tween.interpolate_property(self, "global_position", from, to, 0.12, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT ) if overhead != 0.0: tween.interpolate_property(scroll_container, "scroll_vertical", scroll_container.scroll_vertical, scroll_container.scroll_vertical + overhead, 0.12, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT ) tween.start() yield(tween, "tween_completed") editable.set_as_toplevel(false) editable.mouse_filter = MOUSE_FILTER_PASS rect_min_size.y = 0 editable.rect_position = Vector2.ZERO _in_fit_in_animation = false func _move_block(event): _offset += event.relative.y var virtual_position = _previous_global_position + Vector2(0.0, _offset) var overhead = _topic_overhead(virtual_position.y) virtual_position.y -= overhead if get_index() == topic_feed.get_child_count() - 2: if virtual_position.y > rect_global_position.y: virtual_position.y = rect_global_position.y _set_global_position(virtual_position) scroll_container.scroll_vertical = _previous_scroll + overhead # How much topic is out of scroll_container bounds. func _topic_overhead(y_pos): var scroll_rect = scroll_container.get_global_rect() scroll_rect.size.y -= editable.rect_size.y var overhead = 0 # Higher? if y_pos < scroll_rect.position.y: overhead = y_pos - scroll_rect.position.y # Lower? if y_pos > scroll_rect.end.y: overhead = y_pos - scroll_rect.end.y return overhead func _is_mouse_on_topic() -> bool: var on_topic = get_global_rect().has_point(get_global_mouse_position()) return _is_mouse_on_topic_feed() and on_topic func _is_mouse_on_topic_feed() -> bool: return scroll_container.get_global_rect().has_point(get_global_mouse_position()) func _is_grabbed() -> bool: return topic_feed.currently_grabbed_topic == self func set_previous_position(): _previous_global_position = editable.rect_global_position func check_for_passing() -> bool: var dragged: TopicDraggable = topic_feed.currently_grabbed_topic if dragged == self or dragged == null: return false var dir = sign(dragged.get_index() - get_index()) var self_rect = get_global_rect() var self_half_size = self_rect.size.y * 0.5 # Center line. var self_coll_line = self_rect.position.y + self_half_size var dragged_rect = dragged.editable.get_global_rect() var dragged_half_size = dragged_rect.size.y * 0.5 var dragged_half_pos = dragged_rect.position.y + dragged_half_size # Top or bottom line, depending on if moved up or down. var dragged_coll_line = dragged_half_pos + dragged_half_size * -dir # +1 if dragged_coll_line is below; -1 if above. var coll_dir = sign(self_coll_line - dragged_coll_line) if coll_dir == dir: topic_feed.set_all_previous_positions() topic_feed.move_child( topic_feed.currently_grabbed_topic, get_index() ) _something_passed = true Data.topics.dirty_flag = true return true return false func setup_tween(): if _something_passed: var _previous_local_position = _previous_global_position - rect_global_position tween.stop_all() tween.interpolate_property(self, "local_position", _previous_local_position, Vector2.ZERO, 0.15, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT ) tween.start() _something_passed = false yield(tween, "tween_completed") func _set_global_position(value): editable.set_global_position(value) func _set_local_position(value): editable.rect_position = value