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

input_dialog validation #1715

Open
sandrospadaro opened this issue Jan 29, 2023 · 2 comments
Open

input_dialog validation #1715

sandrospadaro opened this issue Jan 29, 2023 · 2 comments

Comments

@sandrospadaro
Copy link

Validation does not work with input_dialog.

how to reproduce the issue

from prompt_toolkit.validation import Validator
from prompt_toolkit.shortcuts import input_dialog
from prompt_toolkit.validation import Validator


def is_number(text):
    return text.isdigit()

validator = Validator.from_callable(
    is_number,
    error_message='This input contains non-numeric characters')

age = input_dialog(title="Age", text="Your age:",
                   validator=validator).run()

print(f"Your age: {age}")

Fill the input dialog with some string (eg: abc) and press tab key to move focus on OK button. Then press enter key.

Output

Your age: abc

Output expected

Error message on focus lost

About the environment

$ python -V
Python 3.11.1
$ pip freeze
prompt-toolkit==3.0.36
wcwidth==0.2.6
@x24git
Copy link

x24git commented Apr 21, 2023

Hi there, I created a PR that fixes the validation issue for input dialogs #1747.
Until the PR is merged you can make your own input_dialog class that fixes the issue while using the base prompt-toolkit package

class CustomValidationToolbar:
    def __init__(self, show_position: bool = False, buffer: Buffer = None) -> None:
        def get_formatted_text() -> StyleAndTextTuples:
            # If buffer not specified, use the currently focused buffer
            if buffer is None:
                buff = get_app().current_buffer
            else:
                buff = buffer
            if buff.validation_error:
                if show_position:
                    row, column = buff.document.translate_index_to_position(
                        buff.validation_error.cursor_position
                    )

                    text = "{} (line={} column={})".format(
                        buff.validation_error.message,
                        row + 1,
                        column + 1,
                    )
                else:
                    text = buff.validation_error.message

                return [("class:validation-toolbar", text)]
            else:
                return []

        self.control = FormattedTextControl(get_formatted_text)

        self.container = ConditionalContainer(
            content=Window(self.control, height=1), filter=True
        )

    def __pt_container__(self) -> Container:
        return self.container

def input_dialog(
    title: AnyFormattedText = "",
    text: AnyFormattedText = "",
    ok_text: str = "OK",
    cancel_text: str = "Cancel",
    completer: Completer | None = None,
    validator: Validator | None = None,
    validate_while_typing: FilterOrBool = False,
    password: FilterOrBool = False,
    style: BaseStyle | None = None,
    default: str = "",
) -> Application[str]:

    def accept(buf: Buffer) -> bool:
        get_app().layout.focus(ok_button)
        return True  # Keep text.

    def ok_handler() -> None:
        textfield.buffer.validate()  # validate one final time before exiting
        if textfield.buffer.validation_error is None:
            get_app().exit(result=textfield.text)

    ok_button = Button(text=ok_text, handler=ok_handler)
    cancel_button = Button(text=cancel_text, handler=_return_none)

    textfield = TextArea(
        text=default,
        multiline=False,
        password=password,
        completer=completer,
        validator=validator,
        accept_handler=accept,
    )

    textfield.buffer.validate_while_typing = validate_while_typing

    dialog = Dialog(
        title=title,
        body=HSplit(
            [
                Label(text=text, dont_extend_height=True),
                textfield,
                CustomValidationToolbar(textfield.buffer),
            ],
            padding=D(preferred=1, max=1),
        ),
        buttons=[ok_button, cancel_button],
        with_background=True,
    )

    return _create_app(dialog, style)

You can call input_dialog just as you normally would.

result = input_dialog(title='Input dialog example', text='Please type your name:').run()

The only difference is you can also now pass in the validate_while_typing argument as well

result = input_dialog(title='Input dialog example',
                      text='Please type your favorite number:',
                      validator=NumberValidator(),
                      validate_while_typing=True
).run()

t1babin added a commit to FutuRePr0Grammer/python-prompt-toolkit that referenced this issue Apr 29, 2023
@Megver83
Copy link

Megver83 commented Jun 3, 2023

The problem, as mentioned here, is that validation is ran when pressing enter, but not the OK button.

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

3 participants