Gui/controller focus improvements by eruvanos · Pull Request #2757 · pythonarcade/arcade · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
9 changes: 8 additions & 1 deletion arcade/examples/gui/exp_controller_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
python -m arcade.examples.gui.exp_controller_support
"""


import arcade
from arcade import Texture
from arcade.experimental.controller_window import ControllerWindow, ControllerView
Expand Down Expand Up @@ -143,6 +142,7 @@ def __init__(self):
root.add(UIFlatButton(text="Close")).on_click = self.close

self.detect_focusable_widgets()
self.set_focus()

def on_event(self, event):
if super().on_event(event):
Expand Down Expand Up @@ -190,6 +190,13 @@ def on_button_click(self, event: UIOnClickEvent):
print("Button clicked")
self.root.add(ControllerModal())

def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
# make the example close with the escape key
if symbol == arcade.key.ESCAPE:
self.window.close()
return True
return super().on_key_press(symbol, modifiers)


if __name__ == "__main__":
window = ControllerWindow(title="Controller UI Example")
Expand Down
8 changes: 7 additions & 1 deletion arcade/examples/gui/exp_controller_support_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
python -m arcade.examples.gui.exp_controller_support_grid
"""


import arcade
from arcade.examples.gui.exp_controller_support import ControllerIndicator
from arcade.experimental.controller_window import ControllerView, ControllerWindow
Expand Down Expand Up @@ -88,6 +87,13 @@ def __init__(self):

self.root.detect_focusable_widgets()

def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
# make the example close with the escape key
if symbol == arcade.key.ESCAPE:
self.window.close()
return True
return super().on_key_press(symbol, modifiers)


