|
|
|
@@ -6,91 +6,94 @@
|
|
|
|
|
## is treated as a separate tab. You might also want to hide
|
|
|
|
|
## the horizontal scroll bar at the bottom, as it does nothing
|
|
|
|
|
|
|
|
|
|
@tool
|
|
|
|
|
extends ScrollContainer
|
|
|
|
|
|
|
|
|
|
signal tab_switched(tab)
|
|
|
|
|
|
|
|
|
|
onready var tabs_holder := MarginContainer.new()
|
|
|
|
|
onready var children := get_children()
|
|
|
|
|
onready var sizex := rect_size.x
|
|
|
|
|
@onready var tabs_holder := MarginContainer.new()
|
|
|
|
|
@onready var _initial_children := get_children()
|
|
|
|
|
@onready var sizex: float = size.x
|
|
|
|
|
|
|
|
|
|
export(int) var current_tab = 0
|
|
|
|
|
export(float) var swipe_threshold = 128.0
|
|
|
|
|
export(bool) var smooth_switch = true
|
|
|
|
|
export(float) var switch_power = 5.0
|
|
|
|
|
@export var current_tab: int = 0
|
|
|
|
|
@export var swipe_threshold: float = 128.0
|
|
|
|
|
@export var smooth_switch: bool = true
|
|
|
|
|
@export var switch_power: float = 5.0
|
|
|
|
|
|
|
|
|
|
var children: Array = []
|
|
|
|
|
var scroll_velocity := Vector2.ZERO
|
|
|
|
|
var scrolling := false
|
|
|
|
|
var target_scroll := 0.0
|
|
|
|
|
var current_scroll := 0.0
|
|
|
|
|
var prev_on_tab := false
|
|
|
|
|
var drag_init_pos := Vector2.ZERO
|
|
|
|
|
var scrolling := false
|
|
|
|
|
var target_scroll: float = 0.0
|
|
|
|
|
var current_scroll: float = 0.0
|
|
|
|
|
var prev_on_tab := false
|
|
|
|
|
var drag_init_pos := Vector2.ZERO
|
|
|
|
|
var swipe_threshold_reached := false
|
|
|
|
|
var scrolled_with_wheel := false
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
|
|
|
|
# add a tab holder to the container and move all the children to it
|
|
|
|
|
add_child(tabs_holder)
|
|
|
|
|
for child in children:
|
|
|
|
|
for child in _initial_children:
|
|
|
|
|
if(child is ScrollBar): continue
|
|
|
|
|
remove_child(child)
|
|
|
|
|
tabs_holder.add_child(child)
|
|
|
|
|
child.mouse_filter = Control.MOUSE_FILTER_PASS
|
|
|
|
|
children = tabs_holder.get_children()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# adjust all the properties of the containers
|
|
|
|
|
scroll_vertical_enabled = false
|
|
|
|
|
scroll_horizontal_enabled = true
|
|
|
|
|
tabs_holder.size_flags_vertical = 3
|
|
|
|
|
vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
|
|
|
|
horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
|
|
|
|
|
tabs_holder.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
|
|
|
tabs_holder.mouse_filter = Control.MOUSE_FILTER_PASS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# connect required signals
|
|
|
|
|
get_tree().get_root().connect("size_changed", self, "resize")
|
|
|
|
|
tabs_holder.connect("sort_children", self, "resize")
|
|
|
|
|
connect("gui_input", self, "_manage_input")
|
|
|
|
|
get_tree().get_root().size_changed.connect(resize)
|
|
|
|
|
tabs_holder.sort_children.connect(resize)
|
|
|
|
|
gui_input.connect(_manage_input)
|
|
|
|
|
resize()
|
|
|
|
|
switch_tab()
|
|
|
|
|
update_target_scroll(true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
|
|
|
if scrolling:
|
|
|
|
|
current_scroll = scroll_horizontal
|
|
|
|
|
else:
|
|
|
|
|
# smoothly scroll to the current tab
|
|
|
|
|
if prev_on_tab or scrolled_with_wheel: current_scroll = target_scroll
|
|
|
|
|
else: current_scroll += (target_scroll - scroll_horizontal) * 8.0 * delta
|
|
|
|
|
if prev_on_tab or scrolled_with_wheel:
|
|
|
|
|
current_scroll = target_scroll
|
|
|
|
|
else:
|
|
|
|
|
current_scroll += (target_scroll - scroll_horizontal) * 8.0 * delta
|
|
|
|
|
scroll_horizontal = current_scroll
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# prevent actual mouse wheel scrolling
|
|
|
|
|
prev_on_tab = abs(current_scroll - target_scroll) < 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func resize() -> void:
|
|
|
|
|
# handle window resizing
|
|
|
|
|
sizex = rect_size.x
|
|
|
|
|
tabs_holder.rect_min_size.x = sizex * children.size()
|
|
|
|
|
|
|
|
|
|
sizex = size.x
|
|
|
|
|
tabs_holder.custom_minimum_size.x = sizex * children.size()
|
|
|
|
|
|
|
|
|
|
var childi := 0
|
|
|
|
|
for child in children:
|
|
|
|
|
child.rect_size.x = sizex
|
|
|
|
|
child.rect_position.x = sizex * childi
|
|
|
|
|
child.size.x = sizex
|
|
|
|
|
child.position.x = sizex * childi
|
|
|
|
|
childi += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
update_target_scroll(true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _manage_input(event: InputEvent) -> void:
|
|
|
|
|
if event is InputEventMouseButton:
|
|
|
|
|
# when the drag is stopped, end scrolling
|
|
|
|
|
if event.button_index in [BUTTON_WHEEL_UP, BUTTON_WHEEL_DOWN, BUTTON_WHEEL_LEFT, BUTTON_WHEEL_RIGHT]:
|
|
|
|
|
if event.button_index in [MOUSE_BUTTON_WHEEL_UP, MOUSE_BUTTON_WHEEL_DOWN, MOUSE_BUTTON_WHEEL_LEFT, MOUSE_BUTTON_WHEEL_RIGHT]:
|
|
|
|
|
scrolled_with_wheel = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elif event is InputEventScreenDrag:
|
|
|
|
|
scroll_velocity = event.relative
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# check for swipe threshold
|
|
|
|
|
if not swipe_threshold_reached:
|
|
|
|
|
#scroll_horizontal = target_scroll
|
|
|
|
|
var diff = abs(drag_init_pos.x - event.position.x)
|
|
|
|
|
if diff > swipe_threshold:
|
|
|
|
|
if diff > swipe_threshold:
|
|
|
|
|
swipe_threshold_reached = true
|
|
|
|
|
else:
|
|
|
|
|
scroll_horizontal = target_scroll
|
|
|
|
@@ -98,7 +101,7 @@ func _manage_input(event: InputEvent) -> void:
|
|
|
|
|
# fake scroll, because for some reason, real scrolling stops
|
|
|
|
|
current_scroll -= event.relative.x
|
|
|
|
|
scroll_horizontal = current_scroll
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elif event is InputEventScreenTouch:
|
|
|
|
|
if event.is_pressed():
|
|
|
|
|
scrolling = true
|
|
|
|
@@ -107,28 +110,28 @@ func _manage_input(event: InputEvent) -> void:
|
|
|
|
|
scrolled_with_wheel = false
|
|
|
|
|
else:
|
|
|
|
|
end_scroll()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func end_scroll() -> void:
|
|
|
|
|
# calculate current tab, based on the horizontal scroll and the drag velocity
|
|
|
|
|
# calculate current tab, based on the horizontal scroll and the drag velocity
|
|
|
|
|
# and then switch to it
|
|
|
|
|
current_tab = int(clamp(round((scroll_horizontal - min(scroll_velocity.x * switch_power, sizex)) / sizex), 0, children.size()-1))
|
|
|
|
|
current_tab = int(clamp(round((scroll_horizontal - min(scroll_velocity.x * switch_power, sizex)) / sizex), 0, children.size() - 1))
|
|
|
|
|
switch_tab()
|
|
|
|
|
scroll_velocity = Vector2.ZERO
|
|
|
|
|
scrolling = false
|
|
|
|
|
|
|
|
|
|
func update_target_scroll(instant:bool=false) -> void:
|
|
|
|
|
|
|
|
|
|
func update_target_scroll(instant: bool = false) -> void:
|
|
|
|
|
# calculate horizontal scrolling required to fully switch to a single tab, with no overlaps
|
|
|
|
|
# if instant is true, also update the current scroll
|
|
|
|
|
target_scroll = current_tab * sizex
|
|
|
|
|
if instant:
|
|
|
|
|
if instant:
|
|
|
|
|
current_scroll = target_scroll
|
|
|
|
|
scroll_horizontal = current_scroll
|
|
|
|
|
scroll_horizontal = int(current_scroll)
|
|
|
|
|
else:
|
|
|
|
|
prev_on_tab = false
|
|
|
|
|
|
|
|
|
|
func switch_tab(tab:int=-1) -> void:
|
|
|
|
|
if tab >= 0:
|
|
|
|
|
|
|
|
|
|
func switch_tab(tab: int = -1) -> void:
|
|
|
|
|
if tab >= 0:
|
|
|
|
|
current_tab = tab
|
|
|
|
|
scrolled_with_wheel = false
|
|
|
|
|
update_target_scroll(!smooth_switch)
|
|
|
|
|
emit_signal("tab_switched", current_tab)
|
|
|
|
|
tab_switched.emit(current_tab)
|
|
|
|
|