import tkinter
from typing import Callable
from typing import Any
import functools


def bind_tab_key(
    widget: tkinter.Widget, on_tab: Callable[["tkinter.Event[Any]", bool], Any], **bind_kwargs: Any
) -> None:
    """A convenience function for binding Tab and Shift+Tab.

    Use this function like this::

        def on_tab(event, shifted):
            # shifted is True if the user held down shift while pressing
            # tab, and False otherwise
            ...

        utils.bind_tab_key(some_widget, on_tab, add=True)

    The ``event`` argument and ``on_tab()`` return values are treated
    just like with regular bindings.

    Binding ``'<Tab>'`` works just fine everywhere, but binding
    ``'<Shift-Tab>'`` only works on Windows and Mac OSX. This function
    also works on X11.
    """
    # there's something for this in more_functools, but it's a big
    # dependency for something this simple imo
    def callback(shifted: bool, event: tkinter.Event[tkinter.Misc]) -> Any:
        return on_tab(event, shifted)

    if widget.tk.call("tk", "windowingsystem") == "x11":
        # even though the event keysym says Left, holding down the right
        # shift and pressing tab also works :D
        shift_tab = "<ISO_Left_Tab>"
    else:
        shift_tab = "<Shift-Tab>"

    widget.bind("<Tab>", functools.partial(callback, False), **bind_kwargs)  # bindcheck: ignore
    widget.bind(shift_tab, functools.partial(callback, True), **bind_kwargs)