# Released under the MIT License. See LICENSE for details.
#
"""Provides a top level control settings window."""
from __future__ import annotations
from bauiv1lib.popup import PopupMenu
import bascenev1 as bs
import bauiv1 as bui
[docs]
class ControlsSettingsWindow(bui.Window):
"""Top level control settings window."""
def __init__(
self,
transition: str = 'in_right',
origin_widget: bui.Widget | None = None,
):
# FIXME: should tidy up here.
# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
# pylint: disable=cyclic-import
self._have_selected_child = False
scale_origin: tuple[float, float] | None
# If they provided an origin-widget, scale up from that.
if origin_widget is not None:
self._transition_out = 'out_scale'
scale_origin = origin_widget.get_screen_space_center()
transition = 'in_scale'
else:
self._transition_out = 'out_right'
scale_origin = None
self._r = 'configControllersWindow'
app = bui.app
assert app.classic is not None
spacing = 50.0
button_width = 350.0
width = 460.0
height = 130.0
space_height = spacing * 0.3
# FIXME: should create vis settings under platform or app-adapter
# to determine whether to show this stuff; not hard code it.
show_gamepads = False
platform = app.classic.platform
subplatform = app.classic.subplatform
non_vr_windows = platform == 'windows' and (
subplatform != 'oculus' or not app.env.vr
)
if platform in ('linux', 'android', 'mac') or non_vr_windows:
show_gamepads = True
height += spacing
show_touch = False
if bs.have_touchscreen_input():
show_touch = True
height += spacing
show_space_1 = False
if show_gamepads or show_touch:
show_space_1 = True
height += space_height
show_keyboard = False
if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None:
show_keyboard = True
height += spacing
show_keyboard_p2 = False if app.env.vr else show_keyboard
if show_keyboard_p2:
height += spacing
show_space_2 = False
if show_keyboard:
show_space_2 = True
height += space_height
if bool(True):
show_remote = True
height += spacing
else:
show_remote = False
# On windows (outside of oculus/vr), show an option to disable xinput.
show_xinput_toggle = False
if platform == 'windows' and not app.env.vr:
show_xinput_toggle = True
# On mac builds, show an option to switch between generic and
# made-for-iOS/Mac systems
# (we can run into problems where devices register as one of each
# type otherwise)..
# UPDATE: We always use the apple system these days (which should
# support older controllers). So no need for a switch.
show_mac_controller_subsystem = False
# if platform == 'mac' and bui.is_xcode_build():
# show_mac_controller_subsystem = True
if show_mac_controller_subsystem:
height += spacing * 1.5
if show_xinput_toggle:
height += spacing
assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale
smallscale = 1.7 if show_keyboard else 2.2
super().__init__(
root_widget=bui.containerwidget(
size=(width, height),
transition=transition,
scale_origin_stack_offset=scale_origin,
stack_offset=(
(0, -10) if uiscale is bui.UIScale.SMALL else (0, 0)
),
scale=(
smallscale
if uiscale is bui.UIScale.SMALL
else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
),
)
)
self._back_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=(35, height - 60),
size=(140, 65),
scale=0.8,
text_scale=1.2,
autoselect=True,
label=bui.Lstr(resource='backText'),
button_type='back',
on_activate_call=self._back,
)
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
# We need these vars to exist even if the buttons don't.
self._gamepads_button: bui.Widget | None = None
self._touch_button: bui.Widget | None = None
self._keyboard_button: bui.Widget | None = None
self._keyboard_2_button: bui.Widget | None = None
self._idevices_button: bui.Widget | None = None
bui.textwidget(
parent=self._root_widget,
position=(0, height - 49),
size=(width, 25),
text=bui.Lstr(resource=self._r + '.titleText'),
color=bui.app.ui_v1.title_color,
h_align='center',
v_align='top',
)
bui.buttonwidget(
edit=btn,
button_type='backSmall',
size=(60, 60),
label=bui.charstr(bui.SpecialChar.BACK),
)
v = height - 75
v -= spacing
if show_touch:
self._touch_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=((width - button_width) / 2, v),
size=(button_width, 43),
autoselect=True,
label=bui.Lstr(resource=self._r + '.configureTouchText'),
on_activate_call=self._do_touchscreen,
)
if bui.app.ui_v1.use_toolbars:
bui.widget(
edit=btn,
right_widget=bui.get_special_widget('party_button'),
)
if not self._have_selected_child:
bui.containerwidget(
edit=self._root_widget, selected_child=self._touch_button
)
bui.widget(
edit=self._back_button, down_widget=self._touch_button
)
self._have_selected_child = True
v -= spacing
if show_gamepads:
self._gamepads_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=((width - button_width) / 2 - 7, v),
size=(button_width, 43),
autoselect=True,
label=bui.Lstr(resource=self._r + '.configureControllersText'),
on_activate_call=self._do_gamepads,
)
if bui.app.ui_v1.use_toolbars:
bui.widget(
edit=btn,
right_widget=bui.get_special_widget('party_button'),
)
if not self._have_selected_child:
bui.containerwidget(
edit=self._root_widget, selected_child=self._gamepads_button
)
bui.widget(
edit=self._back_button, down_widget=self._gamepads_button
)
self._have_selected_child = True
v -= spacing
else:
self._gamepads_button = None
if show_space_1:
v -= space_height
if show_keyboard:
self._keyboard_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=((width - button_width) / 2 + 5, v),
size=(button_width, 43),
autoselect=True,
label=bui.Lstr(resource=self._r + '.configureKeyboardText'),
on_activate_call=self._config_keyboard,
)
if bui.app.ui_v1.use_toolbars:
bui.widget(
edit=btn,
right_widget=bui.get_special_widget('party_button'),
)
if not self._have_selected_child:
bui.containerwidget(
edit=self._root_widget, selected_child=self._keyboard_button
)
bui.widget(
edit=self._back_button, down_widget=self._keyboard_button
)
self._have_selected_child = True
v -= spacing
if show_keyboard_p2:
self._keyboard_2_button = bui.buttonwidget(
parent=self._root_widget,
position=((width - button_width) / 2 - 3, v),
size=(button_width, 43),
autoselect=True,
label=bui.Lstr(resource=self._r + '.configureKeyboard2Text'),
on_activate_call=self._config_keyboard2,
)
v -= spacing
if show_space_2:
v -= space_height
if show_remote:
self._idevices_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=((width - button_width) / 2 - 5, v),
size=(button_width, 43),
autoselect=True,
label=bui.Lstr(resource=self._r + '.configureMobileText'),
on_activate_call=self._do_mobile_devices,
)
if bui.app.ui_v1.use_toolbars:
bui.widget(
edit=btn,
right_widget=bui.get_special_widget('party_button'),
)
if not self._have_selected_child:
bui.containerwidget(
edit=self._root_widget, selected_child=self._idevices_button
)
bui.widget(
edit=self._back_button, down_widget=self._idevices_button
)
self._have_selected_child = True
v -= spacing
if show_xinput_toggle:
def do_toggle(value: bool) -> None:
bui.screenmessage(
bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
color=(1, 1, 0),
)
bui.getsound('gunCocking').play()
bui.set_low_level_config_value('enablexinput', not value)
bui.checkboxwidget(
parent=self._root_widget,
position=(100, v + 3),
size=(120, 30),
value=(not bui.get_low_level_config_value('enablexinput', 1)),
maxwidth=200,
on_value_change_call=do_toggle,
text=bui.Lstr(resource='disableXInputText'),
autoselect=True,
)
bui.textwidget(
parent=self._root_widget,
position=(width * 0.5, v - 5),
size=(0, 0),
text=bui.Lstr(resource='disableXInputDescriptionText'),
scale=0.5,
h_align='center',
v_align='center',
color=bui.app.ui_v1.infotextcolor,
maxwidth=width * 0.8,
)
v -= spacing
if show_mac_controller_subsystem:
PopupMenu(
parent=self._root_widget,
position=(260, v - 10),
width=160,
button_size=(150, 50),
scale=1.5,
choices=['Classic', 'MFi', 'Both'],
choices_display=[
bui.Lstr(resource='macControllerSubsystemClassicText'),
bui.Lstr(resource='macControllerSubsystemMFiText'),
bui.Lstr(resource='macControllerSubsystemBothText'),
],
current_choice=bui.app.config.resolve(
'Mac Controller Subsystem'
),
on_value_change_call=self._set_mac_controller_subsystem,
)
bui.textwidget(
parent=self._root_widget,
position=(245, v + 13),
size=(0, 0),
text=bui.Lstr(resource='macControllerSubsystemTitleText'),
scale=1.0,
h_align='right',
v_align='center',
color=bui.app.ui_v1.infotextcolor,
maxwidth=180,
)
bui.textwidget(
parent=self._root_widget,
position=(width * 0.5, v - 20),
size=(0, 0),
text=bui.Lstr(resource='macControllerSubsystemDescriptionText'),
scale=0.5,
h_align='center',
v_align='center',
color=bui.app.ui_v1.infotextcolor,
maxwidth=width * 0.8,
)
v -= spacing * 1.5
self._restore_state()
def _set_mac_controller_subsystem(self, val: str) -> None:
cfg = bui.app.config
cfg['Mac Controller Subsystem'] = val
cfg.apply_and_commit()
def _config_keyboard(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
self._save_state()
bui.containerwidget(edit=self._root_widget, transition='out_left')
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
ConfigKeyboardWindow(
bs.getinputdevice('Keyboard', '#1')
).get_root_widget(),
from_window=self._root_widget,
)
def _config_keyboard2(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
self._save_state()
bui.containerwidget(edit=self._root_widget, transition='out_left')
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
ConfigKeyboardWindow(
bs.getinputdevice('Keyboard', '#2')
).get_root_widget(),
from_window=self._root_widget,
)
def _do_mobile_devices(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
self._save_state()
bui.containerwidget(edit=self._root_widget, transition='out_left')
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
RemoteAppSettingsWindow().get_root_widget(),
from_window=self._root_widget,
)
def _do_gamepads(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.settings.gamepadselect import GamepadSelectWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
self._save_state()
bui.containerwidget(edit=self._root_widget, transition='out_left')
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
GamepadSelectWindow().get_root_widget(),
from_window=self._root_widget,
)
def _do_touchscreen(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
self._save_state()
bui.containerwidget(edit=self._root_widget, transition='out_left')
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
TouchscreenSettingsWindow().get_root_widget(),
from_window=self._root_widget,
)
def _save_state(self) -> None:
sel = self._root_widget.get_selected_child()
if sel == self._gamepads_button:
sel_name = 'GamePads'
elif sel == self._touch_button:
sel_name = 'Touch'
elif sel == self._keyboard_button:
sel_name = 'Keyboard'
elif sel == self._keyboard_2_button:
sel_name = 'Keyboard2'
elif sel == self._idevices_button:
sel_name = 'iDevices'
else:
sel_name = 'Back'
assert bui.app.classic is not None
bui.app.ui_v1.window_states[type(self)] = sel_name
def _restore_state(self) -> None:
assert bui.app.classic is not None
sel_name = bui.app.ui_v1.window_states.get(type(self))
if sel_name == 'GamePads':
sel = self._gamepads_button
elif sel_name == 'Touch':
sel = self._touch_button
elif sel_name == 'Keyboard':
sel = self._keyboard_button
elif sel_name == 'Keyboard2':
sel = self._keyboard_2_button
elif sel_name == 'iDevices':
sel = self._idevices_button
elif sel_name == 'Back':
sel = self._back_button
else:
sel = (
self._gamepads_button
if self._gamepads_button is not None
else self._back_button
)
bui.containerwidget(edit=self._root_widget, selected_child=sel)
def _back(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.settings.allsettings import AllSettingsWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
self._save_state()
bui.containerwidget(
edit=self._root_widget, transition=self._transition_out
)
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
AllSettingsWindow(transition='in_left').get_root_widget(),
from_window=self._root_widget,
)