draft tutorial revisions

This commit is contained in:
John McCardle 2026-01-03 11:01:10 -05:00
commit 48359b5a48
70 changed files with 6216 additions and 28 deletions

View file

@ -0,0 +1,120 @@
"""McRogueFace - Health Bar Widget (animated)
Documentation: https://mcrogueface.github.io/cookbook/ui_health_bar
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_health_bar_animated.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
class AnimatedHealthBar:
"""Health bar with smooth fill animation."""
def __init__(self, x, y, w, h, current, maximum):
self.x = x
self.y = y
self.w = w
self.h = h
self.current = current
self.display_current = current # What's visually shown
self.maximum = maximum
self.timer_name = f"hp_anim_{id(self)}"
# Background
self.background = mcrfpy.Frame(x, y, w, h)
self.background.fill_color = mcrfpy.Color(40, 40, 40)
self.background.outline = 2
self.background.outline_color = mcrfpy.Color(60, 60, 60)
# Damage preview (shows recent damage in different color)
self.damage_fill = mcrfpy.Frame(x + 2, y + 2, w - 4, h - 4)
self.damage_fill.fill_color = mcrfpy.Color(180, 50, 50)
self.damage_fill.outline = 0
# Main fill
self.fill = mcrfpy.Frame(x + 2, y + 2, w - 4, h - 4)
self.fill.fill_color = mcrfpy.Color(50, 200, 50)
self.fill.outline = 0
self._update_display()
def _update_display(self):
"""Update the visual fill based on display_current."""
ratio = max(0, min(1, self.display_current / self.maximum))
self.fill.w = (self.w - 4) * ratio
# Color based on ratio
if ratio > 0.6:
self.fill.fill_color = mcrfpy.Color(50, 200, 50)
elif ratio > 0.3:
self.fill.fill_color = mcrfpy.Color(230, 180, 30)
else:
self.fill.fill_color = mcrfpy.Color(200, 50, 50)
def set_health(self, new_current, animate=True):
"""
Set health with optional animation.
Args:
new_current: New health value
animate: Whether to animate the transition
"""
old_current = self.current
self.current = max(0, min(self.maximum, new_current))
if not animate:
self.display_current = self.current
self._update_display()
return
# Show damage preview immediately
if self.current < old_current:
damage_ratio = self.current / self.maximum
self.damage_fill.w = (self.w - 4) * (old_current / self.maximum)
# Animate the fill
self._start_animation()
def _start_animation(self):
"""Start animating toward target health."""
mcrfpy.delTimer(self.timer_name)
def animate_step(dt):
# Lerp toward target
diff = self.current - self.display_current
if abs(diff) < 0.5:
self.display_current = self.current
mcrfpy.delTimer(self.timer_name)
# Also update damage preview
self.damage_fill.w = self.fill.w
else:
# Move 10% of the way each frame
self.display_current += diff * 0.1
self._update_display()
mcrfpy.setTimer(self.timer_name, animate_step, 16)
def damage(self, amount):
"""Apply damage with animation."""
self.set_health(self.current - amount, animate=True)
def heal(self, amount):
"""Apply healing with animation."""
self.set_health(self.current + amount, animate=True)
def add_to_scene(self, ui):
"""Add all frames to scene."""
ui.append(self.background)
ui.append(self.damage_fill)
ui.append(self.fill)
# Usage
hp_bar = AnimatedHealthBar(50, 50, 300, 30, current=100, maximum=100)
hp_bar.add_to_scene(ui)
# Damage will animate smoothly
hp_bar.damage(40)

View file

@ -0,0 +1,43 @@
"""McRogueFace - Health Bar Widget (basic)
Documentation: https://mcrogueface.github.io/cookbook/ui_health_bar
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_health_bar_basic.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
mcrfpy.createScene("game")
mcrfpy.setScene("game")
ui = mcrfpy.sceneUI("game")
# Player health bar at top
player_hp = EnhancedHealthBar(10, 10, 300, 30, 100, 100)
player_hp.add_to_scene(ui)
# Enemy health bar
enemy_hp = EnhancedHealthBar(400, 10, 200, 20, 50, 50)
enemy_hp.add_to_scene(ui)
# Simulate combat
def combat_tick(dt):
import random
if random.random() < 0.3:
player_hp.damage(random.randint(5, 15))
if random.random() < 0.4:
enemy_hp.damage(random.randint(3, 8))
mcrfpy.setTimer("combat", combat_tick, 1000)
# Keyboard controls for testing
def on_key(key, state):
if state != "start":
return
if key == "H":
player_hp.heal(20)
elif key == "D":
player_hp.damage(10)
mcrfpy.keypressScene(on_key)

View file

@ -0,0 +1,123 @@
"""McRogueFace - Health Bar Widget (enhanced)
Documentation: https://mcrogueface.github.io/cookbook/ui_health_bar
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_health_bar_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 EnhancedHealthBar:
"""Health bar with text display, color transitions, and animations."""
def __init__(self, x, y, w, h, current, maximum, show_text=True):
self.x = x
self.y = y
self.w = w
self.h = h
self.current = current
self.maximum = maximum
self.show_text = show_text
# Color thresholds (ratio -> color)
self.colors = {
0.6: mcrfpy.Color(50, 205, 50), # Green when > 60%
0.3: mcrfpy.Color(255, 165, 0), # Orange when > 30%
0.0: mcrfpy.Color(220, 20, 20), # Red when <= 30%
}
# Background frame with dark fill
self.background = mcrfpy.Frame(x, y, w, h)
self.background.fill_color = mcrfpy.Color(30, 30, 30)
self.background.outline = 2
self.background.outline_color = mcrfpy.Color(100, 100, 100)
# Fill frame (nested inside background conceptually)
padding = 2
self.fill = mcrfpy.Frame(
x + padding,
y + padding,
w - padding * 2,
h - padding * 2
)
self.fill.outline = 0
# Text label
self.label = None
if show_text:
self.label = mcrfpy.Caption(
"",
mcrfpy.default_font,
x + w / 2 - 20,
y + h / 2 - 8
)
self.label.fill_color = mcrfpy.Color(255, 255, 255)
self.label.outline = 1
self.label.outline_color = mcrfpy.Color(0, 0, 0)
self._update()
def _get_color_for_ratio(self, ratio):
"""Get the appropriate color based on health ratio."""
for threshold, color in sorted(self.colors.items(), reverse=True):
if ratio > threshold:
return color
# Return the lowest threshold color if ratio is 0 or below
return self.colors[0.0]
def _update(self):
"""Update fill width, color, and text."""
ratio = max(0, min(1, self.current / self.maximum))
# Update fill width (accounting for padding)
padding = 2
self.fill.w = (self.w - padding * 2) * ratio
# Update color based on ratio
self.fill.fill_color = self._get_color_for_ratio(ratio)
# Update text
if self.label:
self.label.text = f"{int(self.current)}/{int(self.maximum)}"
# Center the text
text_width = len(self.label.text) * 8 # Approximate
self.label.x = self.x + (self.w - text_width) / 2
def set_health(self, current, maximum=None):
"""Update health values."""
self.current = max(0, current)
if maximum is not None:
self.maximum = maximum
self._update()
def damage(self, amount):
"""Apply damage (convenience method)."""
self.set_health(self.current - amount)
def heal(self, amount):
"""Apply healing (convenience method)."""
self.set_health(min(self.maximum, self.current + amount))
def add_to_scene(self, ui):
"""Add all components to scene UI."""
ui.append(self.background)
ui.append(self.fill)
if self.label:
ui.append(self.label)
# Usage
mcrfpy.createScene("demo")
mcrfpy.setScene("demo")
ui = mcrfpy.sceneUI("demo")
# Create enhanced health bar
hp = EnhancedHealthBar(50, 50, 250, 25, current=100, maximum=100)
hp.add_to_scene(ui)
# Simulate damage
hp.damage(30) # Now 70/100, shows green
hp.damage(25) # Now 45/100, shows orange
hp.damage(20) # Now 25/100, shows red

View file

@ -0,0 +1,108 @@
"""McRogueFace - Health Bar Widget (multi)
Documentation: https://mcrogueface.github.io/cookbook/ui_health_bar
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_health_bar_multi.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
class ResourceBar:
"""Generic resource bar that can represent any stat."""
def __init__(self, x, y, w, h, current, maximum,
fill_color, bg_color=None, label=""):
self.x = x
self.y = y
self.w = w
self.h = h
self.current = current
self.maximum = maximum
self.label_text = label
if bg_color is None:
bg_color = mcrfpy.Color(30, 30, 30)
# Background
self.background = mcrfpy.Frame(x, y, w, h)
self.background.fill_color = bg_color
self.background.outline = 1
self.background.outline_color = mcrfpy.Color(60, 60, 60)
# Fill
self.fill = mcrfpy.Frame(x + 1, y + 1, w - 2, h - 2)
self.fill.fill_color = fill_color
self.fill.outline = 0
# Label (left side)
self.label = mcrfpy.Caption(label, mcrfpy.default_font, x - 30, y + 2)
self.label.fill_color = mcrfpy.Color(200, 200, 200)
self._update()
def _update(self):
ratio = max(0, min(1, self.current / self.maximum))
self.fill.w = (self.w - 2) * ratio
def set_value(self, current, maximum=None):
self.current = max(0, current)
if maximum:
self.maximum = maximum
self._update()
def add_to_scene(self, ui):
if self.label_text:
ui.append(self.label)
ui.append(self.background)
ui.append(self.fill)
class PlayerStats:
"""Collection of resource bars for a player."""
def __init__(self, x, y):
bar_width = 200
bar_height = 18
spacing = 25
self.hp = ResourceBar(
x, y, bar_width, bar_height,
current=100, maximum=100,
fill_color=mcrfpy.Color(220, 50, 50),
label="HP"
)
self.mp = ResourceBar(
x, y + spacing, bar_width, bar_height,
current=50, maximum=50,
fill_color=mcrfpy.Color(50, 100, 220),
label="MP"
)
self.stamina = ResourceBar(
x, y + spacing * 2, bar_width, bar_height,
current=80, maximum=80,
fill_color=mcrfpy.Color(50, 180, 50),
label="SP"
)
def add_to_scene(self, ui):
self.hp.add_to_scene(ui)
self.mp.add_to_scene(ui)
self.stamina.add_to_scene(ui)
# Usage
mcrfpy.createScene("stats_demo")
mcrfpy.setScene("stats_demo")
ui = mcrfpy.sceneUI("stats_demo")
stats = PlayerStats(80, 20)
stats.add_to_scene(ui)
# Update individual stats
stats.hp.set_value(75)
stats.mp.set_value(30)
stats.stamina.set_value(60)

View file

@ -0,0 +1,53 @@
"""McRogueFace - Selection Menu Widget (basic)
Documentation: https://mcrogueface.github.io/cookbook/ui_menu
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_menu_basic.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
# Setup
mcrfpy.createScene("main_menu")
mcrfpy.setScene("main_menu")
ui = mcrfpy.sceneUI("main_menu")
# Background
bg = mcrfpy.Frame(0, 0, 1024, 768)
bg.fill_color = mcrfpy.Color(20, 20, 35)
ui.append(bg)
# Title
title = mcrfpy.Caption("DUNGEON QUEST", mcrfpy.default_font, 350, 100)
title.fill_color = mcrfpy.Color(255, 200, 50)
ui.append(title)
# Menu
def start_game():
print("Starting game...")
def show_options():
print("Options...")
menu = Menu(
362, 250,
["New Game", "Continue", "Options", "Quit"],
lambda i, opt: {
0: start_game,
1: lambda: print("Continue..."),
2: show_options,
3: mcrfpy.exit
}.get(i, lambda: None)(),
title="Main Menu"
)
menu.add_to_scene(ui)
# Input
def on_key(key, state):
if state != "start":
return
menu.handle_key(key)
mcrfpy.keypressScene(on_key)

View file

@ -0,0 +1,159 @@
"""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()

View file

@ -0,0 +1,54 @@
"""McRogueFace - Message Log Widget (basic)
Documentation: https://mcrogueface.github.io/cookbook/ui_message_log
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_message_log_basic.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
# Initialize
mcrfpy.createScene("game")
mcrfpy.setScene("game")
ui = mcrfpy.sceneUI("game")
# Create log at bottom of screen
log = EnhancedMessageLog(10, 500, 700, 250, line_height=20)
ui.append(log.frame)
# Simulate game events
def simulate_combat(dt):
import random
events = [
("You swing your sword!", "combat"),
("The orc dodges!", "combat"),
("Critical hit!", "combat"),
("You found a potion!", "loot"),
]
event = random.choice(events)
log.add(event[0], event[1])
# Add messages every 2 seconds for demo
mcrfpy.setTimer("combat_sim", simulate_combat, 2000)
# Keyboard controls
def on_key(key, state):
if state != "start":
return
if key == "PageUp":
log.scroll_up(3)
elif key == "PageDown":
log.scroll_down(3)
elif key == "C":
log.set_filter('combat')
elif key == "L":
log.set_filter('loot')
elif key == "A":
log.set_filter(None) # All
mcrfpy.keypressScene(on_key)
log.system("Press PageUp/PageDown to scroll")
log.system("Press C for combat, L for loot, A for all")

View file

@ -0,0 +1,27 @@
"""McRogueFace - Message Log Widget (enhanced)
Documentation: https://mcrogueface.github.io/cookbook/ui_message_log
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_message_log_enhanced.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
def handle_keys(key, state):
if state != "start":
return
if key == "PageUp":
log.scroll_up(5)
elif key == "PageDown":
log.scroll_down(5)
mcrfpy.keypressScene(handle_keys)
# Or with mouse scroll on the frame
def on_log_scroll(x, y, button, action):
# Note: You may need to implement scroll detection
# based on your input system
pass
log.frame.click = on_log_scroll