if __name__ == "__main__":
window = ControllerWindow(title="Controller UI Example")
Expand Down
33 changes: 14 additions & 19 deletions arcade/examples/gui/exp_hidden_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
"""

import arcade
from arcade.experimental.controller_window import ControllerWindow
from arcade.gui import UIInputText, UIOnClickEvent, UIView
from arcade.gui.experimental.focus import UIFocusGroup
from arcade.gui.experimental.password_input import UIPasswordInput
from arcade.gui.widgets.buttons import UIFlatButton
from arcade.gui.widgets.layout import UIGridLayout, UIAnchorLayout
from arcade.gui.widgets.layout import UIGridLayout
from arcade.gui.widgets.text import UILabel
from arcade import resources

Expand Down Expand Up @@ -80,32 +82,25 @@ def __init__(self):
column_span=2,
)

anchor = UIAnchorLayout() # to center grid on screen
anchor = UIFocusGroup() # to center grid on screen
anchor.add(grid)

self.add_widget(anchor)

# activate username input field
self.username_input.activate()
anchor.detect_focusable_widgets()
anchor.set_focus()

def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
# make the example close with the escape key
if symbol == arcade.key.ESCAPE:
self.window.close()
return True

# if username field active, switch fields with enter
if self.username_input.active:
if symbol == arcade.key.TAB:
self.username_input.deactivate()
self.password_input.activate()
return True
elif symbol == arcade.key.ENTER:
elif self.username_input.active or self.password_input.active:
if symbol == arcade.key.ENTER:
self.username_input.deactivate()
self.on_login(None)
return True
# if password field active, login with enter
elif self.password_input.active:
if symbol == arcade.key.TAB:
self.username_input.activate()
self.password_input.deactivate()
return True
elif symbol == arcade.key.ENTER:
self.password_input.deactivate()
self.on_login(None)
return True
Expand All @@ -118,7 +113,7 @@ def on_login(self, event: UIOnClickEvent | None):


def main():
window = arcade.Window(title="GUI Example: Hidden Password")
window = ControllerWindow(title="GUI Example: Hidden Password")
window.show_view(MyView())
window.run()

Expand Down
3 changes: 1 addition & 2 deletions arcade/experimental/controller_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ def __init__(self, window: arcade.Window):
self.on_connect(controller)

def on_connect(self, controller: Controller):
controller.push_handlers(self)

try:
controller.open()
except Exception as e:
warnings.warn(f"Failed to open controller {controller}: {e}")
controller.push_handlers(self)

self.window.dispatch_event("on_connect", controller)

Expand Down
106 changes: 13 additions & 93 deletions arcade/gui/experimental/focus.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,16 @@
from pyglet.math import Vec2

import arcade
from arcade import MOUSE_BUTTON_LEFT
from arcade.gui.events import (
UIControllerButtonPressEvent,
UIControllerButtonReleaseEvent,
UIControllerDpadEvent,
UIControllerEvent,
UIEvent,
UIKeyPressEvent,
UIKeyReleaseEvent,
UIMousePressEvent,
UIMouseReleaseEvent,
)
from arcade.gui.property import ListProperty, Property, bind
from arcade.gui.surface import Surface
from arcade.gui.widgets import FocusMode, UIInteractiveWidget, UIWidget
from arcade.gui.widgets import FocusMode, UIWidget
from arcade.gui.widgets.layout import UIAnchorLayout
from arcade.gui.widgets.slider import UIBaseSlider


class UIFocusable(UIWidget):
Expand Down Expand Up @@ -100,54 +93,22 @@ def on_event(self, event: UIEvent) -> bool | None:

return EVENT_HANDLED

elif event.symbol == arcade.key.SPACE:
self._start_interaction()
elif isinstance(event, UIControllerDpadEvent):
# switch focus
if event.vector.x == 1:
self.focus_right()
return EVENT_HANDLED

elif isinstance(event, UIKeyReleaseEvent):
if event.symbol == arcade.key.SPACE:
self._end_interaction()
elif event.vector.y == 1:
self.focus_up()
return EVENT_HANDLED

elif isinstance(event, UIControllerDpadEvent):
if self._interacting:
# TODO this should be handled in the slider!
# pass dpad events to the interacting widget
if event.vector.x == 1 and isinstance(self._interacting, UIBaseSlider):
self._interacting.norm_value += 0.1
return EVENT_HANDLED

elif event.vector.x == -1 and isinstance(self._interacting, UIBaseSlider):
self._interacting.norm_value -= 0.1
return EVENT_HANDLED

elif event.vector.x == -1:
self.focus_left()
return EVENT_HANDLED

else:
# switch focus
if event.vector.x == 1:
self.focus_right()
return EVENT_HANDLED

elif event.vector.y == 1:
self.focus_up()
return EVENT_HANDLED

elif event.vector.x == -1:
self.focus_left()
return EVENT_HANDLED

elif event.vector.y == -1:
self.focus_down()
return EVENT_HANDLED

elif isinstance(event, UIControllerButtonPressEvent):
if event.button == "a":
self._start_interaction()
return EVENT_HANDLED
elif isinstance(event, UIControllerButtonReleaseEvent):
if event.button == "a":
self._end_interaction()
elif event.vector.y == -1:
self.focus_down()
return EVENT_HANDLED

return EVENT_UNHANDLED
Expand Down Expand Up @@ -278,48 +239,6 @@ def focus_previous(self):
# automatically wrap around via index -1
self.set_focus(self._focusable_widgets[focused_index])

def _start_interaction(self):
# TODO this should be handled in the widget

widget = self.focused_widget

if isinstance(widget, UIInteractiveWidget):
widget.dispatch_ui_event(
UIMousePressEvent(
source=self,
x=int(widget.rect.center_x),
y=int(widget.rect.center_y),
button=MOUSE_BUTTON_LEFT,
modifiers=0,
)
)
self._interacting = widget
else:
print("Cannot interact widget")

def _end_interaction(self):
widget = self.focused_widget

if isinstance(widget, UIInteractiveWidget):
if isinstance(self._interacting, UIBaseSlider):
# if slider, release outside the slider
x = self._interacting.rect.left - 1
y = self._interacting.rect.bottom - 1
else:
x = widget.rect.center_x
y = widget.rect.center_y

self._interacting = None
widget.dispatch_ui_event(
UIMouseReleaseEvent(
source=self,
x=int(x),
y=int(y),
button=MOUSE_BUTTON_LEFT,
modifiers=0,
)
)

def _do_render(self, surface: Surface, force=False) -> bool:
rendered = super()._do_render(surface, force)

Expand Down Expand Up @@ -373,4 +292,5 @@ def is_focusable(widget):


class UIFocusGroup(UIFocusMixin, UIAnchorLayout):
pass
"""This will be removed in the future.
UIFocusMixin is planned to be integrated into UILayout."""
49 changes: 49 additions & 0 deletions arcade/gui/widgets/__init__.py
Loading
Loading