#!/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.command import DiscoveryHit, Provider, Hits, Hit from textual.reactive import reactive from datetime import datetime from BingoBoard import BingoBoard 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 AboutCommand(Provider): async def discover(self) -> Hits: yield DiscoveryHit(display='About', command=self.app.action_toggle_sidebar, help='Link to repo etc.') def show_about(self): pass async def search(self, query: str) -> Hits: matcher = self.matcher(query) command = "About" score = matcher.match(command) if score > 0: yield Hit(score, matcher.highlight(command), self.app.action_toggle_sidebar, 'Link to repo etc.') class Sidebar(Container): def compose(self) -> ComposeResult: 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: print('sidebar button press') self.app.action_toggle_sidebar() class BingoApp(App): '''A Textual app to run a Bingo board.''' CSS_PATH = "bingo.tcss" COMMANDS = App.COMMANDS | {AboutCommand} AUTO_FOCUS = 'Input' show_sidebar = reactive(False) def action_toggle_sidebar(self) -> None: print('sidebar toggle') 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: self.title = 'CCC Bingo' self.sub_title = 'GPN22 Edition' def action_toggle_dark(self) -> None: '''An action to toggle dark mode.''' self.dark = not self.dark class BingoDisplay(Static): 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()