<commit_before># clubsandwich
### A BearLibTerminal framework for Python 3

Features:

* Event loop
* Scene manager
* Geometry data structures
* Convenience functions
* UIKit-like interface framework
* Script runner to help you iterate quickly

## Installation

This package is not yet on PyPI. It is under heavy development. But you can
expect reasonable API stability!

```sh
pip install -e git://github.com/irskep/clubsandwich.git@master#egg=clubsandwich
```

![screenshot 1](readme_images/screenshot1.png)
![screenshot 2](readme_images/screenshot2.png)

## What is this?

### An event loop

clubsandwich uses the `asyncio` library to run an asynchronous update loop.
The `terminal_read(val)` method is called whenever events are in the queue.
`terminal_update()` is called every frame.

```py
from clubsandwich.loop import BearLibTerminalEventLoop

class BasicLoop(BearLibTerminalEventLoop):
    def terminal_read(self, val):
        print("User pressed a key:", val)

    def terminal_update(self):
        # this is called every frame
        self.draw()


if __name__ == '__main__':
    BasicLoop().run()
```

### A scene manager

Most games have more than one screen! This simple "director" object manages
them for you in a really obvious way.

```py
from bearlibterminal import terminal
from clubsandwich.director import DirectorLoop, Scene

class BasicLoop2(DirectorLoop):
    def get_initial_scene(self):
        return MainMenuScene()

class MainMenuScene(Scene):
    def terminal_update(self):
        print(0, 0, "Press Enter to begin game, Esc to quit")

    def terminal_read(self, val):
        if val == terminal.TK_ENTER:
            self.director.push_scene(GameScene())
        elif val == terminal.TK_ESCAPE:
            self.director.pop_scene()

class GameScene(Scene):
    def terminal_update(self):
        print(0, 0, "You are playing the game, it is so fun! Press Esc to stop.")

    def terminal_read(self, val):
        if val == terminal.TK_ESCAPE:
            self.director.pop_scene()

if __name__ == '__main__':
    BasicLoop2().run()
```

### A geometry library

Basic data structures for `Rect`, `Point`, and `Size`, and a very thin wrapper
around `bearlibterminal.terminal` to support them.

```py
from clubsandwich.blt.nice_terminal import terminal
from clubsandwich.geom import Rect, Size, Point

terminal.open()
terminal.bkcolor('#ff0000')
terminal.clear_area(Rect(Point(2, 2), Size(10, 10)))
terminal.refresh()
terminal.read()
terminal.close()
```

### Convenient terminal state access

A super simple wrapper around `terminal.state()` to conveniently read state
values.

```py
from clubsandwich.blt.state import blt_state
from clubsandwich.blt.nice_terminal import terminal
from clubsandwich.geom import Rect, Size, Point

terminal.open()
terminal.bkcolor('#ff0000')
terminal.clear_area(Rect(Point(2, 2), Size(10, 10)))
terminal.refresh()
# less verbose than terminal.state(terminal.TK_ESCAPE)!
while not blt_state.escape:
  terminal.read()
terminal.close()
```

### Transform stack

Easily translate view coordinates between frames of reference.

```py
from clubsandwich.blt.state import blt_state
from clubsandwich.blt.context import BearLibTerminalContext
from clubsandwich.geom import Rect, Size, Point

terminal = BearLibTerminalContext()

terminal.open()
terminal.bkcolor('#ff0000')
with terminal.translate(Point(blt_state.width, blt_state.height)):
  terminal.clear_area(Rect(Point(2, 2), Size(10, 10)))
terminal.refresh()
# less verbose than terminal.state(terminal.TK_ESCAPE)!
while not blt_state.escape:
  terminal.read()
terminal.close()
```

### A UIKit-like user interface library

This is too sophisticated for a short demo, but here's the code for the scene
in the first screenshot:

