You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bingo-cli/bingo.py

143 lines
4.4 KiB
Python

#!/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):
'''Class to represent the About command in the menu'''
async def discover(self) -> Hits:
yield DiscoveryHit(display='About', command=self.app.action_toggle_sidebar, help='Link to repo etc.')
def show_about(self) -> None:
pass
async def search(self, query: str) -> Hits:
'''Called when the search functionality is used'''
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):
'''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()