diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..c83a205 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +# For relative imports to work in Python 3.6 +import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__))) diff --git a/menus.py b/menus.py new file mode 100644 index 0000000..d8e6fda --- /dev/null +++ b/menus.py @@ -0,0 +1,83 @@ +import re +from enum import Enum +from typing import List +from textual import events +from textual.message import Message, MessageTarget +from textual.app import ComposeResult +from textual.containers import Container, Horizontal, Vertical +from textual.geometry import Offset, Region, Size +from textual.reactive import var, reactive +from textual.widget import Widget +from textual.widgets import Button, Static + +def to_snake_case(name): + name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + name = re.sub('__([A-Z])', r'_\1', name) + name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name) + return name.lower() + +class Menu(Container): + """A menu widget.""" + + items = var([]) + focus_index = var(0) + + def __init__(self, items: List[str], **kwargs) -> None: + """Initialize a menu.""" + super().__init__(**kwargs) + self.add_class("menu") + self.items = items + for item in items: + if not hasattr(item, "id"): + item.id = "menu_item_" + to_snake_case(item.name) + + def compose(self) -> ComposeResult: + """Define widget structure.""" + for item in self.items: + if item.type == "separator": + yield Static("─" * 20, classes="menu_separator") + else: + yield Button(item.name, id=item.id, classes="menu_item") + + def on_key(self, event: events.Key) -> None: + """Called when the user presses a key.""" + + if event.key == "up": + self.focus_index -= 1 + if self.focus_index < 0: + self.focus_index = len(self.items) - 1 + elif event.key == "down": + self.focus_index += 1 + if self.focus_index >= len(self.items): + self.focus_index = 0 + elif event.key == "enter": + if self.items[self.focus_index].action: + self.items[self.focus_index].action() + # elif self.items[self.focus_index].submenu: + # self.items[self.focus_index].submenu.open() + +class MenuBar(Menu): + """A menu bar widget.""" + + def __init__(self, items: List[str], **kwargs) -> None: + """Initialize a menu bar.""" + super().__init__(items, **kwargs) + self.add_class("menu_bar") + + +class MenuItem: + """A menu item.""" + + def __init__(self, name: str, action = None, type: str = "item", submenu = None) -> None: + """Initialize a menu item.""" + self.name = name + self.action = action + self.type = type + self.submenu = submenu + +class Separator(MenuItem): + """A menu separator.""" + + def __init__(self) -> None: + """Initialize a menu separator.""" + super().__init__("", None, "separator") diff --git a/paint.css b/paint.css index 7e99ade..032f12b 100644 --- a/paint.css +++ b/paint.css @@ -101,3 +101,23 @@ Button { width: 100%; height: 100%; } + +/* MenuBar { */ +.menu_bar { + layout: horizontal; + background: $panel; + height: auto; + dock: top; + /* border-bottom: hkey $panel-darken-2; */ +} +/* MenuItem { */ +.menu_item { + width: auto; + height: auto; +} +.menu Button { + border: none; + height: 3; + /* This seems to be the only way to get the button to shrink? */ + max-width: 8; +} \ No newline at end of file diff --git a/paint.py b/paint.py index 8046aa5..ed1b506 100644 --- a/paint.py +++ b/paint.py @@ -16,6 +16,7 @@ from textual.reactive import var, reactive from textual.strip import Strip from textual.widget import Widget from textual.widgets import Button, Static +from menus import MenuBar, Menu, MenuItem, Separator class Tool(Enum): """The tools available in the Paint app.""" @@ -581,6 +582,26 @@ class PaintApp(App): def compose(self) -> ComposeResult: """Add our widgets.""" with Container(id="paint"): + yield MenuBar([ + MenuItem("File", submenu=Menu([ + # MenuItem("New", self.file_new), + # MenuItem("Open", self.file_open), + MenuItem("Save", self.file_save), + # MenuItem("Save As", self.file_save_as), + # MenuItem("Exit", self.exit), + ])), + MenuItem("Edit", submenu=Menu([ + MenuItem("Undo", self.undo), + MenuItem("Redo", self.redo), + ])), + MenuItem("View", submenu=Menu([ + # MenuItem("Tools", self.toggle_tools_box), + # MenuItem("Colors", self.toggle_colors_box), + ])), + MenuItem("Image"), + MenuItem("Colors"), + MenuItem("Help"), + ]) yield Container( ToolsBox(), Container(