summaryrefslogtreecommitdiff
path: root/menu.py
diff options
context:
space:
mode:
authordavidovski <david@sendula.com>2023-01-05 11:35:28 +0000
committerdavidovski <david@sendula.com>2023-01-05 11:35:28 +0000
commitb99dbb396c313f4b3130b566c0df42c10eec6084 (patch)
treeffd2bbebe47f66fd1c21000b371ebc897ef84e34 /menu.py
Initial CommitHEADmain
Diffstat (limited to 'menu.py')
-rw-r--r--menu.py282
1 files changed, 282 insertions, 0 deletions
diff --git a/menu.py b/menu.py
new file mode 100644
index 0000000..84e522c
--- /dev/null
+++ b/menu.py
@@ -0,0 +1,282 @@
+import re
+
+from font import Font
+from game import Game, GameSprite
+from sprite import Sprite
+
+
+class MenuItem(GameSprite):
+ """A selectable item in a menu"""
+
+ def __init__(self, game: Game, text, callback):
+ """Initialise the item
+
+ :param game: The game which this belongs to
+ :type game: Game
+ :param text: Text to display for this item
+ :param callback: function to call when this item is selected
+ """
+ image = Font.load_text(game.texture_factory, text)
+ self.text = text
+ self.callback = callback
+ super().__init__(game, image)
+
+ def set_text(self, text):
+ """Update the text of an entry
+
+ :param text:
+ """
+ self.text = text
+ image = Font.load_text(self.game.texture_factory, text)
+ self.set_image(image)
+
+
+class Menu():
+ """Menu object with selectable entries"""
+
+ def __init__(self, game: Game, title) -> None:
+ """Initialise the menu object
+
+ :param game: The game which this belongs to
+ :type game: Game
+ :param title: The title of this menu
+ """
+ self.game = game
+ self.title = title
+ self.padding = 5
+
+ self.game.inputs.add_keypress_handler(self.on_key)
+
+ self.menu_items = []
+
+ self.selection = 0
+
+ carret_image = Font.load_text(game.texture_factory, ">")
+ self.carret = GameSprite(self.game, carret_image)
+
+ title_image = Font.load_text(game.texture_factory, title)
+
+ position = ((self.game.w - len(title)*Font.FONT_WIDTH)//2, 5*2)
+ self.title = GameSprite(self.game, title_image)
+ self.title.set_pos(position)
+
+ self.hidden = True
+
+ self.alpha = 0
+
+ def on_key(self, _):
+ """Handle Key press events
+
+
+ :param event: The key press event to handle
+ """
+ if not self.hidden:
+ inp = self.game.inputs
+
+ if inp.k_down or inp.k_right:
+ self.selection += 1
+
+ if inp.k_up or inp.k_left:
+ self.selection -= 1
+
+ self.selection %= len(self.menu_items)
+ self.update_carret()
+
+ if inp.k_action:
+ self.menu_items[self.selection].callback()
+
+ return True
+
+ return False
+
+ def update_carret(self):
+ """Move the carret to the correct location"""
+ selected = self.menu_items[self.selection]
+ x = selected.x - Font.FONT_SIZE
+ y = selected.y
+ self.carret.set_pos((x, y))
+
+ def arrange_items(self):
+ """Move the menu items to their correct positions"""
+ cy = self.game.h // 2
+
+ max_height = sum(item.h for item in self.menu_items
+ ) + self.padding*(len(self.menu_items)-1)
+ top = cy - max_height//2
+
+ y = top
+
+ for item in self.menu_items:
+ item_x = (self.game.w - item.w) // 2
+ item_y = y
+ y += item.h + self.padding
+ item.set_pos((item_x, item_y))
+ self.update_carret()
+
+ def add_item(self, text: str, callback, index=-1):
+ """Add a menu item
+
+ :param text: Label of this item
+ :type text: str
+ :param callback: Function to call when it is selected
+ :param index: Where to insert this item
+ """
+ if index == -1:
+ index = len(self.menu_items)
+ self.menu_items.insert(index, MenuItem(self.game, text, callback))
+ self.arrange_items()
+
+ def show(self):
+ """Make this object visible"""
+ if self.hidden:
+ for m in self.menu_items:
+ m.show()
+ m.send_to_front()
+ self.carret.show()
+ self.carret.send_to_front()
+
+ self.title.show()
+ self.title.send_to_front()
+
+ self.hidden = False
+
+ def hide(self):
+ """Make this object invisible"""
+ if not self.hidden:
+ for m in self.menu_items:
+ m.hide()
+ self.carret.hide()
+
+ self.title.hide()
+
+ self.hidden = True
+
+ def tick(self):
+ """Update this object"""
+ self.alpha += 1
+ if not self.hidden:
+ if (self.alpha//15) % 2 == 0:
+ self.carret.show()
+ else:
+ self.carret.hide()
+ else:
+ self.carret.hide()
+
+ def has_item(self, text):
+ """Return true if matching item is found
+
+ :param text: Label text to match
+ """
+ for entry in self.menu_items:
+ if entry.text == text:
+ return True
+
+ return False
+
+ def get_item(self, regex) -> MenuItem:
+ """Return an item that is matched
+
+ :param regex: regular expression to match item text to
+ :rtype: MenuItem
+ """
+ for entry in self.menu_items:
+ if re.match(regex, entry.text):
+ return entry
+
+ return self.menu_items[0]
+
+ def edit_item(self, regex, new_text):
+ """Edit the text of a menu item
+
+ :param regex: Regular expression to use to match the item's text to
+ :param new_text: Text to replace to
+ """
+ for entry in self.menu_items:
+ if re.match(regex, entry.text):
+ entry.set_text(new_text)
+
+ self.arrange_items()
+
+ def del_item(self, text):
+ """Remove an item
+
+ :param text: Label text to match
+ """
+ for entry in self.menu_items:
+ if entry.text == text:
+ entry.destroy()
+ self.menu_items = Sprite.remove_destroyed(self.menu_items)
+ self.arrange_items()
+
+ return False
+
+
+class KeybindsMenu(Menu):
+ """A menu for selecting keybinds on"""
+
+ def __init__(self, game: Game, title):
+ """Initialise the menu
+
+ :param game: The game which this belongs to
+ :type game: Game
+ :param title: The title of this menu
+ """
+ super().__init__(game, title)
+ self.key_selecting = ""
+
+ image = Font.load_text(game.texture_factory, "press any key")
+ self.press_key_sprite = GameSprite(self.game, image)
+ self.press_key_sprite.set_pos(
+ ((self.game.w - self.press_key_sprite.w) // 2, self.game.h // 2))
+
+ def on_key(self, event):
+ """Handle Key press events
+
+
+ :param event: The key press event to handle
+ """
+ if self.key_selecting:
+ key = event.keysym
+ self.set_keybind(self.key_selecting, key)
+ self.press_key_sprite.hide()
+ self.show()
+ return True
+ return super().on_key(event)
+
+ def set_keybind(self, name, key):
+ """set_keybind.
+
+ :param name:
+ :param key:
+ """
+ setattr(self.game.inputs.settings, self.key_selecting, key)
+ self.game.inputs.settings.save_inputs()
+ self.key_selecting = ""
+ self.edit_item(f"{name}\\s*<.*>", self.get_label(name, key))
+
+ def select_keybind(self, keyname):
+ """Allow the user to press a key to decide their keybind
+
+ :param keyname:
+ """
+ self.hide()
+ self.press_key_sprite.show()
+ self.key_selecting = keyname
+
+ def get_set_keybind(self, keyname):
+ """Return a function that sets the keybind of a particular keyname
+
+ :param keyname:
+ """
+ return lambda: self.select_keybind(keyname)
+
+ def get_label(self, name, value):
+ """Get a label for a keybind item
+
+ :param name:
+ :param value:
+ """
+ available_width = (self.game.w - Font.FONT_SIZE*2) // Font.FONT_WIDTH
+ num_spaces = available_width - (len(name) + len(value) + 2) - 1
+ spaces = " " * num_spaces
+ return f"{name} {spaces}<{value}>"