Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best way to implement interactive widgets #24

Closed
XayOn opened this issue Jul 9, 2021 · 2 comments
Closed

Best way to implement interactive widgets #24

XayOn opened this issue Jul 9, 2021 · 2 comments

Comments

@XayOn
Copy link

XayOn commented Jul 9, 2021

I'm implementing an interactive rich table, and wondering about the best way to do it.

I'm currently doing something like:

from rich import box
from rich.align import Align
from rich.console import RenderableType
from rich.panel import Panel
from rich.table import Table
import rich.repr

from textual.widget import Reactive, Widget


def public(opt): return not opt.startswith('_')


@rich.repr.auto(angular=False)
class ChoiceTableWidget(Widget, can_focus=True):

    selected: Reactive[int] = Reactive(0)

    def __rich_repr__(self) -> rich.repr.RichReprResult:
        yield Align.center(self.table, vertical="middle")

    def __init__(self, *args, title="Menu", choices=[], **kwargs):
        self.title = title
        self.table = Table()
        self.choices = choices
        self.length = len(choices)
        self.table.box = box.SIMPLE

        for key in filter(public, choices[0]):
            self.table.add_column(key)

        for item in choices:
            self.table.add_row(*[str(b) for a, b in item.items() if public(a)])

        for row in self.table.rows:
            row.style = 'dim'

        self.table.rows[self.selected].style = 'bold'
        super().__init__(*args, **kwargs)

    def render(self) -> RenderableType:
        return Panel(self.table, title=self.title)

    async def move(self, pos):
        if self.length > self.selected + pos >= 0:
            self.table.rows[self.selected].style = 'dim'
            self.selected += pos
            self.table.rows[self.selected].style = 'bold'

    async def on_down(self):
        await self.move(1)

    async def on_up(self):
        await self.move(-1)

    async def on_enter(self, event):
        return await self.choices[self.selected]['_callback'](self)

But I'm still wondering about the best way to bind keys only to a specific widget (whenver it has the focus), and how to navigate views, being able to replace a view with another, and do something like history.back()

@willmcgugan
Copy link
Collaborator

Textual isn't quite ready to build things just yet. There's more work to be done on the API, and nothing is documented.

Haven't tackled replacing views yet, but there will be a history-like stack and back function.

Handling keys goes something like this:

def on_key(self, event: event.Key):
    await self.dispatch_key(event)

def key_up(self, event: event.Key):
   await self.move(-1)

The focused widget will get key events.

Have a look at _scroll_view.py for examples of key handling.

@XayOn
Copy link
Author

XayOn commented Jul 9, 2021

I realize that textual isn't ready yet, what I'm building is nothing serious. I'm a huge fan of rich, and textual has the same feeling. The work you've done up until now looks amazing!

About the views, I was wondering if it was going to be like that. I'm a pure backend developer kind of guy, so I'm a bit out of my element here, yet this feels like a modern web interface framework like vue or react.

Thanks!

@XayOn XayOn closed this as completed Jul 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants