-
-
Notifications
You must be signed in to change notification settings - Fork 671
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
Add a Textual backend. #2065
Add a Textual backend. #2065
Conversation
Textual dev here. Very happy about this work! Mention me if you ever have any Textual questions. BTW, you can make Textual look a little better on MacOS with this tip: https://github.com/Textualize/textual/blob/main/FAQ.md#why-doesn't-textual-look-good-on-macos |
Thanks! For now, I think the only question I have is about event handlers. From everything I'm reading, event handlers are entirely at the level of the screen/app - there's no way to install a handler on a specific widget instance. Is that correct, or have I missed something? The reason I ask: if you've got a GUI got 10 buttons, each of which does something different, you essentially need to write a app/screen-level handler that is essentially a switch statement on the ID of the widget (or some similar redirection). That's not hard to do (in my case, it's a 1 line function)... but it seems like an odd omission in the Textual API, given the prior art of other GUI toolkits I've used.
Thanks for the heads up on that - I've added a note to the docs for the backend. |
You can manage events at the widget level, in the same way as screen / app. You could extend the Button class and implemented a handler there, for instance. Most events bubble so you can handle them on a parent. It's a very JS like model. The on decorator can help you manage a bunch of buttons. If you give each button an def compose(self):
yield Button("Ding", id="bell") @on(Button.Pressed, "#bell")
def bell(self):
self.app.bell() |
Ah - I was overthinking things, and thinking that the
I can see how that might be helpful if you're writing directly against the Textual API, but it's not as helpful in Toga's situation because it's an abstraction layer - we can't register against an ID (at least, not easily) because we don't know what ID the end user is using, or even if they're using an ID at all. Subclassing Button works really well, though. |
I've merged #2020 into this branch; and made a first stab at applying layout to the resulting app. It's not getting the widget sizing completely correct - there's something going on with the rendering size of borders and margins on widgets like button and TextInput; and it's not applying padding around widgets - but it is manifesting flexible sizing that broadly reflects what Pack intends. |
I've worked out what was going on with layout, and I've got it rendering Tutorial0 and Tutorial1 correctly. The issue was that left/top content offsets are relative to the parent, not the predecessor. @mhsmith If you don't get to this first, I'll merge this at the start of the PyCon AU sprints so that we'll have a stable base for contributors to work against. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, just one minor comment:
HORIZONTAL_SCALE = 10 | ||
VERTICAL_SCALE = 25 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
600 // 25 == 24. Suggest making the calculation explicit:
HORIZONTAL_SCALE = 10 | |
VERTICAL_SCALE = 25 | |
HORIZONTAL_SCALE = 800 // 80 | |
VERTICAL_SCALE = 600 // 25 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch on the 24 lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant was that 600 / 25 gives 24 pixels per line, not 25. I don't think subtracting one line for the title bar really makes sense, because the title bar is included in the hypothetical default terminal height of 600 pixels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - I was overthinking things. The aspect ratio mapping of of 80x25->800x600 shouldn't be excluding the titlebar.
Adds a terminal backend, using Textual as a TUI API.
This is very early alpha; but it's sufficiently mature to run Tutorial 0 and Tutorial 1, with the following caveats:
print
to write to console, so that output isn't visibleThis implementation maps a Textual "Screen" to Toga's Window class. This should (eventually) allow an app to have multiple windows, and allow moving between them with a keyboard shortcuts. Dialogs would also be implemented as screens.
Fixing style/layout will be a little bit complication; however, it will be a lot easier when #2020 lands. That PR makes Pack DPI independent, which means we will be able to implement a conversion between "textual DPI" (which has a default screen resolution of 80x25) to Toga's "96dpi ideal screen", and back again. This should mean we can convert Pack's padding, height and width into a character count, and apply those sizes to Textual's "CSS" styles.
To use this backend, you need to either:
TOGA_BACKEND=toga_textual
in your runtime environment (e.g.,TOGA_BACKEND=toga_textual python -m myapp
on macOS/Linux).At least for now, you won't be able to deploy a Textual app with Briefcase on any platform other than Linux. See beeware/briefcase#556 for details. However, you can run a Toga-textual app as
python -m app
.The RTD build is known to fail at present because of a failed URL check - the README references the location in the GitHub repo where this code will live once this PR has been merged.
Fixes #1867.
PR Checklist: