diff --git a/docs/examples/guide/screens/modal01.css b/docs/examples/guide/screens/modal01.css new file mode 100644 index 000000000..8c7bea071 --- /dev/null +++ b/docs/examples/guide/screens/modal01.css @@ -0,0 +1,16 @@ +#dialog { + grid-size: 2; + grid-gutter: 1 2; + margin: 1 2; +} + +#question { + column-span: 2; + content-align: center bottom; + width: 100%; + height: 100%; +} + +Button { + width: 100%; +} diff --git a/docs/examples/guide/screens/modal01.py b/docs/examples/guide/screens/modal01.py new file mode 100644 index 000000000..2ae1dd990 --- /dev/null +++ b/docs/examples/guide/screens/modal01.py @@ -0,0 +1,37 @@ +from textual.app import App, ComposeResult +from textual.containers import Grid +from textual.screen import Screen +from textual.widgets import Static, Header, Footer, Button + + +class QuitScreen(Screen): + def compose(self) -> ComposeResult: + yield Grid( + Static("Are you sure you want to quit?", id="question"), + Button("Quit", variant="error", id="quit"), + Button("Cancel", variant="primary", id="cancel"), + id="dialog", + ) + + def on_button_pressed(self, event: Button.Pressed) -> None: + if event.button.id == "quit": + self.app.exit() + else: + self.app.pop_screen() + + +class ModalApp(App): + CSS_PATH = "modal01.css" + BINDINGS = [("q", "request_quit", "Quit")] + + def compose(self) -> ComposeResult: + yield Header() + yield Footer() + + def action_request_quit(self) -> None: + self.push_screen(QuitScreen()) + + +if __name__ == "__main__": + app = ModalApp() + app.run() diff --git a/docs/examples/guide/screens/screen01.css b/docs/examples/guide/screens/screen01.css new file mode 100644 index 000000000..0ee028ebe --- /dev/null +++ b/docs/examples/guide/screens/screen01.css @@ -0,0 +1,18 @@ +BSOD { + align: center middle; + background: blue; + color: white; +} + +BSOD>Static { + width: 70; +} + +#title { + content-align-horizontal: center; + text-style: reverse; +} + +#any-key { + content-align-horizontal: center; +} diff --git a/docs/examples/guide/screens/screen01.py b/docs/examples/guide/screens/screen01.py new file mode 100644 index 000000000..63a24fd4e --- /dev/null +++ b/docs/examples/guide/screens/screen01.py @@ -0,0 +1,34 @@ +from textual.app import App, Screen, ComposeResult +from textual.widgets import Static + + +ERROR_TEXT = """ +An error has occurred. To continue: + +Press Enter to return to Windows, or + +Press CTRL+ALT+DEL to restart your computer. If you do this, +you will lose any unsaved information in all open applications. + +Error: 0E : 016F : BFF9B3D4 +""" + + +class BSOD(Screen): + BINDINGS = [("escape", "app.pop_screen", "Pop screen")] + + def compose(self) -> ComposeResult: + yield Static(" Windows ", id="title") + yield Static(ERROR_TEXT) + yield Static("Press any key to continue [blink]_[/]", id="any-key") + + +class BSODApp(App): + CSS_PATH = "screen01.css" + SCREENS = {"bsod": BSOD()} + BINDINGS = [("b", "push_screen('bsod')", "BSOD")] + + +if __name__ == "__main__": + app = BSODApp() + app.run() diff --git a/docs/examples/guide/screens/screen02.css b/docs/examples/guide/screens/screen02.css new file mode 100644 index 000000000..0ee028ebe --- /dev/null +++ b/docs/examples/guide/screens/screen02.css @@ -0,0 +1,18 @@ +BSOD { + align: center middle; + background: blue; + color: white; +} + +BSOD>Static { + width: 70; +} + +#title { + content-align-horizontal: center; + text-style: reverse; +} + +#any-key { + content-align-horizontal: center; +} diff --git a/docs/examples/guide/screens/screen02.py b/docs/examples/guide/screens/screen02.py new file mode 100644 index 000000000..0d5d01e46 --- /dev/null +++ b/docs/examples/guide/screens/screen02.py @@ -0,0 +1,36 @@ +from textual.app import App, Screen, ComposeResult +from textual.widgets import Static + + +ERROR_TEXT = """ +An error has occurred. To continue: + +Press Enter to return to Windows, or + +Press CTRL+ALT+DEL to restart your computer. If you do this, +you will lose any unsaved information in all open applications. + +Error: 0E : 016F : BFF9B3D4 +""" + + +class BSOD(Screen): + BINDINGS = [("escape", "app.pop_screen", "Pop screen")] + + def compose(self) -> ComposeResult: + yield Static(" Windows ", id="title") + yield Static(ERROR_TEXT) + yield Static("Press any key to continue [blink]_[/]", id="any-key") + + +class BSODApp(App): + CSS_PATH = "screen02.css" + BINDINGS = [("b", "push_screen('bsod')", "BSOD")] + + def on_mount(self) -> None: + self.install_screen(BSOD(), name="bsod") + + +if __name__ == "__main__": + app = BSODApp() + app.run() diff --git a/docs/guide/actions.md b/docs/guide/actions.md index 1d6fec643..517751450 100644 --- a/docs/guide/actions.md +++ b/docs/guide/actions.md @@ -131,10 +131,26 @@ Textual supports the following builtin actions which are defined on the app. options: show_root_heading: false +### Push screen + +::: textual.app.App.action_push_screen + + +### Pop screen + +::: textual.app.App.action_pop_screen + + ### Screenshot ::: textual.app.App.action_screenshot + +### Switch screen + +::: textual.app.App.action_switch_screen + + ### Toggle_dark ::: textual.app.App.action_toggle_dark diff --git a/docs/guide/screens.md b/docs/guide/screens.md index 688684a43..94c61794e 100644 --- a/docs/guide/screens.md +++ b/docs/guide/screens.md @@ -1,12 +1,151 @@ # Screens -TODO: Screens docs +This chapter covers Textual's screen API. We will discuss how to create screens and switch between them. -- Explanation of screens -- Screens API - - Install screen - - Uninstall screen - - Push screen - - Pop screen - - Switch Screen -- Screens example +## What is a screen? + +Screens are containers for widgets that occupy the dimensions of your terminal. There can be many screens in a given app, but only one screen is visible at a time. + +Textual requires that there be at least one screen object and will create one implicitly in the App class. If you don't change the screen, any widgets you [mount][textual.widget.Widget.mount] or [compose][textual.widget.Widget.compose] will be added to this default screen. + +!!! tip + + Try printing `widget.parent` to see what object your widget is connected to. + +
+--8<-- "docs/images/dom1.excalidraw.svg" +
+ +## Creating a screen + +You can create a screen by extending the [Screen][textual.screen.Screen] class which you can import from `textual.screen`. The screen may be styled in the same way as other widgets, with the exception that you can't modify the screen's dimensions (as these will always be the size of your terminal). + +Let's look at a simple example of writing a screen class to simulate Window's [blue screen of death](https://en.wikipedia.org/wiki/Blue_screen_of_death). + +=== "screen01.py" + + ```python title="screen01.py" hl_lines="17-23 28" + --8<-- "docs/examples/guide/screens/screen01.py" + ``` + +=== "screen01.css" + + ```sass title="screen01.css" + --8<-- "docs/examples/guide/screens/screen01.css" + ``` + +=== "Output" + + ```{.textual path="docs/examples/guide/screens/screen01.py" press="b,_"} + ``` + +If you run this you will see an empty screen. Hit the ++b++ screen to show a blue screen of death. Hit ++escape++ to return to the default screen. + +The `BSOD` class above defines a screen with a key binding and compose method. These should be familiar as they work in the same way as apps. + +The app class has a new `SCREENS` class variable. Textual uses this class variable to associated a name with screen object (the name is used to reference screens in the screen API). Also in the app is a key binding associated with the action `"push_screen('bsod')"`. The screen class has a similar action `"pop_screen"` bound to the ++escape++ key. We will cover these actions below. + +## Named screens + +You can associate a screen with a name by defining a `SCREENS` class variable in your app, which should be dict that maps names on to Screen objects. The name of the screen may be used interchangeably with screen objects in much of the screen API. + +You can also _install_ new named screens dynamically with the [install_screen][textual.app.App.install_screen] method. The following example installs the `BSOD` screen in a mount handler rather than from the `SCREENS` variable. + +=== "screen02.py" + + ```python title="screen02.py" hl_lines="30-31" + --8<-- "docs/examples/guide/screens/screen02.py" + ``` + +=== "screen02.css" + + ```sass title="screen02.css" + --8<-- "docs/examples/guide/screens/screen02.css" + ``` + +=== "Output" + + ```{.textual path="docs/examples/guide/screens/screen02.py" press="b,_"} + ``` + +Although both do the same thing, we recommend the `SCREENS` for screens that exist for the lifetime of your app. + +### Uninstalling screens + +Screens defined in `SCREENS` or added with [install_screen][textual.app.App.install_screen] are _installed_ screens. Textual will keep these screens in memory for the lifetime of your app. + +If you have installed a screen, but you later want it to be removed and cleaned up, you can call [uninstall_screen][textual.app.App.uninstall_screen]. + +## Screen stack + +Textual keeps track of a _stack_ of screens. You can think of the screen stack as a stack of paper, where only the very top sheet is visible. If you remove the top sheet the paper underneath becomes visible. Screens work in a similar way. + +The active screen (top of the stack) will render the screen and receive input events. The following API methods on the App class can manipulate this stack, and let you decide which screen the user can interact with. + +### Push screen + +The [push_screen][textual.app.App.push_screen] method puts a screen on top of the stack and makes that screen active. You can call this method with the name of an installed screen, or a screen object. + +
+--8<-- "docs/images/screens/push_screen.excalidraw.svg" +
+ +#### Action + +You can also push screens with the `"app.push_screen"` action, which requires the name of an installed screen. + +### Pop screen + +The [pop_screen][textual.app.App.pop_screen] method removes the top-most screen from the stack, and makes the new top screen active. + +!!! note + + The screen stack must always have at least one screen. If you attempt to remove the last screen, Textual will raise a [ScreenStackError][textual.app.ScreenStackError] exception. + +
+--8<-- "docs/images/screens/pop_screen.excalidraw.svg" +
+ + +When you pop a screen it will be removed and deleted unless it has been installed or there is another copy of the screen on the stack. + +#### Action + +You can also pop screens with the `"app.pop_screen"` action. + +### Switch screen + +The [switch_screen][textual.app.App.switch_screen] method replaces the top of the stack with a new screen. + +
+--8<-- "docs/images/screens/switch_screen.excalidraw.svg" +
+ +Like [pop_screen](#pop-screen), if the screen being replaced is not installed it will be removed and deleted. + +#### Action + +You can also switch screens with the `"app.switch_screen"` action which accepts the name of the screen to switch to. + +## Modal screens + +Screens can be used to implement modal dialogs. The following example pushes a screen when you hit the ++q++ key to ask you if you really want to quit. + +=== "modal01.py" + + ```python title="modal01.py" hl_lines="18 20 32" + --8<-- "docs/examples/guide/screens/modal01.py" + ``` + +=== "modal01.css" + + ```sass title="modal01.css" + --8<-- "docs/examples/guide/screens/modal01.css" + ``` + +=== "Output" + + ```{.textual path="docs/examples/guide/screens/modal01.py" press="q,_"} + ``` + +Note the `request_quit` action in the app which pushes a new instance of `QuitScreen`. This makes the quit screen active. if you click cancel, the quit screen calls `pop_screen` to return the default screen. This also removes and deletes the `QuitScreen` object. diff --git a/docs/images/screens/pop_screen.excalidraw.svg b/docs/images/screens/pop_screen.excalidraw.svg new file mode 100644 index 000000000..b87957f6a --- /dev/null +++ b/docs/images/screens/pop_screen.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1cXOtT20hcdTAwMTL/nr+C4r7sVcWz0z3vrbq6XHUwMDAyXHUwMDEyXHUwMDEyQnhsyObB3VZK2MLW4ddaMsZs5X+/XHUwMDFlhSDJQorBxnHiXHUwMDBmXHUwMDE4a+RRa+bX3b9+yH8/2djYTKbDcPO3jc3wqlx1MDAxOXSj1iiYbD71xy/DUVx1MDAxY1xy+jSE6ed4MFx1MDAxZTXTMztJMox/+/XXXjC6XGKTYTdohuwyisdBN07GrWjAmoPer1FcdTAwMTL24n/7v4dBL/zXcNBrJSOWXaRcdTAwMTG2omQw+nKtsFx1MDAxYvbCflx1MDAxMtPs/6HPXHUwMDFiXHUwMDFif6d/c9K1oqA36LfS09OBnHhazFx1MDAxZT1cdTAwMWP0U1FBXHUwMDBiLZW28vaEKH5GXHUwMDE3S8JcdTAwMTaNnpPAYTbiXHUwMDBmbcrRNNFcdTAwMDae897Fx119cmhcdTAwMGZOxyfZVc+jbvckmXZTmeJcdTAwMDHdSjZcdTAwMTYno8FF+D5qJVx1MDAxZH/pmeNV31x1MDAxYVxyxu1OP4zjwndcdTAwMDbDoFx1MDAxOSVTOqb47cGg306nyI5cXNGnXHUwMDA2cs6M0Vx1MDAxNqTiXHUwMDEy6G7V7fiXXHRcdTAwMDQz1lx1MDAxOFx1MDAwNUJcdTAwMWEhXHUwMDA1qFx1MDAxOcl2XHUwMDA2XdpcdTAwMDeS7Fx1MDAxZjx9ZbKdXHUwMDA1zYs2XHTYb2XngFxugrPz7JzJzf1Kp5i0Uphs+k5cdTAwMTi1O4nfIauZNcBdfjRcdTAwMGXTTXCgpJNaZlvkrzjca6Vg+HN2XHUwMDE1O8FoeLNam7H/kJPWXHUwMDBi+nxcdTAwMTZJeTTl9lm8grPdXHUwMDEwYKe1v/3X85NcdTAwMDP5+2CrfztXXHUwMDAxesFoNJhs3o58vvkvXHUwMDEzbTxsXHUwMDA1X1x1MDAxMFx1MDAwNVpLa43TXHUwMDEyTVx1MDAwNspu1L+gwf64282OXHKaXHUwMDE3XHUwMDE5XGLTo5+f3lx1MDAxYvp0mSroo+OO0KD03NBcdTAwMGbHU3ux39vnfHz+ctLeiyb6hfue0Fx1MDAwN/5N7IPTTFx1MDAxOSNRc1x1MDAwZVx1MDAwMoyyXHUwMDA17EuBXGalQYKeddo4vlx1MDAxOPbPgzPO1Vx1MDAxMrGPQipwlq9cdTAwMTb7vd45n2zx5NlhNFxmwz9eXHUwMDFlbb86iJeEfVx1MDAwYlxccG6Whf0kvEruXHUwMDAyvkVdXHUwMDA1fFx1MDAxMNZx5NLh3Mh/d941l1fDy5fT3taHwfjj8PiF2F1v5CMqprRBXHUwMDA0dEZ6XHUwMDBiWlx1MDAwML7lwMhcdTAwMDRJclxi1iHkrMBDcG+c4udYxj1wW1x1MDAwNryBWZhrgdL7pp/IxDtcdTAwMGJK2PvAPEPToJ+cRNepjbaFo7tBL+pOXHUwMDBikEjxT1x1MDAwMp40R2HY34D/9n/pRK1W2P9nfsfikK7vJ9TFb251o7bXls1ueF5UoyRcIlx1MDAxZXY7nFxmcmvcJElcdTAwMDKabrTXmr2jwShqR/2g+7Zaqlpt/rLMd6gzUVx1MDAxM5w9nNNnIHojxPz6XFy/8/fQZ5zF5uPps3HMSFx1MDAwMGm4pXdbVGfjJFx1MDAwM81cdTAwMWRcdTAwMWGU6EjjXHUwMDFmRZ1cdTAwMWQyrogwS2MsR9TuXHUwMDBl5XZMIzk6KVFcdTAwMDHXOlx1MDAwM/BXlyaFv4VcdTAwMDeoeipkjao/ijLGSTBKtqN+K+q3aTCzXCJfQ5K9OVx1MDAxY0Sqvs2xl5IzhVxcS8FpI5UgL1x1MDAwNLmT2sHQLyFcdTAwMDMgTqLJZKNVTtibXHUwMDEzPt9cblx1MDAxNfZb31x1MDAxNqk+UMmJ1OBcZml5nPV7pjhqilx1MDAxM0pCSaZcdTAwMDVIXHUwMDA3nITiTlmnSlJ1gzjZXHUwMDE59HpRQmt/PIj6yexcdTAwMWGni7nldbxcdTAwMTNcdTAwMDYl40F3lVx1MDAxZps1XHUwMDA2Qz9j0aZn/21k2pJ+uP3/z6d3nt2ohHI6WkJxNt+T/PtcdTAwMDNcdTAwMTi5sJWGXGYtqVx1MDAwNlx1MDAxMZdM8b/JyM9G2NpcdTAwMGKuw/1nW1x1MDAwN89f9LWNIVhvXlwinGOOTFx1MDAxNVxiNN5y68xcdTAwMTJ8XHRGLeNcdTAwMDLAOeK+ZCaEmJHsIcGo1suj5ESVkKJcYs5cdTAwMWaBrNRYMECFuIqIkYLtSkeLZLhcdTAwMDVwNb+jPdzpXHUwMDFjXGZfXHUwMDFjvdtcdTAwMWRcdTAwMWNfj9xhcjQ+XHUwMDFmivVcdTAwMDao5IJcdJ9cdFGWdFWqYrJEXG7NuOPGXHUwMDEyQqVcdTAwMDZcXFxmnsuOXHUwMDE3SVx1MDAxZaE4mTS7fHDWMenXZ8rufYyvOsPx+MNWdPomuVx1MDAxOMllXHUwMDA1jIQ3yHG7x4O+XHUwMDE1qlxu+k6AUkS65jfN76eHb9o9t3+0r/76NH32abtzKI+WivxWXHUwMDEwd8IlQ98xolx1MDAxZYJcdTAwMTONXHUwMDA0YpGuXHUwMDAwfaGQXHUwMDExKZFcdTAwMDY5cDBcXC9GMi02XHUwMDFkhGqZ6CdcdTAwMTNpSTS+4nSJXHLDl+PX19fds1P96VxcJJGYnvL50P+0bt5Y7U+v4+3jyejgdO/Vofj0x9SeLWHe8+H7ydvG5Ni+v+7Fp1x1MDAxN83wI3ZcdTAwMGaWMC9cdTAwMWab//VO3mLyprl9XHUwMDE1NT/uvlx1MDAxMc1oWfE0J+S5pTnAqrSRrktcdTAwMWKRWjiKvOzcNuAsnuKOONpcdTAwMDXV3u6Mmq33h+r6dL3DTGlcdTAwMWSzSkvNOZleNZMuXHUwMDA1QaNOcrKERJ3JXHUwMDE3zlxudj9cdTAwMTNA6npcdTAwMTbewc1ErqZxq/myrO9CWqIrhj+Ct6tBolx1MDAwMan1fZCYbXiW2Vx1MDAxMVx1MDAxNOWlgYfnwDZcdTAwMTdJXHUwMDE38jzZVb7meYLhkFxyXHUwMDA3w09xmln55e4sj9CF7z12lqckU63qVeZ4dHVkROxXXHUwMDEz71Zyft2rN57L0L1H8L8gmERUXHUwMDE0ZiOZOzFTq7CGIVx1MDAxN9pcYqPJXHUwMDE3w2Ip2yrd40RvXHUwMDAxkVgwXHUwMDAx06E1QpZ1XHUwMDExuPGCmpRvXHUwMDAyXHUwMDE5XHUwMDA0V9ZNQjUpTC7JvopEXHUwMDBmcFqcx0z01NO6jUJWxVEsXHUwMDBmjmyoRE2rmUsx3CRVXHUwMDE0U1x1MDAxNFx1MDAwMyuOdFx1MDAwMtpvZnqKt/EjZVtqQJWOl/GUTfkk/35vo2JyRYVZo0JwXHUwMDAx8iD3yFx1MDAxYtczp/U0Ko5LZi1ZXGZDsaySZtaoXGJmXHUwMDA0V4pWnlxmXHUwMDBizEZcdTAwMWLLMipoOWghfSmfLpIrRuVsimWSglx1MDAwZetcdTAwMWNcdTAwMTiCXG6WykSgjZXOc5PV2lx1MDAxNKCYXCJbte9oU4DRXHUwMDBl0PIopykqJlx1MDAxZVJOXHUwMDFlXHUwMDAzZ8QmrNTkQoThXFyan9SmVEPKv1x1MDAxYWU0LcuiXHUwMDE08l+zXHUwMDE5XFyurZS+nDm3SalPnaynSdHKMCO55lx1MDAxMiVIk+tk8d/XXHUwMDAyXHUwMDE4+X3iMd6x4YItXHUwMDE1VSaFdMFcdTAwMTkjyWhJWnXSiez+b02KU1xmpXbKcsFccqrcrtxYXHUwMDE0dPRdhfpcdTAwMDFcdTAwMDHEQiSFK5fJ8nCDMqt8P4FaN6r3NVx1MDAxZC5t6T3Vuq5XSldTXHUwMDA1Tv5cdTAwMTM06vmpgn6m8FX0cnLZfvfhQrevT49+j79rn+C31ZrIMzBcbj6QTCatr5lpXHUwMDE5UUAkTSluXHUwMDFkUSaXL8ivR2VGeTOvVptcZlhdJ59cdTAwMTbVXlx1MDAwN4Rwvk1s/ui4cbU/tn+1wsvOycdLXHUwMDExTl87PNhZe3Rq5uNcdTAwMDdB2DMobLFwSG6XkS9cIuRcbq2EQbdcdTAwMTA6l16YcUIgUTfzgHB4kdR0I57s7CB2I9lcdTAwMTZcdTAwMWZa4z+O48nFslKyVlpuga9cdTAwMDD7NuegS1xy3IAkjJyfcMX93dFe7+L5azHF8L1otTvDg9Z6Q79cdTAwMDHWMrCKmKXTzmlcdTAwMGVcdTAwMDXoXHUwMDBiKYlcZiuL3KLv6l1cYvlfyjLLLElcdTAwMTJHXHUwMDA0XHUwMDEy7CcpypxtPT9/XHUwMDFmbPHD9rv9t1x1MDAwN83rQZDE46X1xqK0uDSNqlxmYWpcbp2gfHVbajN/W3h92Wd9I1x1MDAxOFxupYVcdTAwMDRSXHUwMDFiXHUwMDAxekafiOiQWfHxi6b9WKzOWVx1MDAxZMBcdTAwMThFXHUwMDExlKPolYiLJZ9VVizincxnZZRcIq/mfLmjpF5cdTAwMTTqy2Jss5pcdTAwMTCG1P1BNZBl50Q448AlxXBKXHUwMDE5jeA4N3d2r/neNk3RKsWlZJBuTvjZklwijWpQfVx1MDAxOS7hKZvxSf79vnVTqWD26C071cjR3ec5k6vXoVx1MDAxY530XHUwMDFiz/u6od1+6/CV6thcboPSXHSanfEoXFxcdTAwMDNcdTAwMWZN+GJcdTAwMDZ89yTB0T/jUEy0XHUwMDFhJ1x1MDAxOHF0X1x1MDAwZVx1MDAxMMpavlD1Jlx1MDAxOVx1MDAwNf14XHUwMDE4jEhd7jAsucxpTdO9XHUwMDE0qJw1YsWM9DGfLfHZXHUwMDAy41bedI9r2XSPizfdc1fdXHJBtsOSf7zHk5P1O38vtV5dP0SDXHUwMDAySlx1MDAwNspYIynYXHUwMDE3zuqZxnuLjLyhptPQmVxccWXpWlxyilx0XHUwMDAwKYiekW1x7q5cdTAwMTJcblx1MDAwMpM+XHLCLYVMoPJcdTAwMWRaN3TBu1x1MDAwN4fwkCzJXCJ0gTyzclx1MDAwZtHLOelCvcvYKDa7k+8z5CONTOvo5bIscEaLJITl3JdR9Ndi5D1cdTAwMWLw61x1MDAxZpcsUFx1MDAxOFx1MDAxMCCNpZ3zXHUwMDE5XHUwMDAyJY0uyWRcdTAwMTjSgCR648jGXHUwMDE5xJJMP1x1MDAxMk+pXHUwMDA2s381yjheXHUwMDE2TVx1MDAxMZWJXHUwMDA0XHUwMDA0n2+mUHV+nlwiPlxcyNa1fHn54tnhm9Z04sK+qirdrFx1MDAwZk9cdTAwMDHBmSFoI7FD32ePRYOGqH1cdTAwMDXRKCAuY6VSXHUwMDBipVx1MDAxM75BVO5o8ypcdTAwMTNcdTAwMTWyXHUwMDFjUlx0IdWPw1Se1s37mFx1MDAxOVx1MDAwNLK0Wq+eXHUwMDAxXHTiXHUwMDFhl1FcdTAwMWOddcN1okBcdTAwMDWxXHUwMDFlxoFcdTAwMTRWXHUwMDA2NuAsoZK4/vxN4fVbv65cdTAwMTTIP/IglFTeLKBRYqYtXHUwMDFjkFx0Ylx1MDAxNVx1MDAxNoUherTYXHUwMDAzO7X2XHUwMDAyXHUwMDFkOUokj01Ow3IpsyvdWlx1MDAwZu3du/TJXHUwMDAwXHS0MVjOl/jSXHUwMDAxRaSr7iF5sFrOSYDqfVGRXHUwMDAwgdCeXHUwMDA3Ulx1MDAxNFxuXHUwMDE2yeHlclx1MDAwNF9cdTAwMTmQZNJvJaBcdTAwMTJcdTAwMWOMeegziPW59qJUXFxcbmPJISljtNEu94NcdTAwMWa3YllmNHFcdTAwMDPaWue4MkKWpPqRSFAlnP2rXHUwMDA05CUxIFGdqCFhUEtcdTAwMGbXue3ZyfXlKzWNj6ZHL+LB5OPWZft479O6MyCK1ohcdTAwMDGh4MSCjK9ZXHUwMDE0XHUwMDEzNUJcdTAwMThGXHUwMDExgnRElFxibvlfUPlOmVx1MDAxYSBcdTAwMWHmIFx1MDAxN1x1MDAxNfzotUPHXHUwMDA1t8LlbmiFmZo15Cm4OE8xrlqvwVx1MDAxN1xyrTXzN6/Ub/2a8lx1MDAxNFxuKsH/mFx1MDAwZjk3w41cdTAwMTC5eCFtXHUwMDEwXHUwMDAwzdAnckxcdTAwMWHai8VcdTAwMWXdrNVr7dtotPDP9/tcdTAwMDdcdTAwMDfhXHUwMDBlLdeGOeu7XHUwMDE2NddWlVx1MDAxYdP8U2zKoV3l7yQspJVz0pR6h7FRKOs4RS9OXHUwMDFiXHUwMDA1WphcXGfyRpZcdTAwMTORXGLCao1cdTAwMWH8XHUwMDEzYuWfJJiLpNT3wszIRFxcSCvyyEJq8lx1MDAxZqIkXHUwMDEzkmfx7Wv+4XY0d7X0/0hcdTAwMTSlXHUwMDEyyOlgXHUwMDExwlVcdTAwMDTlyc3s/jGhk4TwdrtcdTAwMTVcdTAwMDTpqHVjyrNb3LyMwsn2XT056cvbx3QxvVx1MDAxNVxu/Y3+/fnJ5/9cdTAwMDPV4pXXIn0= + + + + Screen 1(hidden)app.pop_screen()Screen 2(hidden)Screen 3(visible)Screen 2(visible) \ No newline at end of file diff --git a/docs/images/screens/push_screen.excalidraw.svg b/docs/images/screens/push_screen.excalidraw.svg new file mode 100644 index 000000000..7bfde1748 --- /dev/null +++ b/docs/images/screens/push_screen.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nOVcXG1z2khcdTAwMTL+nl/h8n3Zq1xu2nnr6Zmturpcbk5cdTAwMWN7XHUwMDEzx/Fb/HK35ZJBgNaAMFx1MDAxMrbxVv779chcdTAwMGVcdTAwMTIvXCJgMEtyVGyMRkitmaeffrpnJn+92tjYTPqdYPO3jc3gvuI3w2rXv9t87Y7fXHUwMDA23TiM2tQk0s9x1OtW0jNcdTAwMWJJ0ol/+/XXlt+9XHUwMDBlkk7Tr1x1MDAwNN5tXHUwMDE49/xmnPSqYeRVotavYVx1MDAxMrTif7vfn/xW8K9O1KomXS+7SSmohknUfbxX0FxmWkE7ienq/6HPXHUwMDFiXHUwMDFif6W/c9ZVQ79cdTAwMTW1q+npaUPOPLSjRz9F7dRUY5hgWqFcdTAwMWGcXHUwMDEwxm/pZklQpdZcdTAwMWFcdTAwMTlcdTAwMWNkLe7QJjQv9MPZWalafuj/WTqqmebJXHUwMDE3nt21XHUwMDE2NptHSb+Z2lx1MDAxNEf0KFlbnHSj6+A0rCZccmrlI8eLvtWNevVGO4jjoe9EXHUwMDFkv1x1MDAxMiZ9d4yxwVG/XU+vkVx1MDAxZLmnT4prjzFccmgtN1x1MDAwMCx7WPd9oYzHuFVWgFx1MDAxMlpxgSOGbUVNXHUwMDFhXHUwMDA2MuxcdTAwMWYsfWWmXfmV6zrZ165m53Dw/aua1tlZd09cdTAwMGasLHjKKIkwaGpcdTAwMDRhvZE4I4z2XGZyZvOtcZCOXHUwMDAyV1ZbhsCzXHUwMDE2d9PObjWFw1x1MDAxZqP92PC7naf+2ozdh5zBztZ3o1jK4yk30sFu9fz86Fg97Fx1MDAxZVx1MDAxZX5IToKqvdy6XHUwMDFmXFxrXGJ8frdcdTAwMWLdbVx1MDAwZVq+Pv2VmdbrVP1HTHGtlWVcdTAwMTJcZkeW9XQzbF9TY7vXbGbHosp1XHUwMDA2w/To19dzg19cdFVcdTAwMDR+Qo7Qmis5O/rvoHFwvV+PXHUwMDBmPnROXHK7XHUwMDEwd1dvP/TWXHUwMDFj/YJ5wjKu6FGNNiiH4c9Re1xujVx1MDAwNcGNsoh8MfjX/CvGYIng18BcdTAwMDRYo/lqwVx1MDAxZuvz8Kh8X98qb5v7crdzqe3t4VLAb1xmXHUwMDFhLkGwZYE/XHTuk0nIXHUwMDA3XHKFyDdGXHUwMDEx+Fx0XHUwMDBmMyNfto9iXHUwMDFktMzno3q3vHuO8mRfrDnvXHUwMDFiXHSe0VJoXHUwMDAzipxcdTAwMWPsXHUwMDEw8ClcdTAwMTB4TCgujaBcdTAwMWZccrBcdTAwMTDu0Vx1MDAwMquJcdxzZsZcdTAwMDGPfFxm5lx1MDAxNH6YlIrJn4fjhbZKSJhcdTAwMDPmXHUwMDE5mqJ2clx1MDAxND5cdTAwMDQpOVxmXHUwMDFk3fZbYbM/XHUwMDA0iVx1MDAxNP9k4FGlXHUwMDFiXHUwMDA07Vxy/t/2L42wWlxy2v/MXHUwMDBmWVx1MDAxY9D93Vx1MDAwNfXwN980w7rzls1mUFx1MDAxYnajJCQpNmhOolxcXHUwMDFmV8hcdTAwMTKfLtfdrY4+UdRccuth229cdTAwMWVcdTAwMTdb9Sxv1kZcdTAwMTR7M1xigUTgXHUwMDE5/L/nzVx1MDAxNyfbd35n//D+Zlx1MDAwN45bV+clXHUwMDE5Xa+5Nyu0XHUwMDFlSoZWUawg+squ4r5P4cVcdTAwMTDgkFx0ozSSWHpcdTAwMTFvXHUwMDE2MiPMgTfnjj15s2aS5FxymiyU/uAxy1x0Nupblj3pipxZbPxCiVN41VxmJjuzgKFvrsiZ81ZNdebHbp7gzVxc5eLCqDtTTFKkj0Xm8N9z5+kjP4c7i1Fsvpg7g9RcdTAwMWVcdTAwMThtKTZLhZSaXHKrUqU8y8iVXHUwMDA1Q2lAczli2HL8XHUwMDE50JOcXiR/XHUwMDE13Vx1MDAwYsxcdTAwMDT3RuY5cVxmkqhF0082bt9it6A2inXyXHUwMDE5+Vlq51x1MDAxNHf/nkPOk0Hl7PC7STlsV8N2nVx1MDAxYTMm+VZm2J0hRqQuXFzpOSuZp4FzSYNI6Vx1MDAwNVx09ixRdV3hd5zN0mOGRFx1MDAwZadMg34o6Xo64+vAqqBd/b5N0/OvIZtcdTAwMTgpXFxOXHRccpL2s1xcXHQ7Zlx1MDAxNNmkZZrzcCYkmT5mU9OPk62o1VxuXHUwMDEz6vrPUdhORrs47cs3zs1cdTAwMWKBP8ZcdTAwMWb0TPm2UT7ouCtcdTAwMGXTevbXRuYw6YfB33+8nnh2MZbda1xmxdnlXuXf52Yyul0hkZGsZsRcdTAwMDSQucz3iGy6XHUwMDFlXUtcIjPUtYpcdTAwMGLrKkdEMyp72DTLkOhRzoeugEN5iFUjdi2HxzR6hmv6p4GowfKMTFx1MDAwNzRmXGJcdTAwMDCaXHUwMDEyaVdH4qBzicZcdTAwMTONSUtcdTAwMDJcdTAwMTJgxSSGXCLPqMsnselp61x1MDAxMGFI5ViCXHUwMDEzYl1HWZM76YnEhCdcdTAwMTEoWilwWog/l8Sml1BzNpXIKGlpXFy4pZtcdTAwMDGiVuNGecagdIqBMkgk++GHZrFSIZTT1jFcdTAwMTTPSWNTXG6F0rDRo1x1MDAwM1wi05K7bFx1MDAxNmZPsPzfXHUwMDBm3vfffLx4+yG86PcvL9tcdTAwMGbNXHUwMDAztt5cdFx1MDAxNiFcdTAwMWY8XHUwMDA1oDSjIE3cnYmhtE5cdTAwMGXCQ8s1UlJPRJZcdTAwMTNsa1ImJ7BokCBeoExeXFzLI6fjdq5cIsdz8Vx0qjBjUOT3zpjZq3kx6NPLclx1MDAxZuO9m/2y6uzU7vq4t/bwtJ6SXHUwMDAyXFzh0lx1MDAwMojhQMuN8Fx1MDAxOKN2XHUwMDEyQYyyXG47qlx1MDAwMP7eOjblXGJcdTAwMDBMw1x1MDAwYpSxp1TgrFwiPWJXXHUwMDAwTi1kXHUwMDExOEFKrTnD2UXgVrVcdTAwMWQlW5c7+zbeerhcdTAwMGXOeVJcdTAwMGab61x1MDAwZU4hPFLXRlJ4XHUwMDAyru1wdaqktYdcdTAwMTTTSGExJPDgYjLQiIrlwVx1MDAxMqkzzcCZYSueYTRBsNP7+PDQvLrQlzWZhLJ/wXJCaCz/XHUwMDE4tHx9Pe26+/H+obi77iad29Ln2t71l/v34tNcdTAwMTKuW8Wbm+B3/Vx0opCfRNt7N3uldydLuO5N+fN+VZ2etu7Ptt9WoFx1MDAwYu/fXHUwMDA0/WVV4Y1GsHJZXHUwMDFjUFSelpJcdTAwMTdcdTAwMTFcdTAwMDBSVkHaWsqZXHTg0n93/mf/rnZcdTAwMTi/3T/dub1Oro72dtY7XHUwMDBipDOMR1x1MDAxOZ5kLolcdTAwMTJsZJa1RKmFJ4hcYkmju0lYu2BcIkhZ01UwQTxBbrZ74PpqzOGVm/cmmbdSqYRcYozPXHUwMDE1jbJcdTAwMTHPSsiUT3NDbKtJoXKj7dA5g4JyhrVvXHUwMDA1Zb/T8Tq9uHFcdTAwMTmnNdxfXHUwMDFl3+TksnKupL+KsnKhbVNdsbAkI6coRZe1XHUwMDAym10oTqe8Zbhi1Y9cdTAwMWLBsoOx9ijUSlxutIYh48PB2IBcdTAwMDecXHUwMDE5amTkhsYsNvFb5IncXHUwMDEzzsW04kZxd5dcdI7JuSRSsKBpRCwl72JsJom7iVx1MDAwMiZI187vqc8vy5BuJLvnWaCQs2Omssx0ibeRL8tYo5hQSlCqRfwhclXNp1xuXGJ4Wlxu5UaUXHUwMDEz/VEnPp1QUJVcdTAwMTl+ilx1MDAxZqg0UoyotHVcZkvZ9V7l3+emXHUwMDEzZVxuQztHkETtYlx1MDAwZXE/XeusKaFcdTAwMDBcdTAwMDePUkpcdEaCXCKVn027pISiSFkjXG5cdTAwMDDNgVx1MDAxOGexuapcIkJcdTAwMTFcdTAwMWVQnm+UIHfgUptcdFx1MDAxYZ9z7qFby6WsK7vz3ErHgdJHXHUwMDFhM3jWcqpF+IS6jWfG/I18UlwiQrGCuVVBjjGktpg765FQKHhcYkB3krbaMGV/UkIpRJR7jWNpWXxC2UAhnyhcdTAwMDZMXG4+R6F1eq63pnxcIjR6qJTRmlx1MDAxMiPQw3RcIoT1pFx1MDAxNVppXHUwMDFhXHUwMDE4o5larJJVLFAsY5xuT9lcbpOcTZj6pifxSEXRXHUwMDE1LKUsbv52lE9QXHUwMDExMii1y1x1MDAxYVZCJ0ipy0vOXHUwMDFhzSFPSJxJKy2iXHUwMDA2olxuXHUwMDFh1DE6sVx1MDAxZadcdTAwMGUkkyl40Kjit6nXn41OXG5cdTAwMDGVNo5BaU46mbbEu3ihK7FcdTAwMWKSaJqjMv7pTInTqFxcOjg9tlx1MDAwZqdYPWbnX2prXnwkXHTmKTBGK0U9j7lcdTAwMTn5lE8096hcdTAwMDeMJFVohFx1MDAxNHKx0sNLbHBAXHUwMDEy+EOZ2ErKj0dcdTAwMTf7b5p7R8FJr1x1MDAwM+XD6qfT3avP0bLKbujkRcbcL1h6l4XhVEmwXHUwMDFhQc++kKxxU2p3XHUwMDBmto7fn15Hl5dxq9xTbz+sO/qBMlx1MDAxZlx1MDAwNsg5PaxQo/tcdTAwMWIs86RbdUSpXHUwMDEx0zQq6zUvxLlgXHUwMDE0RC0+I4IuMDGElph4XHUwMDFlQf5cXHSiLlxctmyF0qTF5+Dmz7+fd9iX3bPem/P6Wb9S7d10+s8qRq1cdTAwMTSdgjRcdTAwMDJ1ttRcdTAwMWElz3XH41x1MDAwNZRcdTAwMDdcXKBcdTAwMTKkXHUwMDExXHUwMDAw0C6WOy59Yki4WXVjV7wrYYF5oe9cdTAwMTKzJI3CzNKIuWg+RPDiJMcyRVx1MDAwMlxi7ezbzirJznZ8dVx1MDAxYpr7sniItpvxzVFcdTAwMTfWPckxaDw3XHUwMDExQsLEas1gWJWUXHUwMDA0+Vx1MDAwNaBUaaqthF5s31lcdTAwMTHy81x1MDAxYlCmrNjnXGYkokSxYqSrOHp7v7d3wpPjg6D8caeCeLK/XHUwMDE0pFPmzjVH8ayqy1wiS/blWi7Zl1x1MDAwYi/ZXHUwMDA3LC6DOpaXgs1cdTAwMTHKplx1MDAwZvx6TnFazTyBXHUwMDAyJMFcbpRcdTAwMWTZRq09bi0pXHUwMDE5Zlx1MDAwNXk7vkwgk5LuopVA6TJcdTAwMWGYtFx1MDAxZMcqz1xiSomsUW5cdTAwMTNcdTAwMGVcdTAwMWL3dVx1MDAwMaBcZua3XHUwMDFmr2Kp69xxJ2fHTEWL6UFiI1+0oFx1MDAwNF1cdTAwMGLLSSy7moTiuZNcdTAwMDZLXVx1MDAwNWjDnO3I3L7EpzN+tqJFqVx1MDAxMFHuNYal7HKv8u9zq1x1MDAwM1moio3klKHjXHUwMDFje/mOXHUwMDBm3vdKeHi6/eXjx/pd/fL0T41FS01cdTAwMWJ+pdHrXHUwMDA266CL0Vx1MDAxNYRAWMNcdTAwMTQoPZK1XHUwMDAxqWYrgCSElJbU68uIg9zE1jRtXHUwMDAwlMhz58Gr1Vx1MDAwNrUusWjnXGaO3zW2XHUwMDBmv1x1MDAxY4ja4bugM5s2eD3tui9a9rDWTZCtTHM8bqldXHUwMDA3nfFkyfO0XHUwMDA1isL/ocWme+D4XHUwMDFjM6zTcTNcdTAwMTdcdTAwMWasUFxcaItcdTAwMWVBR9Cjulx1MDAxZPt2ZFx1MDAxYo1cdTAwMTJcdTAwMWVoXHUwMDA13M2aUFx1MDAxZbtYXHUwMDExpzBZXHUwMDAwj9PFXHLnkrJxJifJXHUwMDBiro1cdTAwMDeIRksrXHUwMDA1XHUwMDEy6Mc20qCbu2GYXHUwMDFisZXMiTzb8WaUXHUwMDE308PMxvCuXHUwMDE1t0NDS+pAt0JcdTAwMDAmLNrgdJLmMt1qwMBcdTAwMDD8rFx1MDAwMqNcdTAwMThT7lVcdTAwMWGH05xcdTAwMTKjkFM0K+RcdTAwMTTQbjGylLNrjOkxY105XHUwMDA1085329qY0E5VjXCKJVx1MDAwNWKYNcYoXHUwMDE0L7Qm2yjPpYaoLaOsg7p9nFLIXHUwMDBlt1KNJCjRXHUwMDFiSW4xXoejhEpxQlHWef9/nFwiaFx1MDAxOK2rXHUwMDE4XHUwMDE56lx1MDAwYs3HOUW6rXCIlIQq1EaQfpzOKUVWTZ9cdTAwMDJcdTAwMWOxijFlJLOKI1GZlVx1MDAxM5jOc5UwTckxxVx1MDAwNUvZxI+9Qa9cdTAwMTDP7lVcdTAwMWGHclx1MDAxMZ29erqDW/x6lFx1MDAxMO5cdTAwMDZcdTAwMDNC0Fx1MDAwZatPSjB7zM3bMLgrT5qRSV9OeKVcdTAwMWTquChwXHUwMDBm+9fXV1//XHUwMDA3mNhRliJ9 + + + + Screen 1(hidden)Screen 2 (visible)app.push_screen(screen3)Screen 3 (visible)hidden \ No newline at end of file diff --git a/docs/images/screens/switch_screen.excalidraw.svg b/docs/images/screens/switch_screen.excalidraw.svg new file mode 100644 index 000000000..02340e556 --- /dev/null +++ b/docs/images/screens/switch_screen.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1ba1PbSlx1MDAxMv2eX0Fxv2SrwmSme563amuLXHUwMDAwSYBcdTAwMDRcYoTn7i1K2DLWxcjGljGQ4r9vjyCW/MRcdTAwMDbh60SpirFGXHUwMDFltWZOnz7dM/rxZmFhMblthIt/LiyGN6WgXHUwMDE2lZtBZ/GdP39cdTAwMWQ2W1E9piZIv7fq7WYpvbKaJI3Wn+/fX1x1MDAwNs2LMGnUglLIrqNWO6i1knY5qrNS/fJ9lISXrf/4/7eCy/DfjfplOWmy7CZLYTlK6s2He4W18DKMk1x1MDAxNvX+X/q+sPAj/T9nXTlcbi7rcTm9PG3ImWeg/+xWPU5NRSOkRFDZXHUwMDA1UWuVbpaEZWqtkMFh1uJPLd5cdTAwMWXVjltfro6vV9pL6+Kq5O6ipJTdtVx1MDAxMtVqe8ltLbWpVadHydpaSbN+XHUwMDExXHUwMDFlRuWkSq2i7/yoXzXr7fNqXHUwMDFjtlo9v6k3glKU3NI5xbsng/g87VwiO3ND35ZQXHRmjLZCKi6F0lZ1231cdTAwMDdSIzPWXHUwMDE4JVBcdTAwMWGUKFSfZSv1XHUwMDFhzVx1MDAwM1n2XHUwMDA3T4/MtrOgdHFOXHUwMDA2xuXsXHUwMDFhoYLgrJJd03l8XulcdTAwMTSTVqLJuq+G0Xk18TNkNbNGcJdvbYXpJFx1MDAwMEiOXHUwMDBlpe02+Ds21sspXHUwMDE4/upcdTAwMWbFatBsPI7WYst/yVnrXHJd60dSXHUwMDFlTbl5XHUwMDBlk60obG/uo908WXL6unZcbvGXbl890Fx1MDAwYprNemex23L/+FdmWrtRXHUwMDBlXHUwMDFlXHUwMDEwJbSW1jonXHUwMDExdTaZtSi+oMa4Xatl5+qli1xmhOnZ+3dTQ1+iXHUwMDFhXHUwMDA1fWGE5YZcdTAwMWKFXHUwMDEzY/9buFx1MDAwYqvy5nTr6/7nw87dyVqwsb77T2Jf8KfBL1xyU8ZI0JxcdTAwMGJcdTAwMTRG92BcdTAwMWZcdTAwMWQwtJxcdTAwMDPhy2nj+MuwX1x0zjhXXHUwMDA1Yp9cZlx1MDAxM85pmDH4d/fU9vH5bny61F4+WNvYu1rhl0Eh4HdcdTAwMWNcdTAwMDXXTpqiwJ+EN8kw5CurRyHfSIfcXHQ+OfCD9u6H1dO/66s78cHhzdHnlS1xsD/vwHeGWVx1MDAwZVx1MDAwNlx1MDAwNXBjre1cdTAwMDW+XHUwMDExiiE5v1x1MDAxMFx1MDAxNlx1MDAxNflcdTAwMDa8XGL3xilegUHcXHUwMDBiblx1MDAwN1x1MDAwMW9EP8yNUmhpSmC2KH89ivcol1x1MDAwNjROgfJcZk31ONmL7sKUXHUwMDFies5+XGYuo9ptXHUwMDBmJFL4k4F7pWZcdTAwMTjGXHUwMDBi4n/x22pULofxv/Iz1lxu6f6+Q937y+VadO6dZbFcdTAwMTZWer0oiUiHdZuTem6MS2RJQN0118v9T1RvRudRXHUwMDFj1L6Ptup5zlxm2H+2XHUwMDFixoBTRFx1MDAxNVx1MDAxYSf35n1zdKW/ru23XHUwMDFijZM1ufql9Gln+2Devdk6Rspccq1CobnJKdo0jKFggk5qhU4rqUyfYcV4M2DGIV1vzp179GZnXHUwMDAwjVQye4LfIGYpQTFj1t5cZlx1MDAwYm8pbYrOauFwb1x1MDAwNtXzy1x1MDAxOXlz3qqx3vwwzEPcWVDAXHUwMDE56c9cdTAwMWEoLmmXXHUwMDEzQ0/58/iZn8Kf+4PgK/ozXHUwMDE4x9ApLZVcdTAwMTXINSVevVx1MDAwZa2tbzZCgKNLQeg+04rxaE20IYSSXHUwMDAwNCXW8Yw3uv7tOLOAVlx1MDAxYnJcdTAwMDTuzDCNSuKCXHUwMDEz7TwjeqdmjvH3MVx1MDAxZamUdWJcdTAwMWFcdTAwMTWZsyNoJlx1MDAxZqK4XHUwMDFjxefUmFHJzyrD+lx1MDAwNFFcIvXhUttbyZlQTkmfOGvCrVWZsPRjXHUwMDExNLzRXGaElTTZpOWl9v9cdTAwMWWvuO+aXHUwMDE1xuWnjVx1MDAxYZ+B5Yxa4lxmXGbnVqRZPyX/w2xSXHUwMDE0Oo2VwklHc2/NgE21oJWs1C8vo4TGfqdcdTAwMWXFSf9cdTAwMTing7nsXHUwMDFkvVx1MDAxYVx1MDAwNlx1MDAwM1xmQs+Ub+tnhIbvsZfYs79cdTAwMTYyl0m/dP/+693Qq0di2Vx1MDAxZlx1MDAwMyjOenuT/5yayizI0Vx0tuJkXHUwMDBiTpFnjFekc8pkXHUwMDE2mEStjDac5Fx1MDAwN++rLlx1MDAxOWlcdTAwMTnpXHUwMDAxi0IgXHUwMDAygf91iMwxS05oXGY33MqhRKZcdTAwMWNcdTAwMDOtkfxPXHUwMDAwOSk561x1MDAwMJORL1BcdTAwMDcua5hcdTAwMDWRPT9RmJDIxievPUSmuSVcckkpIShuOdrcRVx1MDAwZpxhXHUwMDE4ak1ORsiWOTeaksXG11B7LOKSWJ7EpJGCaGpcYon92pw1XG62/lhcdTAwMWFE7JSsNaYwiNb2n/3JW8A1XHUwMDA1MtAyY7aneOvo5KyxvXVcdTAwMTR/aneuoqVyI1o+vKjOd0ZcdTAwMDW+Lmh9YVx1MDAwNIxASiCzp30oiltGvimcc8TgVubSy+dcdTAwMTfFtS6uNOhcYlx1MDAxOJKkV2Z2YVnWU4XrLLi8XuFay5H4XHUwMDE0NGlAUSeXeT6Fz+Xbyn6w8XU5PttapyyhXHUwMDEzr8HaVaH4LFx1MDAwN61qWCxAXHUwMDE1MF+wNs5wXHUwMDA3wPtTfkdhl7SOXHUwMDA2pcGidkVUrlWBtWshXHUwMDA1glO/LUJcdTAwMDUp5tFcdTAwMTB11iiVn5QnKfTz6fftg53D00pjXHUwMDFmXHUwMDBmyzV5Vr9K5pxCVZrWgPJ6QIHKuWzagUNm0WiCgaaxgFxc83NcdTAwMTBqoeREWOTailx1MDAwM0WSddZcdTAwMGKLNlxmP7e/3N3Vzk70aVx1MDAwNZNcYm9PeE78XGZkXHUwMDFk3Zb7d+P6/XDbODk7P7jYdNt3653malx1MDAxNG2eVFxu6Pfo2sa6tb/Z6TTOT+4qzXJUWv5WQL8tsby/oTu3nfKyXGI2VLR0c9Jcbifr96f4XHUwMDE5I6op63WuKFx1MDAxNlx1MDAxOFWWxtwt+lx1MDAxOYA0NHcyX+h6ilx1MDAwMVx1MDAwZfTX6NpcdTAwMWSFV1ut1dW/cev76qe6elx1MDAwZVx1MDAwM8wu+aNcdTAwMTjMlKBcdTAwMThcdTAwMDSG8j8rVe9cIpPgyCgr1Eg6S1x1MDAxOYUvrEtT0D9cdTAwMGKHSCilh6R7ckhAoilRlN28QmF6XFxAUsbBNFDMZjwrXHUwMDFko2FkN4D2UtVq13NNt5CcadSfheSg0WCtXHUwMDBlZVfV01ZavX378IHDXHUwMDBiyjlJNYuC8lx1MDAxOOvG+uPowjJcdTAwMDczyiOdL8+RUlx1MDAxMlx1MDAxMzvkeOYrwiFfQzUqli6+cIJcdTAwMWb5pVx1MDAxMz1cdTAwMWWJUjNcdTAwMDNcdTAwMTIsXHUwMDE31tCI9IuFYlx1MDAxY1x1MDAxMpjvXFwq0Fx1MDAwNqxAXHUwMDFjVo6xgqFDrf3KL4p8evXorWillFx1MDAxNvkzwvNLqjHAKel7jrdOWI1cdTAwMTmv81x1MDAxNvK1XHUwMDBmZ/1cbr1F5Yzk0uSKXHUwMDAzj8VcdTAwMGbFnOD+XCKkqVLcyMdcdTAwMGJGlGN6n+JcdTAwMTcqk4zGkz/6kZT19ib/+YxVqpx7XGaEd1x1MDAwZSQsXHUwMDA1n1xc4I/XO/PJJkZcdTAwMWHm90ZppMdFxL5FKlx0TKVcdTAwMDOhTH5jVZFUwpklia5IO5BzgsYhXCLfOlx1MDAwNqBQUlhcdTAwMTTGXHUwMDFhmVx1MDAxYp9HKlx1MDAxMZo0XGKimm1h1y9cdOlnrVx1MDAxOVx1MDAxN00lS4JRPCAxXCI5ZWJcblx1MDAxMHJcdTAwMTc9UIlkpM80SkEjrX014TelkqWRgPLHXHUwMDAwlFxu41x1MDAxMu5GXHUwMDE3XHUwMDBiiNqk8W40MZeMz/Xmk0uko8HliljVXHUwMDA3M217hVx0XHI545pcdTAwMDNcdTAwMDHQaMrWZZ9hRbFcdFLfaISWipCuzbCigeNMe1qjtFx1MDAwMoBSmiHSxDhcdTAwMTSU7cx4xVx1MDAxYvKe+89KXHUwMDEzolx1MDAxMyBx5pcuKOpqv7d8OKU47lxmjbRfYVx1MDAxZVxcXv49KGVcZqj8MVx1MDAwMKcpOWXc7m4zeluco5F3SuqMdp4klY9tPNupXHUwMDFjrlx1MDAxZlx1MDAxZuhjabf33Ea0Od9cdTAwMTVIazlTToBna45G9pZcdTAwMWZIMjNiem6dQnAoX7bHtfglXHUwMDFj6Ywz5CEzrkfMqkBuxyzh0JCQalx1MDAwNDH5ezdcdTAwMWIrXHUwMDFjNlx1MDAwZjrf4XNyvtWpXFxt/91cXPs07+iUjKZdWy2cXHUwMDA0wL6Ix71cdTAwMTRcdTAwMTHaOFJl3Cr+spBXdHmcNDMxiHqNYlmXXHUwMDAwZ1hcdTAwMWRfOfpcIiN1Uvlm9m+X17ZcdTAwMTMnPn69KPR1nsJcXGrkJujRr7FcdFxuL5JcdTAwMDA5RXFcdTAwMGLwuLSr9nBtvaHg8FPVVHfK8y4hrfSboJXfXGKC1uTX39J0VPjdKU6QN1E+qtzr+Fx1MDAxM1xmS0FcdTAwMDc3QVx1MDAwYr9cdTAwMGZbKy5m/E7D6+HcXHUwMDE3x9BKMfN3XHUwMDFhcC53QePLd0FLXHUwMDE4+YqS8HvpSLfYyfdcdTAwMGWOn/m5XFw+clozUKTMKO/j2kHfXHUwMDBlXHUwMDA3rZimZv+2g7D5d+CK9GdtXHUwMDE4aXfSKlJL4HbYXHUwMDBiS9YxTlx1MDAxM1x1MDAwMlx1MDAwMFx1MDAwZVRPoevR18FcbsVdfq1vJqXqZ3vjhPng+Fx1MDAxMNGTXHUwMDBmXG5LSTHlNyhcdTAwMWRcdFpcdTAwMWOyT1x1MDAwZmimtdc7Wlx1MDAxYU4z/jNccppy6+B4XHUwMDE5uNCzXHUwMDAxWlC+ZZVcdTAwMTbaot+GkL1cdTAwMDHXtVxuXHUwMDE4pa6kcazzV0puXHUwMDA3t2X/SpnoSCz7o1x1MDAxZsVZZ2/ynz5cZqTd+9W5vYQg151cckJ1VH5k8excdTAwMTlcdTAwMTevo7DzYdiWovTw5JiOpmeg0D/pj/s39/9cdTAwMDdcIjOdrCJ9 + + + + Screen 1(hidden)Screen 2 (visible)app.switch_screen(screen3)Screen 3 (visible) \ No newline at end of file diff --git a/docs/reference/app.md b/docs/reference/app.md index 0321acaaf..3a797ce06 100644 --- a/docs/reference/app.md +++ b/docs/reference/app.md @@ -1 +1 @@ -::: textual.app.App +::: textual.app diff --git a/src/textual/app.py b/src/textual/app.py index 188a49791..241e385fe 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -26,20 +26,11 @@ import nanoid import rich import rich.repr from rich.console import Console, RenderableType -from rich.measure import Measurement from rich.protocol import is_renderable from rich.segment import Segment, Segments from rich.traceback import Traceback -from . import ( - Logger, - LogGroup, - LogVerbosity, - actions, - events, - log, - messages, -) +from . import Logger, LogGroup, LogVerbosity, actions, events, log, messages from ._animator import Animator from ._callback import invoke from ._context import active_app @@ -113,7 +104,7 @@ class ScreenError(Exception): class ScreenStackError(ScreenError): - pass + """Raised when attempting to pop the last screen from the stack.""" ReturnType = TypeVar("ReturnType") @@ -223,6 +214,7 @@ class App(Generic[ReturnType], DOMNode): self._installed_screens: WeakValueDictionary[ str, Screen ] = WeakValueDictionary() + self._installed_screens.update(**self.SCREENS) self.devtools = DevtoolsClient() self._return_value: ReturnType | None = None @@ -801,7 +793,7 @@ class App(Generic[ReturnType], DOMNode): try: next_screen = self._installed_screens[screen] except KeyError: - raise KeyError("No screen called {screen!r} installed") from None + raise KeyError(f"No screen called {screen!r} installed") from None else: next_screen = screen if not next_screen.is_running: @@ -829,7 +821,7 @@ class App(Generic[ReturnType], DOMNode): """Push a new screen on the screen stack. Args: - screen (Screen | str): A Screen instance or an id. + screen (Screen | str): A Screen instance or the name of an installed screen. """ next_screen = self.get_screen(screen) @@ -931,7 +923,7 @@ class App(Generic[ReturnType], DOMNode): widget (Widget): Widget to focus. scroll_visible (bool, optional): Scroll widget in to view. """ - if widget == self.focused: + if widget is self.focused: # Widget is already focused return diff --git a/src/textual/geometry.py b/src/textual/geometry.py index 7c22c1a92..be87975ec 100644 --- a/src/textual/geometry.py +++ b/src/textual/geometry.py @@ -223,7 +223,7 @@ class Size(NamedTuple): class Region(NamedTuple): """Defines a rectangular region. - A Region consists a coordinate (x and y) and dimensions (width and height). + A Region consists of a coordinate (x and y) and dimensions (width and height). ``` (x, y) diff --git a/src/textual/screen.py b/src/textual/screen.py index 284bdd283..e3c1787cb 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -40,8 +40,13 @@ class Screen(Widget): dark: Reactive[bool] = Reactive(False) - def __init__(self, name: str | None = None, id: str | None = None) -> None: - super().__init__(name=name, id=id) + def __init__( + self, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + ) -> None: + super().__init__(name=name, id=id, classes=classes) self._compositor = Compositor() self._dirty_widgets: set[Widget] = set() self._update_timer: Timer | None = None diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index 0f82f4f16..8f3a4a4dc 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -10,7 +10,7 @@ from .. import events from .._segment_tools import line_crop from ..binding import Binding from ..geometry import Size -from ..message import Message, MessageTarget +from ..message import Message from ..reactive import reactive from ..widget import Widget