View file

@ -0,0 +1,69 @@
"""McRogueFace - Modal Dialog Widget (basic)
Documentation: https://mcrogueface.github.io/cookbook/ui_modal_dialog
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_modal_dialog_basic.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
# Scene setup
mcrfpy.createScene("game")
mcrfpy.setScene("game")
ui = mcrfpy.sceneUI("game")
# Game background
bg = mcrfpy.Frame(0, 0, 1024, 768)
bg.fill_color = mcrfpy.Color(25, 35, 45)
ui.append(bg)
title = mcrfpy.Caption("My Game", mcrfpy.default_font, 450, 50)
title.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(title)
# Quit button
quit_btn = mcrfpy.Frame(430, 400, 160, 50)
quit_btn.fill_color = mcrfpy.Color(150, 50, 50)
quit_btn.outline = 2
quit_btn.outline_color = mcrfpy.Color(200, 100, 100)
ui.append(quit_btn)
quit_label = mcrfpy.Caption("Quit Game", mcrfpy.default_font, 460, 415)
quit_label.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(quit_label)
# Confirmation dialog
confirm_dialog = None
def show_quit_confirm():
global confirm_dialog
def on_response(index, label):
if label == "Yes":
mcrfpy.exit()
confirm_dialog = EnhancedDialog(
"Quit Game?",
"Are you sure you want to quit?\nUnsaved progress will be lost.",
["Yes", "No"],
DialogStyle.WARNING,
on_response
)
confirm_dialog.add_to_scene(ui)
confirm_dialog.show()
quit_btn.click = lambda x, y, b, a: show_quit_confirm() if a == "start" else None
def on_key(key, state):
if state != "start":
return
if confirm_dialog and confirm_dialog.handle_key(key):
return
if key == "Escape":
show_quit_confirm()
mcrfpy.keypressScene(on_key)

View file

@ -0,0 +1,78 @@
"""McRogueFace - Modal Dialog Widget (enhanced)
Documentation: https://mcrogueface.github.io/cookbook/ui_modal_dialog
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_modal_dialog_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 DialogManager:
"""Manages a queue of dialogs."""
def __init__(self, ui):
self.ui = ui
self.queue = []
self.current = None
def show(self, title, message, buttons=None, style=None, callback=None):
"""
Queue a dialog to show.
If no dialog is active, shows immediately.
Otherwise, queues for later.
"""
dialog_data = {
'title': title,
'message': message,
'buttons': buttons or ["OK"],
'style': style or DialogStyle.INFO,
'callback': callback
}
if self.current is None:
self._show_dialog(dialog_data)
else:
self.queue.append(dialog_data)
def _show_dialog(self, data):
"""Actually display a dialog."""
def on_close(index, label):
if data['callback']:
data['callback'](index, label)
self._on_dialog_closed()
self.current = EnhancedDialog(
data['title'],
data['message'],
data['buttons'],
data['style'],
on_close
)
self.current.add_to_scene(self.ui)
self.current.show()
def _on_dialog_closed(self):
"""Handle dialog close, show next if queued."""
self.current = None
if self.queue:
next_dialog = self.queue.pop(0)
self._show_dialog(next_dialog)
def handle_key(self, key):
"""Forward key events to current dialog."""
if self.current:
return self.current.handle_key(key)
return False
# Usage
manager = DialogManager(ui)
# Queue multiple dialogs
manager.show("First", "This is the first message")
manager.show("Second", "This appears after closing the first")
manager.show("Third", "And this is last", ["Done"])

View file

