2022-06-02 04:13:43 +02:00
# ##############################################################################
#(G)odot (U)nit (T)est class
#
# ##############################################################################
# The MIT License (MIT)
# =====================
#
# Copyright (c) 2020 Tom "Butch" Wesley
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ##############################################################################
# Description
# -----------
# This class sends input to one or more recievers. The receivers' _input,
# _unhandled_input, and _gui_input are called sending InputEvent* events.
# InputEvents can be sent via the helper methods or a custom made InputEvent
# can be sent via send_event(...)
#
# ##############################################################################
#extends "res://addons/gut/input_factory.gd"
# Implemented InputEvent* convenience methods
# InputEventAction
# InputEventKey
# InputEventMouseButton
# InputEventMouseMotion
# Yet to implement InputEvents
# InputEventJoypadButton
# InputEventJoypadMotion
# InputEventMagnifyGesture
# InputEventMIDI
# InputEventPanGesture
# InputEventScreenDrag
# InputEventScreenTouch
class InputQueueItem :
extends Node
var events = [ ]
var time_delay = null
var frame_delay = null
var _waited_frames = 0
var _is_ready = false
var _delay_started = false
signal event_ready
# TODO should this be done in _physics_process instead or should it be
# configurable?
func _physics_process ( delta ) :
if frame_delay > 0 and _delay_started :
_waited_frames + = 1
if _waited_frames > = frame_delay :
emit_signal ( " event_ready " )
2022-11-09 22:52:54 +01:00
func _init ( t_delay , f_delay ) :
2022-06-02 04:13:43 +02:00
time_delay = t_delay
frame_delay = f_delay
_is_ready = time_delay == 0 and frame_delay == 0
func _on_time_timeout ( ) :
_is_ready = true
emit_signal ( " event_ready " )
func _delay_timer ( t ) :
return Engine . get_main_loop ( ) . root . get_tree ( ) . create_timer ( t )
func is_ready ( ) :
return _is_ready
func start ( ) :
_delay_started = true
if time_delay > 0 :
var t = _delay_timer ( time_delay )
2022-11-09 21:57:46 +01:00
t . connect ( " timeout " , Callable ( self , " _on_time_timeout " ) )
2022-06-02 04:13:43 +02:00
# ##############################################################################
#
# ##############################################################################
var _utils = load ( " res://addons/gut/utils.gd " ) . get_instance ( )
var InputFactory = load ( " res://addons/gut/input_factory.gd " )
2022-11-09 21:57:46 +01:00
const INPUT_WARN = " If using Input as a reciever it will not respond to *_down events until a *_up event is recieved. Call the appropriate *_up event or use super.hold_for(...) to automatically release after some duration. "
2022-06-02 04:13:43 +02:00
var _lgr = _utils . get_logger ( )
var _receivers = [ ]
var _input_queue = [ ]
var _next_queue_item = null
# used by mouse_relative_motion. These use this instead of _last_event since
# it is logical to have a series of events happen between mouse motions.
var _last_mouse_motion = null
# used by hold_for and echo.
var _last_event = null
# indexed by scancode, each entry contains a boolean value indicating the
# last emitted "pressed" value for that scancode.
var _pressed_keys = { }
var _pressed_actions = { }
var _pressed_mouse_buttons = { }
signal idle
func _init ( r = null ) :
if r != null :
add_receiver ( r )
func _send_event ( event ) :
if event is InputEventKey :
if ( event . pressed and ! event . echo ) and is_key_pressed ( event . scancode ) :
_lgr . warn (
str (
" InputSender: key_down called for " ,
event . as_text ( ) ,
" when that key is already pressed. " ,
INPUT_WARN
)
)
_pressed_keys [ event . scancode ] = event . pressed
elif event is InputEventAction :
if event . pressed and is_action_pressed ( event . action ) :
_lgr . warn (
str (
" InputSender: action_down called for " ,
event . action ,
" when that action is already pressed. " ,
INPUT_WARN
)
)
_pressed_actions [ event . action ] = event . pressed
elif event is InputEventMouseButton :
if event . pressed and is_mouse_button_pressed ( event . button_index ) :
_lgr . warn (
str (
" InputSender: mouse_button_down called for " ,
event . button_index ,
" when that mouse button is already pressed. " ,
INPUT_WARN
)
)
_pressed_mouse_buttons [ event . button_index ] = event
for r in _receivers :
if r == Input :
Input . parse_input_event ( event )
else :
if r . has_method ( " _input " ) :
r . _input ( event )
if r . has_method ( " _gui_input " ) :
r . _gui_input ( event )
if r . has_method ( " _unhandled_input " ) :
r . _unhandled_input ( event )
func _send_or_record_event ( event ) :
_last_event = event
if _next_queue_item != null :
_next_queue_item . events . append ( event )
else :
_send_event ( event )
func _on_queue_item_ready ( item ) :
for event in item . events :
_send_event ( event )
var done_event = _input_queue . pop_front ( )
done_event . queue_free ( )
if _input_queue . size ( ) == 0 :
_next_queue_item = null
emit_signal ( " idle " )
else :
_input_queue [ 0 ] . start ( )
func _add_queue_item ( item ) :
2022-11-09 21:57:46 +01:00
item . connect ( " event_ready " , Callable ( self , " _on_queue_item_ready " ) . bind ( item ) )
2022-06-02 04:13:43 +02:00
_next_queue_item = item
_input_queue . append ( item )
Engine . get_main_loop ( ) . root . add_child ( item )
if _input_queue . size ( ) == 1 :
item . start ( )
func add_receiver ( obj ) :
_receivers . append ( obj )
func get_receivers ( ) :
return _receivers
func wait ( t ) :
if typeof ( t ) == TYPE_STRING :
var suffix = t . substr ( t . length ( ) - 1 , 1 )
var val = float ( t . rstrip ( " s " ) . rstrip ( " f " ) )
if suffix . to_lower ( ) == " s " :
wait_secs ( val )
elif suffix . to_lower ( ) == " f " :
wait_frames ( val )
else :
wait_secs ( t )
return self
func wait_frames ( num_frames ) :
var item = InputQueueItem . new ( 0 , num_frames )
_add_queue_item ( item )
return self
func wait_secs ( num_secs ) :
var item = InputQueueItem . new ( num_secs , 0 )
_add_queue_item ( item )
return self
# ------------------------------
# Event methods
# ------------------------------
func key_up ( which ) :
var event = InputFactory . key_up ( which )
_send_or_record_event ( event )
return self
func key_down ( which ) :
var event = InputFactory . key_down ( which )
_send_or_record_event ( event )
return self
func key_echo ( ) :
if _last_event != null and _last_event is InputEventKey :
var new_key = _last_event . duplicate ( )
new_key . echo = true
_send_or_record_event ( new_key )
return self
func action_up ( which , strength = 1.0 ) :
var event = InputFactory . action_up ( which , strength )
_send_or_record_event ( event )
return self
func action_down ( which , strength = 1.0 ) :
var event = InputFactory . action_down ( which , strength )
_send_or_record_event ( event )
return self
func mouse_left_button_down ( position , global_position = null ) :
var event = InputFactory . mouse_left_button_down ( position , global_position )
_send_or_record_event ( event )
return self
func mouse_left_button_up ( position , global_position = null ) :
var event = InputFactory . mouse_left_button_up ( position , global_position )
_send_or_record_event ( event )
return self
func mouse_double_click ( position , global_position = null ) :
var event = InputFactory . mouse_double_click ( position , global_position )
event . doubleclick = true
_send_or_record_event ( event )
return self
func mouse_right_button_down ( position , global_position = null ) :
var event = InputFactory . mouse_right_button_down ( position , global_position )
_send_or_record_event ( event )
return self
func mouse_right_button_up ( position , global_position = null ) :
var event = InputFactory . mouse_right_button_up ( position , global_position )
_send_or_record_event ( event )
return self
func mouse_motion ( position , global_position = null ) :
var event = InputFactory . mouse_motion ( position , global_position )
_last_mouse_motion = event
_send_or_record_event ( event )
return self
func mouse_relative_motion ( offset , speed = Vector2 ( 0 , 0 ) ) :
var event = InputFactory . mouse_relative_motion ( offset , _last_mouse_motion , speed )
_last_mouse_motion = event
_send_or_record_event ( event )
return self
func mouse_set_position ( position , global_position = null ) :
_last_mouse_motion = InputFactory . mouse_motion ( position , global_position )
return self
func send_event ( event ) :
_send_or_record_event ( event )
return self
func release_all ( ) :
for key in _pressed_keys :
if _pressed_keys [ key ] :
_send_event ( InputFactory . key_up ( key ) )
_pressed_keys . clear ( )
for key in _pressed_actions :
if _pressed_actions [ key ] :
_send_event ( InputFactory . action_up ( key ) )
_pressed_actions . clear ( )
for key in _pressed_mouse_buttons :
var event = _pressed_mouse_buttons [ key ] . duplicate ( )
if event . pressed :
2022-11-09 21:57:46 +01:00
event . button_pressed = false
2022-06-02 04:13:43 +02:00
_send_event ( event )
_pressed_mouse_buttons . clear ( )
func hold_for ( duration ) :
if _last_event != null and _last_event . pressed :
var next_event = _last_event . duplicate ( )
2022-11-09 21:57:46 +01:00
next_event . button_pressed = false
2022-06-02 04:13:43 +02:00
wait ( duration )
send_event ( next_event )
return self
func clear ( ) :
pass
_last_event = null
_last_mouse_motion = null
_next_queue_item = null
for item in _input_queue :
item . free ( )
_input_queue . clear ( )
_pressed_keys . clear ( )
_pressed_actions . clear ( )
_pressed_mouse_buttons . clear ( )
func is_idle ( ) :
return _input_queue . size ( ) == 0
func is_key_pressed ( which ) :
var event = InputFactory . key_up ( which )
return _pressed_keys . has ( event . scancode ) and _pressed_keys [ event . scancode ]
func is_action_pressed ( which ) :
return _pressed_actions . has ( which ) and _pressed_actions [ which ]
func is_mouse_button_pressed ( which ) :
return _pressed_mouse_buttons . has ( which ) and _pressed_mouse_buttons [ which ]