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

Should Widget.loading place the obscured widget in a disabled state while True? #3516

Closed
davep opened this issue Oct 12, 2023 · 2 comments
Closed
Labels
enhancement New feature or request Task

Comments

@davep
Copy link
Contributor

davep commented Oct 12, 2023

With the new Widget.loading reactive, the obscured widget is still enabled and can be interacted with by the user; this could potentially have unintended consequences as the user attempts to navigate an application's UI. To illustrate, take the example in the documentation, but instead place the pause after the job of populating the table (essentially simulating a loading operation where data is coming in in realtime, with each new item of data causing a short pause).

from asyncio import sleep
from random import randint

from textual import work
from textual.app import App, ComposeResult
from textual.widgets import DataTable

ROWS = [
    ("lane", "swimmer", "country", "time"),
    (4, "Joseph Schooling", "Singapore", 50.39),
    (2, "Michael Phelps", "United States", 51.14),
    (5, "Chad le Clos", "South Africa", 51.14),
    (6, "László Cseh", "Hungary", 51.14),
    (3, "Li Zhuhao", "China", 51.26),
    (8, "Mehdy Metella", "France", 51.58),
    (7, "Tom Shields", "United States", 51.73),
    (1, "Aleksandr Sadovnikov", "Russia", 51.84),
    (10, "Darren Burns", "Scotland", 51.84),
]


class DataApp(App):
    CSS = """
    Screen {
        layout: grid;
        grid-size: 2;
    }
    DataTable {
        height: 1fr;
    }
    """

    def compose(self) -> ComposeResult:
        yield DataTable()
        yield DataTable()
        yield DataTable()
        yield DataTable()

    def on_mount(self) -> None:
        for data_table in self.query(DataTable):
            data_table.loading = True
            self.load_data(data_table)

    @work
    async def load_data(self, data_table: DataTable) -> None:
        data_table.add_columns(*ROWS[0])
        data_table.add_rows(ROWS[1:])
        await sleep(randint(2, 10))
        data_table.loading = False


if __name__ == "__main__":
    app = DataApp()
    app.run()

If you run this and, while the first DataTable is in a loading state, press down a few times, once the loading has finished and the DataTable is revealed you can see that the cursor has moved.

While a mild problem in this example, it could have negative unintended consequences if UI elements being added in the background are more destructive.

It also means that a container in a loading state could be adding to the focus chain, with subsequent containers and widgets being visible and interactive, but the focus is "lost" amongst the obscured but focusable widgets.

As such, perhaps the widget that is obscured by loading being True should also be disabled for the duration, or at the very least loading being true should be treated as if disabled is True from a styling and interactivity point of view.

@davep davep added enhancement New feature or request Task labels Oct 12, 2023
@willmcgugan
Copy link
Collaborator

Closing because I think we implemented this recently. Reopen if I'm mistaken.

Copy link

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Task
Projects
None yet
Development

No branches or pull requests

2 participants