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

Initial value of Input not rendered upon .mount #1443

Closed
mrossinek opened this issue Dec 26, 2022 · 5 comments · Fixed by #1548
Closed

Initial value of Input not rendered upon .mount #1443

mrossinek opened this issue Dec 26, 2022 · 5 comments · Fixed by #1548
Labels
bug Something isn't working

Comments

@mrossinek
Copy link

Have you checked closed issues?

Yes, I did not find anything similar.

Please give a brief but clear explanation of the issue.

The value of Input(value=value) does not get rendered when an Input is added via .mount.
It only gets rendered after an additional character is entered.

What Operating System are you running on?

Arch Linux; Kernel 6.0.12-arch1-1

Feel free to add screenshots and/or videos. These can be very helpful!

Screenshot of the snippet below which yields Input as part of .compose:
screenshot_1672054846

Screenshot of the snippet below which tries to .mount an Input widget after pressing p to trigger the action:
screenshot_1672054926
After inserting an additional character the provided value gets rendered properly:
screenshot_1672054933

If you can, include a complete working example that demonstrates the bug. Check it can run without modifications.

Here is a working example based on .compose:

from textual.app import App, ComposeResult
from textual.widgets import Input


class Prompt(App):

    def compose(self) -> ComposeResult:
        prompt = Input(value=":")
        yield prompt
        prompt.focus()


if __name__ == "__main__":
    Prompt().run()

And here is the broken example trying to use .mount:

from textual.app import App
from textual.widgets import Input


class Prompt(App):

    BINDINGS = [
        ("p", "prompt", "Prompt"),
    ]

    def action_prompt(self) -> None:
        prompt = Input(value=":")
        self.mount(prompt)
        prompt.focus()


if __name__ == "__main__":
    Prompt().run()

Solution Ansatz

I am suspecting the problem to lie in the Input widget itself. Specifically this widget has a view_position attribute which I believe to be the source of the error. Upon mounting this attribute has value 2 and it gets reset to 0 once inserting a character.
You can verify this with the following patch:

diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py
index c2bfa638..21d67772 100644
--- a/src/textual/widgets/_input.py
+++ b/src/textual/widgets/_input.py
@@ -41,6 +41,7 @@ class _InputRenderable:
             segments = Segment.adjust_line_length(segments, width)
             line_length = width

+        yield str(input.view_position)
         line = line_crop(
             list(segments),
             input.view_position,
@github-actions
Copy link

Thank you for your issue. Give us a little time to review it.

PS. You might want to check the FAQ if you haven't done so already.

This is an automated reply, generated by FAQtory

@davep davep added the bug Something isn't working label Jan 3, 2023
@davep
Copy link
Contributor

davep commented Jan 3, 2023

To narrow this down a little: it does get rendered on mount, but it doesn't get rendered if it is mounted and then focused.

It does work as expected if the action is made into an async action and then the mount is awaited before setting focus:

    async def action_prompt(self) -> None:
        prompt = Input(value="This is a test")
        await self.mount(prompt)
        prompt.focus()

@mrossinek
Copy link
Author

To narrow this down a little: it does get rendered on mount, but it doesn't get rendered if it is mounted and then focused.

It does work as expected if the action is made into an async action and then the mount is awaited before setting focus:

    async def action_prompt(self) -> None:
        prompt = Input(value="This is a test")
        await self.mount(prompt)
        prompt.focus()

This is indeed the case, thank you very much! In fact, .focus() is not required meaning that the value gets rendered correctly as long as one awaits the .mount 👍
I suspect this is the intended behavior? If so, then I think this can be closed (a documentation update may be needed somewhere though 🤔)

@davep
Copy link
Contributor

davep commented Jan 5, 2023

I suspect this is the intended behavior? If so, then I think this can be closed

When we get a moment I'd like to double-check this behaviour with @willmcgugan and @darrenburns first. It feels like it's behaving correctly, but I'd like their final say.

rodrigogiraoserrao added a commit that referenced this issue Jan 11, 2023
When an input is instantiated without an argument for the parameter 'value', the first time the watchers associated with its reactive attributes run, the attribute self.view_position is set to 1, which makes no sense because there isn't even any value in the input. Thus, when the value is later modified programmatically, e.g., because it is set with 'some_input_widget.value = some_value', the attribute self.view_position will still be 1 and the text will be rendered starting from the first character.
As two example situations of this, consider #1477 and #1443.
@rodrigogiraoserrao rodrigogiraoserrao linked a pull request Jan 11, 2023 that will close this issue
2 tasks
@github-actions
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
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants