diary/user_interface/topic_panel/topic_draggable.gd
2022-05-21 14:56:02 +02:00

253 lines
6.8 KiB
GDScript

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