diff --git a/data/client.kv b/data/client.kv index dc8a5c9c9d72..9c9f90c799bc 100644 --- a/data/client.kv +++ b/data/client.kv @@ -147,3 +147,8 @@ rectangle: self.x-2, self.y-2, self.width+4, self.height+4 : pos_hint: {'center_y': 0.5, 'center_x': 0.5} + + size_hint_y: None + height: dp(30) + multiline: False + write_tab: False diff --git a/kvui.py b/kvui.py index 2723654214c1..a5b1b7efd2fa 100644 --- a/kvui.py +++ b/kvui.py @@ -37,7 +37,7 @@ from kivy.base import ExceptionHandler, ExceptionManager from kivy.clock import Clock from kivy.factory import Factory -from kivy.properties import BooleanProperty, ObjectProperty +from kivy.properties import BooleanProperty, ObjectProperty, NumericProperty from kivy.metrics import dp from kivy.effects.scroll import ScrollEffect from kivy.uix.widget import Widget @@ -60,6 +60,7 @@ from kivy.uix.recycleview.layout import LayoutSelectionBehavior from kivy.animation import Animation from kivy.uix.popup import Popup +from kivy.uix.dropdown import DropDown fade_in_animation = Animation(opacity=0, duration=0) + Animation(opacity=1, duration=0.25) @@ -301,6 +302,49 @@ def apply_selection(self, rv, index, is_selected): self.selected = is_selected +class AutocompleteHintInput(TextInput): + min_chars = NumericProperty(3) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.dropdown = DropDown() + self.dropdown.bind(on_select=lambda instance, x: setattr(self, 'text', x)) + self.bind(on_text_validate=self.on_message) + + def on_message(self, instance): + App.get_running_app().commandprocessor("!hint "+instance.text) + + def on_text(self, instance, value): + if len(value) >= self.min_chars: + self.dropdown.clear_widgets() + ctx: context_type = App.get_running_app().ctx + if not ctx.game: + return + item_names = ctx.item_names._game_store[ctx.game].values() + + def on_press(button: Button): + split_text = MarkupLabel(text=button.text).markup + return self.dropdown.select("".join(text_frag for text_frag in split_text + if not text_frag.startswith("["))) + lowered = value.lower() + for item_name in item_names: + try: + index = item_name.lower().index(lowered) + except ValueError: + pass # substring not found + else: + text = escape_markup(item_name) + text = text[:index] + "[b]" + text[index:index+len(value)]+"[/b]"+text[index+len(value):] + btn = Button(text=text, size_hint_y=None, height=dp(30), markup=True) + btn.bind(on_release=on_press) + self.dropdown.add_widget(btn) + if not self.dropdown.attach_to: + self.dropdown.open(self) + else: + self.dropdown.dismiss() + + class HintLabel(RecycleDataViewBehavior, BoxLayout): selected = BooleanProperty(False) striped = BooleanProperty(False) @@ -536,8 +580,10 @@ def connect_bar_validate(sender): # show Archipelago tab if other logging is present self.tabs.add_widget(panel) - hint_panel = self.add_client_tab("Hints", HintLog(self.json_to_kivy_parser)) + hint_panel = self.add_client_tab("Hints", HintLayout()) + self.hint_log = HintLog(self.json_to_kivy_parser) self.log_panels["Hints"] = hint_panel.content + hint_panel.content.add_widget(self.hint_log) if len(self.logging_pairs) == 1: self.tabs.default_tab_text = "Archipelago" @@ -664,7 +710,7 @@ def set_new_energy_link_value(self): def update_hints(self): hints = self.ctx.stored_data[f"_read_hints_{self.ctx.team}_{self.ctx.slot}"] - self.log_panels["Hints"].refresh_hints(hints) + self.hint_log.refresh_hints(hints) # default F1 keybind, opens a settings menu, that seems to break the layout engine once closed def open_settings(self, *largs): @@ -719,6 +765,17 @@ def fix_heights(self): element.height = element.texture_size[1] +class HintLayout(BoxLayout): + orientation = "vertical" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + boxlayout = BoxLayout(orientation="horizontal", size_hint_y=None, height=dp(30)) + boxlayout.add_widget(Label(text="New Hint:", size_hint_x=None, size_hint_y=None, height=dp(30))) + boxlayout.add_widget(AutocompleteHintInput()) + self.add_widget(boxlayout) + + class HintLog(RecycleView): header = { "receiving": {"text": "[u]Receiving Player[/u]"},