#!/usr/bin/env python3 from textual.app import App, ComposeResult from textual.widgets import Header, Input, Static, Button from textual.containers import Horizontal, Container from textual.validation import Number from textual.reactive import reactive from datetime import datetime from BingoBoard import BingoBoard from AboutCommand import AboutCommand MESSAGE = ''' Be excellent to each other! Made with :red_heart: by Panki [@click="app.open_link('https://tty0.social/@panki')"]Mastodon:[/] tty0.social/@panki [@click="app.open_link('https://git.theresno.cloud/panki/bingo-cli')"]Source:[/] git.theresno.cloud/panki/bingo-cli Built using [@click="app.open_link('https://textual.textualize.io/')"]Textual[/] ''' class Sidebar(Container): '''Class to represent the sidebar''' def compose(self) -> ComposeResult: '''Create the widgets that make up the sidebar''' yield Static('CCC Bingo', classes='title') yield Static(MESSAGE, classes='message') self.button = Button('< Back', variant='primary', classes='btn_sidebar') self.button.disabled = True yield self.button def on_button_pressed(self, event: Button.Pressed) -> None: '''Closes the sidebar''' self.app.action_toggle_sidebar() class BingoApp(App): ''' A Textual app to run a Bingo board. Contains the sidebar, header, and BingoDisplay. ''' CSS_PATH = "bingo.tcss" COMMANDS = App.COMMANDS | {AboutCommand} AUTO_FOCUS = 'Input' show_sidebar = reactive(False) def action_toggle_sidebar(self) -> None: '''Toggle the sidebar on or off''' sidebar = self.query_one(Sidebar) self.set_focus(None) if sidebar.has_class("-hidden"): sidebar.remove_class("-hidden") sidebar.button.disabled = False else: if sidebar.query("*:focus"): self.screen.set_focus(None) sidebar.add_class("-hidden") sidebar.button.disabled = True def compose(self) -> ComposeResult: '''Create child widgets for the app.''' yield Sidebar(classes="-hidden") yield Header(show_clock=True) yield BingoDisplay() def on_mount(self) -> None: '''Set title of the app''' self.title = 'CCC Bingo' self.sub_title = 'GPN22 Edition' class BingoDisplay(Static): ''' A Widget to represent the bingo UI. Contains the board, input field and re-roll button. Attributes ---------- board : BingoBoard The BingoBoard object input_field : Input User input for game seed ''' def compose(self) -> ComposeResult: '''Create child widgets for the app.''' self.board = BingoBoard() yield self.board self.input_field = Input( str(self.board.seed), type='integer', placeholder='UNIX timestamp', max_length=10, classes='seed_input', validators=[ Number(minimum=1000000000, maximum = 2000000000) ] ) self.input_field.border_title = 'Seed' yield Horizontal( self.input_field, Button.error(':game_die: re-roll', classes='roll_btn'), classes='bottom_line' ) def on_button_pressed(self, event: Button.Pressed) -> None: '''Re-roll the board state with current time as seed''' self.board.roll_board(int(datetime.now().timestamp())) self.input_field.value = str(self.board.seed) def on_input_submitted(self, event: Input.Submitted) -> None: '''Re-roll the board state with the seed from the input''' if event.validation_result.is_valid: self.board.roll_board(int(event.value)) if __name__ == "__main__": app = BingoApp() app.run()