From 3f3c343fb3422ca53146a73e8ab4f863c957c44a Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:42:08 +0100 Subject: [PATCH] APQuest: Fix focus issues (#6090) * My best attempt at fixing focus issues on Android * didn't mean to remove that --- worlds/apquest/client/ap_quest_client.py | 7 ++- worlds/apquest/client/custom_views.py | 64 +++++++++++++----------- worlds/apquest/client/game_manager.py | 4 +- worlds/apquest/game/game.py | 1 + worlds/apquest/game/path_finding.py | 2 +- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/worlds/apquest/client/ap_quest_client.py b/worlds/apquest/client/ap_quest_client.py index 9b6b82b75f..bd67cc55ca 100644 --- a/worlds/apquest/client/ap_quest_client.py +++ b/worlds/apquest/client/ap_quest_client.py @@ -184,7 +184,6 @@ class APQuestContext(CommonContext): assert self.ap_quest_game is not None self.ap_quest_game.gameboard.fill_remote_location_content(remote_item_graphic_overrides) self.render() - self.ui.game_view.bind_keyboard() self.connection_status = ConnectionStatus.GAME_RUNNING self.ui.game_started() @@ -261,6 +260,8 @@ class APQuestContext(CommonContext): return if not self.ap_quest_game.gameboard.ready: return + if not self.ui.game_view.focused > 1: # Must already be in focus + return self.ap_quest_game.queue_auto_move(target_x, target_y) self.ui.start_auto_move() @@ -299,6 +300,10 @@ class APQuestContext(CommonContext): return False self.ap_quest_game.math_problem_replace([int(digit) for digit in raw]) + + if not self.ap_quest_game.active_math_problem: + self.ui.game_view.force_focus() + self.render() return True diff --git a/worlds/apquest/client/custom_views.py b/worlds/apquest/client/custom_views.py index cc44f991d6..90a673f15d 100644 --- a/worlds/apquest/client/custom_views.py +++ b/worlds/apquest/client/custom_views.py @@ -4,10 +4,9 @@ from math import sqrt from random import choice, random from typing import Any -from kivy.core.window import Keyboard, Window +from kivy.core.window import Window from kivy.graphics import Color, Triangle from kivy.graphics.instructions import Canvas -from kivy.input import MotionEvent from kivy.uix.behaviors import ButtonBehavior from kivy.uix.boxlayout import BoxLayout from kivy.uix.gridlayout import GridLayout @@ -19,16 +18,12 @@ from CommonClient import logger from ..game.inputs import Input -INPUT_MAP = { - "up": Input.UP, +INPUT_MAP_STR = { "w": Input.UP, - "down": Input.DOWN, "s": Input.DOWN, - "right": Input.RIGHT, "d": Input.RIGHT, - "left": Input.LEFT, "a": Input.LEFT, - "spacebar": Input.ACTION, + " ": Input.ACTION, "c": Input.CONFETTI, "0": Input.ZERO, "1": Input.ONE, @@ -40,39 +35,52 @@ INPUT_MAP = { "7": Input.SEVEN, "8": Input.EIGHT, "9": Input.NINE, - "backspace": Input.BACKSPACE, +} + +INPUT_MAP_SPECIAL_INT = { + # Arrow Keys and Backspace + 273: Input.UP, + 274: Input.DOWN, + 275: Input.RIGHT, + 276: Input.LEFT, + 8: Input.BACKSPACE, } class APQuestGameView(MDRecycleView): - _keyboard: Keyboard | None = None + focused: int = 1 input_function: Callable[[Input], None] def __init__(self, input_function: Callable[[Input], None], **kwargs: Any) -> None: super().__init__(**kwargs) self.input_function = input_function - self.bind_keyboard() + Window.bind(on_key_down=self._on_keyboard_down) + Window.bind(on_touch_down=self.check_focus) + self.opacity = 0.5 - def on_touch_down(self, touch: MotionEvent) -> bool | None: - self.bind_keyboard() - return super().on_touch_down(touch) - - def bind_keyboard(self) -> None: - if self._keyboard is not None: + def check_focus(self, _, touch, *args, **kwargs) -> None: + if self.parent.collide_point(*touch.pos): + self.focused += 1 + self.opacity = 1 return - self._keyboard = Window.request_keyboard(self._keyboard_closed, self) - self._keyboard.bind(on_key_down=self._on_keyboard_down) - def _keyboard_closed(self) -> None: - if self._keyboard is None: - return - self._keyboard.unbind(on_key_down=self._on_keyboard_down) - self._keyboard = None + self.focused = 0 + self.opacity = 0.5 - def _on_keyboard_down(self, _: Any, keycode: tuple[int, str], _1: Any, _2: Any) -> bool: - if keycode[1] in INPUT_MAP: - self.input_function(INPUT_MAP[keycode[1]]) - return True + def force_focus(self) -> None: + Window.release_keyboard() + self.focused = 1 + self.opacity = 1 + + def _on_keyboard_down(self, _: Any, keycode_int: int, _2: Any, keycode: str, _4: Any) -> bool: + if not self.focused: + return False + + if keycode in INPUT_MAP_STR: + self.input_function(INPUT_MAP_STR[keycode]) + elif keycode_int in INPUT_MAP_SPECIAL_INT: + self.input_function(INPUT_MAP_SPECIAL_INT[keycode_int]) + return False class APQuestGrid(GridLayout): diff --git a/worlds/apquest/client/game_manager.py b/worlds/apquest/client/game_manager.py index 241fbec0ae..ed2793da36 100644 --- a/worlds/apquest/client/game_manager.py +++ b/worlds/apquest/client/game_manager.py @@ -38,7 +38,7 @@ class APQuestManager(GameManager): lower_game_grid: GridLayout upper_game_grid: GridLayout - game_view: MDRecycleView + game_view: MDRecycleView | None = None game_view_tab: MDNavigationItemBase sound_manager: SoundManager @@ -84,6 +84,8 @@ class APQuestManager(GameManager): def game_started(self) -> None: self.switch_to_game_tab() + if self.game_view is not None: + self.game_view.force_focus() self.sound_manager.game_started = True def render(self, game: Game, player_sprite: PlayerSprite) -> None: diff --git a/worlds/apquest/game/game.py b/worlds/apquest/game/game.py index 21bebca681..f032e73e4e 100644 --- a/worlds/apquest/game/game.py +++ b/worlds/apquest/game/game.py @@ -34,6 +34,7 @@ class Game: self.gameboard = create_gameboard(hard_mode, hammer_exists, extra_chest) self.player = Player(self.gameboard, self.queued_events.append) self.active_math_problem = None + self.active_math_problem_input = None self.remotely_received_items = set() if random_object is None: diff --git a/worlds/apquest/game/path_finding.py b/worlds/apquest/game/path_finding.py index 8b3e649b1d..92ddfeb26b 100644 --- a/worlds/apquest/game/path_finding.py +++ b/worlds/apquest/game/path_finding.py @@ -1,5 +1,5 @@ import heapq -from typing import Generator +from collections.abc import Generator Point = tuple[int, int]