@tool extends PanelContainer ########################### ## SETTINGS ########################### @export var auto_show:bool = true @export var animate:bool = true @export_file var custom_layout_file @export var set_tool_tip := true @export_group("Style") @export var separation:Vector2i = Vector2i(0,0) var style_background:StyleBoxFlat = null @export var background:StyleBoxFlat = null: set(new_val): style_background = new_val background = new_val get: return style_background var style_normal:StyleBoxFlat = null @export var normal:StyleBoxFlat = null: set(new_val): style_normal = new_val normal = new_val get: return style_normal var style_hover:StyleBoxFlat = null @export var hover:StyleBoxFlat = null: set(new_val): style_hover = new_val hover = new_val get: return style_hover var style_pressed:StyleBoxFlat = null @export var pressed:StyleBoxFlat = null: set(new_val): style_pressed = new_val pressed = new_val get: return style_pressed var style_special_keys:StyleBoxFlat = null @export var special_keys:StyleBoxFlat = null: set(new_val): style_special_keys = new_val special_keys = new_val get: return style_special_keys @export_group("Font") @export var font:FontFile @export var font_size:int = 20 @export var font_color_normal:Color = Color(1,1,1) @export var font_color_hover:Color = Color(1,1,1) @export var font_color_pressed:Color = Color(1,1,1) ########################### ## SIGNALS ########################### signal layout_changed ########################### ## PANEL ########################### func _enter_tree(): if not get_tree().get_root().size_changed.is_connected(size_changed): get_tree().get_root().size_changed.connect(size_changed) _init_keyboard() #func _exit_tree(): # pass #func _process(delta): # pass func _input(event): _update_auto_display_on_input(event) func size_changed(): if auto_show and visible: _hide_keyboard() ########################### ## INIT ########################### var KeyboardButton var KeyListHandler var layouts = [] var keys = [] var capslock_keys = [] var uppercase = false var tween_position var tween_speed = .2 var hide_position = Vector2() func _init_keyboard(): if custom_layout_file == null: var default_layout = preload("default_layout.gd").new() _create_keyboard(default_layout.data) else: _create_keyboard(_load_json(custom_layout_file)) # init positioning without animation var tmp_anim = animate animate = false if visible: _hide_keyboard() elif visible: _show_keyboard() animate = tmp_anim ########################### ## HIDE/SHOW ########################### var focus_object = null func show(): _show_keyboard() func hide(): _hide_keyboard() var released = true func _update_auto_display_on_input(event): if auto_show == false: return if event is InputEventMouseButton: released = !released if released == false: return var focus_object = get_viewport().gui_get_focus_owner() if focus_object != null: var click_on_input = Rect2(focus_object.global_position, focus_object.size).has_point(get_global_mouse_position()) var click_on_keyboard = Rect2(global_position, size).has_point(get_global_mouse_position()) if click_on_input: if is_keyboard_focus_object(focus_object): _show_keyboard() elif click_on_keyboard: _show_keyboard() else: _hide_keyboard() if event is InputEventKey: var focus_object = get_viewport().gui_get_focus_owner() if focus_object != null: if event.keycode == KEY_ENTER: if is_keyboard_focus_object_complete_on_enter(focus_object): focus_object.release_focus() _hide_keyboard() func _hide_keyboard(key_data=null): if animate: var new_y_pos = get_viewport().get_visible_rect().size.y + 10 animate_position(Vector2(position.x, new_y_pos), true) else: change_visibility(false) func _show_keyboard(key_data=null): change_visibility(true) if animate: var new_y_pos = get_viewport().get_visible_rect().size.y - size.y animate_position(Vector2(position.x, new_y_pos)) func animate_position(new_position, trigger_visibility:bool=false): var tween = get_tree().create_tween() if trigger_visibility: tween.finished.connect(change_visibility.bind(!visible)) tween.tween_property( self,"position", Vector2(new_position), tween_speed ).set_trans(Tween.TRANS_SINE) func change_visibility(value): if value: super.show() else: _set_caps_lock(false) super.hide() visibility_changed.emit() ########################### ## KEY LAYOUT ########################### var prev_prev_layout = null var previous_layout = null var current_layout = null func set_active_layout_by_name(name): for layout in layouts: if layout.get_meta("layout_name") == str(name): _show_layout(layout) else: _hide_layout(layout) func _show_layout(layout): layout.show() current_layout = layout func _hide_layout(layout): layout.hide() func _switch_layout(key_data): prev_prev_layout = previous_layout previous_layout = current_layout layout_changed.emit(key_data.get("layout-name")) for layout in layouts: _hide_layout(layout) if key_data.get("layout-name") == "PREVIOUS-LAYOUT": if prev_prev_layout != null: _show_layout(prev_prev_layout) return for layout in layouts: if layout.get_meta("layout_name") == key_data.get("layout-name"): _show_layout(layout) return _set_caps_lock(false) ########################### ## KEY EVENTS ########################### func _set_caps_lock(value: bool): uppercase = value for key in capslock_keys: if value: if key.get_draw_mode() != BaseButton.DRAW_PRESSED: key.button_pressed = !key.button_pressed else: if key.get_draw_mode() == BaseButton.DRAW_PRESSED: key.button_pressed = !key.button_pressed for key in keys: key.change_uppercase(value) func _trigger_uppercase(key_data): uppercase = !uppercase _set_caps_lock(uppercase) func _key_released(key_data): if key_data.has("output"): var key_value = key_data.get("output") ########################### ## DISPATCH InputEvent ########################### var input_event_key = InputEventKey.new() input_event_key.shift_pressed = uppercase input_event_key.alt_pressed = false input_event_key.meta_pressed = false input_event_key.ctrl_pressed = false input_event_key.pressed = true var key = KeyListHandler.get_key_from_string(key_value) if !uppercase && KeyListHandler.has_lowercase(key_value): key +=32 input_event_key.keycode = key input_event_key.unicode = key Input.parse_input_event(input_event_key) ########################### ## DISABLE CAPSLOCK AFTER ########################### _set_caps_lock(false) ########################### ## CONSTRUCT KEYBOARD ########################### func _set_key_style(style_name:String, key: Control, style:StyleBoxFlat): if style != null: key.add_theme_stylebox_override(style_name, style) func _create_keyboard(layout_data): if layout_data == null: print("ERROR. No layout file found") return KeyListHandler = preload("keylist.gd").new() KeyboardButton = preload("keyboard_button.gd") var ICON_DELETE = preload("icons/delete.png") var ICON_SHIFT = preload("icons/shift.png") var ICON_LEFT = preload("icons/left.png") var ICON_RIGHT = preload("icons/right.png") var ICON_HIDE = preload("icons/hide.png") var ICON_ENTER = preload("icons/enter.png") var data = layout_data if style_background != null: add_theme_stylebox_override('panel', style_background) var index = 0 for layout in data.get("layouts"): var layout_container = PanelContainer.new() if style_background != null: layout_container.add_theme_stylebox_override('panel', style_background) # SHOW FIRST LAYOUT ON DEFAULT if index > 0: layout_container.hide() else: current_layout = layout_container var layout_name = layout.get("name") layout_container.set_meta("layout_name", layout_name) if set_tool_tip: layout_container.tooltip_text = layout_name layouts.push_back(layout_container) add_child(layout_container) var base_vbox = VBoxContainer.new() base_vbox.size_flags_horizontal = SIZE_EXPAND_FILL base_vbox.size_flags_vertical = SIZE_EXPAND_FILL # theme override for spacing base_vbox.add_theme_constant_override("separation", separation.y) for row in layout.get("rows"): var key_row = HBoxContainer.new() key_row.size_flags_horizontal = SIZE_EXPAND_FILL key_row.size_flags_vertical = SIZE_EXPAND_FILL key_row.add_theme_constant_override("separation", separation.x) for key in row.get("keys"): var new_key = KeyboardButton.new(key) _set_key_style("normal",new_key, style_normal) _set_key_style("hover",new_key, style_hover) _set_key_style("pressed",new_key, style_pressed) new_key.set('theme_override_font_sizes/font_size', font_size) if font != null: new_key.set('theme_override_fonts/font', font) if font_color_normal != null: new_key.set('theme_override_colors/font_color', font_color_normal) new_key.set('theme_override_colors/font_hover_color', font_color_hover) new_key.set('theme_override_colors/font_pressed_color', font_color_pressed) new_key.set('theme_override_colors/font_disabled_color', font_color_normal) new_key.released.connect(_key_released) if key.has("type"): if key.get("type") == "switch-layout": new_key.released.connect(_switch_layout) _set_key_style("normal",new_key, style_special_keys) elif key.get("type") == "special": _set_key_style("normal",new_key, style_special_keys) elif key.get("type") == "special-shift": new_key.released.connect(_trigger_uppercase) new_key.toggle_mode = true capslock_keys.push_back(new_key) _set_key_style("normal",new_key, style_special_keys) elif key.get("type") == "special-hide-keyboard": new_key.released.connect(_hide_keyboard) _set_key_style("normal",new_key, style_special_keys) # SET ICONS if key.has("display-icon"): var icon_data = str(key.get("display-icon")).split(":") # PREDEFINED if str(icon_data[0])=="PREDEFINED": if str(icon_data[1])=="DELETE": new_key.set_icon(ICON_DELETE) elif str(icon_data[1])=="SHIFT": new_key.set_icon(ICON_SHIFT) elif str(icon_data[1])=="LEFT": new_key.set_icon(ICON_LEFT) elif str(icon_data[1])=="RIGHT": new_key.set_icon(ICON_RIGHT) elif str(icon_data[1])=="HIDE": new_key.set_icon(ICON_HIDE) elif str(icon_data[1])=="ENTER": new_key.set_icon(ICON_ENTER) # CUSTOM if str(icon_data[0])=="res": var texture = load(key.get("display-icon")) new_key.set_icon(texture) if font_color_normal != null: new_key.set_icon_color(font_color_normal) key_row.add_child(new_key) keys.push_back(new_key) base_vbox.add_child(key_row) layout_container.add_child(base_vbox) index+=1 ########################### ## LOAD SETTINGS ########################### func _load_json(file_path) -> Variant: var json = JSON.new() var json_string = _load_file(file_path) var error = json.parse(json_string) if error == OK: var data_received = json.data # print(data_received) if typeof(data_received) == TYPE_DICTIONARY: return data_received else: return {"msg": "unexpected data => expected DICTIONARY"} else: print("JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line()) return {"msg":json.get_error_message()} func _load_file(file_path): var file = FileAccess.open(file_path, FileAccess.READ) if not file: print("Error loading File. Error: ") var content = file.get_as_text() file.close() return content ########################### ## HELPER ########################### func is_keyboard_focus_object_complete_on_enter(focus_object): if focus_object is LineEdit: return true return false func is_keyboard_focus_object(focus_object): if focus_object is LineEdit or focus_object is TextEdit: return true return false