159 lines
5.3 KiB
Python
159 lines
5.3 KiB
Python
|
|
"""McRogueFace - Selection Menu Widget (enhanced)
|
||
|
|
|
||
|
|
Documentation: https://mcrogueface.github.io/cookbook/ui_menu
|
||
|
|
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_menu_enhanced.py
|
||
|
|
|
||
|
|
This code is extracted from the McRogueFace documentation and can be
|
||
|
|
run directly with: ./mcrogueface path/to/this/file.py
|
||
|
|
"""
|
||
|
|
|
||
|
|
import mcrfpy
|
||
|
|
|
||
|
|
class MenuBar:
|
||
|
|
"""Horizontal menu bar with dropdown submenus."""
|
||
|
|
|
||
|
|
def __init__(self, y=0, items=None):
|
||
|
|
"""
|
||
|
|
Create a menu bar.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
y: Y position (usually 0 for top)
|
||
|
|
items: List of dicts with 'label' and 'options' keys
|
||
|
|
"""
|
||
|
|
self.y = y
|
||
|
|
self.items = items or []
|
||
|
|
self.selected_item = 0
|
||
|
|
self.dropdown_open = False
|
||
|
|
self.dropdown_selected = 0
|
||
|
|
|
||
|
|
self.item_width = 100
|
||
|
|
self.height = 30
|
||
|
|
|
||
|
|
# Main bar frame
|
||
|
|
self.bar = mcrfpy.Frame(0, y, 1024, self.height)
|
||
|
|
self.bar.fill_color = mcrfpy.Color(50, 50, 70)
|
||
|
|
self.bar.outline = 0
|
||
|
|
|
||
|
|
# Item captions
|
||
|
|
self.item_captions = []
|
||
|
|
for i, item in enumerate(items):
|
||
|
|
cap = mcrfpy.Caption(
|
||
|
|
item['label'],
|
||
|
|
mcrfpy.default_font,
|
||
|
|
10 + i * self.item_width,
|
||
|
|
y + 7
|
||
|
|
)
|
||
|
|
cap.fill_color = mcrfpy.Color(200, 200, 200)
|
||
|
|
self.item_captions.append(cap)
|
||
|
|
|
||
|
|
# Dropdown panel (hidden initially)
|
||
|
|
self.dropdown = None
|
||
|
|
self.dropdown_captions = []
|
||
|
|
|
||
|
|
def _update_highlight(self):
|
||
|
|
"""Update visual selection on bar."""
|
||
|
|
for i, cap in enumerate(self.item_captions):
|
||
|
|
if i == self.selected_item and self.dropdown_open:
|
||
|
|
cap.fill_color = mcrfpy.Color(255, 255, 100)
|
||
|
|
else:
|
||
|
|
cap.fill_color = mcrfpy.Color(200, 200, 200)
|
||
|
|
|
||
|
|
def _show_dropdown(self, ui):
|
||
|
|
"""Show dropdown for selected item."""
|
||
|
|
# Remove existing dropdown
|
||
|
|
self._hide_dropdown(ui)
|
||
|
|
|
||
|
|
item = self.items[self.selected_item]
|
||
|
|
options = item.get('options', [])
|
||
|
|
|
||
|
|
if not options:
|
||
|
|
return
|
||
|
|
|
||
|
|
x = 5 + self.selected_item * self.item_width
|
||
|
|
y = self.y + self.height
|
||
|
|
width = 150
|
||
|
|
height = len(options) * 25 + 10
|
||
|
|
|
||
|
|
self.dropdown = mcrfpy.Frame(x, y, width, height)
|
||
|
|
self.dropdown.fill_color = mcrfpy.Color(40, 40, 60, 250)
|
||
|
|
self.dropdown.outline = 1
|
||
|
|
self.dropdown.outline_color = mcrfpy.Color(80, 80, 100)
|
||
|
|
ui.append(self.dropdown)
|
||
|
|
|
||
|
|
self.dropdown_captions = []
|
||
|
|
for i, opt in enumerate(options):
|
||
|
|
cap = mcrfpy.Caption(
|
||
|
|
opt['label'],
|
||
|
|
mcrfpy.default_font,
|
||
|
|
x + 10,
|
||
|
|
y + 5 + i * 25
|
||
|
|
)
|
||
|
|
cap.fill_color = mcrfpy.Color(200, 200, 200)
|
||
|
|
self.dropdown_captions.append(cap)
|
||
|
|
ui.append(cap)
|
||
|
|
|
||
|
|
self.dropdown_selected = 0
|
||
|
|
self._update_dropdown_highlight()
|
||
|
|
|
||
|
|
def _hide_dropdown(self, ui):
|
||
|
|
"""Hide dropdown menu."""
|
||
|
|
if self.dropdown:
|
||
|
|
try:
|
||
|
|
ui.remove(self.dropdown)
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
self.dropdown = None
|
||
|
|
|
||
|
|
for cap in self.dropdown_captions:
|
||
|
|
try:
|
||
|
|
ui.remove(cap)
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
self.dropdown_captions = []
|
||
|
|
|
||
|
|
def _update_dropdown_highlight(self):
|
||
|
|
"""Update dropdown selection highlight."""
|
||
|
|
for i, cap in enumerate(self.dropdown_captions):
|
||
|
|
if i == self.dropdown_selected:
|
||
|
|
cap.fill_color = mcrfpy.Color(255, 255, 100)
|
||
|
|
else:
|
||
|
|
cap.fill_color = mcrfpy.Color(200, 200, 200)
|
||
|
|
|
||
|
|
def add_to_scene(self, ui):
|
||
|
|
ui.append(self.bar)
|
||
|
|
for cap in self.item_captions:
|
||
|
|
ui.append(cap)
|
||
|
|
|
||
|
|
def handle_key(self, key, ui):
|
||
|
|
"""Handle keyboard navigation."""
|
||
|
|
if not self.dropdown_open:
|
||
|
|
if key == "Left":
|
||
|
|
self.selected_item = (self.selected_item - 1) % len(self.items)
|
||
|
|
self._update_highlight()
|
||
|
|
elif key == "Right":
|
||
|
|
self.selected_item = (self.selected_item + 1) % len(self.items)
|
||
|
|
self._update_highlight()
|
||
|
|
elif key == "Return" or key == "Down":
|
||
|
|
self.dropdown_open = True
|
||
|
|
self._show_dropdown(ui)
|
||
|
|
self._update_highlight()
|
||
|
|
else:
|
||
|
|
if key == "Up":
|
||
|
|
options = self.items[self.selected_item].get('options', [])
|
||
|
|
self.dropdown_selected = (self.dropdown_selected - 1) % len(options)
|
||
|
|
self._update_dropdown_highlight()
|
||
|
|
elif key == "Down":
|
||
|
|
options = self.items[self.selected_item].get('options', [])
|
||
|
|
self.dropdown_selected = (self.dropdown_selected + 1) % len(options)
|
||
|
|
self._update_dropdown_highlight()
|
||
|
|
elif key == "Return":
|
||
|
|
opt = self.items[self.selected_item]['options'][self.dropdown_selected]
|
||
|
|
if opt.get('action'):
|
||
|
|
opt['action']()
|
||
|
|
self.dropdown_open = False
|
||
|
|
self._hide_dropdown(ui)
|
||
|
|
self._update_highlight()
|
||
|
|
elif key == "Escape":
|
||
|
|
self.dropdown_open = False
|
||
|
|
self._hide_dropdown(ui)
|
||
|
|
self._update_highlight()
|