```py
class MainMenuScene(UIScene):<commit_msg>More readme<commit_after># clubsandwich
### A BearLibTerminal framework for Python 3

Features:

* Event loop
* Scene manager
* Geometry data structures
* Convenience functions
* UIKit-like interface framework
* Script runner to help you iterate quickly

## Installation

This package is not yet on PyPI. It is under heavy development. But you can
expect reasonable API stability!

```sh
pip install -e git://github.com/irskep/clubsandwich.git@master#egg=clubsandwich
```

![screenshot 1](readme_images/screenshot1.png)
![screenshot 2](readme_images/screenshot2.png)

## What is this?

### An event loop

clubsandwich uses the `asyncio` library to run an asynchronous update loop.
The `terminal_read(val)` method is called whenever events are in the queue.
`terminal_update()` is called every frame.

```py
from clubsandwich.loop import BearLibTerminalEventLoop

class BasicLoop(BearLibTerminalEventLoop):
    def terminal_read(self, val):
        print("User pressed a key:", val)

    def terminal_update(self):
        # this is called every frame
        self.draw()


if __name__ == '__main__':
    BasicLoop().run()
```

### A scene manager

Most games have more than one screen! This simple "director" object manages
them for you in a really obvious way.

```py
from bearlibterminal import terminal
from clubsandwich.director import DirectorLoop, Scene

class BasicLoop2(DirectorLoop):
    def get_initial_scene(self):
        return MainMenuScene()

class MainMenuScene(Scene):
    def terminal_update(self):
        print(0, 0, "Press Enter to begin game, Esc to quit")

    def terminal_read(self, val):
        if val == terminal.TK_ENTER:
            self.director.push_scene(GameScene())
        elif val == terminal.TK_ESCAPE:
            self.director.pop_scene()

class GameScene(Scene):
    def terminal_update(self):
        print(0, 0, "You are playing the game, it is so fun! Press Esc to stop.")

    def terminal_read(self, val):
        if val == terminal.TK_ESCAPE:
            self.director.pop_scene()

if __name__ == '__main__':
    BasicLoop2().run()
```

### A geometry library

Basic data structures for `Rect`, `Point`, and `Size`, and a very thin wrapper
around `bearlibterminal.terminal` to support them.

```py
from clubsandwich.blt.nice_terminal import terminal
from clubsandwich.geom import Rect, Size, Point

terminal.open()
terminal.bkcolor('#ff0000')
terminal.clear_area(Rect(Point(2, 2), Size(10, 10)))
terminal.refresh()
terminal.read()
terminal.close()
```

### Convenient terminal state access

A super simple wrapper around `terminal.state()` to conveniently read state
values.

```py
from clubsandwich.blt.state import blt_state
from clubsandwich.blt.nice_terminal import terminal
from clubsandwich.geom import Rect, Size, Point

terminal.open()
terminal.bkcolor('#ff0000')
terminal.clear_area(Rect(Point(2, 2), Size(10, 10)))
terminal.refresh()
# less verbose than terminal.state(terminal.TK_ESCAPE)!
while not blt_state.escape:
  terminal.read()
terminal.close()
```

### Transform stack

Easily translate view coordinates between frames of reference.

```py
from clubsandwich.blt.state import blt_state
from clubsandwich.blt.context import BearLibTerminalContext
from clubsandwich.geom import Rect, Size, Point

terminal = BearLibTerminalContext()

terminal.open()
terminal.bkcolor('#ff0000')
with terminal.translate(Point(blt_state.width, blt_state.height)):
  terminal.clear_area(Rect(Point(2, 2), Size(10, 10)))
terminal.refresh()
# less verbose than terminal.state(terminal.TK_ESCAPE)!
while not blt_state.escape:
  terminal.read()
terminal.close()
```

### A UIKit-like user interface library

This is too sophisticated for a short demo, but here's the code for the scene
in the first screenshot:

```py
from clubsandwich.ui import (
  LabelView,
  ButtonView,
  UIScene,
)
LOGO = """
  _______     __     ____             __       _     __ 
 / ___/ /_ __/ /    / __/__ ____  ___/ /    __(_)___/ / 
/ /__/ / // / _ \  _\ \/ _ `/ _ \/ _  / |/|/ / / __/ _ \\
\___/_/\_,_/_.__/ /___/\_,_/_//_/\_,_/|__,__/_/\__/_//_/
"""
class MainMenuScene(UIScene):
{'fraction_non_alphanumeric': 0.1111815415821501, 'fraction_numerical': 0.01039553752535497, 'mean_word_length': 4.266355140186916, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1934483', 'n_tokens_mistral': 2602, 'n_tokens_neox': 2389, 'n_words': 749}