@ -0,0 +1,65 @@
"""McRogueFace - Tooltip on Hover (basic)
Documentation: https://mcrogueface.github.io/cookbook/ui_tooltip
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_tooltip_basic.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
mcrfpy.createScene("game")
mcrfpy.setScene("game")
ui = mcrfpy.sceneUI("game")
# Background
bg = mcrfpy.Frame(0, 0, 1024, 768)
bg.fill_color = mcrfpy.Color(25, 25, 35)
ui.append(bg)
# Create inventory slots with tooltips
class InventorySlot:
def __init__(self, x, y, item_name, item_desc, tooltip_mgr):
self.frame = mcrfpy.Frame(x, y, 50, 50)
self.frame.fill_color = mcrfpy.Color(50, 50, 60)
self.frame.outline = 1
self.frame.outline_color = mcrfpy.Color(80, 80, 90)
self.label = mcrfpy.Caption(item_name[:3], mcrfpy.default_font, x + 10, y + 15)
self.label.fill_color = mcrfpy.Color(200, 200, 200)
tooltip_mgr.register(self.frame, item_desc, title=item_name)
def add_to_scene(self, ui):
ui.append(self.frame)
ui.append(self.label)
# Setup tooltip manager
tips = TooltipManager()
tips.hover_delay = 300
# Create inventory
items = [
("Health Potion", "Restores 50 HP\nConsumable"),
("Mana Crystal", "Restores 30 MP\nConsumable"),
("Iron Key", "Opens iron doors\nQuest Item"),
("Gold Ring", "Worth 100 gold\nSell to merchant"),
]
slots = []
for i, (name, desc) in enumerate(items):
slot = InventorySlot(100 + i * 60, 100, name, desc, tips)
slot.add_to_scene(ui)
slots.append(slot)
# Add tooltip last
tips.add_to_scene(ui)
# Update loop
def update(dt):
from mcrfpy import automation
x, y = automation.position()
tips.update(x, y)
mcrfpy.setTimer("update", update, 50)

View file

@ -0,0 +1,80 @@
"""McRogueFace - Tooltip on Hover (multi)
Documentation: https://mcrogueface.github.io/cookbook/ui_tooltip
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_tooltip_multi.py
This code is extracted from the McRogueFace documentation and can be
run directly with: ./mcrogueface path/to/this/file.py
"""
import mcrfpy
def create_info_icon(x, y, tooltip_text, ui):
"""
Create an info icon that shows tooltip on hover.
Args:
x, y: Position of the icon
tooltip_text: Text to show
ui: Scene UI to add elements to
"""
# Info icon (small circle with "i")
icon = mcrfpy.Frame(x, y, 20, 20)
icon.fill_color = mcrfpy.Color(70, 130, 180)
icon.outline = 1
icon.outline_color = mcrfpy.Color(100, 160, 210)
icon_label = mcrfpy.Caption("i", mcrfpy.default_font, x + 6, y + 2)
icon_label.fill_color = mcrfpy.Color(255, 255, 255)
# Tooltip (positioned to the right of icon)
tip_frame = mcrfpy.Frame(x + 25, y - 5, 180, 50)
tip_frame.fill_color = mcrfpy.Color(40, 40, 55, 240)
tip_frame.outline = 1
tip_frame.outline_color = mcrfpy.Color(80, 80, 100)
tip_frame.visible = False
tip_text = mcrfpy.Caption(tooltip_text, mcrfpy.default_font, x + 33, y + 3)
tip_text.fill_color = mcrfpy.Color(220, 220, 220)
tip_text.visible = False
# Hover behavior
def on_icon_hover(mx, my, button, action):
tip_frame.visible = True
tip_text.visible = True
icon.click = on_icon_hover
# Track when to hide
def check_hover(dt):
from mcrfpy import automation
mx, my = automation.position()
if not (icon.x <= mx <= icon.x + icon.w and
icon.y <= my <= icon.y + icon.h):
if tip_frame.visible:
tip_frame.visible = False
tip_text.visible = False
timer_name = f"info_hover_{id(icon)}"
mcrfpy.setTimer(timer_name, check_hover, 100)
# Add to scene
ui.append(icon)
ui.append(icon_label)
ui.append(tip_frame)
ui.append(tip_text)
return icon
# Usage
mcrfpy.createScene("info_demo")
mcrfpy.setScene("info_demo")
ui = mcrfpy.sceneUI("info_demo")
# Setting with info icon
setting_label = mcrfpy.Caption("Difficulty:", mcrfpy.default_font, 100, 100)
setting_label.fill_color = mcrfpy.Color(200, 200, 200)
ui.append(setting_label)
create_info_icon(200, 98, "Affects enemy\nHP and damage", ui)