Skip to content

Commit

Permalink
Merge pull request #27 from apple1417/master
Browse files Browse the repository at this point in the history
use new generic type syntax; make console mod menu fully game agnostic
  • Loading branch information
apple1417 authored Jun 7, 2024
2 parents c4ee1e4 + 8668e6a commit abd3006
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 35 deletions.
8 changes: 3 additions & 5 deletions src/bl3_mod_menu/options_callbacks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, TypeVar
from typing import Any

from mods_base import (
JSON,
Expand Down Expand Up @@ -28,10 +28,8 @@
open_nested_options_menu,
)

_J = TypeVar("_J", bound=JSON)


def update_option_value(option: ValueOption[_J], value: _J) -> None:
def update_option_value[J: JSON](option: ValueOption[J], value: J) -> None:
"""
Updates an option's value, running the callback if needed.
Expand All @@ -45,7 +43,7 @@ def update_option_value(option: ValueOption[_J], value: _J) -> None:


@hook("/Script/OakGame.GFxOptionBase:OnUnimplementedOptionClicked", Type.PRE, auto_enable=True)
def unimplemented_option_clicked(
def unimplemented_option_clicked( # noqa: C901 - imo the match is rated too highly
obj: UObject,
args: WrappedStruct,
_3: Any,
Expand Down
2 changes: 1 addition & 1 deletion src/bl3_mod_menu/options_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def get_displayed_option_at_idx(idx: int) -> BaseOption:
return option_stack[-1].drawn_options[idx]


def draw_options(
def draw_options( # noqa: C901 - imo the match is rated too highly
self: UObject,
options: Sequence[BaseOption],
group_stack: list[GroupedOption],
Expand Down
6 changes: 2 additions & 4 deletions src/console_mod_menu/option_formatting.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
from typing import TypeVar, overload
from typing import overload

from mods_base import JSON, BaseOption, BoolOption, KeybindOption, ValueOption

from .draw import draw, draw_description
from .screens import draw_stack_header

_J = TypeVar("_J", bound=JSON)


@overload
def get_option_value_str(option: ValueOption[_J]) -> str: ...
def get_option_value_str[J: JSON](option: ValueOption[J]) -> str: ...


@overload
Expand Down
17 changes: 12 additions & 5 deletions src/console_mod_menu/screens/keybind.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
raw_keybinds,
remove_next_console_line_capture,
)
from ui_utils import show_hud_message

try:
from ui_utils import show_hud_message
except ImportError:
show_hud_message = None

from unrealsdk.hooks import Block

from console_mod_menu.draw import draw
Expand Down Expand Up @@ -117,10 +122,12 @@ def key_handler(key: str) -> type[Block]: # pyright: ignore[reportUnusedFunctio
self.is_bind_active = False
raw_keybinds.pop()

show_hud_message(
"Console Mod Menu",
f"'{self.parent.option.display_name}' bound to '{key}'",
)
# Try show a notification that we caught the press, if we were able to import it
if show_hud_message is not None:
show_hud_message(
"Console Mod Menu",
f"'{self.parent.option.display_name}' bound to '{key}'",
)

# Bit of hackery to inject back into the menu loop
# Submit a B to close this menu
Expand Down
12 changes: 4 additions & 8 deletions src/console_mod_menu/screens/option.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from abc import abstractmethod
from dataclasses import dataclass, field
from typing import Generic, TypeVar

from mods_base import (
JSON,
Expand All @@ -21,15 +20,12 @@

from . import AbstractScreen, draw_standard_commands, handle_standard_command_input

_T = TypeVar("_T", bound=BaseOption)
_J = TypeVar("_J", bound=JSON)


@dataclass
class OptionScreen(AbstractScreen, Generic[_T, _J]):
class OptionScreen[T: BaseOption, J: JSON](AbstractScreen):
name: str = field(init=False)
mod: Mod
option: _T
option: T

def __post_init__(self) -> None:
self.name = self.option.display_name
Expand All @@ -43,15 +39,15 @@ def draw_option(self) -> None:
"""Draws anything needed for the specific option."""
raise NotImplementedError

def update_value(self, new_value: _J) -> None:
def update_value(self, new_value: J) -> None:
"""
Updates an option's value, running the callback if needed.
Args:
new_value: The option's new value.
"""
assert isinstance(self.option, ValueOption)
option: ValueOption[_J] = self.option # pyright: ignore[reportUnknownMemberType]
option: ValueOption[J] = self.option # type: ignore

if option.on_change is not None:
option.on_change(option, new_value)
Expand Down
6 changes: 5 additions & 1 deletion src/mods_base/html_to_plain_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ def __init__(self) -> None:
def handle_data(self, data: str) -> None:
self.plain_text += data

def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
def handle_starttag( # noqa: C901 - imo the match is rated too highly
self,
tag: str,
attrs: list[tuple[str, str | None]],
) -> None:
match tag.lower():
case "br":
self.plain_text += "\n"
Expand Down
2 changes: 1 addition & 1 deletion src/mods_base/mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class Mod:
on_enable: Callable[[], None] | None = None
on_disable: Callable[[], None] | None = None

def __post_init__(self) -> None:
def __post_init__(self) -> None: # noqa: C901 - difficult to split up
need_to_search_instance_vars = False

new_keybinds: list[KeybindType] = []
Expand Down
5 changes: 4 additions & 1 deletion src/mods_base/mod_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,10 @@ def update_fields_with_module_attributes(module: ModuleType, fields: ModFactoryF
fields["settings_file"] = SETTINGS_DIR / (module.__name__ + ".json")


def update_fields_with_module_search(module: ModuleType, fields: ModFactoryFields) -> None:
def update_fields_with_module_search( # noqa: C901 - difficult to split up
module: ModuleType,
fields: ModFactoryFields,
) -> None:
"""
Updates the mod factory fields with data gathered by searching through all vars in the module.
Expand Down
15 changes: 7 additions & 8 deletions src/mods_base/options.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from abc import ABC, abstractmethod
from collections.abc import Callable, Mapping, Sequence
from dataclasses import KW_ONLY, dataclass, field
from typing import TYPE_CHECKING, Generic, Literal, Self, TypeVar
from typing import TYPE_CHECKING, Literal, Self

