diff --git a/bingo.py b/bingo.py index 1f4eec1..0910281 100644 --- a/bingo.py +++ b/bingo.py @@ -1,17 +1,23 @@ #!/usr/bin/env python3 from textual.app import App, ComposeResult -from textual.widgets import Header, Footer, Button, Label, Switch, Static -from textual.containers import ScrollableContainer +from textual.widgets import Header, Input, Static, Button +from textual.widget import Widget from textual.message import Message -from textual.color import Color +from textual.containers import Horizontal +from textual.validation import Number +from textual.reactive import reactive from asyncio import sleep +from datetime import datetime +import random + class BingoField(Static): """A Bingo field widget.""" - cursor_x, cursor_y = 2 + cursor_x, cursor_y = 2, 2 + text = reactive("temp") class Selected(Message): """Send message to the board containing clicked field info""" @@ -21,10 +27,10 @@ class BingoField(Static): super().__init__() def __init__(self, num, text: str) -> None: - self.text = text self.num = num self.selected = False super().__init__() + self.text = text def on_mount(self) -> None: self.styles.content_align = ("center", "middle") @@ -48,54 +54,102 @@ class BingoField(Static): return str(self.text) class BingoApp(App): - """A Textual app to manage stopwatches.""" + """A Textual app to run a Bingo board.""" CSS_PATH = "bingo.tcss" - BINDINGS = [("d", "toggle_dark", "Toggle dark mode")] - - fieldstate = [False for _ in range(25)] - - fields = [ - 'Datenelch', - '6 Stunden Schlaf', - 'Tschunk getrunken', - 'Spaß gehabt', - 'Sticker getauscht', - 'DECT genutzt', - 'Hardware gehackt', - 'Kabel vergessen', - 'Halle gesucht', - '$dinge gelötet', - 'an SoS teilgenommen', - 'Neue Leute kennengelernt', - 'Wasser getrunken', - 'Waffel gegessen', - 'Corona Test gemacht', - '2 Mahlzeiten gegessen', - 'Fairydust bewundert', - 'Talk angeschaut', - 'CCC Merch getragen', - 'getrollt', - 'In der Lounge getanzt', - 'Etwas Neues ausprobiert', - 'Maske getragen', - 'geduscht', - 'Gulasch gegessen' - ] - - # todo: prng stuff - import random - random.shuffle(fields) - def compose(self) -> ComposeResult: """Create child widgets for the app.""" yield Header() + yield BingoDisplay() + + 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: + """""" + # reroll + 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: + if event.validation_result.is_valid: + self.board.roll_board(int(event.value)) + +class BingoBoard(Widget): + + fields = reactive([], recompose = True) + + def __init__(self) -> None: + self.fieldstate = [False for _ in range(25)] + super().__init__() + self.fields = [ + 'Datenelch', + '6 Stunden Schlaf', + 'Tschunk getrunken', + 'Spaß gehabt', + 'Sticker getauscht', + 'DECT genutzt', + 'Hardware gehackt', + 'Kabel vergessen', + 'Halle gesucht', + '$dinge gelötet', + 'an SoS teilgenommen', + 'Neue Leute kennengelernt', + 'Wasser getrunken', + 'Waffel gegessen', + 'Corona Test gemacht', + '2 Mahlzeiten gegessen', + 'Fairydust bewundert', + 'Talk angeschaut', + 'CCC Merch getragen', + 'getrollt', + 'In der Lounge getanzt', + 'Etwas Neues ausprobiert', + 'Maske getragen', + 'geduscht', + 'Gulasch gegessen' + ] + self.default_fields = self.fields + self.roll_board(int(datetime.now().timestamp())) + + def roll_board(self, seed): + self.seed = seed + random.seed(seed) + self.fields = random.sample(self.default_fields, len(self.fields)) + + def watch_fields(self, new_state) -> None: + self.fieldstate = [False for _ in range(25)] + for idx, field in enumerate(self.query(BingoField)): + field.text = new_state[idx] + def compose(self) -> ComposeResult: + """Create child widgets for the app.""" for _ in range(25): yield BingoField(_, self.fields[_]) - yield Footer() - async def on_bingo_field_selected(self, message: BingoField.Selected) -> None: self.fieldstate[message.num] = message.selected if self.is_bingo(): @@ -139,10 +193,6 @@ class BingoApp(App): ]) return bingo - def action_toggle_dark(self) -> None: - """An action to toggle dark mode.""" - self.dark = not self.dark - if __name__ == "__main__": app = BingoApp() app.run() diff --git a/bingo.tcss b/bingo.tcss index 1f8dd44..5161d28 100644 --- a/bingo.tcss +++ b/bingo.tcss @@ -1,6 +1,25 @@ -Screen { +BingoBoard { layout: grid; grid-size: 5 5; + height: 100%; +} + +BingoDisplay { + layout: vertical; + height: 100%; +} + +.bottom_line { + height: auto; + dock: bottom; +} + +.seed_input { + width: 70%; + border-title-align: left; +} +.roll_btn { + width: 30%; } .box { @@ -9,3 +28,10 @@ Screen { text-align: center; content-align: center middle; } + +Input.-valid { + border: tall $success 60%; +} +Input.-valid:focus { + border: tall $success; +}