playscii/ui_menu_bar.py

364 lines
10 KiB
Python

from math import ceil
from renderable_sprite import UISpriteRenderable
from ui_button import TEXT_CENTER, UIButton
from ui_colors import UIColors
from ui_element import UIElement
from ui_game_menu_pulldown_item import (
GameMenuData,
GameObjectMenuData,
GameRoomMenuData,
GameStateMenuData,
GameViewMenuData,
GameWorldMenuData,
)
from ui_info_dialog import AboutDialog
from ui_menu_pulldown_item import (
ArtMenuData,
CharColorMenuData,
EditMenuData,
FileMenuData,
FrameMenuData,
HelpMenuData,
LayerMenuData,
ToolMenuData,
ViewMenuData,
)
class MenuButton(UIButton):
caption = "Base Class Menu Button"
caption_justify = TEXT_CENTER
# menu data is just a class w/ little more than a list of items, partly
# so we don't have to list all the items here in a different module
menu_data = None
# styling
normal_bg_color = UIColors.white
hovered_bg_color = UIColors.lightgrey
dimmed_bg_color = UIColors.lightgrey
def __init__(self, element):
UIButton.__init__(self, element)
self.callback = self.open_pulldown
def open_pulldown(self):
# don't open menus if a dialog is up
if self.element.ui.active_dialog:
return
# if clicking the same button twice, close it
if self.element.active_menu_name == self.name:
self.element.close_active_menu()
return
# close any existing menu before opening new one
if self.element.active_menu_name:
self.element.close_active_menu()
# tell pulldown what's opening it, it can populate its items based on
# our data
self.pulldown.open_at(self)
self.element.active_menu_name = self.name
# set button state to be dimmed until menu is closed
self.dimmed = True
# playscii logo button = normal UIButton, opens About screen directly
class PlaysciiMenuButton(UIButton):
name = "playscii"
caption = " "
caption_justify = TEXT_CENTER
width = len(caption) + 2
normal_bg_color = MenuButton.normal_bg_color
hovered_bg_color = MenuButton.hovered_bg_color
dimmed_bg_color = MenuButton.dimmed_bg_color
#
# art mode menu buttons
#
class FileMenuButton(MenuButton):
name = "file"
caption = "File"
menu_data = FileMenuData
class EditMenuButton(MenuButton):
name = "edit"
caption = "Edit"
menu_data = EditMenuData
class ToolMenuButton(MenuButton):
name = "tool"
caption = "Tool"
menu_data = ToolMenuData
class ViewMenuButton(MenuButton):
name = "view"
caption = "View"
menu_data = ViewMenuData
class ArtMenuButton(MenuButton):
name = "art"
caption = "Art"
menu_data = ArtMenuData
class FrameMenuButton(MenuButton):
name = "frame"
caption = "Frame"
menu_data = FrameMenuData
class LayerMenuButton(MenuButton):
name = "layer"
caption = "Layer"
menu_data = LayerMenuData
class CharColorMenuButton(MenuButton):
name = "char_color"
caption = "Char/Color"
menu_data = CharColorMenuData
# (appears in both art and game mode menus)
class HelpMenuButton(MenuButton):
name = "help"
caption = "Help"
menu_data = HelpMenuData
#
# game mode menu buttons
#
class GameMenuButton(MenuButton):
name = "game"
caption = "Game"
menu_data = GameMenuData
class StateMenuButton(MenuButton):
name = "state"
caption = "State"
menu_data = GameStateMenuData
class GameViewMenuButton(MenuButton):
name = "view"
caption = "View"
menu_data = GameViewMenuData
class WorldMenuButton(MenuButton):
name = "world"
caption = "World"
menu_data = GameWorldMenuData
class RoomMenuButton(MenuButton):
name = "room"
caption = "Room"
menu_data = GameRoomMenuData
class ObjectMenuButton(MenuButton):
name = "object"
caption = "Object"
menu_data = GameObjectMenuData
class ModeMenuButton(UIButton):
caption_justify = TEXT_CENTER
normal_bg_color = UIColors.black
normal_fg_color = UIColors.white
# hovered_bg_color = UIColors.lightgrey
# dimmed_bg_color = UIColors.lightgrey
class ArtModeMenuButton(ModeMenuButton):
caption = "Game Mode"
width = len(caption) + 2
class GameModeMenuButton(ModeMenuButton):
caption = "Art Mode"
width = len(caption) + 2
class MenuBar(UIElement):
"main menu bar element, has lots of buttons which control the pulldown"
snap_top = True
snap_left = True
always_consume_input = True
# buttons set in subclasses
button_classes = []
# button to toggle between art and game mode
mode_button_class = None
# empty tiles between each button
button_padding = 1
def __init__(self, ui):
# bitmap icon for about menu button
self.playscii_sprite = UISpriteRenderable(ui.app)
self.mode_button = None
UIElement.__init__(self, ui)
self.active_menu_name = None
# list of menu buttons that can be navigated etc
self.menu_buttons = []
x = PlaysciiMenuButton.width + self.button_padding
for button_class in self.button_classes:
button = button_class(self)
button.width = len(button.caption) + 2
button.x = x
x += button.width + self.button_padding
setattr(self, f"{button.name}_button", button)
# NOTE: callback already defined in MenuButton class,
# menu data for pulldown with set in MenuButton subclass
button.pulldown = self.ui.pulldown
self.menu_buttons.append(button)
playscii_button = PlaysciiMenuButton(self)
playscii_button.callback = self.open_about
# implement Playscii logo menu as a normal UIButton that opens
# the About screen directly
self.menu_buttons.append(playscii_button)
self.reset_icon()
# copy from menu buttons, any buttons past this point are not menus
self.buttons = self.menu_buttons[:]
# toggle mode button at far right
if not self.mode_button_class:
return
self.mode_button = self.mode_button_class(self)
self.mode_button.x = (
int(self.ui.width_tiles * self.ui.scale) - self.mode_button.width
)
self.mode_button.callback = self.toggle_game_mode
self.buttons.append(self.mode_button)
def reset_icon(self):
inv_aspect = self.ui.app.window_height / self.ui.app.window_width
self.playscii_sprite.scale_x = self.art.quad_height * inv_aspect
self.playscii_sprite.scale_y = self.art.quad_height
self.playscii_sprite.x = -1 + self.art.quad_width
self.playscii_sprite.y = 1 - self.art.quad_height
def open_about(self):
if self.ui.active_dialog:
return
self.ui.open_dialog(AboutDialog)
def toggle_game_mode(self):
if self.ui.active_dialog:
return
if not self.ui.app.game_mode:
self.ui.app.enter_game_mode()
else:
self.ui.app.exit_game_mode()
self.ui.app.update_window_title()
def close_active_menu(self):
# un-dim active menu button
for button in self.menu_buttons:
if button.name == self.active_menu_name:
button.dimmed = False
button.set_state("normal")
self.active_menu_name = None
self.ui.pulldown.visible = False
self.ui.keyboard_focus_element = None
self.ui.refocus_keyboard()
def refresh_active_menu(self):
if not self.ui.pulldown.visible:
return
for button in self.menu_buttons:
if button.name == self.active_menu_name:
# don't reset keyboard nav index
self.ui.pulldown.open_at(button, False)
def open_menu_by_name(self, menu_name):
if not self.ui.app.can_edit:
return
for button in self.menu_buttons:
if button.name == menu_name:
button.callback()
def open_menu_by_index(self, index):
if index > len(self.menu_buttons) - 1:
return
# don't navigate to the about menu
# (does this mean it's not accessible via kb-only? probably, that's fine)
if self.menu_buttons[index].name == "playscii":
return
self.menu_buttons[index].callback()
def get_menu_index(self, menu_name):
for i, button in enumerate(self.menu_buttons):
if button.name == self.active_menu_name:
return i
def next_menu(self):
i = self.get_menu_index(self.active_menu_name)
self.open_menu_by_index(i + 1)
def previous_menu(self):
i = self.get_menu_index(self.active_menu_name)
self.open_menu_by_index(i - 1)
def reset_art(self):
self.tile_width = ceil(self.ui.width_tiles * self.ui.scale)
# must resize here, as window width will vary
self.art.resize(self.tile_width, self.tile_height)
# repaint bar contents
bg = self.ui.colors.white
fg = self.ui.colors.black
self.art.clear_frame_layer(0, 0, bg, fg)
# reposition right-justified mode switch button
if self.mode_button:
self.mode_button.x = (
int(self.ui.width_tiles * self.ui.scale) - self.mode_button.width
)
# draw buttons, etc
UIElement.reset_art(self)
self.reset_icon()
def render(self):
UIElement.render(self)
self.playscii_sprite.render()
def destroy(self):
UIElement.destroy(self)
self.playscii_sprite.destroy()
class ArtMenuBar(MenuBar):
button_classes = [
FileMenuButton,
EditMenuButton,
ToolMenuButton,
ViewMenuButton,
ArtMenuButton,
FrameMenuButton,
LayerMenuButton,
CharColorMenuButton,
HelpMenuButton,
]
mode_button_class = GameModeMenuButton
class GameMenuBar(MenuBar):
button_classes = [
GameMenuButton,
StateMenuButton,
GameViewMenuButton,
WorldMenuButton,
RoomMenuButton,
ObjectMenuButton,
HelpMenuButton,
]
game_mode_visible = True
mode_button_class = ArtModeMenuButton