from unrealsdk import logging

Expand All @@ -14,7 +14,6 @@
# strong circular dependency - we need to import it to get JSON before we can define most options,
# but it needs to import those options from us
type JSON = Mapping[str, JSON] | Sequence[JSON] | str | int | float | bool | None
_J = TypeVar("_J", bound=JSON)


@dataclass
Expand Down Expand Up @@ -54,7 +53,7 @@ def __post_init__(self) -> None:


@dataclass
class ValueOption(BaseOption, Generic[_J]):
class ValueOption[J: JSON](BaseOption):
"""
Abstract base class for all options storing a value.
Expand All @@ -73,10 +72,10 @@ class ValueOption(BaseOption, Generic[_J]):
default_value: What the value was originally when registered. Does not update on change.
"""

value: _J
default_value: _J = field(init=False)
value: J
default_value: J = field(init=False)
_: KW_ONLY
on_change: Callable[[Self, _J], None] | None = None
on_change: Callable[[Self, J], None] | None = None

@abstractmethod
def __init__(self) -> None:
Expand All @@ -86,7 +85,7 @@ def __post_init__(self) -> None:
super().__post_init__()
self.default_value = self.value

def __call__(self, on_change: Callable[[Self, _J], None]) -> Self:
def __call__(self, on_change: Callable[[Self, J], None]) -> Self:
"""
Sets the on change callback.
Expand All @@ -109,7 +108,7 @@ def __call__(self, on_change: Callable[[Self, _J], None]) -> Self:


@dataclass
class HiddenOption(ValueOption[_J]):
class HiddenOption[J: JSON](ValueOption[J]):
"""
A generic option which is always hidden. Use this to persist arbitrary (JSON-encodeable) data.
Expand Down
5 changes: 4 additions & 1 deletion src/mods_base/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ class BasicModSettings(TypedDict, total=False):
keybinds: dict[str, str | None]


def load_options_dict(options: Sequence[BaseOption], settings: Mapping[str, JSON]) -> None:
def load_options_dict( # noqa: C901 - imo the match is rated too highly, but it's getting there
options: Sequence[BaseOption],
settings: Mapping[str, JSON],
) -> None:
"""
Recursively loads options from their settings dict.
Expand Down

0 comments on commit abd3006

Please sign in to comment.